salmon-loop 0.2.3

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 (655) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +144 -0
  3. package/README.zh-CN.md +144 -0
  4. package/dist/cli/argv/headless-detection.js +60 -0
  5. package/dist/cli/argv/print-mode.js +60 -0
  6. package/dist/cli/authorization/allowlist.js +908 -0
  7. package/dist/cli/authorization/non-interactive.js +166 -0
  8. package/dist/cli/authorization/provider.js +416 -0
  9. package/dist/cli/chat-interface.js +83 -0
  10. package/dist/cli/chat.js +492 -0
  11. package/dist/cli/cli-runtime-context.js +12 -0
  12. package/dist/cli/commander-error-adapter.js +35 -0
  13. package/dist/cli/commander-error-meta.js +13 -0
  14. package/dist/cli/commands/allowlist.js +270 -0
  15. package/dist/cli/commands/chat.js +120 -0
  16. package/dist/cli/commands/config.js +250 -0
  17. package/dist/cli/commands/context.js +57 -0
  18. package/dist/cli/commands/dispatcher.js +53 -0
  19. package/dist/cli/commands/exit.js +9 -0
  20. package/dist/cli/commands/llm-output.js +135 -0
  21. package/dist/cli/commands/log-mode.js +143 -0
  22. package/dist/cli/commands/mode.js +136 -0
  23. package/dist/cli/commands/new.js +18 -0
  24. package/dist/cli/commands/parallel.js +256 -0
  25. package/dist/cli/commands/queue.js +130 -0
  26. package/dist/cli/commands/registry.js +85 -0
  27. package/dist/cli/commands/restore.js +26 -0
  28. package/dist/cli/commands/run/assistant-message.js +14 -0
  29. package/dist/cli/commands/run/config-resolution.js +37 -0
  30. package/dist/cli/commands/run/early-errors.js +108 -0
  31. package/dist/cli/commands/run/execute.js +73 -0
  32. package/dist/cli/commands/run/extensions-resolution.js +22 -0
  33. package/dist/cli/commands/run/handler.js +434 -0
  34. package/dist/cli/commands/run/headless-error-writer.js +182 -0
  35. package/dist/cli/commands/run/instruction-guard.js +24 -0
  36. package/dist/cli/commands/run/loop-params.js +46 -0
  37. package/dist/cli/commands/run/mode.js +8 -0
  38. package/dist/cli/commands/run/parse-options.js +67 -0
  39. package/dist/cli/commands/run/persist-session.js +35 -0
  40. package/dist/cli/commands/run/preflight.js +156 -0
  41. package/dist/cli/commands/run/reporter-factory.js +52 -0
  42. package/dist/cli/commands/run/runtime-llm.js +56 -0
  43. package/dist/cli/commands/run/runtime-options.js +30 -0
  44. package/dist/cli/commands/run/session.js +19 -0
  45. package/dist/cli/commands/run/structured-output.js +106 -0
  46. package/dist/cli/commands/run/types.js +2 -0
  47. package/dist/cli/commands/run/validate-options.js +28 -0
  48. package/dist/cli/commands/run/verbose.js +36 -0
  49. package/dist/cli/commands/run.js +2 -0
  50. package/dist/cli/commands/serve.js +323 -0
  51. package/dist/cli/commands/session.js +77 -0
  52. package/dist/cli/commands/snapshot-interactive.js +165 -0
  53. package/dist/cli/commands/snapshot.js +159 -0
  54. package/dist/cli/commands/status.js +17 -0
  55. package/dist/cli/commands/subagent.js +178 -0
  56. package/dist/cli/commands/subcommand-suggestions.js +63 -0
  57. package/dist/cli/commands/tool-names.js +155 -0
  58. package/dist/cli/commands/types.js +2 -0
  59. package/dist/cli/commands/utils.js +42 -0
  60. package/dist/cli/config.js +16 -0
  61. package/dist/cli/crash-reporter.js +5 -0
  62. package/dist/cli/headless/anthropic-stream-normalized-encoder.js +164 -0
  63. package/dist/cli/headless/anthropic-stream-protocol.js +62 -0
  64. package/dist/cli/headless/json-protocol.js +124 -0
  65. package/dist/cli/headless/native-stream-normalized-encoder.js +206 -0
  66. package/dist/cli/headless/openai-responses-canonical-applier.js +94 -0
  67. package/dist/cli/headless/openai-responses-state.js +294 -0
  68. package/dist/cli/headless/openai-stream-encoder.js +152 -0
  69. package/dist/cli/headless/stdout-writer.js +9 -0
  70. package/dist/cli/headless/stream-json-protocol.js +136 -0
  71. package/dist/cli/index.js +8 -0
  72. package/dist/cli/locales/en.js +409 -0
  73. package/dist/cli/locales/index.js +7 -0
  74. package/dist/cli/program-bootstrap.js +14 -0
  75. package/dist/cli/program-commands.js +106 -0
  76. package/dist/cli/program-options.js +15 -0
  77. package/dist/cli/program-output-mode.js +11 -0
  78. package/dist/cli/program-parse.js +24 -0
  79. package/dist/cli/reporters/anthropic-stream.js +77 -0
  80. package/dist/cli/reporters/base.js +2 -0
  81. package/dist/cli/reporters/json.js +69 -0
  82. package/dist/cli/reporters/openai-stream.js +72 -0
  83. package/dist/cli/reporters/standard.js +226 -0
  84. package/dist/cli/reporters/stderr-log-reporter.js +71 -0
  85. package/dist/cli/reporters/stream-json.js +111 -0
  86. package/dist/cli/run-cli.js +25 -0
  87. package/dist/cli/slash/runtime.js +240 -0
  88. package/dist/cli/ui/App.js +273 -0
  89. package/dist/cli/ui/authorization/bus.js +35 -0
  90. package/dist/cli/ui/components/CommandInput.js +200 -0
  91. package/dist/cli/ui/components/CommandSuggestionList.js +20 -0
  92. package/dist/cli/ui/components/Markdown.js +423 -0
  93. package/dist/cli/ui/components/MessageList.js +34 -0
  94. package/dist/cli/ui/components/StatusBannerLine.js +7 -0
  95. package/dist/cli/ui/components/TodoDrawer.js +60 -0
  96. package/dist/cli/ui/components/WelcomeMessage.js +14 -0
  97. package/dist/cli/ui/components/animations/StretchingThinking.js +51 -0
  98. package/dist/cli/ui/components/animations/ThinkingWave.js +15 -0
  99. package/dist/cli/ui/components/animations/TypeIndicator.js +30 -0
  100. package/dist/cli/ui/components/layout/SplitPane.js +11 -0
  101. package/dist/cli/ui/components/messageList/MessageItem.js +27 -0
  102. package/dist/cli/ui/components/messageList/QueuePreviewList.js +11 -0
  103. package/dist/cli/ui/components/messageList/items/EmphasisMessageItem.js +20 -0
  104. package/dist/cli/ui/components/messageList/items/InterruptMessageItem.js +10 -0
  105. package/dist/cli/ui/components/messageList/items/LightweightMessageItem.js +12 -0
  106. package/dist/cli/ui/components/messageList/items/StandardMessageItem.js +23 -0
  107. package/dist/cli/ui/components/messageList/items/WelcomeMessageItem.js +7 -0
  108. package/dist/cli/ui/components/messageList/messageListLayout.js +27 -0
  109. package/dist/cli/ui/components/messageList/streaming.js +51 -0
  110. package/dist/cli/ui/components/messageList/types.js +2 -0
  111. package/dist/cli/ui/components/messageList/utils.js +7 -0
  112. package/dist/cli/ui/components/sidebar/FileContext.js +8 -0
  113. package/dist/cli/ui/components/sidebar/MissionControl.js +8 -0
  114. package/dist/cli/ui/config.js +59 -0
  115. package/dist/cli/ui/hooks/useCommandLifecycle.js +110 -0
  116. package/dist/cli/ui/hooks/useCommandSuggestions.js +87 -0
  117. package/dist/cli/ui/hooks/useInputHistory.js +57 -0
  118. package/dist/cli/ui/hooks/useLoopEvents.js +382 -0
  119. package/dist/cli/ui/hooks/useLoopState.js +73 -0
  120. package/dist/cli/ui/hooks/useOnionExit.js +31 -0
  121. package/dist/cli/ui/hooks/useTerminalDimensions.js +34 -0
  122. package/dist/cli/ui/index.js +136 -0
  123. package/dist/cli/ui/selection/bus.js +35 -0
  124. package/dist/cli/ui/status/formatStatusBanner.js +8 -0
  125. package/dist/cli/ui/store/context.js +17 -0
  126. package/dist/cli/ui/store/reducer.js +264 -0
  127. package/dist/cli/ui/store/types.js +81 -0
  128. package/dist/cli/ui/styles/theme.js +295 -0
  129. package/dist/cli/ui/types.js +2 -0
  130. package/dist/cli/ui/utils/sanitizer.js +122 -0
  131. package/dist/cli/ui/utils/transcript.js +28 -0
  132. package/dist/cli/utils/asyncQueue.js +125 -0
  133. package/dist/cli/utils/audit-scope.js +10 -0
  134. package/dist/cli/utils/detectors/index.js +38 -0
  135. package/dist/cli/utils/llm-output.js +34 -0
  136. package/dist/cli/utils/outcome-reporter.js +17 -0
  137. package/dist/cli/utils/safe-fs.js +184 -0
  138. package/dist/cli/utils/verify-resolver.js +34 -0
  139. package/dist/cli/utils/worktree-prepare-resolver.js +18 -0
  140. package/dist/core/adapters/fs/atomic-file-writer.js +129 -0
  141. package/dist/core/adapters/fs/file-adapter.js +95 -0
  142. package/dist/core/adapters/fs/filesystem.js +31 -0
  143. package/dist/core/adapters/fs/index.js +5 -0
  144. package/dist/core/adapters/fs/node-fs.js +7 -0
  145. package/dist/core/adapters/fs/readonly-filesystem.js +23 -0
  146. package/dist/core/adapters/git/git-adapter.js +704 -0
  147. package/dist/core/adapters/git/git-runner.js +119 -0
  148. package/dist/core/adapters/git/lock-manager.js +314 -0
  149. package/dist/core/adapters/git/types.js +2 -0
  150. package/dist/core/adapters/path/index.js +2 -0
  151. package/dist/core/adapters/path/path-adapter.js +23 -0
  152. package/dist/core/ast/guard.js +116 -0
  153. package/dist/core/ast/index.js +4 -0
  154. package/dist/core/ast/parser.js +284 -0
  155. package/dist/core/ast/validator.js +46 -0
  156. package/dist/core/backends/salmon-loop/task-executor.js +68 -0
  157. package/dist/core/checkpoint-domain/manifest-store.js +379 -0
  158. package/dist/core/checkpoint-domain/service.js +84 -0
  159. package/dist/core/checkpoint-domain/types.js +2 -0
  160. package/dist/core/config/defaults.js +50 -0
  161. package/dist/core/config/errors.js +11 -0
  162. package/dist/core/config/file-format.js +108 -0
  163. package/dist/core/config/index.js +7 -0
  164. package/dist/core/config/limits.js +77 -0
  165. package/dist/core/config/load.js +34 -0
  166. package/dist/core/config/normalize.js +35 -0
  167. package/dist/core/config/paths.js +20 -0
  168. package/dist/core/config/redact.js +16 -0
  169. package/dist/core/config/resolve-env.js +43 -0
  170. package/dist/core/config/resolve-llm.js +130 -0
  171. package/dist/core/config/resolve.js +68 -0
  172. package/dist/core/config/resolvers/ast-validation.js +8 -0
  173. package/dist/core/config/resolvers/context.js +21 -0
  174. package/dist/core/config/resolvers/observability.js +45 -0
  175. package/dist/core/config/resolvers/output.js +8 -0
  176. package/dist/core/config/resolvers/permission-mode.js +6 -0
  177. package/dist/core/config/resolvers/security.js +14 -0
  178. package/dist/core/config/resolvers/server.js +36 -0
  179. package/dist/core/config/resolvers/tool-authorization.js +39 -0
  180. package/dist/core/config/resolvers/ui.js +26 -0
  181. package/dist/core/config/types/config-file.js +2 -0
  182. package/dist/core/config/types/primitives.js +9 -0
  183. package/dist/core/config/types/resolved.js +2 -0
  184. package/dist/core/config/types.js +4 -0
  185. package/dist/core/config/validate.js +852 -0
  186. package/dist/core/context/assembly/default-prompt-assembler.js +7 -0
  187. package/dist/core/context/assembly/prompt-assembler.js +2 -0
  188. package/dist/core/context/ast/import-extractor.js +28 -0
  189. package/dist/core/context/ast/module-resolver.js +61 -0
  190. package/dist/core/context/ast/source-outline.js +25 -0
  191. package/dist/core/context/audit-constants.js +23 -0
  192. package/dist/core/context/audit.js +54 -0
  193. package/dist/core/context/budget/dynamic-adjuster.js +149 -0
  194. package/dist/core/context/budget/example-integration.js +49 -0
  195. package/dist/core/context/budget/integration.js +93 -0
  196. package/dist/core/context/builder.js +289 -0
  197. package/dist/core/context/cache/errors.js +16 -0
  198. package/dist/core/context/cache/incremental-updater.js +131 -0
  199. package/dist/core/context/cache/index.js +25 -0
  200. package/dist/core/context/cache/path-resolver.js +127 -0
  201. package/dist/core/context/cache/prompt-caching.js +207 -0
  202. package/dist/core/context/cache/store-factory.js +63 -0
  203. package/dist/core/context/cache/store.js +193 -0
  204. package/dist/core/context/cache/types.js +15 -0
  205. package/dist/core/context/compression/js-like-comments.js +139 -0
  206. package/dist/core/context/compression/smart-compress.js +61 -0
  207. package/dist/core/context/compression/whitespace.js +26 -0
  208. package/dist/core/context/dependencies.js +102 -0
  209. package/dist/core/context/effectiveness/index.js +25 -0
  210. package/dist/core/context/effectiveness/tracker.js +253 -0
  211. package/dist/core/context/effectiveness/types.js +15 -0
  212. package/dist/core/context/formatters/index.js +7 -0
  213. package/dist/core/context/formatters/json-converter.js +662 -0
  214. package/dist/core/context/formatters/types.js +6 -0
  215. package/dist/core/context/formatters/xml-context.js +296 -0
  216. package/dist/core/context/gatherers/architecture-gatherer.js +75 -0
  217. package/dist/core/context/gatherers/artifact-gatherer.js +53 -0
  218. package/dist/core/context/gatherers/ast-gatherer.js +370 -0
  219. package/dist/core/context/gatherers/ghost-dependency-gatherer.js +46 -0
  220. package/dist/core/context/gatherers/git-diff-gatherer.js +91 -0
  221. package/dist/core/context/gatherers/git-history-gatherer.js +57 -0
  222. package/dist/core/context/gatherers/knowledge-gatherer.js +101 -0
  223. package/dist/core/context/gatherers/metadata-gatherer.js +59 -0
  224. package/dist/core/context/gatherers/primary-text-gatherer.js +36 -0
  225. package/dist/core/context/gatherers/ripgrep-gatherer.js +104 -0
  226. package/dist/core/context/hash.js +52 -0
  227. package/dist/core/context/index.js +3 -0
  228. package/dist/core/context/keywords.js +179 -0
  229. package/dist/core/context/policies/budget-policy.js +36 -0
  230. package/dist/core/context/policies/pack-until-full.js +419 -0
  231. package/dist/core/context/scoring/relevance.js +191 -0
  232. package/dist/core/context/service-deps.js +32 -0
  233. package/dist/core/context/service-helpers.js +32 -0
  234. package/dist/core/context/service.js +265 -0
  235. package/dist/core/context/steps/context-budget.js +157 -0
  236. package/dist/core/context/steps/context-gather.js +71 -0
  237. package/dist/core/context/steps/context-primary.js +19 -0
  238. package/dist/core/context/steps/context-promotion.js +78 -0
  239. package/dist/core/context/steps/context-targets.js +85 -0
  240. package/dist/core/context/steps/types.js +2 -0
  241. package/dist/core/context/summarization/index.js +27 -0
  242. package/dist/core/context/summarization/prompts.js +80 -0
  243. package/dist/core/context/summarization/summarizer.js +377 -0
  244. package/dist/core/context/summarization/types.js +29 -0
  245. package/dist/core/context/targeting/churn-policy.js +27 -0
  246. package/dist/core/context/targeting/target-resolver.js +491 -0
  247. package/dist/core/context/token/adaptive-budget.js +364 -0
  248. package/dist/core/context/token/cache.js +163 -0
  249. package/dist/core/context/token/counter.js +190 -0
  250. package/dist/core/context/token/encoding-registry.js +173 -0
  251. package/dist/core/context/token/index.js +31 -0
  252. package/dist/core/context/token/token-budget.js +213 -0
  253. package/dist/core/context/token/types.js +10 -0
  254. package/dist/core/context/truncation/index.js +23 -0
  255. package/dist/core/context/truncation/semantic-truncator.js +103 -0
  256. package/dist/core/context/truncation/strategies/error-stack.js +94 -0
  257. package/dist/core/context/truncation/strategies/generic.js +48 -0
  258. package/dist/core/context/truncation/strategies/git-diff.js +99 -0
  259. package/dist/core/context/truncation/strategies/index.js +10 -0
  260. package/dist/core/context/truncation/strategies/json.js +142 -0
  261. package/dist/core/context/truncation/strategies/log.js +131 -0
  262. package/dist/core/context/truncation/strategies/test-result.js +140 -0
  263. package/dist/core/context/truncation/type-detector.js +133 -0
  264. package/dist/core/context/truncation/types.js +16 -0
  265. package/dist/core/context/types.js +2 -0
  266. package/dist/core/extensions/index.js +118 -0
  267. package/dist/core/extensions/load.js +36 -0
  268. package/dist/core/extensions/merge.js +29 -0
  269. package/dist/core/extensions/paths.js +40 -0
  270. package/dist/core/extensions/redact.js +37 -0
  271. package/dist/core/extensions/schemas.js +70 -0
  272. package/dist/core/extensions/types.js +2 -0
  273. package/dist/core/facades/cli-authorization-allowlist.js +3 -0
  274. package/dist/core/facades/cli-authorization-non-interactive.js +3 -0
  275. package/dist/core/facades/cli-authorization-provider.js +2 -0
  276. package/dist/core/facades/cli-chat.js +11 -0
  277. package/dist/core/facades/cli-command-allowlist.js +3 -0
  278. package/dist/core/facades/cli-command-chat.js +8 -0
  279. package/dist/core/facades/cli-command-checkpoint.js +3 -0
  280. package/dist/core/facades/cli-command-config.js +10 -0
  281. package/dist/core/facades/cli-command-dispatcher.js +2 -0
  282. package/dist/core/facades/cli-command-parallel.js +8 -0
  283. package/dist/core/facades/cli-command-session.js +2 -0
  284. package/dist/core/facades/cli-command-tool-names.js +6 -0
  285. package/dist/core/facades/cli-context.js +8 -0
  286. package/dist/core/facades/cli-headless.js +3 -0
  287. package/dist/core/facades/cli-observability.js +3 -0
  288. package/dist/core/facades/cli-program-bootstrap.js +2 -0
  289. package/dist/core/facades/cli-reporters.js +5 -0
  290. package/dist/core/facades/cli-run-execute.js +3 -0
  291. package/dist/core/facades/cli-run-handler.js +7 -0
  292. package/dist/core/facades/cli-run-headless-error-writer.js +2 -0
  293. package/dist/core/facades/cli-run-loop-params.js +2 -0
  294. package/dist/core/facades/cli-run-persist-session.js +2 -0
  295. package/dist/core/facades/cli-run-runtime-llm.js +5 -0
  296. package/dist/core/facades/cli-serve.js +21 -0
  297. package/dist/core/facades/cli-slash-runtime.js +9 -0
  298. package/dist/core/facades/cli-subagent.js +2 -0
  299. package/dist/core/facades/cli-ui.js +5 -0
  300. package/dist/core/facades/cli-utils-llm-output.js +3 -0
  301. package/dist/core/facades/cli-utils-path.js +2 -0
  302. package/dist/core/facades/cli-utils-worktree.js +2 -0
  303. package/dist/core/failure/diagnostics.js +221 -0
  304. package/dist/core/feedback/index.js +28 -0
  305. package/dist/core/feedback/parsers.js +59 -0
  306. package/dist/core/feedback/patterns.js +26 -0
  307. package/dist/core/feedback/types.js +2 -0
  308. package/dist/core/grizzco/domain/grizzco-types.js +41 -0
  309. package/dist/core/grizzco/dsl/DecisionEngine.js +149 -0
  310. package/dist/core/grizzco/dsl/MicroTaskRunner.js +39 -0
  311. package/dist/core/grizzco/dsl/llm-strategy.js +80 -0
  312. package/dist/core/grizzco/dsl/strategies.js +69 -0
  313. package/dist/core/grizzco/dsl/types.js +2 -0
  314. package/dist/core/grizzco/engine/observability/event-adapter.js +41 -0
  315. package/dist/core/grizzco/engine/observability/index.js +3 -0
  316. package/dist/core/grizzco/engine/observability/loop-telemetry.js +51 -0
  317. package/dist/core/grizzco/engine/outcome/index.js +2 -0
  318. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +167 -0
  319. package/dist/core/grizzco/engine/pipeline/pipeline.js +335 -0
  320. package/dist/core/grizzco/engine/pipeline/types.js +2 -0
  321. package/dist/core/grizzco/engine/transaction/attempt-failure.js +242 -0
  322. package/dist/core/grizzco/engine/transaction/authorization-summary.js +44 -0
  323. package/dist/core/grizzco/engine/transaction/index.js +3 -0
  324. package/dist/core/grizzco/engine/transaction/report-mapper.js +50 -0
  325. package/dist/core/grizzco/engine/transaction/retry-policy.js +19 -0
  326. package/dist/core/grizzco/engine/transaction/runner-builder.js +45 -0
  327. package/dist/core/grizzco/engine/transaction/session.js +58 -0
  328. package/dist/core/grizzco/engine/transaction/transaction-runner.js +193 -0
  329. package/dist/core/grizzco/engine/transaction/types.js +2 -0
  330. package/dist/core/grizzco/execution/Executor.js +58 -0
  331. package/dist/core/grizzco/execution/RejectionManager.js +71 -0
  332. package/dist/core/grizzco/execution/WorkerFactory.js +31 -0
  333. package/dist/core/grizzco/flows/SalmonLoopFlow.js +102 -0
  334. package/dist/core/grizzco/runtime/apply-back-runtime.js +136 -0
  335. package/dist/core/grizzco/runtime/apply-back-utils.js +13 -0
  336. package/dist/core/grizzco/runtime/host/host-runner.js +99 -0
  337. package/dist/core/grizzco/runtime/host/index.js +2 -0
  338. package/dist/core/grizzco/runtime/host/types.js +2 -0
  339. package/dist/core/grizzco/services/CachedService.js +42 -0
  340. package/dist/core/grizzco/services/implementations/default/GitConfigService.js +38 -0
  341. package/dist/core/grizzco/services/implementations/mock/MockLockService.js +11 -0
  342. package/dist/core/grizzco/services/implementations/mock/MockUserQuotaService.js +11 -0
  343. package/dist/core/grizzco/services/registry.js +30 -0
  344. package/dist/core/grizzco/services/types.js +2 -0
  345. package/dist/core/grizzco/steps/answer.js +75 -0
  346. package/dist/core/grizzco/steps/apply-back.js +46 -0
  347. package/dist/core/grizzco/steps/apply.js +136 -0
  348. package/dist/core/grizzco/steps/ast-validate.js +37 -0
  349. package/dist/core/grizzco/steps/audit.js +311 -0
  350. package/dist/core/grizzco/steps/context.js +74 -0
  351. package/dist/core/grizzco/steps/display-answer.js +6 -0
  352. package/dist/core/grizzco/steps/display-report.js +158 -0
  353. package/dist/core/grizzco/steps/display-research.js +6 -0
  354. package/dist/core/grizzco/steps/displayReview.js +6 -0
  355. package/dist/core/grizzco/steps/explore.js +245 -0
  356. package/dist/core/grizzco/steps/extractIssues.js +27 -0
  357. package/dist/core/grizzco/steps/generateFixPlan.js +13 -0
  358. package/dist/core/grizzco/steps/generateReview.js +71 -0
  359. package/dist/core/grizzco/steps/patch.js +220 -0
  360. package/dist/core/grizzco/steps/plan.js +191 -0
  361. package/dist/core/grizzco/steps/preflight.js +93 -0
  362. package/dist/core/grizzco/steps/prepare-deps.js +49 -0
  363. package/dist/core/grizzco/steps/read-only-shrink.js +4 -0
  364. package/dist/core/grizzco/steps/research.js +188 -0
  365. package/dist/core/grizzco/steps/rollback.js +138 -0
  366. package/dist/core/grizzco/steps/shrink.js +64 -0
  367. package/dist/core/grizzco/steps/validate.js +40 -0
  368. package/dist/core/grizzco/steps/verify.js +136 -0
  369. package/dist/core/grizzco/validation/AstValidationService.js +133 -0
  370. package/dist/core/grizzco/validation/ContextValidator.js +17 -0
  371. package/dist/core/grizzco/validation/ast-validation-policy.js +11 -0
  372. package/dist/core/grizzco/workers/direct-write-worker.js +44 -0
  373. package/dist/core/grizzco/workers/git-apply-worker.js +75 -0
  374. package/dist/core/grizzco/workers/i-merge-worker.js +2 -0
  375. package/dist/core/grizzco/workers/mm-three-way-worker.js +117 -0
  376. package/dist/core/grizzco/workers/no-op-worker.js +18 -0
  377. package/dist/core/grizzco/workers/overwrite-binary-worker.js +29 -0
  378. package/dist/core/grizzco/workers/strata-sync-worker.js +69 -0
  379. package/dist/core/grizzco/workers/three-way-merge-worker.js +84 -0
  380. package/dist/core/grizzco/workers/three-way-staged-worker.js +93 -0
  381. package/dist/core/grizzco/workers/union-merge-worker.js +71 -0
  382. package/dist/core/history/input-history.js +55 -0
  383. package/dist/core/intent/chat-intent.js +250 -0
  384. package/dist/core/interaction/events/bus.js +52 -0
  385. package/dist/core/interaction/model/events.js +2 -0
  386. package/dist/core/interaction/model/index.js +3 -0
  387. package/dist/core/interaction/model/task-state.js +9 -0
  388. package/dist/core/interaction/model/transition-policy.js +50 -0
  389. package/dist/core/interaction/model/types.js +2 -0
  390. package/dist/core/interaction/orchestration/facade.js +190 -0
  391. package/dist/core/interaction/orchestration/index.js +2 -0
  392. package/dist/core/interaction/orchestration/store.js +32 -0
  393. package/dist/core/interaction/sync/task-sync-engine.js +57 -0
  394. package/dist/core/interaction/turn-stop-reason.js +27 -0
  395. package/dist/core/language-support/index.js +3 -0
  396. package/dist/core/language-support/orchestrator.js +37 -0
  397. package/dist/core/language-support/strategies/extension-candidate-strategy.js +27 -0
  398. package/dist/core/language-support/strategies/index.js +3 -0
  399. package/dist/core/language-support/strategies/language-query-strategy.js +26 -0
  400. package/dist/core/llm/ai-sdk/chat-executor.js +88 -0
  401. package/dist/core/llm/ai-sdk/langfuse-headers.js +28 -0
  402. package/dist/core/llm/ai-sdk/message-mapper.js +240 -0
  403. package/dist/core/llm/ai-sdk/observation-context.js +16 -0
  404. package/dist/core/llm/ai-sdk/provider-factory.js +29 -0
  405. package/dist/core/llm/ai-sdk/request-params.js +18 -0
  406. package/dist/core/llm/ai-sdk/request-runtime.js +168 -0
  407. package/dist/core/llm/ai-sdk/result-mapper.js +31 -0
  408. package/dist/core/llm/ai-sdk/retry-classifier.js +82 -0
  409. package/dist/core/llm/ai-sdk/retry-executor.js +38 -0
  410. package/dist/core/llm/ai-sdk.js +92 -0
  411. package/dist/core/llm/audit.js +2 -0
  412. package/dist/core/llm/base-url.js +18 -0
  413. package/dist/core/llm/contracts/repair.js +68 -0
  414. package/dist/core/llm/errors.js +172 -0
  415. package/dist/core/llm/factory.js +21 -0
  416. package/dist/core/llm/http/index.js +2 -0
  417. package/dist/core/llm/index.js +6 -0
  418. package/dist/core/llm/message-composition.js +25 -0
  419. package/dist/core/llm/openai.js +69 -0
  420. package/dist/core/llm/output-policy.js +192 -0
  421. package/dist/core/llm/phase-router.js +55 -0
  422. package/dist/core/llm/redact.js +37 -0
  423. package/dist/core/llm/registry.js +81 -0
  424. package/dist/core/llm/retry-utils.js +114 -0
  425. package/dist/core/llm/stream-utils.js +87 -0
  426. package/dist/core/llm/utils.js +82 -0
  427. package/dist/core/observability/audit-file.js +199 -0
  428. package/dist/core/observability/audit-trail.js +125 -0
  429. package/dist/core/observability/authorization-decisions.js +54 -0
  430. package/dist/core/observability/debug-artifacts.js +61 -0
  431. package/dist/core/observability/error-envelope.js +63 -0
  432. package/dist/core/observability/error-mapping.js +271 -0
  433. package/dist/core/observability/ignored-error.js +6 -0
  434. package/dist/core/observability/logger.js +457 -0
  435. package/dist/core/observability/loop-event-reporter.js +46 -0
  436. package/dist/core/observability/monitor.js +240 -0
  437. package/dist/core/observability/run-outcome-reporter.js +15 -0
  438. package/dist/core/observability/token-usage.js +36 -0
  439. package/dist/core/observability/ui-log-sanitize.js +35 -0
  440. package/dist/core/patch/aggregator.js +93 -0
  441. package/dist/core/patch/diff.js +298 -0
  442. package/dist/core/permission-gate/default-gate.js +115 -0
  443. package/dist/core/permission-gate/gate.js +2 -0
  444. package/dist/core/permission-gate/types.js +2 -0
  445. package/dist/core/plan/index.js +2 -0
  446. package/dist/core/plan/manager.js +123 -0
  447. package/dist/core/plan/markdown-editor.js +238 -0
  448. package/dist/core/plan/storage.js +75 -0
  449. package/dist/core/plan/types.js +2 -0
  450. package/dist/core/plugin/interface.js +2 -0
  451. package/dist/core/plugin/loader.js +130 -0
  452. package/dist/core/plugin/registry.js +90 -0
  453. package/dist/core/plugin/validator.js +98 -0
  454. package/dist/core/prompts/registry.js +189 -0
  455. package/dist/core/prompts/runtime.js +69 -0
  456. package/dist/core/prompts/schema.js +2 -0
  457. package/dist/core/prompts/templates/phases/explore_user.hbs +26 -0
  458. package/dist/core/prompts/templates/phases/patch_user.hbs +57 -0
  459. package/dist/core/prompts/templates/phases/plan_user.hbs +33 -0
  460. package/dist/core/prompts/templates/system/_context_json_legend.hbs +21 -0
  461. package/dist/core/prompts/templates/system/_tool_defs.hbs +60 -0
  462. package/dist/core/prompts/templates/system/explore_system.hbs +26 -0
  463. package/dist/core/prompts/templates/system/main_system.hbs +18 -0
  464. package/dist/core/prompts/templates/system/patch_system.hbs +10 -0
  465. package/dist/core/prompts/templates/system/plan_system.hbs +1 -0
  466. package/dist/core/prompts/templates/system/reflection.hbs +39 -0
  467. package/dist/core/protocols/a2a/agent-card.js +30 -0
  468. package/dist/core/protocols/a2a/mapper.js +14 -0
  469. package/dist/core/protocols/a2a/sdk/auth-middleware.js +31 -0
  470. package/dist/core/protocols/a2a/sdk/executor.js +301 -0
  471. package/dist/core/protocols/a2a/sdk/server.js +24 -0
  472. package/dist/core/protocols/a2a/task-projection.js +45 -0
  473. package/dist/core/protocols/acp/acp-command-runner.js +204 -0
  474. package/dist/core/protocols/acp/acp-filesystem.js +43 -0
  475. package/dist/core/protocols/acp/checkpoint-meta.js +2 -0
  476. package/dist/core/protocols/acp/formal-agent.js +1201 -0
  477. package/dist/core/protocols/acp/handlers.js +51 -0
  478. package/dist/core/protocols/acp/permission-provider.js +122 -0
  479. package/dist/core/protocols/acp/stdio-server.js +116 -0
  480. package/dist/core/reflection/engine.js +55 -0
  481. package/dist/core/reflection/types.js +2 -0
  482. package/dist/core/runtime/agent-server-runtime.js +88 -0
  483. package/dist/core/runtime/bun-runtime.js +26 -0
  484. package/dist/core/runtime/command-runner-context.js +16 -0
  485. package/dist/core/runtime/exit-codes.js +11 -0
  486. package/dist/core/runtime/fastify-fetch-bridge.js +51 -0
  487. package/dist/core/runtime/fastify-server-bundle.js +26 -0
  488. package/dist/core/runtime/initialize.js +132 -0
  489. package/dist/core/runtime/loop-finalize.js +71 -0
  490. package/dist/core/runtime/loop-run-lifecycle.js +73 -0
  491. package/dist/core/runtime/loop-run-reporter.js +19 -0
  492. package/dist/core/runtime/loop-runtime-config.js +26 -0
  493. package/dist/core/runtime/loop-session-runner.js +30 -0
  494. package/dist/core/runtime/loop.js +84 -0
  495. package/dist/core/runtime/paths.js +84 -0
  496. package/dist/core/runtime/process-runner.js +16 -0
  497. package/dist/core/runtime/process-types.js +2 -0
  498. package/dist/core/runtime/semaphore.js +41 -0
  499. package/dist/core/runtime/sidecar-fastify-plugin.js +35 -0
  500. package/dist/core/runtime/sidecar-paths.js +47 -0
  501. package/dist/core/runtime/sidecar-route-catalog.js +103 -0
  502. package/dist/core/runtime/spawn-command.js +392 -0
  503. package/dist/core/runtime/spawn-interactive.js +71 -0
  504. package/dist/core/security/redaction.js +160 -0
  505. package/dist/core/session/compression.js +323 -0
  506. package/dist/core/session/flow.js +85 -0
  507. package/dist/core/session/manager.js +313 -0
  508. package/dist/core/session/pruning-strategy.js +153 -0
  509. package/dist/core/session/session-context-builder.js +122 -0
  510. package/dist/core/session/summary-sync.js +82 -0
  511. package/dist/core/session/token-tracker.js +82 -0
  512. package/dist/core/session/types.js +2 -0
  513. package/dist/core/skills/bridge.js +33 -0
  514. package/dist/core/skills/index.js +8 -0
  515. package/dist/core/skills/loader.js +80 -0
  516. package/dist/core/skills/parser.js +66 -0
  517. package/dist/core/skills/runtime/MicroTaskRunner.js +102 -0
  518. package/dist/core/skills/runtime/SkillRunner.js +108 -0
  519. package/dist/core/skills/strategy.js +29 -0
  520. package/dist/core/skills/types.js +2 -0
  521. package/dist/core/slash/index.js +6 -0
  522. package/dist/core/slash/parser.js +33 -0
  523. package/dist/core/slash/registry.js +78 -0
  524. package/dist/core/slash/router.js +76 -0
  525. package/dist/core/slash/steps/slash-decide.js +19 -0
  526. package/dist/core/slash/steps/slash-execute.js +73 -0
  527. package/dist/core/slash/steps/types.js +2 -0
  528. package/dist/core/slash/strategy.js +33 -0
  529. package/dist/core/slash/types.js +2 -0
  530. package/dist/core/strata/checkpoint/manager.js +492 -0
  531. package/dist/core/strata/checkpoint/snapshot-audit.js +88 -0
  532. package/dist/core/strata/checkpoint/snapshot-create.js +79 -0
  533. package/dist/core/strata/checkpoint/snapshot-write-tree.js +72 -0
  534. package/dist/core/strata/engine/shadow-merge-engine.js +394 -0
  535. package/dist/core/strata/index.js +15 -0
  536. package/dist/core/strata/interaction/content-guardian.js +59 -0
  537. package/dist/core/strata/interaction/file-system-provider.js +89 -0
  538. package/dist/core/strata/layers/file-state-resolver.js +157 -0
  539. package/dist/core/strata/layers/immutable-git-layer.js +42 -0
  540. package/dist/core/strata/layers/shadow-driver/copy-backend.js +114 -0
  541. package/dist/core/strata/layers/shadow-driver/env.js +29 -0
  542. package/dist/core/strata/layers/shadow-driver/error-classifier.js +41 -0
  543. package/dist/core/strata/layers/shadow-driver/index.js +17 -0
  544. package/dist/core/strata/layers/shadow-driver/readonly-lock.js +221 -0
  545. package/dist/core/strata/layers/shadow-driver/shadow-driver.js +234 -0
  546. package/dist/core/strata/layers/shadow-driver/strategy.js +86 -0
  547. package/dist/core/strata/layers/sidecar-layer.js +96 -0
  548. package/dist/core/strata/layers/worktree.js +240 -0
  549. package/dist/core/strata/runtime/environment.js +377 -0
  550. package/dist/core/strata/runtime/synchronizer.js +819 -0
  551. package/dist/core/strata/types.js +46 -0
  552. package/dist/core/streaming/canonical/canonical-responses-event-emitter.js +326 -0
  553. package/dist/core/streaming/canonical/function-call-item-id.js +13 -0
  554. package/dist/core/streaming/canonical/parts-from-llm-stream-chunk.js +54 -0
  555. package/dist/core/streaming/canonical/responses-event-emitter.js +127 -0
  556. package/dist/core/streaming/canonical/responses-events.js +2 -0
  557. package/dist/core/streaming/normalized-events.js +9 -0
  558. package/dist/core/streaming/normalized-from-text.js +47 -0
  559. package/dist/core/streaming/stream-assembler.js +347 -0
  560. package/dist/core/structured-output/index.js +3 -0
  561. package/dist/core/structured-output/json-extract.js +70 -0
  562. package/dist/core/structured-output/json-schema-validator.js +90 -0
  563. package/dist/core/structured-output/types.js +2 -0
  564. package/dist/core/sub-agent/artifacts/store.js +141 -0
  565. package/dist/core/sub-agent/artifacts/types.js +2 -0
  566. package/dist/core/sub-agent/controller.js +69 -0
  567. package/dist/core/sub-agent/core/loop.js +79 -0
  568. package/dist/core/sub-agent/core/manager.js +246 -0
  569. package/dist/core/sub-agent/registry-defaults.js +52 -0
  570. package/dist/core/sub-agent/registry.js +35 -0
  571. package/dist/core/sub-agent/tools/task-spawn.js +29 -0
  572. package/dist/core/sub-agent/types.js +23 -0
  573. package/dist/core/target-runtime/command-resolver.js +42 -0
  574. package/dist/core/target-runtime/index.js +3 -0
  575. package/dist/core/target-runtime/profile.js +73 -0
  576. package/dist/core/testgen/detector.js +17 -0
  577. package/dist/core/testgen/index.js +38 -0
  578. package/dist/core/testgen/templates.js +46 -0
  579. package/dist/core/tools/audit.js +140 -0
  580. package/dist/core/tools/authorization/types.js +2 -0
  581. package/dist/core/tools/budget.js +118 -0
  582. package/dist/core/tools/builtin/artifact.js +29 -0
  583. package/dist/core/tools/builtin/ast-grep.js +107 -0
  584. package/dist/core/tools/builtin/ast.js +62 -0
  585. package/dist/core/tools/builtin/code-search/backends/powershell.js +84 -0
  586. package/dist/core/tools/builtin/code-search/backends/rg.js +85 -0
  587. package/dist/core/tools/builtin/code-search/executor.js +87 -0
  588. package/dist/core/tools/builtin/code-search/parse/plain-grep.js +59 -0
  589. package/dist/core/tools/builtin/code-search/parse/rg-json.js +31 -0
  590. package/dist/core/tools/builtin/code-search/spec.js +82 -0
  591. package/dist/core/tools/builtin/fs.js +243 -0
  592. package/dist/core/tools/builtin/git.js +118 -0
  593. package/dist/core/tools/builtin/index.js +80 -0
  594. package/dist/core/tools/builtin/interaction.js +120 -0
  595. package/dist/core/tools/builtin/knowledge.js +98 -0
  596. package/dist/core/tools/builtin/plan.js +148 -0
  597. package/dist/core/tools/builtin/proposal.js +207 -0
  598. package/dist/core/tools/builtin/shell.js +71 -0
  599. package/dist/core/tools/builtin/verify.js +41 -0
  600. package/dist/core/tools/capability/executor.js +84 -0
  601. package/dist/core/tools/capability/runner.js +50 -0
  602. package/dist/core/tools/capability/types.js +2 -0
  603. package/dist/core/tools/dispatcher.js +80 -0
  604. package/dist/core/tools/headless-payload.js +37 -0
  605. package/dist/core/tools/loader.js +100 -0
  606. package/dist/core/tools/mapper.js +142 -0
  607. package/dist/core/tools/mcp/client.js +308 -0
  608. package/dist/core/tools/mcp/loader.js +110 -0
  609. package/dist/core/tools/mcp/schema.js +54 -0
  610. package/dist/core/tools/mcp/streamable-http.js +101 -0
  611. package/dist/core/tools/mcp/types.js +26 -0
  612. package/dist/core/tools/parallel/isolation.js +25 -0
  613. package/dist/core/tools/parallel/lock-manager.js +124 -0
  614. package/dist/core/tools/parallel/persistence.js +126 -0
  615. package/dist/core/tools/parallel/plan-builder.js +66 -0
  616. package/dist/core/tools/parallel/plan.js +2 -0
  617. package/dist/core/tools/parallel/refs.js +7 -0
  618. package/dist/core/tools/parallel/resolve-args.js +50 -0
  619. package/dist/core/tools/parallel/resource-helpers.js +35 -0
  620. package/dist/core/tools/parallel/resources.js +2 -0
  621. package/dist/core/tools/parallel/scheduler.js +372 -0
  622. package/dist/core/tools/parser.js +89 -0
  623. package/dist/core/tools/permissions/permission-rules.js +503 -0
  624. package/dist/core/tools/plugins/loader.js +102 -0
  625. package/dist/core/tools/policy.js +87 -0
  626. package/dist/core/tools/registry.js +29 -0
  627. package/dist/core/tools/router.js +514 -0
  628. package/dist/core/tools/sanitize.js +78 -0
  629. package/dist/core/tools/schema-utils.js +71 -0
  630. package/dist/core/tools/session.js +1105 -0
  631. package/dist/core/tools/streaming/ToolCallAccumulator.js +64 -0
  632. package/dist/core/tools/types.js +2 -0
  633. package/dist/core/types/authorization.js +2 -0
  634. package/dist/core/types/context.js +2 -0
  635. package/dist/core/types/errors.js +29 -0
  636. package/dist/core/types/execution.js +65 -0
  637. package/dist/core/types/index.js +9 -0
  638. package/dist/core/types/llm.js +9 -0
  639. package/dist/core/types/loop.js +2 -0
  640. package/dist/core/types/planning.js +2 -0
  641. package/dist/core/types/runtime.js +2 -0
  642. package/dist/core/types/usage.js +2 -0
  643. package/dist/core/ui/kaomoji.js +5 -0
  644. package/dist/core/utils/path.js +116 -0
  645. package/dist/core/utils/platform-shell.js +10 -0
  646. package/dist/core/utils/sanitizer.js +107 -0
  647. package/dist/core/verification/runner.js +265 -0
  648. package/dist/integrations/langfuse/litellm-langfuse-outcome-reporter.js +272 -0
  649. package/dist/integrations/langfuse/outcome-proxy.js +68 -0
  650. package/dist/interfaces/cli/task-runner.js +11 -0
  651. package/dist/languages/typescript/index.js +178 -0
  652. package/dist/locales/en.js +679 -0
  653. package/dist/locales/index.js +11 -0
  654. package/dist/utils/eol.js +35 -0
  655. package/package.json +153 -0
