@prismer/runtime 2.0.8 → 2.2.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/CHANGELOG.md +3430 -0
  2. package/README.md +34 -12
  3. package/apc/skills/FIELD-DICTIONARY.md +111 -0
  4. package/apc/skills/bug-reproduce/SKILL.md +150 -0
  5. package/apc/skills/bug-reproduce/skill.json +96 -0
  6. package/apc/skills/code-review/SKILL.md +198 -0
  7. package/apc/skills/code-review/skill.json +124 -0
  8. package/apc/skills/design-review/SKILL.md +122 -0
  9. package/apc/skills/design-review/skill.json +88 -0
  10. package/apc/skills/doc-sync/SKILL.md +168 -0
  11. package/apc/skills/doc-sync/skill.json +81 -0
  12. package/apc/skills/env-doctor/SKILL.md +194 -0
  13. package/apc/skills/env-doctor/skill.json +209 -0
  14. package/apc/skills/git-ops/SKILL.md +189 -0
  15. package/apc/skills/git-ops/skill.json +94 -0
  16. package/apc/skills/impact-trace/SKILL.md +168 -0
  17. package/apc/skills/impact-trace/skill.json +104 -0
  18. package/apc/skills/observability/SKILL.md +195 -0
  19. package/apc/skills/observability/skill.json +116 -0
  20. package/apc/skills/release-db-config-sync/SKILL.md +186 -0
  21. package/apc/skills/release-db-config-sync/skill.json +109 -0
  22. package/apc/skills/release-ota-promote/SKILL.md +195 -0
  23. package/apc/skills/release-ota-promote/skill.json +176 -0
  24. package/apc/skills/release-preflight/SKILL.md +174 -0
  25. package/apc/skills/release-preflight/skill.json +175 -0
  26. package/apc/skills/release-rollback/SKILL.md +214 -0
  27. package/apc/skills/release-rollback/skill.json +230 -0
  28. package/apc/skills/release-tag/SKILL.md +194 -0
  29. package/apc/skills/release-tag/skill.json +94 -0
  30. package/apc/skills/releasing-prod/SKILL.md +49 -0
  31. package/apc/skills/releasing-test/SKILL.md +135 -0
  32. package/apc/skills/sdk-release/SKILL.md +200 -0
  33. package/apc/skills/spec-intake/SKILL.md +169 -0
  34. package/apc/skills/spec-intake/skill.json +93 -0
  35. package/apc/skills/test-result-feedback/SKILL.md +239 -0
  36. package/apc/skills/test-result-feedback/skill.json +193 -0
  37. package/apc/skills/test-runner/SKILL.md +169 -0
  38. package/apc/skills/test-runner/skill.json +103 -0
  39. package/apc/skills/ui-align/SKILL.md +209 -0
  40. package/apc/skills/ui-align/skill.json +114 -0
  41. package/apc/skills/ui-canvas/SKILL.md +148 -0
  42. package/apc/skills/ui-canvas/skill.json +127 -0
  43. package/built-in-skills/agent-coordination/SKILL.md +59 -37
  44. package/built-in-skills/agent-meta/SKILL.md +1 -0
  45. package/built-in-skills/assets/SKILL.md +8 -6
  46. package/built-in-skills/browser-use/SKILL.md +93 -0
  47. package/built-in-skills/canvas-design/SKILL.md +1 -0
  48. package/built-in-skills/claim-agent-ownership/SKILL.md +3 -2
  49. package/built-in-skills/claude-api/SKILL.md +1 -0
  50. package/built-in-skills/codebase-design/DEEPENING.md +37 -0
  51. package/built-in-skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
  52. package/built-in-skills/codebase-design/LICENSE +21 -0
  53. package/built-in-skills/codebase-design/SKILL.md +116 -0
  54. package/built-in-skills/conversation-compaction/SKILL.md +114 -0
  55. package/built-in-skills/council-creator/SKILL.md +426 -0
  56. package/built-in-skills/diagnosing-bugs/LICENSE +21 -0
  57. package/built-in-skills/diagnosing-bugs/SKILL.md +136 -0
  58. package/built-in-skills/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
  59. package/built-in-skills/doc-coauthoring/SKILL.md +1 -0
  60. package/built-in-skills/document-generation/SKILL.md +105 -0
  61. package/built-in-skills/domain-modeling/ADR-FORMAT.md +47 -0
  62. package/built-in-skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
  63. package/built-in-skills/domain-modeling/LICENSE +21 -0
  64. package/built-in-skills/domain-modeling/SKILL.md +76 -0
  65. package/built-in-skills/frontend-design/SKILL.md +1 -0
  66. package/built-in-skills/human-approval/SKILL.md +17 -2
  67. package/built-in-skills/image-generate/SKILL.md +103 -302
  68. package/built-in-skills/image-generate/scripts/generate-and-deliver.mjs +289 -0
  69. package/built-in-skills/ingest/SKILL.md +13 -45
  70. package/built-in-skills/internal-comms/SKILL.md +1 -0
  71. package/built-in-skills/liteparse/SKILL.md +130 -110
  72. package/built-in-skills/mcp-builder/SKILL.md +1 -0
  73. package/built-in-skills/memory/SKILL.md +420 -55
  74. package/built-in-skills/memory-dream/SKILL.md +339 -0
  75. package/built-in-skills/office-artifacts/SKILL.md +17 -4
  76. package/built-in-skills/okr/SKILL.md +154 -0
  77. package/built-in-skills/persona/SKILL.md +81 -0
  78. package/built-in-skills/persona-generator/SKILL.md +296 -0
  79. package/built-in-skills/pkf-svg/SKILL.md +253 -0
  80. package/built-in-skills/pkf-writing/SKILL.md +236 -0
  81. package/built-in-skills/prismer-im-collab/SKILL.md +26 -6
  82. package/built-in-skills/proactivity/SKILL.md +84 -0
  83. package/built-in-skills/remotion/SKILL.md +431 -0
  84. package/built-in-skills/role-builder/SKILL.md +203 -0
  85. package/built-in-skills/role-builder/scripts/author-role.mjs +334 -0
  86. package/built-in-skills/role-builder/scripts/ingest-role.mjs +223 -0
  87. package/built-in-skills/role-builder/scripts/instantiate-and-run.mjs +290 -0
  88. package/built-in-skills/role-builder/scripts/operation-harness.mjs +267 -0
  89. package/built-in-skills/skill-authoring/SKILL.md +110 -100
  90. package/built-in-skills/skill-authoring/skill.json +3 -3
  91. package/built-in-skills/skill-builder/SKILL.md +171 -0
  92. package/built-in-skills/skill-builder/scripts/ingest.mjs +265 -0
  93. package/built-in-skills/skill-creator/SKILL.md +165 -423
  94. package/built-in-skills/skill-creator/references/external-library-import.md +110 -0
  95. package/built-in-skills/skill-creator/scripts/import-library.mjs +475 -0
  96. package/built-in-skills/slack-gif-creator/SKILL.md +20 -0
  97. package/built-in-skills/tasks/SKILL.md +38 -23
  98. package/built-in-skills/tdd/LICENSE +21 -0
  99. package/built-in-skills/tdd/SKILL.md +110 -0
  100. package/built-in-skills/tdd/mocking.md +59 -0
  101. package/built-in-skills/tdd/refactoring.md +10 -0
  102. package/built-in-skills/tdd/tests.md +61 -0
  103. package/built-in-skills/team/SKILL.md +2 -1
  104. package/built-in-skills/web-artifacts-builder/SKILL.md +1 -0
  105. package/built-in-skills/webapp-testing/SKILL.md +1 -0
  106. package/built-in-skills/wechat-pay/SKILL.md +59 -0
  107. package/dist/cli.cjs +71872 -19960
  108. package/dist/cli.js +71803 -19846
  109. package/dist/index.cjs +72010 -19966
  110. package/dist/index.d.cts +4258 -712
  111. package/dist/index.d.ts +4258 -712
  112. package/dist/index.js +72156 -20118
  113. package/package.json +37 -6
  114. package/plugins/memory/prismer/__init__.py +1211 -0
  115. package/plugins/memory/prismer/plugin.yaml +8 -0
  116. package/plugins/memory/prismer/tool-schemas.generated.json +249 -0
  117. package/plugins/tools/prismer-recall/__init__.py +282 -0
  118. package/plugins/tools/prismer-recall/plugin.yaml +15 -0
  119. package/built-in-skills/memory-curation/SKILL.md +0 -135
