@bastani/atomic 0.9.5-alpha.9 → 0.9.5

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 (377) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/README.md +2 -2
  3. package/dist/builtin/cursor/CHANGELOG.md +12 -0
  4. package/dist/builtin/cursor/package.json +3 -3
  5. package/dist/builtin/cursor/src/model-mapper.ts +6 -3
  6. package/dist/builtin/intercom/CHANGELOG.md +37 -0
  7. package/dist/builtin/intercom/README.md +4 -2
  8. package/dist/builtin/intercom/broker/broker.ts +6 -114
  9. package/dist/builtin/intercom/broker/client.ts +29 -54
  10. package/dist/builtin/intercom/broker/delivered-message-cache.ts +44 -0
  11. package/dist/builtin/intercom/broker/pending-send-registry.ts +142 -0
  12. package/dist/builtin/intercom/broker/send-handler.ts +106 -0
  13. package/dist/builtin/intercom/broker/send-signature.ts +37 -0
  14. package/dist/builtin/intercom/foreground-detach-handoff.ts +136 -0
  15. package/dist/builtin/intercom/index-heavy.ts +49 -53
  16. package/dist/builtin/intercom/index.ts +278 -262
  17. package/dist/builtin/intercom/lazy-heavy-proxy.ts +114 -0
  18. package/dist/builtin/intercom/lazy-subagent-ack.ts +20 -0
  19. package/dist/builtin/intercom/lazy-tool-execution.ts +39 -0
  20. package/dist/builtin/intercom/lifecycle-lease.ts +51 -0
  21. package/dist/builtin/intercom/lifecycle.ts +37 -20
  22. package/dist/builtin/intercom/package.json +3 -6
  23. package/dist/builtin/intercom/reply-routing.ts +17 -0
  24. package/dist/builtin/intercom/subagent-relay.ts +58 -12
  25. package/dist/builtin/intercom/types.ts +3 -3
  26. package/dist/builtin/mcp/CHANGELOG.md +27 -0
  27. package/dist/builtin/mcp/OAUTH.md +1 -0
  28. package/dist/builtin/mcp/README.md +4 -0
  29. package/dist/builtin/mcp/apps-cancellation.ts +32 -0
  30. package/dist/builtin/mcp/call-tool-result.ts +9 -0
  31. package/dist/builtin/mcp/caller-wait.ts +50 -0
  32. package/dist/builtin/mcp/command-registration.ts +82 -0
  33. package/dist/builtin/mcp/direct-tool-executor.ts +279 -0
  34. package/dist/builtin/mcp/direct-tools.ts +6 -239
  35. package/dist/builtin/mcp/host-html-template.ts +4 -2
  36. package/dist/builtin/mcp/index.ts +258 -250
  37. package/dist/builtin/mcp/init.ts +96 -87
  38. package/dist/builtin/mcp/mcp-auth-flow.ts +247 -237
  39. package/dist/builtin/mcp/mcp-callback-server.ts +89 -68
  40. package/dist/builtin/mcp/mcp-oauth-provider.ts +23 -1
  41. package/dist/builtin/mcp/metadata-hydration.ts +21 -6
  42. package/dist/builtin/mcp/package.json +3 -3
  43. package/dist/builtin/mcp/proxy-auth.ts +4 -4
  44. package/dist/builtin/mcp/proxy-call.ts +111 -40
  45. package/dist/builtin/mcp/proxy-connect.ts +35 -15
  46. package/dist/builtin/mcp/proxy-info-modes.ts +49 -19
  47. package/dist/builtin/mcp/proxy-modes.ts +3 -3
  48. package/dist/builtin/mcp/session-cleanup-barrier.ts +43 -0
  49. package/dist/builtin/mcp/state-lease.ts +12 -0
  50. package/dist/builtin/mcp/ui-server.ts +8 -8
  51. package/dist/builtin/mcp/ui-session.ts +9 -18
  52. package/dist/builtin/subagents/CHANGELOG.md +66 -0
  53. package/dist/builtin/subagents/README.md +30 -29
  54. package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
  55. package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
  56. package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
  57. package/dist/builtin/subagents/agents/codebase-online-researcher.md +2 -2
  58. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
  59. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
  60. package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
  61. package/dist/builtin/subagents/agents/debugger.md +2 -2
  62. package/dist/builtin/subagents/agents/worker.md +2 -2
  63. package/dist/builtin/subagents/package.json +5 -5
  64. package/dist/builtin/subagents/prompts/review-loop.md +1 -1
  65. package/dist/builtin/subagents/skills/subagent/SKILL.md +37 -36
  66. package/dist/builtin/subagents/src/extension/api-lifecycle.ts +64 -0
  67. package/dist/builtin/subagents/src/extension/fanout-child.ts +44 -51
  68. package/dist/builtin/subagents/src/extension/index.ts +287 -307
  69. package/dist/builtin/subagents/src/extension/prompt-guidance.ts +7 -10
  70. package/dist/builtin/subagents/src/extension/schemas.ts +22 -5
  71. package/dist/builtin/subagents/src/extension/tool-description.ts +29 -0
  72. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +13 -2
  73. package/dist/builtin/subagents/src/intercom/result-intercom.ts +4 -4
  74. package/dist/builtin/subagents/src/runs/background/async-event-journal.ts +106 -0
  75. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +3 -2
  76. package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +13 -2
  77. package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +3 -0
  78. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +1 -1
  79. package/dist/builtin/subagents/src/runs/background/completion-claims.ts +189 -0
  80. package/dist/builtin/subagents/src/runs/background/completion-dedupe.ts +44 -7
  81. package/dist/builtin/subagents/src/runs/background/completion-notification.ts +34 -0
  82. package/dist/builtin/subagents/src/runs/background/notify.ts +72 -22
  83. package/dist/builtin/subagents/src/runs/background/result-delivery-processor.ts +232 -0
  84. package/dist/builtin/subagents/src/runs/background/result-file-claims.ts +151 -0
  85. package/dist/builtin/subagents/src/runs/background/result-quarantine.ts +72 -0
  86. package/dist/builtin/subagents/src/runs/background/result-retry-scheduler.ts +48 -0
  87. package/dist/builtin/subagents/src/runs/background/result-status.ts +81 -0
  88. package/dist/builtin/subagents/src/runs/background/result-watcher-data.ts +59 -0
  89. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +172 -238
  90. package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +3 -2
  91. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +73 -14
  92. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +17 -13
  93. package/dist/builtin/subagents/src/runs/background/top-level-async.ts +1 -2
  94. package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +1 -0
  95. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +1 -0
  96. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +1 -0
  97. package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +7 -9
  98. package/dist/builtin/subagents/src/runs/foreground/chain-execution-types.ts +3 -13
  99. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +2 -15
  100. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-finalize.ts +2 -2
  101. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +53 -44
  102. package/dist/builtin/subagents/src/runs/foreground/execution-detach-reservations.ts +48 -0
  103. package/dist/builtin/subagents/src/runs/foreground/execution-detach-route.ts +17 -0
  104. package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +39 -26
  105. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +3 -1
  106. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-chain.ts +3 -51
  107. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +6 -11
  108. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-input.ts +11 -2
  109. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +2 -0
  110. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +5 -112
  111. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-resume.ts +2 -0
  112. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +68 -118
  113. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +79 -7
  114. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +2 -2
  115. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +3 -0
  116. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +2 -2
  117. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +27 -11
  118. package/dist/builtin/subagents/src/shared/artifacts.ts +22 -11
  119. package/dist/builtin/subagents/src/shared/event-jsonl-writer.ts +294 -0
  120. package/dist/builtin/subagents/src/shared/exclusive-file-publication.ts +44 -0
  121. package/dist/builtin/subagents/src/shared/jsonl-writer.ts +1 -0
  122. package/dist/builtin/subagents/src/shared/model-info.ts +2 -2
  123. package/dist/builtin/subagents/src/shared/settings.ts +20 -10
  124. package/dist/builtin/subagents/src/shared/types-async.ts +3 -1
  125. package/dist/builtin/subagents/src/shared/types-config.ts +2 -0
  126. package/dist/builtin/subagents/src/shared/types-runtime.ts +3 -2
  127. package/dist/builtin/subagents/src/slash/slash-commands.ts +9 -12
  128. package/dist/builtin/subagents/src/slash/slash-live-state.ts +63 -32
  129. package/dist/builtin/subagents/src/tui/render-result.ts +7 -0
  130. package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -0
  131. package/dist/builtin/subagents/src/tui/render-widget.ts +128 -89
  132. package/dist/builtin/web-access/CHANGELOG.md +28 -0
  133. package/dist/builtin/web-access/README.md +4 -0
  134. package/dist/builtin/web-access/content-tools.ts +8 -3
  135. package/dist/builtin/web-access/index-heavy.ts +1 -1
  136. package/dist/builtin/web-access/index.ts +190 -46
  137. package/dist/builtin/web-access/lifecycle-lease.ts +38 -0
  138. package/dist/builtin/web-access/package.json +2 -2
  139. package/dist/builtin/web-access/result-renderers.ts +1 -1
  140. package/dist/builtin/web-access/summary-review.ts +1 -1
  141. package/dist/builtin/web-access/web-search-activity.ts +1 -1
  142. package/dist/builtin/web-access/web-search-return.ts +7 -0
  143. package/dist/builtin/web-access/web-search-summary.ts +1 -1
  144. package/dist/builtin/web-access/web-search-tool.ts +4 -2
  145. package/dist/builtin/workflows/CHANGELOG.md +68 -0
  146. package/dist/builtin/workflows/README.md +4 -4
  147. package/dist/builtin/workflows/builtin/deep-research-codebase-utils.ts +27 -4
  148. package/dist/builtin/workflows/builtin/goal-runner.ts +47 -26
  149. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +10 -0
  150. package/dist/builtin/workflows/builtin/ralph-core.ts +9 -9
  151. package/dist/builtin/workflows/builtin/ralph-models.ts +71 -36
  152. package/dist/builtin/workflows/builtin/ralph-runner.ts +20 -31
  153. package/dist/builtin/workflows/package.json +2 -2
  154. package/dist/builtin/workflows/src/durable/stage-primitive.ts +2 -1
  155. package/dist/builtin/workflows/src/engine/primitives/task.ts +2 -1
  156. package/dist/builtin/workflows/src/extension/workflow-prompts.ts +17 -22
  157. package/dist/builtin/workflows/src/extension/workflow-schema.ts +15 -2
  158. package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +1 -2
  159. package/dist/builtin/workflows/src/runs/shared/model-fallback-candidates.ts +1 -1
  160. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +2 -2
  161. package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +2 -2
  162. package/dist/builtin/workflows/src/shared/resume-continuation.ts +2 -0
  163. package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +1 -23
  164. package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +54 -9
  165. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +5 -2
  166. package/dist/cli/args.d.ts.map +1 -1
  167. package/dist/cli/args.js +2 -2
  168. package/dist/cli/args.js.map +1 -1
  169. package/dist/core/agent-session-events.d.ts.map +1 -1
  170. package/dist/core/agent-session-events.js +2 -1
  171. package/dist/core/agent-session-events.js.map +1 -1
  172. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  173. package/dist/core/agent-session-message-queue.js +1 -1
  174. package/dist/core/agent-session-message-queue.js.map +1 -1
  175. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  176. package/dist/core/agent-session-prompt.js +1 -1
  177. package/dist/core/agent-session-prompt.js.map +1 -1
  178. package/dist/core/agent-session-retry.d.ts.map +1 -1
  179. package/dist/core/agent-session-retry.js +1 -1
  180. package/dist/core/agent-session-retry.js.map +1 -1
  181. package/dist/core/agent-session-services.d.ts.map +1 -1
  182. package/dist/core/agent-session-services.js +3 -2
  183. package/dist/core/agent-session-services.js.map +1 -1
  184. package/dist/core/agent-session-types.d.ts.map +1 -1
  185. package/dist/core/agent-session-types.js +1 -1
  186. package/dist/core/agent-session-types.js.map +1 -1
  187. package/dist/core/anthropic-thinking-guard.d.ts +5 -5
  188. package/dist/core/anthropic-thinking-guard.d.ts.map +1 -1
  189. package/dist/core/anthropic-thinking-guard.js +42 -32
  190. package/dist/core/anthropic-thinking-guard.js.map +1 -1
  191. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  192. package/dist/core/atomic-guide-command.js +15 -15
  193. package/dist/core/atomic-guide-command.js.map +1 -1
  194. package/dist/core/auth-storage.d.ts.map +1 -1
  195. package/dist/core/auth-storage.js +14 -9
  196. package/dist/core/auth-storage.js.map +1 -1
  197. package/dist/core/compaction/compaction.d.ts +2 -0
  198. package/dist/core/compaction/compaction.d.ts.map +1 -1
  199. package/dist/core/compaction/compaction.js +79 -46
  200. package/dist/core/compaction/compaction.js.map +1 -1
  201. package/dist/core/compaction/context-assistant-turns.d.ts +42 -0
  202. package/dist/core/compaction/context-assistant-turns.d.ts.map +1 -0
  203. package/dist/core/compaction/context-assistant-turns.js +87 -0
  204. package/dist/core/compaction/context-assistant-turns.js.map +1 -0
  205. package/dist/core/compaction/context-compaction-critical.d.ts +1 -1
  206. package/dist/core/compaction/context-compaction-critical.d.ts.map +1 -1
  207. package/dist/core/compaction/context-compaction-critical.js +2 -2
  208. package/dist/core/compaction/context-compaction-critical.js.map +1 -1
  209. package/dist/core/compaction/context-compaction-eviction-alternates.d.ts +18 -0
  210. package/dist/core/compaction/context-compaction-eviction-alternates.d.ts.map +1 -0
  211. package/dist/core/compaction/context-compaction-eviction-alternates.js +186 -0
  212. package/dist/core/compaction/context-compaction-eviction-alternates.js.map +1 -0
  213. package/dist/core/compaction/context-compaction-eviction.d.ts +10 -2
  214. package/dist/core/compaction/context-compaction-eviction.d.ts.map +1 -1
  215. package/dist/core/compaction/context-compaction-eviction.js +196 -146
  216. package/dist/core/compaction/context-compaction-eviction.js.map +1 -1
  217. package/dist/core/compaction/context-compaction-prompt.d.ts.map +1 -1
  218. package/dist/core/compaction/context-compaction-prompt.js +1 -1
  219. package/dist/core/compaction/context-compaction-prompt.js.map +1 -1
  220. package/dist/core/compaction/context-compaction-types.d.ts +2 -0
  221. package/dist/core/compaction/context-compaction-types.d.ts.map +1 -1
  222. package/dist/core/compaction/context-compaction-types.js.map +1 -1
  223. package/dist/core/compaction/context-deletion-application.d.ts +5 -4
  224. package/dist/core/compaction/context-deletion-application.d.ts.map +1 -1
  225. package/dist/core/compaction/context-deletion-application.js +17 -15
  226. package/dist/core/compaction/context-deletion-application.js.map +1 -1
  227. package/dist/core/compaction/context-deletion-store.d.ts.map +1 -1
  228. package/dist/core/compaction/context-deletion-store.js +1 -1
  229. package/dist/core/compaction/context-deletion-store.js.map +1 -1
  230. package/dist/core/compaction/context-deletion-targets.d.ts +2 -1
  231. package/dist/core/compaction/context-deletion-targets.d.ts.map +1 -1
  232. package/dist/core/compaction/context-deletion-targets.js +36 -31
  233. package/dist/core/compaction/context-deletion-targets.js.map +1 -1
  234. package/dist/core/compaction/context-deletion-tool-helpers.d.ts.map +1 -1
  235. package/dist/core/compaction/context-deletion-tool-helpers.js +116 -43
  236. package/dist/core/compaction/context-deletion-tool-helpers.js.map +1 -1
  237. package/dist/core/compaction/context-transcript-analysis.d.ts +1 -2
  238. package/dist/core/compaction/context-transcript-analysis.d.ts.map +1 -1
  239. package/dist/core/compaction/context-transcript-analysis.js +33 -37
  240. package/dist/core/compaction/context-transcript-analysis.js.map +1 -1
  241. package/dist/core/copilot-model-synthesis.d.ts.map +1 -1
  242. package/dist/core/copilot-model-synthesis.js +3 -1
  243. package/dist/core/copilot-model-synthesis.js.map +1 -1
  244. package/dist/core/extensions/provider-types.d.ts +2 -7
  245. package/dist/core/extensions/provider-types.d.ts.map +1 -1
  246. package/dist/core/extensions/provider-types.js.map +1 -1
  247. package/dist/core/messages.d.ts +9 -0
  248. package/dist/core/messages.d.ts.map +1 -1
  249. package/dist/core/messages.js +100 -18
  250. package/dist/core/messages.js.map +1 -1
  251. package/dist/core/model-registry-builtins.d.ts.map +1 -1
  252. package/dist/core/model-registry-builtins.js +5 -0
  253. package/dist/core/model-registry-builtins.js.map +1 -1
  254. package/dist/core/model-registry-custom-loader.d.ts.map +1 -1
  255. package/dist/core/model-registry-custom-loader.js +49 -8
  256. package/dist/core/model-registry-custom-loader.js.map +1 -1
  257. package/dist/core/model-registry-dynamic.d.ts.map +1 -1
  258. package/dist/core/model-registry-dynamic.js +11 -5
  259. package/dist/core/model-registry-dynamic.js.map +1 -1
  260. package/dist/core/model-registry-loader.d.ts.map +1 -1
  261. package/dist/core/model-registry-loader.js +8 -0
  262. package/dist/core/model-registry-loader.js.map +1 -1
  263. package/dist/core/model-registry-schemas.d.ts +93 -8
  264. package/dist/core/model-registry-schemas.d.ts.map +1 -1
  265. package/dist/core/model-registry-schemas.js +24 -12
  266. package/dist/core/model-registry-schemas.js.map +1 -1
  267. package/dist/core/model-registry-types.d.ts +7 -6
  268. package/dist/core/model-registry-types.d.ts.map +1 -1
  269. package/dist/core/model-registry-types.js.map +1 -1
  270. package/dist/core/model-registry.d.ts +5 -0
  271. package/dist/core/model-registry.d.ts.map +1 -1
  272. package/dist/core/model-registry.js +23 -0
  273. package/dist/core/model-registry.js.map +1 -1
  274. package/dist/core/model-resolver-cli.d.ts.map +1 -1
  275. package/dist/core/model-resolver-cli.js +33 -5
  276. package/dist/core/model-resolver-cli.js.map +1 -1
  277. package/dist/core/model-resolver-initial.d.ts +6 -1
  278. package/dist/core/model-resolver-initial.d.ts.map +1 -1
  279. package/dist/core/model-resolver-initial.js +12 -7
  280. package/dist/core/model-resolver-initial.js.map +1 -1
  281. package/dist/core/model-resolver.d.ts +1 -1
  282. package/dist/core/model-resolver.d.ts.map +1 -1
  283. package/dist/core/model-resolver.js +1 -1
  284. package/dist/core/model-resolver.js.map +1 -1
  285. package/dist/core/resource-loader-context-files.d.ts +1 -0
  286. package/dist/core/resource-loader-context-files.d.ts.map +1 -1
  287. package/dist/core/resource-loader-context-files.js +13 -10
  288. package/dist/core/resource-loader-context-files.js.map +1 -1
  289. package/dist/core/sdk.d.ts.map +1 -1
  290. package/dist/core/sdk.js +2 -2
  291. package/dist/core/sdk.js.map +1 -1
  292. package/dist/core/session-entry-normalization.d.ts +7 -0
  293. package/dist/core/session-entry-normalization.d.ts.map +1 -0
  294. package/dist/core/session-entry-normalization.js +14 -0
  295. package/dist/core/session-entry-normalization.js.map +1 -0
  296. package/dist/core/session-manager-core.d.ts.map +1 -1
  297. package/dist/core/session-manager-core.js +1 -0
  298. package/dist/core/session-manager-core.js.map +1 -1
  299. package/dist/core/session-manager-history.d.ts +2 -2
  300. package/dist/core/session-manager-history.d.ts.map +1 -1
  301. package/dist/core/session-manager-history.js +127 -108
  302. package/dist/core/session-manager-history.js.map +1 -1
  303. package/dist/core/settings-manager-basic-accessors.d.ts +2 -2
  304. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  305. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  306. package/dist/core/settings-types.d.ts +1 -1
  307. package/dist/core/settings-types.d.ts.map +1 -1
  308. package/dist/core/settings-types.js.map +1 -1
  309. package/dist/core/tools/bash.d.ts.map +1 -1
  310. package/dist/core/tools/bash.js +11 -8
  311. package/dist/core/tools/bash.js.map +1 -1
  312. package/dist/main-session.d.ts.map +1 -1
  313. package/dist/main-session.js +1 -0
  314. package/dist/main-session.js.map +1 -1
  315. package/dist/modes/interactive/components/settings-selector-options.d.ts.map +1 -1
  316. package/dist/modes/interactive/components/settings-selector-options.js +3 -1
  317. package/dist/modes/interactive/components/settings-selector-options.js.map +1 -1
  318. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  319. package/dist/modes/interactive/components/thinking-selector.js +2 -1
  320. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  321. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  322. package/dist/modes/interactive/interactive-agent-events.js +2 -1
  323. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  324. package/dist/modes/interactive/interactive-child-ordering.d.ts +7 -0
  325. package/dist/modes/interactive/interactive-child-ordering.d.ts.map +1 -0
  326. package/dist/modes/interactive/interactive-child-ordering.js +26 -0
  327. package/dist/modes/interactive/interactive-child-ordering.js.map +1 -0
  328. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  329. package/dist/modes/interactive/interactive-deferred-startup.js +2 -2
  330. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  331. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
  332. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  333. package/dist/modes/interactive/interactive-mode-deps.js +1 -1
  334. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  335. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  336. package/dist/modes/interactive/interactive-model-routing.js +8 -3
  337. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  338. package/dist/modes/interactive/theme/theme-class.d.ts +1 -1
  339. package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
  340. package/dist/modes/interactive/theme/theme-class.js +2 -0
  341. package/dist/modes/interactive/theme/theme-class.js.map +1 -1
  342. package/dist/utils/clipboard-image.d.ts.map +1 -1
  343. package/dist/utils/clipboard-image.js +3 -0
  344. package/dist/utils/clipboard-image.js.map +1 -1
  345. package/docs/changelog.mdx +19 -0
  346. package/docs/compaction.md +55 -48
  347. package/docs/custom-provider.md +18 -5
  348. package/docs/extensions.md +7 -3
  349. package/docs/models.md +69 -10
  350. package/docs/providers.md +1 -1
  351. package/docs/quickstart.md +11 -7
  352. package/docs/rpc.md +2 -2
  353. package/docs/sdk.md +1 -1
  354. package/docs/settings.md +3 -3
  355. package/docs/subagents.md +36 -5
  356. package/docs/tools.md +1 -1
  357. package/docs/usage.md +4 -2
  358. package/docs/workflows.md +26 -43
  359. package/examples/extensions/preset.ts +2 -2
  360. package/examples/extensions/subagent/README.md +2 -2
  361. package/examples/extensions/subagent/index.ts +1 -2
  362. package/examples/extensions/subagent/schemas.ts +4 -1
  363. package/examples/sdk/12-full-control.ts +1 -1
  364. package/npm-shrinkwrap.json +48 -51
  365. package/package.json +7 -7
  366. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-behavior.ts +0 -75
  367. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-component.ts +0 -202
  368. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-edit.ts +0 -97
  369. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-editor.ts +0 -160
  370. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-frame.ts +0 -72
  371. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-render-modes.ts +0 -161
  372. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-render-selectors.ts +0 -203
  373. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-selectors.ts +0 -234
  374. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-state.ts +0 -103
  375. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-types.ts +0 -29
  376. package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +0 -9
  377. package/dist/builtin/subagents/src/runs/foreground/chain-execution-clarify.ts +0 -117