@@ -0,0 +1,1105 @@
1
+ import * as crypto from 'crypto';
2
+ import path from 'path';
3
+ import { emitLlmOutput, emitLlmStreamDelta, emitLlmStreamEnd } from '../llm/output-policy.js';
4
+ import { redactErrorMessage, redactJsonString, redactValue } from '../llm/redact.js';
5
+ import { recordAuditEvent } from '../observability/audit-trail.js';
6
+ import { getLogger } from '../observability/logger.js';
7
+ import { CanonicalResponsesEventEmitter, } from '../streaming/canonical/canonical-responses-event-emitter.js';
8
+ import { mapLlmStreamChunkToCanonicalStreamParts } from '../streaming/canonical/parts-from-llm-stream-chunk.js';
9
+ import { Phase } from '../types/runtime.js';
10
+ import { isSafeRelativePath, normalizePath } from '../utils/path.js';
11
+ import { buildHeadlessToolInputPayload } from './headless-payload.js';
12
+ import { toolToOpenAI } from './mapper.js';
13
+ import { InMemoryLockManager } from './parallel/lock-manager.js';
14
+ import { PlanPersistence } from './parallel/persistence.js';
15
+ import { ParallelScheduler } from './parallel/scheduler.js';
16
+ import { ToolCallAccumulator } from './streaming/ToolCallAccumulator.js';
17
+ function safeParseJson(argsText) {
18
+ if (typeof argsText !== 'string') {
19
+ return { ok: true, value: argsText };
20
+ }
21
+ const trimmed = argsText.trim();
22
+ if (!trimmed) {
23
+ return { ok: true, value: {} };
24
+ }
25
+ try {
26
+ let value = JSON.parse(trimmed);
27
+ // Some models will JSON-encode the arguments object as a string (double-encoded JSON).
28
+ // Example raw args: "\"{\\\"pattern\\\":\\\"README\\\"}\"" -> first parse returns string.
29
+ if (typeof value === 'string') {
30
+ const nested = value.trim();
31
+ const looksJsonObject = (nested.startsWith('{') && nested.endsWith('}')) ||
32
+ (nested.startsWith('[') && nested.endsWith(']'));
33
+ if (looksJsonObject) {
34
+ try {
35
+ value = JSON.parse(nested);
36
+ }
37
+ catch {
38
+ // Ignore: fall back to the first parse result to preserve observability.
39
+ }
40
+ }
41
+ }
42
+ return { ok: true, value };
43
+ }
44
+ catch (e) {
45
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
46
+ }
47
+ }
48
+ function formatToolResultForModel(result) {
49
+ const payload = {
50
+ id: result.id,
51
+ toolName: result.toolName,
52
+ status: result.status,
53
+ output: result.output,
54
+ summary: result.summary,
55
+ error: result.error,
56
+ meta: result.meta,
57
+ durationMs: result.durationMs,
58
+ };
59
+ try {
60
+ return JSON.stringify(payload);
61
+ }
62
+ catch {
63
+ return JSON.stringify({
64
+ id: result.id,
65
+ toolName: result.toolName,
66
+ status: 'error',
67
+ error: {
68
+ code: 'SERIALIZE_ERROR',
69
+ message: 'Failed to serialize tool result',
70
+ retryable: false,
71
+ },
72
+ });
73
+ }
74
+ }
75
+ function safeStringifyForAudit(value) {
76
+ try {
77
+ return redactJsonString(JSON.stringify(redactValue(value)));
78
+ }
79
+ catch {
80
+ return '[Unserializable]';
81
+ }
82
+ }
83
+ function defaultMaxToolCallsTotalForPhase(phase) {
84
+ if (phase === Phase.EXPLORE)
85
+ return 18;
86
+ if (phase === Phase.PLAN)
87
+ return 10;
88
+ if (phase === Phase.PATCH)
89
+ return 10;
90
+ return 10;
91
+ }
92
+ function defaultMaxToolCallsPerRoundForPhase(phase) {
93
+ if (phase === Phase.EXPLORE)
94
+ return 6;
95
+ if (phase === Phase.PLAN)
96
+ return 4;
97
+ if (phase === Phase.PATCH)
98
+ return 4;
99
+ return 4;
100
+ }
101
+ function getToolCallBudget(session) {
102
+ const maxTotal = session.maxToolCallsTotal ?? defaultMaxToolCallsTotalForPhase(session.phase);
103
+ const maxPerRound = session.maxToolCallsPerRound ?? defaultMaxToolCallsPerRoundForPhase(session.phase);
104
+ return {
105
+ maxTotal: Math.max(0, Math.floor(maxTotal)),
106
+ maxPerRound: Math.max(0, Math.floor(maxPerRound)),
107
+ };
108
+ }
109
+ function resetToolCallBudgetState(session) {
110
+ const budget = getToolCallBudget(session);
111
+ const state = { used: 0, ...budget };
112
+ session.__toolCallBudgetState = state;
113
+ return state;
114
+ }
115
+ function getToolCallBudgetState(session) {
116
+ const budget = getToolCallBudget(session);
117
+ const anySession = session;
118
+ const existing = anySession.__toolCallBudgetState;
119
+ if (!existing) {
120
+ const created = { used: 0, ...budget };
121
+ anySession.__toolCallBudgetState = created;
122
+ return created;
123
+ }
124
+ // Ensure runtime overrides are respected.
125
+ existing.maxTotal = budget.maxTotal;
126
+ existing.maxPerRound = budget.maxPerRound;
127
+ return existing;
128
+ }
129
+ function initToolCallRoundBudget(params) {
130
+ const budgetState = getToolCallBudgetState(params.session);
131
+ const roundCap = Math.min(budgetState.maxPerRound, Math.max(0, budgetState.maxTotal - budgetState.used));
132
+ budgetState.used += params.preparedCount;
133
+ if (params.preparedCount > roundCap) {
134
+ params.session.emit?.({
135
+ type: 'log',
136
+ level: 'warn',
137
+ message: `Tool call budget exceeded; denying ${params.preparedCount - roundCap} tool calls (phase=${params.phase}, round=${params.round})`,
138
+ timestamp: new Date(),
139
+ });
140
+ }
141
+ return { roundCap, budgetState };
142
+ }
143
+ function isFunctionCallStreamPart(part) {
144
+ switch (part.type) {
145
+ case 'function_call.start':
146
+ case 'function_call_arguments.delta':
147
+ case 'function_call_arguments.done':
148
+ case 'function_call.done':
149
+ return typeof part.callId === 'string';
150
+ default:
151
+ return false;
152
+ }
153
+ }
154
+ function groupNewFunctionCallPartsByCallId(params) {
155
+ const out = new Map();
156
+ for (const part of params.parts) {
157
+ if (!isFunctionCallStreamPart(part))
158
+ continue;
159
+ if (!part.callId)
160
+ continue;
161
+ if (params.alreadyEmittedCallIds.has(part.callId))
162
+ continue;
163
+ const bucket = out.get(part.callId);
164
+ if (bucket)
165
+ bucket.push(part);
166
+ else
167
+ out.set(part.callId, [part]);
168
+ }
169
+ return out;
170
+ }
171
+ function emitCanonicalResponsesEvents(params) {
172
+ for (const event of params.events) {
173
+ params.emit({
174
+ type: 'llm.responses.event',
175
+ kind: params.llmOutput.kind,
176
+ step: params.llmOutput.step,
177
+ streamId: params.streamId,
178
+ phase: params.phase,
179
+ round: params.round,
180
+ source: params.source,
181
+ event,
182
+ timestamp: params.timestamp,
183
+ });
184
+ }
185
+ }
186
+ async function consumeAssistantStreamTurn(params) {
187
+ const stream = params.session.llm.chatStream(params.messages, {
188
+ ...params.chatOptions,
189
+ phase: params.phase,
190
+ tools: params.openAITools,
191
+ toolSpecs: params.allowedSpecs,
192
+ toolChoice: params.openAITools.length > 0 ? 'auto' : undefined,
193
+ });
194
+ let content = '';
195
+ let finishReason;
196
+ let finishUsage;
197
+ for await (const chunk of stream) {
198
+ if (params.canonicalEmitter && params.session.emit && params.session.llmOutput) {
199
+ const parts = mapLlmStreamChunkToCanonicalStreamParts({ streamId: params.streamId, chunk });
200
+ const at = new Date();
201
+ const source = chunk.source ?? 'provider';
202
+ const toolPartsByCallId = groupNewFunctionCallPartsByCallId({
203
+ parts,
204
+ alreadyEmittedCallIds: params.emittedModelToolCallIds,
205
+ });
206
+ for (const [callId, callParts] of toolPartsByCallId) {
207
+ for (const part of callParts) {
208
+ const events = params.canonicalEmitter.push(part);
209
+ emitCanonicalResponsesEvents({
210
+ emit: params.session.emit,
211
+ llmOutput: params.session.llmOutput,
212
+ streamId: params.streamId,
213
+ phase: params.phase,
214
+ round: params.round,
215
+ source,
216
+ events,
217
+ timestamp: at,
218
+ });
219
+ }
220
+ params.emittedModelToolCallIds.add(callId);
221
+ }
222
+ }
223
+ if (typeof chunk?.contentDelta === 'string' && chunk.contentDelta) {
224
+ if (params.session.llmOutput) {
225
+ emitLlmStreamDelta({
226
+ emit: params.session.emit,
227
+ policy: params.session.llmOutput.policy,
228
+ kind: params.session.llmOutput.kind,
229
+ step: params.session.llmOutput.step,
230
+ streamId: params.streamId,
231
+ content: chunk.contentDelta,
232
+ });
233
+ }
234
+ content += chunk.contentDelta;
235
+ }
236
+ params.toolCalls.append(chunk);
237
+ if (chunk?.done) {
238
+ finishReason = chunk.finishReason;
239
+ if (chunk.usage &&
240
+ typeof chunk.usage.promptTokens === 'number' &&
241
+ typeof chunk.usage.completionTokens === 'number') {
242
+ finishUsage = chunk.usage;
243
+ }
244
+ break;
245
+ }
246
+ }
247
+ return { content, finishReason, finishUsage };
248
+ }
249
+ async function applyEmptyStreamFallback(params) {
250
+ if (params.content.trim() !== '' || params.collectedToolCalls.length > 0) {
251
+ return { usedFallback: false, content: params.content, toolCalls: params.collectedToolCalls };
252
+ }
253
+ recordAuditEvent('llm.stream.empty_fallback', { phase: params.phase, round: params.round }, { source: 'llm', severity: 'low', scope: 'session', phase: params.phase });
254
+ const fallback = await params.session.llm.chat(params.messages, {
255
+ ...params.chatOptions,
256
+ phase: params.phase,
257
+ tools: params.openAITools,
258
+ toolSpecs: params.allowedSpecs,
259
+ toolChoice: params.openAITools.length > 0 ? 'auto' : undefined,
260
+ });
261
+ const finalContent = fallback.content || '';
262
+ const finalCalls = Array.isArray(fallback.tool_calls) ? fallback.tool_calls : [];
263
+ if (params.session.llmOutput && finalContent) {
264
+ emitLlmOutput({
265
+ emit: params.session.emit,
266
+ policy: params.session.llmOutput.policy,
267
+ kind: params.session.llmOutput.kind,
268
+ step: params.session.llmOutput.step,
269
+ content: finalContent,
270
+ });
271
+ }
272
+ return { usedFallback: true, content: finalContent, toolCalls: finalCalls };
273
+ }
274
+ function emitSynthesizedFunctionCallClosures(params) {
275
+ if (!params.canonicalEmitter)
276
+ return;
277
+ if (params.collectedToolCalls.length === 0)
278
+ return;
279
+ const seenDoneIds = new Set();
280
+ for (const call of params.collectedToolCalls) {
281
+ const callId = call?.id;
282
+ const toolName = call?.function?.name;
283
+ const argsText = call?.function?.arguments;
284
+ if (typeof callId !== 'string' || !callId)
285
+ continue;
286
+ if (typeof toolName !== 'string' || !toolName)
287
+ continue;
288
+ if (seenDoneIds.has(callId))
289
+ continue;
290
+ seenDoneIds.add(callId);
291
+ if (!params.emittedModelToolCallIds.has(callId)) {
292
+ params.emittedModelToolCallIds.add(callId);
293
+ const at = new Date();
294
+ const startEvents = params.canonicalEmitter.push({
295
+ type: 'function_call.start',
296
+ streamId: params.streamId,
297
+ callId,
298
+ name: toolName,
299
+ });
300
+ const argEvents = params.canonicalEmitter.push({
301
+ type: 'function_call_arguments.done',
302
+ streamId: params.streamId,
303
+ callId,
304
+ name: toolName,
305
+ arguments: typeof argsText === 'string' ? argsText : '{}',
306
+ });
307
+ emitCanonicalResponsesEvents({
308
+ emit: params.emit,
309
+ llmOutput: params.llmOutput,
310
+ streamId: params.streamId,
311
+ phase: params.phase,
312
+ round: params.round,
313
+ source: 'synthesized',
314
+ events: [...startEvents, ...argEvents],
315
+ timestamp: at,
316
+ });
317
+ }
318
+ const doneAt = new Date();
319
+ const doneEvents = params.canonicalEmitter.push({
320
+ type: 'function_call.done',
321
+ streamId: params.streamId,
322
+ callId,
323
+ name: toolName,
324
+ arguments: typeof argsText === 'string' ? argsText : '{}',
325
+ });
326
+ emitCanonicalResponsesEvents({
327
+ emit: params.emit,
328
+ llmOutput: params.llmOutput,
329
+ streamId: params.streamId,
330
+ phase: params.phase,
331
+ round: params.round,
332
+ source: 'synthesized',
333
+ events: doneEvents,
334
+ timestamp: doneAt,
335
+ });
336
+ }
337
+ }
338
+ function resolveToolCallingForSession(session, phase) {
339
+ const allowedSpecs = session.toolstack.registry.listAll().filter((spec) => {
340
+ return session.toolstack.policy.decide(phase, spec, {
341
+ worktreeRoot: session.runtime.worktreeRoot,
342
+ }).allowed;
343
+ });
344
+ const openAITools = allowedSpecs.map(toolToOpenAI);
345
+ return { allowedSpecs, openAITools };
346
+ }
347
+ function emitToolCallingEnabledLogIfNeeded(session, openAITools) {
348
+ if (openAITools.length === 0)
349
+ return;
350
+ session.emit?.({
351
+ type: 'log',
352
+ level: 'debug',
353
+ message: `Tool calling enabled (${openAITools.length} tools available)`,
354
+ timestamp: new Date(),
355
+ });
356
+ }
357
+ function createCanonicalEmitterRegistry(session) {
358
+ if (!session.emit || !session.llmOutput) {
359
+ return {
360
+ get: () => null,
361
+ release: () => { },
362
+ clear: () => { },
363
+ };
364
+ }
365
+ const emitters = new Map();
366
+ return {
367
+ get(streamId) {
368
+ if (!streamId)
369
+ return null;
370
+ const existing = emitters.get(streamId);
371
+ if (existing)
372
+ return existing;
373
+ const created = new CanonicalResponsesEventEmitter();
374
+ emitters.set(streamId, created);
375
+ return created;
376
+ },
377
+ release(streamId) {
378
+ if (!streamId)
379
+ return;
380
+ emitters.delete(streamId);
381
+ },
382
+ clear() {
383
+ emitters.clear();
384
+ },
385
+ };
386
+ }
387
+ function createStreamingStreamId(params) {
388
+ if (!params.session.llmOutput)
389
+ return '';
390
+ return `llm-${params.session.llmOutput.kind}-${params.phase}-${params.round}-${crypto.randomUUID()}`;
391
+ }
392
+ /**
393
+ * Runs an OpenAI-style tool calling loop:
394
+ * - Send messages (+ tools)
395
+ * - If assistant returns tool_calls, execute them via ToolRouter
396
+ * - Feed tool results back as role='tool' messages
397
+ * - Repeat until no tool_calls or maxRounds reached
398
+ */
399
+ export async function chatWithTools(initialMessages, chatOptions, session) {
400
+ const maxRounds = session.maxRounds ?? 6;
401
+ const phase = session.phase;
402
+ resetToolCallBudgetState(session);
403
+ const { allowedSpecs, openAITools } = resolveToolCallingForSession(session, phase);
404
+ emitToolCallingEnabledLogIfNeeded(session, openAITools);
405
+ const messages = [...initialMessages];
406
+ for (let round = 0; round < maxRounds; round++) {
407
+ // Check for abort before starting a new round
408
+ if (chatOptions.signal?.aborted) {
409
+ throw new Error('Operation aborted');
410
+ }
411
+ const roundStartedAt = Date.now();
412
+ let assistant;
413
+ try {
414
+ assistant = await session.llm.chat(messages, {
415
+ ...chatOptions,
416
+ phase,
417
+ tools: openAITools,
418
+ toolSpecs: allowedSpecs,
419
+ toolChoice: openAITools.length > 0 ? 'auto' : undefined,
420
+ });
421
+ }
422
+ catch (e) {
423
+ recordAuditEvent('llm.round', {
424
+ status: 'error',
425
+ streamed: false,
426
+ phase,
427
+ round,
428
+ model: session.runtime.model,
429
+ durationMs: Date.now() - roundStartedAt,
430
+ }, { source: 'llm', severity: 'low', scope: 'session', phase });
431
+ throw e;
432
+ }
433
+ recordAuditEvent('llm.round', {
434
+ status: 'ok',
435
+ streamed: false,
436
+ phase,
437
+ round,
438
+ model: session.runtime.model,
439
+ durationMs: Date.now() - roundStartedAt,
440
+ contentChars: typeof assistant.content === 'string' ? assistant.content.length : 0,
441
+ toolCallCount: Array.isArray(assistant.tool_calls) ? assistant.tool_calls.length : 0,
442
+ }, { source: 'llm', severity: 'low', scope: 'session', phase });
443
+ messages.push({
444
+ role: 'assistant',
445
+ content: assistant.content || '',
446
+ tool_calls: assistant.tool_calls,
447
+ });
448
+ const toolCalls = assistant.tool_calls || [];
449
+ if (!Array.isArray(toolCalls) || toolCalls.length === 0) {
450
+ if (session.llmOutput) {
451
+ emitLlmOutput({
452
+ emit: session.emit,
453
+ policy: session.llmOutput.policy,
454
+ kind: session.llmOutput.kind,
455
+ step: session.llmOutput.step,
456
+ content: assistant.content || '',
457
+ });
458
+ }
459
+ return assistant;
460
+ }
461
+ // Check for abort before executing tools
462
+ if (chatOptions.signal?.aborted) {
463
+ throw new Error('Operation aborted');
464
+ }
465
+ await executeToolCalls(session, phase, round, toolCalls, messages, chatOptions.signal);
466
+ }
467
+ // If we reach here, the model is stuck in tool calling. Return the last assistant content.
468
+ session.emit?.({
469
+ type: 'log',
470
+ level: 'warn',
471
+ message: `Tool calling exceeded maximum rounds (${maxRounds}); continuing without further tool execution`,
472
+ timestamp: new Date(),
473
+ });
474
+ const lastAssistant = [...messages].reverse().find((m) => m.role === 'assistant');
475
+ if (session.llmOutput && lastAssistant?.content) {
476
+ emitLlmOutput({
477
+ emit: session.emit,
478
+ policy: session.llmOutput.policy,
479
+ kind: session.llmOutput.kind,
480
+ step: session.llmOutput.step,
481
+ content: lastAssistant.content,
482
+ });
483
+ }
484
+ return lastAssistant || { role: 'assistant', content: '' };
485
+ }
486
+ function isObjectRecord(value) {
487
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
488
+ }
489
+ function unwrapRetryError(err) {
490
+ if (!err || typeof err !== 'object')
491
+ return err;
492
+ const candidate = err;
493
+ if (candidate.lastError)
494
+ return candidate.lastError;
495
+ return err;
496
+ }
497
+ function extractStatusCode(err) {
498
+ const unwrapped = unwrapRetryError(err);
499
+ if (!unwrapped || typeof unwrapped !== 'object')
500
+ return undefined;
501
+ const meta = unwrapped?.meta;
502
+ if (meta && typeof meta === 'object' && typeof meta.statusCode === 'number') {
503
+ return meta.statusCode;
504
+ }
505
+ const statusCode = unwrapped?.statusCode;
506
+ if (typeof statusCode === 'number')
507
+ return statusCode;
508
+ const response = unwrapped?.response;
509
+ if (response && typeof response === 'object' && typeof response.status === 'number') {
510
+ return response.status;
511
+ }
512
+ return undefined;
513
+ }
514
+ function extractNetworkCode(err) {
515
+ const unwrapped = unwrapRetryError(err);
516
+ if (!unwrapped || typeof unwrapped !== 'object')
517
+ return undefined;
518
+ const code = unwrapped?.code;
519
+ if (typeof code === 'string')
520
+ return code;
521
+ const cause = unwrapped?.cause;
522
+ if (cause && typeof cause === 'object' && typeof cause.code === 'string') {
523
+ return cause.code;
524
+ }
525
+ const meta = unwrapped?.meta;
526
+ if (meta && typeof meta === 'object' && typeof meta.causeName === 'string') {
527
+ return meta.causeName;
528
+ }
529
+ return undefined;
530
+ }
531
+ function extractProvider(err) {
532
+ const unwrapped = unwrapRetryError(err);
533
+ if (!unwrapped || typeof unwrapped !== 'object')
534
+ return undefined;
535
+ const meta = unwrapped?.meta;
536
+ if (meta && typeof meta === 'object' && typeof meta.provider === 'string') {
537
+ return meta.provider;
538
+ }
539
+ const provider = unwrapped?.provider;
540
+ if (typeof provider === 'string')
541
+ return provider;
542
+ return undefined;
543
+ }
544
+ const ENABLE_TOOL_ARG_REPAIR = process.env.SALMONLOOP_ENABLE_TOOL_ARG_REPAIR === '1' ||
545
+ process.env.SALMONLOOP_ENABLE_TOOL_ARG_REPAIR === 'true';
546
+ const SAFE_INFERRED_EXTENSIONS = new Set([
547
+ '.ts',
548
+ '.tsx',
549
+ '.js',
550
+ '.jsx',
551
+ '.json',
552
+ '.md',
553
+ '.txt',
554
+ '.css',
555
+ '.html',
556
+ '.vue',
557
+ '.py',
558
+ '.rs',
559
+ '.go',
560
+ '.java',
561
+ '.c',
562
+ '.cpp',
563
+ '.h',
564
+ ]);
565
+ function inferHighConfidenceFiles(instruction) {
566
+ const candidates = [];
567
+ const normalized = instruction || '';
568
+ if (/README\b/i.test(normalized)) {
569
+ candidates.push('README.md');
570
+ }
571
+ const pathLike = /(?:^|\s)([a-zA-Z0-9][a-zA-Z0-9._/-]*\.[a-zA-Z0-9]{1,8})(?:\s|$)/g;
572
+ let match = null;
573
+ while ((match = pathLike.exec(normalized)) !== null) {
574
+ const raw = match[1];
575
+ if (!raw)
576
+ continue;
577
+ const rel = normalizePath(raw).replace(/^(\.\/|\/)+/, '');
578
+ if (!rel)
579
+ continue;
580
+ if (!isSafeRelativePath(rel))
581
+ continue;
582
+ const lower = rel.toLowerCase();
583
+ if (lower.startsWith('.'))
584
+ continue;
585
+ if (lower.includes('/.'))
586
+ continue;
587
+ if (lower.startsWith('.git/') || lower.startsWith('.salmonloop/'))
588
+ continue;
589
+ if (lower.includes('node_modules/'))
590
+ continue;
591
+ const ext = path.extname(rel).toLowerCase();
592
+ if (!SAFE_INFERRED_EXTENSIONS.has(ext))
593
+ continue;
594
+ candidates.push(rel);
595
+ if (candidates.length >= 3)
596
+ break;
597
+ }
598
+ return Array.from(new Set(candidates));
599
+ }
600
+ function extractInstructionText(messages) {
601
+ const lastUser = [...messages].reverse().find((m) => m.role === 'user');
602
+ const text = typeof lastUser?.content === 'string' ? lastUser.content : '';
603
+ if (!text)
604
+ return '';
605
+ const match = text.match(/(^|\n)# Instruction\s*\n([\s\S]*?)(\n# |$)/);
606
+ if (!match)
607
+ return text;
608
+ return match[2]?.trim() || '';
609
+ }
610
+ function prepareToolCallRequests(calls) {
611
+ return calls.map((call) => {
612
+ const callId = call?.id || crypto.randomUUID();
613
+ const toolName = call?.function?.name;
614
+ const rawArgs = call?.function?.arguments;
615
+ return { callId, toolName, rawArgs };
616
+ });
617
+ }
618
+ async function runToolExecutionPlan(params) {
619
+ const scheduler = new ParallelScheduler(params.session.toolstack.router, new InMemoryLockManager());
620
+ const runSignal = params.signal ?? new AbortController().signal;
621
+ let result = (await scheduler.run(params.plan, { ...params.session.runtime, phase: params.phase }, runSignal));
622
+ const persistEnabled = process.env.NODE_ENV !== 'test';
623
+ const persistenceRoot = params.session.runtime.persistenceRoot || params.session.runtime.repoRoot;
624
+ if (persistEnabled) {
625
+ await PlanPersistence.save(persistenceRoot, params.plan, result, {
626
+ repoRoot: params.session.runtime.repoRoot,
627
+ worktreeRoot: params.session.runtime.worktreeRoot,
628
+ persistenceRoot: params.session.runtime.persistenceRoot,
629
+ phase: params.phase,
630
+ model: params.session.runtime.model,
631
+ });
632
+ }
633
+ const waitForAuthorization = params.session.toolstack.router.waitForAuthorization;
634
+ const canWaitForAuth = typeof waitForAuthorization === 'function';
635
+ let resumeAttempts = 0;
636
+ while (result.blockedApprovals.length > 0 && canWaitForAuth && !runSignal.aborted) {
637
+ resumeAttempts++;
638
+ if (resumeAttempts > 10)
639
+ break;
640
+ await Promise.all(result.blockedApprovals.map(async (a) => {
641
+ await waitForAuthorization?.(a.nodeId, runSignal);
642
+ }));
643
+ result = (await scheduler.run(params.plan, { ...params.session.runtime, phase: params.phase }, runSignal, {
644
+ initialResults: result.nodeResults,
645
+ resumeBlockedApprovals: true,
646
+ }));
647
+ if (persistEnabled) {
648
+ await PlanPersistence.save(persistenceRoot, params.plan, result, {
649
+ repoRoot: params.session.runtime.repoRoot,
650
+ worktreeRoot: params.session.runtime.worktreeRoot,
651
+ persistenceRoot: params.session.runtime.persistenceRoot,
652
+ phase: params.phase,
653
+ model: params.session.runtime.model,
654
+ });
655
+ }
656
+ }
657
+ return result;
658
+ }
659
+ function applyStrictToolOutputSchemaValidation(params) {
660
+ if (params.result.status !== 'ok')
661
+ return;
662
+ const spec = params.session.toolstack.router.getSpec?.(params.toolName) ||
663
+ params.session.toolstack.registry.listAll().find((s) => s.name === params.toolName);
664
+ if (!spec?.outputSchema)
665
+ return;
666
+ const parsed = spec.outputSchema.safeParse(params.result.output);
667
+ if (parsed.success) {
668
+ params.result.output = parsed.data;
669
+ return;
670
+ }
671
+ const validationError = parsed.error.message;
672
+ getLogger().error(`[tool] schema violation for ${params.toolName} (callId: ${params.callId}): ${validationError}`);
673
+ params.result.status = 'error';
674
+ params.result.error = {
675
+ code: 'SCHEMA_VIOLATION',
676
+ message: `Tool output does not match expected schema: ${validationError}`,
677
+ retryable: false,
678
+ failurePhase: params.phase,
679
+ };
680
+ }
681
+ async function executeToolCalls(session, phase, round, calls, messages, signal) {
682
+ const prepared = prepareToolCallRequests(calls);
683
+ const { roundCap } = initToolCallRoundBudget({
684
+ session,
685
+ phase,
686
+ round,
687
+ preparedCount: prepared.length,
688
+ });
689
+ const toolResults = new Map();
690
+ const nodes = [];
691
+ const toolArgsPreviewByCallId = new Map();
692
+ const rawArgsPreviewByCallId = new Map();
693
+ const rawArgsTypeByCallId = new Map();
694
+ let allowedUsed = 0;
695
+ for (const item of prepared) {
696
+ const { callId, toolName, rawArgs } = item;
697
+ const normalizedToolName = typeof toolName === 'string' ? toolName : 'unknown';
698
+ let parsedArgsOk = true;
699
+ let argsValue = undefined;
700
+ let parsedArgsError;
701
+ const parsed = safeParseJson(rawArgs);
702
+ if (parsed.ok) {
703
+ argsValue = parsed.value;
704
+ rawArgsPreviewByCallId.set(callId, typeof rawArgs === 'string' ? redactJsonString(rawArgs) : undefined);
705
+ rawArgsTypeByCallId.set(callId, typeof rawArgs);
706
+ }
707
+ else {
708
+ parsedArgsOk = false;
709
+ parsedArgsError = parsed.error;
710
+ rawArgsPreviewByCallId.set(callId, typeof rawArgs === 'string' ? redactJsonString(rawArgs) : undefined);
711
+ rawArgsTypeByCallId.set(callId, typeof rawArgs);
712
+ }
713
+ // Repair common missing-args tool calls for weak tool-call models:
714
+ // - fs.read called with `{}` is almost always a missing `file` parameter.
715
+ // We only apply a conservative inference based on the explicit instruction block.
716
+ if (parsedArgsOk &&
717
+ ENABLE_TOOL_ARG_REPAIR &&
718
+ phase === Phase.EXPLORE &&
719
+ normalizedToolName === 'fs.read' &&
720
+ isObjectRecord(argsValue) &&
721
+ typeof argsValue.file !== 'string') {
722
+ const instruction = extractInstructionText(messages);
723
+ const inferred = inferHighConfidenceFiles(instruction);
724
+ if (inferred.length > 0) {
725
+ argsValue = { ...argsValue, file: inferred[0] };
726
+ }
727
+ }
728
+ if (parsedArgsOk) {
729
+ toolArgsPreviewByCallId.set(callId, safeStringifyForAudit(argsValue));
730
+ }
731
+ const input = session.eventPayload?.includeToolInput && parsedArgsOk
732
+ ? buildHeadlessToolInputPayload(argsValue)
733
+ : undefined;
734
+ const spec = typeof toolName === 'string'
735
+ ? session.toolstack.registry.listAll().find((s) => s.name === toolName)
736
+ : undefined;
737
+ if (typeof toolName === 'string') {
738
+ session.emit?.({
739
+ type: 'log',
740
+ level: 'debug',
741
+ message: `[tool] start ${toolName}`,
742
+ timestamp: new Date(),
743
+ });
744
+ }
745
+ session.emit?.({
746
+ type: 'tool.call.start',
747
+ callId,
748
+ toolName: normalizedToolName,
749
+ toolIntent: spec?.intent,
750
+ phase,
751
+ round,
752
+ input,
753
+ timestamp: new Date(),
754
+ });
755
+ // Hard budget: deny tool execution once the session exceeds the configured budget.
756
+ // We still return a tool result for protocol completeness and observability.
757
+ if (allowedUsed >= roundCap) {
758
+ toolResults.set(callId, {
759
+ id: callId,
760
+ toolName: typeof toolName === 'string' ? toolName : 'unknown',
761
+ source: 'builtin',
762
+ status: 'error',
763
+ error: {
764
+ code: 'TOOL_CALL_BUDGET_EXCEEDED',
765
+ message: 'Tool call denied: tool calling budget exceeded for this session. Continue without additional tool calls.',
766
+ retryable: false,
767
+ failurePhase: phase,
768
+ },
769
+ durationMs: 0,
770
+ });
771
+ continue;
772
+ }
773
+ allowedUsed++;
774
+ if (!toolName || typeof toolName !== 'string') {
775
+ getLogger().warn('Received malformed tool call (missing function.name)');
776
+ session.toolCallingAudit?.event({
777
+ timestamp: new Date().toISOString(),
778
+ phase,
779
+ round,
780
+ callId,
781
+ toolName: normalizedToolName,
782
+ rawArgsType: typeof rawArgs,
783
+ rawArgsPreview: typeof rawArgs === 'string' ? redactJsonString(rawArgs) : undefined,
784
+ parsedArgsOk: false,
785
+ parsedArgsError: 'Missing function.name',
786
+ toolResultStatus: 'error',
787
+ toolResultErrorCode: 'MALFORMED_TOOL_CALL',
788
+ });
789
+ toolResults.set(callId, {
790
+ id: callId,
791
+ toolName: normalizedToolName,
792
+ source: 'builtin',
793
+ status: 'error',
794
+ error: {
795
+ code: 'MALFORMED_TOOL_CALL',
796
+ message: 'Missing function.name',
797
+ retryable: true,
798
+ failurePhase: phase,
799
+ },
800
+ });
801
+ continue;
802
+ }
803
+ session.emit?.({
804
+ type: 'log',
805
+ level: 'debug',
806
+ message: `Tool call requested: ${toolName}`,
807
+ timestamp: new Date(),
808
+ });
809
+ if (!parsedArgsOk) {
810
+ const error = parsedArgsError ?? 'Invalid tool arguments';
811
+ session.toolCallingAudit?.event({
812
+ timestamp: new Date().toISOString(),
813
+ phase,
814
+ round,
815
+ callId,
816
+ toolName,
817
+ rawArgsType: typeof rawArgs,
818
+ rawArgsPreview: typeof rawArgs === 'string' ? redactJsonString(rawArgs) : undefined,
819
+ parsedArgsOk: false,
820
+ parsedArgsError: redactErrorMessage(error),
821
+ toolResultStatus: 'error',
822
+ toolResultErrorCode: 'INVALID_TOOL_ARGUMENTS_JSON',
823
+ });
824
+ toolResults.set(callId, {
825
+ id: callId,
826
+ toolName,
827
+ source: 'builtin',
828
+ status: 'error',
829
+ error: {
830
+ code: 'INVALID_TOOL_ARGUMENTS_JSON',
831
+ message: error,
832
+ retryable: true,
833
+ failurePhase: phase,
834
+ },
835
+ });
836
+ continue;
837
+ }
838
+ session.toolCallingAudit?.event({
839
+ timestamp: new Date().toISOString(),
840
+ phase,
841
+ round,
842
+ callId,
843
+ toolName,
844
+ toolIntent: spec?.intent,
845
+ rawArgsType: typeof rawArgs,
846
+ rawArgsPreview: typeof rawArgs === 'string' ? redactJsonString(rawArgs) : undefined,
847
+ parsedArgsOk: true,
848
+ parsedArgsPreview: safeStringifyForAudit(argsValue),
849
+ });
850
+ nodes.push({ id: callId, toolName, args: argsValue, deps: [] });
851
+ }
852
+ if (nodes.length > 0) {
853
+ const plan = {
854
+ id: `tool-round-${round}-${crypto.randomUUID()}`,
855
+ nodes,
856
+ policy: {
857
+ maxParallelism: Math.min(nodes.length, 8),
858
+ readParallelism: Math.min(nodes.length, 8),
859
+ writeParallelism: 1,
860
+ failFast: false,
861
+ deterministic: true,
862
+ },
863
+ };
864
+ const result = await runToolExecutionPlan({ session, phase, plan, signal });
865
+ for (const node of nodes) {
866
+ const r = result.nodeResults[node.id];
867
+ const toolResult = r?.toolResult;
868
+ if (toolResult) {
869
+ toolResults.set(node.id, toolResult);
870
+ continue;
871
+ }
872
+ toolResults.set(node.id, {
873
+ id: node.id,
874
+ toolName: node.toolName,
875
+ source: 'builtin',
876
+ status: 'error',
877
+ error: {
878
+ code: 'PPD_TOOL_RESULT_MISSING',
879
+ message: 'Parallel scheduler did not return tool result',
880
+ retryable: true,
881
+ failurePhase: phase,
882
+ },
883
+ });
884
+ }
885
+ }
886
+ for (const item of prepared) {
887
+ const { callId, toolName, rawArgs } = item;
888
+ const result = toolResults.get(callId);
889
+ if (!result)
890
+ continue;
891
+ // Strict output schema validation
892
+ if (typeof toolName === 'string') {
893
+ applyStrictToolOutputSchemaValidation({ session, phase, callId, toolName, result });
894
+ }
895
+ session.emit?.({
896
+ type: 'log',
897
+ level: result.status === 'ok' ? 'info' : 'warn',
898
+ message: `[tool] done ${toolName || 'unknown'} status=${result.status}`,
899
+ timestamp: new Date(),
900
+ });
901
+ session.emit?.({
902
+ type: 'tool.call.end',
903
+ callId,
904
+ toolName: typeof toolName === 'string' ? toolName : 'unknown',
905
+ phase,
906
+ round,
907
+ status: result.status,
908
+ durationMs: result.durationMs,
909
+ errorCode: result.error?.code,
910
+ outputSummary: session.eventPayload?.includeToolOutput &&
911
+ (typeof result.outputSummary === 'string' || typeof result.summary === 'string')
912
+ ? (result.outputSummary ?? result.summary)
913
+ : undefined,
914
+ timestamp: new Date(),
915
+ });
916
+ if (result.status !== 'ok' &&
917
+ result.error?.code === 'INTERRUPT_REQUIRED' &&
918
+ result.meta?.interrupt) {
919
+ const err = new Error(result.error.message || 'Interrupt required');
920
+ err.code = 'INTERRUPT_REQUIRED';
921
+ err.interrupt = result.meta.interrupt;
922
+ throw err;
923
+ }
924
+ if (result.status !== 'ok') {
925
+ const errorCode = result.error?.code;
926
+ const attachArgsPreview = errorCode === 'INVALID_INPUT';
927
+ session.toolCallingAudit?.event({
928
+ timestamp: new Date().toISOString(),
929
+ phase,
930
+ round,
931
+ callId,
932
+ toolName: typeof toolName === 'string' ? toolName : 'unknown',
933
+ rawArgsType: rawArgsTypeByCallId.get(callId) ?? typeof rawArgs,
934
+ parsedArgsOk: true,
935
+ toolResultStatus: result.status,
936
+ toolResultErrorCode: errorCode,
937
+ toolResultErrorMessage: attachArgsPreview && result.error?.message
938
+ ? redactErrorMessage(result.error.message)
939
+ : undefined,
940
+ rawArgsPreview: attachArgsPreview ? rawArgsPreviewByCallId.get(callId) : undefined,
941
+ parsedArgsPreview: attachArgsPreview ? toolArgsPreviewByCallId.get(callId) : undefined,
942
+ });
943
+ }
944
+ messages.push({
945
+ role: 'tool',
946
+ name: typeof toolName === 'string' ? toolName : 'unknown',
947
+ tool_call_id: callId,
948
+ content: formatToolResultForModel(result),
949
+ });
950
+ }
951
+ }
952
+ /**
953
+ * Streaming variant of {@link chatWithTools}. It consumes {@link LLM.chatStream} to assemble the
954
+ * assistant message (content + tool_calls), then executes tool calls the same way as the
955
+ * non-streaming loop.
956
+ *
957
+ * Notes:
958
+ * - Tool execution is still round-based: tools are executed only after the assistant turn completes.
959
+ * - Text deltas are only emitted when an LLM output policy enables them.
960
+ */
961
+ export async function chatWithToolsStreaming(initialMessages, chatOptions, session) {
962
+ if (!session.llm.chatStream) {
963
+ return chatWithTools(initialMessages, chatOptions, session);
964
+ }
965
+ const maxRounds = session.maxRounds ?? 6;
966
+ const phase = session.phase;
967
+ resetToolCallBudgetState(session);
968
+ const { allowedSpecs, openAITools } = resolveToolCallingForSession(session, phase);
969
+ emitToolCallingEnabledLogIfNeeded(session, openAITools);
970
+ const messages = [...initialMessages];
971
+ const canonicalEmitters = createCanonicalEmitterRegistry(session);
972
+ try {
973
+ for (let round = 0; round < maxRounds; round++) {
974
+ const roundStartedAt = Date.now();
975
+ const toolCalls = new ToolCallAccumulator();
976
+ const emittedModelToolCallIds = new Set();
977
+ const streamId = createStreamingStreamId({ session, phase, round });
978
+ const canonicalEmitter = canonicalEmitters.get(streamId);
979
+ let usedFallback = false;
980
+ let finishReason;
981
+ try {
982
+ let streamContent = '';
983
+ let finishUsage;
984
+ try {
985
+ const consumed = await consumeAssistantStreamTurn({
986
+ session,
987
+ messages,
988
+ chatOptions,
989
+ openAITools,
990
+ allowedSpecs,
991
+ phase,
992
+ round,
993
+ streamId,
994
+ canonicalEmitter,
995
+ emittedModelToolCallIds,
996
+ toolCalls,
997
+ });
998
+ streamContent = consumed.content;
999
+ finishReason = consumed.finishReason;
1000
+ finishUsage = consumed.finishUsage;
1001
+ }
1002
+ catch (e) {
1003
+ recordAuditEvent('llm.round', {
1004
+ status: 'error',
1005
+ streamed: true,
1006
+ usedFallback: false,
1007
+ phase,
1008
+ round,
1009
+ model: session.runtime.model,
1010
+ durationMs: Date.now() - roundStartedAt,
1011
+ provider: extractProvider(e),
1012
+ statusCode: extractStatusCode(e),
1013
+ networkCode: extractNetworkCode(e),
1014
+ errorName: e instanceof Error ? e.name : 'UnknownError',
1015
+ errorCode: typeof e?.llmCode === 'string'
1016
+ ? e.llmCode
1017
+ : typeof e?.code === 'string'
1018
+ ? e.code
1019
+ : undefined,
1020
+ }, { source: 'llm', severity: 'low', scope: 'session', phase });
1021
+ throw e;
1022
+ }
1023
+ if (finishUsage) {
1024
+ recordAuditEvent('llm.usage', {
1025
+ promptTokens: finishUsage.promptTokens,
1026
+ completionTokens: finishUsage.completionTokens,
1027
+ }, { source: 'llm', severity: 'low', scope: 'session', phase });
1028
+ }
1029
+ const drainedToolCalls = toolCalls.drain();
1030
+ const fallback = await applyEmptyStreamFallback({
1031
+ session,
1032
+ messages,
1033
+ chatOptions,
1034
+ openAITools,
1035
+ allowedSpecs,
1036
+ phase,
1037
+ round,
1038
+ content: streamContent,
1039
+ collectedToolCalls: drainedToolCalls,
1040
+ });
1041
+ usedFallback = fallback.usedFallback;
1042
+ const assistant = {
1043
+ role: 'assistant',
1044
+ content: fallback.content,
1045
+ tool_calls: fallback.toolCalls.length > 0 ? fallback.toolCalls : undefined,
1046
+ };
1047
+ if (session.emit && session.llmOutput) {
1048
+ emitSynthesizedFunctionCallClosures({
1049
+ emit: session.emit,
1050
+ llmOutput: session.llmOutput,
1051
+ canonicalEmitter,
1052
+ streamId,
1053
+ phase,
1054
+ round,
1055
+ collectedToolCalls: fallback.toolCalls,
1056
+ emittedModelToolCallIds,
1057
+ });
1058
+ }
1059
+ if (session.llmOutput) {
1060
+ emitLlmStreamEnd({
1061
+ emit: session.emit,
1062
+ policy: session.llmOutput.policy,
1063
+ kind: session.llmOutput.kind,
1064
+ step: session.llmOutput.step,
1065
+ streamId,
1066
+ finishReason,
1067
+ });
1068
+ }
1069
+ recordAuditEvent('llm.round', {
1070
+ status: 'ok',
1071
+ streamed: true,
1072
+ usedFallback,
1073
+ phase,
1074
+ round,
1075
+ model: session.runtime.model,
1076
+ durationMs: Date.now() - roundStartedAt,
1077
+ finishReason,
1078
+ contentChars: assistant.content.length,
1079
+ toolCallCount: fallback.toolCalls.length,
1080
+ }, { source: 'llm', severity: 'low', scope: 'session', phase });
1081
+ messages.push(assistant);
1082
+ const calls = assistant.tool_calls || [];
1083
+ if (!Array.isArray(calls) || calls.length === 0) {
1084
+ return assistant;
1085
+ }
1086
+ await executeToolCalls(session, phase, round, calls, messages, chatOptions.signal);
1087
+ }
1088
+ finally {
1089
+ canonicalEmitters.release(streamId);
1090
+ }
1091
+ }
1092
+ }
1093
+ finally {
1094
+ canonicalEmitters.clear();
1095
+ }
1096
+ session.emit?.({
1097
+ type: 'log',
1098
+ level: 'warn',
1099
+ message: `Tool calling exceeded maximum rounds (${maxRounds}); continuing without further tool execution`,
1100
+ timestamp: new Date(),
1101
+ });
1102
+ const lastAssistant = [...messages].reverse().find((m) => m.role === 'assistant');
1103
+ return lastAssistant || { role: 'assistant', content: '' };
1104
+ }
1105
+ //# sourceMappingURL=session.js.map