@@ -1,327 +1,128 @@
1
1
  ---
2
2
  name: image-generate
3
- description: Generate an image from a text prompt via the cloud LLM image proxy, persist it as a content-addressed workspace asset, and return a ContentBlock that downstream renderers can attach. Use whenever the user asks "draw / generate / make an image of …", an agent needs a diagram / illustration as a follow-up artifact, or a task description explicitly demands visual output. Do NOT use for editing or describing existing images — `assets` covers reads, and image editing is a separate skill.
3
+ scope: persistence
4
+ description: Generate one new image from a text prompt and deliver it once as the current Prismer reply attachment. Use for draw, generate-image, poster, diagram, illustration, or other text-to-image requests. Do not use for editing or merely describing an existing image.
4
5
  applies_to: [hermes, claude-code, openclaw, codex]
5
6
  requires:
6
7
  - assets
7
8
  phaseModel:
8
9
  defaultPhase: tool_use
9
- version: 1
10
+ version: 2
11
+ config:
12
+ - key: IMAGE_GEN_MODEL
13
+ type: string
14
+ required: false
15
+ default: null
16
+ bindable: [global, role, agent]
17
+ description: Preferred image-generation model id. The helper discovers the deployment model list and falls back by priority.
18
+ prompt:
19
+ zh: "生成图片时希望默认用哪个模型?"
20
+ en: "Which image model should be used by default?"
10
21
  ---
