dsh-plugin-kie-ai
DeepSeek Harness plugin: image and video generation over the KIE AI API
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 28, 2026
- Updated
- Aug 28, 2026
Introduction
dsh-plugin-kie-ai
Image and video generation for a DeepSeek Harness agent, over the KIE AI REST API.
A community plugin, not an official one. It registers three tools on ctx.tools and consumes nothing else — no new capability seam, no change to the agent loop.
| Tool | What it does |
|---|---|
kie_generate_image | Create an image task, poll every 5s until it completes (default 5 min), and return the final image URLs. |
kie_generate_video | Create a video task, poll every 5s until it completes (default 5 min), and return the final video URLs. |
kie_task_status | Snapshot one task's status and any asset URLs produced so far — no waiting. |
The bearer token is read from the environment per operation (via apiKeyEnv, default KIE_API_KEY). It is never hardcoded, never logged, and never written to a config file.
Install
npm install dsh-plugin-kie-ai
Then add a row to your profile's cordis.patch.yml:
- insert:
- id: tool-kie-ai
name: 'dsh-plugin-kie-ai'
Set the key before calling the tools:
export KIE_API_KEY=<your KIE API key>
dsh web
The plugin declares @deepseek-ai/cordis, @deepseek-ai/dsh-tools, and @deepseek-ai/dsh-llm as peers; a harness install already provides them.
Configuration
- id: tool-kie-ai
config:
baseUrl: https://api.kie.ai/api/v1
apiKeyEnv: KIE_API_KEY
pollIntervalMs: 5000
pollTimeoutMs: 300000
| Option | Default | Meaning |
|---|---|---|
baseUrl | https://api.kie.ai/api/v1 | API base; /jobs/... is appended. |
apiKeyEnv | KIE_API_KEY | Environment variable holding the bearer token. |
pollIntervalMs | 5000 | Delay between status checks. |
pollTimeoutMs | 300000 | Total polling budget (5 minutes). |
How it works
Both generation tools call POST /jobs/createTask and then poll GET /jobs/recordInfo?taskId=... until the task completes, fails, exhausts the budget, or the agent turn is cancelled.
Failures are classified so the model gets something to act on rather than a status code to guess at:
| Condition | Error kind |
|---|---|
HTTP 402/429, or a body mentioning balance or quota | insufficient-balance |
A KIE status of failed/error/cancelled/… | task-failed |
| No terminal state within the budget | timeout |
| The call was aborted by the agent | cancelled |
| A response with no task id, or a success with no URLs | protocol |
Responses are parsed defensively: task ids are read from task_id, taskId, id, or those nested in data, and result URLs are discovered by recursing for http(s) strings, bounded to depth 6 and 64 hits.
An unrecognized status is treated as pending, never as failure. The provider adds status strings, and treating an unknown one as terminal would abandon a task that is still running.
What the model reads back
Generated image with nano-banana-pro (task T-9, success):
- https://cdn.example/asset.png
Task T-9: state pending, status rendering
Known limitations
- A generation holds the turn. Both generation tools poll inside one call, so a five-minute video occupies the tool slot for five minutes. Handing the wait to a background-job seam would fix this and is not what this plugin does.
- Results are URLs, not attachments. Assets stay at the provider and expire on its schedule. Nothing is admitted to the harness attachment store, so a later turn cannot look at a generated image — only at its link.
- Model ids pass straight through. An unknown one surfaces as a provider error rather than a refusal at the call.
- No cost reporting. A generation is billed by the provider and nothing here prices it, so it does not appear in a session's spend report.
Test
npm test
globalThis.fetch is stubbed; nothing reaches the real endpoint. What the tests pin is the reading of the provider format, that the token never appears in what a caller sees, and that the tools register and unregister with the plugin fiber. A live check needs a key and is a manual step.
License
MIT