@duckmind/dm-windows-x64 0.60.6 → 0.60.8

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 (197) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +70 -123
  3. package/extensions/dm-9router-ext/src/index.js +410 -5
  4. package/extensions/dm-caveman/extensions/caveman.js +283 -12
  5. package/extensions/dm-cliproxy/index.js +182 -2
  6. package/extensions/dm-cliproxy/scripts/check-config-migration.js +66 -8
  7. package/extensions/dm-cliproxy/src/apply.js +228 -1
  8. package/extensions/dm-cliproxy/src/cache.js +42 -1
  9. package/extensions/dm-cliproxy/src/commands.js +50 -2
  10. package/extensions/dm-cliproxy/src/compat.js +81 -1
  11. package/extensions/dm-cliproxy/src/config.js +192 -2
  12. package/extensions/dm-cliproxy/src/conflicts.js +46 -1
  13. package/extensions/dm-cliproxy/src/fetch-models.js +190 -1
  14. package/extensions/dm-cliproxy/src/fetch-usage.js +41 -1
  15. package/extensions/dm-cliproxy/src/log.js +23 -1
  16. package/extensions/dm-cliproxy/src/status-quota.js +77 -1
  17. package/extensions/dm-cliproxy/src/ui-frame.js +50 -1
  18. package/extensions/dm-cliproxy/src/ui-hub/hub.js +199 -2
  19. package/extensions/dm-cliproxy/src/ui-hub/index.js +26 -2
  20. package/extensions/dm-cliproxy/src/ui-hub/shell.js +46 -1
  21. package/extensions/dm-cliproxy/src/ui-hub/view-diagnostics.js +69 -1
  22. package/extensions/dm-cliproxy/src/ui-hub/view-models.js +379 -1
  23. package/extensions/dm-cliproxy/src/ui-hub/view-usage.js +106 -1
  24. package/extensions/dm-cliproxy/src/ui-picker/catalog.js +41 -1
  25. package/extensions/dm-cliproxy/src/ui-picker/mutate.js +115 -1
  26. package/extensions/dm-cliproxy/src/ui-picker/prompt-confirm.js +35 -1
  27. package/extensions/dm-cliproxy/src/ui-picker/prompt-name.js +62 -1
  28. package/extensions/dm-cliproxy/src/ui-picker/providers.js +45 -1
  29. package/extensions/dm-cliproxy/src/ui-picker/render-text.js +34 -1
  30. package/extensions/dm-cliproxy/src/ui-picker/rows.js +57 -1
  31. package/extensions/dm-cliproxy/src/ui-setup.js +260 -2
  32. package/extensions/dm-cliproxy/src/ui-usage.js +151 -1
  33. package/extensions/dm-cliproxy/src/usage-shared-cache.js +97 -1
  34. package/extensions/dm-context/src/context.js +144 -1
  35. package/extensions/dm-context/src/index.js +339 -7
  36. package/extensions/dm-context/src/utils.js +9 -1
  37. package/extensions/dm-cua/bin/browser-cua.mjs +73 -8
  38. package/extensions/dm-cua/index.js +75 -6
  39. package/extensions/dm-cua/src/browser-cua-lib.mjs +490 -6
  40. package/extensions/dm-cua/src/browser-install.mjs +331 -2
  41. package/extensions/dm-fff/src/index.js +688 -12
  42. package/extensions/dm-fff/src/query.js +60 -1
  43. package/extensions/dm-goal/src/goal.js +894 -23
  44. package/extensions/dm-image2/index.js +103 -9
  45. package/extensions/dm-image2/src/image-lib.mjs +1275 -8
  46. package/extensions/dm-subagents/install.mjs +62 -8
  47. package/extensions/dm-subagents/src/agents/agent-management.js +1190 -36
  48. package/extensions/dm-subagents/src/agents/agent-memory.js +216 -6
  49. package/extensions/dm-subagents/src/agents/agent-scope.js +5 -1
  50. package/extensions/dm-subagents/src/agents/agent-selection.js +20 -1
  51. package/extensions/dm-subagents/src/agents/agent-serializer.js +120 -5
  52. package/extensions/dm-subagents/src/agents/agents.js +1139 -11
  53. package/extensions/dm-subagents/src/agents/chain-serializer.js +299 -11
  54. package/extensions/dm-subagents/src/agents/frontmatter.js +65 -6
  55. package/extensions/dm-subagents/src/agents/identity.js +29 -1
  56. package/extensions/dm-subagents/src/agents/proactive-skills.js +141 -1
  57. package/extensions/dm-subagents/src/agents/skills.js +614 -6
  58. package/extensions/dm-subagents/src/extension/config.js +35 -2
  59. package/extensions/dm-subagents/src/extension/control-notices.js +69 -4
  60. package/extensions/dm-subagents/src/extension/doctor.js +172 -15
  61. package/extensions/dm-subagents/src/extension/fanout-child.js +158 -231
  62. package/extensions/dm-subagents/src/extension/index.js +521 -359
  63. package/extensions/dm-subagents/src/extension/rpc.js +266 -7
  64. package/extensions/dm-subagents/src/extension/schemas.js +275 -1
  65. package/extensions/dm-subagents/src/extension/tool-description.js +111 -6
  66. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +126 -4
  67. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +452 -5
  68. package/extensions/dm-subagents/src/intercom/result-intercom.js +319 -3
  69. package/extensions/dm-subagents/src/profiles/profiles.js +458 -3
  70. package/extensions/dm-subagents/src/runs/background/async-execution.js +834 -40
  71. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +435 -14
  72. package/extensions/dm-subagents/src/runs/background/async-resume.js +334 -8
  73. package/extensions/dm-subagents/src/runs/background/async-status.js +313 -12
  74. package/extensions/dm-subagents/src/runs/background/chain-append.js +245 -2
  75. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +136 -1
  76. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +94 -1
  77. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +54 -1
  78. package/extensions/dm-subagents/src/runs/background/control-channel.js +190 -1
  79. package/extensions/dm-subagents/src/runs/background/fleet-view.js +483 -17
  80. package/extensions/dm-subagents/src/runs/background/notify.js +129 -3
  81. package/extensions/dm-subagents/src/runs/background/parallel-groups.js +34 -1
  82. package/extensions/dm-subagents/src/runs/background/result-watcher.js +236 -6
  83. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +76 -4
  84. package/extensions/dm-subagents/src/runs/background/run-status.js +427 -23
  85. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +487 -4
  86. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +306 -9
  87. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +2849 -73
  88. package/extensions/dm-subagents/src/runs/background/top-level-async.js +5 -1
  89. package/extensions/dm-subagents/src/runs/background/wait.js +206 -11
  90. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +1013 -12
  91. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +980 -101
  92. package/extensions/dm-subagents/src/runs/foreground/execution.js +1165 -45
  93. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +3157 -222
  94. package/extensions/dm-subagents/src/runs/shared/acceptance.js +835 -3
  95. package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +104 -1
  96. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +116 -3
  97. package/extensions/dm-subagents/src/runs/shared/dm-args.js +208 -1
  98. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +90 -1
  99. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +282 -1
  100. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +148 -1
  101. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +305 -1
  102. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +194 -1
  103. package/extensions/dm-subagents/src/runs/shared/model-scope.js +65 -1
  104. package/extensions/dm-subagents/src/runs/shared/nested-events.js +851 -8
  105. package/extensions/dm-subagents/src/runs/shared/nested-path.js +41 -1
  106. package/extensions/dm-subagents/src/runs/shared/nested-render.js +105 -1
  107. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +81 -4
  108. package/extensions/dm-subagents/src/runs/shared/run-history.js +51 -4
  109. package/extensions/dm-subagents/src/runs/shared/single-output.js +149 -8
  110. package/extensions/dm-subagents/src/runs/shared/structured-output.js +58 -1
  111. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +166 -5
  112. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +329 -13
  113. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +73 -1
  114. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +47 -4
  115. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +196 -1
  116. package/extensions/dm-subagents/src/runs/shared/worktree.js +435 -3
  117. package/extensions/dm-subagents/src/shared/artifacts.js +92 -2
  118. package/extensions/dm-subagents/src/shared/atomic-json.js +55 -1
  119. package/extensions/dm-subagents/src/shared/child-transcript.js +167 -5
  120. package/extensions/dm-subagents/src/shared/file-coalescer.js +25 -1
  121. package/extensions/dm-subagents/src/shared/fork-context.js +147 -4
  122. package/extensions/dm-subagents/src/shared/formatters.js +98 -7
  123. package/extensions/dm-subagents/src/shared/jsonl-writer.js +56 -2
  124. package/extensions/dm-subagents/src/shared/model-info.js +62 -1
  125. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +68 -1
  126. package/extensions/dm-subagents/src/shared/session-identity.js +6 -1
  127. package/extensions/dm-subagents/src/shared/session-tokens.js +39 -2
  128. package/extensions/dm-subagents/src/shared/settings.js +198 -11
  129. package/extensions/dm-subagents/src/shared/status-format.js +53 -1
  130. package/extensions/dm-subagents/src/shared/types.js +184 -6
  131. package/extensions/dm-subagents/src/shared/utils.js +462 -2
  132. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +288 -1
  133. package/extensions/dm-subagents/src/slash/prompt-workflows.js +297 -7
  134. package/extensions/dm-subagents/src/slash/slash-bridge.js +118 -1
  135. package/extensions/dm-subagents/src/slash/slash-commands.js +1287 -31
  136. package/extensions/dm-subagents/src/slash/slash-live-state.js +240 -4
  137. package/extensions/dm-subagents/src/tui/render-helpers.js +64 -1
  138. package/extensions/dm-subagents/src/tui/render.js +1542 -4
  139. package/extensions/dm-usage/index.js +1294 -9
  140. package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +40 -9
  141. package/extensions/greedysearch-dm/bin/cdp-headless.mjs +5 -2
  142. package/extensions/greedysearch-dm/bin/cdp-visible.mjs +5 -2
  143. package/extensions/greedysearch-dm/bin/cdp.mjs +896 -30
  144. package/extensions/greedysearch-dm/bin/gschrome.mjs +30 -2
  145. package/extensions/greedysearch-dm/bin/kill-visible.mjs +7 -2
  146. package/extensions/greedysearch-dm/bin/launch-visible.mjs +13 -2
  147. package/extensions/greedysearch-dm/bin/launch.mjs +282 -10
  148. package/extensions/greedysearch-dm/bin/mcp.mjs +386 -361
  149. package/extensions/greedysearch-dm/bin/search.mjs +620 -540
  150. package/extensions/greedysearch-dm/bin/visible.mjs +22 -2
  151. package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +329 -579
  152. package/extensions/greedysearch-dm/extractors/chatgpt.mjs +301 -583
  153. package/extensions/greedysearch-dm/extractors/common.mjs +408 -32
  154. package/extensions/greedysearch-dm/extractors/consensus.mjs +376 -365
  155. package/extensions/greedysearch-dm/extractors/consent.mjs +303 -14
  156. package/extensions/greedysearch-dm/extractors/gemini.mjs +228 -592
  157. package/extensions/greedysearch-dm/extractors/google-ai.mjs +78 -499
  158. package/extensions/greedysearch-dm/extractors/logically.mjs +270 -347
  159. package/extensions/greedysearch-dm/extractors/perplexity.mjs +243 -581
  160. package/extensions/greedysearch-dm/extractors/selectors.mjs +32 -1
  161. package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +130 -317
  162. package/extensions/greedysearch-dm/index.js +123 -23
  163. package/extensions/greedysearch-dm/src/fetcher.mjs +576 -2
  164. package/extensions/greedysearch-dm/src/formatters/results.js +95 -10
  165. package/extensions/greedysearch-dm/src/formatters/sources.js +57 -1
  166. package/extensions/greedysearch-dm/src/formatters/synthesis.js +49 -1
  167. package/extensions/greedysearch-dm/src/github.mjs +222 -7
  168. package/extensions/greedysearch-dm/src/reddit.mjs +145 -14
  169. package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +340 -9
  170. package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +112 -4
  171. package/extensions/greedysearch-dm/src/search/chrome.mjs +486 -285
  172. package/extensions/greedysearch-dm/src/search/constants.mjs +109 -8
  173. package/extensions/greedysearch-dm/src/search/defaults.mjs +10 -1
  174. package/extensions/greedysearch-dm/src/search/engines.mjs +79 -9
  175. package/extensions/greedysearch-dm/src/search/fetch-source.mjs +441 -349
  176. package/extensions/greedysearch-dm/src/search/file-sources.mjs +29 -7
  177. package/extensions/greedysearch-dm/src/search/minimize.mjs +86 -1
  178. package/extensions/greedysearch-dm/src/search/output.mjs +51 -5
  179. package/extensions/greedysearch-dm/src/search/paths.mjs +48 -1
  180. package/extensions/greedysearch-dm/src/search/pdf.mjs +63 -2
  181. package/extensions/greedysearch-dm/src/search/port-pid.mjs +69 -1
  182. package/extensions/greedysearch-dm/src/search/progress.mjs +109 -2
  183. package/extensions/greedysearch-dm/src/search/query.mjs +21 -1
  184. package/extensions/greedysearch-dm/src/search/recovery.mjs +49 -1
  185. package/extensions/greedysearch-dm/src/search/research.mjs +2227 -458
  186. package/extensions/greedysearch-dm/src/search/scale-aware.mjs +61 -11
  187. package/extensions/greedysearch-dm/src/search/simple-research.mjs +396 -805
  188. package/extensions/greedysearch-dm/src/search/sources.mjs +412 -1
  189. package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +127 -12
  190. package/extensions/greedysearch-dm/src/search/synthesis.mjs +202 -12
  191. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +209 -23
  192. package/extensions/greedysearch-dm/src/tools/shared.js +226 -10
  193. package/extensions/greedysearch-dm/src/utils/content.mjs +35 -4
  194. package/extensions/greedysearch-dm/src/utils/helpers.js +22 -1
  195. package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +10 -1
  196. package/extensions/greedysearch-dm/src/utils/system-cmds.mjs +61 -1
  197. package/package.json +1 -1