11
22
 
12
23
  # Image Generate
13
24
 
14
- Turn a **text prompt** into a **content-addressed image asset** plus a v2.0 §4.6
15
- ContentBlock that the chat renderer can surface inline. The skill is a thin
16
- orchestration over two existing surfaces:
25
+ Generate and deliver the requested image through the bundled helper. The helper
26
+ owns model discovery, fallback, byte decoding, hashing, file creation, and the
27
+ single `cloud deliver` call. Do not rebuild those steps in Python, curl, or a
28
+ temporary script.
17
29
 
18
- 1. **LLM image gateway** — the cloud's NewAPI proxy at
19
- `POST /api/v1/images/generations` (OpenAI-compatible — see §"Hand-off" if
20
- the endpoint is not yet wired in your deployment).
21
- 2. **Asset store** — `POST /api/im/assets` (multipart) on the cloud, returning
22
- a stable `assetId` + `contentHash`. Generated bytes flow through the
23
- content-addressed pipeline like any other asset — same de-dup, same audit
24
- trail, same URI scheme (`prismer://assets/<assetId>`).
30
+ ## Run
25
31
 
26
- The skill's **only** output contract is a ContentBlock referencing the asset.
27
- Raw bytes / `data:` URIs / pre-signed URLs MUST NOT be embedded in prose; doing
28
- so defeats caching and makes follow-up retrieval impossible (same rule as the
29
- `assets` skill).
30
-
31
- ## When to use
32
-
33
- - The user says "draw", "generate an image of …", "make me a picture / poster /
34
- diagram / illustration".
35
- - A task description includes a `produce_image:` field or a `kind: image`
36
- artifact expectation.
37
- - You need to **fabricate** a visual that does not exist in any source — if
38
- the visual already exists, use `assets` to read it, not this skill.
39
- - A downstream skill (`canvas-design`, `slack-gif-creator`,
40
- `web-artifacts-builder`) requires a generated source image as input.
41
-
42
- ## Not when to use
43
-
44
- - Editing / variation / inpainting an existing image — that's a separate
45
- upcoming skill (`image-edit`). Don't fake it by reading + regenerating.
46
- - Describing what's in an image — use a vision-capable adapter, no generation
47
- needed.
48
- - Pure ASCII / SVG / Mermaid graphics that the LLM can emit as text — those
49
- belong in the chat body, not in an asset.
50
- - Privacy-sensitive renderings (faces, identifiable individuals) without
51
- explicit user confirmation. The skill does not gate this; the agent must.
52
-
53
- ## API Reference
54
-
55
- There is **no `cloud image generate` subcommand** in the runtime CLI today
56
- (release 201 audit, `sdk/prismer-cloud/runtime/src/cli/commands/`). Call the
57
- cloud HTTP endpoint directly from a small Python / Node script in the skill
58
- runtime, then hand the bytes to `cloud asset upload` for the content-addressed
59
- write. Adding a dedicated CLI verb is tracked as a future release; until then,
60
- do **not** invent the command — it will exit with "unknown command".
61
-
62
- HTTP shape:
63
-
64
- ```http
65
- POST /api/v1/images/generations
66
- Authorization: Bearer <user JWT or sk-prismer-* key>
67
- Content-Type: application/json
68
-
69
- {
70
- "prompt": "<text prompt, 1..4000 chars>",
71
- "model": "gpt-image-1", // or "dall-e-3", deployment-dependent
72
- "size": "1024x1024", // 256x256 | 512x512 | 1024x1024 | 1792x1024 | 1024x1792
73
- "n": 1, // skill always uses 1 (return single ContentBlock)
74
- "response_format": "b64_json" // skill requires bytes — never "url"
75
- }
76
-
77
- Successful response (OpenAI shape):
78
- {
79
- "created": 1716345600,
80
- "data": [{ "b64_json": "<base64 PNG bytes>" }]
81
- }
82
- ```
83
-
84
- After receiving bytes the skill **MUST** upload to the cloud asset store
85
- (`POST /api/im/assets`, multipart) and use the returned `assetId` in the
86
- ContentBlock. Bytes never leak into chat.
87
-
88
- ## Workflow
89
-
90
- 1. **Validate inputs.** Prompt 1..4000 chars; size in the allow-list above;
91
- workspaceId resolved (defaulting to the active workspace if none supplied).
92
- 2. **Generate.** POST to `/api/v1/images/generations` with
93
- `response_format: 'b64_json'`. Capture `b64_json` (single image).
94
- 3. **Decode + hash.** Base64-decode to bytes, compute SHA-256 client-side, and
95
- compare against the upload response's `contentHash` field (server validates
96
- too — use `x-content-sha256` header).
97
- 4. **Upload as asset.** Multipart POST to `/api/im/assets`:
98
- - `file` — Blob with `image/png` MIME and filename
99
- `generated-${shortHash}.png`
100
- - `workspaceId`, `kind=image`, `description=<prompt[0..500]>`
101
- - `sourceTaskId` / `sourceAgentImUserId` (if available from runtime context)
102
- - `folderPath=/generated/images/${YYYY-MM}` (auto-organized, optional)
103
- 5. **Emit ContentBlock.** Return — and only return — a ContentBlock pointing
104
- at the new asset. Shape exactly as v2.0 §4.6 (Anthropic-shape, not OpenAI):
105
-
106
- ```json
107
- {
108
- "kind": "image",
109
- "assetId": "<returned assetId>",
110
- "mediaType": "image/png",
111
- "alt": "<prompt truncated to 100 chars>"
112
- }
113
- ```
114
-
115
- ## Operating Rules
116
-
117
- - **Always `response_format: b64_json`.** Never `url` — the OpenAI URL is
118
- short-lived, doesn't survive the content-address round-trip, and tempts you
119
- to leak it into chat (which defeats the asset model).
120
- - **One image per call.** `n=1` only. If the user wants variants, call the
121
- skill multiple times — each variant gets its own assetId so the user can
122
- pick + delete cleanly.
123
- - **Hash check is non-negotiable.** Server enforces `x-content-sha256`; if the
124
- hashes disagree, abort and surface the mismatch — the bytes were corrupted
125
- in flight.
126
- - **Never inline base64 / `data:<mime>;base64,...` in the reply body.** The
127
- whole point of the skill is to avoid that anti-pattern. If you find yourself
128
- about to do so, stop and check that the asset upload actually succeeded.
129
- - **Default size = 1024x1024** unless the user explicitly asks for portrait
130
- (1024x1792) or landscape (1792x1024). 256/512 only when budget is tight.
131
- - **Cost-aware:** image gen is far more expensive than a chat completion.
132
- Tell the user the model + size you picked before spending more than 1
133
- credit's worth, and surface the actual cost from the response.
134
-
135
- ## ContentBlock output (v2.0 §4.6 / Gap E-⑤)
136
-
137
- This skill produces a **single image ContentBlock** per successful generation.
138
- It MUST NOT also dump the base64 / pre-signed URL into the reply — that
139
- violates the §4.6 rule (asset-by-reference, not asset-by-value) and breaks the
140
- chat renderer's preview pipeline.
141
-
142
- ```json
143
- { "kind": "image", "assetId": "<assetId>", "mediaType": "image/png", "alt": "<prompt summary, ≤100 chars>" }
144
- ```
145
-
146
- The reply envelope from this skill (when invoked via the agent runtime) looks
147
- like:
148
-
149
- ```json
150
- {
151
- "ok": true,
152
- "result": {
153
- "assetId": "<assetId>",
154
- "contentHash": "<sha256 hex>",
155
- "sizeBytes": 123456,
156
- "cdnUrl": "<optional, server may include>",
157
- "model": "gpt-image-1",
158
- "size": "1024x1024",
159
- "promptHash": "<sha256 of prompt for de-dup>"
160
- },
161
- "contentBlocks": [
162
- { "kind": "image", "assetId": "<assetId>", "mediaType": "image/png", "alt": "<prompt[0..100]>" }
163
- ]
164
- }
165
- ```
166
-
167
- Legacy callers that only know how to parse `result.assetId` keep working
168
- (field preserved). Multimodal-aware callers prefer `contentBlocks[]` —
169
- adapters and the chat renderer both check `contentBlocks` first per the §4.6
170
- prefer-blocks rule.
171
-
172
- ## Failure modes
173
-
174
- | Status | Where | Cause | What to surface |
175
- |---|---|---|---|
176
- | 400 | LLM proxy | prompt too long / disallowed content | Prompt rejected; surface the proxy's error message verbatim. Do NOT retry the same prompt. |
177
- | 402 | LLM proxy | not enough credits | Tell the user the cost + ask them to top up. Don't burn credits on retries. |
178
- | 415 | asset upload | server rejected MIME (not in allow-list) | Should not happen — `image/png` is allow-listed. If it does, this is a deployment bug; flag it. |
179
- | 422 | asset upload | `x-content-sha256` mismatch | Bytes corrupted in flight. Retry once; if it persists, fail the skill and tell the user. |
180
- | 5xx | either | upstream outage | Retry with exponential backoff up to 2 times, then fail loudly. Do NOT fabricate the assetId. |
181
-
182
- ## Output reporting
183
-
184
- After successful generation:
32
+ From this skill directory, run:
185
33
 
