将Supervisor共享核心身份与最终回复规则拆成独立Prompt section
由manifest组合interaction和final-reply system prompt
让background planning只注入核心身份并移除final-reply user context重复合同
补齐请求级单次注入断言、构建期组合校验和边界文档
description: Guide use of Genarrative's external editor/canvas OpenAPI. Use when a user describes a canvas/editor integration need and Codex must infer the right `/api/external/v1` API automatically, prepare canvas and asset-library context, abstract reusable art specs before generating assets, upload references, draft curl/HTTP/SDK requests, or set up and safely handle a Genarrative developer API Key.
description: Guide use of Genarrative's hosted external editor/canvas MCP or asynchronous `/api/external/v1` OpenAPI. Use when an Agent needs to discover the hosted integration, choose a canvas or asset operation, upload local reference media, create or update projects and asset-library records, submit and poll image/video/audio generation, interpret generated artifacts and warnings, draft HTTP/Python calls, or securely handle a Genarrative developer API Key.
---
# Genarrative External Editor API
Use the live OpenAPI contract as the source of truth:`GET https://www.genarrative.world/api/external/v1/openapi.json`. In this repository, the same contract is `docs/openapi/genarrative-external-v1.openapi.json`. If exact fields or enums matter, read the contract before emitting final code.
Discover the live integration through `GET https://www.genarrative.world/api/external/v1/agent-integration.json`. Treat`GET https://www.genarrative.world/api/external/v1/openapi.json` as the field-level source of truth. In this repository, the same contract is `docs/openapi/genarrative-external-v1.openapi.json`.
Prefer the bundled Python helper for runnable examples: `scripts/genarrative_external_api.py`. It uses only Python stdlib, reads the local JSON API Key file, fixes the production base URL, and wraps upload/confirm/generation routes.
Prefer the hosted Streamable HTTP MCP at `https://www.genarrative.world/api/external/v1/mcp` when the Agent supports remote MCP with a custom Bearer token. It exposes the External v1 operations as tools and the Skill documentation as resources; it does not require a local MCP server. Use this complete Skill package when remote MCP is unavailable or local-file upload needs client-side orchestration.
Prefer `scripts/genarrative_external_api.py` for runnable REST calls. It uses only Python stdlib, reads the local private API Key file, keeps the production base URL fixed, uploads local references, and wraps asynchronous submission, polling, and result retrieval.
## Workflow
1.At the start of a new conversation, ask the user for the canvas name before the first generation call unless an existing session is already provided. Create or use a project with that name and an asset-library folderwith the same name. Keep `canvasName`, `projectId`, `assetFolderId`, and the current art spec in conversation state.
2. Before any art asset generation, abstract the user's request into a reusable art spec. Ask only for missing spec fields required by the selected asset type. If a current spec already exists and the user does not request a new style/spec, reuse it automatically.
3.Classify the user's natural-language intent. Do not ask the user to choose an API:
- "生成/生图/做一张图" -> image generation
- "重绘/修改这张图" -> image edit
- "用这张参考图/基于本地图生成" -> upload local reference image, then generation or edit
- "上传本地素材" -> upload ticket, OSS form upload, object confirm
- "保存画板/更新布局" -> canvas save
- "读取私有素材" -> signed read URL
4. Ask only for missing inputs that affect the request body or an actually ambiguous route:
- credentials JSON path only if the user cannot use the default local path
- canvas name when no current canvas session exists; existing `projectId`, folder/resource IDs only when resuming a known project
- media type, prompt, references, dimensions, model, ratio, duration, and resolution
- whether referenced media is already uploaded as `objectKey` or still local
5. Every external generation must write to both the canvas and the asset library. Include `projectId`, `assetFolderId`, a display label, and `canvasCompletion` whenever the target endpoint supports them. For character animation, use the helper's two-step fallback: generate with `projectId` + `canvasCompletion`, then create a library asset from the first returned frame in the session folder.
6. If the user lacks an API Key, guide setup before request design.
7. Read `references/api-selection.md` before finalizing any request. Use the core table below for fast routing, then verify details in the reference.
8. Use `scripts/genarrative_external_api.py` when the user wants runnable Python, reference image upload, canvas/folder session setup, art-spec carrying, or a chain that should execute with fewer hand-written curl steps.
9. Keep to `/api/external/v1` unless the user explicitly asks for internal profile/admin APIs.
1.Discover the integration manifest. Choose hosted MCP when supported; otherwise use the helper or direct REST.
2. Before the first generation in a new conversation, obtain a canvas name unless an existing `projectId` and `assetFolderId` were supplied. Create or reuse a project and a same-name asset-library folder. Retain `canvasName`, `projectId`, `assetFolderId`, and the current art spec.
3.Normalize art requests into a reusable spec. Ask only for missing values that block the selected operation. Reuse the spec until the user changes its style, subject family, palette, format, or constraints.
4. Infer the operation from the user's intent. Do not ask the user to select an API unless two operations would produce materially different artifacts.
5. If a reference exists only as a local file, upload and confirm it first. Pass the stable returned `objectKey` to operations that accept object references; never substitute a temporary signed URL. For an icon-spritesheet primary spec, additionally create a project resource or asset record with `assetKind="icon-spec"`, then pass the returned resource or asset ID as `referenceId`.
6. For generation endpoints that support the fields, include `projectId`, `assetFolderId`, an asset label, and `canvasCompletion` so the result enters both the canvas and its same-name library folder.
7. Treat every generation POST as asynchronous. Send one stable `Idempotency-Key` per logical request, retain the returned `operationId`, and poll the returned `statusUrl` or `GET /api/external/v1/generations/{operationId}` according to `pollAfterMs`.
8. Consume `result` only after `status=completed`. On `failed`, surface the safe error. On a client timeout or lost response, retain the operation/key; do not create a replacement request.
9. Reload the normal project or asset-library read endpoint when the caller needs complete authoritative state. Generation results are intentionally compact.
10. Stay within `/api/external/v1`. Never call internal workers, queues, admin/profile APIs, or SpacetimeDB endpoints unless the user explicitly changes scope.
| Background music | `POST /api/external/v1/editor/audios/background-music/generations` | `gptDescriptionPrompt`, `makeInstrumental` |
- Authenticate MCP and business API calls with `Authorization: Bearer <tnr_sk_...>`. Never ask the user to paste a key into chat or place one in repository files.
- All eight generation POST routes require `Idempotency-Key` and return HTTP `202`; `202` is durable acceptance, not a media result.
- Retry an uncertain submission only with the exact same body and the same idempotency key. A polling timeout is not permission to generate again.
- Use stable references such as `objectKey`, project resource ID, or asset ID where each operation permits them. Image edit/redraw is stricter: `sourceReferenceId` accepts only a registered project resource ID or asset ID; upload confirmation alone is not enough. Use `/assets/read-url` only for temporary preview/download access.
- Preserve both warning channels after completion. A general`warning` can coexist with `sliceWarning`; do not discard either.
- Do not invent missing derivatives. A source-preserved warning means the main source remains usable but requested post-processing failed. A slice warning means the complete transparent sheet is usable but individual slices are absent.
- For successful `style="pixelArt"`, treat completed-result and nested resource/asset dimensions as the final logical-grid PNG dimensions. They may differ from `size`, `imageSize`, the provider image, and `canvasCompletion.placeholder`; do not rescale or reject the artifact to match those inputs.
- Keep generated artifacts in the canvas and asset library together. Character animation accepts `assetFolderId` and `assetLabel`; its completed result directly returns the final `assetKind="character-animation"` resource and asset with formal sequence fields. Do not create a duplicate first-frame record.
## Art Spec Interface
## Documentation Navigation
Maintain one current art spec per conversation. A compact spec is enough:
Read only the references needed for the task, but always verify exact schemas and enums against live OpenAPI:
-`references/capability-routing.md`: read before selecting an MCP tool or REST operation, creating a canvas session, or working in the AI game creator visual DAG.
-`references/api-operations.md`: read when constructing project, canvas, asset-library, upload, generation, or generation-status calls.
-`references/authentication-and-safety.md`: read before handling credentials, local files, OSS form upload, retries, private media, or logs.
-`references/requests-and-outputs.md`: read before building generation payloads, polling, interpreting compact results, applying canvas completion, or handling post-processing warnings.
For a first spec, infer fields from the user's words and ask only for missing fields that block the selected API. Examples: character animation needs source image/layer, dimensions, motion, ratio, frame count, and duration; UI extraction needs source design image plus target density; icon spritesheet needs reference image and icon descriptions. After a spec exists, reuse it for later assets unless the user changes style, subject family, palette, format, or constraints.
The hosted MCP exposes the same documents through:
The OpenAPI JSON endpoint is public; every other external endpoint requires the Bearer API Key.
The archive contains this main file, four one-level references, the Python helper, and `agents/openai.yaml`. Verify its SHA-256 against `agent-integration.json` before installing. Discovery, OpenAPI, and Skill downloads are public; MCP and business operations require authentication.
Use this fixed production base URL:
## Python Helper
```text
https://www.genarrative.world/
```
Guide the user to create a key from the logged-in product UI under `开发者 API Key`. The raw key is shown only once; never ask the user to paste it into chat. Tell them to store it in this local private JSON file, outside the repository:
```text
~/.config/genarrative/external-editor-api.json
```
Store the API Key outside the repository at `~/.config/genarrative/external-editor-api.json`:
```json
{
@@ -95,246 +73,47 @@ Guide the user to create a key from the logged-in product UI under `开发者 AP
}
```
Set the file readable only by the current user where possible: `chmod 600 ~/.config/genarrative/external-editor-api.json`. Do not use environment variables for this API.
Smoke test by reading the JSON file, without printing the key:
Use the helper directly from this skill path, or copy it into the caller's project. Do not change the fixed base URL or move the API Key into environment variables.
Helper convenience methods wait locally, but the server still uses short asynchronous submit/status requests. For durable caller-controlled orchestration, call `submit_generation`, persist its `operationId` and idempotency key, then call `get_generation` or `wait_for_generation`.
Generate an image and save it into both the canvas and the asset-library folder:
```json
{
"prompt":"一张横版幻想森林背景,适合游戏主视觉",
"kind":"spec",
"aspectRatio":"16:9",
"imageSize":"1K",
"projectId":"<projectId>",
"assetFolderId":"<assetFolderId>",
"assetLabel":"森林背景",
"generationInputs":{
"artSpec":{
"assetType":"background",
"style":"手绘游戏概念图"
}
},
"canvasCompletion":{
"title":"森林背景",
"placeholder":{
"x":0,
"y":0,
"width":1024,
"height":576,
"originalWidth":1024,
"originalHeight":576
}
}
}
```
Then call `POST /api/external/v1/editor/images/generations`.
For direct HTTP/curl, create or find the folder first with `GET /api/external/v1/editor/assets/library` and `POST /api/external/v1/editor/assets/folders`. The folder label should match the canvas name.
## Reference Images
When the user provides a local reference image path/file, upload it first; do not ask the user to convert it to base64.
After OSS form upload, confirm the object with `assetKind: "editor_reference_image"`. Put the returned `objectKey` into the generation request:
- image generation: `referenceImageSrcs`
- image edit/redraw: `sourceImageSrc`; extra references go in `referenceImageSrcs`
- icon spritesheet: `referenceImageSrc`
- UI asset extraction: `sourceImageSrc`; extra references go in `referenceImageSrcs`
- character animation: `sourceImageSrc`
- video generation image references: `referenceImageSrcs`
Use `signedUrl` only for display/download. For generation requests, use `objectKey`, project resource ID, asset ID, public URL, or Data URL as the endpoint allows; prefer uploaded `objectKey` for local/private reference images.
OSS form upload shape, using the ticket response saved as `ticket.json`. The default response has `upload`; if the caller explicitly requested the API response envelope, use `data.upload`:
throw new Error(`OSS upload failed: ${response.status} ${await response.text()}`);
}
})().catch((error) => {
console.error(error.message);
process.exit(1);
});
NODE
```
Then confirm with `contentLength`:
```json
{
"objectKey":"<ticket upload.objectKey>",
"contentType":"image/png",
"contentLength":12345,
"assetKind":"editor_reference_image",
"accessPolicy":"private"
}
```
`contentLength` is a JSON number from the local file byte size, not a quoted string.
For character animation from an uploaded local image, set:
```json
{
"sourceLayerId":"external-reference-hero",
"sourceImageSrc":"<uploaded objectKey>",
"sourceWidth":720,
"sourceHeight":1280,
"promptText":"让角色自然呼吸并轻微转身",
"resolution":"720p",
"ratio":"9:16",
"frameCount":40,
"durationSeconds":5,
"model":"seedance2.0-fast"
}
```
Use an existing canvas layer ID when the image came from a project layer. If it came only from a local upload, derive a stable synthetic `sourceLayerId` from the file name, for example `external-reference-hero`. Read `sourceWidth` and `sourceHeight` from the actual image before upload; ask the user only if the dimensions cannot be determined.
The helper uses a 420 second timeout for generation calls, including character animation and video. Direct HTTP clients should not use a 70 second request timeout for animation.
Character animation currently returns canvas completion data but not a direct `asset` payload. To keep the "canvas + asset library" invariant, call `client.animate_character(..., canvasSession=session, canvasTitle="...")`; the helper creates a library asset from the first returned frame in the session folder after the animation call succeeds.
For video generation, always include `mode: "std"`. When using image/video/audio references, default to `model: "seedance2.0-fast"` unless the user asks for another listed model, because reference media support is limited to the Seedance 2.0 family.
For image edit/redraw that should replace an existing canvas layer, pass `projectId` and `targetLayerId`. If the user instead gives an explicit `canvasCompletion`, let that placement win.
For sound effects and BGM, `assetFolderId` and `assetLabel` can write the generated audio to the account asset library, same as image/video generation.
## Successful Responses with Warnings
Character image generation (including character redraw through `kind: "character"`), icon spritesheet generation, and UI asset extraction can return HTTP 2xx with an optional structured `warning`. A 2xx response means the task completed, but it does not guarantee that every requested post-processed derivative exists.
- Apply the returned `project` and media snapshots before interpreting optional derivatives: character responses use `resource` / `asset`, while icon spritesheet and UI extraction responses use `spritesheetResource` / `spritesheetAsset`. When `warning.code` is `postprocess-failed-source-preserved`, the saved provider source image is the authoritative main result. Character output has no transparent derivative; icon spritesheet and UI extraction output have neither a transparent spritesheet nor slices. Display `warning.reason` directly, and do not synthesize missing derivatives or restart generation.
-`sliceWarning` is a separate condition used only when transparent spritesheet post-processing succeeded but automatic slicing failed. Keep `sliceWarning.reason` as the original diagnostic and continue using the complete transparent spritesheet; a UI may add context when displaying it, but must not rewrite the stored reason.
- The service contract keeps `warning` and `sliceWarning` mutually exclusive. As defensive handling for a malformed response containing both, treat the general `warning` as authoritative and do not misclassify the source-preserved result as a slicing-only warning.
For character animation, pass the canvas session and asset label to `animate_character`. The helper submits asynchronously and returns the completed compact result containing the authoritative formal `resource` and `asset`; do not synthesize a library asset from the first frame.
## Guardrails
- Do not invent endpoints outside the OpenAPI, especially internal worker or runtime task-list routes.
- Do not omit canvas/library context for generation. New generated assets should enter both the canvas and the asset-library folder named after the canvas.
- Do not put API Keys in repository files, generated project files, command history snippets with literal secrets, logs, docs, commits, or screenshots. The only default storage is the user's local private JSON credentials file.
- Do not use account JWT endpoints as the default external integration path. The profile API can create/revoke keys for logged-in product users, but it is not part of the external editor OpenAPI.
-When an endpoint returns `project`, `resource`, or `asset`, treat those as the authoritative updated project/resource/asset snapshots.
## Resources
-`references/api-selection.md`: intent routing and required-field cheat sheet.
-`scripts/genarrative_external_api.py`: stdlib Python helper for OpenAPI fetch, API Key loading, local reference upload, object confirm, project/canvas calls, and generation requests.
- Do not change the fixed production base URL in generated examples.
- Do not move the API Key into environment variables, source files, generated projects, logs, docs, screenshots, or shell snippets containing literal secrets.
- Do not treat a Data URL, Blob URL, expiring signed URL, worker lease, or provider diagnostic as a durable result.
- Do not reconstruct authoritative canvas, resource, or library snapshots from a compact generation response.
-Do not replace icon-spritesheet generation with ordinary image generation when the deliverable requires a reusable transparent atlas.
short_description:"Auto-route canvas API generation"
default_prompt:"Use $genarrative-external-editor-api to prepare a canvas session, infer the right API, and generate assets into the canvas and library."
default_prompt:"Use $genarrative-external-editor-api to discover the hosted integration, prepare a canvas session, and submit and poll asset generation into the canvas and library."
Use this reference after selecting a capability. Treat `GET /api/external/v1/openapi.json` as authoritative for exact request/response schemas, required fields, constraints, and operation IDs.
All paths below are relative to `https://www.genarrative.world`. Discovery and Skill download routes are public. Project, asset, upload, generation, and generation-query operations require the Bearer API Key.
Canvas save uses optimistic revision control. Pass the last authoritative `expectedRevision`; on conflict, reload instead of replaying a stale full layout.
Project listing supports two views:
-`view=full` is the REST default and returns the complete project, canvas, layers, and resources.
-`view=summary` returns only `projectId`, `title`, `updatedAt`, and nullable `cover`, so callers can display, search, disambiguate same-name projects, and select a safe target without loading every canvas snapshot.
- Hosted MCP `list_editor_projects` always uses `summary`; call `get_editor_project` after selecting a `projectId` when complete authoritative state is required.
-`cover` contains only `resourceId`, stable `objectKey`, dimensions, and `updatedAt`. It never embeds image bytes, a Data URL, or a signed URL. To display it, pass `cover.objectKey` to `get_external_asset_read_url`; signed URLs are temporary and must not be persisted or reused as generation references.
| Update asset record | `PATCH /api/external/v1/editor/assets/{assetId}` | `label` or `folderId` |
| Delete asset record | `DELETE /api/external/v1/editor/assets/{assetId}` | `assetId` |
Upload is a three-step client flow: create a ticket, POST the file and returned fields directly to the OSS form endpoint, then confirm the returned `objectKey`. See `authentication-and-safety.md` before implementing this flow.
## Generation Operations
Every generation row requires a stable `Idempotency-Key` header and returns HTTP `202` with an asynchronous submission, not the generated media.
| Capability | POST path | Required body fields | Common optional body fields |
Supply the `operationId` returned by submission. Poll no faster than `pollAfterMs` and retain the ID after a caller-side timeout.
## Canvas and Library Field Rules
- Pass `projectId` and `canvasCompletion` to write generated output into the canvas.
- Pass `assetFolderId` plus `assetLabel` for image, edit, icon spritesheet, video, sound effect, and BGM operations when supported.
- UI extraction uses `assetFolderId` and `spritesheetLabel`.
- Character animation accepts `assetFolderId` and `assetLabel`. Its completed compact result directly returns the final `assetKind="character-animation"` resource and asset with `imageSequenceFrames` and `imageSequenceDurationMs`; never create a duplicate first-frame resource or asset.
- If a caller must manually create a `character-animation` resource or asset, put the authoritative frames and total sequence duration in `imageSequenceFrames` and `imageSequenceDurationMs`. Keep `generationInputs` replayable: it must not contain legacy runtime fields such as `characterAnimation`, `frames`, `previewVideoPath`, `frameCount`, `fps`, or `durationSeconds`.
- Reload project/library state after completion when full current state is required.
## Reference Field Mapping
After confirming a local upload, pass its stable `objectKey` into operations that accept object references:
| Target capability | Field |
| --- | --- |
| Image generation | `referenceImageSrcs` |
| Image edit/redraw | `sourceReferenceId` must be a registered project resource ID or asset ID; additional references remain in `referenceImageSrcs` |
| Icon spritesheet | Register the primary spec as an `assetKind="icon-spec"` project resource or asset, then pass its returned ID as `referenceId`; additional style references remain in `referenceImageSrcs` |
| Video with image references | `referenceImageSrcs` |
For image edit/redraw, confirming an upload is not sufficient: create a project resource or asset-library record first, then pass that record's ID as `sourceReferenceId`. The main source never accepts objectKey, URL, Data URL, or Blob URL. Use video/audio reference arrays only with models that support them. Do not pass an expiring signed read URL as a generation reference.
The icon-spritesheet primary `referenceId` is intentionally stricter than ordinary image references: it accepts only a current-owner project resource ID or asset ID whose authoritative `assetKind` is `icon-spec`. It does not accept an `objectKey`, URL, Data URL, or Blob URL.
`sliceLayout: "grid-2x2"` is an opt-in contract for four fixed game-runtime assets. The provider prompt and server persistence both preserve the ordered slots left-top, right-top, left-bottom, right-bottom. Omit it to retain the default connected-component slicing behaviour for ordinary free-form icon sheets.
## Common Values
Use OpenAPI as the final authority; these common values are a routing aid:
- Image `kind`: `spec`, `character`, `quick-edit`, `ui-design`, `publication-material`; ordinary image generation may omit it.
- External v1 currently has no structured game-scene generation operation. Do not send `kind: "scene"` or `assetKind: "scene"` through generic image generation; the server rejects both before queueing.
- Video `model`: `seedance2.0`, `seedance2.0-fast`, `kling3.0`, `kling3.0-omni`, `veo3.1`, `veo3.1-fast`.
- Video `aspectRatio`: `16:9`, `9:16`, `1:1`, `4:3`, `3:4`, `21:9`.
- Video `resolution`: `480p`, `720p`, `1080p`; `mode`: `std`; `sound`: `on` or `off`.
- Character animation uses `model: "seedance2.0-fast"`; `resolution`: `480p` or `720p`; `frameCount`: `32`, `40`, or `48`; `durationSeconds`: `4`, `5`, or `6`; `ratio`: `same`, `1:1`, `4:3`, `16:9`, `9:16`, or `3:4`.
- Sound effect uses canonical model `eleven_text_to_sound_v2`; omit `duration` or send `null` for automatic duration, otherwise send a finite `0.5-30` number. `loop` defaults to `false` and remains independent from Prompt text.
- UI extraction uses `aspectRatio: "1:1"`; use `imageSize: "1K"` for normal/small extraction and `2K` for dense designs.
Do not hard-code this list as a replacement client schema. In particular, the top-level image `style` field is intentionally extensible; see `requests-and-outputs.md` for its fallback behavior.
- Default credentials file: `~/.config/genarrative/external-editor-api.json` with an `apiKey` string.
- Generation clients should allow long-running responses. Use at least 420 seconds for character animation and video; 70 seconds is too short for animation.
## Canvas Session and Art Spec
At the start of a new conversation, ask for a canvas name before the first generation call unless the user already supplied `projectId` and `assetFolderId`. Create or reuse:
1.`POST /api/external/v1/editor/projects` with `title` = canvas name.
2.`GET /api/external/v1/editor/assets/library`; if no folder has the same label, `POST /api/external/v1/editor/assets/folders` with `label` = canvas name.
3. Keep `canvasName`, `projectId`, `assetFolderId`, and the current art spec in conversation state.
Before generating art assets, normalize the user's request into a current art spec with `assetType`, `subject`, `style`, `palette`, `composition`, `format`, `constraints`, and `references`. Ask follow-up questions only for missing fields that block the selected endpoint. Reuse the current spec automatically when the user asks for another asset without changing style/spec requirements. Put the spec in `generationInputs.artSpec` and summarize it in the prompt when useful.
## Intent Routing
Infer the endpoint from the user's description. Do not present this as a menu unless the request is genuinely ambiguous.
Ask a follow-up only when two routes could both be correct and produce different artifacts, for example "处理这张图" without saying edit, extract UI assets, or use it as a reference for new generation.
| Generate character animation | `POST /api/external/v1/editor/character-animations/generations` | `sourceLayerId`, `sourceImageSrc`, `sourceWidth`, `sourceHeight`, `promptText`, `resolution`, `ratio`, `frameCount`, `durationSeconds`, `model` | `projectId`, `sourceResourceId`, `canvasCompletion`; then create a library asset from the first returned frame |
All generation requests should be placed into both the current canvas and its same-name asset-library folder. For endpoints that support `assetLabel`, pass it. For UI extraction, use `spritesheetLabel`. For icon spritesheet, the folder is enough. For character animation, the endpoint does not return `asset`; after success call `POST /api/external/v1/editor/assets` using the first returned frame as `imageSrc`, the session `assetFolderId`, and `assetKind: "character-animation"`.
## HTTP 2xx Warning Handling
Character image generation (including character redraw through `kind: "character"`), icon spritesheet generation, and UI asset extraction may return HTTP 2xx while carrying a structured `warning`; completion does not imply that all post-processed derivatives exist.
- Consume the returned `project` and media snapshots as authoritative: character responses use `resource` / `asset`, while icon spritesheet and UI extraction responses use `spritesheetResource` / `spritesheetAsset`. `warning.code: "postprocess-failed-source-preserved"` means the saved provider source is the main result. Character output has no transparent derivative, while icon spritesheet and UI extraction have no transparent spritesheet and no slices. Display `warning.reason` directly; do not construct missing assets or retry the provider generation from scratch.
-`sliceWarning` is only for a transparent spritesheet that was created successfully but could not be split automatically. Use the complete transparent spritesheet and preserve `sliceWarning.reason` as the original diagnostic; it is not a post-processing/source-preserved warning.
- The service contract keeps `warning` and `sliceWarning` mutually exclusive. If a malformed response contains both, prioritize the general `warning` over `sliceWarning` defensively.
## Reference Image Upload
If the user provides a local file as a reference image, run upload before the generation request:
Use `legacyPrefix: "generated-character-drafts"`, `pathSegments: ["editor", "external-editor-references"]`, original `fileName`, detected image `contentType`, and `access: "private"`.
2. Upload the file to the returned OSS form endpoint with all returned `formFields`.
3.`POST /api/external/v1/assets/objects/confirm` with returned `objectKey`, detected `contentType`, `contentLength` if known, `assetKind: "editor_reference_image"`, and `accessPolicy: "private"`.
4. Use the returned `objectKey` in the actual editor request.
OSS form upload uses `upload.host` and every non-null `upload.formFields` entry, then the file part named `file`. Default responses expose `upload`; envelope responses expose `data.upload`. Save the upload ticket response as `ticket.json`:
| Video generation with image references | `referenceImageSrcs` |
Do not put the signed read URL into generation fields. Signed URLs are for user-visible preview/download; generation fields should use the stable `objectKey` for uploaded private references.
-`contentLength` in object confirm is a JSON number from local byte size, not a string.
- For character animation, use an existing project layer ID as `sourceLayerId` when available.
- If the source is only an uploaded local image, derive `sourceLayerId` from the file name, such as `external-reference-hero`, and keep it stable across retries.
- Read `sourceWidth` and `sourceHeight` from the local image. If dimensions cannot be read, ask instead of inventing dimensions.
- UI design extraction uses fixed `aspectRatio: "1:1"`; choose `imageSize: "1K"` for normal/small extractions and `2K` for dense designs.
- Video image/video/audio references are supported only by the Seedance 2.0 family; default referenced-media video requests to `model: "seedance2.0-fast"`, `mode: "std"`, and explicit `sound`.
- Image edit/redraw can pass `targetLayerId` with `projectId` to replace an existing canvas layer when no explicit `canvasCompletion` is supplied.
- Image, edit, video, sound effect, and BGM generation can pass `assetFolderId` and `assetLabel`; response `asset` is the created/updated library record.
- Icon spritesheet and UI extraction can pass `assetFolderId`; UI extraction can also pass `spritesheetLabel`.
## Canvas Completion
Use `canvasCompletion` for generation in this skill so the generated result is written back into the project canvas by the backend.
Required:
```json
{
"title":"素材名称",
"placeholder":{
"x":0,
"y":0,
"width":512,
"height":512,
"originalWidth":512,
"originalHeight":512
}
}
```
`dialogId` is optional. If the response includes `project`, `resource`, or `asset`, use those snapshots instead of reconstructing canvas/resource/library state locally.
## Upload Flow
For a local file that should become a project resource or library asset:
1.`POST /api/external/v1/assets/direct-upload-tickets` with `legacyPrefix`, `fileName`, and optional `contentType`, `access`, `maxSizeBytes`.
2. Submit the file to the returned OSS form endpoint with returned `formFields`.
3.`POST /api/external/v1/assets/objects/confirm` with returned `objectKey` and an `assetKind`.
4. Create a project resource or library asset with the confirmed `assetObjectId`/`objectKey`.
For reading private/generated assets, call `GET /api/external/v1/assets/read-url?objectKey=...` and use the returned `signedUrl`.
- [Stable and Temporary Media References](#stable-and-temporary-media-references)
- [Logging and Command Safety](#logging-and-command-safety)
- [Scope and Retry Guardrails](#scope-and-retry-guardrails)
## API Key Setup
Authenticated calls use:
```text
Authorization: Bearer <tnr_sk_...>
```
Guide a logged-in user to create a key in the product UI under `开发者 API Key`. The raw key is shown only once. Never ask the user to paste it into chat.
Store it outside repositories in the user's private JSON file:
```text
~/.config/genarrative/external-editor-api.json
```
```json
{
"apiKey":"tnr_sk_..."
}
```
Set the file readable only by the current user where supported:
Do not use environment variables as the default API Key storage for this integration. Generated clients must load the JSON file, fail clearly when it is absent or malformed, and redact credentials from errors and logs.
The OpenAPI document, integration manifest, raw Skill entry, and Skill archive are public. Hosted MCP and all project, asset, upload, generation, and generation-query operations require the Bearer API Key.
## Idempotency and Unknown Outcomes
For each logical generation:
1. Create one printable ASCII `Idempotency-Key` of 1-128 bytes.
2. Persist the key with the exact request body and returned `operationId`.
3. If submission transport fails or the response is lost, resend only the exact same body with the same key.
4. Never allocate a new key merely because the outcome is unknown.
5. On a polling timeout, retain `operationId` and query later. Do not submit another generation.
Treat a different body under the same key as invalid. Do not automatically replay a failed terminal generation unless the user intentionally requests a new logical generation.
## Local Reference Upload
Do not ask the user to convert local files to base64. Upload from the Agent/client machine:
1. Detect the original filename, MIME type, byte length, and image dimensions when relevant.
2. Create a ticket with `POST /api/external/v1/assets/direct-upload-tickets`.
3. POST all returned non-null `formFields` and the file part named `file` directly to `upload.host`.
4. Confirm the object with `POST /api/external/v1/assets/objects/confirm`.
5. Pass the confirmed stable `objectKey` to the selected editor operation.
For a private reference image, use a ticket body shaped like:
The default response exposes `upload`; an explicitly enveloped response exposes `data.upload`. Treat the returned host and form fields as opaque. Do not log the entire ticket or persist it longer than needed.
Confirm with the actual file metadata:
```json
{
"objectKey":"<upload.objectKey>",
"contentType":"image/png",
"contentLength":12345,
"assetKind":"editor_reference_image",
"accessPolicy":"private"
}
```
`contentLength` is a JSON number in bytes, not a quoted string. Never invent `sourceWidth` or `sourceHeight`; read them from the local image or ask the user if they cannot be determined.
For character animation, reuse a real canvas layer ID when available. For a local-only source, derive a stable synthetic `sourceLayerId`, such as `external-reference-hero`, from the filename and keep it unchanged across retries.
The bundled helper implements ticket creation, a stdlib multipart upload, confirmation, dimension detection for common formats, and stable source-layer IDs:
Do not print the complete confirmation response if it may contain temporary access data. Prefer passing the returned `objectKey` directly to the next call.
## Stable and Temporary Media References
- Use `objectKey`, project resource ID, asset ID, or an allowed durable public URL for generation input.
- Use a Data URL only when the endpoint explicitly allows it and the caller has a deliberate reason; do not persist it as a durable output.
- Never use a Blob URL outside the browser process that created it.
- Use `GET /api/external/v1/assets/read-url` to obtain a short-lived `signedUrl` for display/download.
- Never store or feed an expiring signed URL back into generation when a stable `objectKey` exists.
## Logging and Command Safety
- Never place an API Key in repository files, generated projects, command arguments containing a literal key, docs, commits, screenshots, stack traces, test fixtures, or telemetry.
- Redact `Authorization`, API Key values, upload signatures, cookies, signed URL query strings, and private absolute paths from logs and user-visible errors.
- Do not print credentials while diagnosing JSON configuration. Report only presence/absence and safe validation errors.
- Do not commit the credentials file or copy it into the Skill archive.
- Do not expose provider diagnostics, worker leases, queue internals, or server filesystem paths returned by an unexpected error.
## Scope and Retry Guardrails
- Do not use account JWT/profile endpoints as the default external integration. Logged-in profile APIs may create/revoke developer keys, but they are outside this external editor contract.
- Do not call internal workers, queues, SpacetimeDB, or admin endpoints.
- Do not bypass upload confirmation or invent an object key.
- Do not retry post-processing locally by fabricating assets. Respect completed warning semantics from `requests-and-outputs.md`.
- Use bounded polling. A local wait budget ending does not cancel or fail the server operation.
Use this reference to translate user intent into a hosted MCP tool or its corresponding External v1 REST operation. Use `genarrative://external-editor/openapi` or `GET /api/external/v1/openapi.json` for exact schemas.
## Integration Surface
- Fixed production base URL: `https://www.genarrative.world/`.
- Hosted MCP: `/api/external/v1/mcp`, Streamable HTTP, authenticated with the same Bearer API Key as REST.
- Public contract: `GET /api/external/v1/openapi.json`.
- Skill fallback: `GET /api/external/v1/skill/SKILL.md` or `GET /api/external/v1/skill.zip`.
Prefer MCP when the Agent supports a remote endpoint plus a custom Bearer token. Prefer the complete Skill and Python helper when MCP is unavailable or a client-side local-file upload must be orchestrated. The MCP tool names are derived from OpenAPI `operationId` values in snake case; select by capability instead of memorizing the name.
## Canvas Session
Before the first generation in a new conversation, obtain a canvas name unless the user already supplied an existing `projectId` and `assetFolderId`.
1. List or create a project. When creating one, use the canvas name as `title`.
2. Read the asset library. Reuse a folder with the same label or create one with the canvas name.
3. Retain `canvasName`, `projectId`, `assetFolderId`, and the current art spec in conversation state.
Generated artifacts must enter both the current canvas and its same-name library folder whenever the endpoint supports that invariant. Pass `projectId`, `assetFolderId`, the endpoint's label field, and `canvasCompletion`. Character animation returns the final formal resource and asset directly; use those records and never create a duplicate from the first frame.
## Art Spec Routing
Before art generation, normalize the user's request into:
Infer what is already clear and ask only for missing fields that block the selected endpoint. Reuse the current spec unless the user changes style, subject family, palette, format, or constraints. Store structured context under `generationInputs.artSpec` where supported and summarize it in the prompt when useful.
## Intent Map
| User intent | MCP/REST capability |
| --- | --- |
| Generate a background, character, spec, UI mockup, or publication image | Image generation |
| Redraw, retouch, or replace an existing image | Image edit |
| Generate from a local reference | Upload and confirm the local file, then image generation or edit |
| Build a reusable transparent icon/game atlas from a visual spec | Icon spritesheet generation |
| Extract marked assets from an existing UI design | UI design asset extraction |
| Animate a character into frames | Character animation generation |
| Generate video | Video generation |
| Generate a sound effect | Sound-effect generation |
| Upload a local image/audio/video asset | Upload ticket -> OSS form upload -> object confirm |
| Save viewport/layers | Canvas save |
| Create, load, rename, or delete a canvas | Project operations |
| Organize folders and asset records | Asset-library operations |
| Obtain temporary access to private media | Signed read URL |
| Check generation progress or retrieve its result | Generation query |
Do not present an API menu unless the request is genuinely ambiguous. Ask a follow-up when two routes create different artifacts, for example “处理这张图” could mean edit, extract marked UI assets, or use it as a reference for a new generation.
## Route-Specific Decisions
- Use image edit when the requested output replaces or modifies a source image. With `projectId`, pass `targetLayerId` to replace an existing layer when no explicit `canvasCompletion` is supplied.
- Use icon spritesheet generation for a transparent reusable atlas when a stable visual-spec reference and concrete `iconDescriptions` exist. Do not use ordinary image generation just because it can draw several objects.
- Use UI extraction only for an existing UI design image with red-box annotations. It is not UI generation.
- Use a project layer ID as character animation `sourceLayerId` when one exists. For a local-only source, derive a stable synthetic ID from the filename.
- For video with image/video/audio references, use a Seedance 2.0-family model; default to `seedance2.0-fast`, `mode: "std"`, and explicit `sound`.
- Use `signedUrl` only for preview/download. Feed stable `objectKey` or registered resource/asset identifiers into generation.
## AI Game Creator Canonical Visual DAG
Keep the existing autonomous-build task graph. Do not add a parallel task system or collapse these artifacts into one ordinary generation request:
1.`art-director` generates `assets/art-spec.png` with image generation, `kind: "spec"`, then registers it as `assetKind: "icon-spec"`. This image is the authoritative visual spec; `generationInputs.artSpec` is supporting structured context.
2.`design-foundation` generates `assets/ui-prototype.png` with `kind: "ui-design"`, using the registered art-spec resource ID in `referenceImageSrcs`.
3.`art-asset-plan` generates transparent `assets/art-spritesheet.png` through icon spritesheet generation, using the same registered art-spec resource ID as `referenceId` plus concrete `iconDescriptions`. For the four-category game contract it must also send `sliceLayout: "grid-2x2"`; this is an explicit fixed-slot contract, not a client-side guessed crop.
For a playable Canvas game, do not stop at generation. Make `code-prototype` depend on `art-asset-plan` and consume the persisted `iconImageSrcs` slices for core players, blocks or targets, scene obstacles, and feedback. For the four-category game-chat contract, require response `sliceLayout: "grid-2x2"` and exactly four slices before registering the local runtime sheet; both fewer and extra components fail closed. Treat `art-spec.png` as reference-only. A full-sheet `<img>`, CSS background, path-only mention, guessed equal-grid crop, or code-drawn replacement for core entities is not runtime asset use. If slicing produces `sliceWarning`, keep the complete transparent sheet as a valid editor artifact, but fail the playable game asset gate until real slice files or verified atlas coordinates exist; never invent coordinates or replace the icon-spritesheet route with ordinary image generation.
Never use `assets/ui-prototype.png` as the spritesheet visual-spec reference. UI extraction is outside this canonical DAG.
## Scope Boundary
Stay within `/api/external/v1`. Do not invent worker, queue, runtime task-list, admin, profile, or SpacetimeDB calls. The only external generation query is `GET /api/external/v1/generations/{operationId}`.
Use this reference to build generation payloads, carry canvas/library context, poll asynchronous jobs, and interpret compact completed results. Verify exact schemas against `GET /api/external/v1/openapi.json`.
Persist the key, exact request body, and `operationId`. If submission outcome is uncertain, reuse the same body and key; do not submit a replacement key.
## Polling State Machine
Poll `statusUrl`, or `GET /api/external/v1/generations/{operationId}`, no faster than `pollAfterMs`:
-`queued` / `running`: retain `operationId`; show `phaseLabel`, `phaseDetail`, and `progress` when present; wait before querying again.
-`completed`: consume the compact `result` and all warning fields, then stop polling.
-`failed`: surface the safe `error`, stop polling, and do not infer provider or worker internals.
A caller-side timeout leaves the operation pending. Persist the ID for later query. Do not keep the original POST connection open and do not infer failure from a local wait budget.
The helper's convenience generation methods block only in the local process while sending short submit and status requests. Its default overall wait budget is 1800 seconds. For explicit orchestration:
`dialogId` is optional. The placeholder supplies canvas placement and completion coordinates; it is not a final media pixel-size constraint. For successful pixel-art snapping, the result layer uses the final logical-grid PNG dimensions even when they differ from the placeholder. Do not reconstruct canvas state from completion results. Reload the project and asset library when complete authoritative snapshots are needed.
Character animation accepts `assetFolderId` and `assetLabel` and persists the final transparent sequence directly. Its completed compact result includes the authoritative `assetKind="character-animation"` resource and asset with `imageSequenceFrames` and `imageSequenceDurationMs`. Use those records directly and never synthesize a duplicate asset from the first frame.
For the lower-level asset/resource creation endpoints, `generationInputs` is replayable request context rather than a media-runtime container. When `assetKind` is `character-animation`, the server rejects legacy runtime keys including `characterAnimation`, `frames`, `previewVideoPath`, `frameCount`, `fps`, and `durationSeconds`; send the formal sequence through `imageSequenceFrames` and `imageSequenceDurationMs`. Internal processing audit keys such as `screenColorHex`, `mattingProvider`, and `mattingModel` are removed before persistence.
## Art Spec and Image Request
Generic External v1 image generation does not expose the main-site structured game-scene contract. `kind: "scene"` and `assetKind: "scene"` are both invalid and return HTTP `400` before any generation job is queued. Do not replace the structured scene fields and server-owned prompt assembly with a generic image prompt.
Carry the current art spec in `generationInputs.artSpec` and reflect important constraints in the prompt:
```json
{
"prompt":"一张横版幻想森林背景,适合游戏主视觉,无文字",
"aspectRatio":"16:9",
"imageSize":"1K",
"projectId":"<projectId>",
"assetFolderId":"<assetFolderId>",
"assetLabel":"森林背景",
"generationInputs":{
"artSpec":{
"assetType":"background",
"subject":"幻想森林主视觉",
"style":"手绘游戏概念图",
"palette":"翡翠绿与金色光斑",
"composition":"横版,中心留出角色站位",
"format":"16:9, 1K",
"constraints":"无文字、无 UI 按钮",
"references":[]
}
},
"canvasCompletion":{
"title":"森林背景",
"placeholder":{
"x":0,
"y":0,
"width":1024,
"height":576,
"originalWidth":1024,
"originalHeight":576
}
}
}
```
The top-level `style` field is not the art spec's visual-style prose. It appends a short server-side clause to the prompt sent to the provider and enables deterministic post-processing:
- Omitted, `null`, empty string, or `"none"`: no clause is appended and no post-processing runs, without warning.
-`"pixelArt"`: append one short pixel-art line to the end of the prompt sent to the provider, and enable pixel-art snapping, for ordinary image generation, `kind: "character"`, and icon spritesheet generation. On successful snapping, each detected grid cell becomes one output pixel and the logical-grid PNG is persisted directly; it is not resized back to `size`, `imageSize`, the provider image, or `canvasCompletion.placeholder`. The line is appended, not substituted — the rest of your prompt is unchanged. For the exact per-kind wording, read the `style` field description in the OpenAPI document; it is the contract, and this guide deliberately does not copy it.
- Unknown strings, or `"pixelArt"` on unsupported kinds such as `spec`, `quick-edit`, `ui-design`, or `publication-material`: continue without style processing and return `warning.code: "unsupported-image-style"`.
Image edit/redraw has a stricter main-source identity rule. After upload confirmation, create either a project resource or an asset-library record and pass its `resourceId` or `assetId` as `sourceReferenceId`. Do not pass the uploaded objectKey as the main source; objectKey remains valid only for auxiliary `referenceImageSrcs` where the OpenAPI permits it.
Icon spritesheet generation has a stricter primary-spec contract. After upload confirmation, create a project resource or asset record with `assetKind: "icon-spec"`, retain its returned `resourceId` or `assetId`, and pass that ID as `referenceId`. The primary spec does not accept the uploaded `objectKey` directly; only additional style references may continue to use stable object keys in `referenceImageSrcs`.
For character animation from a local-only source, use actual dimensions and a stable synthetic layer ID:
```json
{
"sourceLayerId":"external-reference-hero",
"sourceImageSrc":"<confirmed objectKey>",
"sourceWidth":720,
"sourceHeight":1280,
"promptText":"让角色自然呼吸并轻微转身",
"resolution":"720p",
"ratio":"9:16",
"frameCount":40,
"durationSeconds":5,
"model":"seedance2.0-fast",
"assetFolderId":"<assetFolderId>",
"assetLabel":"角色呼吸动画"
}
```
Do not guess dimensions or pass a temporary signed read URL. See `authentication-and-safety.md` for upload and credential rules.
## Compact Completed Result
The completed `result` may contain stable artifact fields such as:
-`objectKey`, media type, dimensions, or task ID. For successful `pixelArt`, image `width`/`height`, icon `spritesheetWidth`/`spritesheetHeight`, and nested resource/asset dimensions are the actual final logical-grid PNG dimensions rather than requested, provider, or placeholder dimensions.
- Sound-effect `durationSeconds` is the probed MP3 duration and `loop` is the frozen request boolean; neither is inferred from Prompt text.
-`resource`, `resourceId`, or equivalent canvas reference.
-`asset`, `assetId`, or equivalent library reference.
-`spritesheetResource`, `spritesheetAsset`, and stable spritesheet metadata.
-`warning` and `sliceWarning` structures.
It deliberately excludes a complete project/canvas/library snapshot, Data URL, Blob URL, expiring signed URL, worker lease, queue state, and internal provider diagnostics. Use `/assets/read-url` for temporary access to a stable `objectKey`.
## Warning Semantics
Interpret warnings only after the query reaches `status=completed`. The query-level `warning` is display-ready text. Compact `result.warning` and `result.sliceWarning` preserve structured artifact semantics.
### Delivery-size normalization result before pixel-art snapping
`result.warning.code: "dimension-restore-fallback"` records only the delivery-size normalization result established before any subsequent `pixelArt` snapping: the provider image could not be safely normalized, so its dimensions were preserved at that processing boundary. It does not describe or constrain the dimensions after `pixelArt`; if snapping succeeds, use the completed result's actual logical-grid dimensions as authoritative.
### Source-preserved post-processing failure
When `result.warning.code` is `postprocess-failed-source-preserved`:
- Treat the saved provider source as the authoritative main result.
- For character output, do not claim a transparent derivative.
- For icon spritesheet or UI extraction, do not claim a transparent spritesheet or individual slices.
- Display the safe reason.
- Do not fabricate derivatives or restart generation automatically.
Use `resource` / `asset` for character results and `spritesheetResource` / `spritesheetAsset` for icon/UI results, then reload authoritative project/library state.
### Slice failure after transparent-sheet success
`result.sliceWarning` means transparent spritesheet post-processing succeeded but automatic splitting failed:
- Continue using the complete transparent spritesheet.
- Do not claim individual slices.
- Display the slice reason.
### Coexisting warnings
`warning` and `sliceWarning` are mutually exclusive only for `postprocess-failed-source-preserved`, because that path never reaches slicing. A general warning from unsupported style normalization or pixel-art snapping can coexist with `sliceWarning`. Render both reasons.
Before registering a requested transparent deliverable, verify the full sheet actually contains transparency. If source-preserved warning is present, do not register the opaque provider source as the requested transparent atlas. If only `sliceWarning` is present, the transparent full sheet remains valid.
## Output Handling Checklist
1. Require terminal `completed` before consuming artifacts.
2. Preserve stable IDs and `objectKey` values.
3. Surface all warning channels without downgrading completion to failure.
4. Avoid claiming absent transparent derivatives or slices.
5. Obtain temporary preview/download URLs only through `/assets/read-url`.
6. Reload authoritative project and library state when downstream logic needs complete records.
- 涉及 AI 游戏创作独立 App、多智能体 Runtime、本地项目产物或本地 HTTP 预览时,先读取 [`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`](docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md)。
- 新增、补齐、迁移或重构玩法入口、玩法类型、创作工作台、生成页、结果页、发布、运行态、作品架、广场或公开 read model 前,必须读取并按 [`genarrative-play-type-integration`](.codex/skills/genarrative-play-type-integration/SKILL.md) 执行。
- 涉及 `npm run dev` / `npm run dev:spacetime` / `npm run dev:api-server` / `npm run dev:web` / `npm run dev:admin-web` 的端口探测、端口漂移、SpacetimeDB publish server、api-server 环境变量、Vite 代理目标或后台 dev 端口时,按 [`.hermes/skills/genarrative-dev-stack-port-routing/SKILL.md`](.hermes/skills/genarrative-dev-stack-port-routing/SKILL.md) 执行。
@@ -127,6 +131,7 @@ export function AdminAccountsPage({
displayName: normalizedDisplayName,
...(password?{password}:{}),
tabPermissions,
actionPermissions,
enabled,
})
:awaitcreateAdminAccount(token,{
@@ -134,6 +139,7 @@ export function AdminAccountsPage({
displayName: normalizedDisplayName,
password,
tabPermissions,
actionPermissions,
enabled,
});
setAccounts((current)=>{
@@ -289,6 +295,28 @@ export function AdminAccountsPage({
</div>
</fieldset>
<fieldsetclassName="admin-permission-fieldset">
<legend>独立操作权限</legend>
<divclassName="admin-permission-grid">
<label>
<input
checked={actionPermissions.includes(
consumptionReconcilePermission,
)}
type="checkbox"
onChange={(event)=>
setActionPermissions(
event.target.checked
?[consumptionReconcilePermission]
:[],
)
}
/>
<span>手动对账用户历史花费</span>
</label>
</div>
</fieldset>
<button
className="admin-primary-button"
disabled={isSaving}
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.