@@ -1,9 +1,103 @@
1
- import{existsSync as X$}from"node:fs";import{join as f$}from"node:path";import{Type as D}from"@sinclair/typebox";import{getAgentDir as H1}from"@duckmind/dm-coding-agent";import{randomUUID as i}from"node:crypto";import{existsSync as w,mkdirSync as x,readFileSync as k,unlinkSync as J$,writeFileSync as o}from"node:fs";import{dirname as A,join as N,resolve as S}from"node:path";import{spawn as Q$,spawnSync as z$}from"node:child_process";import{homedir as u}from"node:os";import{getAgentDir as r}from"@duckmind/dm-coding-agent";var V$="https://chatgpt.com/backend-api/codex/responses",O$="https://openrouter.ai/api/v1/chat/completions",P="1:1",U$="png",v=900000,B$="DM_IMAGE_TEST_DELAY_MS",s="DM_IMAGE_ALLOW_LEGACY_FALLBACK",I="@preset/image2",_$=I,R="gpt-5.5",q$=new Set(["image2",I,"duckmind/image2","duckmind/@preset/image2","openrouter/image2","openrouter/@preset/image2"]),N$=new Set([R,`openai-codex/${R}`,`codex/${R}`]),F={"1:1":{width:1024,height:1024},"4:5":{width:1024,height:1280},"16:9":{width:1600,height:900}},a=S(A(new URL(import.meta.url).pathname),"..","vendor","imagen"),y=N(a,"imagen.py");function D$($){return String($??"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,48)||"image"}function g($){return typeof $==="string"&&$ in F?$:P}function h($){return $==="svg"?"svg":U$}function t($){let j=String($??"").trim();if(N$.has(j))return R;return q$.has(j)?I:_$}function M$($){return t($)===I}function L$($=process.env[s]){return typeof $==="string"&&/^(1|true|yes|on)$/i.test($.trim())}function C($="Request was aborted"){let j=Error($);return j.name="AbortError",j}function b($){if(!$)return!1;if($ instanceof Error&&$.name==="AbortError")return!0;let j=$ instanceof Error?$.message:String($);return j==="Request was aborted"||j==="This operation was aborted"}function E($){if(!$?.aborted)return;if($.reason instanceof Error)throw $.reason;throw C()}function S$($,j=v){let Y=new AbortController,Z=setTimeout(()=>Y.abort(C(`Image request timed out after ${Math.round(j/1000)}s`)),j),G=()=>{Y.abort($?.reason instanceof Error?$.reason:C())};if($?.aborted)G();else if($)$.addEventListener("abort",G,{once:!0});return{signal:Y.signal,cleanup(){clearTimeout(Z),$?.removeEventListener("abort",G)}}}function T$($,j){if(!Number.isFinite($)||$<=0)return Promise.resolve();return E(j),new Promise((Y,Z)=>{let G=setTimeout(()=>{j?.removeEventListener("abort",H),Y()},$),H=()=>{clearTimeout(G),j?.removeEventListener("abort",H),Z(C())};j?.addEventListener("abort",H,{once:!0})})}function b$(){let $=String(process.env[B$]??"").trim();if(!$)return 0;let j=Number.parseInt($,10);if(!Number.isFinite(j)||j<=0)return 0;return Math.min(j,300000)}function e($){return N(r(),$)}function p($){try{return JSON.parse(k($,"utf-8"))}catch{return}}function w$($){if(!$||typeof $!=="object")return;let j=Object.keys($).filter((H)=>H==="openai-codex"||/^openai-codex-account-\d+$/.test(H)).sort((H,W)=>{if(H==="openai-codex")return-1;if(W==="openai-codex")return 1;return H.localeCompare(W,void 0,{numeric:!0})}).find((H)=>{let W=$[H];return W&&typeof W==="object"&&!Array.isArray(W)}),Y=j?$[j]:void 0;if(!Y||typeof Y!=="object"||Array.isArray(Y))return;let Z=typeof Y.access==="string"?Y.access:void 0,G=typeof Y.accountId==="string"?Y.accountId:typeof Y.account_id==="string"?Y.account_id:void 0;if(!Z||!G)return;return{accessToken:Z,accountId:G,email:typeof Y.email==="string"?Y.email:void 0}}function k$($){try{let j=String($??"").split(".");if(j.length!==3)return;let Z=JSON.parse(Buffer.from(j[1],"base64url").toString("utf8"))?.["https://api.openai.com/auth"]?.chatgpt_account_id;return typeof Z==="string"&&Z.trim()?Z.trim():void 0}catch{return}}function d($,j){if(!$||typeof $!=="object")return;let Y=j.toLowerCase();for(let[Z,G]of Object.entries($)){if(String(Z).toLowerCase()!==Y)continue;if(typeof G==="string"&&G.trim())return G.trim()}return}function F$($){let j=$?.modelRegistry,Y=[],Z=new Set,G=(H)=>{if(!H||typeof H!=="object")return;if(H.provider!=="openai-codex")return;let W=`${H.provider}/${H.id}`;if(Z.has(W))return;Z.add(W),Y.push(H)};if(G($?.model),j&&typeof j.find==="function")G(j.find("openai-codex",R));if(j&&typeof j.getAvailable==="function")for(let H of j.getAvailable())G(H);return Y}async function C$($){let j=$?.modelRegistry;if(!j||typeof j.getApiKeyAndHeaders!=="function")return;for(let Y of F$($)){let Z=await j.getApiKeyAndHeaders(Y);if(!Z?.ok||!Z.apiKey)continue;let G=k$(Z.apiKey)||d(Z.headers,"ChatGPT-Account-Id")||d(Z.headers,"chatgpt-account-id");if(!G)continue;return{accessToken:Z.apiKey,accountId:G,email:void 0,source:`model-registry:${Y.provider}/${Y.id}`}}return}function $$(){let $=p(e("auth.json")),j=w$($);if(j)return{...j,source:"auth.json"};throw Error("Missing active Codex credentials. Ensure ~/.dm/agent/auth.json contains an openai-codex or openai-codex-account-N account.")}function E$(){return["You are a focused image-generation assistant.","Finish by calling image_generation for the final answer.","Do not stop at plain text when the user asked for an image.","Follow the requested visual direction, aspect ratio, and subject details closely."].join(" ")}function R$(){return["You are a vector illustrator.","Return exactly one standalone SVG image and no prose.","Do not wrap the SVG in markdown fences.","Do not return a data URI unless absolutely necessary.","Do not mention limitations, policies, or explanations in the answer."].join(" ")}function I$($,j){let Y=F[j]??F[P];return[`Create one polished self-contained SVG illustration for this brief: ${$}`,`Use a ${j} aspect ratio with a ${Y.width}x${Y.height} canvas and a matching viewBox.`,"Return only raw <svg>...</svg> markup.","Use simple shapes, gradients, paths, and fills; avoid external fonts, scripts, or remote URLs.","If the request is a portrait, make it respectful, stylized, and presentation-ready."].join(`
2
- `)}function x$($,j){let Y=F[j]??F[P];return[$,`Output in exactly ${Y.width}px x ${Y.height}px (${j}).`,"Prefer production-ready composition, grounded local detail when relevant, and the strongest final image_generation call."].join(`
3
- `)}function j$($,j){let Y=F[j]??F[P];return[$,`Target aspect ratio: ${j}.`,`Compose for roughly ${Y.width}x${Y.height} output.`,"Generate the final answer by calling the image_generation tool.","If the prompt is a portrait or public person, keep it respectful and presentation-ready."].join(`
4
- `)}function A$($){let j=String($??"").trim();if(!j)throw Error("Codex returned an empty image response.");let Y=j.match(/!\[[^\]]*]\((data:image\/svg\+xml[^)]+)\)/i);if(Y?.[1]){let X=Y[1],f=X.indexOf(",");if(f!==-1)return decodeURIComponent(X.slice(f+1)).trim()}let Z=j.match(/data:image\/svg\+xml[^,]*,(.+)$/is);if(Z?.[1])return decodeURIComponent(Z[1]).trim();let H=j.match(/```(?:svg)?\s*([\s\S]*?)```/i)?.[1]?.trim();if(H?.startsWith("<svg"))return H;let W=j.match(/<svg[\s\S]*<\/svg>/i);if(W?.[0])return W[0].trim();throw Error(`Expected SVG markup from Codex, received: ${j.slice(0,240)}`)}async function l($,j,Y){let Z=S$(Y,v),G;try{G=await fetch(V$,{method:"POST",signal:Z.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${j.accessToken}`,Accept:"text/event-stream","Cache-Control":"no-cache",originator:"Codex Desktop","User-Agent":"Codex Desktop/26.212.1823 (darwin; arm64)","ChatGPT-Account-Id":j.accountId},body:JSON.stringify($)})}catch(K){if(Z.cleanup(),b(K)||Z.signal.aborted)throw C();throw K}if(!G.ok){Z.cleanup();let K=await G.text().catch(()=>"");throw Error(`Codex image request failed: HTTP ${G.status} ${K.slice(0,240)}`)}if(!G.body)throw Z.cleanup(),Error("Codex image request returned no response body.");let H=new TextDecoder,W=G.body.getReader(),X="",f=null,V=[],O="",z,B=[],U=()=>{if(!f||V.length===0)return f=null,V=[],!1;let K=JSON.parse(V.join(`
5
- `));if(f==="response.output_text.delta"&&typeof K.delta==="string")O+=K.delta;if(f==="response.output_item.done"&&K.item&&typeof K.item==="object")B.push(K.item);if(f==="response.completed"||f==="response.done")return z=K.response?.id??z,f=null,V=[],!0;if(f==="response.failed"||f==="response.incomplete"){let Q=K.response?.error?.message||K.response?.incomplete_details?.reason||"Codex image request did not complete.";throw Error(String(Q))}return f=null,V=[],!1};try{while(!0){E(Z.signal);let{value:K,done:Q}=await W.read();X+=H.decode(K??new Uint8Array,{stream:!Q});while(!0){let _=X.indexOf(`
6
- `);if(_===-1)break;let J=X.slice(0,_);if(X=X.slice(_+1),J.endsWith("\r"))J=J.slice(0,-1);if(!J){if(U())return{assistantText:O.trim(),outputItems:B,responseId:z};continue}if(J.startsWith("event: "))f=J.slice(7).trim();else if(J.startsWith("data: "))V.push(J.slice(6))}if(Q)break}if(V.length>0)U();return{assistantText:O.trim(),outputItems:B,responseId:z}}catch(K){if(await W.cancel().catch(()=>{}),b(K)||Z.signal.aborted)throw C();throw K}finally{Z.cleanup(),W.releaseLock()}}function P$($,j,Y){switch(j){case"global":return S(N(r(),"generated-images"));case"custom":if(!Y||!String(Y).trim())throw Error("save=custom requires saveDir.");return S(String(Y).trim());case"project":default:return S($,".dm","generated-images")}}function y$($,j,Y,Z){let G=Z||`run-${i().slice(0,8)}`;return S("/tmp","dm-imagen-runs",G)}function m($,j){x(A($),{recursive:!0}),o($,j,"utf-8")}function c($,j){x(A($),{recursive:!0}),o($,j)}function v$($,j){let Y=z$("sips",["-s","format","png",$,"--out",j],{encoding:"utf-8"});if(Y.status===0&&w(j))return;let Z=(Y.stderr||Y.stdout||`exit ${Y.status??"unknown"}`).trim();throw Error(`Failed to convert SVG to PNG via sips: ${Z}`)}function Y$($,j="png"){let Y=String($??"").trim();if(!Y)throw Error("image_generation.result is empty");let Z=Y,G="";if(Y.startsWith("data:")){let X=Y.indexOf(",");if(X===-1)throw Error("image_generation.result data URL is malformed");let f=Y.slice(5,X);G=String(f.split(";",1)[0]??"").trim(),Z=Y.slice(X+1)}let H=Buffer.from(Z,"base64");if(!H.length)throw Error("image_generation.result decoded to empty bytes");let W=String(j||"png").trim().toLowerCase().replace(/^\./,"")||"png";if(G==="image/jpeg")W="jpg";else if(G==="image/webp")W="webp";else if(G==="image/gif")W="gif";else if(G==="image/png")W="png";return{bytes:H,mimeType:G||(W==="jpg"?"image/jpeg":`image/${W}`),extension:W}}function g$($){return String($??"").trim().replace(/^['"]|['"]$/g,"")}function h$($){if(!$||!w($))return;try{for(let j of k($,"utf-8").split(/\r?\n/)){let Y=j.trim();if(!Y||Y.startsWith("#")||!Y.includes("="))continue;if(Y.startsWith("export "))Y=Y.slice(7).trim();let[Z,...G]=Y.split("="),H=Z.trim(),W=g$(G.join("="));if((H==="OPENROUTER_API_KEY"||H==="OPENROUTER_KEY")&&W)return{apiKey:W,source:`config:${$}`}}}catch{return}return}function n($){let j=[],Y=S($||process.cwd());while(!0){j.push(N(Y,"config","openrouter.cnf"));let Z=A(Y);if(Z===Y)break;Y=Z}return j}function p$($){let j=process.env.OPENROUTER_API_KEY||process.env.OPENROUTER_KEY;if(j)return{apiKey:j,source:"env:OPENROUTER_API_KEY"};let Y=[...n($),...n(process.cwd()),N(u(),".dm","openrouter.cnf"),N(u(),".config","dm","openrouter.cnf")],Z=new Set;for(let G of Y){if(Z.has(G))continue;Z.add(G);let H=h$(G);if(H)return H}return}async function m$($){let j=$?.modelRegistry;if(!j||typeof j!=="object")return;if(typeof j.getApiKeyForProvider==="function")for(let H of["openrouter","duckmind"]){let W=await j.getApiKeyForProvider(H);if(typeof W==="string"&&W.trim())return{apiKey:W.trim(),source:`model-registry:${H}`}}if(typeof j.getApiKeyAndHeaders!=="function")return;let Y=[],Z=new Set,G=(H)=>{if(!H||typeof H!=="object")return;let W=String(H.provider??"").toLowerCase();if(W!=="openrouter"&&W!=="duckmind")return;let X=String(H.id??H.modelId??""),f=`${W}/${X}`;if(Z.has(f))return;Z.add(f),Y.push(H)};if(G($?.model),typeof j.find==="function")G(j.find("openrouter",I)),G(j.find("openrouter","@preset/free"));if(typeof j.getAvailable==="function")for(let H of j.getAvailable())G(H);for(let H of Y){let W=await j.getApiKeyAndHeaders(H);if(W?.ok&&typeof W.apiKey==="string"&&W.apiKey.trim())return{apiKey:W.apiKey.trim(),source:`model-registry:${H.provider}/${H.id??H.modelId??""}`}}return}async function c$($,j){return await m$(j)??p$($)}function u$($){let j=$?.content;if(typeof j==="string")return j.trim();if(!Array.isArray(j))return"";return j.map((Y)=>{if(!Y||typeof Y!=="object")return"";return typeof Y.text==="string"?Y.text:""}).join("").trim()}function d$($){for(let j of Array.isArray($?.choices)?$.choices:[]){let Y=j?.message;for(let Z of Array.isArray(Y?.images)?Y.images:[]){let G=Z?.image_url,H=typeof G==="string"?G:G?.url??Z?.url;if(typeof H==="string"&&H.trim())return H.trim()}for(let Z of Array.isArray(Y?.content)?Y.content:[]){let G=Z?.image_url,H=typeof G==="string"?G:G?.url??Z?.url;if(typeof H==="string"&&H.trim())return H.trim()}}return""}async function l$({prompt:$,aspectRatio:j,outputFormat:Y,model:Z,outputRoot:G,baseName:H,cwd:W,signal:X,runtimeAuthContext:f}){if(Y!=="png")throw Error("DuckMind image2 currently supports PNG output only.");if(typeof fetch!=="function")throw Error("DuckMind image2 requires a runtime with global fetch support.");let V=await c$(W,f);if(!V?.apiKey)throw Error("Missing DuckMind API key for image2. Set OPENROUTER_API_KEY, paste a DuckMind API key via /login, or provide a local DuckMind image service config.");let O={model:Z,modalities:["image","text"],messages:[{role:"user",content:[{type:"text",text:j$($,j)}]}]},z=await fetch(O$,{method:"POST",headers:{Authorization:`Bearer ${V.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(O),signal:X}),B=await z.text(),U;try{U=B?JSON.parse(B):{}}catch{U={}}if(!z.ok){let q=U?.error?.message||U?.message||B.slice(0,400)||z.statusText;throw Error(`DuckMind image_generation failed (${z.status}): ${q}`)}let K=d$(U);if(!K)throw Error("DuckMind image_generation returned no image data.");let Q=Y$(K,Y),_=N(G,`${H}.${Q.extension}`);c(_,Q.bytes);let J=U?.choices?.[0]?.message;return{status:"ok",imagePath:_,svgPath:"",imageBase64:Q.bytes.toString("base64"),mimeType:Q.mimeType,prompt:$,aspectRatio:j,model:Z,source:V.source,responseId:typeof U?.id==="string"?U.id:"",revisedPrompt:"",backendMode:"openrouter-image-generation",requestedBackend:"openrouter:image2",authSource:V.source,assistantText:u$(J),openRouterModel:typeof U?.model==="string"?U.model:""}}function n$($){return Array.isArray($)?$.filter((j)=>j&&typeof j==="object"&&j.type==="image_generation_call"):[]}function i$(){let $=$$();return{tokens:{access_token:$.accessToken,account_id:$.accountId}}}function o$($){let j=N($,"auth.json");return m(j,JSON.stringify(i$(),null,2)+`
7
- `),j}function Z$(){let $=[],j=new Set;for(let[Y,Z]of[["env:auth_file",process.env.auth_file],["env:M_AUTH_FILE",process.env.M_AUTH_FILE]]){if(typeof Z!=="string"||!Z.trim())continue;let G=S(Z);if(!w(G)||j.has(G))continue;j.add(G),$.push({kind:"file",authSource:Y,authFile:G})}return $}function G$(){let $=p(e("auth.json"));if(!$||typeof $!=="object")return[];return Object.keys($).filter((j)=>j==="openai-codex"||/^openai-codex-account-\d+$/.test(j)).sort((j,Y)=>{if(j==="openai-codex")return-1;if(Y==="openai-codex")return 1;return j.localeCompare(Y,void 0,{numeric:!0})}).map((j)=>{let Y=$[j],Z=typeof Y?.access==="string"?Y.access:"",G=typeof Y?.accountId==="string"?Y.accountId:typeof Y?.account_id==="string"?Y.account_id:"";if(!Z||!G)return;return{kind:"managed",authSource:`auth.json:${j}`,email:typeof Y?.email==="string"?Y.email:j,authData:{tokens:{access_token:Z,account_id:G}}}}).filter(Boolean)}function r$($,j){let Y=[],Z=new Set,G=(X,f)=>{let V=f?.tokens?.access_token,O=f?.tokens?.account_id;if(typeof V!=="string"||!V.trim())return;if(typeof O!=="string"||!O.trim())return;let z=`inline:${X}:${O}`;if(Z.has(z))return;Z.add(z),Y.push({kind:"managed",authSource:X,email:X,authData:f})},H=(X,f)=>{if(!f)return;let V=S(f);if(!w(V))return;let O=`file:${V}`;if(Z.has(O))return;Z.add(O),Y.push({kind:"file",authSource:X,authFile:V})},W=j?.liveCodexCredentials;if(W?.accessToken&&W?.accountId)G(W.source||"model-registry",{tokens:{access_token:W.accessToken,account_id:W.accountId}});for(let X of Z$())H(X.authSource,X.authFile);for(let X of G$()){let f=`managed:${X.email}`;if(Z.has(f))continue;Z.add(f),Y.push(X)}if(Y.length===0)Y.push({kind:"file",authSource:"dm-auth-shim",authFile:o$($)});return Y}function s$($){let Y=p($)?.tokens,Z=typeof Y?.access_token==="string"?Y.access_token.trim():"",G=typeof Y?.account_id==="string"?Y.account_id.trim():"";if(!Z||!G)return;return{accessToken:Z,accountId:G,email:void 0,source:$}}function a$($){let j=$?.tokens,Y=typeof j?.access_token==="string"?j.access_token.trim():"",Z=typeof j?.account_id==="string"?j.account_id.trim():"";if(!Y||!Z)return;return{accessToken:Y,accountId:Z,email:void 0,source:"managed-inline"}}function t$(){for(let $ of Z$()){let j=s$($.authFile);if(j)return{...j,source:$.authSource}}for(let $ of G$()){let j=a$($.authData);if(j)return{...j,source:$.authSource}}return $$()}function H$($){return String($??"").split(/\r?\n/).map((j)=>j.trim()).filter(Boolean).map((j)=>S(j))}function e$($){if(!w($))return[];return k($,"utf-8").split(/\r?\n/).map((j)=>j.trim()).filter(Boolean).map((j)=>{try{return JSON.parse(j)}catch{return null}}).filter(Boolean)}function $1($){let j=e$($),Y="",Z="";for(let G of j)if(G?.type==="image_generation_call"&&typeof G.saved_path==="string"&&G.saved_path){if(Y=G.saved_path,typeof G.revised_prompt==="string"&&G.revised_prompt)Z=G.revised_prompt}return{savedPath:Y,revisedPrompt:Z}}async function j1({prompt:$,aspectRatio:j,model:Y,outputFormat:Z,cwd:G,signal:H,sessionRoot:W,candidate:X,index:f}){let V=N(W,`session-${f}.jsonl`),O=X.authFile;if(X.kind==="managed")O=N(W,`auth-${f}.json`),m(O,JSON.stringify(X.authData,null,2)+`
8
- `);let z={...process.env,PYTHONPATH:a+(process.env.PYTHONPATH?`:${process.env.PYTHONPATH}`:""),auth_file:O,model:Y,quiet:"1",no_session_lock:"1",session:`new:${V}`},B=v;return await new Promise((U,K)=>{let Q=!1,_="",J="",q=Q$("python3",[y,x$($,j)],{cwd:G,env:z,stdio:["ignore","pipe","pipe"]}),T=setTimeout(()=>{q.kill("SIGTERM")},B),M=()=>{q.kill("SIGTERM")};H?.addEventListener("abort",M,{once:!0}),q.stdout.on("data",(L)=>{_+=L.toString("utf-8")}),q.stderr.on("data",(L)=>{J+=L.toString("utf-8")}),q.on("error",(L)=>{if(Q)return;Q=!0,clearTimeout(T),H?.removeEventListener("abort",M),K(L)}),q.on("close",(L,K$)=>{if(Q)return;if(Q=!0,clearTimeout(T),H?.removeEventListener("abort",M),H?.aborted){K(C());return}if(K$==="SIGTERM"){K(Error(`imagen.py timed out after ${Math.round(B/1000)}s`));return}if(L!==0){K(Error(J.trim()||_.trim()||`imagen.py exited ${L}`));return}U({stdout:_,stderr:J,sessionFile:V,sessionRoot:W,authSource:X.authSource})})})}async function Y1({prompt:$,aspectRatio:j,model:Y,outputFormat:Z,cwd:G,save:H,saveDir:W,signal:X,runId:f,runtimeAuthContext:V}){if(Z!=="png")throw Error("The bundled imagen.py lane only supports PNG output.");if(!w(y))throw Error(`Bundled imagen.py is missing: ${y}`);let O=y$(G,H,W,f);x(O,{recursive:!0});let z=r$(O,V),B=[];for(let[U,K]of z.entries())try{let Q=await j1({prompt:$,aspectRatio:j,model:Y,outputFormat:Z,cwd:G,signal:X,sessionRoot:O,candidate:K,index:U});if(H$(Q.stdout).length>0)return Q;B.push(`${K.authSource}: no saved image path`)}catch(Q){if(b(Q))throw Q;B.push(`${K.authSource}: ${Q instanceof Error?Q.message:String(Q)}`)}throw Error(`imagen.py candidates exhausted: ${B.slice(-5).join(" | ")}`)}function Z1($,j,Y){let Z=S($);if(!w(Z))throw Error(`imagen.py reported a missing artifact: ${Z}`);let G=Z.split(".").pop()||"png",H=N(j,`${Y}.${G}`);return c(H,k(Z)),H}async function G1({prompt:$,aspectRatio:j,outputFormat:Y,model:Z,outputRoot:G,baseName:H,credentials:W,signal:X,imagenError:f,requestedBackend:V}){let O="",z="",B="",U="image/png";try{if(Y==="png"){let J=await l({model:Z,instructions:E$(),input:[{type:"message",role:"user",content:[{type:"input_text",text:j$($,j)}]}],reasoning:{effort:"medium",summary:"detailed"},tools:[{type:"image_generation",output_format:"png"}],tool_choice:"auto",parallel_tool_calls:!1,store:!1,stream:!0,include:["reasoning.encrypted_content"]},W,X),q=n$(J.outputItems);if(q.length>0){let T=q[0],M=Y$(T.result,"png");return z=N(G,`${H}.${M.extension}`),c(z,M.bytes),B=k(z).toString("base64"),{status:"ok",imagePath:z,svgPath:"",imageBase64:B,mimeType:M.mimeType,prompt:$,aspectRatio:j,model:Z,source:W.source,responseId:J.responseId,revisedPrompt:typeof T.revised_prompt==="string"?T.revised_prompt:"",backendMode:"native-image-generation",requestedBackend:V,imagenError:f,assistantText:J.assistantText}}}let{assistantText:K,responseId:Q}=await l({model:Z,instructions:R$(),input:[{type:"message",role:"user",content:[{type:"input_text",text:I$($,j)}]}],reasoning:{effort:"medium",summary:"detailed"},tool_choice:"auto",parallel_tool_calls:!1,store:!1,stream:!0,include:["reasoning.encrypted_content"]},W,X);E(X);let _=A$(K);if(O=N(G,`${H}.svg`),m(O,_),z=O,U="image/svg+xml",Y==="png"){let J=N(G,`${H}.png`);E(X),v$(O,J),E(X),z=J,B=k(J).toString("base64"),U="image/png"}return{status:"ok",imagePath:z,svgPath:O,imageBase64:B,mimeType:U,prompt:$,aspectRatio:j,model:Z,source:W.source,responseId:Q,backendMode:"svg-fallback",requestedBackend:V,imagenError:f,assistantText:K}}catch(K){if(b(K))for(let Q of[z,O]){if(!Q||!w(Q))continue;try{J$(Q)}catch{}}throw K}}async function W$($,j,Y,Z=void 0){let G=String($.prompt??"").trim();if(!G)throw Error("image_generation.prompt is required.");E(Y);let H=g($.aspectRatio),W=h($.outputFormat),X=t($.model),f=P$(j,$.save??"project",$.saveDir);x(f,{recursive:!0});let V=new Date().toISOString().replace(/[:.]/g,"-"),O=D$(G),z=`image-${V}-${O}-${i().slice(0,8)}`;if(M$(X))return await l$({prompt:G,aspectRatio:H,outputFormat:W,model:X,outputRoot:f,baseName:z,cwd:j,signal:Y,runtimeAuthContext:Z});let B=await C$(Z),U=B??t$();try{let K=b$();if(K>0)await T$(K,Y);let Q="",_=L$();if(W==="png"){try{let J=await Y1({prompt:G,aspectRatio:H,model:X,outputFormat:W,cwd:j,save:$.save??"project",saveDir:$.saveDir,signal:Y,runId:z,runtimeAuthContext:B?{liveCodexCredentials:B}:Z}),q=H$(J.stdout);if(q.length>0){let T=$1(J.sessionFile),M=Z1(T.savedPath||q[0],f,z),L=k(M).toString("base64");return{status:"ok",imagePath:M,svgPath:"",imageBase64:L,mimeType:"image/png",prompt:G,aspectRatio:H,model:X,source:U.source,responseId:"",revisedPrompt:T.revisedPrompt||"",backendMode:"imagen-py",requestedBackend:"imagen-py",authSource:J.authSource,assistantText:String(J.stderr??"").trim(),sessionFile:J.sessionFile,imagenSavedPaths:q}}Q=`imagen.py produced no saved image path. stderr=${J.stderr.trim().slice(0,400)}`}catch(J){if(b(J))throw J;Q=J instanceof Error?J.message:String(J)}if(!_)throw Error(`Bundled imagen.py is required for PNG image_generation, but it failed: ${Q}. Provide a healthy DM Codex credential source or set ${s}=1 only for emergency legacy fallback debugging.`)}return await G1({prompt:G,aspectRatio:H,outputFormat:W,model:X,outputRoot:f,baseName:z,credentials:U,signal:Y,imagenError:Q,requestedBackend:"legacy-fallback"})}catch(K){if(b(K))throw K;throw K}}var W1=["project","global","custom"],X1=D.Object({prompt:D.String({description:"Detailed art direction for the image. Include subject, pose, mood, style, background, and any research findings you want preserved."}),aspectRatio:D.Optional(D.String({description:"Target aspect ratio. Supported: 1:1, 4:5, 16:9. Default: 1:1."})),model:D.Optional(D.String({description:"Optional model override. Defaults to DuckMind image2/@preset/image2. Use gpt-5.5/openai-codex/gpt-5.5 only for the legacy Codex lane."})),outputFormat:D.Optional(D.String({description:"Artifact format. Supported: png, svg. Default: png."})),save:D.Optional(D.String({description:"Artifact destination: project, global, or custom. Default: project."})),saveDir:D.Optional(D.String({description:"Custom artifact directory when save=custom."}))},{additionalProperties:!1});function f1($){let j=[`Saved image: ${$.imagePath}`];if($.svgPath&&$.svgPath!==$.imagePath)j.push(`SVG source: ${$.svgPath}`);if(j.push(`Model: ${$.model}`),j.push(`Aspect: ${$.aspectRatio}`),$.backendMode)j.push(`Backend: ${$.backendMode}`);if($.source)j.push(`Auth source: ${$.source}`);return j.join(`
9
- `)}function K1($){$.on("session_start",async(j,Y)=>{let Z=H1(),G=typeof Y.modelRegistry?.getProviderAuthStatus==="function"?Y.modelRegistry.getProviderAuthStatus("openrouter"):void 0;if(!(process.env.OPENROUTER_API_KEY||process.env.OPENROUTER_KEY||X$(f$(process.cwd(),"config","openrouter.cnf"))||G?.configured)&&!X$(f$(Z,"auth.json")))Y.ui.notify("dm-image2: no DuckMind API key or Codex auth.json account found. image2 can use OPENROUTER_API_KEY, /login, or local config; the legacy Codex lane needs ~/.dm/agent/auth.json.","warning")}),$.registerTool({name:"image_generation",label:"Image Generation",description:"Generate a polished PNG image artifact using DuckMind image2 by default, or the bundled imagen.py lane from M only when model=gpt-5.5/openai-codex/gpt-5.5. Use this after research for logos, diagrams, posters, portraits, and other image tasks instead of asking the model to dump raw image data inline.",promptSnippet:"Dedicated image-generation lane. Use it for logos, portraits, diagrams, product shots, posters, UI mockups, character sheets, and artifacts you want saved to disk.",promptGuidelines:["Prefer image_generation over raw SVG or markdown image data in the final answer.","Use the bundled image-generation skill's pattern families: product hero, ad storyboard, editorial portrait, poster, character sheet, UI mockup, and comparison exploration.","Do research first when the image must reflect real people, products, or public facts.","Do not call third-party image APIs; route generation through this tool.","Answer with the saved artifact path after the tool succeeds."],parameters:X1,async execute(j,Y,Z,G,H){try{let W=await W$({prompt:Y.prompt,aspectRatio:g(Y.aspectRatio),model:Y.model,outputFormat:h(Y.outputFormat),save:typeof Y.save==="string"&&W1.includes(Y.save)?Y.save:"project",saveDir:typeof Y.saveDir==="string"?Y.saveDir:void 0},H.cwd,Z,{modelRegistry:H.modelRegistry,model:H.model}),X=[{type:"text",text:f1(W)}];if(W.imageBase64&&W.mimeType==="image/png")X.push({type:"image",data:W.imageBase64,mimeType:W.mimeType});return{content:X,details:W}}catch(W){if(Z?.aborted||b(W))throw W;let X=W instanceof Error?W.message:String(W);return{content:[{type:"text",text:`image_generation failed: ${X}`}],details:{status:"error",message:X},isError:!0}}}})}export{K1 as default};
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { Type } from "@sinclair/typebox";
4
+ import { getAgentDir } from "@duckmind/dm-coding-agent";
5
+ import { generateVectorImage, isAbortError, normalizeAspectRatio, resolveOutputFormat } from "./src/image-lib.mjs";
6
+ const SAVE_MODES = ["project", "global", "custom"];
7
+ const imageGenerationSchema = Type.Object({
8
+ prompt: Type.String({
9
+ description: "Detailed art direction for the image. Include subject, pose, mood, style, background, and any research findings you want preserved."
10
+ }),
11
+ aspectRatio: Type.Optional(Type.String({
12
+ description: "Target aspect ratio. Supported: 1:1, 4:5, 16:9. Default: 1:1."
13
+ })),
14
+ model: Type.Optional(Type.String({
15
+ description: "Optional model override. Defaults to DuckMind image2/@preset/image2. Use gpt-5.5/openai-codex/gpt-5.5 only for the legacy Codex lane."
16
+ })),
17
+ outputFormat: Type.Optional(Type.String({
18
+ description: "Artifact format. Supported: png, svg. Default: png."
19
+ })),
20
+ save: Type.Optional(Type.String({
21
+ description: "Artifact destination: project, global, or custom. Default: project."
22
+ })),
23
+ saveDir: Type.Optional(Type.String({
24
+ description: "Custom artifact directory when save=custom."
25
+ }))
26
+ }, { additionalProperties: false });
27
+ function renderImageGenerationText(result) {
28
+ const lines = [`Saved image: ${result.imagePath}`];
29
+ if (result.svgPath && result.svgPath !== result.imagePath) {
30
+ lines.push(`SVG source: ${result.svgPath}`);
31
+ }
32
+ lines.push(`Model: ${result.model}`);
33
+ lines.push(`Aspect: ${result.aspectRatio}`);
34
+ if (result.backendMode) {
35
+ lines.push(`Backend: ${result.backendMode}`);
36
+ }
37
+ if (result.source) {
38
+ lines.push(`Auth source: ${result.source}`);
39
+ }
40
+ return lines.join(`
41
+ `);
42
+ }
43
+ export default function registerDmImage(pi) {
44
+ pi.on("session_start", async (_event, ctx) => {
45
+ const agentDir = getAgentDir();
46
+ const openRouterAuthStatus = typeof ctx.modelRegistry?.getProviderAuthStatus === "function" ? ctx.modelRegistry.getProviderAuthStatus("openrouter") : undefined;
47
+ const hasOpenRouterKey = process.env.OPENROUTER_API_KEY || process.env.OPENROUTER_KEY || existsSync(join(process.cwd(), "config", "openrouter.cnf")) || openRouterAuthStatus?.configured;
48
+ if (!hasOpenRouterKey && !existsSync(join(agentDir, "auth.json"))) {
49
+ ctx.ui.notify("dm-image2: no DuckMind API key or Codex auth.json account found. image2 can use OPENROUTER_API_KEY, /login, or local config; the legacy Codex lane needs ~/.dm/agent/auth.json.", "warning");
50
+ }
51
+ });
52
+ pi.registerTool({
53
+ name: "image_generation",
54
+ label: "Image Generation",
55
+ description: "Generate a polished PNG image artifact using DuckMind image2 by default, or the bundled imagen.py lane from M only when model=gpt-5.5/openai-codex/gpt-5.5. " + "Use this after research for logos, diagrams, posters, portraits, and other image tasks instead of asking the model to dump raw image data inline.",
56
+ promptSnippet: "Dedicated image-generation lane. Use it for logos, portraits, diagrams, product shots, posters, UI mockups, character sheets, and artifacts you want saved to disk.",
57
+ promptGuidelines: [
58
+ "Prefer image_generation over raw SVG or markdown image data in the final answer.",
59
+ "Use the bundled image-generation skill's pattern families: product hero, ad storyboard, editorial portrait, poster, character sheet, UI mockup, and comparison exploration.",
60
+ "Do research first when the image must reflect real people, products, or public facts.",
61
+ "Do not call third-party image APIs; route generation through this tool.",
62
+ "Answer with the saved artifact path after the tool succeeds."
63
+ ],
64
+ parameters: imageGenerationSchema,
65
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
66
+ try {
67
+ const result = await generateVectorImage({
68
+ prompt: params.prompt,
69
+ aspectRatio: normalizeAspectRatio(params.aspectRatio),
70
+ model: params.model,
71
+ outputFormat: resolveOutputFormat(params.outputFormat),
72
+ save: typeof params.save === "string" && SAVE_MODES.includes(params.save) ? params.save : "project",
73
+ saveDir: typeof params.saveDir === "string" ? params.saveDir : undefined
74
+ }, ctx.cwd, signal, { modelRegistry: ctx.modelRegistry, model: ctx.model });
75
+ const content = [{ type: "text", text: renderImageGenerationText(result) }];
76
+ if (result.imageBase64 && result.mimeType === "image/png") {
77
+ content.push({
78
+ type: "image",
79
+ data: result.imageBase64,
80
+ mimeType: result.mimeType
81
+ });
82
+ }
83
+ return {
84
+ content,
85
+ details: result
86
+ };
87
+ } catch (error) {
88
+ if (signal?.aborted || isAbortError(error)) {
89
+ throw error;
90
+ }
91
+ const message = error instanceof Error ? error.message : String(error);
92
+ return {
93
+ content: [{ type: "text", text: `image_generation failed: ${message}` }],
94
+ details: {
95
+ status: "error",
96
+ message
97
+ },
98
+ isError: true
99
+ };
100
+ }
101
+ }
102
+ });
103
+ }