34
+ ```bash
35
+ node scripts/generate-and-deliver.mjs \
36
+ --prompt '<complete generation prompt>' \
37
+ --size 1024x1024
186
38
  ```
187
- [image-generate] generated assetId=<id> model=<model> size=<WxH> sha=<short>
188
- cost=<credits>c prompt="<first 60 chars>…"
189
- ```
190
-
191
- Then in chat, return ONLY the ContentBlock (the renderer surfaces the image
192
- preview). One-line caption may accompany it if useful ("Here's the
193
- illustration you asked for.").
194
39
 
195
- After failure: report `status + proxy error code + message` verbatim. Do not
196
- silently retry on 4xx (those are the user's prompt / quota — they need to
197
- know).
198
-
199
- ## Backing capabilities (Gap E-⑤ mapping)
200
-
201
- - **LLM image gateway:** `POST /api/v1/images/generations` (NewAPI proxy
202
- wired in Wave 6 G1 at `src/app/api/images/generations/route.ts`; gateway
203
- reuses `proxyToNewAPI` in `src/lib/llm-proxy.ts` with image-specific
204
- billing via `calculateImageCredits`). Setting env `MOCK_LLM_IMAGES=true`
205
- bypasses NewAPI and returns a fixture 1×1 PNG (for integration tests
206
- that should not burn real image-gen credits).
207
- - **Asset store:** `POST /api/im/assets` (multipart) — `src/im/api/assets.ts`
208
- line 2729+. Returns `IMAsset` with `id`, `contentHash`, `cdnUrl`,
209
- `sizeBytes`.
210
- - **ContentBlock contract:** v2.0 §4.6 `sdk/prismer-cloud/typescript/src/types.ts`
211
- lines 278–296 (8-variant discriminated union, Anthropic-shape).
212
- - **Reply attachment plumbing:** `AgentDispatchReplyPayload.attachments` —
213
- same path as the `assets` skill's image-resolve output. Chat renderer
214
- surfaces previews from `attachments` / `contentBlocks` automatically.
40
+ Optional flags:
41
+
42
+ - `--model <id>` overrides `IMAGE_GEN_MODEL` for this call.
43
+ - `--output <path>` selects the local PNG/JPEG/WebP filename. Without it, the
44
+ helper writes a content-hashed file under `PRISMER_ARTIFACTS_DIR` (or cwd when
45
+ no dispatch artifacts directory is available).
46
+ - `--size` accepts `256x256`, `512x512`, `1024x1024`, `1792x1024`, or
47
+ `1024x1792`; the selected deployment model must advertise that size.
48
+
49
+ Defaults: square `1024x1024`; portrait `1024x1792` or landscape `1792x1024`
50
+ only when the user asks for that orientation. Generate one image per helper
51
+ invocation.
52
+
53
+ ## Delivery contract
54
+
55
+ The helper ends by running `cloud deliver <file> --json` exactly once and
56
+ consumes that machine output internally. Runtime turns the delivered asset into
57
+ the reply's structured attachment and the chat renderer shows the preview.
58
+
59
+ Read the helper's one-line status before replying:
60
+
61
+ - `[image-generate] delivered` means the asset was uploaded for this reply.
62
+ Reply with a short natural-language caption; model and size may be mentioned.
63
+ - `[image-generate] queued` means the bytes are durable locally but the cloud
64
+ upload is pending reconnection. Say it was generated and queued for upload;
65
+ do not claim it is already attached.
66
+ - `[image-generate] uploaded-unattached` means the cloud stored the image under
67
+ the run archive but no active reply dispatch existed. Say it was generated
68
+ and archived; do not claim it is attached.
69
+ - Do not paste the helper's JSON, assetId object, ContentBlock, base64, data URI,
70
+ signed URL, or local path into the message body.
71
+ - Do not run `cloud deliver`, `cloud file send`, `cloud task attach`, an asset
72
+ upload command, or a multipart request again for the same output.
73
+ - Do not manually construct a structured attachment. Runtime owns that wire
74
+ representation.
75
+
76
+ The generated file may also be observed by Runtime's dispatch-final artifact
77
+ scan. That scan and `cloud deliver` share the same run/task scope and
78
+ content-addressed dedup key; agents must not add another upload path.
79
+
80
+ ## Prompt handling
81
+
82
+ Use the user's requested subject, composition, style, lighting, camera angle,
83
+ palette, text, and exclusions. Expand a vague request only enough to make those
84
+ visual choices explicit; do not silently change the subject or intent.
85
+
86
+ Do not generate privacy-sensitive depictions of identifiable people without
87
+ the user's explicit request. For editing, variation, or inpainting of an
88
+ existing image, use an image-editing capability instead. For reading an
89
+ existing image, use the asset/vision path.
90
+
91
+ ## Helper behavior
92
+
93
+ The bundled script:
94
+
95
+ 1. Resolves `PRISMER_CLOUD_BASE` / `PRISMER_BASE_URL` and `PRISMER_API_KEY`,
96
+ falling back to the Prismer runtime config.
97
+ 2. Discovers available image models, filters them by the requested size before
98
+ generation, and honors a compatible `--model` or `IMAGE_GEN_MODEL` first.
99
+ 3. Retries the next model only for model-not-found, quota/rate-limit, or server
100
+ failures. Prompt rejection and insufficient credits stop immediately.
101
+ 4. Accepts either base64 image bytes or a short-lived image URL, validates the
102
+ resulting PNG/JPEG/WebP bytes, and writes one content-hashed file.
103
+ 5. Delivers that file once through the daemon-aware CLI path, distinguishing a
104
+ completed upload from a durable offline queue receipt.
105
+
106
+ If it fails, report its status/code/message and stop. Do not fabricate an
107
+ assetId, claim that delivery succeeded, or retry a rejected prompt unchanged.
215
108
 
216
109
  ## Examples
217
110
 
218
- ### Example 1 — User asks for an illustration
219
-
220
- ```
221
- User: "Draw an isometric server room with glowing blue racks"
222
-
223
- Skill flow:
224
- POST /api/v1/images/generations
225
- { prompt: "An isometric...", model: "gpt-image-1",
226
- size: "1024x1024", n: 1, response_format: "b64_json" }
227
- ← 200 { data: [{ b64_json: "<bytes>" }] }
228
-
229
- decode + sha256 → "8f4a..."
230
-
231
- POST /api/im/assets (multipart)
232
- file=generated-8f4a.png kind=image workspaceId=...
233
- x-content-sha256: 8f4a...
234
- ← 200 { data: { id: "asset_<...>", contentHash: "8f4a...",
235
- cdnUrl: "/api/im/assets/asset_<...>" } }
236
-
237
- Reply envelope:
238
- { ok: true,
239
- result: { assetId: "asset_<...>", contentHash: "8f4a...", ... },
240
- contentBlocks: [
241
- { kind: "image", assetId: "asset_<...>", mediaType: "image/png",
242
- alt: "An isometric server room with glowing blue racks" }
243
- ] }
244
- ```
245
-
246
- ### Example 2 — Task-pinned generation for kanban artifact
111
+ Square illustration:
247
112
 
113
+ ```bash
114
+ node scripts/generate-and-deliver.mjs \
115
+ --prompt 'Isometric server room, glowing blue racks, cinematic lighting' \
116
+ --size 1024x1024
248
117
  ```
249
- Task input: { produce_image: { prompt: "Logo: minimalist owl, monochrome",
250
- size: "1024x1024" } }
251
-
252
- Skill call: POST /api/v1/images/generations with the prompt, then
253
- `cloud asset upload generated.png --task-id "$PRISMER_TASK_ID"` so the
254
- kanban task review board surfaces the image inline (chat renderer reads
255
- contentBlocks from the asset attachment).
256
- ```
257
-
258
- ### Example 3 — Failure: prompt rejected
259
-
260
- ```
261
- User: "<disallowed content>"
262
- LLM proxy: 400 { error: { code: "content_policy_violation", ... } }
263
-
264
- Skill response:
265
- { ok: false,
266
- error: { code: "content_policy_violation",
267
- message: "<proxy's verbatim message>" } }
268
-
269
- Do NOT retry. Do NOT fabricate an assetId.
270
- ```
271
-
272
- ## Anti-patterns
273
-
274
- - ❌ Returning the raw base64 in `result.image_b64` for chat to render. The
275
- chat renderer expects ContentBlock referencing an asset; raw bytes bypass
276
- caching.
277
- - ❌ Using `response_format: 'url'` and pasting the OpenAI URL into the
278
- reply. URL expires; user clicks later → 404.
279
- - ❌ Skipping the asset upload "to save time" when the generated image is
280
- tiny. Tiny images still need stable IDs for follow-up retrieval and audit
281
- trail.
282
- - ❌ Calling the skill in a loop to "generate variants" — call once per
283
- variant with explicit prompt deltas. The de-dup hash will catch identical
284
- prompts.
285
- - ❌ Setting `n > 1`. The ContentBlock output shape is single-image; multi
286
- would force you to fabricate which one to attach.
287
-
288
- ## Hand-off — endpoint provisioning
289
-
290
- **Cloud-side endpoint wired in Wave 6 G1.** `POST /api/v1/images/generations`
291
- is now provisioned at `src/app/api/images/generations/route.ts` and uses
292
- the same `proxyToNewAPI` helper that backs `/api/chat/completions` +
293
- `/api/embeddings`. Image-specific billing lives in
294
- `src/lib/llm-pricing.ts::calculateImageCredits` (per-image USD pricing,
295
- no token semantics). Setting `MOCK_LLM_IMAGES=true` returns a fixture PNG
296
- without hitting NewAPI — used by F6 integration tests.
297
-
298
- Historical handler template (matches the landed implementation):
299
118
 
