@caupulican/pi-adaptative 0.81.26 → 0.81.29

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 (286) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/cli/args.d.ts.map +1 -1
  3. package/dist/cli/args.js +3 -2
  4. package/dist/cli/args.js.map +1 -1
  5. package/dist/core/agent-session-services.d.ts +4 -0
  6. package/dist/core/agent-session-services.d.ts.map +1 -1
  7. package/dist/core/agent-session-services.js +19 -2
  8. package/dist/core/agent-session-services.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +30 -12
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +128 -36
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/auth-storage.d.ts +6 -0
  14. package/dist/core/auth-storage.d.ts.map +1 -1
  15. package/dist/core/auth-storage.js +35 -0
  16. package/dist/core/auth-storage.js.map +1 -1
  17. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
  18. package/dist/core/autonomy/bounded-completion.js +59 -19
  19. package/dist/core/autonomy/bounded-completion.js.map +1 -1
  20. package/dist/core/autonomy/contracts.d.ts +2 -0
  21. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  22. package/dist/core/autonomy/contracts.js.map +1 -1
  23. package/dist/core/autonomy/lane-tool-surface.d.ts +31 -0
  24. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -0
  25. package/dist/core/autonomy/lane-tool-surface.js +96 -0
  26. package/dist/core/autonomy/lane-tool-surface.js.map +1 -0
  27. package/dist/core/autonomy/lane-tracker.d.ts +5 -0
  28. package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
  29. package/dist/core/autonomy/lane-tracker.js +14 -3
  30. package/dist/core/autonomy/lane-tracker.js.map +1 -1
  31. package/dist/core/background-lane-controller.d.ts +28 -4
  32. package/dist/core/background-lane-controller.d.ts.map +1 -1
  33. package/dist/core/background-lane-controller.js +237 -28
  34. package/dist/core/background-lane-controller.js.map +1 -1
  35. package/dist/core/billing-failover-controller.d.ts.map +1 -1
  36. package/dist/core/billing-failover-controller.js +1 -1
  37. package/dist/core/billing-failover-controller.js.map +1 -1
  38. package/dist/core/context/current-work-memory.d.ts +9 -0
  39. package/dist/core/context/current-work-memory.d.ts.map +1 -0
  40. package/dist/core/context/current-work-memory.js +61 -0
  41. package/dist/core/context/current-work-memory.js.map +1 -0
  42. package/dist/core/context/file-store-memory-provider.d.ts +9 -0
  43. package/dist/core/context/file-store-memory-provider.d.ts.map +1 -0
  44. package/dist/core/context/file-store-memory-provider.js +106 -0
  45. package/dist/core/context/file-store-memory-provider.js.map +1 -0
  46. package/dist/core/context/long-term-memory-trigger.d.ts +15 -0
  47. package/dist/core/context/long-term-memory-trigger.d.ts.map +1 -0
  48. package/dist/core/context/long-term-memory-trigger.js +39 -0
  49. package/dist/core/context/long-term-memory-trigger.js.map +1 -0
  50. package/dist/core/context/memory-prompt-block.d.ts +3 -1
  51. package/dist/core/context/memory-prompt-block.d.ts.map +1 -1
  52. package/dist/core/context/memory-prompt-block.js +20 -3
  53. package/dist/core/context/memory-prompt-block.js.map +1 -1
  54. package/dist/core/context/memory-prompt-budget.d.ts +17 -0
  55. package/dist/core/context/memory-prompt-budget.d.ts.map +1 -0
  56. package/dist/core/context/memory-prompt-budget.js +64 -0
  57. package/dist/core/context/memory-prompt-budget.js.map +1 -0
  58. package/dist/core/context/memory-provider-contract.d.ts.map +1 -1
  59. package/dist/core/context/memory-provider-contract.js +2 -5
  60. package/dist/core/context/memory-provider-contract.js.map +1 -1
  61. package/dist/core/context/memory-tier-composer.d.ts +27 -0
  62. package/dist/core/context/memory-tier-composer.d.ts.map +1 -0
  63. package/dist/core/context/memory-tier-composer.js +79 -0
  64. package/dist/core/context/memory-tier-composer.js.map +1 -0
  65. package/dist/core/context-gc.d.ts.map +1 -1
  66. package/dist/core/context-gc.js +2 -0
  67. package/dist/core/context-gc.js.map +1 -1
  68. package/dist/core/context-pipeline.d.ts +5 -3
  69. package/dist/core/context-pipeline.d.ts.map +1 -1
  70. package/dist/core/context-pipeline.js +15 -5
  71. package/dist/core/context-pipeline.js.map +1 -1
  72. package/dist/core/cost/cost-summary.d.ts +3 -1
  73. package/dist/core/cost/cost-summary.d.ts.map +1 -1
  74. package/dist/core/cost/cost-summary.js +3 -3
  75. package/dist/core/cost/cost-summary.js.map +1 -1
  76. package/dist/core/cost-guard.d.ts +17 -7
  77. package/dist/core/cost-guard.d.ts.map +1 -1
  78. package/dist/core/cost-guard.js +33 -10
  79. package/dist/core/cost-guard.js.map +1 -1
  80. package/dist/core/default-tool-surface.d.ts +9 -0
  81. package/dist/core/default-tool-surface.d.ts.map +1 -0
  82. package/dist/core/default-tool-surface.js +18 -0
  83. package/dist/core/default-tool-surface.js.map +1 -0
  84. package/dist/core/delegation/worker-result.d.ts.map +1 -1
  85. package/dist/core/delegation/worker-result.js +3 -0
  86. package/dist/core/delegation/worker-result.js.map +1 -1
  87. package/dist/core/delegation/worker-runner.d.ts +11 -5
  88. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  89. package/dist/core/delegation/worker-runner.js +77 -16
  90. package/dist/core/delegation/worker-runner.js.map +1 -1
  91. package/dist/core/extensions/loader.d.ts.map +1 -1
  92. package/dist/core/extensions/loader.js +15 -0
  93. package/dist/core/extensions/loader.js.map +1 -1
  94. package/dist/core/extensions/runner.d.ts +2 -0
  95. package/dist/core/extensions/runner.d.ts.map +1 -1
  96. package/dist/core/extensions/runner.js +7 -0
  97. package/dist/core/extensions/runner.js.map +1 -1
  98. package/dist/core/extensions/types.d.ts +9 -0
  99. package/dist/core/extensions/types.d.ts.map +1 -1
  100. package/dist/core/extensions/types.js.map +1 -1
  101. package/dist/core/failure-corpus.d.ts.map +1 -1
  102. package/dist/core/failure-corpus.js +2 -4
  103. package/dist/core/failure-corpus.js.map +1 -1
  104. package/dist/core/footer-data-provider.d.ts.map +1 -1
  105. package/dist/core/footer-data-provider.js +9 -1
  106. package/dist/core/footer-data-provider.js.map +1 -1
  107. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  108. package/dist/core/goals/goal-continuation-prompt.js +4 -3
  109. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  110. package/dist/core/http-dispatcher.d.ts +12 -0
  111. package/dist/core/http-dispatcher.d.ts.map +1 -1
  112. package/dist/core/http-dispatcher.js +42 -4
  113. package/dist/core/http-dispatcher.js.map +1 -1
  114. package/dist/core/local-runtime-controller.d.ts +16 -0
  115. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  116. package/dist/core/local-runtime-controller.js +107 -28
  117. package/dist/core/local-runtime-controller.js.map +1 -1
  118. package/dist/core/memory/memory-manager.d.ts +8 -3
  119. package/dist/core/memory/memory-manager.d.ts.map +1 -1
  120. package/dist/core/memory/memory-manager.js +27 -11
  121. package/dist/core/memory/memory-manager.js.map +1 -1
  122. package/dist/core/memory/memory-provider.d.ts +5 -1
  123. package/dist/core/memory/memory-provider.d.ts.map +1 -1
  124. package/dist/core/memory/memory-provider.js.map +1 -1
  125. package/dist/core/memory/providers/file-store.d.ts +3 -1
  126. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  127. package/dist/core/memory/providers/file-store.js +7 -2
  128. package/dist/core/memory/providers/file-store.js.map +1 -1
  129. package/dist/core/memory/providers/transcript-recall.d.ts +1 -0
  130. package/dist/core/memory/providers/transcript-recall.d.ts.map +1 -1
  131. package/dist/core/memory/providers/transcript-recall.js +4 -7
  132. package/dist/core/memory/providers/transcript-recall.js.map +1 -1
  133. package/dist/core/memory-controller.d.ts +36 -10
  134. package/dist/core/memory-controller.d.ts.map +1 -1
  135. package/dist/core/memory-controller.js +137 -21
  136. package/dist/core/memory-controller.js.map +1 -1
  137. package/dist/core/model-registry.d.ts +25 -0
  138. package/dist/core/model-registry.d.ts.map +1 -1
  139. package/dist/core/model-registry.js +91 -8
  140. package/dist/core/model-registry.js.map +1 -1
  141. package/dist/core/model-resolver.d.ts.map +1 -1
  142. package/dist/core/model-resolver.js +30 -11
  143. package/dist/core/model-resolver.js.map +1 -1
  144. package/dist/core/model-router-controller.d.ts.map +1 -1
  145. package/dist/core/model-router-controller.js +33 -29
  146. package/dist/core/model-router-controller.js.map +1 -1
  147. package/dist/core/model-selection-controller.d.ts +3 -0
  148. package/dist/core/model-selection-controller.d.ts.map +1 -1
  149. package/dist/core/model-selection-controller.js +8 -1
  150. package/dist/core/model-selection-controller.js.map +1 -1
  151. package/dist/core/models/local-runtime.d.ts +2 -0
  152. package/dist/core/models/local-runtime.d.ts.map +1 -1
  153. package/dist/core/models/local-runtime.js +10 -4
  154. package/dist/core/models/local-runtime.js.map +1 -1
  155. package/dist/core/models/perf-profile.d.ts.map +1 -1
  156. package/dist/core/models/perf-profile.js +95 -33
  157. package/dist/core/models/perf-profile.js.map +1 -1
  158. package/dist/core/models/runtime-arbiter.d.ts +11 -0
  159. package/dist/core/models/runtime-arbiter.d.ts.map +1 -1
  160. package/dist/core/models/runtime-arbiter.js +35 -13
  161. package/dist/core/models/runtime-arbiter.js.map +1 -1
  162. package/dist/core/profile-filter-controller.d.ts +11 -0
  163. package/dist/core/profile-filter-controller.d.ts.map +1 -1
  164. package/dist/core/profile-filter-controller.js +56 -11
  165. package/dist/core/profile-filter-controller.js.map +1 -1
  166. package/dist/core/profile-registry.d.ts +2 -2
  167. package/dist/core/profile-registry.d.ts.map +1 -1
  168. package/dist/core/profile-registry.js +58 -7
  169. package/dist/core/profile-registry.js.map +1 -1
  170. package/dist/core/reflection-controller.d.ts +9 -7
  171. package/dist/core/reflection-controller.d.ts.map +1 -1
  172. package/dist/core/reflection-controller.js +81 -12
  173. package/dist/core/reflection-controller.js.map +1 -1
  174. package/dist/core/research/research-runner.d.ts +2 -2
  175. package/dist/core/research/research-runner.d.ts.map +1 -1
  176. package/dist/core/research/research-runner.js +8 -6
  177. package/dist/core/research/research-runner.js.map +1 -1
  178. package/dist/core/resource-loader.d.ts +4 -2
  179. package/dist/core/resource-loader.d.ts.map +1 -1
  180. package/dist/core/resource-loader.js +11 -1
  181. package/dist/core/resource-loader.js.map +1 -1
  182. package/dist/core/runtime-builder.d.ts +30 -3
  183. package/dist/core/runtime-builder.d.ts.map +1 -1
  184. package/dist/core/runtime-builder.js +140 -40
  185. package/dist/core/runtime-builder.js.map +1 -1
  186. package/dist/core/sdk.d.ts +8 -7
  187. package/dist/core/sdk.d.ts.map +1 -1
  188. package/dist/core/sdk.js +38 -5
  189. package/dist/core/sdk.js.map +1 -1
  190. package/dist/core/security/secret-text.d.ts +3 -0
  191. package/dist/core/security/secret-text.d.ts.map +1 -0
  192. package/dist/core/security/secret-text.js +26 -0
  193. package/dist/core/security/secret-text.js.map +1 -0
  194. package/dist/core/settings-manager.d.ts +59 -17
  195. package/dist/core/settings-manager.d.ts.map +1 -1
  196. package/dist/core/settings-manager.js +300 -51
  197. package/dist/core/settings-manager.js.map +1 -1
  198. package/dist/core/skills.d.ts.map +1 -1
  199. package/dist/core/skills.js +1 -1
  200. package/dist/core/skills.js.map +1 -1
  201. package/dist/core/system-prompt-builder.d.ts +7 -0
  202. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  203. package/dist/core/system-prompt-builder.js +23 -1
  204. package/dist/core/system-prompt-builder.js.map +1 -1
  205. package/dist/core/tool-gate-controller.d.ts +3 -0
  206. package/dist/core/tool-gate-controller.d.ts.map +1 -1
  207. package/dist/core/tool-gate-controller.js +20 -15
  208. package/dist/core/tool-gate-controller.js.map +1 -1
  209. package/dist/core/tool-selection/expected-utility.d.ts +51 -0
  210. package/dist/core/tool-selection/expected-utility.d.ts.map +1 -0
  211. package/dist/core/tool-selection/expected-utility.js +85 -0
  212. package/dist/core/tool-selection/expected-utility.js.map +1 -0
  213. package/dist/core/tool-selection/tool-performance-store.d.ts +82 -0
  214. package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -0
  215. package/dist/core/tool-selection/tool-performance-store.js +249 -0
  216. package/dist/core/tool-selection/tool-performance-store.js.map +1 -0
  217. package/dist/core/tool-selection/tool-selection-controller.d.ts +41 -0
  218. package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -0
  219. package/dist/core/tool-selection/tool-selection-controller.js +199 -0
  220. package/dist/core/tool-selection/tool-selection-controller.js.map +1 -0
  221. package/dist/core/tools/delegate-status.d.ts +9 -0
  222. package/dist/core/tools/delegate-status.d.ts.map +1 -0
  223. package/dist/core/tools/delegate-status.js +52 -0
  224. package/dist/core/tools/delegate-status.js.map +1 -0
  225. package/dist/core/tools/delegate.d.ts +10 -0
  226. package/dist/core/tools/delegate.d.ts.map +1 -1
  227. package/dist/core/tools/delegate.js +36 -13
  228. package/dist/core/tools/delegate.js.map +1 -1
  229. package/dist/core/tools/run-toolkit-script.d.ts +3 -0
  230. package/dist/core/tools/run-toolkit-script.d.ts.map +1 -1
  231. package/dist/core/tools/run-toolkit-script.js +17 -7
  232. package/dist/core/tools/run-toolkit-script.js.map +1 -1
  233. package/dist/main.d.ts.map +1 -1
  234. package/dist/main.js +2 -0
  235. package/dist/main.js.map +1 -1
  236. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  237. package/dist/modes/interactive/components/footer.js +8 -1
  238. package/dist/modes/interactive/components/footer.js.map +1 -1
  239. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  240. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  241. package/dist/modes/interactive/components/settings-selector.js +69 -29
  242. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  243. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  244. package/dist/modes/interactive/components/thinking-selector.js +2 -0
  245. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  246. package/dist/modes/interactive/config-backup.d.ts +7 -1
  247. package/dist/modes/interactive/config-backup.d.ts.map +1 -1
  248. package/dist/modes/interactive/config-backup.js +196 -32
  249. package/dist/modes/interactive/config-backup.js.map +1 -1
  250. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  251. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  252. package/dist/modes/interactive/interactive-mode.js +22 -28
  253. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  254. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  255. package/dist/modes/interactive/local-model-commands.js +2 -0
  256. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  257. package/dist/modes/interactive/profile-menu-controller.d.ts +6 -4
  258. package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
  259. package/dist/modes/interactive/profile-menu-controller.js +248 -65
  260. package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
  261. package/dist/modes/interactive/report-commands.d.ts.map +1 -1
  262. package/dist/modes/interactive/report-commands.js +8 -2
  263. package/dist/modes/interactive/report-commands.js.map +1 -1
  264. package/dist/modes/interactive/settings-selector-flow.d.ts.map +1 -1
  265. package/dist/modes/interactive/settings-selector-flow.js +8 -0
  266. package/dist/modes/interactive/settings-selector-flow.js.map +1 -1
  267. package/dist/modes/interactive/theme/theme-schema.json +1 -1
  268. package/dist/modes/interactive/theme/theme.d.ts +1 -1
  269. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  270. package/dist/modes/interactive/theme/theme.js +3 -1
  271. package/dist/modes/interactive/theme/theme.js.map +1 -1
  272. package/docs/extensions.md +32 -0
  273. package/docs/integration-sweep-builder-blueprint-2026-07-09.md +365 -0
  274. package/docs/integration-sweep-resume-2026-07-09.md +407 -0
  275. package/docs/resources.md +15 -7
  276. package/docs/settings.md +56 -5
  277. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  278. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  279. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  280. package/examples/extensions/preset.ts +2 -2
  281. package/examples/extensions/sandbox/package-lock.json +2 -2
  282. package/examples/extensions/sandbox/package.json +1 -1
  283. package/examples/extensions/with-deps/package-lock.json +2 -2
  284. package/examples/extensions/with-deps/package.json +1 -1
  285. package/npm-shrinkwrap.json +44 -35
  286. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"delegate.js","sourceRoot":"","sources":["../../../src/core/tools/delegate.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport type { LaneRecord } from \"../autonomy/lane-tracker.ts\";\nimport type { WorkerRunOutcome } from \"../delegation/worker-runner.ts\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\n\nconst delegateSchema = Type.Object(\n\t{\n\t\tinstructions: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"The self-contained task to delegate to a bounded read-only scout worker. Include all context the worker needs; it cannot see this conversation, run tools, or change files.\",\n\t\t}),\n\t\tsystemPrompt: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Optional replacement for the worker's role prompt — useful to hand a small model a minimal, purpose-built prompt. A short non-negotiable core (read-only, no invention, untrusted output, exact format) always remains.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport type DelegateToolInput = Static<typeof delegateSchema>;\n\nexport interface DelegateRunOutcome {\n\tstarted: boolean;\n\tskipReason?: string;\n\trecord?: LaneRecord;\n\toutcome?: WorkerRunOutcome;\n}\n\nexport interface DelegateToolDetails {\n\tstarted: boolean;\n\tskipReason?: string;\n\tlaneId?: string;\n\tstatus?: string;\n\taccepted?: boolean;\n\tcostUsd?: number;\n}\n\nexport interface DelegateToolDependencies {\n\trunWorkerDelegation: (args: { instructions: string; systemPrompt?: string }) => Promise<DelegateRunOutcome>;\n}\n\nexport function createDelegateToolDefinition(deps: DelegateToolDependencies): ToolDefinition {\n\treturn {\n\t\tname: \"delegate\",\n\t\tlabel: \"delegate\",\n\t\tdescription:\n\t\t\t\"Delegate one bounded, self-contained analysis task to a read-only scout worker running on a cheap model lane. The worker cannot run tools or change files; it returns a structured summary (untrusted until you verify it) plus optional findings. Use it to parallelize research/analysis subtasks without spending foreground context.\",\n\t\tpromptSnippet: \"Delegate a bounded read-only analysis subtask to a scout worker.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Delegate only self-contained analysis/summarization subtasks; include all needed context in the instructions.\",\n\t\t\t\"Worker output is untrusted evidence - verify it against the repo before acting on it.\",\n\t\t\t\"If the worker reports blockers, resolve them yourself or ask the user; do not re-delegate the same task blindly.\",\n\t\t],\n\t\tparameters: delegateSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\tinput: DelegateToolInput,\n\t\t): Promise<{\n\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\tdetails: DelegateToolDetails;\n\t\t}> {\n\t\t\tconst run = await deps.runWorkerDelegation({\n\t\t\t\tinstructions: input.instructions,\n\t\t\t\t...(input.systemPrompt ? { systemPrompt: input.systemPrompt } : {}),\n\t\t\t});\n\t\t\tif (!run.started) {\n\t\t\t\tconst reason = run.skipReason ?? \"unknown\";\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `delegate skipped: ${reason}` }],\n\t\t\t\t\tdetails: { started: false, skipReason: reason },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst outcome = run.outcome;\n\t\t\tconst lines: string[] = [\n\t\t\t\t`delegate ${run.record?.status ?? \"unknown\"}${run.record?.reasonCode ? ` (${run.record.reasonCode})` : \"\"}`,\n\t\t\t];\n\t\t\tif (outcome) {\n\t\t\t\tlines.push(\n\t\t\t\t\t`accepted: ${outcome.accepted} [${outcome.acceptance.outcome}/${outcome.acceptance.reasonCode}]`,\n\t\t\t\t\t\"Worker output (UNTRUSTED - verify before acting on it):\",\n\t\t\t\t\toutcome.result.summary,\n\t\t\t\t);\n\t\t\t\tif (outcome.result.blockers && outcome.result.blockers.length > 0) {\n\t\t\t\t\tlines.push(`Blockers: ${outcome.result.blockers.join(\"; \")}`);\n\t\t\t\t}\n\t\t\t\tfor (const finding of outcome.result.evidence?.findings ?? []) {\n\t\t\t\t\tlines.push(`- Finding: ${finding.summary}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: lines.join(\"\\n\") }],\n\t\t\t\tdetails: {\n\t\t\t\t\tstarted: true,\n\t\t\t\t\tlaneId: run.record?.laneId,\n\t\t\t\t\tstatus: run.record?.status,\n\t\t\t\t\taccepted: outcome?.accepted,\n\t\t\t\t\tcostUsd: outcome?.costUsd,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"delegate.js","sourceRoot":"","sources":["../../../src/core/tools/delegate.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport type { LaneRecord } from \"../autonomy/lane-tracker.ts\";\nimport type { WorkerRunOutcome } from \"../delegation/worker-runner.ts\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\n\nfunction createDelegateSchema() {\n\treturn Type.Object(\n\t\t{\n\t\t\tinstructions: Type.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"The self-contained task for a bounded worker with classified workspace tools. It is read-only unless workerDelegation.writeEnabled, non-empty writePaths, and its lane profile all grant write/edit; any write is path-scoped and parent-reviewed. Include all context it needs; it cannot see this conversation.\",\n\t\t\t}),\n\t\t\tsystemPrompt: Type.Optional(\n\t\t\t\tType.String({\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Optional replacement for the worker's role prompt — useful to hand a small model a minimal, purpose-built prompt. A short non-negotiable core (read-only, no invention, untrusted output, exact format) always remains.\",\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t\t{ additionalProperties: false },\n\t);\n}\n\nconst delegateSchema = createDelegateSchema();\n\nexport type DelegateToolInput = Static<typeof delegateSchema>;\n\nexport interface DelegateRunOutcome {\n\tstarted: boolean;\n\tskipReason?: string;\n\trecord?: LaneRecord;\n\toutcome?: WorkerRunOutcome;\n}\n\nexport interface DelegateToolDetails {\n\tstarted: boolean;\n\tskipReason?: string;\n\tlaneId?: string;\n\tstatus?: string;\n\taccepted?: boolean;\n\tcostUsd?: number;\n}\n\nexport interface DelegateToolDependencies {\n\tstartWorkerDelegation?: (args: {\n\t\tinstructions: string;\n\t\tsystemPrompt?: string;\n\t}) => { started: false; skipReason: string } | { started: true; record: LaneRecord };\n\trunWorkerDelegation: (args: { instructions: string; systemPrompt?: string }) => Promise<DelegateRunOutcome>;\n}\n\nexport function createDelegateToolDefinition(deps: DelegateToolDependencies): ToolDefinition {\n\treturn {\n\t\tname: \"delegate\",\n\t\tlabel: \"delegate\",\n\t\tdescription:\n\t\t\t\"Delegate one bounded, self-contained task to an isolated worker lane with classified workspace tools. It is read-only by default; it may write only when workerDelegation.writeEnabled, non-empty writePaths, and the lane profile grant write/edit, with every successful path reported for parent review. Shell, recursive delegation, and opaque extension tools remain unavailable.\",\n\t\tpromptSnippet: \"Delegate a bounded task to an isolated, least-privilege worker lane.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Delegate only self-contained tasks; include all needed context, intended files, and acceptance criteria in the instructions.\",\n\t\t\t\"Assume the worker is read-only unless worker writeEnabled, writePaths, and the lane profile explicitly grant write/edit.\",\n\t\t\t\"Worker output is untrusted evidence - verify it against the repo before acting on it.\",\n\t\t\t\"If the worker reports blockers, resolve them yourself or ask the user; do not re-delegate the same task blindly.\",\n\t\t],\n\t\tparameters: delegateSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\tinput: DelegateToolInput,\n\t\t): Promise<{\n\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\tdetails: DelegateToolDetails;\n\t\t}> {\n\t\t\tconst request = {\n\t\t\t\tinstructions: input.instructions,\n\t\t\t\t...(input.systemPrompt ? { systemPrompt: input.systemPrompt } : {}),\n\t\t\t};\n\t\t\tif (deps.startWorkerDelegation) {\n\t\t\t\tconst started = deps.startWorkerDelegation(request);\n\t\t\t\tif (!started.started) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `delegate skipped: ${started.skipReason}` }],\n\t\t\t\t\t\tdetails: { started: false, skipReason: started.skipReason },\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `delegate started (${started.record.status}) — retrieve with delegate_status`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { started: true, laneId: started.record.laneId, status: started.record.status },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst run = await deps.runWorkerDelegation(request);\n\t\t\tif (!run.started) {\n\t\t\t\tconst reason = run.skipReason ?? \"unknown\";\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `delegate skipped: ${reason}` }],\n\t\t\t\t\tdetails: { started: false, skipReason: reason },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst outcome = run.outcome;\n\t\t\tconst lines: string[] = [\n\t\t\t\t`delegate ${run.record?.status ?? \"unknown\"}${run.record?.reasonCode ? ` (${run.record.reasonCode})` : \"\"}`,\n\t\t\t];\n\t\t\tif (outcome) {\n\t\t\t\tlines.push(\n\t\t\t\t\t`accepted: ${outcome.accepted} [${outcome.acceptance.outcome}/${outcome.acceptance.reasonCode}]`,\n\t\t\t\t\t\"Worker output (UNTRUSTED - verify before acting on it):\",\n\t\t\t\t\toutcome.result.summary,\n\t\t\t\t);\n\t\t\t\tif (outcome.result.blockers && outcome.result.blockers.length > 0) {\n\t\t\t\t\tlines.push(`Blockers: ${outcome.result.blockers.join(\"; \")}`);\n\t\t\t\t}\n\t\t\t\tfor (const finding of outcome.result.evidence?.findings ?? []) {\n\t\t\t\t\tlines.push(`- Finding: ${finding.summary}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: lines.join(\"\\n\") }],\n\t\t\t\tdetails: {\n\t\t\t\t\tstarted: true,\n\t\t\t\t\tlaneId: run.record?.laneId,\n\t\t\t\t\tstatus: run.record?.status,\n\t\t\t\t\taccepted: outcome?.accepted,\n\t\t\t\t\tcostUsd: outcome?.costUsd,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { type Static, Type } from "typebox";
2
+ import type { ArtifactStore } from "../context/context-artifacts.ts";
2
3
  import type { ToolDefinition } from "../extensions/types.ts";
3
4
  import { type ReflexPlan } from "../toolkit/reflex-interpreter.ts";
4
5
  import { type ToolkitScript } from "../toolkit/script-registry.ts";
@@ -15,10 +16,12 @@ export interface RunToolkitScriptDetails {
15
16
  exitCode?: number | null;
16
17
  durationMs?: number;
17
18
  shortlist?: string[];
19
+ artifactId?: string;
18
20
  }
19
21
  export interface RunToolkitScriptDependencies {
20
22
  getScripts: () => ToolkitScript[];
21
23
  execute: (script: ToolkitScript, args: readonly string[]) => Promise<ScriptExecution>;
24
+ artifactStore?: ArtifactStore;
22
25
  /**
23
26
  * Optional reflex interpreter (local brain): consulted ONLY when the deterministic Level-0
24
27
  * matcher is ambiguous, to resolve fuzzy phrasing ("prepare db" vs "update db") into a
@@ -1 +1 @@
1
- {"version":3,"file":"run-toolkit-script.d.ts","sourceRoot":"","sources":["../../../src/core/tools/run-toolkit-script.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,kCAAkC,CAAC;AACrF,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,QAAA,MAAM,sBAAsB;;;;EAc3B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE1E,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,uBAAuB,GAAG,gBAAgB,CAAC;IACxG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,4BAA4B;IAC5C,UAAU,EAAE,MAAM,aAAa,EAAE,CAAC;IAClC,OAAO,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IACtF;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,aAAa,EAAE,KAAK,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;CACpG;AAMD,wBAAgB,oCAAoC,CAAC,IAAI,EAAE,4BAA4B,GAAG,cAAc,CAmHvG","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { acceptReflexPlan, type ReflexPlan } from \"../toolkit/reflex-interpreter.ts\";\nimport { matchToolkitScript, type ToolkitScript } from \"../toolkit/script-registry.ts\";\nimport type { ScriptExecution } from \"../toolkit/script-runner.ts\";\n\nconst runToolkitScriptSchema = Type.Object(\n\t{\n\t\tscript: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"The toolkit script to run: its registered name, a taught alias, or a natural request (e.g. 'restore-db' or 'restore the database'). Ambiguous requests return a shortlist instead of executing.\",\n\t\t}),\n\t\targs: Type.Optional(Type.Array(Type.String(), { description: \"Arguments passed to the script as-is.\" })),\n\t\tconfirm: Type.Optional(\n\t\t\tType.Boolean({\n\t\t\t\tdescription: \"Required true to run a script flagged danger: scripts marked dangerous never run without it.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport type RunToolkitScriptInput = Static<typeof runToolkitScriptSchema>;\n\nexport interface RunToolkitScriptDetails {\n\toutcome: \"executed\" | \"failed\" | \"ambiguous\" | \"not_found\" | \"confirmation_required\" | \"empty_registry\";\n\tscriptName?: string;\n\texitCode?: number | null;\n\tdurationMs?: number;\n\tshortlist?: string[];\n}\n\nexport interface RunToolkitScriptDependencies {\n\tgetScripts: () => ToolkitScript[];\n\texecute: (script: ToolkitScript, args: readonly string[]) => Promise<ScriptExecution>;\n\t/**\n\t * Optional reflex interpreter (local brain): consulted ONLY when the deterministic Level-0\n\t * matcher is ambiguous, to resolve fuzzy phrasing (\"prepare db\" vs \"update db\") into a\n\t * registry pick. Its plan is advisory — the danger/confirm rules and structural execution\n\t * contract apply identically to brain-selected scripts.\n\t */\n\tinterpret?: (request: string, scripts: readonly ToolkitScript[]) => Promise<ReflexPlan | undefined>;\n}\n\nfunction boundedOutput(text: string, maxChars = 4000): string {\n\treturn text.length <= maxChars ? text : `${text.slice(0, maxChars)}\\n… (truncated)`;\n}\n\nexport function createRunToolkitScriptToolDefinition(deps: RunToolkitScriptDependencies): ToolDefinition {\n\treturn {\n\t\tname: \"run_toolkit_script\",\n\t\tlabel: \"run_toolkit_script\",\n\t\tdescription:\n\t\t\t\"Run one of the user's registered toolkit scripts (daily-ops allowlist). Finding is a registry lookup — ambiguous requests return a shortlist to disambiguate, never a guess. The harness executes (uv/powershell/bash) and ALWAYS returns exit code, stdout, and stderr; failures are reported as errors — a failed script can never look like success.\",\n\t\tpromptSnippet: \"Run a registered toolkit script by name/alias; output and errors come back structurally.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Prefer the exact registered name; on a shortlist response, ask the user which one (or pick only when their request clearly names it).\",\n\t\t\t\"Scripts flagged dangerous require confirm: true — obtain explicit user confirmation first.\",\n\t\t\t\"Report the script's real output/exit code; never claim success when exitCode is non-zero.\",\n\t\t],\n\t\tparameters: runToolkitScriptSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\tinput: RunToolkitScriptInput,\n\t\t): Promise<{\n\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\tdetails: RunToolkitScriptDetails;\n\t\t\tisError?: boolean;\n\t\t}> {\n\t\t\tconst scripts = deps.getScripts();\n\t\t\tif (scripts.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: \"run_toolkit_script: no scripts registered (settings.toolkit.scripts).\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"empty_registry\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst match = matchToolkitScript(input.script, scripts);\n\t\t\tif (match.kind === \"none\") {\n\t\t\t\tconst closest = match.closest.map((script) => `${script.name} — ${script.description}`).join(\"\\n\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `script_not_found: nothing registered matches \"${input.script}\".${closest ? `\\nClosest:\\n${closest}` : \"\"}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"not_found\" },\n\t\t\t\t\tisError: true,\n\t\t\t\t};\n\t\t\t}\n\t\t\tlet interpreted: { script: ToolkitScript; args: string[]; confidence: number } | undefined;\n\t\t\tif (match.kind === \"ambiguous\" && deps.interpret) {\n\t\t\t\ttry {\n\t\t\t\t\tconst plan = await deps.interpret(input.script, scripts);\n\t\t\t\t\tconst accepted = acceptReflexPlan(plan, scripts);\n\t\t\t\t\tif (accepted && plan) {\n\t\t\t\t\t\tinterpreted = { ...accepted, confidence: plan.confidence };\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// interpreter is best-effort; ambiguity handling below stays authoritative\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (match.kind === \"ambiguous\" && !interpreted) {\n\t\t\t\tconst shortlist = match.shortlist.map((script) => `${script.name} — ${script.description}`);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Ambiguous request — did you mean:\\n${shortlist.join(\"\\n\")}\\nCall again with the exact name.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"ambiguous\", shortlist: match.shortlist.map((script) => script.name) },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst script = interpreted?.script ?? (match as { script: ToolkitScript }).script;\n\t\t\tif (script.danger && input.confirm !== true) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `\"${script.name}\" is flagged DANGEROUS (${script.description}). It was NOT run. Confirm with the user, then call again with confirm: true.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"confirmation_required\", scriptName: script.name },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Explicit args from the caller win; a brain-extracted arg list fills in for fuzzy requests.\n\t\t\tconst execution = await deps.execute(script, input.args ?? interpreted?.args ?? []);\n\t\t\tconst failed = execution.exitCode !== 0 || execution.timedOut;\n\t\t\tconst header = failed\n\t\t\t\t? `FAILED: ${script.name} exited ${execution.timedOut ? \"by timeout\" : execution.exitCode} after ${execution.durationMs}ms`\n\t\t\t\t: `${script.name} succeeded in ${execution.durationMs}ms`;\n\t\t\tconst body = [\n\t\t\t\theader,\n\t\t\t\texecution.stdout.trim() ? `stdout:\\n${boundedOutput(execution.stdout.trim())}` : \"stdout: (empty)\",\n\t\t\t\texecution.stderr.trim() ? `stderr:\\n${boundedOutput(execution.stderr.trim())}` : \"\",\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\"\\n\");\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: body }],\n\t\t\t\tdetails: {\n\t\t\t\t\t...(interpreted\n\t\t\t\t\t\t? { interpreter: { script: interpreted.script.name, confidence: interpreted.confidence } }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\toutcome: failed ? \"failed\" : \"executed\",\n\t\t\t\t\tscriptName: script.name,\n\t\t\t\t\texitCode: execution.exitCode,\n\t\t\t\t\tdurationMs: execution.durationMs,\n\t\t\t\t},\n\t\t\t\t...(failed ? { isError: true } : {}),\n\t\t\t};\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"run-toolkit-script.d.ts","sourceRoot":"","sources":["../../../src/core/tools/run-toolkit-script.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAErE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,kCAAkC,CAAC;AACrF,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,QAAA,MAAM,sBAAsB;;;;EAc3B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE1E,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,uBAAuB,GAAG,gBAAgB,CAAC;IACxG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,4BAA4B;IAC5C,UAAU,EAAE,MAAM,aAAa,EAAE,CAAC;IAClC,OAAO,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IACtF,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,aAAa,EAAE,KAAK,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;CACpG;AAED,wBAAgB,oCAAoC,CAAC,IAAI,EAAE,4BAA4B,GAAG,cAAc,CAmIvG","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport type { ArtifactStore } from \"../context/context-artifacts.ts\";\nimport { formatArtifactNotice, packToolOutput } from \"../context/tool-output-packer.ts\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { acceptReflexPlan, type ReflexPlan } from \"../toolkit/reflex-interpreter.ts\";\nimport { matchToolkitScript, type ToolkitScript } from \"../toolkit/script-registry.ts\";\nimport type { ScriptExecution } from \"../toolkit/script-runner.ts\";\n\nconst runToolkitScriptSchema = Type.Object(\n\t{\n\t\tscript: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"The toolkit script to run: its registered name, a taught alias, or a natural request (e.g. 'restore-db' or 'restore the database'). Ambiguous requests return a shortlist instead of executing.\",\n\t\t}),\n\t\targs: Type.Optional(Type.Array(Type.String(), { description: \"Arguments passed to the script as-is.\" })),\n\t\tconfirm: Type.Optional(\n\t\t\tType.Boolean({\n\t\t\t\tdescription: \"Required true to run a script flagged danger: scripts marked dangerous never run without it.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport type RunToolkitScriptInput = Static<typeof runToolkitScriptSchema>;\n\nexport interface RunToolkitScriptDetails {\n\toutcome: \"executed\" | \"failed\" | \"ambiguous\" | \"not_found\" | \"confirmation_required\" | \"empty_registry\";\n\tscriptName?: string;\n\texitCode?: number | null;\n\tdurationMs?: number;\n\tshortlist?: string[];\n\tartifactId?: string;\n}\n\nexport interface RunToolkitScriptDependencies {\n\tgetScripts: () => ToolkitScript[];\n\texecute: (script: ToolkitScript, args: readonly string[]) => Promise<ScriptExecution>;\n\tartifactStore?: ArtifactStore;\n\t/**\n\t * Optional reflex interpreter (local brain): consulted ONLY when the deterministic Level-0\n\t * matcher is ambiguous, to resolve fuzzy phrasing (\"prepare db\" vs \"update db\") into a\n\t * registry pick. Its plan is advisory — the danger/confirm rules and structural execution\n\t * contract apply identically to brain-selected scripts.\n\t */\n\tinterpret?: (request: string, scripts: readonly ToolkitScript[]) => Promise<ReflexPlan | undefined>;\n}\n\nexport function createRunToolkitScriptToolDefinition(deps: RunToolkitScriptDependencies): ToolDefinition {\n\treturn {\n\t\tname: \"run_toolkit_script\",\n\t\tlabel: \"run_toolkit_script\",\n\t\tdescription:\n\t\t\t\"Run one of the user's registered toolkit scripts (daily-ops allowlist). Finding is a registry lookup — ambiguous requests return a shortlist to disambiguate, never a guess. The harness executes (uv/powershell/bash) and ALWAYS returns exit code, stdout, and stderr; failures are reported as errors — a failed script can never look like success.\",\n\t\tpromptSnippet: \"Run a registered toolkit script by name/alias; output and errors come back structurally.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Prefer the exact registered name; on a shortlist response, ask the user which one (or pick only when their request clearly names it).\",\n\t\t\t\"Scripts flagged dangerous require confirm: true — obtain explicit user confirmation first.\",\n\t\t\t\"Report the script's real output/exit code; never claim success when exitCode is non-zero.\",\n\t\t],\n\t\tparameters: runToolkitScriptSchema,\n\t\tasync execute(\n\t\t\ttoolCallId,\n\t\t\tinput: RunToolkitScriptInput,\n\t\t): Promise<{\n\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\tdetails: RunToolkitScriptDetails;\n\t\t\tisError?: boolean;\n\t\t}> {\n\t\t\tconst scripts = deps.getScripts();\n\t\t\tif (scripts.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: \"run_toolkit_script: no scripts registered (settings.toolkit.scripts).\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"empty_registry\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst match = matchToolkitScript(input.script, scripts);\n\t\t\tif (match.kind === \"none\") {\n\t\t\t\tconst closest = match.closest.map((script) => `${script.name} — ${script.description}`).join(\"\\n\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `script_not_found: nothing registered matches \"${input.script}\".${closest ? `\\nClosest:\\n${closest}` : \"\"}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"not_found\" },\n\t\t\t\t\tisError: true,\n\t\t\t\t};\n\t\t\t}\n\t\t\tlet interpreted: { script: ToolkitScript; args: string[]; confidence: number } | undefined;\n\t\t\tif (match.kind === \"ambiguous\" && deps.interpret) {\n\t\t\t\ttry {\n\t\t\t\t\tconst plan = await deps.interpret(input.script, scripts);\n\t\t\t\t\tconst accepted = acceptReflexPlan(plan, scripts);\n\t\t\t\t\tif (accepted && plan) {\n\t\t\t\t\t\tinterpreted = { ...accepted, confidence: plan.confidence };\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// interpreter is best-effort; ambiguity handling below stays authoritative\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (match.kind === \"ambiguous\" && !interpreted) {\n\t\t\t\tconst shortlist = match.shortlist.map((script) => `${script.name} — ${script.description}`);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Ambiguous request — did you mean:\\n${shortlist.join(\"\\n\")}\\nCall again with the exact name.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"ambiguous\", shortlist: match.shortlist.map((script) => script.name) },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst script = interpreted?.script ?? (match as { script: ToolkitScript }).script;\n\t\t\tif (script.danger && input.confirm !== true) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `\"${script.name}\" is flagged DANGEROUS (${script.description}). It was NOT run. Confirm with the user, then call again with confirm: true.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"confirmation_required\", scriptName: script.name },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Explicit args from the caller win; a brain-extracted arg list fills in for fuzzy requests.\n\t\t\tconst execution = await deps.execute(script, input.args ?? interpreted?.args ?? []);\n\t\t\tconst failed = execution.exitCode !== 0 || execution.timedOut;\n\t\t\tconst header = failed\n\t\t\t\t? `FAILED: ${script.name} exited ${execution.timedOut ? \"by timeout\" : execution.exitCode} after ${execution.durationMs}ms`\n\t\t\t\t: `${script.name} succeeded in ${execution.durationMs}ms`;\n\t\t\tconst rawBody = [\n\t\t\t\theader,\n\t\t\t\texecution.stdout.trim() ? `stdout:\\n${execution.stdout.trim()}` : \"stdout: (empty)\",\n\t\t\t\texecution.stderr.trim() ? `stderr:\\n${execution.stderr.trim()}` : \"\",\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\"\\n\");\n\t\t\tconst packed = packToolOutput(\n\t\t\t\t{\n\t\t\t\t\ttoolName: \"run_toolkit_script\",\n\t\t\t\t\tcommand: script.name,\n\t\t\t\t\tpath: script.path,\n\t\t\t\t\trawContent: rawBody,\n\t\t\t\t\treproducible: false,\n\t\t\t\t\ttruncation: { maxLines: 400, maxBytes: 8 * 1024 },\n\t\t\t\t},\n\t\t\t\tdeps.artifactStore,\n\t\t\t\ttoolCallId,\n\t\t\t);\n\t\t\tconst body = packed.artifactId\n\t\t\t\t? `${packed.content}\\n\\n[${formatArtifactNotice(packed.artifactId)}]`\n\t\t\t\t: packed.content;\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: body }],\n\t\t\t\tdetails: {\n\t\t\t\t\t...(interpreted\n\t\t\t\t\t\t? { interpreter: { script: interpreted.script.name, confidence: interpreted.confidence } }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\toutcome: failed ? \"failed\" : \"executed\",\n\t\t\t\t\tscriptName: script.name,\n\t\t\t\t\texitCode: execution.exitCode,\n\t\t\t\t\tdurationMs: execution.durationMs,\n\t\t\t\t\t...(packed.artifactId ? { artifactId: packed.artifactId } : {}),\n\t\t\t\t},\n\t\t\t\t...(failed ? { isError: true } : {}),\n\t\t\t};\n\t\t},\n\t};\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { Type } from "typebox";
2
+ import { formatArtifactNotice, packToolOutput } from "../context/tool-output-packer.js";
2
3
  import { acceptReflexPlan } from "../toolkit/reflex-interpreter.js";
3
4
  import { matchToolkitScript } from "../toolkit/script-registry.js";
4
5
  const runToolkitScriptSchema = Type.Object({
@@ -10,9 +11,6 @@ const runToolkitScriptSchema = Type.Object({
10
11
  description: "Required true to run a script flagged danger: scripts marked dangerous never run without it.",
11
12
  })),
12
13
  }, { additionalProperties: false });
13
- function boundedOutput(text, maxChars = 4000) {
14
- return text.length <= maxChars ? text : `${text.slice(0, maxChars)}\n… (truncated)`;
15
- }
16
14
  export function createRunToolkitScriptToolDefinition(deps) {
17
15
  return {
18
16
  name: "run_toolkit_script",
@@ -25,7 +23,7 @@ export function createRunToolkitScriptToolDefinition(deps) {
25
23
  "Report the script's real output/exit code; never claim success when exitCode is non-zero.",
26
24
  ],
27
25
  parameters: runToolkitScriptSchema,
28
- async execute(_toolCallId, input) {
26
+ async execute(toolCallId, input) {
29
27
  const scripts = deps.getScripts();
30
28
  if (scripts.length === 0) {
31
29
  return {
@@ -95,13 +93,24 @@ export function createRunToolkitScriptToolDefinition(deps) {
95
93
  const header = failed
96
94
  ? `FAILED: ${script.name} exited ${execution.timedOut ? "by timeout" : execution.exitCode} after ${execution.durationMs}ms`
97
95
  : `${script.name} succeeded in ${execution.durationMs}ms`;
98
- const body = [
96
+ const rawBody = [
99
97
  header,
100
- execution.stdout.trim() ? `stdout:\n${boundedOutput(execution.stdout.trim())}` : "stdout: (empty)",
101
- execution.stderr.trim() ? `stderr:\n${boundedOutput(execution.stderr.trim())}` : "",
98
+ execution.stdout.trim() ? `stdout:\n${execution.stdout.trim()}` : "stdout: (empty)",
99
+ execution.stderr.trim() ? `stderr:\n${execution.stderr.trim()}` : "",
102
100
  ]
103
101
  .filter(Boolean)
104
102
  .join("\n");
103
+ const packed = packToolOutput({
104
+ toolName: "run_toolkit_script",
105
+ command: script.name,
106
+ path: script.path,
107
+ rawContent: rawBody,
108
+ reproducible: false,
109
+ truncation: { maxLines: 400, maxBytes: 8 * 1024 },
110
+ }, deps.artifactStore, toolCallId);
111
+ const body = packed.artifactId
112
+ ? `${packed.content}\n\n[${formatArtifactNotice(packed.artifactId)}]`
113
+ : packed.content;
105
114
  return {
106
115
  content: [{ type: "text", text: body }],
107
116
  details: {
@@ -112,6 +121,7 @@ export function createRunToolkitScriptToolDefinition(deps) {
112
121
  scriptName: script.name,
113
122
  exitCode: execution.exitCode,
114
123
  durationMs: execution.durationMs,
124
+ ...(packed.artifactId ? { artifactId: packed.artifactId } : {}),
115
125
  },
116
126
  ...(failed ? { isError: true } : {}),
117
127
  };
@@ -1 +1 @@
1
- {"version":3,"file":"run-toolkit-script.js","sourceRoot":"","sources":["../../../src/core/tools/run-toolkit-script.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { acceptReflexPlan, type ReflexPlan } from \"../toolkit/reflex-interpreter.ts\";\nimport { matchToolkitScript, type ToolkitScript } from \"../toolkit/script-registry.ts\";\nimport type { ScriptExecution } from \"../toolkit/script-runner.ts\";\n\nconst runToolkitScriptSchema = Type.Object(\n\t{\n\t\tscript: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"The toolkit script to run: its registered name, a taught alias, or a natural request (e.g. 'restore-db' or 'restore the database'). Ambiguous requests return a shortlist instead of executing.\",\n\t\t}),\n\t\targs: Type.Optional(Type.Array(Type.String(), { description: \"Arguments passed to the script as-is.\" })),\n\t\tconfirm: Type.Optional(\n\t\t\tType.Boolean({\n\t\t\t\tdescription: \"Required true to run a script flagged danger: scripts marked dangerous never run without it.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport type RunToolkitScriptInput = Static<typeof runToolkitScriptSchema>;\n\nexport interface RunToolkitScriptDetails {\n\toutcome: \"executed\" | \"failed\" | \"ambiguous\" | \"not_found\" | \"confirmation_required\" | \"empty_registry\";\n\tscriptName?: string;\n\texitCode?: number | null;\n\tdurationMs?: number;\n\tshortlist?: string[];\n}\n\nexport interface RunToolkitScriptDependencies {\n\tgetScripts: () => ToolkitScript[];\n\texecute: (script: ToolkitScript, args: readonly string[]) => Promise<ScriptExecution>;\n\t/**\n\t * Optional reflex interpreter (local brain): consulted ONLY when the deterministic Level-0\n\t * matcher is ambiguous, to resolve fuzzy phrasing (\"prepare db\" vs \"update db\") into a\n\t * registry pick. Its plan is advisory — the danger/confirm rules and structural execution\n\t * contract apply identically to brain-selected scripts.\n\t */\n\tinterpret?: (request: string, scripts: readonly ToolkitScript[]) => Promise<ReflexPlan | undefined>;\n}\n\nfunction boundedOutput(text: string, maxChars = 4000): string {\n\treturn text.length <= maxChars ? text : `${text.slice(0, maxChars)}\\n… (truncated)`;\n}\n\nexport function createRunToolkitScriptToolDefinition(deps: RunToolkitScriptDependencies): ToolDefinition {\n\treturn {\n\t\tname: \"run_toolkit_script\",\n\t\tlabel: \"run_toolkit_script\",\n\t\tdescription:\n\t\t\t\"Run one of the user's registered toolkit scripts (daily-ops allowlist). Finding is a registry lookup — ambiguous requests return a shortlist to disambiguate, never a guess. The harness executes (uv/powershell/bash) and ALWAYS returns exit code, stdout, and stderr; failures are reported as errors — a failed script can never look like success.\",\n\t\tpromptSnippet: \"Run a registered toolkit script by name/alias; output and errors come back structurally.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Prefer the exact registered name; on a shortlist response, ask the user which one (or pick only when their request clearly names it).\",\n\t\t\t\"Scripts flagged dangerous require confirm: true — obtain explicit user confirmation first.\",\n\t\t\t\"Report the script's real output/exit code; never claim success when exitCode is non-zero.\",\n\t\t],\n\t\tparameters: runToolkitScriptSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\tinput: RunToolkitScriptInput,\n\t\t): Promise<{\n\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\tdetails: RunToolkitScriptDetails;\n\t\t\tisError?: boolean;\n\t\t}> {\n\t\t\tconst scripts = deps.getScripts();\n\t\t\tif (scripts.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: \"run_toolkit_script: no scripts registered (settings.toolkit.scripts).\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"empty_registry\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst match = matchToolkitScript(input.script, scripts);\n\t\t\tif (match.kind === \"none\") {\n\t\t\t\tconst closest = match.closest.map((script) => `${script.name} — ${script.description}`).join(\"\\n\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `script_not_found: nothing registered matches \"${input.script}\".${closest ? `\\nClosest:\\n${closest}` : \"\"}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"not_found\" },\n\t\t\t\t\tisError: true,\n\t\t\t\t};\n\t\t\t}\n\t\t\tlet interpreted: { script: ToolkitScript; args: string[]; confidence: number } | undefined;\n\t\t\tif (match.kind === \"ambiguous\" && deps.interpret) {\n\t\t\t\ttry {\n\t\t\t\t\tconst plan = await deps.interpret(input.script, scripts);\n\t\t\t\t\tconst accepted = acceptReflexPlan(plan, scripts);\n\t\t\t\t\tif (accepted && plan) {\n\t\t\t\t\t\tinterpreted = { ...accepted, confidence: plan.confidence };\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// interpreter is best-effort; ambiguity handling below stays authoritative\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (match.kind === \"ambiguous\" && !interpreted) {\n\t\t\t\tconst shortlist = match.shortlist.map((script) => `${script.name} — ${script.description}`);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Ambiguous request — did you mean:\\n${shortlist.join(\"\\n\")}\\nCall again with the exact name.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"ambiguous\", shortlist: match.shortlist.map((script) => script.name) },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst script = interpreted?.script ?? (match as { script: ToolkitScript }).script;\n\t\t\tif (script.danger && input.confirm !== true) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `\"${script.name}\" is flagged DANGEROUS (${script.description}). It was NOT run. Confirm with the user, then call again with confirm: true.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"confirmation_required\", scriptName: script.name },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Explicit args from the caller win; a brain-extracted arg list fills in for fuzzy requests.\n\t\t\tconst execution = await deps.execute(script, input.args ?? interpreted?.args ?? []);\n\t\t\tconst failed = execution.exitCode !== 0 || execution.timedOut;\n\t\t\tconst header = failed\n\t\t\t\t? `FAILED: ${script.name} exited ${execution.timedOut ? \"by timeout\" : execution.exitCode} after ${execution.durationMs}ms`\n\t\t\t\t: `${script.name} succeeded in ${execution.durationMs}ms`;\n\t\t\tconst body = [\n\t\t\t\theader,\n\t\t\t\texecution.stdout.trim() ? `stdout:\\n${boundedOutput(execution.stdout.trim())}` : \"stdout: (empty)\",\n\t\t\t\texecution.stderr.trim() ? `stderr:\\n${boundedOutput(execution.stderr.trim())}` : \"\",\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\"\\n\");\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: body }],\n\t\t\t\tdetails: {\n\t\t\t\t\t...(interpreted\n\t\t\t\t\t\t? { interpreter: { script: interpreted.script.name, confidence: interpreted.confidence } }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\toutcome: failed ? \"failed\" : \"executed\",\n\t\t\t\t\tscriptName: script.name,\n\t\t\t\t\texitCode: execution.exitCode,\n\t\t\t\t\tdurationMs: execution.durationMs,\n\t\t\t\t},\n\t\t\t\t...(failed ? { isError: true } : {}),\n\t\t\t};\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"run-toolkit-script.js","sourceRoot":"","sources":["../../../src/core/tools/run-toolkit-script.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport type { ArtifactStore } from \"../context/context-artifacts.ts\";\nimport { formatArtifactNotice, packToolOutput } from \"../context/tool-output-packer.ts\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { acceptReflexPlan, type ReflexPlan } from \"../toolkit/reflex-interpreter.ts\";\nimport { matchToolkitScript, type ToolkitScript } from \"../toolkit/script-registry.ts\";\nimport type { ScriptExecution } from \"../toolkit/script-runner.ts\";\n\nconst runToolkitScriptSchema = Type.Object(\n\t{\n\t\tscript: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"The toolkit script to run: its registered name, a taught alias, or a natural request (e.g. 'restore-db' or 'restore the database'). Ambiguous requests return a shortlist instead of executing.\",\n\t\t}),\n\t\targs: Type.Optional(Type.Array(Type.String(), { description: \"Arguments passed to the script as-is.\" })),\n\t\tconfirm: Type.Optional(\n\t\t\tType.Boolean({\n\t\t\t\tdescription: \"Required true to run a script flagged danger: scripts marked dangerous never run without it.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport type RunToolkitScriptInput = Static<typeof runToolkitScriptSchema>;\n\nexport interface RunToolkitScriptDetails {\n\toutcome: \"executed\" | \"failed\" | \"ambiguous\" | \"not_found\" | \"confirmation_required\" | \"empty_registry\";\n\tscriptName?: string;\n\texitCode?: number | null;\n\tdurationMs?: number;\n\tshortlist?: string[];\n\tartifactId?: string;\n}\n\nexport interface RunToolkitScriptDependencies {\n\tgetScripts: () => ToolkitScript[];\n\texecute: (script: ToolkitScript, args: readonly string[]) => Promise<ScriptExecution>;\n\tartifactStore?: ArtifactStore;\n\t/**\n\t * Optional reflex interpreter (local brain): consulted ONLY when the deterministic Level-0\n\t * matcher is ambiguous, to resolve fuzzy phrasing (\"prepare db\" vs \"update db\") into a\n\t * registry pick. Its plan is advisory — the danger/confirm rules and structural execution\n\t * contract apply identically to brain-selected scripts.\n\t */\n\tinterpret?: (request: string, scripts: readonly ToolkitScript[]) => Promise<ReflexPlan | undefined>;\n}\n\nexport function createRunToolkitScriptToolDefinition(deps: RunToolkitScriptDependencies): ToolDefinition {\n\treturn {\n\t\tname: \"run_toolkit_script\",\n\t\tlabel: \"run_toolkit_script\",\n\t\tdescription:\n\t\t\t\"Run one of the user's registered toolkit scripts (daily-ops allowlist). Finding is a registry lookup — ambiguous requests return a shortlist to disambiguate, never a guess. The harness executes (uv/powershell/bash) and ALWAYS returns exit code, stdout, and stderr; failures are reported as errors — a failed script can never look like success.\",\n\t\tpromptSnippet: \"Run a registered toolkit script by name/alias; output and errors come back structurally.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Prefer the exact registered name; on a shortlist response, ask the user which one (or pick only when their request clearly names it).\",\n\t\t\t\"Scripts flagged dangerous require confirm: true — obtain explicit user confirmation first.\",\n\t\t\t\"Report the script's real output/exit code; never claim success when exitCode is non-zero.\",\n\t\t],\n\t\tparameters: runToolkitScriptSchema,\n\t\tasync execute(\n\t\t\ttoolCallId,\n\t\t\tinput: RunToolkitScriptInput,\n\t\t): Promise<{\n\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\tdetails: RunToolkitScriptDetails;\n\t\t\tisError?: boolean;\n\t\t}> {\n\t\t\tconst scripts = deps.getScripts();\n\t\t\tif (scripts.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: \"run_toolkit_script: no scripts registered (settings.toolkit.scripts).\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"empty_registry\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst match = matchToolkitScript(input.script, scripts);\n\t\t\tif (match.kind === \"none\") {\n\t\t\t\tconst closest = match.closest.map((script) => `${script.name} — ${script.description}`).join(\"\\n\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `script_not_found: nothing registered matches \"${input.script}\".${closest ? `\\nClosest:\\n${closest}` : \"\"}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"not_found\" },\n\t\t\t\t\tisError: true,\n\t\t\t\t};\n\t\t\t}\n\t\t\tlet interpreted: { script: ToolkitScript; args: string[]; confidence: number } | undefined;\n\t\t\tif (match.kind === \"ambiguous\" && deps.interpret) {\n\t\t\t\ttry {\n\t\t\t\t\tconst plan = await deps.interpret(input.script, scripts);\n\t\t\t\t\tconst accepted = acceptReflexPlan(plan, scripts);\n\t\t\t\t\tif (accepted && plan) {\n\t\t\t\t\t\tinterpreted = { ...accepted, confidence: plan.confidence };\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// interpreter is best-effort; ambiguity handling below stays authoritative\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (match.kind === \"ambiguous\" && !interpreted) {\n\t\t\t\tconst shortlist = match.shortlist.map((script) => `${script.name} — ${script.description}`);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Ambiguous request — did you mean:\\n${shortlist.join(\"\\n\")}\\nCall again with the exact name.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"ambiguous\", shortlist: match.shortlist.map((script) => script.name) },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst script = interpreted?.script ?? (match as { script: ToolkitScript }).script;\n\t\t\tif (script.danger && input.confirm !== true) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `\"${script.name}\" is flagged DANGEROUS (${script.description}). It was NOT run. Confirm with the user, then call again with confirm: true.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { outcome: \"confirmation_required\", scriptName: script.name },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Explicit args from the caller win; a brain-extracted arg list fills in for fuzzy requests.\n\t\t\tconst execution = await deps.execute(script, input.args ?? interpreted?.args ?? []);\n\t\t\tconst failed = execution.exitCode !== 0 || execution.timedOut;\n\t\t\tconst header = failed\n\t\t\t\t? `FAILED: ${script.name} exited ${execution.timedOut ? \"by timeout\" : execution.exitCode} after ${execution.durationMs}ms`\n\t\t\t\t: `${script.name} succeeded in ${execution.durationMs}ms`;\n\t\t\tconst rawBody = [\n\t\t\t\theader,\n\t\t\t\texecution.stdout.trim() ? `stdout:\\n${execution.stdout.trim()}` : \"stdout: (empty)\",\n\t\t\t\texecution.stderr.trim() ? `stderr:\\n${execution.stderr.trim()}` : \"\",\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\"\\n\");\n\t\t\tconst packed = packToolOutput(\n\t\t\t\t{\n\t\t\t\t\ttoolName: \"run_toolkit_script\",\n\t\t\t\t\tcommand: script.name,\n\t\t\t\t\tpath: script.path,\n\t\t\t\t\trawContent: rawBody,\n\t\t\t\t\treproducible: false,\n\t\t\t\t\ttruncation: { maxLines: 400, maxBytes: 8 * 1024 },\n\t\t\t\t},\n\t\t\t\tdeps.artifactStore,\n\t\t\t\ttoolCallId,\n\t\t\t);\n\t\t\tconst body = packed.artifactId\n\t\t\t\t? `${packed.content}\\n\\n[${formatArtifactNotice(packed.artifactId)}]`\n\t\t\t\t: packed.content;\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: body }],\n\t\t\t\tdetails: {\n\t\t\t\t\t...(interpreted\n\t\t\t\t\t\t? { interpreter: { script: interpreted.script.name, confidence: interpreted.confidence } }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\toutcome: failed ? \"failed\" : \"executed\",\n\t\t\t\t\tscriptName: script.name,\n\t\t\t\t\texitCode: execution.exitCode,\n\t\t\t\t\tdurationMs: execution.durationMs,\n\t\t\t\t\t...(packed.artifactId ? { artifactId: packed.artifactId } : {}),\n\t\t\t\t},\n\t\t\t\t...(failed ? { isError: true } : {}),\n\t\t\t};\n\t\t},\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuBH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAwjBnE,MAAM,WAAW,WAAW;IAC3B,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACxC;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,iBAyX/D","sourcesContent":["/**\n * Main entry point for the coding agent CLI.\n *\n * This file handles CLI argument parsing and translates them into\n * createAgentSession() options. The SDK does the heavy lifting.\n */\n\nimport { createInterface } from \"node:readline\";\nimport { assertValidSessionId, SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport { type ImageContent, modelsAreEqual } from \"@caupulican/pi-ai\";\nimport { ProcessTerminal, setKeybindings, TUI } from \"@caupulican/pi-tui\";\nimport chalk from \"chalk\";\nimport { type Args, type Mode, parseArgs, printHelp } from \"./cli/args.ts\";\nimport { processFileArguments } from \"./cli/file-processor.ts\";\nimport { buildInitialMessage } from \"./cli/initial-message.ts\";\nimport { listModels } from \"./cli/list-models.ts\";\nimport { selectSession } from \"./cli/session-picker.ts\";\nimport { ENV_SESSION_DIR, expandTildePath, getAgentDir, getPackageDir, VERSION } from \"./config.ts\";\nimport { type CreateAgentSessionRuntimeFactory, createAgentSessionRuntime } from \"./core/agent-session-runtime.ts\";\nimport {\n\ttype AgentSessionRuntimeDiagnostic,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionServices,\n} from \"./core/agent-session-services.ts\";\nimport { formatNoModelsAvailableMessage } from \"./core/auth-guidance.ts\";\nimport { AuthStorage } from \"./core/auth-storage.ts\";\nimport { formatDoctorReport, runDoctor, runUpdatePreflight } from \"./core/doctor.ts\";\nimport { exportFromFile } from \"./core/export-html/index.ts\";\nimport type { ExtensionFactory } from \"./core/extensions/types.ts\";\nimport { configureHttpDispatcher } from \"./core/http-dispatcher.ts\";\nimport { KeybindingsManager } from \"./core/keybindings.ts\";\nimport type { ModelRegistry } from \"./core/model-registry.ts\";\nimport {\n\tresolveCliModel,\n\tresolveCliProviderDefault,\n\tresolveModelScope,\n\tresolveProfileModelSettings,\n\ttype ScopedModel,\n} from \"./core/model-resolver.ts\";\nimport { collectModelRouterConfigDiagnostics } from \"./core/model-router/config-diagnostics.ts\";\nimport { restoreStdout, takeOverStdout } from \"./core/output-guard.ts\";\nimport { parseResourceProfileInput } from \"./core/resource-profile-blocks.ts\";\nimport type { CreateAgentSessionOptions } from \"./core/sdk.ts\";\nimport {\n\tformatMissingSessionCwdPrompt,\n\tgetMissingSessionCwdIssue,\n\tMissingSessionCwdError,\n\ttype SessionCwdIssue,\n} from \"./core/session-cwd.ts\";\nimport {\n\tcontinueRecentSession,\n\tcreateSession,\n\tforkSession,\n\tlistAllSessions,\n\tlistSessions,\n\topenSession,\n} from \"./core/session-manager-factory.ts\";\nimport { SettingsManager } from \"./core/settings-manager.ts\";\nimport { printTimings, resetTimings, time } from \"./core/timings.ts\";\nimport { hasProjectTrustInputs, ProjectTrustStore } from \"./core/trust-manager.ts\";\nimport { runMigrations, showDeprecationWarnings } from \"./migrations.ts\";\n\nimport { handleConfigCommand, handlePackageCommand } from \"./package-manager-cli.ts\";\nimport { isLocalPath, normalizePath, resolvePath } from \"./utils/paths.ts\";\nimport { cleanupWindowsSelfUpdateQuarantine } from \"./utils/windows-self-update.ts\";\n\n/**\n * Read all content from piped stdin.\n * Returns undefined if stdin is a TTY (interactive terminal).\n */\nasync function readPipedStdin(): Promise<string | undefined> {\n\t// If stdin is a TTY, we're running interactively - don't read stdin\n\tif (process.stdin.isTTY) {\n\t\treturn undefined;\n\t}\n\n\treturn new Promise((resolve) => {\n\t\tlet data = \"\";\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.on(\"data\", (chunk) => {\n\t\t\tdata += chunk;\n\t\t});\n\t\tprocess.stdin.on(\"end\", () => {\n\t\t\tresolve(data.trim() || undefined);\n\t\t});\n\t\tprocess.stdin.resume();\n\t});\n}\n\nfunction collectSettingsDiagnostics(\n\tsettingsManager: SettingsManager,\n\tcontext: string,\n): AgentSessionRuntimeDiagnostic[] {\n\treturn settingsManager.drainErrors().map(({ scope, error }) => ({\n\t\ttype: \"warning\",\n\t\tmessage: `(${context}, ${scope} settings) ${error.message}`,\n\t}));\n}\n\nfunction reportDiagnostics(diagnostics: readonly AgentSessionRuntimeDiagnostic[]): void {\n\tfor (const diagnostic of diagnostics) {\n\t\tconst color = diagnostic.type === \"error\" ? chalk.red : diagnostic.type === \"warning\" ? chalk.yellow : chalk.dim;\n\t\tconst prefix = diagnostic.type === \"error\" ? \"Error: \" : diagnostic.type === \"warning\" ? \"Warning: \" : \"\";\n\t\tconsole.error(color(`${prefix}${diagnostic.message}`));\n\t}\n}\n\nfunction isTruthyEnvFlag(value: string | undefined): boolean {\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"yes\";\n}\n\ntype AppMode = \"interactive\" | \"print\" | \"json\" | \"rpc\";\n\nfunction resolveAppMode(parsed: Args, stdinIsTTY: boolean): AppMode {\n\tif (parsed.mode === \"rpc\") {\n\t\treturn \"rpc\";\n\t}\n\tif (parsed.mode === \"json\") {\n\t\treturn \"json\";\n\t}\n\tif (parsed.print || !stdinIsTTY) {\n\t\treturn \"print\";\n\t}\n\treturn \"interactive\";\n}\n\nfunction toPrintOutputMode(appMode: AppMode): Exclude<Mode, \"rpc\"> {\n\treturn appMode === \"json\" ? \"json\" : \"text\";\n}\n\nasync function prepareInitialMessage(\n\tparsed: Args,\n\tautoResizeImages: boolean,\n\tstdinContent?: string,\n): Promise<{\n\tinitialMessage?: string;\n\tinitialImages?: ImageContent[];\n}> {\n\tif (parsed.fileArgs.length === 0) {\n\t\treturn buildInitialMessage({ parsed, stdinContent });\n\t}\n\n\tconst { text, images } = await processFileArguments(parsed.fileArgs, { autoResizeImages });\n\treturn buildInitialMessage({\n\t\tparsed,\n\t\tfileText: text,\n\t\tfileImages: images,\n\t\tstdinContent,\n\t});\n}\n\n/** Result from resolving a session argument */\ntype ResolvedSession =\n\t| { type: \"path\"; path: string } // Direct file path\n\t| { type: \"local\"; path: string } // Found in current project\n\t| { type: \"global\"; path: string; cwd: string } // Found in different project\n\t| { type: \"not_found\"; arg: string }; // Not found anywhere\n\n/**\n * Resolve a session argument to a file path.\n * If it looks like a path, use as-is. Otherwise try to match as session ID prefix.\n */\nasync function findLocalSessionByExactId(\n\tsessionId: string,\n\tcwd: string,\n\tsessionDir?: string,\n): Promise<{ type: \"local\"; path: string } | undefined> {\n\tconst localSessions = await listSessions(cwd, sessionDir);\n\tconst localMatch = localSessions.find((s) => s.id === sessionId);\n\treturn localMatch ? { type: \"local\", path: localMatch.path } : undefined;\n}\n\nasync function resolveSessionPath(sessionArg: string, cwd: string, sessionDir?: string): Promise<ResolvedSession> {\n\t// If it looks like a file path, resolve it before handing it to the session manager.\n\tif (sessionArg.includes(\"/\") || sessionArg.includes(\"\\\\\") || sessionArg.endsWith(\".jsonl\")) {\n\t\treturn { type: \"path\", path: resolvePath(sessionArg, cwd) };\n\t}\n\n\t// Try to match as session ID in current project first\n\tconst localSessions = await listSessions(cwd, sessionDir);\n\tconst localMatch =\n\t\tlocalSessions.find((s) => s.id === sessionArg) ?? localSessions.find((s) => s.id.startsWith(sessionArg));\n\n\tif (localMatch) {\n\t\treturn { type: \"local\", path: localMatch.path };\n\t}\n\n\t// Try global search across all projects\n\tconst allSessions = await listAllSessions(sessionDir);\n\tconst globalMatch =\n\t\tallSessions.find((s) => s.id === sessionArg) ?? allSessions.find((s) => s.id.startsWith(sessionArg));\n\n\tif (globalMatch) {\n\t\treturn { type: \"global\", path: globalMatch.path, cwd: globalMatch.cwd };\n\t}\n\n\t// Not found anywhere\n\treturn { type: \"not_found\", arg: sessionArg };\n}\n\n/** Prompt user for yes/no confirmation */\nasync function promptConfirm(message: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst rl = createInterface({\n\t\t\tinput: process.stdin,\n\t\t\toutput: process.stdout,\n\t\t});\n\t\trl.question(`${message} [y/N] `, (answer) => {\n\t\t\trl.close();\n\t\t\tresolve(answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\");\n\t\t});\n\t});\n}\n\nfunction validateForkFlags(parsed: Args): void {\n\tif (!parsed.fork) return;\n\n\tconst conflictingFlags = [\n\t\tparsed.session ? \"--session\" : undefined,\n\t\tparsed.continue ? \"--continue\" : undefined,\n\t\tparsed.resume ? \"--resume\" : undefined,\n\t\tparsed.noSession ? \"--no-session\" : undefined,\n\t].filter((flag): flag is string => flag !== undefined);\n\n\tif (conflictingFlags.length > 0) {\n\t\tconsole.error(chalk.red(`Error: --fork cannot be combined with ${conflictingFlags.join(\", \")}`));\n\t\tprocess.exit(1);\n\t}\n}\n\nfunction validateSessionIdFlags(parsed: Args): void {\n\tif (parsed.sessionId === undefined) return;\n\n\tconst conflictingFlags = [\n\t\tparsed.session ? \"--session\" : undefined,\n\t\tparsed.continue ? \"--continue\" : undefined,\n\t\tparsed.resume ? \"--resume\" : undefined,\n\t\tparsed.noSession ? \"--no-session\" : undefined,\n\t].filter((flag): flag is string => flag !== undefined);\n\n\tif (conflictingFlags.length > 0) {\n\t\tconsole.error(chalk.red(`Error: --session-id cannot be combined with ${conflictingFlags.join(\", \")}`));\n\t\tprocess.exit(1);\n\t}\n\n\ttry {\n\t\tassertValidSessionId(parsed.sessionId);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\tprocess.exit(1);\n\t}\n}\n\nfunction forkSessionOrExit(sourcePath: string, cwd: string, sessionDir?: string, sessionId?: string): SessionManager {\n\ttry {\n\t\treturn forkSession(sourcePath, cwd, sessionDir, { id: sessionId });\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\tprocess.exit(1);\n\t}\n}\n\nasync function createSessionManager(\n\tparsed: Args,\n\tcwd: string,\n\tsessionDir: string | undefined,\n\tsettingsManager: SettingsManager,\n): Promise<SessionManager> {\n\tif (parsed.noSession || parsed.help || parsed.listModels !== undefined) {\n\t\treturn SessionManager.inMemory(cwd);\n\t}\n\n\tif (parsed.fork) {\n\t\tif (parsed.sessionId) {\n\t\t\tconst existingTarget = await findLocalSessionByExactId(parsed.sessionId, cwd, sessionDir);\n\t\t\tif (existingTarget) {\n\t\t\t\tconsole.error(chalk.red(`Session already exists with id '${parsed.sessionId}'`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\n\t\tconst resolved = await resolveSessionPath(parsed.fork, cwd, sessionDir);\n\n\t\tswitch (resolved.type) {\n\t\t\tcase \"path\":\n\t\t\tcase \"local\":\n\t\t\tcase \"global\":\n\t\t\t\treturn forkSessionOrExit(resolved.path, cwd, sessionDir, parsed.sessionId);\n\n\t\t\tcase \"not_found\":\n\t\t\t\tconsole.error(chalk.red(`No session found matching '${resolved.arg}'`));\n\t\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\tif (parsed.session) {\n\t\tconst resolved = await resolveSessionPath(parsed.session, cwd, sessionDir);\n\n\t\tswitch (resolved.type) {\n\t\t\tcase \"path\":\n\t\t\tcase \"local\":\n\t\t\t\treturn openSession(resolved.path, sessionDir);\n\n\t\t\tcase \"global\": {\n\t\t\t\tconsole.log(chalk.yellow(`Session found in different project: ${resolved.cwd}`));\n\t\t\t\tconst shouldFork = await promptConfirm(\"Fork this session into current directory?\");\n\t\t\t\tif (!shouldFork) {\n\t\t\t\t\tconsole.log(chalk.dim(\"Aborted.\"));\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t}\n\t\t\t\treturn forkSessionOrExit(resolved.path, cwd, sessionDir);\n\t\t\t}\n\n\t\t\tcase \"not_found\":\n\t\t\t\tconsole.error(chalk.red(`No session found matching '${resolved.arg}'`));\n\t\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\tif (parsed.resume) {\n\t\tconst { initTheme, stopThemeWatcher } = await import(\"./modes/interactive/theme/theme.ts\");\n\t\tinitTheme(settingsManager.getTheme(), true);\n\t\ttry {\n\t\t\tconst selectedPath = await selectSession(\n\t\t\t\t(onProgress) => listSessions(cwd, sessionDir, onProgress),\n\t\t\t\t(onProgress) => listAllSessions(sessionDir, onProgress),\n\t\t\t);\n\t\t\tif (!selectedPath) {\n\t\t\t\tconsole.log(chalk.dim(\"No session selected\"));\n\t\t\t\tprocess.exit(0);\n\t\t\t}\n\t\t\treturn openSession(selectedPath, sessionDir);\n\t\t} finally {\n\t\t\tstopThemeWatcher();\n\t\t}\n\t}\n\n\tif (parsed.continue) {\n\t\treturn continueRecentSession(cwd, sessionDir);\n\t}\n\n\tif (parsed.sessionId) {\n\t\tconst existingSession = await findLocalSessionByExactId(parsed.sessionId, cwd, sessionDir);\n\t\tif (existingSession) {\n\t\t\treturn openSession(existingSession.path, sessionDir);\n\t\t}\n\t}\n\n\treturn createSession(cwd, sessionDir, { id: parsed.sessionId });\n}\n\nfunction buildSessionOptions(\n\tparsed: Args,\n\tscopedModels: ScopedModel[],\n\thasExistingSession: boolean,\n\tmodelRegistry: ModelRegistry,\n\tsettingsManager: SettingsManager,\n\tcwd: string,\n): {\n\toptions: CreateAgentSessionOptions;\n\tcliThinkingFromModel: boolean;\n\tdiagnostics: AgentSessionRuntimeDiagnostic[];\n} {\n\tconst options: CreateAgentSessionOptions = {};\n\tconst diagnostics: AgentSessionRuntimeDiagnostic[] = [];\n\tlet cliThinkingFromModel = false;\n\n\t// Model from CLI\n\t// - supports --provider <name> --model <pattern>\n\t// - supports --provider <name> to select that provider's default model\n\t// - supports --model <provider>/<pattern>\n\tif (parsed.model || parsed.provider) {\n\t\tconst resolved = parsed.model\n\t\t\t? resolveCliModel({\n\t\t\t\t\tcliProvider: parsed.provider,\n\t\t\t\t\tcliModel: parsed.model,\n\t\t\t\t\tmodelRegistry,\n\t\t\t\t})\n\t\t\t: resolveCliProviderDefault({\n\t\t\t\t\tcliProvider: parsed.provider,\n\t\t\t\t\tmodelRegistry,\n\t\t\t\t});\n\t\tif (resolved.warning) {\n\t\t\tdiagnostics.push({ type: \"warning\", message: resolved.warning });\n\t\t}\n\t\tif (resolved.error) {\n\t\t\tdiagnostics.push({ type: \"error\", message: resolved.error });\n\t\t}\n\t\tif (resolved.model) {\n\t\t\toptions.model = resolved.model;\n\t\t\t// Allow \"--model <pattern>:<thinking>\" as a shorthand.\n\t\t\t// Explicit --thinking still takes precedence (applied later).\n\t\t\tif (parsed.model && !parsed.thinking && resolved.thinkingLevel) {\n\t\t\t\toptions.thinkingLevel = resolved.thinkingLevel;\n\t\t\t\tcliThinkingFromModel = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Record whether model/thinking came from an explicit CLI flag (before the profile fills them\n\t// in below), so AgentSession can re-apply profile model/thinking on reload without clobbering\n\t// an explicit --model/--thinking.\n\toptions.isExplicitModel = !!parsed.model;\n\toptions.isExplicitThinking = !!parsed.thinking || cliThinkingFromModel;\n\n\t// Model/Thinking from active profiles if not explicitly set via CLI flags\n\tconst activeProfileNames = settingsManager.getActiveResourceProfileNames();\n\tif (activeProfileNames.length > 0) {\n\t\tconst profileSettings = resolveProfileModelSettings({\n\t\t\tactiveProfileNames,\n\t\t\tregistry: settingsManager.getProfileRegistry(),\n\t\t\tmodelRegistry,\n\t\t\tcwd,\n\t\t});\n\t\tif (profileSettings.warning) {\n\t\t\tdiagnostics.push({ type: \"warning\", message: profileSettings.warning });\n\t\t}\n\t\tif (profileSettings.error) {\n\t\t\tdiagnostics.push({\n\t\t\t\ttype: \"warning\",\n\t\t\t\tmessage: `Active profile model resolution error: ${profileSettings.error}`,\n\t\t\t});\n\t\t}\n\t\tif (!options.model && profileSettings.model) {\n\t\t\toptions.model = profileSettings.model;\n\t\t}\n\t\tif (!parsed.thinking && profileSettings.thinkingLevel) {\n\t\t\toptions.thinkingLevel = profileSettings.thinkingLevel;\n\t\t}\n\t}\n\n\tif (!options.model && scopedModels.length > 0 && !hasExistingSession) {\n\t\t// Check if saved default is in scoped models - use it if so, otherwise first scoped model\n\t\tconst savedProvider = settingsManager.getDefaultProvider();\n\t\tconst savedModelId = settingsManager.getDefaultModel();\n\t\tconst savedModel = savedProvider && savedModelId ? modelRegistry.find(savedProvider, savedModelId) : undefined;\n\t\tconst savedInScope = savedModel ? scopedModels.find((sm) => modelsAreEqual(sm.model, savedModel)) : undefined;\n\n\t\tif (savedInScope) {\n\t\t\toptions.model = savedInScope.model;\n\t\t\t// Use thinking level from scoped model config if explicitly set\n\t\t\tif (!parsed.thinking && savedInScope.thinkingLevel) {\n\t\t\t\toptions.thinkingLevel = savedInScope.thinkingLevel;\n\t\t\t}\n\t\t} else {\n\t\t\toptions.model = scopedModels[0].model;\n\t\t\t// Use thinking level from first scoped model if explicitly set\n\t\t\tif (!parsed.thinking && scopedModels[0].thinkingLevel) {\n\t\t\t\toptions.thinkingLevel = scopedModels[0].thinkingLevel;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Thinking level from CLI (takes precedence over scoped model thinking levels set above)\n\tif (parsed.thinking) {\n\t\toptions.thinkingLevel = parsed.thinking;\n\t}\n\n\t// Scoped models for Ctrl+P cycling\n\t// Keep thinking level undefined when not explicitly set in the model pattern.\n\t// Undefined means \"inherit current session thinking level\" during cycling.\n\tif (scopedModels.length > 0) {\n\t\toptions.scopedModels = scopedModels.map((sm) => ({\n\t\t\tmodel: sm.model,\n\t\t\tthinkingLevel: sm.thinkingLevel,\n\t\t}));\n\t}\n\n\t// API key from CLI - set in authStorage\n\t// (handled by caller before createAgentSession)\n\n\t// Tools\n\tif (parsed.noTools) {\n\t\toptions.noTools = \"all\";\n\t} else if (parsed.noBuiltinTools) {\n\t\toptions.noTools = \"builtin\";\n\t}\n\tif (parsed.tools) {\n\t\toptions.tools = [...parsed.tools];\n\t}\n\tif (parsed.excludeTools) {\n\t\toptions.excludeTools = [...parsed.excludeTools];\n\t}\n\n\treturn { options, cliThinkingFromModel, diagnostics };\n}\n\nfunction resolveCliPaths(cwd: string, paths: string[] | undefined): string[] | undefined {\n\treturn paths?.map((value) => (isLocalPath(value) ? resolvePath(value, cwd) : value));\n}\n\nasync function showStartupSelector<T>(\n\tsettingsManager: SettingsManager,\n\ttitle: string,\n\toptions: Array<{ label: string; value: T }>,\n): Promise<T | undefined> {\n\tconst [{ ExtensionSelectorComponent }, { initTheme }] = await Promise.all([\n\t\timport(\"./modes/interactive/components/extension-selector.ts\"),\n\t\timport(\"./modes/interactive/theme/theme.ts\"),\n\t]);\n\tinitTheme(settingsManager.getTheme());\n\tsetKeybindings(KeybindingsManager.create());\n\n\treturn new Promise((resolve) => {\n\t\tconst ui = new TUI(new ProcessTerminal(), settingsManager.getShowHardwareCursor());\n\t\tui.setClearOnShrink(settingsManager.getClearOnShrink());\n\n\t\tlet settled = false;\n\t\tconst finish = (result: T | undefined) => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tui.stop();\n\t\t\tresolve(result);\n\t\t};\n\n\t\tconst selector = new ExtensionSelectorComponent(\n\t\t\ttitle,\n\t\t\toptions.map((option) => option.label),\n\t\t\t(option) => finish(options.find((entry) => entry.label === option)?.value),\n\t\t\t() => finish(undefined),\n\t\t\t{ tui: ui },\n\t\t);\n\t\tui.addChild(selector);\n\t\tui.setFocus(selector);\n\t\tui.start();\n\t});\n}\n\nasync function promptForMissingSessionCwd(\n\tissue: SessionCwdIssue,\n\tsettingsManager: SettingsManager,\n): Promise<string | undefined> {\n\treturn showStartupSelector(settingsManager, formatMissingSessionCwdPrompt(issue), [\n\t\t{ label: \"Continue\", value: issue.fallbackCwd },\n\t\t{ label: \"Cancel\", value: undefined },\n\t]);\n}\n\ninterface ProjectTrustPromptResult {\n\ttrusted: boolean;\n\tremember: boolean;\n}\n\nasync function promptForProjectTrust(\n\tcwd: string,\n\tsettingsManager: SettingsManager,\n): Promise<ProjectTrustPromptResult | undefined> {\n\treturn showStartupSelector(\n\t\tsettingsManager,\n\t\t`Trust project folder?\\n${cwd}\\n\\nThis allows pi to read project instructions (AGENTS.md/CLAUDE.md/GEMINI.md), load .pi settings and resources, install missing project packages, and execute project extensions.`,\n\t\t[\n\t\t\t{ label: \"Trust\", value: { trusted: true, remember: true } },\n\t\t\t{ label: \"Trust (this session only)\", value: { trusted: true, remember: false } },\n\t\t\t{ label: \"Do not trust\", value: { trusted: false, remember: true } },\n\t\t\t{ label: \"Do not trust (this session only)\", value: { trusted: false, remember: false } },\n\t\t],\n\t);\n}\n\nasync function resolveProjectTrusted(options: {\n\tcwd: string;\n\ttrustStore: ProjectTrustStore;\n\ttrustOverride?: boolean;\n\tappMode: AppMode;\n\tsettingsManagerForPrompt: SettingsManager;\n}): Promise<boolean> {\n\tif (options.trustOverride !== undefined) {\n\t\treturn options.trustOverride;\n\t}\n\tif (!hasProjectTrustInputs(options.cwd)) {\n\t\treturn true;\n\t}\n\n\tconst decision = options.trustStore.get(options.cwd);\n\tif (decision !== null) {\n\t\treturn decision;\n\t}\n\tif (options.appMode !== \"interactive\") {\n\t\treturn false;\n\t}\n\n\tconst selected = await promptForProjectTrust(options.cwd, options.settingsManagerForPrompt);\n\tif (selected !== undefined) {\n\t\tif (selected.remember) {\n\t\t\toptions.trustStore.set(options.cwd, selected.trusted);\n\t\t}\n\t\treturn selected.trusted;\n\t}\n\treturn false;\n}\n\nexport interface MainOptions {\n\textensionFactories?: ExtensionFactory[];\n}\n\nexport async function main(args: string[], options?: MainOptions) {\n\tresetTimings();\n\tconst offlineMode = args.includes(\"--offline\") || isTruthyEnvFlag(process.env.PI_OFFLINE);\n\tif (offlineMode) {\n\t\tprocess.env.PI_OFFLINE = \"1\";\n\t\tprocess.env.PI_SKIP_VERSION_CHECK = \"1\";\n\t}\n\n\tif (process.platform === \"win32\") {\n\t\tcleanupWindowsSelfUpdateQuarantine(getPackageDir());\n\t}\n\n\tif (args[0] === \"doctor\") {\n\t\t// Interactive command: show fff-node install progress rather than a\n\t\t// silent multi-second gap (runUpdatePreflight's background run stays\n\t\t// quiet -- see the `{ silent: true }` default on runDoctor).\n\t\tconst report = await runDoctor(undefined, { silent: false });\n\t\tconsole.log(formatDoctorReport(report));\n\t\tprocess.exitCode = report.checks.some((check) => !check.present) ? 1 : 0;\n\t\treturn;\n\t}\n\n\tif (await handlePackageCommand(args)) {\n\t\t// Verify + provision required tooling right after an update, instead of\n\t\t// leaving it to lazy first-use (see doctor.ts's module doc). Best-effort\n\t\t// and non-fatal: runUpdatePreflight() never throws, so a broken\n\t\t// environment check can't turn a successful update into a failed one.\n\t\tif (args[0] === \"update\" && process.exitCode !== 1) {\n\t\t\tawait runUpdatePreflight();\n\t\t}\n\t\treturn;\n\t}\n\n\tif (await handleConfigCommand(args)) {\n\t\treturn;\n\t}\n\n\tconst parsed = parseArgs(args);\n\tif (parsed.diagnostics.length > 0) {\n\t\tfor (const d of parsed.diagnostics) {\n\t\t\tconst color = d.type === \"error\" ? chalk.red : chalk.yellow;\n\t\t\tconsole.error(color(`${d.type === \"error\" ? \"Error\" : \"Warning\"}: ${d.message}`));\n\t\t}\n\t\tif (parsed.diagnostics.some((d) => d.type === \"error\")) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\ttime(\"parseArgs\");\n\tlet appMode = resolveAppMode(parsed, process.stdin.isTTY);\n\tconst shouldTakeOverStdout = appMode !== \"interactive\";\n\tif (shouldTakeOverStdout) {\n\t\ttakeOverStdout();\n\t}\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\tprocess.exit(0);\n\t}\n\n\tif (parsed.export) {\n\t\tlet result: string;\n\t\ttry {\n\t\t\tconst outputPath = parsed.messages.length > 0 ? parsed.messages[0] : undefined;\n\t\t\tresult = await exportFromFile(parsed.export, outputPath);\n\t\t} catch (error: unknown) {\n\t\t\tconst message = error instanceof Error ? error.message : \"Failed to export session\";\n\t\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tconsole.log(`Exported to: ${result}`);\n\t\tprocess.exit(0);\n\t}\n\n\tif (parsed.mode === \"rpc\" && parsed.fileArgs.length > 0) {\n\t\tconsole.error(chalk.red(\"Error: @file arguments are not supported in RPC mode\"));\n\t\tprocess.exit(1);\n\t}\n\n\tvalidateForkFlags(parsed);\n\tvalidateSessionIdFlags(parsed);\n\n\t// Run migrations (pass cwd for project-local migrations)\n\tconst { migratedAuthProviders: migratedProviders, deprecationWarnings } = runMigrations(process.cwd());\n\ttime(\"runMigrations\");\n\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst trustStore = new ProjectTrustStore(agentDir);\n\tconst startupProjectTrusted =\n\t\tparsed.projectTrustOverride ?? (!hasProjectTrustInputs(cwd) || trustStore.get(cwd) === true);\n\tconst startupSettingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: startupProjectTrusted });\n\treportDiagnostics(collectSettingsDiagnostics(startupSettingsManager, \"startup session lookup\"));\n\n\t// Decide the final runtime cwd before creating cwd-bound runtime services.\n\t// --session and --resume may select a session from another project, so project-local\n\t// settings, resources, provider registrations, and models must be resolved only after\n\t// the target session cwd is known. The startup-cwd settings manager is used only for\n\t// sessionDir lookup during session selection.\n\tconst envSessionDir = process.env[ENV_SESSION_DIR];\n\tconst sessionDir =\n\t\t(parsed.sessionDir ? normalizePath(parsed.sessionDir) : undefined) ??\n\t\t(envSessionDir ? expandTildePath(envSessionDir) : undefined) ??\n\t\tstartupSettingsManager.getSessionDir();\n\tlet sessionManager = await createSessionManager(parsed, cwd, sessionDir, startupSettingsManager);\n\tconst missingSessionCwdIssue = getMissingSessionCwdIssue(sessionManager, cwd);\n\tif (missingSessionCwdIssue) {\n\t\tif (appMode === \"interactive\") {\n\t\t\tconst selectedCwd = await promptForMissingSessionCwd(missingSessionCwdIssue, startupSettingsManager);\n\t\t\tif (!selectedCwd) {\n\t\t\t\tprocess.exit(0);\n\t\t\t}\n\t\t\tsessionManager = openSession(missingSessionCwdIssue.sessionFile!, sessionDir, selectedCwd);\n\t\t} else {\n\t\t\tconsole.error(chalk.red(new MissingSessionCwdError(missingSessionCwdIssue).message));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\tif (parsed.name !== undefined) {\n\t\tconst name = parsed.name.trim();\n\t\tif (!name) {\n\t\t\tconsole.error(chalk.red(\"Error: --name requires a non-empty value\"));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tsessionManager.appendSessionInfo(name);\n\t}\n\ttime(\"createSessionManager\");\n\n\tconst trustPromptMode: AppMode = parsed.help || parsed.listModels !== undefined ? \"print\" : appMode;\n\tconst projectTrustedForSession = await resolveProjectTrusted({\n\t\tcwd: sessionManager.getCwd(),\n\t\ttrustStore,\n\t\ttrustOverride: parsed.projectTrustOverride,\n\t\tappMode: trustPromptMode,\n\t\tsettingsManagerForPrompt: startupSettingsManager,\n\t});\n\n\tconst resolvedExtensionPaths = resolveCliPaths(cwd, parsed.extensions);\n\tconst resolvedSkillPaths = resolveCliPaths(cwd, parsed.skills);\n\tconst resolvedPromptTemplatePaths = resolveCliPaths(cwd, parsed.promptTemplates);\n\tconst resolvedThemePaths = resolveCliPaths(cwd, parsed.themes);\n\tconst authStorage = AuthStorage.create();\n\tconst createRuntime: CreateAgentSessionRuntimeFactory = async ({\n\t\tcwd,\n\t\tagentDir,\n\t\tsessionManager,\n\t\tsessionStartEvent,\n\t}) => {\n\t\tconst projectTrusted =\n\t\t\tcwd === sessionManager.getCwd()\n\t\t\t\t? projectTrustedForSession\n\t\t\t\t: (parsed.projectTrustOverride ?? (!hasProjectTrustInputs(cwd) || trustStore.get(cwd) === true));\n\t\tconst runtimeSettingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted });\n\t\tif (parsed.resourceProfileJson) {\n\t\t\tfor (const profileInput of parsed.resourceProfileJson) {\n\t\t\t\tconst { profiles, errors } = parseResourceProfileInput(profileInput);\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\treportDiagnostics(\n\t\t\t\t\t\terrors.map((message) => ({\n\t\t\t\t\t\t\ttype: \"warning\" as const,\n\t\t\t\t\t\t\tmessage: `--resource-profile-json ignored entry: ${message}`,\n\t\t\t\t\t\t})),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\truntimeSettingsManager.addInlineResourceProfileDefinitions(profiles);\n\t\t\t}\n\t\t}\n\t\tif (parsed.resourceProfiles && parsed.resourceProfiles.length > 0) {\n\t\t\truntimeSettingsManager.setRuntimeResourceProfiles(parsed.resourceProfiles);\n\t\t}\n\t\tconst services = await createAgentSessionServices({\n\t\t\tcwd,\n\t\t\tagentDir,\n\t\t\tauthStorage,\n\t\t\tsettingsManager: runtimeSettingsManager,\n\t\t\textensionFlagValues: parsed.unknownFlags,\n\t\t\tresourceLoaderOptions: {\n\t\t\t\tadditionalExtensionPaths: resolvedExtensionPaths,\n\t\t\t\tadditionalSkillPaths: resolvedSkillPaths,\n\t\t\t\tadditionalPromptTemplatePaths: resolvedPromptTemplatePaths,\n\t\t\t\tadditionalThemePaths: resolvedThemePaths,\n\t\t\t\tnoExtensions: parsed.noExtensions,\n\t\t\t\tnoSkills: parsed.noSkills,\n\t\t\t\tnoPromptTemplates: parsed.noPromptTemplates,\n\t\t\t\tnoThemes: parsed.noThemes,\n\t\t\t\tnoContextFiles: parsed.noContextFiles,\n\t\t\t\tsystemPrompt: parsed.systemPrompt,\n\t\t\t\tappendSystemPrompt: parsed.appendSystemPrompt,\n\t\t\t\textensionFactories: options?.extensionFactories,\n\t\t\t},\n\t\t});\n\t\tconst { settingsManager, modelRegistry, resourceLoader } = services;\n\t\tconst diagnostics: AgentSessionRuntimeDiagnostic[] = [\n\t\t\t...services.diagnostics,\n\t\t\t...collectSettingsDiagnostics(settingsManager, \"runtime creation\"),\n\t\t\t...resourceLoader.getExtensions().errors.map(({ path, error }) => ({\n\t\t\t\ttype: \"error\" as const,\n\t\t\t\tmessage: `Failed to load extension \"${path}\": ${error}`,\n\t\t\t})),\n\t\t];\n\n\t\tdiagnostics.push(\n\t\t\t...collectModelRouterConfigDiagnostics(settingsManager.getModelRouterSettings(), modelRegistry, agentDir).map(\n\t\t\t\t(message) => ({\n\t\t\t\t\ttype: \"warning\" as const,\n\t\t\t\t\tmessage,\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\n\t\tconst modelPatterns = parsed.models ?? settingsManager.getEnabledModels();\n\t\tconst scopedModels =\n\t\t\tmodelPatterns && modelPatterns.length > 0 ? await resolveModelScope(modelPatterns, modelRegistry) : [];\n\t\tconst {\n\t\t\toptions: sessionOptions,\n\t\t\tcliThinkingFromModel,\n\t\t\tdiagnostics: sessionOptionDiagnostics,\n\t\t} = buildSessionOptions(\n\t\t\tparsed,\n\t\t\tscopedModels,\n\t\t\tsessionManager.buildSessionContext().messages.length > 0,\n\t\t\tmodelRegistry,\n\t\t\tsettingsManager,\n\t\t\tcwd,\n\t\t);\n\t\tdiagnostics.push(...sessionOptionDiagnostics);\n\n\t\tif (parsed.apiKey) {\n\t\t\tif (!sessionOptions.model) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\tmessage: \"--api-key requires a model to be specified via --model, --provider/--model, or --models\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tauthStorage.setRuntimeApiKey(sessionOptions.model.provider, parsed.apiKey);\n\t\t\t}\n\t\t}\n\n\t\tconst created = await createAgentSessionFromServices({\n\t\t\tservices,\n\t\t\tsessionManager,\n\t\t\tsessionStartEvent,\n\t\t\tmodel: sessionOptions.model,\n\t\t\tthinkingLevel: sessionOptions.thinkingLevel,\n\t\t\tscopedModels: sessionOptions.scopedModels,\n\t\t\ttools: sessionOptions.tools,\n\t\t\texcludeTools: sessionOptions.excludeTools,\n\t\t\tnoTools: sessionOptions.noTools,\n\t\t\ttoolProfileFilter: settingsManager.getResourceProfileFilter(\"tools\"),\n\t\t\tcustomTools: sessionOptions.customTools,\n\t\t});\n\t\tconst cliThinkingOverride = parsed.thinking !== undefined || cliThinkingFromModel;\n\t\tif (created.session.model && cliThinkingOverride) {\n\t\t\tcreated.session.setThinkingLevel(created.session.thinkingLevel);\n\t\t}\n\n\t\treturn {\n\t\t\t...created,\n\t\t\tservices,\n\t\t\tdiagnostics,\n\t\t};\n\t};\n\ttime(\"createRuntime\");\n\tconst runtime = await createAgentSessionRuntime(createRuntime, {\n\t\tcwd: sessionManager.getCwd(),\n\t\tagentDir,\n\t\tsessionManager,\n\t});\n\ttime(\"createAgentSessionRuntime\");\n\tconst { services, session, modelFallbackMessage } = runtime;\n\tconst { settingsManager, modelRegistry, resourceLoader } = services;\n\tconfigureHttpDispatcher(settingsManager.getHttpIdleTimeoutMs());\n\n\tif (parsed.help) {\n\t\tconst extensionFlags = resourceLoader\n\t\t\t.getExtensions()\n\t\t\t.extensions.flatMap((extension) => Array.from(extension.flags.values()));\n\t\tprintHelp(extensionFlags);\n\t\tprocess.exit(0);\n\t}\n\n\tif (parsed.listModels !== undefined) {\n\t\tconst searchPattern = typeof parsed.listModels === \"string\" ? parsed.listModels : undefined;\n\t\tawait listModels(modelRegistry, searchPattern);\n\t\tprocess.exit(0);\n\t}\n\n\t// Read piped stdin content (if any) - skip for RPC mode which uses stdin for JSON-RPC\n\tlet stdinContent: string | undefined;\n\tif (appMode !== \"rpc\") {\n\t\tstdinContent = await readPipedStdin();\n\t\tif (stdinContent !== undefined && appMode === \"interactive\") {\n\t\t\tappMode = \"print\";\n\t\t}\n\t}\n\ttime(\"readPipedStdin\");\n\n\tconst { initialMessage, initialImages } = await prepareInitialMessage(\n\t\tparsed,\n\t\tsettingsManager.getImageAutoResize(),\n\t\tstdinContent,\n\t);\n\ttime(\"prepareInitialMessage\");\n\tconst { initTheme, stopThemeWatcher } = await import(\"./modes/interactive/theme/theme.ts\");\n\tinitTheme(settingsManager.getTheme(), appMode === \"interactive\");\n\ttime(\"initTheme\");\n\n\t// Show deprecation warnings in interactive mode\n\tif (appMode === \"interactive\" && deprecationWarnings.length > 0) {\n\t\tawait showDeprecationWarnings(deprecationWarnings);\n\t}\n\n\ttime(\"resolveModelScope\");\n\treportDiagnostics(runtime.diagnostics);\n\tif (runtime.diagnostics.some((diagnostic) => diagnostic.type === \"error\")) {\n\t\tprocess.exit(1);\n\t}\n\ttime(\"createAgentSession\");\n\n\tif (appMode !== \"interactive\" && !session.model) {\n\t\tconsole.error(chalk.red(formatNoModelsAvailableMessage()));\n\t\tprocess.exit(1);\n\t}\n\n\tconst startupBenchmark = isTruthyEnvFlag(process.env.PI_STARTUP_BENCHMARK);\n\tif (startupBenchmark && appMode !== \"interactive\") {\n\t\tconsole.error(chalk.red(\"Error: PI_STARTUP_BENCHMARK only supports interactive mode\"));\n\t\tprocess.exit(1);\n\t}\n\n\tif (appMode === \"rpc\") {\n\t\tprintTimings();\n\t\tconst { runRpcMode } = await import(\"./modes/rpc/rpc-mode.ts\");\n\t\tawait runRpcMode(runtime);\n\t} else if (appMode === \"interactive\") {\n\t\tconst { InteractiveMode } = await import(\"./modes/interactive/interactive-mode.ts\");\n\t\tconst interactiveMode = new InteractiveMode(runtime, {\n\t\t\tmigratedProviders,\n\t\t\tmodelFallbackMessage,\n\t\t\tinitialMessage,\n\t\t\tinitialImages,\n\t\t\tinitialMessages: parsed.messages,\n\t\t\tverbose: parsed.verbose,\n\t\t});\n\t\tif (startupBenchmark) {\n\t\t\tawait interactiveMode.init();\n\t\t\ttime(\"interactiveMode.init\");\n\t\t\tprintTimings();\n\t\t\tinteractiveMode.stop();\n\t\t\tstopThemeWatcher();\n\t\t\tif (process.stdout.writableLength > 0) {\n\t\t\t\tawait new Promise<void>((resolve) => process.stdout.once(\"drain\", resolve));\n\t\t\t}\n\t\t\tif (process.stderr.writableLength > 0) {\n\t\t\t\tawait new Promise<void>((resolve) => process.stderr.once(\"drain\", resolve));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tprintTimings();\n\t\tawait interactiveMode.run();\n\t} else {\n\t\tprintTimings();\n\t\tconst { runPrintMode } = await import(\"./modes/print-mode.ts\");\n\t\tconst exitCode = await runPrintMode(runtime, {\n\t\t\tmode: toPrintOutputMode(appMode),\n\t\t\tmessages: parsed.messages,\n\t\t\tinitialMessage,\n\t\t\tinitialImages,\n\t\t\tprintUsage: parsed.printUsage,\n\t\t});\n\t\tstopThemeWatcher();\n\t\trestoreStdout();\n\t\tif (exitCode !== 0) {\n\t\t\tprocess.exitCode = exitCode;\n\t\t}\n\t\treturn;\n\t}\n}\n"]}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuBH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAwjBnE,MAAM,WAAW,WAAW;IAC3B,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACxC;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,iBA2X/D","sourcesContent":["/**\n * Main entry point for the coding agent CLI.\n *\n * This file handles CLI argument parsing and translates them into\n * createAgentSession() options. The SDK does the heavy lifting.\n */\n\nimport { createInterface } from \"node:readline\";\nimport { assertValidSessionId, SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport { type ImageContent, modelsAreEqual } from \"@caupulican/pi-ai\";\nimport { ProcessTerminal, setKeybindings, TUI } from \"@caupulican/pi-tui\";\nimport chalk from \"chalk\";\nimport { type Args, type Mode, parseArgs, printHelp } from \"./cli/args.ts\";\nimport { processFileArguments } from \"./cli/file-processor.ts\";\nimport { buildInitialMessage } from \"./cli/initial-message.ts\";\nimport { listModels } from \"./cli/list-models.ts\";\nimport { selectSession } from \"./cli/session-picker.ts\";\nimport { ENV_SESSION_DIR, expandTildePath, getAgentDir, getPackageDir, VERSION } from \"./config.ts\";\nimport { type CreateAgentSessionRuntimeFactory, createAgentSessionRuntime } from \"./core/agent-session-runtime.ts\";\nimport {\n\ttype AgentSessionRuntimeDiagnostic,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionServices,\n} from \"./core/agent-session-services.ts\";\nimport { formatNoModelsAvailableMessage } from \"./core/auth-guidance.ts\";\nimport { AuthStorage } from \"./core/auth-storage.ts\";\nimport { formatDoctorReport, runDoctor, runUpdatePreflight } from \"./core/doctor.ts\";\nimport { exportFromFile } from \"./core/export-html/index.ts\";\nimport type { ExtensionFactory } from \"./core/extensions/types.ts\";\nimport { configureHttpDispatcher } from \"./core/http-dispatcher.ts\";\nimport { KeybindingsManager } from \"./core/keybindings.ts\";\nimport type { ModelRegistry } from \"./core/model-registry.ts\";\nimport {\n\tresolveCliModel,\n\tresolveCliProviderDefault,\n\tresolveModelScope,\n\tresolveProfileModelSettings,\n\ttype ScopedModel,\n} from \"./core/model-resolver.ts\";\nimport { collectModelRouterConfigDiagnostics } from \"./core/model-router/config-diagnostics.ts\";\nimport { restoreStdout, takeOverStdout } from \"./core/output-guard.ts\";\nimport { parseResourceProfileInput } from \"./core/resource-profile-blocks.ts\";\nimport type { CreateAgentSessionOptions } from \"./core/sdk.ts\";\nimport {\n\tformatMissingSessionCwdPrompt,\n\tgetMissingSessionCwdIssue,\n\tMissingSessionCwdError,\n\ttype SessionCwdIssue,\n} from \"./core/session-cwd.ts\";\nimport {\n\tcontinueRecentSession,\n\tcreateSession,\n\tforkSession,\n\tlistAllSessions,\n\tlistSessions,\n\topenSession,\n} from \"./core/session-manager-factory.ts\";\nimport { SettingsManager } from \"./core/settings-manager.ts\";\nimport { printTimings, resetTimings, time } from \"./core/timings.ts\";\nimport { hasProjectTrustInputs, ProjectTrustStore } from \"./core/trust-manager.ts\";\nimport { runMigrations, showDeprecationWarnings } from \"./migrations.ts\";\n\nimport { handleConfigCommand, handlePackageCommand } from \"./package-manager-cli.ts\";\nimport { isLocalPath, normalizePath, resolvePath } from \"./utils/paths.ts\";\nimport { cleanupWindowsSelfUpdateQuarantine } from \"./utils/windows-self-update.ts\";\n\n/**\n * Read all content from piped stdin.\n * Returns undefined if stdin is a TTY (interactive terminal).\n */\nasync function readPipedStdin(): Promise<string | undefined> {\n\t// If stdin is a TTY, we're running interactively - don't read stdin\n\tif (process.stdin.isTTY) {\n\t\treturn undefined;\n\t}\n\n\treturn new Promise((resolve) => {\n\t\tlet data = \"\";\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.on(\"data\", (chunk) => {\n\t\t\tdata += chunk;\n\t\t});\n\t\tprocess.stdin.on(\"end\", () => {\n\t\t\tresolve(data.trim() || undefined);\n\t\t});\n\t\tprocess.stdin.resume();\n\t});\n}\n\nfunction collectSettingsDiagnostics(\n\tsettingsManager: SettingsManager,\n\tcontext: string,\n): AgentSessionRuntimeDiagnostic[] {\n\treturn settingsManager.drainErrors().map(({ scope, error }) => ({\n\t\ttype: \"warning\",\n\t\tmessage: `(${context}, ${scope} settings) ${error.message}`,\n\t}));\n}\n\nfunction reportDiagnostics(diagnostics: readonly AgentSessionRuntimeDiagnostic[]): void {\n\tfor (const diagnostic of diagnostics) {\n\t\tconst color = diagnostic.type === \"error\" ? chalk.red : diagnostic.type === \"warning\" ? chalk.yellow : chalk.dim;\n\t\tconst prefix = diagnostic.type === \"error\" ? \"Error: \" : diagnostic.type === \"warning\" ? \"Warning: \" : \"\";\n\t\tconsole.error(color(`${prefix}${diagnostic.message}`));\n\t}\n}\n\nfunction isTruthyEnvFlag(value: string | undefined): boolean {\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"yes\";\n}\n\ntype AppMode = \"interactive\" | \"print\" | \"json\" | \"rpc\";\n\nfunction resolveAppMode(parsed: Args, stdinIsTTY: boolean): AppMode {\n\tif (parsed.mode === \"rpc\") {\n\t\treturn \"rpc\";\n\t}\n\tif (parsed.mode === \"json\") {\n\t\treturn \"json\";\n\t}\n\tif (parsed.print || !stdinIsTTY) {\n\t\treturn \"print\";\n\t}\n\treturn \"interactive\";\n}\n\nfunction toPrintOutputMode(appMode: AppMode): Exclude<Mode, \"rpc\"> {\n\treturn appMode === \"json\" ? \"json\" : \"text\";\n}\n\nasync function prepareInitialMessage(\n\tparsed: Args,\n\tautoResizeImages: boolean,\n\tstdinContent?: string,\n): Promise<{\n\tinitialMessage?: string;\n\tinitialImages?: ImageContent[];\n}> {\n\tif (parsed.fileArgs.length === 0) {\n\t\treturn buildInitialMessage({ parsed, stdinContent });\n\t}\n\n\tconst { text, images } = await processFileArguments(parsed.fileArgs, { autoResizeImages });\n\treturn buildInitialMessage({\n\t\tparsed,\n\t\tfileText: text,\n\t\tfileImages: images,\n\t\tstdinContent,\n\t});\n}\n\n/** Result from resolving a session argument */\ntype ResolvedSession =\n\t| { type: \"path\"; path: string } // Direct file path\n\t| { type: \"local\"; path: string } // Found in current project\n\t| { type: \"global\"; path: string; cwd: string } // Found in different project\n\t| { type: \"not_found\"; arg: string }; // Not found anywhere\n\n/**\n * Resolve a session argument to a file path.\n * If it looks like a path, use as-is. Otherwise try to match as session ID prefix.\n */\nasync function findLocalSessionByExactId(\n\tsessionId: string,\n\tcwd: string,\n\tsessionDir?: string,\n): Promise<{ type: \"local\"; path: string } | undefined> {\n\tconst localSessions = await listSessions(cwd, sessionDir);\n\tconst localMatch = localSessions.find((s) => s.id === sessionId);\n\treturn localMatch ? { type: \"local\", path: localMatch.path } : undefined;\n}\n\nasync function resolveSessionPath(sessionArg: string, cwd: string, sessionDir?: string): Promise<ResolvedSession> {\n\t// If it looks like a file path, resolve it before handing it to the session manager.\n\tif (sessionArg.includes(\"/\") || sessionArg.includes(\"\\\\\") || sessionArg.endsWith(\".jsonl\")) {\n\t\treturn { type: \"path\", path: resolvePath(sessionArg, cwd) };\n\t}\n\n\t// Try to match as session ID in current project first\n\tconst localSessions = await listSessions(cwd, sessionDir);\n\tconst localMatch =\n\t\tlocalSessions.find((s) => s.id === sessionArg) ?? localSessions.find((s) => s.id.startsWith(sessionArg));\n\n\tif (localMatch) {\n\t\treturn { type: \"local\", path: localMatch.path };\n\t}\n\n\t// Try global search across all projects\n\tconst allSessions = await listAllSessions(sessionDir);\n\tconst globalMatch =\n\t\tallSessions.find((s) => s.id === sessionArg) ?? allSessions.find((s) => s.id.startsWith(sessionArg));\n\n\tif (globalMatch) {\n\t\treturn { type: \"global\", path: globalMatch.path, cwd: globalMatch.cwd };\n\t}\n\n\t// Not found anywhere\n\treturn { type: \"not_found\", arg: sessionArg };\n}\n\n/** Prompt user for yes/no confirmation */\nasync function promptConfirm(message: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst rl = createInterface({\n\t\t\tinput: process.stdin,\n\t\t\toutput: process.stdout,\n\t\t});\n\t\trl.question(`${message} [y/N] `, (answer) => {\n\t\t\trl.close();\n\t\t\tresolve(answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\");\n\t\t});\n\t});\n}\n\nfunction validateForkFlags(parsed: Args): void {\n\tif (!parsed.fork) return;\n\n\tconst conflictingFlags = [\n\t\tparsed.session ? \"--session\" : undefined,\n\t\tparsed.continue ? \"--continue\" : undefined,\n\t\tparsed.resume ? \"--resume\" : undefined,\n\t\tparsed.noSession ? \"--no-session\" : undefined,\n\t].filter((flag): flag is string => flag !== undefined);\n\n\tif (conflictingFlags.length > 0) {\n\t\tconsole.error(chalk.red(`Error: --fork cannot be combined with ${conflictingFlags.join(\", \")}`));\n\t\tprocess.exit(1);\n\t}\n}\n\nfunction validateSessionIdFlags(parsed: Args): void {\n\tif (parsed.sessionId === undefined) return;\n\n\tconst conflictingFlags = [\n\t\tparsed.session ? \"--session\" : undefined,\n\t\tparsed.continue ? \"--continue\" : undefined,\n\t\tparsed.resume ? \"--resume\" : undefined,\n\t\tparsed.noSession ? \"--no-session\" : undefined,\n\t].filter((flag): flag is string => flag !== undefined);\n\n\tif (conflictingFlags.length > 0) {\n\t\tconsole.error(chalk.red(`Error: --session-id cannot be combined with ${conflictingFlags.join(\", \")}`));\n\t\tprocess.exit(1);\n\t}\n\n\ttry {\n\t\tassertValidSessionId(parsed.sessionId);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\tprocess.exit(1);\n\t}\n}\n\nfunction forkSessionOrExit(sourcePath: string, cwd: string, sessionDir?: string, sessionId?: string): SessionManager {\n\ttry {\n\t\treturn forkSession(sourcePath, cwd, sessionDir, { id: sessionId });\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\tprocess.exit(1);\n\t}\n}\n\nasync function createSessionManager(\n\tparsed: Args,\n\tcwd: string,\n\tsessionDir: string | undefined,\n\tsettingsManager: SettingsManager,\n): Promise<SessionManager> {\n\tif (parsed.noSession || parsed.help || parsed.listModels !== undefined) {\n\t\treturn SessionManager.inMemory(cwd);\n\t}\n\n\tif (parsed.fork) {\n\t\tif (parsed.sessionId) {\n\t\t\tconst existingTarget = await findLocalSessionByExactId(parsed.sessionId, cwd, sessionDir);\n\t\t\tif (existingTarget) {\n\t\t\t\tconsole.error(chalk.red(`Session already exists with id '${parsed.sessionId}'`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\n\t\tconst resolved = await resolveSessionPath(parsed.fork, cwd, sessionDir);\n\n\t\tswitch (resolved.type) {\n\t\t\tcase \"path\":\n\t\t\tcase \"local\":\n\t\t\tcase \"global\":\n\t\t\t\treturn forkSessionOrExit(resolved.path, cwd, sessionDir, parsed.sessionId);\n\n\t\t\tcase \"not_found\":\n\t\t\t\tconsole.error(chalk.red(`No session found matching '${resolved.arg}'`));\n\t\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\tif (parsed.session) {\n\t\tconst resolved = await resolveSessionPath(parsed.session, cwd, sessionDir);\n\n\t\tswitch (resolved.type) {\n\t\t\tcase \"path\":\n\t\t\tcase \"local\":\n\t\t\t\treturn openSession(resolved.path, sessionDir);\n\n\t\t\tcase \"global\": {\n\t\t\t\tconsole.log(chalk.yellow(`Session found in different project: ${resolved.cwd}`));\n\t\t\t\tconst shouldFork = await promptConfirm(\"Fork this session into current directory?\");\n\t\t\t\tif (!shouldFork) {\n\t\t\t\t\tconsole.log(chalk.dim(\"Aborted.\"));\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t}\n\t\t\t\treturn forkSessionOrExit(resolved.path, cwd, sessionDir);\n\t\t\t}\n\n\t\t\tcase \"not_found\":\n\t\t\t\tconsole.error(chalk.red(`No session found matching '${resolved.arg}'`));\n\t\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\tif (parsed.resume) {\n\t\tconst { initTheme, stopThemeWatcher } = await import(\"./modes/interactive/theme/theme.ts\");\n\t\tinitTheme(settingsManager.getTheme(), true);\n\t\ttry {\n\t\t\tconst selectedPath = await selectSession(\n\t\t\t\t(onProgress) => listSessions(cwd, sessionDir, onProgress),\n\t\t\t\t(onProgress) => listAllSessions(sessionDir, onProgress),\n\t\t\t);\n\t\t\tif (!selectedPath) {\n\t\t\t\tconsole.log(chalk.dim(\"No session selected\"));\n\t\t\t\tprocess.exit(0);\n\t\t\t}\n\t\t\treturn openSession(selectedPath, sessionDir);\n\t\t} finally {\n\t\t\tstopThemeWatcher();\n\t\t}\n\t}\n\n\tif (parsed.continue) {\n\t\treturn continueRecentSession(cwd, sessionDir);\n\t}\n\n\tif (parsed.sessionId) {\n\t\tconst existingSession = await findLocalSessionByExactId(parsed.sessionId, cwd, sessionDir);\n\t\tif (existingSession) {\n\t\t\treturn openSession(existingSession.path, sessionDir);\n\t\t}\n\t}\n\n\treturn createSession(cwd, sessionDir, { id: parsed.sessionId });\n}\n\nfunction buildSessionOptions(\n\tparsed: Args,\n\tscopedModels: ScopedModel[],\n\thasExistingSession: boolean,\n\tmodelRegistry: ModelRegistry,\n\tsettingsManager: SettingsManager,\n\tcwd: string,\n): {\n\toptions: CreateAgentSessionOptions;\n\tcliThinkingFromModel: boolean;\n\tdiagnostics: AgentSessionRuntimeDiagnostic[];\n} {\n\tconst options: CreateAgentSessionOptions = {};\n\tconst diagnostics: AgentSessionRuntimeDiagnostic[] = [];\n\tlet cliThinkingFromModel = false;\n\n\t// Model from CLI\n\t// - supports --provider <name> --model <pattern>\n\t// - supports --provider <name> to select that provider's default model\n\t// - supports --model <provider>/<pattern>\n\tif (parsed.model || parsed.provider) {\n\t\tconst resolved = parsed.model\n\t\t\t? resolveCliModel({\n\t\t\t\t\tcliProvider: parsed.provider,\n\t\t\t\t\tcliModel: parsed.model,\n\t\t\t\t\tmodelRegistry,\n\t\t\t\t})\n\t\t\t: resolveCliProviderDefault({\n\t\t\t\t\tcliProvider: parsed.provider,\n\t\t\t\t\tmodelRegistry,\n\t\t\t\t});\n\t\tif (resolved.warning) {\n\t\t\tdiagnostics.push({ type: \"warning\", message: resolved.warning });\n\t\t}\n\t\tif (resolved.error) {\n\t\t\tdiagnostics.push({ type: \"error\", message: resolved.error });\n\t\t}\n\t\tif (resolved.model) {\n\t\t\toptions.model = resolved.model;\n\t\t\t// Allow \"--model <pattern>:<thinking>\" as a shorthand.\n\t\t\t// Explicit --thinking still takes precedence (applied later).\n\t\t\tif (parsed.model && !parsed.thinking && resolved.thinkingLevel) {\n\t\t\t\toptions.thinkingLevel = resolved.thinkingLevel;\n\t\t\t\tcliThinkingFromModel = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Record whether model/thinking came from an explicit CLI flag (before the profile fills them\n\t// in below), so AgentSession can re-apply profile model/thinking on reload without clobbering\n\t// an explicit --model/--thinking.\n\toptions.isExplicitModel = !!parsed.model;\n\toptions.isExplicitThinking = !!parsed.thinking || cliThinkingFromModel;\n\n\t// Model/Thinking from active profiles if not explicitly set via CLI flags\n\tconst activeProfileNames = settingsManager.getActiveResourceProfileNames();\n\tif (activeProfileNames.length > 0) {\n\t\tconst profileSettings = resolveProfileModelSettings({\n\t\t\tactiveProfileNames,\n\t\t\tregistry: settingsManager.getProfileRegistry(),\n\t\t\tmodelRegistry,\n\t\t\tcwd,\n\t\t});\n\t\tif (profileSettings.warning) {\n\t\t\tdiagnostics.push({ type: \"warning\", message: profileSettings.warning });\n\t\t}\n\t\tif (profileSettings.error) {\n\t\t\tdiagnostics.push({\n\t\t\t\ttype: \"warning\",\n\t\t\t\tmessage: `Active profile model resolution error: ${profileSettings.error}`,\n\t\t\t});\n\t\t}\n\t\tif (!options.model && profileSettings.model) {\n\t\t\toptions.model = profileSettings.model;\n\t\t}\n\t\tif (!parsed.thinking && profileSettings.thinkingLevel) {\n\t\t\toptions.thinkingLevel = profileSettings.thinkingLevel;\n\t\t}\n\t}\n\n\tif (!options.model && scopedModels.length > 0 && !hasExistingSession) {\n\t\t// Check if saved default is in scoped models - use it if so, otherwise first scoped model\n\t\tconst savedProvider = settingsManager.getDefaultProvider();\n\t\tconst savedModelId = settingsManager.getDefaultModel();\n\t\tconst savedModel = savedProvider && savedModelId ? modelRegistry.find(savedProvider, savedModelId) : undefined;\n\t\tconst savedInScope = savedModel ? scopedModels.find((sm) => modelsAreEqual(sm.model, savedModel)) : undefined;\n\n\t\tif (savedInScope) {\n\t\t\toptions.model = savedInScope.model;\n\t\t\t// Use thinking level from scoped model config if explicitly set\n\t\t\tif (!parsed.thinking && savedInScope.thinkingLevel) {\n\t\t\t\toptions.thinkingLevel = savedInScope.thinkingLevel;\n\t\t\t}\n\t\t} else {\n\t\t\toptions.model = scopedModels[0].model;\n\t\t\t// Use thinking level from first scoped model if explicitly set\n\t\t\tif (!parsed.thinking && scopedModels[0].thinkingLevel) {\n\t\t\t\toptions.thinkingLevel = scopedModels[0].thinkingLevel;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Thinking level from CLI (takes precedence over scoped model thinking levels set above)\n\tif (parsed.thinking) {\n\t\toptions.thinkingLevel = parsed.thinking;\n\t}\n\n\t// Scoped models for Ctrl+P cycling\n\t// Keep thinking level undefined when not explicitly set in the model pattern.\n\t// Undefined means \"inherit current session thinking level\" during cycling.\n\tif (scopedModels.length > 0) {\n\t\toptions.scopedModels = scopedModels.map((sm) => ({\n\t\t\tmodel: sm.model,\n\t\t\tthinkingLevel: sm.thinkingLevel,\n\t\t}));\n\t}\n\n\t// API key from CLI - set in authStorage\n\t// (handled by caller before createAgentSession)\n\n\t// Tools\n\tif (parsed.noTools) {\n\t\toptions.noTools = \"all\";\n\t} else if (parsed.noBuiltinTools) {\n\t\toptions.noTools = \"builtin\";\n\t}\n\tif (parsed.tools) {\n\t\toptions.tools = [...parsed.tools];\n\t}\n\tif (parsed.excludeTools) {\n\t\toptions.excludeTools = [...parsed.excludeTools];\n\t}\n\n\treturn { options, cliThinkingFromModel, diagnostics };\n}\n\nfunction resolveCliPaths(cwd: string, paths: string[] | undefined): string[] | undefined {\n\treturn paths?.map((value) => (isLocalPath(value) ? resolvePath(value, cwd) : value));\n}\n\nasync function showStartupSelector<T>(\n\tsettingsManager: SettingsManager,\n\ttitle: string,\n\toptions: Array<{ label: string; value: T }>,\n): Promise<T | undefined> {\n\tconst [{ ExtensionSelectorComponent }, { initTheme }] = await Promise.all([\n\t\timport(\"./modes/interactive/components/extension-selector.ts\"),\n\t\timport(\"./modes/interactive/theme/theme.ts\"),\n\t]);\n\tinitTheme(settingsManager.getTheme());\n\tsetKeybindings(KeybindingsManager.create());\n\n\treturn new Promise((resolve) => {\n\t\tconst ui = new TUI(new ProcessTerminal(), settingsManager.getShowHardwareCursor());\n\t\tui.setClearOnShrink(settingsManager.getClearOnShrink());\n\n\t\tlet settled = false;\n\t\tconst finish = (result: T | undefined) => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tui.stop();\n\t\t\tresolve(result);\n\t\t};\n\n\t\tconst selector = new ExtensionSelectorComponent(\n\t\t\ttitle,\n\t\t\toptions.map((option) => option.label),\n\t\t\t(option) => finish(options.find((entry) => entry.label === option)?.value),\n\t\t\t() => finish(undefined),\n\t\t\t{ tui: ui },\n\t\t);\n\t\tui.addChild(selector);\n\t\tui.setFocus(selector);\n\t\tui.start();\n\t});\n}\n\nasync function promptForMissingSessionCwd(\n\tissue: SessionCwdIssue,\n\tsettingsManager: SettingsManager,\n): Promise<string | undefined> {\n\treturn showStartupSelector(settingsManager, formatMissingSessionCwdPrompt(issue), [\n\t\t{ label: \"Continue\", value: issue.fallbackCwd },\n\t\t{ label: \"Cancel\", value: undefined },\n\t]);\n}\n\ninterface ProjectTrustPromptResult {\n\ttrusted: boolean;\n\tremember: boolean;\n}\n\nasync function promptForProjectTrust(\n\tcwd: string,\n\tsettingsManager: SettingsManager,\n): Promise<ProjectTrustPromptResult | undefined> {\n\treturn showStartupSelector(\n\t\tsettingsManager,\n\t\t`Trust project folder?\\n${cwd}\\n\\nThis allows pi to read project instructions (AGENTS.md/CLAUDE.md/GEMINI.md), load .pi settings and resources, install missing project packages, and execute project extensions.`,\n\t\t[\n\t\t\t{ label: \"Trust\", value: { trusted: true, remember: true } },\n\t\t\t{ label: \"Trust (this session only)\", value: { trusted: true, remember: false } },\n\t\t\t{ label: \"Do not trust\", value: { trusted: false, remember: true } },\n\t\t\t{ label: \"Do not trust (this session only)\", value: { trusted: false, remember: false } },\n\t\t],\n\t);\n}\n\nasync function resolveProjectTrusted(options: {\n\tcwd: string;\n\ttrustStore: ProjectTrustStore;\n\ttrustOverride?: boolean;\n\tappMode: AppMode;\n\tsettingsManagerForPrompt: SettingsManager;\n}): Promise<boolean> {\n\tif (options.trustOverride !== undefined) {\n\t\treturn options.trustOverride;\n\t}\n\tif (!hasProjectTrustInputs(options.cwd)) {\n\t\treturn true;\n\t}\n\n\tconst decision = options.trustStore.get(options.cwd);\n\tif (decision !== null) {\n\t\treturn decision;\n\t}\n\tif (options.appMode !== \"interactive\") {\n\t\treturn false;\n\t}\n\n\tconst selected = await promptForProjectTrust(options.cwd, options.settingsManagerForPrompt);\n\tif (selected !== undefined) {\n\t\tif (selected.remember) {\n\t\t\toptions.trustStore.set(options.cwd, selected.trusted);\n\t\t}\n\t\treturn selected.trusted;\n\t}\n\treturn false;\n}\n\nexport interface MainOptions {\n\textensionFactories?: ExtensionFactory[];\n}\n\nexport async function main(args: string[], options?: MainOptions) {\n\tresetTimings();\n\tconst offlineMode = args.includes(\"--offline\") || isTruthyEnvFlag(process.env.PI_OFFLINE);\n\tif (offlineMode) {\n\t\tprocess.env.PI_OFFLINE = \"1\";\n\t\tprocess.env.PI_SKIP_VERSION_CHECK = \"1\";\n\t}\n\n\tif (process.platform === \"win32\") {\n\t\tcleanupWindowsSelfUpdateQuarantine(getPackageDir());\n\t}\n\n\tif (args[0] === \"doctor\") {\n\t\t// Interactive command: show fff-node install progress rather than a\n\t\t// silent multi-second gap (runUpdatePreflight's background run stays\n\t\t// quiet -- see the `{ silent: true }` default on runDoctor).\n\t\tconst report = await runDoctor(undefined, { silent: false });\n\t\tconsole.log(formatDoctorReport(report));\n\t\tprocess.exitCode = report.checks.some((check) => !check.present) ? 1 : 0;\n\t\treturn;\n\t}\n\n\tif (await handlePackageCommand(args)) {\n\t\t// Verify + provision required tooling right after an update, instead of\n\t\t// leaving it to lazy first-use (see doctor.ts's module doc). Best-effort\n\t\t// and non-fatal: runUpdatePreflight() never throws, so a broken\n\t\t// environment check can't turn a successful update into a failed one.\n\t\tif (args[0] === \"update\" && process.exitCode !== 1) {\n\t\t\tawait runUpdatePreflight();\n\t\t}\n\t\treturn;\n\t}\n\n\tif (await handleConfigCommand(args)) {\n\t\treturn;\n\t}\n\n\tconst parsed = parseArgs(args);\n\tif (parsed.diagnostics.length > 0) {\n\t\tfor (const d of parsed.diagnostics) {\n\t\t\tconst color = d.type === \"error\" ? chalk.red : chalk.yellow;\n\t\t\tconsole.error(color(`${d.type === \"error\" ? \"Error\" : \"Warning\"}: ${d.message}`));\n\t\t}\n\t\tif (parsed.diagnostics.some((d) => d.type === \"error\")) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\ttime(\"parseArgs\");\n\tlet appMode = resolveAppMode(parsed, process.stdin.isTTY);\n\tconst shouldTakeOverStdout = appMode !== \"interactive\";\n\tif (shouldTakeOverStdout) {\n\t\ttakeOverStdout();\n\t}\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\tprocess.exit(0);\n\t}\n\n\tif (parsed.export) {\n\t\tlet result: string;\n\t\ttry {\n\t\t\tconst outputPath = parsed.messages.length > 0 ? parsed.messages[0] : undefined;\n\t\t\tresult = await exportFromFile(parsed.export, outputPath);\n\t\t} catch (error: unknown) {\n\t\t\tconst message = error instanceof Error ? error.message : \"Failed to export session\";\n\t\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tconsole.log(`Exported to: ${result}`);\n\t\tprocess.exit(0);\n\t}\n\n\tif (parsed.mode === \"rpc\" && parsed.fileArgs.length > 0) {\n\t\tconsole.error(chalk.red(\"Error: @file arguments are not supported in RPC mode\"));\n\t\tprocess.exit(1);\n\t}\n\n\tvalidateForkFlags(parsed);\n\tvalidateSessionIdFlags(parsed);\n\n\t// Run migrations (pass cwd for project-local migrations)\n\tconst { migratedAuthProviders: migratedProviders, deprecationWarnings } = runMigrations(process.cwd());\n\ttime(\"runMigrations\");\n\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst trustStore = new ProjectTrustStore(agentDir);\n\tconst startupProjectTrusted =\n\t\tparsed.projectTrustOverride ?? (!hasProjectTrustInputs(cwd) || trustStore.get(cwd) === true);\n\tconst startupSettingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: startupProjectTrusted });\n\treportDiagnostics(collectSettingsDiagnostics(startupSettingsManager, \"startup session lookup\"));\n\n\t// Decide the final runtime cwd before creating cwd-bound runtime services.\n\t// --session and --resume may select a session from another project, so project-local\n\t// settings, resources, provider registrations, and models must be resolved only after\n\t// the target session cwd is known. The startup-cwd settings manager is used only for\n\t// sessionDir lookup during session selection.\n\tconst envSessionDir = process.env[ENV_SESSION_DIR];\n\tconst sessionDir =\n\t\t(parsed.sessionDir ? normalizePath(parsed.sessionDir) : undefined) ??\n\t\t(envSessionDir ? expandTildePath(envSessionDir) : undefined) ??\n\t\tstartupSettingsManager.getSessionDir();\n\tlet sessionManager = await createSessionManager(parsed, cwd, sessionDir, startupSettingsManager);\n\tconst missingSessionCwdIssue = getMissingSessionCwdIssue(sessionManager, cwd);\n\tif (missingSessionCwdIssue) {\n\t\tif (appMode === \"interactive\") {\n\t\t\tconst selectedCwd = await promptForMissingSessionCwd(missingSessionCwdIssue, startupSettingsManager);\n\t\t\tif (!selectedCwd) {\n\t\t\t\tprocess.exit(0);\n\t\t\t}\n\t\t\tsessionManager = openSession(missingSessionCwdIssue.sessionFile!, sessionDir, selectedCwd);\n\t\t} else {\n\t\t\tconsole.error(chalk.red(new MissingSessionCwdError(missingSessionCwdIssue).message));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\tif (parsed.name !== undefined) {\n\t\tconst name = parsed.name.trim();\n\t\tif (!name) {\n\t\t\tconsole.error(chalk.red(\"Error: --name requires a non-empty value\"));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tsessionManager.appendSessionInfo(name);\n\t}\n\ttime(\"createSessionManager\");\n\n\tconst trustPromptMode: AppMode = parsed.help || parsed.listModels !== undefined ? \"print\" : appMode;\n\tconst projectTrustedForSession = await resolveProjectTrusted({\n\t\tcwd: sessionManager.getCwd(),\n\t\ttrustStore,\n\t\ttrustOverride: parsed.projectTrustOverride,\n\t\tappMode: trustPromptMode,\n\t\tsettingsManagerForPrompt: startupSettingsManager,\n\t});\n\n\tconst resolvedExtensionPaths = resolveCliPaths(cwd, parsed.extensions);\n\tconst resolvedSkillPaths = resolveCliPaths(cwd, parsed.skills);\n\tconst resolvedPromptTemplatePaths = resolveCliPaths(cwd, parsed.promptTemplates);\n\tconst resolvedThemePaths = resolveCliPaths(cwd, parsed.themes);\n\tconst authStorage = AuthStorage.create();\n\tconst createRuntime: CreateAgentSessionRuntimeFactory = async ({\n\t\tcwd,\n\t\tagentDir,\n\t\tsessionManager,\n\t\tsessionStartEvent,\n\t}) => {\n\t\tconst projectTrusted =\n\t\t\tcwd === sessionManager.getCwd()\n\t\t\t\t? projectTrustedForSession\n\t\t\t\t: (parsed.projectTrustOverride ?? (!hasProjectTrustInputs(cwd) || trustStore.get(cwd) === true));\n\t\tconst runtimeSettingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted });\n\t\tif (parsed.resourceProfileJson) {\n\t\t\tfor (const profileInput of parsed.resourceProfileJson) {\n\t\t\t\tconst { profiles, errors } = parseResourceProfileInput(profileInput);\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\treportDiagnostics(\n\t\t\t\t\t\terrors.map((message) => ({\n\t\t\t\t\t\t\ttype: \"warning\" as const,\n\t\t\t\t\t\t\tmessage: `--resource-profile-json ignored entry: ${message}`,\n\t\t\t\t\t\t})),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\truntimeSettingsManager.addInlineResourceProfileDefinitions(profiles);\n\t\t\t}\n\t\t}\n\t\tif (parsed.resourceProfiles && parsed.resourceProfiles.length > 0) {\n\t\t\truntimeSettingsManager.setRuntimeResourceProfiles(parsed.resourceProfiles);\n\t\t}\n\t\tconst services = await createAgentSessionServices({\n\t\t\tcwd,\n\t\t\tagentDir,\n\t\t\tauthStorage,\n\t\t\tsettingsManager: runtimeSettingsManager,\n\t\t\textensionFlagValues: parsed.unknownFlags,\n\t\t\tresourceLoaderOptions: {\n\t\t\t\tadditionalExtensionPaths: resolvedExtensionPaths,\n\t\t\t\tadditionalSkillPaths: resolvedSkillPaths,\n\t\t\t\tadditionalPromptTemplatePaths: resolvedPromptTemplatePaths,\n\t\t\t\tadditionalThemePaths: resolvedThemePaths,\n\t\t\t\tnoExtensions: parsed.noExtensions,\n\t\t\t\tnoSkills: parsed.noSkills,\n\t\t\t\tnoPromptTemplates: parsed.noPromptTemplates,\n\t\t\t\tnoThemes: parsed.noThemes,\n\t\t\t\tnoContextFiles: parsed.noContextFiles,\n\t\t\t\tsystemPrompt: parsed.systemPrompt,\n\t\t\t\tappendSystemPrompt: parsed.appendSystemPrompt,\n\t\t\t\textensionFactories: options?.extensionFactories,\n\t\t\t},\n\t\t});\n\t\tconst { settingsManager, modelRegistry, resourceLoader } = services;\n\t\tconst diagnostics: AgentSessionRuntimeDiagnostic[] = [\n\t\t\t...services.diagnostics,\n\t\t\t...collectSettingsDiagnostics(settingsManager, \"runtime creation\"),\n\t\t\t...resourceLoader.getExtensions().errors.map(({ path, error }) => ({\n\t\t\t\ttype: \"error\" as const,\n\t\t\t\tmessage: `Failed to load extension \"${path}\": ${error}`,\n\t\t\t})),\n\t\t];\n\n\t\tdiagnostics.push(\n\t\t\t...collectModelRouterConfigDiagnostics(settingsManager.getModelRouterSettings(), modelRegistry, agentDir).map(\n\t\t\t\t(message) => ({\n\t\t\t\t\ttype: \"warning\" as const,\n\t\t\t\t\tmessage,\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\n\t\tconst modelPatterns = parsed.models ?? settingsManager.getEnabledModels();\n\t\tconst scopedModels =\n\t\t\tmodelPatterns && modelPatterns.length > 0 ? await resolveModelScope(modelPatterns, modelRegistry) : [];\n\t\tconst {\n\t\t\toptions: sessionOptions,\n\t\t\tcliThinkingFromModel,\n\t\t\tdiagnostics: sessionOptionDiagnostics,\n\t\t} = buildSessionOptions(\n\t\t\tparsed,\n\t\t\tscopedModels,\n\t\t\tsessionManager.buildSessionContext().messages.length > 0,\n\t\t\tmodelRegistry,\n\t\t\tsettingsManager,\n\t\t\tcwd,\n\t\t);\n\t\tdiagnostics.push(...sessionOptionDiagnostics);\n\n\t\tif (parsed.apiKey) {\n\t\t\tif (!sessionOptions.model) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\tmessage: \"--api-key requires a model to be specified via --model, --provider/--model, or --models\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tauthStorage.setRuntimeApiKey(sessionOptions.model.provider, parsed.apiKey);\n\t\t\t}\n\t\t}\n\n\t\tconst created = await createAgentSessionFromServices({\n\t\t\tservices,\n\t\t\tsessionManager,\n\t\t\tsessionStartEvent,\n\t\t\tmodel: sessionOptions.model,\n\t\t\tthinkingLevel: sessionOptions.thinkingLevel,\n\t\t\tisExplicitModel: sessionOptions.isExplicitModel,\n\t\t\tisExplicitThinking: sessionOptions.isExplicitThinking,\n\t\t\tscopedModels: sessionOptions.scopedModels,\n\t\t\ttools: sessionOptions.tools,\n\t\t\texcludeTools: sessionOptions.excludeTools,\n\t\t\tnoTools: sessionOptions.noTools,\n\t\t\ttoolProfileFilter: settingsManager.getResourceProfileFilter(\"tools\"),\n\t\t\tcustomTools: sessionOptions.customTools,\n\t\t});\n\t\tconst cliThinkingOverride = parsed.thinking !== undefined || cliThinkingFromModel;\n\t\tif (created.session.model && cliThinkingOverride) {\n\t\t\tcreated.session.setThinkingLevel(created.session.thinkingLevel);\n\t\t}\n\n\t\treturn {\n\t\t\t...created,\n\t\t\tservices,\n\t\t\tdiagnostics,\n\t\t};\n\t};\n\ttime(\"createRuntime\");\n\tconst runtime = await createAgentSessionRuntime(createRuntime, {\n\t\tcwd: sessionManager.getCwd(),\n\t\tagentDir,\n\t\tsessionManager,\n\t});\n\ttime(\"createAgentSessionRuntime\");\n\tconst { services, session, modelFallbackMessage } = runtime;\n\tconst { settingsManager, modelRegistry, resourceLoader } = services;\n\tconfigureHttpDispatcher(settingsManager.getHttpIdleTimeoutMs());\n\n\tif (parsed.help) {\n\t\tconst extensionFlags = resourceLoader\n\t\t\t.getExtensions()\n\t\t\t.extensions.flatMap((extension) => Array.from(extension.flags.values()));\n\t\tprintHelp(extensionFlags);\n\t\tprocess.exit(0);\n\t}\n\n\tif (parsed.listModels !== undefined) {\n\t\tconst searchPattern = typeof parsed.listModels === \"string\" ? parsed.listModels : undefined;\n\t\tawait listModels(modelRegistry, searchPattern);\n\t\tprocess.exit(0);\n\t}\n\n\t// Read piped stdin content (if any) - skip for RPC mode which uses stdin for JSON-RPC\n\tlet stdinContent: string | undefined;\n\tif (appMode !== \"rpc\") {\n\t\tstdinContent = await readPipedStdin();\n\t\tif (stdinContent !== undefined && appMode === \"interactive\") {\n\t\t\tappMode = \"print\";\n\t\t}\n\t}\n\ttime(\"readPipedStdin\");\n\n\tconst { initialMessage, initialImages } = await prepareInitialMessage(\n\t\tparsed,\n\t\tsettingsManager.getImageAutoResize(),\n\t\tstdinContent,\n\t);\n\ttime(\"prepareInitialMessage\");\n\tconst { initTheme, stopThemeWatcher } = await import(\"./modes/interactive/theme/theme.ts\");\n\tinitTheme(settingsManager.getTheme(), appMode === \"interactive\");\n\ttime(\"initTheme\");\n\n\t// Show deprecation warnings in interactive mode\n\tif (appMode === \"interactive\" && deprecationWarnings.length > 0) {\n\t\tawait showDeprecationWarnings(deprecationWarnings);\n\t}\n\n\ttime(\"resolveModelScope\");\n\treportDiagnostics(runtime.diagnostics);\n\tif (runtime.diagnostics.some((diagnostic) => diagnostic.type === \"error\")) {\n\t\tprocess.exit(1);\n\t}\n\ttime(\"createAgentSession\");\n\n\tif (appMode !== \"interactive\" && !session.model) {\n\t\tconsole.error(chalk.red(formatNoModelsAvailableMessage()));\n\t\tprocess.exit(1);\n\t}\n\n\tconst startupBenchmark = isTruthyEnvFlag(process.env.PI_STARTUP_BENCHMARK);\n\tif (startupBenchmark && appMode !== \"interactive\") {\n\t\tconsole.error(chalk.red(\"Error: PI_STARTUP_BENCHMARK only supports interactive mode\"));\n\t\tprocess.exit(1);\n\t}\n\n\tif (appMode === \"rpc\") {\n\t\tprintTimings();\n\t\tconst { runRpcMode } = await import(\"./modes/rpc/rpc-mode.ts\");\n\t\tawait runRpcMode(runtime);\n\t} else if (appMode === \"interactive\") {\n\t\tconst { InteractiveMode } = await import(\"./modes/interactive/interactive-mode.ts\");\n\t\tconst interactiveMode = new InteractiveMode(runtime, {\n\t\t\tmigratedProviders,\n\t\t\tmodelFallbackMessage,\n\t\t\tinitialMessage,\n\t\t\tinitialImages,\n\t\t\tinitialMessages: parsed.messages,\n\t\t\tverbose: parsed.verbose,\n\t\t});\n\t\tif (startupBenchmark) {\n\t\t\tawait interactiveMode.init();\n\t\t\ttime(\"interactiveMode.init\");\n\t\t\tprintTimings();\n\t\t\tinteractiveMode.stop();\n\t\t\tstopThemeWatcher();\n\t\t\tif (process.stdout.writableLength > 0) {\n\t\t\t\tawait new Promise<void>((resolve) => process.stdout.once(\"drain\", resolve));\n\t\t\t}\n\t\t\tif (process.stderr.writableLength > 0) {\n\t\t\t\tawait new Promise<void>((resolve) => process.stderr.once(\"drain\", resolve));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tprintTimings();\n\t\tawait interactiveMode.run();\n\t} else {\n\t\tprintTimings();\n\t\tconst { runPrintMode } = await import(\"./modes/print-mode.ts\");\n\t\tconst exitCode = await runPrintMode(runtime, {\n\t\t\tmode: toPrintOutputMode(appMode),\n\t\t\tmessages: parsed.messages,\n\t\t\tinitialMessage,\n\t\t\tinitialImages,\n\t\t\tprintUsage: parsed.printUsage,\n\t\t});\n\t\tstopThemeWatcher();\n\t\trestoreStdout();\n\t\tif (exitCode !== 0) {\n\t\t\tprocess.exitCode = exitCode;\n\t\t}\n\t\treturn;\n\t}\n}\n"]}
package/dist/main.js CHANGED
@@ -642,6 +642,8 @@ export async function main(args, options) {
642
642
  sessionStartEvent,
643
643
  model: sessionOptions.model,
644
644
  thinkingLevel: sessionOptions.thinkingLevel,
645
+ isExplicitModel: sessionOptions.isExplicitModel,
646
+ isExplicitThinking: sessionOptions.isExplicitThinking,
645
647
  scopedModels: sessionOptions.scopedModels,
646
648
  tools: sessionOptions.tools,
647
649
  excludeTools: sessionOptions.excludeTools,