@@ -1 +1 @@
1
- {"version":3,"file":"agent-session-prompt.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-prompt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAK9E,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,wBAAsB,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA0KrG;AAGD,wBAAsB,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,GAAG,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAShH;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzE;AAGD,wBAAsB,4BAA4B,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAQpF;AAED;;GAEG;AAEH,wBAAsB,8BAA8B,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgCvG;AAED;;GAEG;AAEH,wBAAsB,2BAA2B,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBpG;AAED;;;;GAIG;AAEH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAwB5E;AAED;;;;;;;GAOG;AAEH,wBAAsB,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAWpG;AAED;;;;;;GAMG;AAEH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAWvG;AAED;;GAEG;AAEH,wBAAsB,eAAe,CAAC,IAAI,EAAE,YAAY,EACvD,OAAO,EAAE,MAAM,GAAG,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,EAChD,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,GAAG,UAAU,CAAA;CAAE,GAC5C,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;;;GAIG;AAEH,eAAO,MAAM,yBAAyB;;;;;;;;;;;CAWrC,CAAC","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport { ATOMIC_GUIDE_COMMAND_NAME, ATOMIC_GUIDE_HELP_CHOICES, atomicGuideModeForChoice, getAtomicGuideMessage, isAtomicGuideHelpChoice, normalizeAtomicGuideMode } from \"./atomic-guide-command.ts\";\nimport { formatAuthStorageLoadFailedMessage, formatNoApiKeyFoundMessage, formatNoModelSelectedMessage, formatUnresolvedModelMessage } from \"./auth-guidance.ts\";\nimport { expandPromptTemplate } from \"./prompt-templates.ts\";\nimport { stripFrontmatter } from \"../utils/frontmatter.ts\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\nimport type { PromptOptions } from \"./agent-session-types.ts\";\n\nexport async function prompt(this: AgentSession, text: string, options?: PromptOptions): Promise<void> {\n\tconst expandPromptTemplates = options?.expandPromptTemplates ?? true;\n\tconst preflightResult = options?.preflightResult;\n\tlet messages: AgentMessage[] | undefined;\n\n\ttry {\n\t\t// Handle slash commands first (execute immediately, even during streaming).\n\t\t// Builtin and extension commands manage their own LLM interaction via custom messages.\n\t\tif (expandPromptTemplates && text.startsWith(\"/\")) {\n\t\t\tconst handledBuiltin = await this._tryExecuteBuiltinSlashCommand(text);\n\t\t\tif (handledBuiltin) {\n\t\t\t\tpreflightResult?.(true);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst handledExtension = await this._tryExecuteExtensionCommand(text);\n\t\t\tif (handledExtension) {\n\t\t\t\tpreflightResult?.(true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Emit input event for extension interception (before skill/template expansion)\n\t\tlet currentText = text;\n\t\tlet currentImages = options?.images;\n\t\tif (this._extensionRunner.hasHandlers(\"input\")) {\n\t\t\tconst inputResult = await this._extensionRunner.emitInput(\n\t\t\t\tcurrentText,\n\t\t\t\tcurrentImages,\n\t\t\t\toptions?.source ?? \"interactive\",\n\t\t\t\tthis.isStreaming ? options?.streamingBehavior : undefined,\n\t\t\t);\n\t\t\tif (inputResult.action === \"handled\") {\n\t\t\t\tpreflightResult?.(true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (inputResult.action === \"transform\") {\n\t\t\t\tcurrentText = inputResult.text;\n\t\t\t\tcurrentImages = inputResult.images ?? currentImages;\n\t\t\t}\n\t\t}\n\n\t\t// Expand skill commands (/skill:name args) and prompt templates (/template args)\n\t\tlet expandedText = currentText;\n\t\tif (expandPromptTemplates) {\n\t\t\texpandedText = this._expandSkillCommand(expandedText);\n\t\t\texpandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);\n\t\t}\n\n\t\t// If streaming, queue via steer() or followUp() based on option\n\t\tif (this.isStreaming) {\n\t\t\tif (!options?.streamingBehavior) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (options.streamingBehavior === \"followUp\") {\n\t\t\t\tawait this._queueFollowUp(expandedText, currentImages);\n\t\t\t} else {\n\t\t\t\tawait this._queueSteer(expandedText, currentImages);\n\t\t\t}\n\t\t\tpreflightResult?.(true);\n\t\t\treturn;\n\t\t}\n\n\t\t// Flush any pending bash messages before the new prompt\n\t\tthis._flushPendingBashMessages();\n\n\t\t// Validate model\n\t\tif (!this.model) {\n\t\t\tthrow new Error(formatNoModelSelectedMessage());\n\t\t}\n\n\t\t// Defensive guard: a model that never resolved to a real provider\n\t\t// (for example an unknown/unresolved model id that reached this path\n\t\t// as a bare string) has no `provider`, which would otherwise fail deep\n\t\t// in auth resolution as the confusing \"No API key found for undefined\".\n\t\t// Surface a clear, accurate \"unknown model\" error instead.\n\t\tconst resolvedProvider = (this.model as { provider?: unknown }).provider;\n\t\tif (typeof resolvedProvider !== \"string\" || resolvedProvider.length === 0) {\n\t\t\tthrow new Error(formatUnresolvedModelMessage(this.model));\n\t\t}\n\n\t\tif (!this._modelRegistry.hasConfiguredAuth(this.model)) {\n\t\t\t// A failed credential-store load (for example auth.json briefly locked\n\t\t\t// by a concurrent process, or invalid JSON) leaves an empty in-memory\n\t\t\t// credential set. That would otherwise be misreported here as\n\t\t\t// \"No API key found\" even though the credentials exist on disk. Surface\n\t\t\t// the real load failure instead so configured providers are not falsely\n\t\t\t// reported as unauthenticated (issue #1431).\n\t\t\tconst authLoadError = this._modelRegistry.authStorage.getLoadError();\n\t\t\tif (authLoadError) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tformatAuthStorageLoadFailedMessage(this.model.provider, authLoadError),\n\t\t\t\t\t{ cause: authLoadError },\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst isOAuth = this._modelRegistry.isUsingOAuth(this.model);\n\t\t\tif (isOAuth) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Authentication failed for \"${this.model.provider}\". ` +\n\t\t\t\t\t\t`Credentials may have expired or network is unavailable. ` +\n\t\t\t\t\t\t`Run '/login ${this.model.provider}' to re-authenticate.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new Error(formatNoApiKeyFoundMessage(this.model.provider));\n\t\t}\n\n\t\t// Check if we need to compact before sending (catches aborted responses)\n\t\tconst lastAssistant = this._findLastAssistantMessage();\n\t\tif (lastAssistant) {\n\t\t\tawait this._checkCompaction(lastAssistant, false);\n\t\t}\n\n\t\t// Build messages array (custom message if any, then user message)\n\t\tmessages = [];\n\n\t\t// Add user message\n\t\tconst userContent: (TextContent | ImageContent)[] = [{ type: \"text\", text: expandedText }];\n\t\tif (currentImages) {\n\t\t\tuserContent.push(...currentImages);\n\t\t}\n\t\tmessages.push({\n\t\t\trole: \"user\",\n\t\t\tcontent: userContent,\n\t\t\ttimestamp: Date.now(),\n\t\t});\n\n\t\t// Inject any pending \"nextTurn\" messages as context alongside the user message\n\t\tfor (const msg of this._pendingNextTurnMessages) {\n\t\t\tmessages.push(msg);\n\t\t}\n\t\tthis._pendingNextTurnMessages = [];\n\n\t\t// Emit before_agent_start extension event\n\t\tconst result = await this._extensionRunner.emitBeforeAgentStart(\n\t\t\texpandedText,\n\t\t\tcurrentImages,\n\t\t\tthis._baseSystemPrompt,\n\t\t\tthis._baseSystemPromptOptions,\n\t\t);\n\t\t// Add all custom messages from extensions\n\t\tif (result?.messages) {\n\t\t\tfor (const msg of result.messages) {\n\t\t\t\tmessages.push({\n\t\t\t\t\trole: \"custom\",\n\t\t\t\t\tcustomType: msg.customType,\n\t\t\t\t\tcontent: msg.content,\n\t\t\t\t\tdisplay: msg.display,\n\t\t\t\t\tdetails: msg.details,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\t// Apply extension-modified system prompt, or reset to base\n\t\tif (result?.systemPrompt !== undefined) {\n\t\t\tthis._systemPromptOverride = result.systemPrompt;\n\t\t\tthis.agent.state.systemPrompt = result.systemPrompt;\n\t\t} else {\n\t\t\t// Ensure we're using the base prompt (in case previous turn had modifications)\n\t\t\tthis._systemPromptOverride = undefined;\n\t\t\tthis.agent.state.systemPrompt = this._baseSystemPrompt;\n\t\t}\n\t} catch (error) {\n\t\tpreflightResult?.(false);\n\t\tthrow error;\n\t}\n\n\tpreflightResult?.(true);\n\tawait this._runAgentPrompt(messages);\n}\n\n\nexport async function _runAgentPrompt(this: AgentSession, messages: AgentMessage | AgentMessage[]): Promise<void> {\n\ttry {\n\t\tawait this.agent.prompt(messages);\n\t\tawait this.waitForRetry();\n\t\tawait this._continueQueuedAgentMessages();\n\t\tawait this._awaitPendingPostCompactionContinuation();\n\t} finally {\n\t\tthis._systemPromptOverride = undefined;\n\t}\n}\n\nexport async function _runAgentContinue(this: AgentSession): Promise<void> {\n\tawait this.agent.continue();\n\tawait this.waitForRetry();\n\tawait this._continueQueuedAgentMessages();\n}\n\n\nexport async function _continueQueuedAgentMessages(this: AgentSession): Promise<void> {\n\tawait this._agentEventQueue;\n\n\twhile (this.agent.hasQueuedMessages()) {\n\t\tawait this.agent.continue();\n\t\tawait this.waitForRetry();\n\t\tawait this._agentEventQueue;\n\t}\n}\n\n/**\n * Try to execute a built-in slash command. Returns true if command was found and executed.\n */\n\nexport async function _tryExecuteBuiltinSlashCommand(this: AgentSession, text: string): Promise<boolean> {\n\tconst spaceIndex = text.indexOf(\" \");\n\tconst commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);\n\tif (commandName !== ATOMIC_GUIDE_COMMAND_NAME) return false;\n\n\tconst args = spaceIndex === -1 ? \"\" : text.slice(spaceIndex + 1);\n\tconst mode = normalizeAtomicGuideMode(args);\n\tif (mode === \"help\" && this._extensionUIContext) {\n\t\tconst choice = await this._extensionUIContext.select(\"Atomic. Select where to start:\", [\n\t\t\t...ATOMIC_GUIDE_HELP_CHOICES,\n\t\t]);\n\t\tif (!choice || !isAtomicGuideHelpChoice(choice)) return true;\n\t\tawait this.sendCustomMessage(\n\t\t\t{\n\t\t\t\tcustomType: \"atomic\",\n\t\t\t\tcontent: getAtomicGuideMessage(atomicGuideModeForChoice(choice), this._cwd),\n\t\t\t\tdisplay: true,\n\t\t\t},\n\t\t\t{ triggerTurn: false },\n\t\t);\n\t\treturn true;\n\t}\n\n\tawait this.sendCustomMessage(\n\t\t{\n\t\t\tcustomType: \"atomic\",\n\t\t\tcontent: getAtomicGuideMessage(mode, this._cwd),\n\t\t\tdisplay: true,\n\t\t},\n\t\t{ triggerTurn: false },\n\t);\n\treturn true;\n}\n\n/**\n * Try to execute an extension command. Returns true if command was found and executed.\n */\n\nexport async function _tryExecuteExtensionCommand(this: AgentSession, text: string): Promise<boolean> {\n\t// Parse command name and args\n\tconst spaceIndex = text.indexOf(\" \");\n\tconst commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);\n\tconst args = spaceIndex === -1 ? \"\" : text.slice(spaceIndex + 1);\n\n\tconst command = this._extensionRunner.getCommand(commandName);\n\tif (!command) return false;\n\n\t// Get command context from extension runner (includes session control methods)\n\tconst ctx = this._extensionRunner.createCommandContext();\n\n\ttry {\n\t\tawait command.handler(args, ctx);\n\t\treturn true;\n\t} catch (err) {\n\t\t// Emit error via extension runner\n\t\tthis._extensionRunner.emitError({\n\t\t\textensionPath: `command:${commandName}`,\n\t\t\tevent: \"command\",\n\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t});\n\t\treturn true;\n\t}\n}\n\n/**\n * Expand skill commands (/skill:name args) to their full content.\n * Returns the expanded text, or the original text if not a skill command or skill not found.\n * Emits errors via extension runner if file read fails.\n */\n\nexport function _expandSkillCommand(this: AgentSession, text: string): string {\n\tif (!text.startsWith(\"/skill:\")) return text;\n\n\tconst spaceIndex = text.indexOf(\" \");\n\tconst skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);\n\tconst args = spaceIndex === -1 ? \"\" : text.slice(spaceIndex + 1).trim();\n\n\tconst skill = this.resourceLoader.getSkills().skills.find((s) => s.name === skillName);\n\tif (!skill) return text; // Unknown skill, pass through\n\n\ttry {\n\t\tconst content = readFileSync(skill.filePath, \"utf-8\");\n\t\tconst body = stripFrontmatter(content).trim();\n\t\tconst skillBlock = `<skill name=\"${skill.name}\" location=\"${skill.filePath}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n\t\treturn args ? `${skillBlock}\\n\\n${args}` : skillBlock;\n\t} catch (err) {\n\t\t// Emit error like extension commands do\n\t\tthis._extensionRunner.emitError({\n\t\t\textensionPath: skill.filePath,\n\t\t\tevent: \"skill_expansion\",\n\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t});\n\t\treturn text; // Return original on error\n\t}\n}\n\n/**\n * Queue a steering message while the agent is running.\n * Delivered after the current assistant turn finishes executing its tool calls,\n * before the next LLM call.\n * Expands skill commands and prompt templates. Errors on extension commands.\n * @param images Optional image attachments to include with the message\n * @throws Error if text is an extension command\n */\n\nexport async function steer(this: AgentSession, text: string, images?: ImageContent[]): Promise<void> {\n\t// Check for extension commands (cannot be queued)\n\tif (text.startsWith(\"/\")) {\n\t\tthis._throwIfExtensionCommand(text);\n\t}\n\n\t// Expand skill commands and prompt templates\n\tlet expandedText = this._expandSkillCommand(text);\n\texpandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);\n\n\tawait this._queueSteer(expandedText, images);\n}\n\n/**\n * Queue a follow-up message to be processed after the agent finishes.\n * Delivered only when agent has no more tool calls or steering messages.\n * Expands skill commands and prompt templates. Errors on extension commands.\n * @param images Optional image attachments to include with the message\n * @throws Error if text is an extension command\n */\n\nexport async function followUp(this: AgentSession, text: string, images?: ImageContent[]): Promise<void> {\n\t// Check for extension commands (cannot be queued)\n\tif (text.startsWith(\"/\")) {\n\t\tthis._throwIfExtensionCommand(text);\n\t}\n\n\t// Expand skill commands and prompt templates\n\tlet expandedText = this._expandSkillCommand(text);\n\texpandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);\n\n\tawait this._queueFollowUp(expandedText, images);\n}\n\n/**\n * Internal: Queue a steering message (already expanded, no extension command check).\n */\n\nexport async function sendUserMessage(this: AgentSession, \n\tcontent: string | (TextContent | ImageContent)[],\n\toptions?: { deliverAs?: \"steer\" | \"followUp\" },\n): Promise<void> {\n\t// Normalize content to text string + optional images\n\tlet text: string;\n\tlet images: ImageContent[] | undefined;\n\n\tif (typeof content === \"string\") {\n\t\ttext = content;\n\t} else {\n\t\tconst textParts: string[] = [];\n\t\timages = [];\n\t\tfor (const part of content) {\n\t\t\tif (part.type === \"text\") {\n\t\t\t\ttextParts.push(part.text);\n\t\t\t} else {\n\t\t\t\timages.push(part);\n\t\t\t}\n\t\t}\n\t\ttext = textParts.join(\"\\n\");\n\t\tif (images.length === 0) images = undefined;\n\t}\n\n\t// Use prompt() with expandPromptTemplates: false to skip command handling and template expansion\n\tawait this.prompt(text, {\n\t\texpandPromptTemplates: false,\n\t\tstreamingBehavior: options?.deliverAs,\n\t\timages,\n\t\tsource: \"extension\",\n\t});\n}\n\n/**\n * Clear all queued messages and return them.\n * Useful for restoring to editor when user aborts.\n * @returns Object with steering and followUp arrays\n */\n\nexport const agentSessionPromptMethods = {\n\tprompt,\n\t_runAgentPrompt,\n\t_runAgentContinue,\n\t_continueQueuedAgentMessages,\n\t_tryExecuteBuiltinSlashCommand,\n\t_tryExecuteExtensionCommand,\n\t_expandSkillCommand,\n\tsteer,\n\tfollowUp,\n\tsendUserMessage,\n};\n"]}
1
+ {"version":3,"file":"agent-session-prompt.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-prompt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAK9E,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,wBAAsB,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA0KrG;AAGD,wBAAsB,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,GAAG,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAShH;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzE;AAGD,wBAAsB,4BAA4B,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAQpF;AAED;;GAEG;AAEH,wBAAsB,8BAA8B,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgCvG;AAED;;GAEG;AAEH,wBAAsB,2BAA2B,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBpG;AAED;;;;GAIG;AAEH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAwB5E;AAED;;;;;;;GAOG;AAEH,wBAAsB,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAWpG;AAED;;;;;;GAMG;AAEH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAWvG;AAED;;GAEG;AAEH,wBAAsB,eAAe,CAAC,IAAI,EAAE,YAAY,EACvD,OAAO,EAAE,MAAM,GAAG,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,EAChD,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,GAAG,UAAU,CAAA;CAAE,GAC5C,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;;;GAIG;AAEH,eAAO,MAAM,yBAAyB;;;;;;;;;;;CAWrC,CAAC","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport { ATOMIC_GUIDE_COMMAND_NAME, ATOMIC_GUIDE_HELP_CHOICES, atomicGuideModeForChoice, getAtomicGuideMessage, isAtomicGuideHelpChoice, normalizeAtomicGuideMode } from \"./atomic-guide-command.ts\";\nimport { formatAuthStorageLoadFailedMessage, formatNoApiKeyFoundMessage, formatNoModelSelectedMessage, formatUnresolvedModelMessage } from \"./auth-guidance.ts\";\nimport { expandPromptTemplate } from \"./prompt-templates.ts\";\nimport { stripFrontmatter } from \"../utils/frontmatter.ts\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\nimport type { PromptOptions } from \"./agent-session-types.ts\";\n\nexport async function prompt(this: AgentSession, text: string, options?: PromptOptions): Promise<void> {\n\tconst expandPromptTemplates = options?.expandPromptTemplates ?? true;\n\tconst preflightResult = options?.preflightResult;\n\tlet messages: AgentMessage[] | undefined;\n\n\ttry {\n\t\t// Handle slash commands first (execute immediately, even during streaming).\n\t\t// Builtin and extension commands manage their own LLM interaction via custom messages.\n\t\tif (expandPromptTemplates && text.startsWith(\"/\")) {\n\t\t\tconst handledBuiltin = await this._tryExecuteBuiltinSlashCommand(text);\n\t\t\tif (handledBuiltin) {\n\t\t\t\tpreflightResult?.(true);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst handledExtension = await this._tryExecuteExtensionCommand(text);\n\t\t\tif (handledExtension) {\n\t\t\t\tpreflightResult?.(true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Emit input event for extension interception (before skill/template expansion)\n\t\tlet currentText = text;\n\t\tlet currentImages = options?.images;\n\t\tif (this._extensionRunner.hasHandlers(\"input\")) {\n\t\t\tconst inputResult = await this._extensionRunner.emitInput(\n\t\t\t\tcurrentText,\n\t\t\t\tcurrentImages,\n\t\t\t\toptions?.source ?? \"interactive\",\n\t\t\t\tthis.isStreaming ? options?.streamingBehavior : undefined,\n\t\t\t);\n\t\t\tif (inputResult.action === \"handled\") {\n\t\t\t\tpreflightResult?.(true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (inputResult.action === \"transform\") {\n\t\t\t\tcurrentText = inputResult.text;\n\t\t\t\tcurrentImages = inputResult.images ?? currentImages;\n\t\t\t}\n\t\t}\n\n\t\t// Expand skill commands (/skill:name args) and prompt templates (/template args)\n\t\tlet expandedText = currentText;\n\t\tif (expandPromptTemplates) {\n\t\t\texpandedText = this._expandSkillCommand(expandedText);\n\t\t\texpandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);\n\t\t}\n\n\t\t// If streaming, queue via steer() or followUp() based on option\n\t\tif (this.isStreaming) {\n\t\t\tif (!options?.streamingBehavior) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (options.streamingBehavior === \"followUp\") {\n\t\t\t\tawait this._queueFollowUp(expandedText, currentImages);\n\t\t\t} else {\n\t\t\t\tawait this._queueSteer(expandedText, currentImages);\n\t\t\t}\n\t\t\tpreflightResult?.(true);\n\t\t\treturn;\n\t\t}\n\n\t\t// Flush any pending bash messages before the new prompt\n\t\tthis._flushPendingBashMessages();\n\n\t\t// Validate model\n\t\tif (!this.model) {\n\t\t\tthrow new Error(formatNoModelSelectedMessage());\n\t\t}\n\n\t\t// Defensive guard: a model that never resolved to a real provider\n\t\t// (for example an unknown/unresolved model id that reached this path\n\t\t// as a bare string) has no `provider`, which would otherwise fail deep\n\t\t// in auth resolution as the confusing \"No API key found for undefined\".\n\t\t// Surface a clear, accurate \"unknown model\" error instead.\n\t\tconst resolvedProvider = (this.model as { provider?: unknown }).provider;\n\t\tif (typeof resolvedProvider !== \"string\" || resolvedProvider.length === 0) {\n\t\t\tthrow new Error(formatUnresolvedModelMessage(this.model));\n\t\t}\n\n\t\tif (!this._modelRegistry.hasConfiguredAuth(this.model)) {\n\t\t\t// A failed credential-store load (for example auth.json briefly locked\n\t\t\t// by a concurrent process, or invalid JSON) leaves an empty in-memory\n\t\t\t// credential set. That would otherwise be misreported here as\n\t\t\t// \"No API key found\" even though the credentials exist on disk. Surface\n\t\t\t// the real load failure instead so configured providers are not falsely\n\t\t\t// reported as unauthenticated (issue #1431).\n\t\t\tconst authLoadError = this._modelRegistry.authStorage.getLoadError();\n\t\t\tif (authLoadError) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tformatAuthStorageLoadFailedMessage(this.model.provider, authLoadError),\n\t\t\t\t\t{ cause: authLoadError },\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst isOAuth = this._modelRegistry.isUsingOAuth(this.model);\n\t\t\tif (isOAuth) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Authentication failed for \"${this.model.provider}\". ` +\n\t\t\t\t\t\t`Credentials may have expired or network is unavailable. ` +\n\t\t\t\t\t\t`Run '/login ${this.model.provider}' to re-authenticate.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new Error(formatNoApiKeyFoundMessage(this.model.provider));\n\t\t}\n\n\t\t// Check if we need to compact before sending (catches aborted responses)\n\t\tconst lastAssistant = this._findLastAssistantMessage();\n\t\tif (lastAssistant) {\n\t\t\tawait this._checkCompaction(lastAssistant, false);\n\t\t}\n\n\t\t// Build messages array (custom message if any, then user message)\n\t\tmessages = [];\n\n\t\t// Add user message\n\t\tconst userContent: (TextContent | ImageContent)[] = [{ type: \"text\", text: expandedText }];\n\t\tif (currentImages) {\n\t\t\tuserContent.push(...currentImages);\n\t\t}\n\t\tmessages.push({\n\t\t\trole: \"user\",\n\t\t\tcontent: userContent,\n\t\t\ttimestamp: Date.now(),\n\t\t});\n\n\t\t// Inject any pending \"nextTurn\" messages as context alongside the user message\n\t\tfor (const msg of this._pendingNextTurnMessages) {\n\t\t\tmessages.push(msg);\n\t\t}\n\t\tthis._pendingNextTurnMessages = [];\n\n\t\t// Emit before_agent_start extension event\n\t\tconst result = await this._extensionRunner.emitBeforeAgentStart(\n\t\t\texpandedText,\n\t\t\tcurrentImages,\n\t\t\tthis._baseSystemPrompt,\n\t\t\tthis._baseSystemPromptOptions,\n\t\t);\n\t\t// Add all custom messages from extensions\n\t\tif (result?.messages) {\n\t\t\tfor (const msg of result.messages) {\n\t\t\t\tmessages.push({\n\t\t\t\t\trole: \"custom\",\n\t\t\t\t\tcustomType: msg.customType,\n\t\t\t\t\tcontent: msg.content ?? [],\n\t\t\t\t\tdisplay: msg.display,\n\t\t\t\t\tdetails: msg.details,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\t// Apply extension-modified system prompt, or reset to base\n\t\tif (result?.systemPrompt !== undefined) {\n\t\t\tthis._systemPromptOverride = result.systemPrompt;\n\t\t\tthis.agent.state.systemPrompt = result.systemPrompt;\n\t\t} else {\n\t\t\t// Ensure we're using the base prompt (in case previous turn had modifications)\n\t\t\tthis._systemPromptOverride = undefined;\n\t\t\tthis.agent.state.systemPrompt = this._baseSystemPrompt;\n\t\t}\n\t} catch (error) {\n\t\tpreflightResult?.(false);\n\t\tthrow error;\n\t}\n\n\tpreflightResult?.(true);\n\tawait this._runAgentPrompt(messages);\n}\n\n\nexport async function _runAgentPrompt(this: AgentSession, messages: AgentMessage | AgentMessage[]): Promise<void> {\n\ttry {\n\t\tawait this.agent.prompt(messages);\n\t\tawait this.waitForRetry();\n\t\tawait this._continueQueuedAgentMessages();\n\t\tawait this._awaitPendingPostCompactionContinuation();\n\t} finally {\n\t\tthis._systemPromptOverride = undefined;\n\t}\n}\n\nexport async function _runAgentContinue(this: AgentSession): Promise<void> {\n\tawait this.agent.continue();\n\tawait this.waitForRetry();\n\tawait this._continueQueuedAgentMessages();\n}\n\n\nexport async function _continueQueuedAgentMessages(this: AgentSession): Promise<void> {\n\tawait this._agentEventQueue;\n\n\twhile (this.agent.hasQueuedMessages()) {\n\t\tawait this.agent.continue();\n\t\tawait this.waitForRetry();\n\t\tawait this._agentEventQueue;\n\t}\n}\n\n/**\n * Try to execute a built-in slash command. Returns true if command was found and executed.\n */\n\nexport async function _tryExecuteBuiltinSlashCommand(this: AgentSession, text: string): Promise<boolean> {\n\tconst spaceIndex = text.indexOf(\" \");\n\tconst commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);\n\tif (commandName !== ATOMIC_GUIDE_COMMAND_NAME) return false;\n\n\tconst args = spaceIndex === -1 ? \"\" : text.slice(spaceIndex + 1);\n\tconst mode = normalizeAtomicGuideMode(args);\n\tif (mode === \"help\" && this._extensionUIContext) {\n\t\tconst choice = await this._extensionUIContext.select(\"Atomic. Select where to start:\", [\n\t\t\t...ATOMIC_GUIDE_HELP_CHOICES,\n\t\t]);\n\t\tif (!choice || !isAtomicGuideHelpChoice(choice)) return true;\n\t\tawait this.sendCustomMessage(\n\t\t\t{\n\t\t\t\tcustomType: \"atomic\",\n\t\t\t\tcontent: getAtomicGuideMessage(atomicGuideModeForChoice(choice), this._cwd),\n\t\t\t\tdisplay: true,\n\t\t\t},\n\t\t\t{ triggerTurn: false },\n\t\t);\n\t\treturn true;\n\t}\n\n\tawait this.sendCustomMessage(\n\t\t{\n\t\t\tcustomType: \"atomic\",\n\t\t\tcontent: getAtomicGuideMessage(mode, this._cwd),\n\t\t\tdisplay: true,\n\t\t},\n\t\t{ triggerTurn: false },\n\t);\n\treturn true;\n}\n\n/**\n * Try to execute an extension command. Returns true if command was found and executed.\n */\n\nexport async function _tryExecuteExtensionCommand(this: AgentSession, text: string): Promise<boolean> {\n\t// Parse command name and args\n\tconst spaceIndex = text.indexOf(\" \");\n\tconst commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);\n\tconst args = spaceIndex === -1 ? \"\" : text.slice(spaceIndex + 1);\n\n\tconst command = this._extensionRunner.getCommand(commandName);\n\tif (!command) return false;\n\n\t// Get command context from extension runner (includes session control methods)\n\tconst ctx = this._extensionRunner.createCommandContext();\n\n\ttry {\n\t\tawait command.handler(args, ctx);\n\t\treturn true;\n\t} catch (err) {\n\t\t// Emit error via extension runner\n\t\tthis._extensionRunner.emitError({\n\t\t\textensionPath: `command:${commandName}`,\n\t\t\tevent: \"command\",\n\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t});\n\t\treturn true;\n\t}\n}\n\n/**\n * Expand skill commands (/skill:name args) to their full content.\n * Returns the expanded text, or the original text if not a skill command or skill not found.\n * Emits errors via extension runner if file read fails.\n */\n\nexport function _expandSkillCommand(this: AgentSession, text: string): string {\n\tif (!text.startsWith(\"/skill:\")) return text;\n\n\tconst spaceIndex = text.indexOf(\" \");\n\tconst skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);\n\tconst args = spaceIndex === -1 ? \"\" : text.slice(spaceIndex + 1).trim();\n\n\tconst skill = this.resourceLoader.getSkills().skills.find((s) => s.name === skillName);\n\tif (!skill) return text; // Unknown skill, pass through\n\n\ttry {\n\t\tconst content = readFileSync(skill.filePath, \"utf-8\");\n\t\tconst body = stripFrontmatter(content).trim();\n\t\tconst skillBlock = `<skill name=\"${skill.name}\" location=\"${skill.filePath}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n\t\treturn args ? `${skillBlock}\\n\\n${args}` : skillBlock;\n\t} catch (err) {\n\t\t// Emit error like extension commands do\n\t\tthis._extensionRunner.emitError({\n\t\t\textensionPath: skill.filePath,\n\t\t\tevent: \"skill_expansion\",\n\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t});\n\t\treturn text; // Return original on error\n\t}\n}\n\n/**\n * Queue a steering message while the agent is running.\n * Delivered after the current assistant turn finishes executing its tool calls,\n * before the next LLM call.\n * Expands skill commands and prompt templates. Errors on extension commands.\n * @param images Optional image attachments to include with the message\n * @throws Error if text is an extension command\n */\n\nexport async function steer(this: AgentSession, text: string, images?: ImageContent[]): Promise<void> {\n\t// Check for extension commands (cannot be queued)\n\tif (text.startsWith(\"/\")) {\n\t\tthis._throwIfExtensionCommand(text);\n\t}\n\n\t// Expand skill commands and prompt templates\n\tlet expandedText = this._expandSkillCommand(text);\n\texpandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);\n\n\tawait this._queueSteer(expandedText, images);\n}\n\n/**\n * Queue a follow-up message to be processed after the agent finishes.\n * Delivered only when agent has no more tool calls or steering messages.\n * Expands skill commands and prompt templates. Errors on extension commands.\n * @param images Optional image attachments to include with the message\n * @throws Error if text is an extension command\n */\n\nexport async function followUp(this: AgentSession, text: string, images?: ImageContent[]): Promise<void> {\n\t// Check for extension commands (cannot be queued)\n\tif (text.startsWith(\"/\")) {\n\t\tthis._throwIfExtensionCommand(text);\n\t}\n\n\t// Expand skill commands and prompt templates\n\tlet expandedText = this._expandSkillCommand(text);\n\texpandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);\n\n\tawait this._queueFollowUp(expandedText, images);\n}\n\n/**\n * Internal: Queue a steering message (already expanded, no extension command check).\n */\n\nexport async function sendUserMessage(this: AgentSession, \n\tcontent: string | (TextContent | ImageContent)[],\n\toptions?: { deliverAs?: \"steer\" | \"followUp\" },\n): Promise<void> {\n\t// Normalize content to text string + optional images\n\tlet text: string;\n\tlet images: ImageContent[] | undefined;\n\n\tif (typeof content === \"string\") {\n\t\ttext = content;\n\t} else {\n\t\tconst textParts: string[] = [];\n\t\timages = [];\n\t\tfor (const part of content) {\n\t\t\tif (part.type === \"text\") {\n\t\t\t\ttextParts.push(part.text);\n\t\t\t} else {\n\t\t\t\timages.push(part);\n\t\t\t}\n\t\t}\n\t\ttext = textParts.join(\"\\n\");\n\t\tif (images.length === 0) images = undefined;\n\t}\n\n\t// Use prompt() with expandPromptTemplates: false to skip command handling and template expansion\n\tawait this.prompt(text, {\n\t\texpandPromptTemplates: false,\n\t\tstreamingBehavior: options?.deliverAs,\n\t\timages,\n\t\tsource: \"extension\",\n\t});\n}\n\n/**\n * Clear all queued messages and return them.\n * Useful for restoring to editor when user aborts.\n * @returns Object with steering and followUp arrays\n */\n\nexport const agentSessionPromptMethods = {\n\tprompt,\n\t_runAgentPrompt,\n\t_runAgentContinue,\n\t_continueQueuedAgentMessages,\n\t_tryExecuteBuiltinSlashCommand,\n\t_tryExecuteExtensionCommand,\n\t_expandSkillCommand,\n\tsteer,\n\tfollowUp,\n\tsendUserMessage,\n};\n"]}
@@ -120,7 +120,7 @@ export async function prompt(text, options) {
120
120
  messages.push({
121
121
  role: "custom",
122
122
  customType: msg.customType,
123
- content: msg.content,
123
+ content: msg.content ?? [],
124
124
  display: msg.display,
125
125
  details: msg.details,
126
126
  timestamp: Date.now(),
@@ -1 +1 @@
1
- {"version":3,"file":"agent-session-prompt.js","sourceRoot":"","sources":["../../src/core/agent-session-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport { ATOMIC_GUIDE_COMMAND_NAME, ATOMIC_GUIDE_HELP_CHOICES, atomicGuideModeForChoice, getAtomicGuideMessage, isAtomicGuideHelpChoice, normalizeAtomicGuideMode } from \"./atomic-guide-command.ts\";\nimport { formatAuthStorageLoadFailedMessage, formatNoApiKeyFoundMessage, formatNoModelSelectedMessage, formatUnresolvedModelMessage } from \"./auth-guidance.ts\";\nimport { expandPromptTemplate } from \"./prompt-templates.ts\";\nimport { stripFrontmatter } from \"../utils/frontmatter.ts\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\nimport type { PromptOptions } from \"./agent-session-types.ts\";\n\nexport async function prompt(this: AgentSession, text: string, options?: PromptOptions): Promise<void> {\n\tconst expandPromptTemplates = options?.expandPromptTemplates ?? true;\n\tconst preflightResult = options?.preflightResult;\n\tlet messages: AgentMessage[] | undefined;\n\n\ttry {\n\t\t// Handle slash commands first (execute immediately, even during streaming).\n\t\t// Builtin and extension commands manage their own LLM interaction via custom messages.\n\t\tif (expandPromptTemplates && text.startsWith(\"/\")) {\n\t\t\tconst handledBuiltin = await this._tryExecuteBuiltinSlashCommand(text);\n\t\t\tif (handledBuiltin) {\n\t\t\t\tpreflightResult?.(true);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst handledExtension = await this._tryExecuteExtensionCommand(text);\n\t\t\tif (handledExtension) {\n\t\t\t\tpreflightResult?.(true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Emit input event for extension interception (before skill/template expansion)\n\t\tlet currentText = text;\n\t\tlet currentImages = options?.images;\n\t\tif (this._extensionRunner.hasHandlers(\"input\")) {\n\t\t\tconst inputResult = await this._extensionRunner.emitInput(\n\t\t\t\tcurrentText,\n\t\t\t\tcurrentImages,\n\t\t\t\toptions?.source ?? \"interactive\",\n\t\t\t\tthis.isStreaming ? options?.streamingBehavior : undefined,\n\t\t\t);\n\t\t\tif (inputResult.action === \"handled\") {\n\t\t\t\tpreflightResult?.(true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (inputResult.action === \"transform\") {\n\t\t\t\tcurrentText = inputResult.text;\n\t\t\t\tcurrentImages = inputResult.images ?? currentImages;\n\t\t\t}\n\t\t}\n\n\t\t// Expand skill commands (/skill:name args) and prompt templates (/template args)\n\t\tlet expandedText = currentText;\n\t\tif (expandPromptTemplates) {\n\t\t\texpandedText = this._expandSkillCommand(expandedText);\n\t\t\texpandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);\n\t\t}\n\n\t\t// If streaming, queue via steer() or followUp() based on option\n\t\tif (this.isStreaming) {\n\t\t\tif (!options?.streamingBehavior) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (options.streamingBehavior === \"followUp\") {\n\t\t\t\tawait this._queueFollowUp(expandedText, currentImages);\n\t\t\t} else {\n\t\t\t\tawait this._queueSteer(expandedText, currentImages);\n\t\t\t}\n\t\t\tpreflightResult?.(true);\n\t\t\treturn;\n\t\t}\n\n\t\t// Flush any pending bash messages before the new prompt\n\t\tthis._flushPendingBashMessages();\n\n\t\t// Validate model\n\t\tif (!this.model) {\n\t\t\tthrow new Error(formatNoModelSelectedMessage());\n\t\t}\n\n\t\t// Defensive guard: a model that never resolved to a real provider\n\t\t// (for example an unknown/unresolved model id that reached this path\n\t\t// as a bare string) has no `provider`, which would otherwise fail deep\n\t\t// in auth resolution as the confusing \"No API key found for undefined\".\n\t\t// Surface a clear, accurate \"unknown model\" error instead.\n\t\tconst resolvedProvider = (this.model as { provider?: unknown }).provider;\n\t\tif (typeof resolvedProvider !== \"string\" || resolvedProvider.length === 0) {\n\t\t\tthrow new Error(formatUnresolvedModelMessage(this.model));\n\t\t}\n\n\t\tif (!this._modelRegistry.hasConfiguredAuth(this.model)) {\n\t\t\t// A failed credential-store load (for example auth.json briefly locked\n\t\t\t// by a concurrent process, or invalid JSON) leaves an empty in-memory\n\t\t\t// credential set. That would otherwise be misreported here as\n\t\t\t// \"No API key found\" even though the credentials exist on disk. Surface\n\t\t\t// the real load failure instead so configured providers are not falsely\n\t\t\t// reported as unauthenticated (issue #1431).\n\t\t\tconst authLoadError = this._modelRegistry.authStorage.getLoadError();\n\t\t\tif (authLoadError) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tformatAuthStorageLoadFailedMessage(this.model.provider, authLoadError),\n\t\t\t\t\t{ cause: authLoadError },\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst isOAuth = this._modelRegistry.isUsingOAuth(this.model);\n\t\t\tif (isOAuth) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Authentication failed for \"${this.model.provider}\". ` +\n\t\t\t\t\t\t`Credentials may have expired or network is unavailable. ` +\n\t\t\t\t\t\t`Run '/login ${this.model.provider}' to re-authenticate.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new Error(formatNoApiKeyFoundMessage(this.model.provider));\n\t\t}\n\n\t\t// Check if we need to compact before sending (catches aborted responses)\n\t\tconst lastAssistant = this._findLastAssistantMessage();\n\t\tif (lastAssistant) {\n\t\t\tawait this._checkCompaction(lastAssistant, false);\n\t\t}\n\n\t\t// Build messages array (custom message if any, then user message)\n\t\tmessages = [];\n\n\t\t// Add user message\n\t\tconst userContent: (TextContent | ImageContent)[] = [{ type: \"text\", text: expandedText }];\n\t\tif (currentImages) {\n\t\t\tuserContent.push(...currentImages);\n\t\t}\n\t\tmessages.push({\n\t\t\trole: \"user\",\n\t\t\tcontent: userContent,\n\t\t\ttimestamp: Date.now(),\n\t\t});\n\n\t\t// Inject any pending \"nextTurn\" messages as context alongside the user message\n\t\tfor (const msg of this._pendingNextTurnMessages) {\n\t\t\tmessages.push(msg);\n\t\t}\n\t\tthis._pendingNextTurnMessages = [];\n\n\t\t// Emit before_agent_start extension event\n\t\tconst result = await this._extensionRunner.emitBeforeAgentStart(\n\t\t\texpandedText,\n\t\t\tcurrentImages,\n\t\t\tthis._baseSystemPrompt,\n\t\t\tthis._baseSystemPromptOptions,\n\t\t);\n\t\t// Add all custom messages from extensions\n\t\tif (result?.messages) {\n\t\t\tfor (const msg of result.messages) {\n\t\t\t\tmessages.push({\n\t\t\t\t\trole: \"custom\",\n\t\t\t\t\tcustomType: msg.customType,\n\t\t\t\t\tcontent: msg.content,\n\t\t\t\t\tdisplay: msg.display,\n\t\t\t\t\tdetails: msg.details,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\t// Apply extension-modified system prompt, or reset to base\n\t\tif (result?.systemPrompt !== undefined) {\n\t\t\tthis._systemPromptOverride = result.systemPrompt;\n\t\t\tthis.agent.state.systemPrompt = result.systemPrompt;\n\t\t} else {\n\t\t\t// Ensure we're using the base prompt (in case previous turn had modifications)\n\t\t\tthis._systemPromptOverride = undefined;\n\t\t\tthis.agent.state.systemPrompt = this._baseSystemPrompt;\n\t\t}\n\t} catch (error) {\n\t\tpreflightResult?.(false);\n\t\tthrow error;\n\t}\n\n\tpreflightResult?.(true);\n\tawait this._runAgentPrompt(messages);\n}\n\n\nexport async function _runAgentPrompt(this: AgentSession, messages: AgentMessage | AgentMessage[]): Promise<void> {\n\ttry {\n\t\tawait this.agent.prompt(messages);\n\t\tawait this.waitForRetry();\n\t\tawait this._continueQueuedAgentMessages();\n\t\tawait this._awaitPendingPostCompactionContinuation();\n\t} finally {\n\t\tthis._systemPromptOverride = undefined;\n\t}\n}\n\nexport async function _runAgentContinue(this: AgentSession): Promise<void> {\n\tawait this.agent.continue();\n\tawait this.waitForRetry();\n\tawait this._continueQueuedAgentMessages();\n}\n\n\nexport async function _continueQueuedAgentMessages(this: AgentSession): Promise<void> {\n\tawait this._agentEventQueue;\n\n\twhile (this.agent.hasQueuedMessages()) {\n\t\tawait this.agent.continue();\n\t\tawait this.waitForRetry();\n\t\tawait this._agentEventQueue;\n\t}\n}\n\n/**\n * Try to execute a built-in slash command. Returns true if command was found and executed.\n */\n\nexport async function _tryExecuteBuiltinSlashCommand(this: AgentSession, text: string): Promise<boolean> {\n\tconst spaceIndex = text.indexOf(\" \");\n\tconst commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);\n\tif (commandName !== ATOMIC_GUIDE_COMMAND_NAME) return false;\n\n\tconst args = spaceIndex === -1 ? \"\" : text.slice(spaceIndex + 1);\n\tconst mode = normalizeAtomicGuideMode(args);\n\tif (mode === \"help\" && this._extensionUIContext) {\n\t\tconst choice = await this._extensionUIContext.select(\"Atomic. Select where to start:\", [\n\t\t\t...ATOMIC_GUIDE_HELP_CHOICES,\n\t\t]);\n\t\tif (!choice || !isAtomicGuideHelpChoice(choice)) return true;\n\t\tawait this.sendCustomMessage(\n\t\t\t{\n\t\t\t\tcustomType: \"atomic\",\n\t\t\t\tcontent: getAtomicGuideMessage(atomicGuideModeForChoice(choice), this._cwd),\n\t\t\t\tdisplay: true,\n\t\t\t},\n\t\t\t{ triggerTurn: false },\n\t\t);\n\t\treturn true;\n\t}\n\n\tawait this.sendCustomMessage(\n\t\t{\n\t\t\tcustomType: \"atomic\",\n\t\t\tcontent: getAtomicGuideMessage(mode, this._cwd),\n\t\t\tdisplay: true,\n\t\t},\n\t\t{ triggerTurn: false },\n\t);\n\treturn true;\n}\n\n/**\n * Try to execute an extension command. Returns true if command was found and executed.\n */\n\nexport async function _tryExecuteExtensionCommand(this: AgentSession, text: string): Promise<boolean> {\n\t// Parse command name and args\n\tconst spaceIndex = text.indexOf(\" \");\n\tconst commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);\n\tconst args = spaceIndex === -1 ? \"\" : text.slice(spaceIndex + 1);\n\n\tconst command = this._extensionRunner.getCommand(commandName);\n\tif (!command) return false;\n\n\t// Get command context from extension runner (includes session control methods)\n\tconst ctx = this._extensionRunner.createCommandContext();\n\n\ttry {\n\t\tawait command.handler(args, ctx);\n\t\treturn true;\n\t} catch (err) {\n\t\t// Emit error via extension runner\n\t\tthis._extensionRunner.emitError({\n\t\t\textensionPath: `command:${commandName}`,\n\t\t\tevent: \"command\",\n\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t});\n\t\treturn true;\n\t}\n}\n\n/**\n * Expand skill commands (/skill:name args) to their full content.\n * Returns the expanded text, or the original text if not a skill command or skill not found.\n * Emits errors via extension runner if file read fails.\n */\n\nexport function _expandSkillCommand(this: AgentSession, text: string): string {\n\tif (!text.startsWith(\"/skill:\")) return text;\n\n\tconst spaceIndex = text.indexOf(\" \");\n\tconst skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);\n\tconst args = spaceIndex === -1 ? \"\" : text.slice(spaceIndex + 1).trim();\n\n\tconst skill = this.resourceLoader.getSkills().skills.find((s) => s.name === skillName);\n\tif (!skill) return text; // Unknown skill, pass through\n\n\ttry {\n\t\tconst content = readFileSync(skill.filePath, \"utf-8\");\n\t\tconst body = stripFrontmatter(content).trim();\n\t\tconst skillBlock = `<skill name=\"${skill.name}\" location=\"${skill.filePath}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n\t\treturn args ? `${skillBlock}\\n\\n${args}` : skillBlock;\n\t} catch (err) {\n\t\t// Emit error like extension commands do\n\t\tthis._extensionRunner.emitError({\n\t\t\textensionPath: skill.filePath,\n\t\t\tevent: \"skill_expansion\",\n\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t});\n\t\treturn text; // Return original on error\n\t}\n}\n\n/**\n * Queue a steering message while the agent is running.\n * Delivered after the current assistant turn finishes executing its tool calls,\n * before the next LLM call.\n * Expands skill commands and prompt templates. Errors on extension commands.\n * @param images Optional image attachments to include with the message\n * @throws Error if text is an extension command\n */\n\nexport async function steer(this: AgentSession, text: string, images?: ImageContent[]): Promise<void> {\n\t// Check for extension commands (cannot be queued)\n\tif (text.startsWith(\"/\")) {\n\t\tthis._throwIfExtensionCommand(text);\n\t}\n\n\t// Expand skill commands and prompt templates\n\tlet expandedText = this._expandSkillCommand(text);\n\texpandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);\n\n\tawait this._queueSteer(expandedText, images);\n}\n\n/**\n * Queue a follow-up message to be processed after the agent finishes.\n * Delivered only when agent has no more tool calls or steering messages.\n * Expands skill commands and prompt templates. Errors on extension commands.\n * @param images Optional image attachments to include with the message\n * @throws Error if text is an extension command\n */\n\nexport async function followUp(this: AgentSession, text: string, images?: ImageContent[]): Promise<void> {\n\t// Check for extension commands (cannot be queued)\n\tif (text.startsWith(\"/\")) {\n\t\tthis._throwIfExtensionCommand(text);\n\t}\n\n\t// Expand skill commands and prompt templates\n\tlet expandedText = this._expandSkillCommand(text);\n\texpandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);\n\n\tawait this._queueFollowUp(expandedText, images);\n}\n\n/**\n * Internal: Queue a steering message (already expanded, no extension command check).\n */\n\nexport async function sendUserMessage(this: AgentSession, \n\tcontent: string | (TextContent | ImageContent)[],\n\toptions?: { deliverAs?: \"steer\" | \"followUp\" },\n): Promise<void> {\n\t// Normalize content to text string + optional images\n\tlet text: string;\n\tlet images: ImageContent[] | undefined;\n\n\tif (typeof content === \"string\") {\n\t\ttext = content;\n\t} else {\n\t\tconst textParts: string[] = [];\n\t\timages = [];\n\t\tfor (const part of content) {\n\t\t\tif (part.type === \"text\") {\n\t\t\t\ttextParts.push(part.text);\n\t\t\t} else {\n\t\t\t\timages.push(part);\n\t\t\t}\n\t\t}\n\t\ttext = textParts.join(\"\\n\");\n\t\tif (images.length === 0) images = undefined;\n\t}\n\n\t// Use prompt() with expandPromptTemplates: false to skip command handling and template expansion\n\tawait this.prompt(text, {\n\t\texpandPromptTemplates: false,\n\t\tstreamingBehavior: options?.deliverAs,\n\t\timages,\n\t\tsource: \"extension\",\n\t});\n}\n\n/**\n * Clear all queued messages and return them.\n * Useful for restoring to editor when user aborts.\n * @returns Object with steering and followUp arrays\n */\n\nexport const agentSessionPromptMethods = {\n\tprompt,\n\t_runAgentPrompt,\n\t_runAgentContinue,\n\t_continueQueuedAgentMessages,\n\t_tryExecuteBuiltinSlashCommand,\n\t_tryExecuteExtensionCommand,\n\t_expandSkillCommand,\n\tsteer,\n\tfollowUp,\n\tsendUserMessage,\n};\n"]}
1
+ {"version":3,"file":"agent-session-prompt.js","sourceRoot":"","sources":["../../src/core/agent-session-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrM,OAAO,EAAE,kCAAkC,EAAE,0BAA0B,EAAE,4BAA4B,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAChK,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAI3D,MAAM,CAAC,KAAK,UAAU,MAAM,CAAqB,IAAY,EAAE,OAAuB;IACrF,MAAM,qBAAqB,GAAG,OAAO,EAAE,qBAAqB,IAAI,IAAI,CAAC;IACrE,MAAM,eAAe,GAAG,OAAO,EAAE,eAAe,CAAC;IACjD,IAAI,QAAoC,CAAC;IAEzC,IAAI,CAAC;QACJ,4EAA4E;QAC5E,uFAAuF;QACvF,IAAI,qBAAqB,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACnD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,8BAA8B,CAAC,IAAI,CAAC,CAAC;YACvE,IAAI,cAAc,EAAE,CAAC;gBACpB,eAAe,EAAE,CAAC,IAAI,CAAC,CAAC;gBACxB,OAAO;YACR,CAAC;YAED,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC,CAAC;YACtE,IAAI,gBAAgB,EAAE,CAAC;gBACtB,eAAe,EAAE,CAAC,IAAI,CAAC,CAAC;gBACxB,OAAO;YACR,CAAC;QACF,CAAC;QAED,gFAAgF;QAChF,IAAI,WAAW,GAAG,IAAI,CAAC;QACvB,IAAI,aAAa,GAAG,OAAO,EAAE,MAAM,CAAC;QACpC,IAAI,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;YAChD,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,SAAS,CACxD,WAAW,EACX,aAAa,EACb,OAAO,EAAE,MAAM,IAAI,aAAa,EAChC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,SAAS,CACzD,CAAC;YACF,IAAI,WAAW,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBACtC,eAAe,EAAE,CAAC,IAAI,CAAC,CAAC;gBACxB,OAAO;YACR,CAAC;YACD,IAAI,WAAW,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBACxC,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC;gBAC/B,aAAa,GAAG,WAAW,CAAC,MAAM,IAAI,aAAa,CAAC;YACrD,CAAC;QACF,CAAC;QAED,iFAAiF;QACjF,IAAI,YAAY,GAAG,WAAW,CAAC;QAC/B,IAAI,qBAAqB,EAAE,CAAC;YAC3B,YAAY,GAAG,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC;YACtD,YAAY,GAAG,oBAAoB,CAAC,YAAY,EAAE,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;QAC9E,CAAC;QAED,gEAAgE;QAChE,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,IAAI,CAAC,OAAO,EAAE,iBAAiB,EAAE,CAAC;gBACjC,MAAM,IAAI,KAAK,CACd,sGAAsG,CACtG,CAAC;YACH,CAAC;YACD,IAAI,OAAO,CAAC,iBAAiB,KAAK,UAAU,EAAE,CAAC;gBAC9C,MAAM,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;YACxD,CAAC;iBAAM,CAAC;gBACP,MAAM,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;YACrD,CAAC;YACD,eAAe,EAAE,CAAC,IAAI,CAAC,CAAC;YACxB,OAAO;QACR,CAAC;QAED,wDAAwD;QACxD,IAAI,CAAC,yBAAyB,EAAE,CAAC;QAEjC,iBAAiB;QACjB,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,4BAA4B,EAAE,CAAC,CAAC;QACjD,CAAC;QAED,kEAAkE;QAClE,qEAAqE;QACrE,uEAAuE;QACvE,wEAAwE;QACxE,2DAA2D;QAC3D,MAAM,gBAAgB,GAAI,IAAI,CAAC,KAAgC,CAAC,QAAQ,CAAC;QACzE,IAAI,OAAO,gBAAgB,KAAK,QAAQ,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3E,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACxD,uEAAuE;YACvE,sEAAsE;YACtE,8DAA8D;YAC9D,wEAAwE;YACxE,wEAAwE;YACxE,6CAA6C;YAC7C,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC;YACrE,IAAI,aAAa,EAAE,CAAC;gBACnB,MAAM,IAAI,KAAK,CACd,kCAAkC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,aAAa,CAAC,EACtE,EAAE,KAAK,EAAE,aAAa,EAAE,CACxB,CAAC;YACH,CAAC;YACD,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC7D,IAAI,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CACd,8BAA8B,IAAI,CAAC,KAAK,CAAC,QAAQ,KAAK;oBACrD,0DAA0D;oBAC1D,eAAe,IAAI,CAAC,KAAK,CAAC,QAAQ,uBAAuB,CAC1D,CAAC;YACH,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QAClE,CAAC;QAED,yEAAyE;QACzE,MAAM,aAAa,GAAG,IAAI,CAAC,yBAAyB,EAAE,CAAC;QACvD,IAAI,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QACnD,CAAC;QAED,kEAAkE;QAClE,QAAQ,GAAG,EAAE,CAAC;QAEd,mBAAmB;QACnB,MAAM,WAAW,GAAmC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;QAC3F,IAAI,aAAa,EAAE,CAAC;YACnB,WAAW,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,CAAC;QACpC,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,WAAW;YACpB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACrB,CAAC,CAAC;QAEH,+EAA+E;QAC/E,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,wBAAwB,EAAE,CAAC;YACjD,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,wBAAwB,GAAG,EAAE,CAAC;QAEnC,0CAA0C;QAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,oBAAoB,CAC9D,YAAY,EACZ,aAAa,EACb,IAAI,CAAC,iBAAiB,EACtB,IAAI,CAAC,wBAAwB,CAC7B,CAAC;QACF,0CAA0C;QAC1C,IAAI,MAAM,EAAE,QAAQ,EAAE,CAAC;YACtB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACnC,QAAQ,CAAC,IAAI,CAAC;oBACb,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,GAAG,CAAC,UAAU;oBAC1B,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE;oBAC1B,OAAO,EAAE,GAAG,CAAC,OAAO;oBACpB,OAAO,EAAE,GAAG,CAAC,OAAO;oBACpB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACrB,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;QACD,2DAA2D;QAC3D,IAAI,MAAM,EAAE,YAAY,KAAK,SAAS,EAAE,CAAC;YACxC,IAAI,CAAC,qBAAqB,GAAG,MAAM,CAAC,YAAY,CAAC;YACjD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;QACrD,CAAC;aAAM,CAAC;YACP,+EAA+E;YAC/E,IAAI,CAAC,qBAAqB,GAAG,SAAS,CAAC;YACvC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,iBAAiB,CAAC;QACxD,CAAC;IACF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,eAAe,EAAE,CAAC,KAAK,CAAC,CAAC;QACzB,MAAM,KAAK,CAAC;IACb,CAAC;IAED,eAAe,EAAE,CAAC,IAAI,CAAC,CAAC;IACxB,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;AACtC,CAAC;AAGD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAqB,QAAuC;IAChG,IAAI,CAAC;QACJ,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC1B,MAAM,IAAI,CAAC,4BAA4B,EAAE,CAAC;QAC1C,MAAM,IAAI,CAAC,uCAAuC,EAAE,CAAC;IACtD,CAAC;YAAS,CAAC;QACV,IAAI,CAAC,qBAAqB,GAAG,SAAS,CAAC;IACxC,CAAC;AACF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACtC,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC5B,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;IAC1B,MAAM,IAAI,CAAC,4BAA4B,EAAE,CAAC;AAC3C,CAAC;AAGD,MAAM,CAAC,KAAK,UAAU,4BAA4B;IACjD,MAAM,IAAI,CAAC,gBAAgB,CAAC;IAE5B,OAAO,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC1B,MAAM,IAAI,CAAC,gBAAgB,CAAC;IAC7B,CAAC;AACF,CAAC;AAED;;GAEG;AAEH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAqB,IAAY;IACpF,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,WAAW,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAClF,IAAI,WAAW,KAAK,yBAAyB;QAAE,OAAO,KAAK,CAAC;IAE5D,MAAM,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;IACjE,MAAM,IAAI,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACjD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,gCAAgC,EAAE;YACtF,GAAG,yBAAyB;SAC5B,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7D,MAAM,IAAI,CAAC,iBAAiB,CAC3B;YACC,UAAU,EAAE,QAAQ;YACpB,OAAO,EAAE,qBAAqB,CAAC,wBAAwB,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;YAC3E,OAAO,EAAE,IAAI;SACb,EACD,EAAE,WAAW,EAAE,KAAK,EAAE,CACtB,CAAC;QACF,OAAO,IAAI,CAAC;IACb,CAAC;IAED,MAAM,IAAI,CAAC,iBAAiB,CAC3B;QACC,UAAU,EAAE,QAAQ;QACpB,OAAO,EAAE,qBAAqB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC;QAC/C,OAAO,EAAE,IAAI;KACb,EACD,EAAE,WAAW,EAAE,KAAK,EAAE,CACtB,CAAC;IACF,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;GAEG;AAEH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAqB,IAAY;IACjF,8BAA8B;IAC9B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,WAAW,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAClF,MAAM,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;IAEjE,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAC9D,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAE3B,+EAA+E;IAC/E,MAAM,GAAG,GAAG,IAAI,CAAC,gBAAgB,CAAC,oBAAoB,EAAE,CAAC;IAEzD,IAAI,CAAC;QACJ,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,kCAAkC;QAClC,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC;YAC/B,aAAa,EAAE,WAAW,WAAW,EAAE;YACvC,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACvD,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED;;;;GAIG;AAEH,MAAM,UAAU,mBAAmB,CAAqB,IAAY;IACnE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAE7C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,SAAS,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAChF,MAAM,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAExE,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;IACvF,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC,CAAC,8BAA8B;IAEvD,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACtD,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,MAAM,UAAU,GAAG,gBAAgB,KAAK,CAAC,IAAI,eAAe,KAAK,CAAC,QAAQ,kCAAkC,KAAK,CAAC,OAAO,QAAQ,IAAI,YAAY,CAAC;QAClJ,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,UAAU,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;IACvD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,wCAAwC;QACxC,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC;YAC/B,aAAa,EAAE,KAAK,CAAC,QAAQ;YAC7B,KAAK,EAAE,iBAAiB;YACxB,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACvD,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,CAAC,2BAA2B;IACzC,CAAC;AACF,CAAC;AAED;;;;;;;GAOG;AAEH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAqB,IAAY,EAAE,MAAuB;IACpF,kDAAkD;IAClD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,6CAA6C;IAC7C,IAAI,YAAY,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAClD,YAAY,GAAG,oBAAoB,CAAC,YAAY,EAAE,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;IAE7E,MAAM,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;GAMG;AAEH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAqB,IAAY,EAAE,MAAuB;IACvF,kDAAkD;IAClD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,6CAA6C;IAC7C,IAAI,YAAY,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAClD,YAAY,GAAG,oBAAoB,CAAC,YAAY,EAAE,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;IAE7E,MAAM,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;AACjD,CAAC;AAED;;GAEG;AAEH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,OAAgD,EAChD,OAA8C;IAE9C,qDAAqD;IACrD,IAAI,IAAY,CAAC;IACjB,IAAI,MAAkC,CAAC;IAEvC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QACjC,IAAI,GAAG,OAAO,CAAC;IAChB,CAAC;SAAM,CAAC;QACP,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,MAAM,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC1B,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3B,CAAC;iBAAM,CAAC;gBACP,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;QACF,CAAC;QACD,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,CAAC;IAED,iGAAiG;IACjG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;QACvB,qBAAqB,EAAE,KAAK;QAC5B,iBAAiB,EAAE,OAAO,EAAE,SAAS;QACrC,MAAM;QACN,MAAM,EAAE,WAAW;KACnB,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACxC,MAAM;IACN,eAAe;IACf,iBAAiB;IACjB,4BAA4B;IAC5B,8BAA8B;IAC9B,2BAA2B;IAC3B,mBAAmB;IACnB,KAAK;IACL,QAAQ;IACR,eAAe;CACf,CAAC","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport { ATOMIC_GUIDE_COMMAND_NAME, ATOMIC_GUIDE_HELP_CHOICES, atomicGuideModeForChoice, getAtomicGuideMessage, isAtomicGuideHelpChoice, normalizeAtomicGuideMode } from \"./atomic-guide-command.ts\";\nimport { formatAuthStorageLoadFailedMessage, formatNoApiKeyFoundMessage, formatNoModelSelectedMessage, formatUnresolvedModelMessage } from \"./auth-guidance.ts\";\nimport { expandPromptTemplate } from \"./prompt-templates.ts\";\nimport { stripFrontmatter } from \"../utils/frontmatter.ts\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\nimport type { PromptOptions } from \"./agent-session-types.ts\";\n\nexport async function prompt(this: AgentSession, text: string, options?: PromptOptions): Promise<void> {\n\tconst expandPromptTemplates = options?.expandPromptTemplates ?? true;\n\tconst preflightResult = options?.preflightResult;\n\tlet messages: AgentMessage[] | undefined;\n\n\ttry {\n\t\t// Handle slash commands first (execute immediately, even during streaming).\n\t\t// Builtin and extension commands manage their own LLM interaction via custom messages.\n\t\tif (expandPromptTemplates && text.startsWith(\"/\")) {\n\t\t\tconst handledBuiltin = await this._tryExecuteBuiltinSlashCommand(text);\n\t\t\tif (handledBuiltin) {\n\t\t\t\tpreflightResult?.(true);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst handledExtension = await this._tryExecuteExtensionCommand(text);\n\t\t\tif (handledExtension) {\n\t\t\t\tpreflightResult?.(true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Emit input event for extension interception (before skill/template expansion)\n\t\tlet currentText = text;\n\t\tlet currentImages = options?.images;\n\t\tif (this._extensionRunner.hasHandlers(\"input\")) {\n\t\t\tconst inputResult = await this._extensionRunner.emitInput(\n\t\t\t\tcurrentText,\n\t\t\t\tcurrentImages,\n\t\t\t\toptions?.source ?? \"interactive\",\n\t\t\t\tthis.isStreaming ? options?.streamingBehavior : undefined,\n\t\t\t);\n\t\t\tif (inputResult.action === \"handled\") {\n\t\t\t\tpreflightResult?.(true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (inputResult.action === \"transform\") {\n\t\t\t\tcurrentText = inputResult.text;\n\t\t\t\tcurrentImages = inputResult.images ?? currentImages;\n\t\t\t}\n\t\t}\n\n\t\t// Expand skill commands (/skill:name args) and prompt templates (/template args)\n\t\tlet expandedText = currentText;\n\t\tif (expandPromptTemplates) {\n\t\t\texpandedText = this._expandSkillCommand(expandedText);\n\t\t\texpandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);\n\t\t}\n\n\t\t// If streaming, queue via steer() or followUp() based on option\n\t\tif (this.isStreaming) {\n\t\t\tif (!options?.streamingBehavior) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (options.streamingBehavior === \"followUp\") {\n\t\t\t\tawait this._queueFollowUp(expandedText, currentImages);\n\t\t\t} else {\n\t\t\t\tawait this._queueSteer(expandedText, currentImages);\n\t\t\t}\n\t\t\tpreflightResult?.(true);\n\t\t\treturn;\n\t\t}\n\n\t\t// Flush any pending bash messages before the new prompt\n\t\tthis._flushPendingBashMessages();\n\n\t\t// Validate model\n\t\tif (!this.model) {\n\t\t\tthrow new Error(formatNoModelSelectedMessage());\n\t\t}\n\n\t\t// Defensive guard: a model that never resolved to a real provider\n\t\t// (for example an unknown/unresolved model id that reached this path\n\t\t// as a bare string) has no `provider`, which would otherwise fail deep\n\t\t// in auth resolution as the confusing \"No API key found for undefined\".\n\t\t// Surface a clear, accurate \"unknown model\" error instead.\n\t\tconst resolvedProvider = (this.model as { provider?: unknown }).provider;\n\t\tif (typeof resolvedProvider !== \"string\" || resolvedProvider.length === 0) {\n\t\t\tthrow new Error(formatUnresolvedModelMessage(this.model));\n\t\t}\n\n\t\tif (!this._modelRegistry.hasConfiguredAuth(this.model)) {\n\t\t\t// A failed credential-store load (for example auth.json briefly locked\n\t\t\t// by a concurrent process, or invalid JSON) leaves an empty in-memory\n\t\t\t// credential set. That would otherwise be misreported here as\n\t\t\t// \"No API key found\" even though the credentials exist on disk. Surface\n\t\t\t// the real load failure instead so configured providers are not falsely\n\t\t\t// reported as unauthenticated (issue #1431).\n\t\t\tconst authLoadError = this._modelRegistry.authStorage.getLoadError();\n\t\t\tif (authLoadError) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tformatAuthStorageLoadFailedMessage(this.model.provider, authLoadError),\n\t\t\t\t\t{ cause: authLoadError },\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst isOAuth = this._modelRegistry.isUsingOAuth(this.model);\n\t\t\tif (isOAuth) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Authentication failed for \"${this.model.provider}\". ` +\n\t\t\t\t\t\t`Credentials may have expired or network is unavailable. ` +\n\t\t\t\t\t\t`Run '/login ${this.model.provider}' to re-authenticate.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new Error(formatNoApiKeyFoundMessage(this.model.provider));\n\t\t}\n\n\t\t// Check if we need to compact before sending (catches aborted responses)\n\t\tconst lastAssistant = this._findLastAssistantMessage();\n\t\tif (lastAssistant) {\n\t\t\tawait this._checkCompaction(lastAssistant, false);\n\t\t}\n\n\t\t// Build messages array (custom message if any, then user message)\n\t\tmessages = [];\n\n\t\t// Add user message\n\t\tconst userContent: (TextContent | ImageContent)[] = [{ type: \"text\", text: expandedText }];\n\t\tif (currentImages) {\n\t\t\tuserContent.push(...currentImages);\n\t\t}\n\t\tmessages.push({\n\t\t\trole: \"user\",\n\t\t\tcontent: userContent,\n\t\t\ttimestamp: Date.now(),\n\t\t});\n\n\t\t// Inject any pending \"nextTurn\" messages as context alongside the user message\n\t\tfor (const msg of this._pendingNextTurnMessages) {\n\t\t\tmessages.push(msg);\n\t\t}\n\t\tthis._pendingNextTurnMessages = [];\n\n\t\t// Emit before_agent_start extension event\n\t\tconst result = await this._extensionRunner.emitBeforeAgentStart(\n\t\t\texpandedText,\n\t\t\tcurrentImages,\n\t\t\tthis._baseSystemPrompt,\n\t\t\tthis._baseSystemPromptOptions,\n\t\t);\n\t\t// Add all custom messages from extensions\n\t\tif (result?.messages) {\n\t\t\tfor (const msg of result.messages) {\n\t\t\t\tmessages.push({\n\t\t\t\t\trole: \"custom\",\n\t\t\t\t\tcustomType: msg.customType,\n\t\t\t\t\tcontent: msg.content ?? [],\n\t\t\t\t\tdisplay: msg.display,\n\t\t\t\t\tdetails: msg.details,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\t// Apply extension-modified system prompt, or reset to base\n\t\tif (result?.systemPrompt !== undefined) {\n\t\t\tthis._systemPromptOverride = result.systemPrompt;\n\t\t\tthis.agent.state.systemPrompt = result.systemPrompt;\n\t\t} else {\n\t\t\t// Ensure we're using the base prompt (in case previous turn had modifications)\n\t\t\tthis._systemPromptOverride = undefined;\n\t\t\tthis.agent.state.systemPrompt = this._baseSystemPrompt;\n\t\t}\n\t} catch (error) {\n\t\tpreflightResult?.(false);\n\t\tthrow error;\n\t}\n\n\tpreflightResult?.(true);\n\tawait this._runAgentPrompt(messages);\n}\n\n\nexport async function _runAgentPrompt(this: AgentSession, messages: AgentMessage | AgentMessage[]): Promise<void> {\n\ttry {\n\t\tawait this.agent.prompt(messages);\n\t\tawait this.waitForRetry();\n\t\tawait this._continueQueuedAgentMessages();\n\t\tawait this._awaitPendingPostCompactionContinuation();\n\t} finally {\n\t\tthis._systemPromptOverride = undefined;\n\t}\n}\n\nexport async function _runAgentContinue(this: AgentSession): Promise<void> {\n\tawait this.agent.continue();\n\tawait this.waitForRetry();\n\tawait this._continueQueuedAgentMessages();\n}\n\n\nexport async function _continueQueuedAgentMessages(this: AgentSession): Promise<void> {\n\tawait this._agentEventQueue;\n\n\twhile (this.agent.hasQueuedMessages()) {\n\t\tawait this.agent.continue();\n\t\tawait this.waitForRetry();\n\t\tawait this._agentEventQueue;\n\t}\n}\n\n/**\n * Try to execute a built-in slash command. Returns true if command was found and executed.\n */\n\nexport async function _tryExecuteBuiltinSlashCommand(this: AgentSession, text: string): Promise<boolean> {\n\tconst spaceIndex = text.indexOf(\" \");\n\tconst commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);\n\tif (commandName !== ATOMIC_GUIDE_COMMAND_NAME) return false;\n\n\tconst args = spaceIndex === -1 ? \"\" : text.slice(spaceIndex + 1);\n\tconst mode = normalizeAtomicGuideMode(args);\n\tif (mode === \"help\" && this._extensionUIContext) {\n\t\tconst choice = await this._extensionUIContext.select(\"Atomic. Select where to start:\", [\n\t\t\t...ATOMIC_GUIDE_HELP_CHOICES,\n\t\t]);\n\t\tif (!choice || !isAtomicGuideHelpChoice(choice)) return true;\n\t\tawait this.sendCustomMessage(\n\t\t\t{\n\t\t\t\tcustomType: \"atomic\",\n\t\t\t\tcontent: getAtomicGuideMessage(atomicGuideModeForChoice(choice), this._cwd),\n\t\t\t\tdisplay: true,\n\t\t\t},\n\t\t\t{ triggerTurn: false },\n\t\t);\n\t\treturn true;\n\t}\n\n\tawait this.sendCustomMessage(\n\t\t{\n\t\t\tcustomType: \"atomic\",\n\t\t\tcontent: getAtomicGuideMessage(mode, this._cwd),\n\t\t\tdisplay: true,\n\t\t},\n\t\t{ triggerTurn: false },\n\t);\n\treturn true;\n}\n\n/**\n * Try to execute an extension command. Returns true if command was found and executed.\n */\n\nexport async function _tryExecuteExtensionCommand(this: AgentSession, text: string): Promise<boolean> {\n\t// Parse command name and args\n\tconst spaceIndex = text.indexOf(\" \");\n\tconst commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);\n\tconst args = spaceIndex === -1 ? \"\" : text.slice(spaceIndex + 1);\n\n\tconst command = this._extensionRunner.getCommand(commandName);\n\tif (!command) return false;\n\n\t// Get command context from extension runner (includes session control methods)\n\tconst ctx = this._extensionRunner.createCommandContext();\n\n\ttry {\n\t\tawait command.handler(args, ctx);\n\t\treturn true;\n\t} catch (err) {\n\t\t// Emit error via extension runner\n\t\tthis._extensionRunner.emitError({\n\t\t\textensionPath: `command:${commandName}`,\n\t\t\tevent: \"command\",\n\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t});\n\t\treturn true;\n\t}\n}\n\n/**\n * Expand skill commands (/skill:name args) to their full content.\n * Returns the expanded text, or the original text if not a skill command or skill not found.\n * Emits errors via extension runner if file read fails.\n */\n\nexport function _expandSkillCommand(this: AgentSession, text: string): string {\n\tif (!text.startsWith(\"/skill:\")) return text;\n\n\tconst spaceIndex = text.indexOf(\" \");\n\tconst skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);\n\tconst args = spaceIndex === -1 ? \"\" : text.slice(spaceIndex + 1).trim();\n\n\tconst skill = this.resourceLoader.getSkills().skills.find((s) => s.name === skillName);\n\tif (!skill) return text; // Unknown skill, pass through\n\n\ttry {\n\t\tconst content = readFileSync(skill.filePath, \"utf-8\");\n\t\tconst body = stripFrontmatter(content).trim();\n\t\tconst skillBlock = `<skill name=\"${skill.name}\" location=\"${skill.filePath}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n\t\treturn args ? `${skillBlock}\\n\\n${args}` : skillBlock;\n\t} catch (err) {\n\t\t// Emit error like extension commands do\n\t\tthis._extensionRunner.emitError({\n\t\t\textensionPath: skill.filePath,\n\t\t\tevent: \"skill_expansion\",\n\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t});\n\t\treturn text; // Return original on error\n\t}\n}\n\n/**\n * Queue a steering message while the agent is running.\n * Delivered after the current assistant turn finishes executing its tool calls,\n * before the next LLM call.\n * Expands skill commands and prompt templates. Errors on extension commands.\n * @param images Optional image attachments to include with the message\n * @throws Error if text is an extension command\n */\n\nexport async function steer(this: AgentSession, text: string, images?: ImageContent[]): Promise<void> {\n\t// Check for extension commands (cannot be queued)\n\tif (text.startsWith(\"/\")) {\n\t\tthis._throwIfExtensionCommand(text);\n\t}\n\n\t// Expand skill commands and prompt templates\n\tlet expandedText = this._expandSkillCommand(text);\n\texpandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);\n\n\tawait this._queueSteer(expandedText, images);\n}\n\n/**\n * Queue a follow-up message to be processed after the agent finishes.\n * Delivered only when agent has no more tool calls or steering messages.\n * Expands skill commands and prompt templates. Errors on extension commands.\n * @param images Optional image attachments to include with the message\n * @throws Error if text is an extension command\n */\n\nexport async function followUp(this: AgentSession, text: string, images?: ImageContent[]): Promise<void> {\n\t// Check for extension commands (cannot be queued)\n\tif (text.startsWith(\"/\")) {\n\t\tthis._throwIfExtensionCommand(text);\n\t}\n\n\t// Expand skill commands and prompt templates\n\tlet expandedText = this._expandSkillCommand(text);\n\texpandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);\n\n\tawait this._queueFollowUp(expandedText, images);\n}\n\n/**\n * Internal: Queue a steering message (already expanded, no extension command check).\n */\n\nexport async function sendUserMessage(this: AgentSession, \n\tcontent: string | (TextContent | ImageContent)[],\n\toptions?: { deliverAs?: \"steer\" | \"followUp\" },\n): Promise<void> {\n\t// Normalize content to text string + optional images\n\tlet text: string;\n\tlet images: ImageContent[] | undefined;\n\n\tif (typeof content === \"string\") {\n\t\ttext = content;\n\t} else {\n\t\tconst textParts: string[] = [];\n\t\timages = [];\n\t\tfor (const part of content) {\n\t\t\tif (part.type === \"text\") {\n\t\t\t\ttextParts.push(part.text);\n\t\t\t} else {\n\t\t\t\timages.push(part);\n\t\t\t}\n\t\t}\n\t\ttext = textParts.join(\"\\n\");\n\t\tif (images.length === 0) images = undefined;\n\t}\n\n\t// Use prompt() with expandPromptTemplates: false to skip command handling and template expansion\n\tawait this.prompt(text, {\n\t\texpandPromptTemplates: false,\n\t\tstreamingBehavior: options?.deliverAs,\n\t\timages,\n\t\tsource: \"extension\",\n\t});\n}\n\n/**\n * Clear all queued messages and return them.\n * Useful for restoring to editor when user aborts.\n * @returns Object with steering and followUp arrays\n */\n\nexport const agentSessionPromptMethods = {\n\tprompt,\n\t_runAgentPrompt,\n\t_runAgentContinue,\n\t_continueQueuedAgentMessages,\n\t_tryExecuteBuiltinSlashCommand,\n\t_tryExecuteExtensionCommand,\n\t_expandSkillCommand,\n\tsteer,\n\tfollowUp,\n\tsendUserMessage,\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"agent-session-retry.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-retry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAO,gBAAgB,EAAS,MAAM,8BAA8B,CAAC;AAMjF,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AA4E9F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CA6BxF;AAED;;;;;;;;GAQG;AAEH,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAWrG;AAED;;;;;;;;;;;;;GAaG;AAEH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAsBzF;AAmCD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAsBvF;AAED;;;GAGG;AAEH,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAmE/G;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CA4E3G;AAED;;GAEG;AAEH,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAInD;AAED;;;GAGG;AAEH,wBAAsB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpE;AAED,kDAAkD;AAElD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAE9E;AAMD;;;;;;;GAOG;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;CAUpC,CAAC","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, AssistantMessage, Model } from \"@earendil-works/pi-ai/compat\";\nimport { clampThinkingLevel, isContextOverflow, modelsAreEqual } from \"@earendil-works/pi-ai/compat\";\nimport { sleep } from \"../utils/sleep.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport { isCopilotGeminiModel } from \"./copilot-gemini-payload-sanitizer.ts\";\nimport { normalizeToolArgumentsForModel } from \"./copilot-gemini-tool-arguments.ts\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\n\n\nconst THINKING_SUFFIXES = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"] as const satisfies readonly ThinkingLevel[];\nconst THINKING_SUFFIX_SET: ReadonlySet<string> = new Set(THINKING_SUFFIXES);\n\nfunction modelLabel(model: Model<Api> | undefined): string {\n\treturn model ? `${model.provider}/${model.id}` : \"unknown model\";\n}\n\nfunction splitFallbackModel(value: string): { modelId: string; thinkingLevel?: ThinkingLevel } {\n\tconst trimmed = value.trim();\n\tconst index = trimmed.lastIndexOf(\":\");\n\tif (index < 0) return { modelId: trimmed };\n\tconst suffix = trimmed.slice(index + 1);\n\tif (!THINKING_SUFFIX_SET.has(suffix)) return { modelId: trimmed };\n\treturn { modelId: trimmed.slice(0, index), thinkingLevel: suffix as ThinkingLevel };\n}\n\nfunction resolveFallbackModel(this: AgentSession, value: string): { model: Model<Api>; thinkingLevel?: ThinkingLevel } | undefined {\n\tconst parsed = splitFallbackModel(value);\n\tif (!parsed.modelId.includes(\"/\")) {\n\t\tconst available = this._modelRegistry.getAvailable().filter((model) => model.id === parsed.modelId);\n\t\tconst preferredProvider = this.model?.provider ?? this.settingsManager.getDefaultProvider();\n\t\tconst model = available.find((candidate) => candidate.provider === preferredProvider) ?? (available.length === 1 ? available[0] : undefined);\n\t\treturn model ? { model, thinkingLevel: parsed.thinkingLevel } : undefined;\n\t}\n\tconst slash = parsed.modelId.indexOf(\"/\");\n\tconst provider = parsed.modelId.slice(0, slash);\n\tconst modelId = parsed.modelId.slice(slash + 1);\n\tconst model = this._modelRegistry.find(provider, modelId);\n\tif (!model || !this._modelRegistry.hasConfiguredAuth(model)) return undefined;\n\treturn { model, thinkingLevel: parsed.thinkingLevel };\n}\n\nfunction fallbackKey(model: Model<Api>, thinkingLevel: ThinkingLevel | undefined): string {\n\treturn `${model.provider}/${model.id}:${thinkingLevel ?? \"\"}`;\n}\n\nfunction hasProviderTransportDiagnostic(value: unknown, seen = new Set<unknown>(), includeMessageFields = false): boolean {\n\tif (value === null || value === undefined || seen.has(value)) return false;\n\tif (typeof value !== \"object\") {\n\t\treturn /provider_transport_failure|websocket.*error|sse.*404/i.test(String(value));\n\t}\n\tseen.add(value);\n\tconst record = value as Record<string, unknown>;\n\tconst fields = includeMessageFields\n\t\t? [record.type, record.code, record.name, record.message, record.errorMessage, record.status, record.statusCode]\n\t\t: [record.type, record.code, record.name, record.status, record.statusCode];\n\tfor (const field of fields) {\n\t\t if (typeof field === \"string\" || typeof field === \"number\") {\n\t\t\tif (/provider_transport_failure|websocket.*error|sse.*404|\\b404\\b/i.test(String(field))) return true;\n\t\t}\n\t}\n\tfor (const nested of [record.error, record.cause, ...(Array.isArray(record.diagnostics) ? record.diagnostics : [])]) {\n\t\tif (hasProviderTransportDiagnostic(nested, seen, true)) return true;\n\t}\n\treturn false;\n}\n\nfunction hasProviderModelUnavailableDiagnostic(value: unknown, seen = new Set<unknown>(), includeMessageFields = false): boolean {\n\tif (value === null || value === undefined || seen.has(value)) return false;\n\tif (typeof value !== \"object\") return false;\n\tseen.add(value);\n\tconst record = value as Record<string, unknown>;\n\tconst fields = includeMessageFields\n\t\t? [record.type, record.code, record.name, record.message, record.errorMessage]\n\t\t: [record.type, record.code, record.name, record.errorMessage];\n\tfor (const field of fields) {\n\t\tif (typeof field === \"string\" && /model(?:[_\\s-].*)?(?:not[_\\s-]?found|unavailable|unknown|disabled)|model[_-]?not[_-]?found/i.test(field)) return true;\n\t}\n\tfor (const nested of [record.error, record.cause, ...(Array.isArray(record.diagnostics) ? record.diagnostics : [])]) {\n\t\tif (hasProviderModelUnavailableDiagnostic(nested, seen, true)) return true;\n\t}\n\treturn false;\n}\nexport function _isRetryableError(this: AgentSession, message: AssistantMessage): boolean {\n\tif (message.stopReason !== \"error\") return false;\n\n\t// Context overflow is handled by compaction, not retry\n\tconst contextWindow = this.model?.contextWindow ?? 0;\n\tif (isContextOverflow(message, contextWindow)) return false;\n\n\tif (hasProviderTransportDiagnostic(message) || hasProviderModelUnavailableDiagnostic(message)) return true;\n\tif (!message.errorMessage) return false;\n\n\tconst err = message.errorMessage;\n\n\t// Safety triggers surface through structured API signals that pi-ai maps to\n\t// stopReason \"error\":\n\t// - Anthropic `refusal` stops become pi-ai's canned \"The model refused to\n\t// complete the request\" error message;\n\t// - OpenAI-style APIs (and github-copilot CAPI, which also maps spurious\n\t// Gemini RECITATION/safety blocks this way) surface\n\t// `finish_reason: content_filter`.\n\t// Spurious safety triggers are common in agentic settings, so these are\n\t// re-requested like transient failures, bounded by maxRetries (issue #1608).\n\tif (/refused to complete the request|finish.?reason:?\\s*content.?filter/i.test(err)) {\n\t\treturn true;\n\t}\n\n\t// Match: overloaded_error, provider returned error, rate limit, 429, 500, 502, 503, 504, service unavailable, network/connection errors (including connection lost), WebSocket transport closes/errors, fetch failed, premature stream endings, HTTP/2 closed before response, terminated, retry delay exceeded, and a bare/transient provider finish_reason \"error\" (e.g. github-copilot Gemini's CAPI mapping of MALFORMED_FUNCTION_CALL/OTHER/UNEXPECTED_TOOL_CALL). These are provider-agnostic transient failures.\n\treturn /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay|finish.?reason:?\\s*error/i.test(\n\t\terr,\n\t);\n}\n\n/**\n * For GitHub Copilot Gemini, reconstruct flattened tool-call arguments\n * (for example `edits[0].newText`) into the nested arrays/objects Gemini\n * produced before the assistant message is persisted, so saved transcripts\n * never carry the flattened CAPI wire shape and replays loaded from disk match\n * the structure Gemini signed. In-place, gated to Copilot Gemini, and a no-op\n * for well-formed arguments or any other provider/model. The outbound replay\n * normalizer still heals already-persisted (legacy) sessions on the wire.\n */\n\nexport function _normalizePersistedGeminiToolArgs(this: AgentSession, message: AssistantMessage): void {\n\tconst model = this.model;\n\tif (!model || !isCopilotGeminiModel(model)) return;\n\tfor (const block of message.content) {\n\t\tif (block.type !== \"toolCall\") continue;\n\t\tconst tool = this._toolRegistry.get(block.name);\n\t\tconst normalized = normalizeToolArgumentsForModel(block.arguments, model, tool?.parameters);\n\t\tif (normalized !== block.arguments && normalized !== null && typeof normalized === \"object\") {\n\t\t\tblock.arguments = normalized as Record<string, unknown>;\n\t\t}\n\t}\n}\n\n/**\n * Detect a degenerate empty completion: the provider ended the stream with no\n * usable content and zero output tokens. Seen with github-copilot Gemini models\n * that emit finish_reason \"stop\" (or a tool-use stop) with an empty content array\n * and 0 output tokens, leaving the turn dead instead of producing the next step.\n *\n * These are treated as retryable so the harness re-issues the request rather than\n * silently stopping mid-task. Guarded tightly (no text, no tool call, no thinking,\n * and output === 0) so legitimate non-empty turns are never matched.\n *\n * Intentionally provider-agnostic (not gated to Copilot Gemini): a degenerate\n * empty turn is a transient failure for any provider. It is bounded by\n * `maxRetries` and falls through to normal handling on exhaustion.\n */\n\nexport function _isEmptyCompletion(this: AgentSession, message: AssistantMessage): boolean {\n\t// Only \"completed\" stop reasons can be deceptively empty. Real errors are handled\n\t// by _isRetryableError; aborted/length turns are intentional outcomes.\n\tif (message.stopReason !== \"stop\" && message.stopReason !== \"toolUse\") return false;\n\n\tconst content = message.content;\n\tif (Array.isArray(content)) {\n\t\tconst hasContent = content.some((part) => {\n\t\t\tif (part.type === \"text\") return part.text.trim().length > 0;\n\t\t\tif (part.type === \"toolCall\") return true;\n\t\t\tif (part.type === \"thinking\") return part.redacted === true || part.thinking.trim().length > 0;\n\t\t\treturn true; // unknown part types count as content\n\t\t});\n\t\tif (hasContent) return false;\n\t}\n\n\t// A turn that produced output tokens but no surfaced content is not \"empty\"\n\t// (e.g. reasoning-only responses); leave those alone. Note: a provider that\n\t// fails to report `usage` (output defaults to 0) would make every\n\t// content-less turn match here; the dual requirement (empty content AND zero\n\t// output) keeps that false-positive risk low in practice.\n\treturn (message.usage?.output ?? 0) === 0;\n}\n\n/**\n * Detect a canned provider-side safety refusal that arrives as a *successful*\n * completion instead of an error. Seen with github-copilot GPT models under\n * heavy contexts: the endpoint intercepts the request and returns exactly\n * \"I'm sorry, but I cannot assist with that request.\" with zero usage and a\n * spurious stopReason of \"length\" (or \"stop\"), which the agent would otherwise\n * accept as the final answer and dead-end the turn (issue #1608).\n *\n * A text heuristic is unavoidable here: the OpenAI Responses API does signal\n * these interceptions structurally (`refusal` content parts plus\n * `incomplete_details.reason: \"content_filter\"`), but pi-ai's normalization\n * folds refusal parts into plain text blocks and collapses the `incomplete`\n * status to stopReason \"length\", discarding the reason — so no structured\n * refusal marker survives on the AssistantMessage. Providers whose safety\n * signals DO survive as structured errors (Anthropic refusal stops, OpenAI\n * `finish_reason: content_filter`) are matched in _isRetryableError instead.\n *\n * Guarded tightly so legitimate turns are never matched:\n * - only non-error completion stops (\"stop\" | \"length\" | \"toolUse\");\n * - content must be a single short canned refusal text — any tool call,\n * thinking block, or additional prose disqualifies the message;\n * - usage.output must be 0: a genuine model-authored refusal bills output\n * tokens, while the intercepted canned refusal reports zero usage.\n *\n * Treated as retryable so the harness re-requests the model call rather than\n * accepting the refusal; bounded by `maxRetries` like every other retry path.\n */\n\nconst CANNED_SAFETY_REFUSAL_PATTERN =\n\t/^(?:i['’]?m sorry[,.]?\\s+(?:but\\s+)?|sorry[,.]?\\s+(?:but\\s+)?)?i\\s+(?:cannot|can['’]?t|can\\s+not|am\\s+unable\\s+to|am\\s+not\\s+able\\s+to)\\s+(?:assist|help|comply|continue)(?:\\s+with)?(?:\\s+(?:that|this))?(?:\\s+(?:request|task))?\\.?$/i;\n\nconst CANNED_SAFETY_REFUSAL_MAX_LENGTH = 120;\n\nexport function _isSafetyRefusal(this: AgentSession, message: AssistantMessage): boolean {\n\t// Real errors are handled by _isRetryableError; aborts are user-initiated.\n\tif (message.stopReason !== \"stop\" && message.stopReason !== \"length\" && message.stopReason !== \"toolUse\") {\n\t\treturn false;\n\t}\n\n\tconst content = message.content;\n\tif (!Array.isArray(content) || content.length === 0) return false;\n\n\tlet text = \"\";\n\tfor (const part of content) {\n\t\tif (part.type === \"toolCall\") return false;\n\t\tif (part.type === \"thinking\" && (part.redacted === true || part.thinking.trim().length > 0)) return false;\n\t\tif (part.type === \"text\") text += part.text;\n\t}\n\ttext = text.trim();\n\tif (text.length === 0 || text.length > CANNED_SAFETY_REFUSAL_MAX_LENGTH) return false;\n\tif (!CANNED_SAFETY_REFUSAL_PATTERN.test(text)) return false;\n\n\t// Zero billed output distinguishes a provider interception from legitimate\n\t// model-authored refusal prose, which is never auto-retried.\n\treturn (message.usage?.output ?? 0) === 0;\n}\n\n/**\n * Handle retryable errors with exponential backoff.\n * @returns true if retry was initiated, false if max retries exceeded or disabled\n */\n\nexport async function _trySwitchToFallbackModel(this: AgentSession, message: AssistantMessage): Promise<boolean> {\n\tif (this._fallbackModels.length === 0 || !this.model) return false;\n\n\tthis._fallbackAttemptedKeys.add(fallbackKey(this.model, this.thinkingLevel));\n\tconst fromModel = this.model;\n\tfor (const rawCandidate of this._fallbackModels) {\n\t\tconst candidate = resolveFallbackModel.call(this, rawCandidate);\n\t\tif (!candidate) continue;\n\t\tconst key = fallbackKey(candidate.model, candidate.thinkingLevel);\n\t\tif (this._fallbackAttemptedKeys.has(key)) continue;\n\t\tconst nextModel = this._withContextWindowForModelSwitch(candidate.model);\n\t\tconst nextLevel = clampThinkingLevel(\n\t\t\tnextModel,\n\t\t\tcandidate.thinkingLevel ?? this.settingsManager.getDefaultThinkingLevel() ?? this.thinkingLevel ?? DEFAULT_THINKING_LEVEL,\n\t\t) as ThinkingLevel;\n\t\tif (modelsAreEqual(candidate.model, fromModel) && nextLevel === this.thinkingLevel) continue;\n\t\tif (this._retryAttempt > 0) {\n\t\t\tthis._emit({\n\t\t\t\ttype: \"auto_retry_end\",\n\t\t\t\tsuccess: true,\n\t\t\t\tattempt: Math.max(0, this._retryAttempt - 1),\n\t\t\t});\n\t\t}\n\t\tthis._fallbackAttemptedKeys.add(key);\n\t\tthis._emit({\n\t\t\ttype: \"model_fallback_start\",\n\t\t\tfrom: modelLabel(fromModel),\n\t\t\tto: `${nextModel.provider}/${nextModel.id}`,\n\t\t\treason: message.errorMessage || \"Retryable model error\",\n\t\t\tattempt: this._fallbackAttemptedKeys.size - 1,\n\t\t});\n\n\t\tconst messages = this.agent.state.messages;\n\t\tif (messages.length > 0 && messages[messages.length - 1]?.role === \"assistant\") {\n\t\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t\t}\n\t\tthis.agent.state.model = nextModel;\n\t\tthis.sessionManager.appendModelChange(nextModel.provider, nextModel.id);\n\t\tthis._appendContextWindowChangeIfChanged(fromModel, nextModel);\n\t\tthis.agent.state.thinkingLevel = nextLevel;\n\t\tthis.sessionManager.appendThinkingLevelChange(nextLevel);\n\t\tthis._refreshBaseSystemPromptFromActiveTools();\n\t\tthis._emitModelChanged(nextModel, fromModel, \"fallback\");\n\t\tawait this._emitModelSelect(nextModel, fromModel, \"fallback\");\n\t\tthis._retryAttempt = 0;\n\n\t\tsetTimeout(() => {\n\t\t\tthis.agent.continue().then(\n\t\t\t\t() => {\n\t\t\t\t\t// A resolved continuation may still have produced an assistant\n\t\t\t\t\t// error that will be classified by agent_end and may advance to\n\t\t\t\t\t// the next fallback. Do not emit a successful fallback end here;\n\t\t\t\t\t// agent_end/turn_end clear UI state for successful turns, while\n\t\t\t\t\t// fallback exhaustion emits the failure end event.\n\t\t\t\t},\n\t\t\t\t(error: unknown) => {\n\t\t\t\t\tconst finalError = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tthis._emit({ type: \"model_fallback_end\", success: false, from: modelLabel(fromModel), to: modelLabel(nextModel), finalError });\n\t\t\t\t\tthis._retryAttempt = 0;\n\t\t\t\t\tthis._resolveRetry();\n\t\t\t\t},\n\t\t\t);\n\t\t}, 0);\n\t\treturn true;\n\t}\n\tthis._emit({ type: \"model_fallback_end\", success: false, from: modelLabel(fromModel), finalError: message.errorMessage });\n\treturn false;\n}\n\nexport async function _handleRetryableError(this: AgentSession, message: AssistantMessage): Promise<boolean> {\n\tconst settings = this.settingsManager.getRetrySettings();\n\tif (!settings.enabled) {\n\t\treturn this._trySwitchToFallbackModel(message);\n\t}\n\n\t// Retry promise is created synchronously in _handleAgentEvent for agent_end.\n\t// Keep a defensive fallback here in case a future refactor bypasses that path.\n\tif (!this._retryPromise) {\n\t\tthis._retryPromise = new Promise((resolve) => {\n\t\t\tthis._retryResolve = resolve;\n\t\t});\n\t}\n\n\tthis._retryAttempt++;\n\n\tif (this._retryAttempt > settings.maxRetries) {\n\t\tif (await this._trySwitchToFallbackModel(message)) {\n\t\t\treturn true;\n\t\t}\n\t\t// Max retries exceeded, emit final failure and reset\n\t\tthis._emit({\n\t\t\ttype: \"auto_retry_end\",\n\t\t\tsuccess: false,\n\t\t\tattempt: this._retryAttempt - 1,\n\t\t\tfinalError: message.errorMessage,\n\t\t});\n\t\tthis._retryAttempt = 0;\n\t\tthis._resolveRetry(); // Resolve so waitForRetry() completes\n\t\treturn false;\n\t}\n\n\tconst delayMs = settings.baseDelayMs * 2 ** (this._retryAttempt - 1);\n\n\tthis._emit({\n\t\ttype: \"auto_retry_start\",\n\t\tattempt: this._retryAttempt,\n\t\tmaxAttempts: settings.maxRetries,\n\t\tdelayMs,\n\t\terrorMessage: message.errorMessage || \"Unknown error\",\n\t});\n\n\t// Remove error message from agent state (keep in session for history)\n\tconst messages = this.agent.state.messages;\n\tif (messages.length > 0 && messages[messages.length - 1].role === \"assistant\") {\n\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t}\n\n\t// Wait with exponential backoff (abortable)\n\tthis._retryAbortController = new AbortController();\n\ttry {\n\t\tawait sleep(delayMs, this._retryAbortController.signal);\n\t} catch {\n\t\t// Aborted during sleep - emit end event so UI can clean up\n\t\tconst attempt = this._retryAttempt;\n\t\tthis._retryAttempt = 0;\n\t\tthis._retryAbortController = undefined;\n\t\tthis._emit({\n\t\t\ttype: \"auto_retry_end\",\n\t\t\tsuccess: false,\n\t\t\tattempt,\n\t\t\tfinalError: \"Retry cancelled\",\n\t\t});\n\t\tthis._resolveRetry();\n\t\treturn false;\n\t}\n\tthis._retryAbortController = undefined;\n\n\t// Retry via continue() - use setTimeout to break out of event handler chain\n\tsetTimeout(() => {\n\t\tthis.agent.continue().catch(() => {\n\t\t\t// Retry failed - will be caught by next agent_end\n\t\t});\n\t}, 0);\n\n\treturn true;\n}\n\n/**\n * Cancel in-progress retry.\n */\n\nexport function abortRetry(this: AgentSession): void {\n\tthis._retryAbortController?.abort();\n\t// Note: _retryAttempt is reset in the catch block of _autoRetry\n\tthis._resolveRetry();\n}\n\n/**\n * Wait for any in-progress retry to complete.\n * Returns immediately if no retry is in progress.\n */\n\nexport async function waitForRetry(this: AgentSession): Promise<void> {\n\tif (!this._retryPromise) {\n\t\treturn;\n\t}\n\n\tawait this._retryPromise;\n\tawait this.agent.waitForIdle();\n}\n\n/** Whether auto-retry is currently in progress */\n\nexport function setAutoRetryEnabled(this: AgentSession, enabled: boolean): void {\n\tthis.settingsManager.setRetryEnabled(enabled);\n}\n\n// =========================================================================\n// Bash Execution\n// =========================================================================\n\n/**\n * Execute a bash command.\n * Adds result to agent context and session.\n * @param command The bash command to execute\n * @param onChunk Optional streaming callback for output\n * @param options.excludeFromContext If true, command output won't be sent to LLM (!! prefix)\n * @param options.operations Custom BashOperations for remote execution\n */\n\nexport const agentSessionRetryMethods = {\n\t_isRetryableError,\n\t_normalizePersistedGeminiToolArgs,\n\t_isEmptyCompletion,\n\t_isSafetyRefusal,\n\t_handleRetryableError,\n\t_trySwitchToFallbackModel,\n\tabortRetry,\n\twaitForRetry,\n\tsetAutoRetryEnabled,\n};\n"]}
1
+ {"version":3,"file":"agent-session-retry.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-retry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAO,gBAAgB,EAAS,MAAM,8BAA8B,CAAC;AAMjF,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AA4E9F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CA6BxF;AAED;;;;;;;;GAQG;AAEH,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAWrG;AAED;;;;;;;;;;;;;GAaG;AAEH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAsBzF;AAmCD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAsBvF;AAED;;;GAGG;AAEH,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAmE/G;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CA4E3G;AAED;;GAEG;AAEH,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAInD;AAED;;;GAGG;AAEH,wBAAsB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpE;AAED,kDAAkD;AAElD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAE9E;AAMD;;;;;;;GAOG;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;CAUpC,CAAC","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, AssistantMessage, Model } from \"@earendil-works/pi-ai/compat\";\nimport { clampThinkingLevel, isContextOverflow, modelsAreEqual } from \"@earendil-works/pi-ai/compat\";\nimport { sleep } from \"../utils/sleep.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport { isCopilotGeminiModel } from \"./copilot-gemini-payload-sanitizer.ts\";\nimport { normalizeToolArgumentsForModel } from \"./copilot-gemini-tool-arguments.ts\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\n\n\nconst THINKING_SUFFIXES = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const satisfies readonly ThinkingLevel[];\nconst THINKING_SUFFIX_SET: ReadonlySet<string> = new Set(THINKING_SUFFIXES);\n\nfunction modelLabel(model: Model<Api> | undefined): string {\n\treturn model ? `${model.provider}/${model.id}` : \"unknown model\";\n}\n\nfunction splitFallbackModel(value: string): { modelId: string; thinkingLevel?: ThinkingLevel } {\n\tconst trimmed = value.trim();\n\tconst index = trimmed.lastIndexOf(\":\");\n\tif (index < 0) return { modelId: trimmed };\n\tconst suffix = trimmed.slice(index + 1);\n\tif (!THINKING_SUFFIX_SET.has(suffix)) return { modelId: trimmed };\n\treturn { modelId: trimmed.slice(0, index), thinkingLevel: suffix as ThinkingLevel };\n}\n\nfunction resolveFallbackModel(this: AgentSession, value: string): { model: Model<Api>; thinkingLevel?: ThinkingLevel } | undefined {\n\tconst parsed = splitFallbackModel(value);\n\tif (!parsed.modelId.includes(\"/\")) {\n\t\tconst available = this._modelRegistry.getAvailable().filter((model) => model.id === parsed.modelId);\n\t\tconst preferredProvider = this.model?.provider ?? this.settingsManager.getDefaultProvider();\n\t\tconst model = available.find((candidate) => candidate.provider === preferredProvider) ?? (available.length === 1 ? available[0] : undefined);\n\t\treturn model ? { model, thinkingLevel: parsed.thinkingLevel } : undefined;\n\t}\n\tconst slash = parsed.modelId.indexOf(\"/\");\n\tconst provider = parsed.modelId.slice(0, slash);\n\tconst modelId = parsed.modelId.slice(slash + 1);\n\tconst model = this._modelRegistry.find(provider, modelId);\n\tif (!model || !this._modelRegistry.hasConfiguredAuth(model)) return undefined;\n\treturn { model, thinkingLevel: parsed.thinkingLevel };\n}\n\nfunction fallbackKey(model: Model<Api>, thinkingLevel: ThinkingLevel | undefined): string {\n\treturn `${model.provider}/${model.id}:${thinkingLevel ?? \"\"}`;\n}\n\nfunction hasProviderTransportDiagnostic(value: unknown, seen = new Set<unknown>(), includeMessageFields = false): boolean {\n\tif (value === null || value === undefined || seen.has(value)) return false;\n\tif (typeof value !== \"object\") {\n\t\treturn /provider_transport_failure|websocket.*error|sse.*404/i.test(String(value));\n\t}\n\tseen.add(value);\n\tconst record = value as Record<string, unknown>;\n\tconst fields = includeMessageFields\n\t\t? [record.type, record.code, record.name, record.message, record.errorMessage, record.status, record.statusCode]\n\t\t: [record.type, record.code, record.name, record.status, record.statusCode];\n\tfor (const field of fields) {\n\t\t if (typeof field === \"string\" || typeof field === \"number\") {\n\t\t\tif (/provider_transport_failure|websocket.*error|sse.*404|\\b404\\b/i.test(String(field))) return true;\n\t\t}\n\t}\n\tfor (const nested of [record.error, record.cause, ...(Array.isArray(record.diagnostics) ? record.diagnostics : [])]) {\n\t\tif (hasProviderTransportDiagnostic(nested, seen, true)) return true;\n\t}\n\treturn false;\n}\n\nfunction hasProviderModelUnavailableDiagnostic(value: unknown, seen = new Set<unknown>(), includeMessageFields = false): boolean {\n\tif (value === null || value === undefined || seen.has(value)) return false;\n\tif (typeof value !== \"object\") return false;\n\tseen.add(value);\n\tconst record = value as Record<string, unknown>;\n\tconst fields = includeMessageFields\n\t\t? [record.type, record.code, record.name, record.message, record.errorMessage]\n\t\t: [record.type, record.code, record.name, record.errorMessage];\n\tfor (const field of fields) {\n\t\tif (typeof field === \"string\" && /model(?:[_\\s-].*)?(?:not[_\\s-]?found|unavailable|unknown|disabled)|model[_-]?not[_-]?found/i.test(field)) return true;\n\t}\n\tfor (const nested of [record.error, record.cause, ...(Array.isArray(record.diagnostics) ? record.diagnostics : [])]) {\n\t\tif (hasProviderModelUnavailableDiagnostic(nested, seen, true)) return true;\n\t}\n\treturn false;\n}\nexport function _isRetryableError(this: AgentSession, message: AssistantMessage): boolean {\n\tif (message.stopReason !== \"error\") return false;\n\n\t// Context overflow is handled by compaction, not retry\n\tconst contextWindow = this.model?.contextWindow ?? 0;\n\tif (isContextOverflow(message, contextWindow)) return false;\n\n\tif (hasProviderTransportDiagnostic(message) || hasProviderModelUnavailableDiagnostic(message)) return true;\n\tif (!message.errorMessage) return false;\n\n\tconst err = message.errorMessage;\n\n\t// Safety triggers surface through structured API signals that pi-ai maps to\n\t// stopReason \"error\":\n\t// - Anthropic `refusal` stops become pi-ai's canned \"The model refused to\n\t// complete the request\" error message;\n\t// - OpenAI-style APIs (and github-copilot CAPI, which also maps spurious\n\t// Gemini RECITATION/safety blocks this way) surface\n\t// `finish_reason: content_filter`.\n\t// Spurious safety triggers are common in agentic settings, so these are\n\t// re-requested like transient failures, bounded by maxRetries (issue #1608).\n\tif (/refused to complete the request|finish.?reason:?\\s*content.?filter/i.test(err)) {\n\t\treturn true;\n\t}\n\n\t// Match: overloaded_error, provider returned error, rate limit, 429, 500, 502, 503, 504, service unavailable, network/connection errors (including connection lost), WebSocket transport closes/errors, fetch failed, premature stream endings, HTTP/2 closed before response, terminated, retry delay exceeded, and a bare/transient provider finish_reason \"error\" (e.g. github-copilot Gemini's CAPI mapping of MALFORMED_FUNCTION_CALL/OTHER/UNEXPECTED_TOOL_CALL). These are provider-agnostic transient failures.\n\treturn /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay|finish.?reason:?\\s*error/i.test(\n\t\terr,\n\t);\n}\n\n/**\n * For GitHub Copilot Gemini, reconstruct flattened tool-call arguments\n * (for example `edits[0].newText`) into the nested arrays/objects Gemini\n * produced before the assistant message is persisted, so saved transcripts\n * never carry the flattened CAPI wire shape and replays loaded from disk match\n * the structure Gemini signed. In-place, gated to Copilot Gemini, and a no-op\n * for well-formed arguments or any other provider/model. The outbound replay\n * normalizer still heals already-persisted (legacy) sessions on the wire.\n */\n\nexport function _normalizePersistedGeminiToolArgs(this: AgentSession, message: AssistantMessage): void {\n\tconst model = this.model;\n\tif (!model || !isCopilotGeminiModel(model)) return;\n\tfor (const block of message.content) {\n\t\tif (block.type !== \"toolCall\") continue;\n\t\tconst tool = this._toolRegistry.get(block.name);\n\t\tconst normalized = normalizeToolArgumentsForModel(block.arguments, model, tool?.parameters);\n\t\tif (normalized !== block.arguments && normalized !== null && typeof normalized === \"object\") {\n\t\t\tblock.arguments = normalized as Record<string, unknown>;\n\t\t}\n\t}\n}\n\n/**\n * Detect a degenerate empty completion: the provider ended the stream with no\n * usable content and zero output tokens. Seen with github-copilot Gemini models\n * that emit finish_reason \"stop\" (or a tool-use stop) with an empty content array\n * and 0 output tokens, leaving the turn dead instead of producing the next step.\n *\n * These are treated as retryable so the harness re-issues the request rather than\n * silently stopping mid-task. Guarded tightly (no text, no tool call, no thinking,\n * and output === 0) so legitimate non-empty turns are never matched.\n *\n * Intentionally provider-agnostic (not gated to Copilot Gemini): a degenerate\n * empty turn is a transient failure for any provider. It is bounded by\n * `maxRetries` and falls through to normal handling on exhaustion.\n */\n\nexport function _isEmptyCompletion(this: AgentSession, message: AssistantMessage): boolean {\n\t// Only \"completed\" stop reasons can be deceptively empty. Real errors are handled\n\t// by _isRetryableError; aborted/length turns are intentional outcomes.\n\tif (message.stopReason !== \"stop\" && message.stopReason !== \"toolUse\") return false;\n\n\tconst content = message.content;\n\tif (Array.isArray(content)) {\n\t\tconst hasContent = content.some((part) => {\n\t\t\tif (part.type === \"text\") return part.text.trim().length > 0;\n\t\t\tif (part.type === \"toolCall\") return true;\n\t\t\tif (part.type === \"thinking\") return part.redacted === true || part.thinking.trim().length > 0;\n\t\t\treturn true; // unknown part types count as content\n\t\t});\n\t\tif (hasContent) return false;\n\t}\n\n\t// A turn that produced output tokens but no surfaced content is not \"empty\"\n\t// (e.g. reasoning-only responses); leave those alone. Note: a provider that\n\t// fails to report `usage` (output defaults to 0) would make every\n\t// content-less turn match here; the dual requirement (empty content AND zero\n\t// output) keeps that false-positive risk low in practice.\n\treturn (message.usage?.output ?? 0) === 0;\n}\n\n/**\n * Detect a canned provider-side safety refusal that arrives as a *successful*\n * completion instead of an error. Seen with github-copilot GPT models under\n * heavy contexts: the endpoint intercepts the request and returns exactly\n * \"I'm sorry, but I cannot assist with that request.\" with zero usage and a\n * spurious stopReason of \"length\" (or \"stop\"), which the agent would otherwise\n * accept as the final answer and dead-end the turn (issue #1608).\n *\n * A text heuristic is unavoidable here: the OpenAI Responses API does signal\n * these interceptions structurally (`refusal` content parts plus\n * `incomplete_details.reason: \"content_filter\"`), but pi-ai's normalization\n * folds refusal parts into plain text blocks and collapses the `incomplete`\n * status to stopReason \"length\", discarding the reason — so no structured\n * refusal marker survives on the AssistantMessage. Providers whose safety\n * signals DO survive as structured errors (Anthropic refusal stops, OpenAI\n * `finish_reason: content_filter`) are matched in _isRetryableError instead.\n *\n * Guarded tightly so legitimate turns are never matched:\n * - only non-error completion stops (\"stop\" | \"length\" | \"toolUse\");\n * - content must be a single short canned refusal text — any tool call,\n * thinking block, or additional prose disqualifies the message;\n * - usage.output must be 0: a genuine model-authored refusal bills output\n * tokens, while the intercepted canned refusal reports zero usage.\n *\n * Treated as retryable so the harness re-requests the model call rather than\n * accepting the refusal; bounded by `maxRetries` like every other retry path.\n */\n\nconst CANNED_SAFETY_REFUSAL_PATTERN =\n\t/^(?:i['’]?m sorry[,.]?\\s+(?:but\\s+)?|sorry[,.]?\\s+(?:but\\s+)?)?i\\s+(?:cannot|can['’]?t|can\\s+not|am\\s+unable\\s+to|am\\s+not\\s+able\\s+to)\\s+(?:assist|help|comply|continue)(?:\\s+with)?(?:\\s+(?:that|this))?(?:\\s+(?:request|task))?\\.?$/i;\n\nconst CANNED_SAFETY_REFUSAL_MAX_LENGTH = 120;\n\nexport function _isSafetyRefusal(this: AgentSession, message: AssistantMessage): boolean {\n\t// Real errors are handled by _isRetryableError; aborts are user-initiated.\n\tif (message.stopReason !== \"stop\" && message.stopReason !== \"length\" && message.stopReason !== \"toolUse\") {\n\t\treturn false;\n\t}\n\n\tconst content = message.content;\n\tif (!Array.isArray(content) || content.length === 0) return false;\n\n\tlet text = \"\";\n\tfor (const part of content) {\n\t\tif (part.type === \"toolCall\") return false;\n\t\tif (part.type === \"thinking\" && (part.redacted === true || part.thinking.trim().length > 0)) return false;\n\t\tif (part.type === \"text\") text += part.text;\n\t}\n\ttext = text.trim();\n\tif (text.length === 0 || text.length > CANNED_SAFETY_REFUSAL_MAX_LENGTH) return false;\n\tif (!CANNED_SAFETY_REFUSAL_PATTERN.test(text)) return false;\n\n\t// Zero billed output distinguishes a provider interception from legitimate\n\t// model-authored refusal prose, which is never auto-retried.\n\treturn (message.usage?.output ?? 0) === 0;\n}\n\n/**\n * Handle retryable errors with exponential backoff.\n * @returns true if retry was initiated, false if max retries exceeded or disabled\n */\n\nexport async function _trySwitchToFallbackModel(this: AgentSession, message: AssistantMessage): Promise<boolean> {\n\tif (this._fallbackModels.length === 0 || !this.model) return false;\n\n\tthis._fallbackAttemptedKeys.add(fallbackKey(this.model, this.thinkingLevel));\n\tconst fromModel = this.model;\n\tfor (const rawCandidate of this._fallbackModels) {\n\t\tconst candidate = resolveFallbackModel.call(this, rawCandidate);\n\t\tif (!candidate) continue;\n\t\tconst key = fallbackKey(candidate.model, candidate.thinkingLevel);\n\t\tif (this._fallbackAttemptedKeys.has(key)) continue;\n\t\tconst nextModel = this._withContextWindowForModelSwitch(candidate.model);\n\t\tconst nextLevel = clampThinkingLevel(\n\t\t\tnextModel,\n\t\t\tcandidate.thinkingLevel ?? this.settingsManager.getDefaultThinkingLevel() ?? this.thinkingLevel ?? DEFAULT_THINKING_LEVEL,\n\t\t) as ThinkingLevel;\n\t\tif (modelsAreEqual(candidate.model, fromModel) && nextLevel === this.thinkingLevel) continue;\n\t\tif (this._retryAttempt > 0) {\n\t\t\tthis._emit({\n\t\t\t\ttype: \"auto_retry_end\",\n\t\t\t\tsuccess: true,\n\t\t\t\tattempt: Math.max(0, this._retryAttempt - 1),\n\t\t\t});\n\t\t}\n\t\tthis._fallbackAttemptedKeys.add(key);\n\t\tthis._emit({\n\t\t\ttype: \"model_fallback_start\",\n\t\t\tfrom: modelLabel(fromModel),\n\t\t\tto: `${nextModel.provider}/${nextModel.id}`,\n\t\t\treason: message.errorMessage || \"Retryable model error\",\n\t\t\tattempt: this._fallbackAttemptedKeys.size - 1,\n\t\t});\n\n\t\tconst messages = this.agent.state.messages;\n\t\tif (messages.length > 0 && messages[messages.length - 1]?.role === \"assistant\") {\n\t\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t\t}\n\t\tthis.agent.state.model = nextModel;\n\t\tthis.sessionManager.appendModelChange(nextModel.provider, nextModel.id);\n\t\tthis._appendContextWindowChangeIfChanged(fromModel, nextModel);\n\t\tthis.agent.state.thinkingLevel = nextLevel;\n\t\tthis.sessionManager.appendThinkingLevelChange(nextLevel);\n\t\tthis._refreshBaseSystemPromptFromActiveTools();\n\t\tthis._emitModelChanged(nextModel, fromModel, \"fallback\");\n\t\tawait this._emitModelSelect(nextModel, fromModel, \"fallback\");\n\t\tthis._retryAttempt = 0;\n\n\t\tsetTimeout(() => {\n\t\t\tthis.agent.continue().then(\n\t\t\t\t() => {\n\t\t\t\t\t// A resolved continuation may still have produced an assistant\n\t\t\t\t\t// error that will be classified by agent_end and may advance to\n\t\t\t\t\t// the next fallback. Do not emit a successful fallback end here;\n\t\t\t\t\t// agent_end/turn_end clear UI state for successful turns, while\n\t\t\t\t\t// fallback exhaustion emits the failure end event.\n\t\t\t\t},\n\t\t\t\t(error: unknown) => {\n\t\t\t\t\tconst finalError = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tthis._emit({ type: \"model_fallback_end\", success: false, from: modelLabel(fromModel), to: modelLabel(nextModel), finalError });\n\t\t\t\t\tthis._retryAttempt = 0;\n\t\t\t\t\tthis._resolveRetry();\n\t\t\t\t},\n\t\t\t);\n\t\t}, 0);\n\t\treturn true;\n\t}\n\tthis._emit({ type: \"model_fallback_end\", success: false, from: modelLabel(fromModel), finalError: message.errorMessage });\n\treturn false;\n}\n\nexport async function _handleRetryableError(this: AgentSession, message: AssistantMessage): Promise<boolean> {\n\tconst settings = this.settingsManager.getRetrySettings();\n\tif (!settings.enabled) {\n\t\treturn this._trySwitchToFallbackModel(message);\n\t}\n\n\t// Retry promise is created synchronously in _handleAgentEvent for agent_end.\n\t// Keep a defensive fallback here in case a future refactor bypasses that path.\n\tif (!this._retryPromise) {\n\t\tthis._retryPromise = new Promise((resolve) => {\n\t\t\tthis._retryResolve = resolve;\n\t\t});\n\t}\n\n\tthis._retryAttempt++;\n\n\tif (this._retryAttempt > settings.maxRetries) {\n\t\tif (await this._trySwitchToFallbackModel(message)) {\n\t\t\treturn true;\n\t\t}\n\t\t// Max retries exceeded, emit final failure and reset\n\t\tthis._emit({\n\t\t\ttype: \"auto_retry_end\",\n\t\t\tsuccess: false,\n\t\t\tattempt: this._retryAttempt - 1,\n\t\t\tfinalError: message.errorMessage,\n\t\t});\n\t\tthis._retryAttempt = 0;\n\t\tthis._resolveRetry(); // Resolve so waitForRetry() completes\n\t\treturn false;\n\t}\n\n\tconst delayMs = settings.baseDelayMs * 2 ** (this._retryAttempt - 1);\n\n\tthis._emit({\n\t\ttype: \"auto_retry_start\",\n\t\tattempt: this._retryAttempt,\n\t\tmaxAttempts: settings.maxRetries,\n\t\tdelayMs,\n\t\terrorMessage: message.errorMessage || \"Unknown error\",\n\t});\n\n\t// Remove error message from agent state (keep in session for history)\n\tconst messages = this.agent.state.messages;\n\tif (messages.length > 0 && messages[messages.length - 1].role === \"assistant\") {\n\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t}\n\n\t// Wait with exponential backoff (abortable)\n\tthis._retryAbortController = new AbortController();\n\ttry {\n\t\tawait sleep(delayMs, this._retryAbortController.signal);\n\t} catch {\n\t\t// Aborted during sleep - emit end event so UI can clean up\n\t\tconst attempt = this._retryAttempt;\n\t\tthis._retryAttempt = 0;\n\t\tthis._retryAbortController = undefined;\n\t\tthis._emit({\n\t\t\ttype: \"auto_retry_end\",\n\t\t\tsuccess: false,\n\t\t\tattempt,\n\t\t\tfinalError: \"Retry cancelled\",\n\t\t});\n\t\tthis._resolveRetry();\n\t\treturn false;\n\t}\n\tthis._retryAbortController = undefined;\n\n\t// Retry via continue() - use setTimeout to break out of event handler chain\n\tsetTimeout(() => {\n\t\tthis.agent.continue().catch(() => {\n\t\t\t// Retry failed - will be caught by next agent_end\n\t\t});\n\t}, 0);\n\n\treturn true;\n}\n\n/**\n * Cancel in-progress retry.\n */\n\nexport function abortRetry(this: AgentSession): void {\n\tthis._retryAbortController?.abort();\n\t// Note: _retryAttempt is reset in the catch block of _autoRetry\n\tthis._resolveRetry();\n}\n\n/**\n * Wait for any in-progress retry to complete.\n * Returns immediately if no retry is in progress.\n */\n\nexport async function waitForRetry(this: AgentSession): Promise<void> {\n\tif (!this._retryPromise) {\n\t\treturn;\n\t}\n\n\tawait this._retryPromise;\n\tawait this.agent.waitForIdle();\n}\n\n/** Whether auto-retry is currently in progress */\n\nexport function setAutoRetryEnabled(this: AgentSession, enabled: boolean): void {\n\tthis.settingsManager.setRetryEnabled(enabled);\n}\n\n// =========================================================================\n// Bash Execution\n// =========================================================================\n\n/**\n * Execute a bash command.\n * Adds result to agent context and session.\n * @param command The bash command to execute\n * @param onChunk Optional streaming callback for output\n * @param options.excludeFromContext If true, command output won't be sent to LLM (!! prefix)\n * @param options.operations Custom BashOperations for remote execution\n */\n\nexport const agentSessionRetryMethods = {\n\t_isRetryableError,\n\t_normalizePersistedGeminiToolArgs,\n\t_isEmptyCompletion,\n\t_isSafetyRefusal,\n\t_handleRetryableError,\n\t_trySwitchToFallbackModel,\n\tabortRetry,\n\twaitForRetry,\n\tsetAutoRetryEnabled,\n};\n"]}
@@ -3,7 +3,7 @@ import { sleep } from "../utils/sleep.js";
3
3
  import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
4
4
  import { isCopilotGeminiModel } from "./copilot-gemini-payload-sanitizer.js";
5
5
  import { normalizeToolArgumentsForModel } from "./copilot-gemini-tool-arguments.js";
6
- const THINKING_SUFFIXES = ["off", "minimal", "low", "medium", "high", "xhigh"];
6
+ const THINKING_SUFFIXES = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
7
7
  const THINKING_SUFFIX_SET = new Set(THINKING_SUFFIXES);
8
8
  function modelLabel(model) {
9
9
  return model ? `${model.provider}/${model.id}` : "unknown model";
@@ -1 +1 @@
1
- {"version":3,"file":"agent-session-retry.js","sourceRoot":"","sources":["../../src/core/agent-session-retry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, AssistantMessage, Model } from \"@earendil-works/pi-ai/compat\";\nimport { clampThinkingLevel, isContextOverflow, modelsAreEqual } from \"@earendil-works/pi-ai/compat\";\nimport { sleep } from \"../utils/sleep.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport { isCopilotGeminiModel } from \"./copilot-gemini-payload-sanitizer.ts\";\nimport { normalizeToolArgumentsForModel } from \"./copilot-gemini-tool-arguments.ts\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\n\n\nconst THINKING_SUFFIXES = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"] as const satisfies readonly ThinkingLevel[];\nconst THINKING_SUFFIX_SET: ReadonlySet<string> = new Set(THINKING_SUFFIXES);\n\nfunction modelLabel(model: Model<Api> | undefined): string {\n\treturn model ? `${model.provider}/${model.id}` : \"unknown model\";\n}\n\nfunction splitFallbackModel(value: string): { modelId: string; thinkingLevel?: ThinkingLevel } {\n\tconst trimmed = value.trim();\n\tconst index = trimmed.lastIndexOf(\":\");\n\tif (index < 0) return { modelId: trimmed };\n\tconst suffix = trimmed.slice(index + 1);\n\tif (!THINKING_SUFFIX_SET.has(suffix)) return { modelId: trimmed };\n\treturn { modelId: trimmed.slice(0, index), thinkingLevel: suffix as ThinkingLevel };\n}\n\nfunction resolveFallbackModel(this: AgentSession, value: string): { model: Model<Api>; thinkingLevel?: ThinkingLevel } | undefined {\n\tconst parsed = splitFallbackModel(value);\n\tif (!parsed.modelId.includes(\"/\")) {\n\t\tconst available = this._modelRegistry.getAvailable().filter((model) => model.id === parsed.modelId);\n\t\tconst preferredProvider = this.model?.provider ?? this.settingsManager.getDefaultProvider();\n\t\tconst model = available.find((candidate) => candidate.provider === preferredProvider) ?? (available.length === 1 ? available[0] : undefined);\n\t\treturn model ? { model, thinkingLevel: parsed.thinkingLevel } : undefined;\n\t}\n\tconst slash = parsed.modelId.indexOf(\"/\");\n\tconst provider = parsed.modelId.slice(0, slash);\n\tconst modelId = parsed.modelId.slice(slash + 1);\n\tconst model = this._modelRegistry.find(provider, modelId);\n\tif (!model || !this._modelRegistry.hasConfiguredAuth(model)) return undefined;\n\treturn { model, thinkingLevel: parsed.thinkingLevel };\n}\n\nfunction fallbackKey(model: Model<Api>, thinkingLevel: ThinkingLevel | undefined): string {\n\treturn `${model.provider}/${model.id}:${thinkingLevel ?? \"\"}`;\n}\n\nfunction hasProviderTransportDiagnostic(value: unknown, seen = new Set<unknown>(), includeMessageFields = false): boolean {\n\tif (value === null || value === undefined || seen.has(value)) return false;\n\tif (typeof value !== \"object\") {\n\t\treturn /provider_transport_failure|websocket.*error|sse.*404/i.test(String(value));\n\t}\n\tseen.add(value);\n\tconst record = value as Record<string, unknown>;\n\tconst fields = includeMessageFields\n\t\t? [record.type, record.code, record.name, record.message, record.errorMessage, record.status, record.statusCode]\n\t\t: [record.type, record.code, record.name, record.status, record.statusCode];\n\tfor (const field of fields) {\n\t\t if (typeof field === \"string\" || typeof field === \"number\") {\n\t\t\tif (/provider_transport_failure|websocket.*error|sse.*404|\\b404\\b/i.test(String(field))) return true;\n\t\t}\n\t}\n\tfor (const nested of [record.error, record.cause, ...(Array.isArray(record.diagnostics) ? record.diagnostics : [])]) {\n\t\tif (hasProviderTransportDiagnostic(nested, seen, true)) return true;\n\t}\n\treturn false;\n}\n\nfunction hasProviderModelUnavailableDiagnostic(value: unknown, seen = new Set<unknown>(), includeMessageFields = false): boolean {\n\tif (value === null || value === undefined || seen.has(value)) return false;\n\tif (typeof value !== \"object\") return false;\n\tseen.add(value);\n\tconst record = value as Record<string, unknown>;\n\tconst fields = includeMessageFields\n\t\t? [record.type, record.code, record.name, record.message, record.errorMessage]\n\t\t: [record.type, record.code, record.name, record.errorMessage];\n\tfor (const field of fields) {\n\t\tif (typeof field === \"string\" && /model(?:[_\\s-].*)?(?:not[_\\s-]?found|unavailable|unknown|disabled)|model[_-]?not[_-]?found/i.test(field)) return true;\n\t}\n\tfor (const nested of [record.error, record.cause, ...(Array.isArray(record.diagnostics) ? record.diagnostics : [])]) {\n\t\tif (hasProviderModelUnavailableDiagnostic(nested, seen, true)) return true;\n\t}\n\treturn false;\n}\nexport function _isRetryableError(this: AgentSession, message: AssistantMessage): boolean {\n\tif (message.stopReason !== \"error\") return false;\n\n\t// Context overflow is handled by compaction, not retry\n\tconst contextWindow = this.model?.contextWindow ?? 0;\n\tif (isContextOverflow(message, contextWindow)) return false;\n\n\tif (hasProviderTransportDiagnostic(message) || hasProviderModelUnavailableDiagnostic(message)) return true;\n\tif (!message.errorMessage) return false;\n\n\tconst err = message.errorMessage;\n\n\t// Safety triggers surface through structured API signals that pi-ai maps to\n\t// stopReason \"error\":\n\t// - Anthropic `refusal` stops become pi-ai's canned \"The model refused to\n\t// complete the request\" error message;\n\t// - OpenAI-style APIs (and github-copilot CAPI, which also maps spurious\n\t// Gemini RECITATION/safety blocks this way) surface\n\t// `finish_reason: content_filter`.\n\t// Spurious safety triggers are common in agentic settings, so these are\n\t// re-requested like transient failures, bounded by maxRetries (issue #1608).\n\tif (/refused to complete the request|finish.?reason:?\\s*content.?filter/i.test(err)) {\n\t\treturn true;\n\t}\n\n\t// Match: overloaded_error, provider returned error, rate limit, 429, 500, 502, 503, 504, service unavailable, network/connection errors (including connection lost), WebSocket transport closes/errors, fetch failed, premature stream endings, HTTP/2 closed before response, terminated, retry delay exceeded, and a bare/transient provider finish_reason \"error\" (e.g. github-copilot Gemini's CAPI mapping of MALFORMED_FUNCTION_CALL/OTHER/UNEXPECTED_TOOL_CALL). These are provider-agnostic transient failures.\n\treturn /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay|finish.?reason:?\\s*error/i.test(\n\t\terr,\n\t);\n}\n\n/**\n * For GitHub Copilot Gemini, reconstruct flattened tool-call arguments\n * (for example `edits[0].newText`) into the nested arrays/objects Gemini\n * produced before the assistant message is persisted, so saved transcripts\n * never carry the flattened CAPI wire shape and replays loaded from disk match\n * the structure Gemini signed. In-place, gated to Copilot Gemini, and a no-op\n * for well-formed arguments or any other provider/model. The outbound replay\n * normalizer still heals already-persisted (legacy) sessions on the wire.\n */\n\nexport function _normalizePersistedGeminiToolArgs(this: AgentSession, message: AssistantMessage): void {\n\tconst model = this.model;\n\tif (!model || !isCopilotGeminiModel(model)) return;\n\tfor (const block of message.content) {\n\t\tif (block.type !== \"toolCall\") continue;\n\t\tconst tool = this._toolRegistry.get(block.name);\n\t\tconst normalized = normalizeToolArgumentsForModel(block.arguments, model, tool?.parameters);\n\t\tif (normalized !== block.arguments && normalized !== null && typeof normalized === \"object\") {\n\t\t\tblock.arguments = normalized as Record<string, unknown>;\n\t\t}\n\t}\n}\n\n/**\n * Detect a degenerate empty completion: the provider ended the stream with no\n * usable content and zero output tokens. Seen with github-copilot Gemini models\n * that emit finish_reason \"stop\" (or a tool-use stop) with an empty content array\n * and 0 output tokens, leaving the turn dead instead of producing the next step.\n *\n * These are treated as retryable so the harness re-issues the request rather than\n * silently stopping mid-task. Guarded tightly (no text, no tool call, no thinking,\n * and output === 0) so legitimate non-empty turns are never matched.\n *\n * Intentionally provider-agnostic (not gated to Copilot Gemini): a degenerate\n * empty turn is a transient failure for any provider. It is bounded by\n * `maxRetries` and falls through to normal handling on exhaustion.\n */\n\nexport function _isEmptyCompletion(this: AgentSession, message: AssistantMessage): boolean {\n\t// Only \"completed\" stop reasons can be deceptively empty. Real errors are handled\n\t// by _isRetryableError; aborted/length turns are intentional outcomes.\n\tif (message.stopReason !== \"stop\" && message.stopReason !== \"toolUse\") return false;\n\n\tconst content = message.content;\n\tif (Array.isArray(content)) {\n\t\tconst hasContent = content.some((part) => {\n\t\t\tif (part.type === \"text\") return part.text.trim().length > 0;\n\t\t\tif (part.type === \"toolCall\") return true;\n\t\t\tif (part.type === \"thinking\") return part.redacted === true || part.thinking.trim().length > 0;\n\t\t\treturn true; // unknown part types count as content\n\t\t});\n\t\tif (hasContent) return false;\n\t}\n\n\t// A turn that produced output tokens but no surfaced content is not \"empty\"\n\t// (e.g. reasoning-only responses); leave those alone. Note: a provider that\n\t// fails to report `usage` (output defaults to 0) would make every\n\t// content-less turn match here; the dual requirement (empty content AND zero\n\t// output) keeps that false-positive risk low in practice.\n\treturn (message.usage?.output ?? 0) === 0;\n}\n\n/**\n * Detect a canned provider-side safety refusal that arrives as a *successful*\n * completion instead of an error. Seen with github-copilot GPT models under\n * heavy contexts: the endpoint intercepts the request and returns exactly\n * \"I'm sorry, but I cannot assist with that request.\" with zero usage and a\n * spurious stopReason of \"length\" (or \"stop\"), which the agent would otherwise\n * accept as the final answer and dead-end the turn (issue #1608).\n *\n * A text heuristic is unavoidable here: the OpenAI Responses API does signal\n * these interceptions structurally (`refusal` content parts plus\n * `incomplete_details.reason: \"content_filter\"`), but pi-ai's normalization\n * folds refusal parts into plain text blocks and collapses the `incomplete`\n * status to stopReason \"length\", discarding the reason — so no structured\n * refusal marker survives on the AssistantMessage. Providers whose safety\n * signals DO survive as structured errors (Anthropic refusal stops, OpenAI\n * `finish_reason: content_filter`) are matched in _isRetryableError instead.\n *\n * Guarded tightly so legitimate turns are never matched:\n * - only non-error completion stops (\"stop\" | \"length\" | \"toolUse\");\n * - content must be a single short canned refusal text — any tool call,\n * thinking block, or additional prose disqualifies the message;\n * - usage.output must be 0: a genuine model-authored refusal bills output\n * tokens, while the intercepted canned refusal reports zero usage.\n *\n * Treated as retryable so the harness re-requests the model call rather than\n * accepting the refusal; bounded by `maxRetries` like every other retry path.\n */\n\nconst CANNED_SAFETY_REFUSAL_PATTERN =\n\t/^(?:i['’]?m sorry[,.]?\\s+(?:but\\s+)?|sorry[,.]?\\s+(?:but\\s+)?)?i\\s+(?:cannot|can['’]?t|can\\s+not|am\\s+unable\\s+to|am\\s+not\\s+able\\s+to)\\s+(?:assist|help|comply|continue)(?:\\s+with)?(?:\\s+(?:that|this))?(?:\\s+(?:request|task))?\\.?$/i;\n\nconst CANNED_SAFETY_REFUSAL_MAX_LENGTH = 120;\n\nexport function _isSafetyRefusal(this: AgentSession, message: AssistantMessage): boolean {\n\t// Real errors are handled by _isRetryableError; aborts are user-initiated.\n\tif (message.stopReason !== \"stop\" && message.stopReason !== \"length\" && message.stopReason !== \"toolUse\") {\n\t\treturn false;\n\t}\n\n\tconst content = message.content;\n\tif (!Array.isArray(content) || content.length === 0) return false;\n\n\tlet text = \"\";\n\tfor (const part of content) {\n\t\tif (part.type === \"toolCall\") return false;\n\t\tif (part.type === \"thinking\" && (part.redacted === true || part.thinking.trim().length > 0)) return false;\n\t\tif (part.type === \"text\") text += part.text;\n\t}\n\ttext = text.trim();\n\tif (text.length === 0 || text.length > CANNED_SAFETY_REFUSAL_MAX_LENGTH) return false;\n\tif (!CANNED_SAFETY_REFUSAL_PATTERN.test(text)) return false;\n\n\t// Zero billed output distinguishes a provider interception from legitimate\n\t// model-authored refusal prose, which is never auto-retried.\n\treturn (message.usage?.output ?? 0) === 0;\n}\n\n/**\n * Handle retryable errors with exponential backoff.\n * @returns true if retry was initiated, false if max retries exceeded or disabled\n */\n\nexport async function _trySwitchToFallbackModel(this: AgentSession, message: AssistantMessage): Promise<boolean> {\n\tif (this._fallbackModels.length === 0 || !this.model) return false;\n\n\tthis._fallbackAttemptedKeys.add(fallbackKey(this.model, this.thinkingLevel));\n\tconst fromModel = this.model;\n\tfor (const rawCandidate of this._fallbackModels) {\n\t\tconst candidate = resolveFallbackModel.call(this, rawCandidate);\n\t\tif (!candidate) continue;\n\t\tconst key = fallbackKey(candidate.model, candidate.thinkingLevel);\n\t\tif (this._fallbackAttemptedKeys.has(key)) continue;\n\t\tconst nextModel = this._withContextWindowForModelSwitch(candidate.model);\n\t\tconst nextLevel = clampThinkingLevel(\n\t\t\tnextModel,\n\t\t\tcandidate.thinkingLevel ?? this.settingsManager.getDefaultThinkingLevel() ?? this.thinkingLevel ?? DEFAULT_THINKING_LEVEL,\n\t\t) as ThinkingLevel;\n\t\tif (modelsAreEqual(candidate.model, fromModel) && nextLevel === this.thinkingLevel) continue;\n\t\tif (this._retryAttempt > 0) {\n\t\t\tthis._emit({\n\t\t\t\ttype: \"auto_retry_end\",\n\t\t\t\tsuccess: true,\n\t\t\t\tattempt: Math.max(0, this._retryAttempt - 1),\n\t\t\t});\n\t\t}\n\t\tthis._fallbackAttemptedKeys.add(key);\n\t\tthis._emit({\n\t\t\ttype: \"model_fallback_start\",\n\t\t\tfrom: modelLabel(fromModel),\n\t\t\tto: `${nextModel.provider}/${nextModel.id}`,\n\t\t\treason: message.errorMessage || \"Retryable model error\",\n\t\t\tattempt: this._fallbackAttemptedKeys.size - 1,\n\t\t});\n\n\t\tconst messages = this.agent.state.messages;\n\t\tif (messages.length > 0 && messages[messages.length - 1]?.role === \"assistant\") {\n\t\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t\t}\n\t\tthis.agent.state.model = nextModel;\n\t\tthis.sessionManager.appendModelChange(nextModel.provider, nextModel.id);\n\t\tthis._appendContextWindowChangeIfChanged(fromModel, nextModel);\n\t\tthis.agent.state.thinkingLevel = nextLevel;\n\t\tthis.sessionManager.appendThinkingLevelChange(nextLevel);\n\t\tthis._refreshBaseSystemPromptFromActiveTools();\n\t\tthis._emitModelChanged(nextModel, fromModel, \"fallback\");\n\t\tawait this._emitModelSelect(nextModel, fromModel, \"fallback\");\n\t\tthis._retryAttempt = 0;\n\n\t\tsetTimeout(() => {\n\t\t\tthis.agent.continue().then(\n\t\t\t\t() => {\n\t\t\t\t\t// A resolved continuation may still have produced an assistant\n\t\t\t\t\t// error that will be classified by agent_end and may advance to\n\t\t\t\t\t// the next fallback. Do not emit a successful fallback end here;\n\t\t\t\t\t// agent_end/turn_end clear UI state for successful turns, while\n\t\t\t\t\t// fallback exhaustion emits the failure end event.\n\t\t\t\t},\n\t\t\t\t(error: unknown) => {\n\t\t\t\t\tconst finalError = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tthis._emit({ type: \"model_fallback_end\", success: false, from: modelLabel(fromModel), to: modelLabel(nextModel), finalError });\n\t\t\t\t\tthis._retryAttempt = 0;\n\t\t\t\t\tthis._resolveRetry();\n\t\t\t\t},\n\t\t\t);\n\t\t}, 0);\n\t\treturn true;\n\t}\n\tthis._emit({ type: \"model_fallback_end\", success: false, from: modelLabel(fromModel), finalError: message.errorMessage });\n\treturn false;\n}\n\nexport async function _handleRetryableError(this: AgentSession, message: AssistantMessage): Promise<boolean> {\n\tconst settings = this.settingsManager.getRetrySettings();\n\tif (!settings.enabled) {\n\t\treturn this._trySwitchToFallbackModel(message);\n\t}\n\n\t// Retry promise is created synchronously in _handleAgentEvent for agent_end.\n\t// Keep a defensive fallback here in case a future refactor bypasses that path.\n\tif (!this._retryPromise) {\n\t\tthis._retryPromise = new Promise((resolve) => {\n\t\t\tthis._retryResolve = resolve;\n\t\t});\n\t}\n\n\tthis._retryAttempt++;\n\n\tif (this._retryAttempt > settings.maxRetries) {\n\t\tif (await this._trySwitchToFallbackModel(message)) {\n\t\t\treturn true;\n\t\t}\n\t\t// Max retries exceeded, emit final failure and reset\n\t\tthis._emit({\n\t\t\ttype: \"auto_retry_end\",\n\t\t\tsuccess: false,\n\t\t\tattempt: this._retryAttempt - 1,\n\t\t\tfinalError: message.errorMessage,\n\t\t});\n\t\tthis._retryAttempt = 0;\n\t\tthis._resolveRetry(); // Resolve so waitForRetry() completes\n\t\treturn false;\n\t}\n\n\tconst delayMs = settings.baseDelayMs * 2 ** (this._retryAttempt - 1);\n\n\tthis._emit({\n\t\ttype: \"auto_retry_start\",\n\t\tattempt: this._retryAttempt,\n\t\tmaxAttempts: settings.maxRetries,\n\t\tdelayMs,\n\t\terrorMessage: message.errorMessage || \"Unknown error\",\n\t});\n\n\t// Remove error message from agent state (keep in session for history)\n\tconst messages = this.agent.state.messages;\n\tif (messages.length > 0 && messages[messages.length - 1].role === \"assistant\") {\n\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t}\n\n\t// Wait with exponential backoff (abortable)\n\tthis._retryAbortController = new AbortController();\n\ttry {\n\t\tawait sleep(delayMs, this._retryAbortController.signal);\n\t} catch {\n\t\t// Aborted during sleep - emit end event so UI can clean up\n\t\tconst attempt = this._retryAttempt;\n\t\tthis._retryAttempt = 0;\n\t\tthis._retryAbortController = undefined;\n\t\tthis._emit({\n\t\t\ttype: \"auto_retry_end\",\n\t\t\tsuccess: false,\n\t\t\tattempt,\n\t\t\tfinalError: \"Retry cancelled\",\n\t\t});\n\t\tthis._resolveRetry();\n\t\treturn false;\n\t}\n\tthis._retryAbortController = undefined;\n\n\t// Retry via continue() - use setTimeout to break out of event handler chain\n\tsetTimeout(() => {\n\t\tthis.agent.continue().catch(() => {\n\t\t\t// Retry failed - will be caught by next agent_end\n\t\t});\n\t}, 0);\n\n\treturn true;\n}\n\n/**\n * Cancel in-progress retry.\n */\n\nexport function abortRetry(this: AgentSession): void {\n\tthis._retryAbortController?.abort();\n\t// Note: _retryAttempt is reset in the catch block of _autoRetry\n\tthis._resolveRetry();\n}\n\n/**\n * Wait for any in-progress retry to complete.\n * Returns immediately if no retry is in progress.\n */\n\nexport async function waitForRetry(this: AgentSession): Promise<void> {\n\tif (!this._retryPromise) {\n\t\treturn;\n\t}\n\n\tawait this._retryPromise;\n\tawait this.agent.waitForIdle();\n}\n\n/** Whether auto-retry is currently in progress */\n\nexport function setAutoRetryEnabled(this: AgentSession, enabled: boolean): void {\n\tthis.settingsManager.setRetryEnabled(enabled);\n}\n\n// =========================================================================\n// Bash Execution\n// =========================================================================\n\n/**\n * Execute a bash command.\n * Adds result to agent context and session.\n * @param command The bash command to execute\n * @param onChunk Optional streaming callback for output\n * @param options.excludeFromContext If true, command output won't be sent to LLM (!! prefix)\n * @param options.operations Custom BashOperations for remote execution\n */\n\nexport const agentSessionRetryMethods = {\n\t_isRetryableError,\n\t_normalizePersistedGeminiToolArgs,\n\t_isEmptyCompletion,\n\t_isSafetyRefusal,\n\t_handleRetryableError,\n\t_trySwitchToFallbackModel,\n\tabortRetry,\n\twaitForRetry,\n\tsetAutoRetryEnabled,\n};\n"]}
1
+ {"version":3,"file":"agent-session-retry.js","sourceRoot":"","sources":["../../src/core/agent-session-retry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, AssistantMessage, Model } from \"@earendil-works/pi-ai/compat\";\nimport { clampThinkingLevel, isContextOverflow, modelsAreEqual } from \"@earendil-works/pi-ai/compat\";\nimport { sleep } from \"../utils/sleep.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport { isCopilotGeminiModel } from \"./copilot-gemini-payload-sanitizer.ts\";\nimport { normalizeToolArgumentsForModel } from \"./copilot-gemini-tool-arguments.ts\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\n\n\nconst THINKING_SUFFIXES = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const satisfies readonly ThinkingLevel[];\nconst THINKING_SUFFIX_SET: ReadonlySet<string> = new Set(THINKING_SUFFIXES);\n\nfunction modelLabel(model: Model<Api> | undefined): string {\n\treturn model ? `${model.provider}/${model.id}` : \"unknown model\";\n}\n\nfunction splitFallbackModel(value: string): { modelId: string; thinkingLevel?: ThinkingLevel } {\n\tconst trimmed = value.trim();\n\tconst index = trimmed.lastIndexOf(\":\");\n\tif (index < 0) return { modelId: trimmed };\n\tconst suffix = trimmed.slice(index + 1);\n\tif (!THINKING_SUFFIX_SET.has(suffix)) return { modelId: trimmed };\n\treturn { modelId: trimmed.slice(0, index), thinkingLevel: suffix as ThinkingLevel };\n}\n\nfunction resolveFallbackModel(this: AgentSession, value: string): { model: Model<Api>; thinkingLevel?: ThinkingLevel } | undefined {\n\tconst parsed = splitFallbackModel(value);\n\tif (!parsed.modelId.includes(\"/\")) {\n\t\tconst available = this._modelRegistry.getAvailable().filter((model) => model.id === parsed.modelId);\n\t\tconst preferredProvider = this.model?.provider ?? this.settingsManager.getDefaultProvider();\n\t\tconst model = available.find((candidate) => candidate.provider === preferredProvider) ?? (available.length === 1 ? available[0] : undefined);\n\t\treturn model ? { model, thinkingLevel: parsed.thinkingLevel } : undefined;\n\t}\n\tconst slash = parsed.modelId.indexOf(\"/\");\n\tconst provider = parsed.modelId.slice(0, slash);\n\tconst modelId = parsed.modelId.slice(slash + 1);\n\tconst model = this._modelRegistry.find(provider, modelId);\n\tif (!model || !this._modelRegistry.hasConfiguredAuth(model)) return undefined;\n\treturn { model, thinkingLevel: parsed.thinkingLevel };\n}\n\nfunction fallbackKey(model: Model<Api>, thinkingLevel: ThinkingLevel | undefined): string {\n\treturn `${model.provider}/${model.id}:${thinkingLevel ?? \"\"}`;\n}\n\nfunction hasProviderTransportDiagnostic(value: unknown, seen = new Set<unknown>(), includeMessageFields = false): boolean {\n\tif (value === null || value === undefined || seen.has(value)) return false;\n\tif (typeof value !== \"object\") {\n\t\treturn /provider_transport_failure|websocket.*error|sse.*404/i.test(String(value));\n\t}\n\tseen.add(value);\n\tconst record = value as Record<string, unknown>;\n\tconst fields = includeMessageFields\n\t\t? [record.type, record.code, record.name, record.message, record.errorMessage, record.status, record.statusCode]\n\t\t: [record.type, record.code, record.name, record.status, record.statusCode];\n\tfor (const field of fields) {\n\t\t if (typeof field === \"string\" || typeof field === \"number\") {\n\t\t\tif (/provider_transport_failure|websocket.*error|sse.*404|\\b404\\b/i.test(String(field))) return true;\n\t\t}\n\t}\n\tfor (const nested of [record.error, record.cause, ...(Array.isArray(record.diagnostics) ? record.diagnostics : [])]) {\n\t\tif (hasProviderTransportDiagnostic(nested, seen, true)) return true;\n\t}\n\treturn false;\n}\n\nfunction hasProviderModelUnavailableDiagnostic(value: unknown, seen = new Set<unknown>(), includeMessageFields = false): boolean {\n\tif (value === null || value === undefined || seen.has(value)) return false;\n\tif (typeof value !== \"object\") return false;\n\tseen.add(value);\n\tconst record = value as Record<string, unknown>;\n\tconst fields = includeMessageFields\n\t\t? [record.type, record.code, record.name, record.message, record.errorMessage]\n\t\t: [record.type, record.code, record.name, record.errorMessage];\n\tfor (const field of fields) {\n\t\tif (typeof field === \"string\" && /model(?:[_\\s-].*)?(?:not[_\\s-]?found|unavailable|unknown|disabled)|model[_-]?not[_-]?found/i.test(field)) return true;\n\t}\n\tfor (const nested of [record.error, record.cause, ...(Array.isArray(record.diagnostics) ? record.diagnostics : [])]) {\n\t\tif (hasProviderModelUnavailableDiagnostic(nested, seen, true)) return true;\n\t}\n\treturn false;\n}\nexport function _isRetryableError(this: AgentSession, message: AssistantMessage): boolean {\n\tif (message.stopReason !== \"error\") return false;\n\n\t// Context overflow is handled by compaction, not retry\n\tconst contextWindow = this.model?.contextWindow ?? 0;\n\tif (isContextOverflow(message, contextWindow)) return false;\n\n\tif (hasProviderTransportDiagnostic(message) || hasProviderModelUnavailableDiagnostic(message)) return true;\n\tif (!message.errorMessage) return false;\n\n\tconst err = message.errorMessage;\n\n\t// Safety triggers surface through structured API signals that pi-ai maps to\n\t// stopReason \"error\":\n\t// - Anthropic `refusal` stops become pi-ai's canned \"The model refused to\n\t// complete the request\" error message;\n\t// - OpenAI-style APIs (and github-copilot CAPI, which also maps spurious\n\t// Gemini RECITATION/safety blocks this way) surface\n\t// `finish_reason: content_filter`.\n\t// Spurious safety triggers are common in agentic settings, so these are\n\t// re-requested like transient failures, bounded by maxRetries (issue #1608).\n\tif (/refused to complete the request|finish.?reason:?\\s*content.?filter/i.test(err)) {\n\t\treturn true;\n\t}\n\n\t// Match: overloaded_error, provider returned error, rate limit, 429, 500, 502, 503, 504, service unavailable, network/connection errors (including connection lost), WebSocket transport closes/errors, fetch failed, premature stream endings, HTTP/2 closed before response, terminated, retry delay exceeded, and a bare/transient provider finish_reason \"error\" (e.g. github-copilot Gemini's CAPI mapping of MALFORMED_FUNCTION_CALL/OTHER/UNEXPECTED_TOOL_CALL). These are provider-agnostic transient failures.\n\treturn /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay|finish.?reason:?\\s*error/i.test(\n\t\terr,\n\t);\n}\n\n/**\n * For GitHub Copilot Gemini, reconstruct flattened tool-call arguments\n * (for example `edits[0].newText`) into the nested arrays/objects Gemini\n * produced before the assistant message is persisted, so saved transcripts\n * never carry the flattened CAPI wire shape and replays loaded from disk match\n * the structure Gemini signed. In-place, gated to Copilot Gemini, and a no-op\n * for well-formed arguments or any other provider/model. The outbound replay\n * normalizer still heals already-persisted (legacy) sessions on the wire.\n */\n\nexport function _normalizePersistedGeminiToolArgs(this: AgentSession, message: AssistantMessage): void {\n\tconst model = this.model;\n\tif (!model || !isCopilotGeminiModel(model)) return;\n\tfor (const block of message.content) {\n\t\tif (block.type !== \"toolCall\") continue;\n\t\tconst tool = this._toolRegistry.get(block.name);\n\t\tconst normalized = normalizeToolArgumentsForModel(block.arguments, model, tool?.parameters);\n\t\tif (normalized !== block.arguments && normalized !== null && typeof normalized === \"object\") {\n\t\t\tblock.arguments = normalized as Record<string, unknown>;\n\t\t}\n\t}\n}\n\n/**\n * Detect a degenerate empty completion: the provider ended the stream with no\n * usable content and zero output tokens. Seen with github-copilot Gemini models\n * that emit finish_reason \"stop\" (or a tool-use stop) with an empty content array\n * and 0 output tokens, leaving the turn dead instead of producing the next step.\n *\n * These are treated as retryable so the harness re-issues the request rather than\n * silently stopping mid-task. Guarded tightly (no text, no tool call, no thinking,\n * and output === 0) so legitimate non-empty turns are never matched.\n *\n * Intentionally provider-agnostic (not gated to Copilot Gemini): a degenerate\n * empty turn is a transient failure for any provider. It is bounded by\n * `maxRetries` and falls through to normal handling on exhaustion.\n */\n\nexport function _isEmptyCompletion(this: AgentSession, message: AssistantMessage): boolean {\n\t// Only \"completed\" stop reasons can be deceptively empty. Real errors are handled\n\t// by _isRetryableError; aborted/length turns are intentional outcomes.\n\tif (message.stopReason !== \"stop\" && message.stopReason !== \"toolUse\") return false;\n\n\tconst content = message.content;\n\tif (Array.isArray(content)) {\n\t\tconst hasContent = content.some((part) => {\n\t\t\tif (part.type === \"text\") return part.text.trim().length > 0;\n\t\t\tif (part.type === \"toolCall\") return true;\n\t\t\tif (part.type === \"thinking\") return part.redacted === true || part.thinking.trim().length > 0;\n\t\t\treturn true; // unknown part types count as content\n\t\t});\n\t\tif (hasContent) return false;\n\t}\n\n\t// A turn that produced output tokens but no surfaced content is not \"empty\"\n\t// (e.g. reasoning-only responses); leave those alone. Note: a provider that\n\t// fails to report `usage` (output defaults to 0) would make every\n\t// content-less turn match here; the dual requirement (empty content AND zero\n\t// output) keeps that false-positive risk low in practice.\n\treturn (message.usage?.output ?? 0) === 0;\n}\n\n/**\n * Detect a canned provider-side safety refusal that arrives as a *successful*\n * completion instead of an error. Seen with github-copilot GPT models under\n * heavy contexts: the endpoint intercepts the request and returns exactly\n * \"I'm sorry, but I cannot assist with that request.\" with zero usage and a\n * spurious stopReason of \"length\" (or \"stop\"), which the agent would otherwise\n * accept as the final answer and dead-end the turn (issue #1608).\n *\n * A text heuristic is unavoidable here: the OpenAI Responses API does signal\n * these interceptions structurally (`refusal` content parts plus\n * `incomplete_details.reason: \"content_filter\"`), but pi-ai's normalization\n * folds refusal parts into plain text blocks and collapses the `incomplete`\n * status to stopReason \"length\", discarding the reason — so no structured\n * refusal marker survives on the AssistantMessage. Providers whose safety\n * signals DO survive as structured errors (Anthropic refusal stops, OpenAI\n * `finish_reason: content_filter`) are matched in _isRetryableError instead.\n *\n * Guarded tightly so legitimate turns are never matched:\n * - only non-error completion stops (\"stop\" | \"length\" | \"toolUse\");\n * - content must be a single short canned refusal text — any tool call,\n * thinking block, or additional prose disqualifies the message;\n * - usage.output must be 0: a genuine model-authored refusal bills output\n * tokens, while the intercepted canned refusal reports zero usage.\n *\n * Treated as retryable so the harness re-requests the model call rather than\n * accepting the refusal; bounded by `maxRetries` like every other retry path.\n */\n\nconst CANNED_SAFETY_REFUSAL_PATTERN =\n\t/^(?:i['’]?m sorry[,.]?\\s+(?:but\\s+)?|sorry[,.]?\\s+(?:but\\s+)?)?i\\s+(?:cannot|can['’]?t|can\\s+not|am\\s+unable\\s+to|am\\s+not\\s+able\\s+to)\\s+(?:assist|help|comply|continue)(?:\\s+with)?(?:\\s+(?:that|this))?(?:\\s+(?:request|task))?\\.?$/i;\n\nconst CANNED_SAFETY_REFUSAL_MAX_LENGTH = 120;\n\nexport function _isSafetyRefusal(this: AgentSession, message: AssistantMessage): boolean {\n\t// Real errors are handled by _isRetryableError; aborts are user-initiated.\n\tif (message.stopReason !== \"stop\" && message.stopReason !== \"length\" && message.stopReason !== \"toolUse\") {\n\t\treturn false;\n\t}\n\n\tconst content = message.content;\n\tif (!Array.isArray(content) || content.length === 0) return false;\n\n\tlet text = \"\";\n\tfor (const part of content) {\n\t\tif (part.type === \"toolCall\") return false;\n\t\tif (part.type === \"thinking\" && (part.redacted === true || part.thinking.trim().length > 0)) return false;\n\t\tif (part.type === \"text\") text += part.text;\n\t}\n\ttext = text.trim();\n\tif (text.length === 0 || text.length > CANNED_SAFETY_REFUSAL_MAX_LENGTH) return false;\n\tif (!CANNED_SAFETY_REFUSAL_PATTERN.test(text)) return false;\n\n\t// Zero billed output distinguishes a provider interception from legitimate\n\t// model-authored refusal prose, which is never auto-retried.\n\treturn (message.usage?.output ?? 0) === 0;\n}\n\n/**\n * Handle retryable errors with exponential backoff.\n * @returns true if retry was initiated, false if max retries exceeded or disabled\n */\n\nexport async function _trySwitchToFallbackModel(this: AgentSession, message: AssistantMessage): Promise<boolean> {\n\tif (this._fallbackModels.length === 0 || !this.model) return false;\n\n\tthis._fallbackAttemptedKeys.add(fallbackKey(this.model, this.thinkingLevel));\n\tconst fromModel = this.model;\n\tfor (const rawCandidate of this._fallbackModels) {\n\t\tconst candidate = resolveFallbackModel.call(this, rawCandidate);\n\t\tif (!candidate) continue;\n\t\tconst key = fallbackKey(candidate.model, candidate.thinkingLevel);\n\t\tif (this._fallbackAttemptedKeys.has(key)) continue;\n\t\tconst nextModel = this._withContextWindowForModelSwitch(candidate.model);\n\t\tconst nextLevel = clampThinkingLevel(\n\t\t\tnextModel,\n\t\t\tcandidate.thinkingLevel ?? this.settingsManager.getDefaultThinkingLevel() ?? this.thinkingLevel ?? DEFAULT_THINKING_LEVEL,\n\t\t) as ThinkingLevel;\n\t\tif (modelsAreEqual(candidate.model, fromModel) && nextLevel === this.thinkingLevel) continue;\n\t\tif (this._retryAttempt > 0) {\n\t\t\tthis._emit({\n\t\t\t\ttype: \"auto_retry_end\",\n\t\t\t\tsuccess: true,\n\t\t\t\tattempt: Math.max(0, this._retryAttempt - 1),\n\t\t\t});\n\t\t}\n\t\tthis._fallbackAttemptedKeys.add(key);\n\t\tthis._emit({\n\t\t\ttype: \"model_fallback_start\",\n\t\t\tfrom: modelLabel(fromModel),\n\t\t\tto: `${nextModel.provider}/${nextModel.id}`,\n\t\t\treason: message.errorMessage || \"Retryable model error\",\n\t\t\tattempt: this._fallbackAttemptedKeys.size - 1,\n\t\t});\n\n\t\tconst messages = this.agent.state.messages;\n\t\tif (messages.length > 0 && messages[messages.length - 1]?.role === \"assistant\") {\n\t\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t\t}\n\t\tthis.agent.state.model = nextModel;\n\t\tthis.sessionManager.appendModelChange(nextModel.provider, nextModel.id);\n\t\tthis._appendContextWindowChangeIfChanged(fromModel, nextModel);\n\t\tthis.agent.state.thinkingLevel = nextLevel;\n\t\tthis.sessionManager.appendThinkingLevelChange(nextLevel);\n\t\tthis._refreshBaseSystemPromptFromActiveTools();\n\t\tthis._emitModelChanged(nextModel, fromModel, \"fallback\");\n\t\tawait this._emitModelSelect(nextModel, fromModel, \"fallback\");\n\t\tthis._retryAttempt = 0;\n\n\t\tsetTimeout(() => {\n\t\t\tthis.agent.continue().then(\n\t\t\t\t() => {\n\t\t\t\t\t// A resolved continuation may still have produced an assistant\n\t\t\t\t\t// error that will be classified by agent_end and may advance to\n\t\t\t\t\t// the next fallback. Do not emit a successful fallback end here;\n\t\t\t\t\t// agent_end/turn_end clear UI state for successful turns, while\n\t\t\t\t\t// fallback exhaustion emits the failure end event.\n\t\t\t\t},\n\t\t\t\t(error: unknown) => {\n\t\t\t\t\tconst finalError = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tthis._emit({ type: \"model_fallback_end\", success: false, from: modelLabel(fromModel), to: modelLabel(nextModel), finalError });\n\t\t\t\t\tthis._retryAttempt = 0;\n\t\t\t\t\tthis._resolveRetry();\n\t\t\t\t},\n\t\t\t);\n\t\t}, 0);\n\t\treturn true;\n\t}\n\tthis._emit({ type: \"model_fallback_end\", success: false, from: modelLabel(fromModel), finalError: message.errorMessage });\n\treturn false;\n}\n\nexport async function _handleRetryableError(this: AgentSession, message: AssistantMessage): Promise<boolean> {\n\tconst settings = this.settingsManager.getRetrySettings();\n\tif (!settings.enabled) {\n\t\treturn this._trySwitchToFallbackModel(message);\n\t}\n\n\t// Retry promise is created synchronously in _handleAgentEvent for agent_end.\n\t// Keep a defensive fallback here in case a future refactor bypasses that path.\n\tif (!this._retryPromise) {\n\t\tthis._retryPromise = new Promise((resolve) => {\n\t\t\tthis._retryResolve = resolve;\n\t\t});\n\t}\n\n\tthis._retryAttempt++;\n\n\tif (this._retryAttempt > settings.maxRetries) {\n\t\tif (await this._trySwitchToFallbackModel(message)) {\n\t\t\treturn true;\n\t\t}\n\t\t// Max retries exceeded, emit final failure and reset\n\t\tthis._emit({\n\t\t\ttype: \"auto_retry_end\",\n\t\t\tsuccess: false,\n\t\t\tattempt: this._retryAttempt - 1,\n\t\t\tfinalError: message.errorMessage,\n\t\t});\n\t\tthis._retryAttempt = 0;\n\t\tthis._resolveRetry(); // Resolve so waitForRetry() completes\n\t\treturn false;\n\t}\n\n\tconst delayMs = settings.baseDelayMs * 2 ** (this._retryAttempt - 1);\n\n\tthis._emit({\n\t\ttype: \"auto_retry_start\",\n\t\tattempt: this._retryAttempt,\n\t\tmaxAttempts: settings.maxRetries,\n\t\tdelayMs,\n\t\terrorMessage: message.errorMessage || \"Unknown error\",\n\t});\n\n\t// Remove error message from agent state (keep in session for history)\n\tconst messages = this.agent.state.messages;\n\tif (messages.length > 0 && messages[messages.length - 1].role === \"assistant\") {\n\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t}\n\n\t// Wait with exponential backoff (abortable)\n\tthis._retryAbortController = new AbortController();\n\ttry {\n\t\tawait sleep(delayMs, this._retryAbortController.signal);\n\t} catch {\n\t\t// Aborted during sleep - emit end event so UI can clean up\n\t\tconst attempt = this._retryAttempt;\n\t\tthis._retryAttempt = 0;\n\t\tthis._retryAbortController = undefined;\n\t\tthis._emit({\n\t\t\ttype: \"auto_retry_end\",\n\t\t\tsuccess: false,\n\t\t\tattempt,\n\t\t\tfinalError: \"Retry cancelled\",\n\t\t});\n\t\tthis._resolveRetry();\n\t\treturn false;\n\t}\n\tthis._retryAbortController = undefined;\n\n\t// Retry via continue() - use setTimeout to break out of event handler chain\n\tsetTimeout(() => {\n\t\tthis.agent.continue().catch(() => {\n\t\t\t// Retry failed - will be caught by next agent_end\n\t\t});\n\t}, 0);\n\n\treturn true;\n}\n\n/**\n * Cancel in-progress retry.\n */\n\nexport function abortRetry(this: AgentSession): void {\n\tthis._retryAbortController?.abort();\n\t// Note: _retryAttempt is reset in the catch block of _autoRetry\n\tthis._resolveRetry();\n}\n\n/**\n * Wait for any in-progress retry to complete.\n * Returns immediately if no retry is in progress.\n */\n\nexport async function waitForRetry(this: AgentSession): Promise<void> {\n\tif (!this._retryPromise) {\n\t\treturn;\n\t}\n\n\tawait this._retryPromise;\n\tawait this.agent.waitForIdle();\n}\n\n/** Whether auto-retry is currently in progress */\n\nexport function setAutoRetryEnabled(this: AgentSession, enabled: boolean): void {\n\tthis.settingsManager.setRetryEnabled(enabled);\n}\n\n// =========================================================================\n// Bash Execution\n// =========================================================================\n\n/**\n * Execute a bash command.\n * Adds result to agent context and session.\n * @param command The bash command to execute\n * @param onChunk Optional streaming callback for output\n * @param options.excludeFromContext If true, command output won't be sent to LLM (!! prefix)\n * @param options.operations Custom BashOperations for remote execution\n */\n\nexport const agentSessionRetryMethods = {\n\t_isRetryableError,\n\t_normalizePersistedGeminiToolArgs,\n\t_isEmptyCompletion,\n\t_isSafetyRefusal,\n\t_handleRetryableError,\n\t_trySwitchToFallbackModel,\n\tabortRetry,\n\twaitForRetry,\n\tsetAutoRetryEnabled,\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"agent-session-services.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-services.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai/compat\";\nimport { getAgentDir } from \"../config.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { AuthStorage } from \"./auth-storage.ts\";\nimport type { SessionStartEvent, ToolDefinition } from \"./extensions/index.ts\";\nimport { ModelRegistry } from \"./model-registry.ts\";\nimport {\n\tDefaultResourceLoader,\n\ttype DefaultResourceLoaderOptions,\n\ttype ResourceLoader,\n\ttype ResourceLoaderReloadOptions,\n} from \"./resource-loader.ts\";\nimport { type CreateAgentSessionOptions, type CreateAgentSessionResult, createAgentSession } from \"./sdk.ts\";\nimport type { SessionManager } from \"./session-manager.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\nimport { endTimingSpan, startTimingSpan } from \"./timings.ts\";\n\n/**\n * Non-fatal issues collected while creating services or sessions.\n *\n * Runtime creation returns diagnostics to the caller instead of printing or\n * exiting. The app layer decides whether warnings should be shown and whether\n * errors should abort startup.\n */\nexport interface AgentSessionRuntimeDiagnostic {\n\ttype: \"info\" | \"warning\" | \"error\";\n\tmessage: string;\n}\n\n/**\n * Inputs for creating cwd-bound runtime services.\n *\n * These services are recreated whenever the effective session cwd changes.\n * CLI-provided resource paths should be resolved to absolute paths before they\n * reach this function, so later cwd switches do not reinterpret them.\n */\nexport interface CreateAgentSessionServicesOptions {\n\tcwd: string;\n\tagentDir?: string;\n\tauthStorage?: AuthStorage;\n\tsettingsManager?: SettingsManager;\n\tmodelRegistry?: ModelRegistry;\n\textensionFlagValues?: Map<string, boolean | string>;\n\tresourceLoaderOptions?: Omit<DefaultResourceLoaderOptions, \"cwd\" | \"agentDir\" | \"settingsManager\">;\n\tresourceLoaderReloadOptions?: ResourceLoaderReloadOptions;\n}\n\n/**\n * Inputs for creating an AgentSession from already-created services.\n *\n * Use this after services exist and any cwd-bound model/tool/session options\n * have been resolved against those services.\n */\nexport interface CreateAgentSessionFromServicesOptions {\n\tservices: AgentSessionServices;\n\tsessionManager: SessionManager;\n\tsessionStartEvent?: SessionStartEvent;\n\tmodel?: Model<Api>;\n\tthinkingLevel?: ThinkingLevel;\n\tfallbackModels?: CreateAgentSessionOptions[\"fallbackModels\"];\n\tcontextWindow?: number;\n\tcontextWindowStrict?: boolean;\n\tscopedModels?: Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }>;\n\ttools?: CreateAgentSessionOptions[\"tools\"];\n\texcludedTools?: CreateAgentSessionOptions[\"excludedTools\"];\n\tnoTools?: CreateAgentSessionOptions[\"noTools\"];\n\tcustomTools?: ToolDefinition[];\n}\n\n/**\n * Coherent cwd-bound runtime services for one effective session cwd.\n *\n * This is infrastructure only. The AgentSession itself is created separately so\n * session options can be resolved against these services first.\n */\nexport interface AgentSessionServices {\n\tcwd: string;\n\tagentDir: string;\n\tauthStorage: AuthStorage;\n\tsettingsManager: SettingsManager;\n\tmodelRegistry: ModelRegistry;\n\tresourceLoader: ResourceLoader;\n\tdiagnostics: AgentSessionRuntimeDiagnostic[];\n}\n\nfunction applyExtensionFlagValues(\n\tresourceLoader: ResourceLoader,\n\textensionFlagValues: Map<string, boolean | string> | undefined,\n): AgentSessionRuntimeDiagnostic[] {\n\tif (!extensionFlagValues) {\n\t\treturn [];\n\t}\n\n\tconst diagnostics: AgentSessionRuntimeDiagnostic[] = [];\n\tconst extensionsResult = resourceLoader.getExtensions();\n\tconst registeredFlags = new Map<string, { type: \"boolean\" | \"string\" }>();\n\tfor (const extension of extensionsResult.extensions) {\n\t\tfor (const [name, flag] of extension.flags) {\n\t\t\tregisteredFlags.set(name, { type: flag.type });\n\t\t}\n\t}\n\n\tconst unknownFlags: string[] = [];\n\tfor (const [name, value] of extensionFlagValues) {\n\t\tconst flag = registeredFlags.get(name);\n\t\tif (!flag) {\n\t\t\tunknownFlags.push(name);\n\t\t\tcontinue;\n\t\t}\n\t\tif (flag.type === \"boolean\") {\n\t\t\textensionsResult.runtime.flagValues.set(name, true);\n\t\t\tcontinue;\n\t\t}\n\t\tif (typeof value === \"string\") {\n\t\t\textensionsResult.runtime.flagValues.set(name, value);\n\t\t\tcontinue;\n\t\t}\n\t\tdiagnostics.push({\n\t\t\ttype: \"error\",\n\t\t\tmessage: `Extension flag \"--${name}\" requires a value`,\n\t\t});\n\t}\n\n\tif (unknownFlags.length > 0) {\n\t\tdiagnostics.push({\n\t\t\ttype: \"error\",\n\t\t\tmessage: `Unknown option${unknownFlags.length === 1 ? \"\" : \"s\"}: ${unknownFlags.map((name) => `--${name}`).join(\", \")}`,\n\t\t});\n\t}\n\n\treturn diagnostics;\n}\n\n/**\n * Create cwd-bound runtime services.\n *\n * Returns services plus diagnostics. It does not create an AgentSession.\n */\nexport async function createAgentSessionServices(\n\toptions: CreateAgentSessionServicesOptions,\n): Promise<AgentSessionServices> {\n\tconst cwd = resolvePath(options.cwd);\n\tconst agentDir = options.agentDir ? resolvePath(options.agentDir) : getAgentDir();\n\tconst authStorageSpan = startTimingSpan(\"createAgentSessionServices.authStorage\");\n\tconst authStorage = options.authStorage ?? AuthStorage.create(join(agentDir, \"auth.json\"));\n\tendTimingSpan(authStorageSpan);\n\tconst settingsSpan = startTimingSpan(\"createAgentSessionServices.settingsManager\");\n\tconst settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n\tendTimingSpan(settingsSpan);\n\tconst modelRegistrySpan = startTimingSpan(\"createAgentSessionServices.modelRegistry\");\n\tconst modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, join(agentDir, \"models.json\"));\n\tendTimingSpan(modelRegistrySpan);\n\tconst resourceLoader = new DefaultResourceLoader({\n\t\t...(options.resourceLoaderOptions ?? {}),\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t});\n\tconst reloadSpan = startTimingSpan(\"createAgentSessionServices.resourceLoader.reload\");\n\tawait resourceLoader.reload(options.resourceLoaderReloadOptions);\n\tendTimingSpan(reloadSpan);\n\n\tconst diagnostics: AgentSessionRuntimeDiagnostic[] = [];\n\tconst providerSpan = startTimingSpan(\"createAgentSessionServices.providerRegistrations\");\n\tconst extensionsResult = resourceLoader.getExtensions();\n\tfor (const { name, config, extensionPath } of extensionsResult.runtime.pendingProviderRegistrations) {\n\t\ttry {\n\t\t\tmodelRegistry.registerProvider(name, config);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tdiagnostics.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage: `Extension \"${extensionPath}\" error: ${message}`,\n\t\t\t});\n\t\t}\n\t}\n\textensionsResult.runtime.pendingProviderRegistrations = [];\n\tendTimingSpan(providerSpan);\n\tconst flagSpan = startTimingSpan(\"createAgentSessionServices.extensionFlagValidation\");\n\tdiagnostics.push(...applyExtensionFlagValues(resourceLoader, options.extensionFlagValues));\n\tendTimingSpan(flagSpan);\n\n\treturn {\n\t\tcwd,\n\t\tagentDir,\n\t\tauthStorage,\n\t\tsettingsManager,\n\t\tmodelRegistry,\n\t\tresourceLoader,\n\t\tdiagnostics,\n\t};\n}\n\n/**\n * Create an AgentSession from previously created services.\n *\n * This keeps session creation separate from service creation so callers can\n * resolve model, thinking, tools, and other session inputs against the target\n * cwd before constructing the session.\n */\nexport async function createAgentSessionFromServices(\n\toptions: CreateAgentSessionFromServicesOptions,\n): Promise<CreateAgentSessionResult> {\n\treturn createAgentSession({\n\t\tcwd: options.services.cwd,\n\t\tagentDir: options.services.agentDir,\n\t\tauthStorage: options.services.authStorage,\n\t\tsettingsManager: options.services.settingsManager,\n\t\tmodelRegistry: options.services.modelRegistry,\n\t\tresourceLoader: options.services.resourceLoader,\n\t\tsessionManager: options.sessionManager,\n\t\tmodel: options.model,\n\t\tthinkingLevel: options.thinkingLevel,\n\t\tfallbackModels: options.fallbackModels,\n\t\tcontextWindow: options.contextWindow,\n\t\tcontextWindowStrict: options.contextWindowStrict,\n\t\tscopedModels: options.scopedModels,\n\t\ttools: options.tools,\n\t\texcludedTools: options.excludedTools,\n\t\tnoTools: options.noTools,\n\t\tcustomTools: options.customTools,\n\t\tsessionStartEvent: options.sessionStartEvent,\n\t});\n}\n"]}
1
+ {"version":3,"file":"agent-session-services.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-services.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai/compat\";\nimport { getAgentConfigPaths, getAgentDir } from \"../config.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { AuthStorage } from \"./auth-storage.ts\";\nimport type { SessionStartEvent, ToolDefinition } from \"./extensions/index.ts\";\nimport { ModelRegistry } from \"./model-registry.ts\";\nimport {\n\tDefaultResourceLoader,\n\ttype DefaultResourceLoaderOptions,\n\ttype ResourceLoader,\n\ttype ResourceLoaderReloadOptions,\n} from \"./resource-loader.ts\";\nimport { type CreateAgentSessionOptions, type CreateAgentSessionResult, createAgentSession } from \"./sdk.ts\";\nimport type { SessionManager } from \"./session-manager.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\nimport { endTimingSpan, startTimingSpan } from \"./timings.ts\";\n\n/**\n * Non-fatal issues collected while creating services or sessions.\n *\n * Runtime creation returns diagnostics to the caller instead of printing or\n * exiting. The app layer decides whether warnings should be shown and whether\n * errors should abort startup.\n */\nexport interface AgentSessionRuntimeDiagnostic {\n\ttype: \"info\" | \"warning\" | \"error\";\n\tmessage: string;\n}\n\n/**\n * Inputs for creating cwd-bound runtime services.\n *\n * These services are recreated whenever the effective session cwd changes.\n * CLI-provided resource paths should be resolved to absolute paths before they\n * reach this function, so later cwd switches do not reinterpret them.\n */\nexport interface CreateAgentSessionServicesOptions {\n\tcwd: string;\n\tagentDir?: string;\n\tauthStorage?: AuthStorage;\n\tsettingsManager?: SettingsManager;\n\tmodelRegistry?: ModelRegistry;\n\textensionFlagValues?: Map<string, boolean | string>;\n\tresourceLoaderOptions?: Omit<DefaultResourceLoaderOptions, \"cwd\" | \"agentDir\" | \"settingsManager\">;\n\tresourceLoaderReloadOptions?: ResourceLoaderReloadOptions;\n}\n\n/**\n * Inputs for creating an AgentSession from already-created services.\n *\n * Use this after services exist and any cwd-bound model/tool/session options\n * have been resolved against those services.\n */\nexport interface CreateAgentSessionFromServicesOptions {\n\tservices: AgentSessionServices;\n\tsessionManager: SessionManager;\n\tsessionStartEvent?: SessionStartEvent;\n\tmodel?: Model<Api>;\n\tthinkingLevel?: ThinkingLevel;\n\tfallbackModels?: CreateAgentSessionOptions[\"fallbackModels\"];\n\tcontextWindow?: number;\n\tcontextWindowStrict?: boolean;\n\tscopedModels?: Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }>;\n\ttools?: CreateAgentSessionOptions[\"tools\"];\n\texcludedTools?: CreateAgentSessionOptions[\"excludedTools\"];\n\tnoTools?: CreateAgentSessionOptions[\"noTools\"];\n\tcustomTools?: ToolDefinition[];\n}\n\n/**\n * Coherent cwd-bound runtime services for one effective session cwd.\n *\n * This is infrastructure only. The AgentSession itself is created separately so\n * session options can be resolved against these services first.\n */\nexport interface AgentSessionServices {\n\tcwd: string;\n\tagentDir: string;\n\tauthStorage: AuthStorage;\n\tsettingsManager: SettingsManager;\n\tmodelRegistry: ModelRegistry;\n\tresourceLoader: ResourceLoader;\n\tdiagnostics: AgentSessionRuntimeDiagnostic[];\n}\n\nfunction applyExtensionFlagValues(\n\tresourceLoader: ResourceLoader,\n\textensionFlagValues: Map<string, boolean | string> | undefined,\n): AgentSessionRuntimeDiagnostic[] {\n\tif (!extensionFlagValues) {\n\t\treturn [];\n\t}\n\n\tconst diagnostics: AgentSessionRuntimeDiagnostic[] = [];\n\tconst extensionsResult = resourceLoader.getExtensions();\n\tconst registeredFlags = new Map<string, { type: \"boolean\" | \"string\" }>();\n\tfor (const extension of extensionsResult.extensions) {\n\t\tfor (const [name, flag] of extension.flags) {\n\t\t\tregisteredFlags.set(name, { type: flag.type });\n\t\t}\n\t}\n\n\tconst unknownFlags: string[] = [];\n\tfor (const [name, value] of extensionFlagValues) {\n\t\tconst flag = registeredFlags.get(name);\n\t\tif (!flag) {\n\t\t\tunknownFlags.push(name);\n\t\t\tcontinue;\n\t\t}\n\t\tif (flag.type === \"boolean\") {\n\t\t\textensionsResult.runtime.flagValues.set(name, true);\n\t\t\tcontinue;\n\t\t}\n\t\tif (typeof value === \"string\") {\n\t\t\textensionsResult.runtime.flagValues.set(name, value);\n\t\t\tcontinue;\n\t\t}\n\t\tdiagnostics.push({\n\t\t\ttype: \"error\",\n\t\t\tmessage: `Extension flag \"--${name}\" requires a value`,\n\t\t});\n\t}\n\n\tif (unknownFlags.length > 0) {\n\t\tdiagnostics.push({\n\t\t\ttype: \"error\",\n\t\t\tmessage: `Unknown option${unknownFlags.length === 1 ? \"\" : \"s\"}: ${unknownFlags.map((name) => `--${name}`).join(\", \")}`,\n\t\t});\n\t}\n\n\treturn diagnostics;\n}\n\n/**\n * Create cwd-bound runtime services.\n *\n * Returns services plus diagnostics. It does not create an AgentSession.\n */\nexport async function createAgentSessionServices(\n\toptions: CreateAgentSessionServicesOptions,\n): Promise<AgentSessionServices> {\n\tconst cwd = resolvePath(options.cwd);\n\tconst agentDir = options.agentDir ? resolvePath(options.agentDir) : getAgentDir();\n\tconst authStorageSpan = startTimingSpan(\"createAgentSessionServices.authStorage\");\n\tconst authStorage = options.authStorage ?? AuthStorage.create(join(agentDir, \"auth.json\"));\n\tendTimingSpan(authStorageSpan);\n\tconst settingsSpan = startTimingSpan(\"createAgentSessionServices.settingsManager\");\n\tconst settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n\tendTimingSpan(settingsSpan);\n\tconst modelRegistrySpan = startTimingSpan(\"createAgentSessionServices.modelRegistry\");\n\tconst modelsJsonPaths = agentDir === getAgentDir() ? getAgentConfigPaths(\"models.json\") : join(agentDir, \"models.json\");\n\tconst modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, modelsJsonPaths);\n\tendTimingSpan(modelRegistrySpan);\n\tconst resourceLoader = new DefaultResourceLoader({\n\t\t...(options.resourceLoaderOptions ?? {}),\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t});\n\tconst reloadSpan = startTimingSpan(\"createAgentSessionServices.resourceLoader.reload\");\n\tawait resourceLoader.reload(options.resourceLoaderReloadOptions);\n\tendTimingSpan(reloadSpan);\n\n\tconst diagnostics: AgentSessionRuntimeDiagnostic[] = [];\n\tconst providerSpan = startTimingSpan(\"createAgentSessionServices.providerRegistrations\");\n\tconst extensionsResult = resourceLoader.getExtensions();\n\tfor (const { name, config, extensionPath } of extensionsResult.runtime.pendingProviderRegistrations) {\n\t\ttry {\n\t\t\tmodelRegistry.registerProvider(name, config);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tdiagnostics.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage: `Extension \"${extensionPath}\" error: ${message}`,\n\t\t\t});\n\t\t}\n\t}\n\textensionsResult.runtime.pendingProviderRegistrations = [];\n\tendTimingSpan(providerSpan);\n\tconst flagSpan = startTimingSpan(\"createAgentSessionServices.extensionFlagValidation\");\n\tdiagnostics.push(...applyExtensionFlagValues(resourceLoader, options.extensionFlagValues));\n\tendTimingSpan(flagSpan);\n\n\treturn {\n\t\tcwd,\n\t\tagentDir,\n\t\tauthStorage,\n\t\tsettingsManager,\n\t\tmodelRegistry,\n\t\tresourceLoader,\n\t\tdiagnostics,\n\t};\n}\n\n/**\n * Create an AgentSession from previously created services.\n *\n * This keeps session creation separate from service creation so callers can\n * resolve model, thinking, tools, and other session inputs against the target\n * cwd before constructing the session.\n */\nexport async function createAgentSessionFromServices(\n\toptions: CreateAgentSessionFromServicesOptions,\n): Promise<CreateAgentSessionResult> {\n\treturn createAgentSession({\n\t\tcwd: options.services.cwd,\n\t\tagentDir: options.services.agentDir,\n\t\tauthStorage: options.services.authStorage,\n\t\tsettingsManager: options.services.settingsManager,\n\t\tmodelRegistry: options.services.modelRegistry,\n\t\tresourceLoader: options.services.resourceLoader,\n\t\tsessionManager: options.sessionManager,\n\t\tmodel: options.model,\n\t\tthinkingLevel: options.thinkingLevel,\n\t\tfallbackModels: options.fallbackModels,\n\t\tcontextWindow: options.contextWindow,\n\t\tcontextWindowStrict: options.contextWindowStrict,\n\t\tscopedModels: options.scopedModels,\n\t\ttools: options.tools,\n\t\texcludedTools: options.excludedTools,\n\t\tnoTools: options.noTools,\n\t\tcustomTools: options.customTools,\n\t\tsessionStartEvent: options.sessionStartEvent,\n\t});\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { join } from "node:path";
2
- import { getAgentDir } from "../config.js";
2
+ import { getAgentConfigPaths, getAgentDir } from "../config.js";
3
3
  import { resolvePath } from "../utils/paths.js";
4
4
  import { AuthStorage } from "./auth-storage.js";
5
5
  import { ModelRegistry } from "./model-registry.js";
@@ -62,7 +62,8 @@ export async function createAgentSessionServices(options) {
62
62
  const settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir);
63
63
  endTimingSpan(settingsSpan);
64
64
  const modelRegistrySpan = startTimingSpan("createAgentSessionServices.modelRegistry");
65
- const modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, join(agentDir, "models.json"));
65
+ const modelsJsonPaths = agentDir === getAgentDir() ? getAgentConfigPaths("models.json") : join(agentDir, "models.json");
66
+ const modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, modelsJsonPaths);
66
67
  endTimingSpan(modelRegistrySpan);
67
68
  const resourceLoader = new DefaultResourceLoader({
68
69
  ...(options.resourceLoaderOptions ?? {}),