300
- ```ts
301
- // src/app/api/images/generations/route.ts (NEW)
302
- import { NextRequest, NextResponse } from 'next/server';
303
- import { apiGuard } from '@/lib/api-guard';
304
- import { checkRateLimit, rateLimitResponse } from '@/lib/rate-limit';
305
- import { FEATURE_FLAGS } from '@/lib/feature-flags';
306
- import { proxyToNewAPI } from '@/lib/llm-proxy';
307
- import { ensureNacosConfig } from '@/lib/nacos-config';
119
+ Portrait poster with an explicit model preference:
308
120
 
309
- export async function POST(request: NextRequest) {
310
- await ensureNacosConfig();
311
- if (!FEATURE_FLAGS.LLM_PROXY_ENABLED) {
312
- return NextResponse.json(
313
- { error: { message: 'LLM proxy is not enabled' } }, { status: 503 });
314
- }
315
- const guard = await apiGuard(request, { tier: 'tracked' });
316
- if (!guard.ok) return guard.response;
317
- const rl = checkRateLimit(guard.auth.userId, 'llm');
318
- if (!rl.allowed) return rateLimitResponse(rl);
319
- return proxyToNewAPI(request, guard, '/v1/images/generations');
320
- }
121
+ ```bash
122
+ node scripts/generate-and-deliver.mjs \
123
+ --prompt 'Minimalist monochrome owl poster, centered subject, clean negative space' \
124
+ --size 1024x1792 \
125
+ --model "$IMAGE_GEN_MODEL"
321
126
  ```
322
127
 
323
- Until this lands, the skill operates in **mock mode** — see
324
- `scripts/test-image-generate-skill.ts` which proves the
325
- generate→upload→ContentBlock chain by stubbing the LLM bytes (a 1x1 PNG) and
326
- hitting the real `/api/im/assets` upload, so the asset half of the contract
327
- is fully validated against production code paths.
128
+ Successful chat reply example: `图已生成并附在这条消息中。`