@cjhyy/code-shell-core 0.5.0-rc.0

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 (484) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/dist/agent/coordinator.d.ts +49 -0
  4. package/dist/agent/coordinator.js +77 -0
  5. package/dist/arena/arena.d.ts +43 -0
  6. package/dist/arena/arena.js +334 -0
  7. package/dist/arena/context/context-tools.d.ts +16 -0
  8. package/dist/arena/context/context-tools.js +231 -0
  9. package/dist/arena/detect-mode.d.ts +20 -0
  10. package/dist/arena/detect-mode.js +78 -0
  11. package/dist/arena/digest-builder.d.ts +25 -0
  12. package/dist/arena/digest-builder.js +120 -0
  13. package/dist/arena/index.d.ts +29 -0
  14. package/dist/arena/index.js +29 -0
  15. package/dist/arena/iterate/convergence.d.ts +25 -0
  16. package/dist/arena/iterate/convergence.js +103 -0
  17. package/dist/arena/iterate/formats/index.d.ts +22 -0
  18. package/dist/arena/iterate/formats/index.js +283 -0
  19. package/dist/arena/iterate/index.d.ts +11 -0
  20. package/dist/arena/iterate/index.js +9 -0
  21. package/dist/arena/iterate/iterative-arena.d.ts +23 -0
  22. package/dist/arena/iterate/iterative-arena.js +237 -0
  23. package/dist/arena/iterate/parse.d.ts +42 -0
  24. package/dist/arena/iterate/parse.js +123 -0
  25. package/dist/arena/iterate/phases/argue.d.ts +22 -0
  26. package/dist/arena/iterate/phases/argue.js +159 -0
  27. package/dist/arena/iterate/phases/revise.d.ts +16 -0
  28. package/dist/arena/iterate/phases/revise.js +62 -0
  29. package/dist/arena/iterate/phases/tournament.d.ts +34 -0
  30. package/dist/arena/iterate/phases/tournament.js +113 -0
  31. package/dist/arena/iterate/tools/web-tools.d.ts +13 -0
  32. package/dist/arena/iterate/tools/web-tools.js +54 -0
  33. package/dist/arena/iterate/types.d.ts +152 -0
  34. package/dist/arena/iterate/types.js +8 -0
  35. package/dist/arena/ledger.d.ts +47 -0
  36. package/dist/arena/ledger.js +151 -0
  37. package/dist/arena/lenses/architecture.d.ts +5 -0
  38. package/dist/arena/lenses/architecture.js +22 -0
  39. package/dist/arena/lenses/engineering.d.ts +5 -0
  40. package/dist/arena/lenses/engineering.js +22 -0
  41. package/dist/arena/lenses/general.d.ts +5 -0
  42. package/dist/arena/lenses/general.js +20 -0
  43. package/dist/arena/lenses/index.d.ts +16 -0
  44. package/dist/arena/lenses/index.js +47 -0
  45. package/dist/arena/lenses/product.d.ts +5 -0
  46. package/dist/arena/lenses/product.js +22 -0
  47. package/dist/arena/model-presets.d.ts +23 -0
  48. package/dist/arena/model-presets.js +44 -0
  49. package/dist/arena/phases/adjudication.d.ts +24 -0
  50. package/dist/arena/phases/adjudication.js +144 -0
  51. package/dist/arena/phases/build-consensus.d.ts +29 -0
  52. package/dist/arena/phases/build-consensus.js +86 -0
  53. package/dist/arena/phases/claim-registry.d.ts +26 -0
  54. package/dist/arena/phases/claim-registry.js +60 -0
  55. package/dist/arena/phases/cross-review.d.ts +45 -0
  56. package/dist/arena/phases/cross-review.js +224 -0
  57. package/dist/arena/phases/debate-rounds.d.ts +27 -0
  58. package/dist/arena/phases/debate-rounds.js +162 -0
  59. package/dist/arena/phases/participant-research.d.ts +38 -0
  60. package/dist/arena/phases/participant-research.js +317 -0
  61. package/dist/arena/phases/planning-detail-expansion.d.ts +38 -0
  62. package/dist/arena/phases/planning-detail-expansion.js +121 -0
  63. package/dist/arena/planner.d.ts +27 -0
  64. package/dist/arena/planner.js +312 -0
  65. package/dist/arena/providers/docs.d.ts +6 -0
  66. package/dist/arena/providers/docs.js +108 -0
  67. package/dist/arena/providers/git.d.ts +8 -0
  68. package/dist/arena/providers/git.js +174 -0
  69. package/dist/arena/providers/index.d.ts +32 -0
  70. package/dist/arena/providers/index.js +132 -0
  71. package/dist/arena/providers/none.d.ts +7 -0
  72. package/dist/arena/providers/none.js +11 -0
  73. package/dist/arena/providers/repo.d.ts +6 -0
  74. package/dist/arena/providers/repo.js +255 -0
  75. package/dist/arena/render/session.d.ts +17 -0
  76. package/dist/arena/render/session.js +190 -0
  77. package/dist/arena/render/terminal.d.ts +34 -0
  78. package/dist/arena/render/terminal.js +286 -0
  79. package/dist/arena/strategies/discussion.d.ts +25 -0
  80. package/dist/arena/strategies/discussion.js +143 -0
  81. package/dist/arena/strategies/index.d.ts +15 -0
  82. package/dist/arena/strategies/index.js +28 -0
  83. package/dist/arena/strategies/language-wrapper.d.ts +17 -0
  84. package/dist/arena/strategies/language-wrapper.js +102 -0
  85. package/dist/arena/strategies/lens-wrapper.d.ts +16 -0
  86. package/dist/arena/strategies/lens-wrapper.js +236 -0
  87. package/dist/arena/strategies/planning.d.ts +30 -0
  88. package/dist/arena/strategies/planning.js +225 -0
  89. package/dist/arena/strategies/review.d.ts +26 -0
  90. package/dist/arena/strategies/review.js +168 -0
  91. package/dist/arena/strategies/utils.d.ts +39 -0
  92. package/dist/arena/strategies/utils.js +647 -0
  93. package/dist/arena/tools/selector.d.ts +17 -0
  94. package/dist/arena/tools/selector.js +61 -0
  95. package/dist/arena/transitions.d.ts +48 -0
  96. package/dist/arena/transitions.js +92 -0
  97. package/dist/arena/types.d.ts +508 -0
  98. package/dist/arena/types.js +27 -0
  99. package/dist/cli/agent-server-stdio.d.ts +32 -0
  100. package/dist/cli/agent-server-stdio.js +116 -0
  101. package/dist/colorizer.d.ts +23 -0
  102. package/dist/colorizer.js +12 -0
  103. package/dist/context/compaction.d.ts +116 -0
  104. package/dist/context/compaction.js +534 -0
  105. package/dist/context/manager.d.ts +145 -0
  106. package/dist/context/manager.js +423 -0
  107. package/dist/context/token-counter.d.ts +27 -0
  108. package/dist/context/token-counter.js +91 -0
  109. package/dist/context/tool-result-storage.d.ts +96 -0
  110. package/dist/context/tool-result-storage.js +296 -0
  111. package/dist/cost-tracker.d.ts +64 -0
  112. package/dist/cost-tracker.js +314 -0
  113. package/dist/cron/scheduler.d.ts +34 -0
  114. package/dist/cron/scheduler.js +115 -0
  115. package/dist/data/deepseek-models.json +46 -0
  116. package/dist/data/gemini-models.json +67 -0
  117. package/dist/data/openai-models.json +175 -0
  118. package/dist/data/openrouter-models.d.ts +35 -0
  119. package/dist/data/openrouter-models.js +36 -0
  120. package/dist/data/openrouter-models.json +4626 -0
  121. package/dist/data/openrouter-sync.d.ts +16 -0
  122. package/dist/data/openrouter-sync.js +53 -0
  123. package/dist/data/static-catalogs.d.ts +25 -0
  124. package/dist/data/static-catalogs.js +26 -0
  125. package/dist/data/zai-models.json +56 -0
  126. package/dist/engine/cost-store.d.ts +22 -0
  127. package/dist/engine/cost-store.js +15 -0
  128. package/dist/engine/engine.d.ts +295 -0
  129. package/dist/engine/engine.js +1467 -0
  130. package/dist/engine/model-facade.d.ts +24 -0
  131. package/dist/engine/model-facade.js +194 -0
  132. package/dist/engine/parse-task.d.ts +59 -0
  133. package/dist/engine/parse-task.js +135 -0
  134. package/dist/engine/patch-orphaned-tools.d.ts +52 -0
  135. package/dist/engine/patch-orphaned-tools.js +95 -0
  136. package/dist/engine/query.d.ts +50 -0
  137. package/dist/engine/query.js +107 -0
  138. package/dist/engine/runtime.d.ts +50 -0
  139. package/dist/engine/runtime.js +59 -0
  140. package/dist/engine/streaming-tool-queue.d.ts +34 -0
  141. package/dist/engine/streaming-tool-queue.js +61 -0
  142. package/dist/engine/token-budget.d.ts +22 -0
  143. package/dist/engine/token-budget.js +40 -0
  144. package/dist/engine/tool-summary.d.ts +13 -0
  145. package/dist/engine/tool-summary.js +35 -0
  146. package/dist/engine/turn-loop.d.ts +122 -0
  147. package/dist/engine/turn-loop.js +602 -0
  148. package/dist/engine/turn-state.d.ts +20 -0
  149. package/dist/engine/turn-state.js +17 -0
  150. package/dist/exceptions.d.ts +61 -0
  151. package/dist/exceptions.js +116 -0
  152. package/dist/git/utils.d.ts +35 -0
  153. package/dist/git/utils.js +98 -0
  154. package/dist/git/worktree.d.ts +36 -0
  155. package/dist/git/worktree.js +139 -0
  156. package/dist/hooks/events.d.ts +114 -0
  157. package/dist/hooks/events.js +4 -0
  158. package/dist/hooks/inject.d.ts +19 -0
  159. package/dist/hooks/inject.js +29 -0
  160. package/dist/hooks/registry.d.ts +15 -0
  161. package/dist/hooks/registry.js +84 -0
  162. package/dist/hooks/shell-runner.d.ts +45 -0
  163. package/dist/hooks/shell-runner.js +173 -0
  164. package/dist/index.d.ts +117 -0
  165. package/dist/index.js +145 -0
  166. package/dist/llm/api-key-sanitize.d.ts +22 -0
  167. package/dist/llm/api-key-sanitize.js +81 -0
  168. package/dist/llm/capabilities/index.d.ts +20 -0
  169. package/dist/llm/capabilities/index.js +39 -0
  170. package/dist/llm/capabilities/rules.d.ts +26 -0
  171. package/dist/llm/capabilities/rules.js +215 -0
  172. package/dist/llm/capabilities/types.d.ts +114 -0
  173. package/dist/llm/capabilities/types.js +24 -0
  174. package/dist/llm/client-base.d.ts +29 -0
  175. package/dist/llm/client-base.js +137 -0
  176. package/dist/llm/client-factory.d.ts +10 -0
  177. package/dist/llm/client-factory.js +29 -0
  178. package/dist/llm/model-cache.d.ts +21 -0
  179. package/dist/llm/model-cache.js +46 -0
  180. package/dist/llm/model-fetcher.d.ts +29 -0
  181. package/dist/llm/model-fetcher.js +282 -0
  182. package/dist/llm/model-pool.d.ts +95 -0
  183. package/dist/llm/model-pool.js +246 -0
  184. package/dist/llm/provider-catalog.d.ts +32 -0
  185. package/dist/llm/provider-catalog.js +50 -0
  186. package/dist/llm/provider-kinds.d.ts +21 -0
  187. package/dist/llm/provider-kinds.js +132 -0
  188. package/dist/llm/providers/anthropic.d.ts +19 -0
  189. package/dist/llm/providers/anthropic.js +267 -0
  190. package/dist/llm/providers/openai.d.ts +64 -0
  191. package/dist/llm/providers/openai.js +606 -0
  192. package/dist/llm/retry.d.ts +12 -0
  193. package/dist/llm/retry.js +17 -0
  194. package/dist/llm/stream-watchdog.d.ts +42 -0
  195. package/dist/llm/stream-watchdog.js +70 -0
  196. package/dist/llm/token-counter.d.ts +17 -0
  197. package/dist/llm/token-counter.js +36 -0
  198. package/dist/llm/types.d.ts +37 -0
  199. package/dist/llm/types.js +4 -0
  200. package/dist/logging/logger.d.ts +109 -0
  201. package/dist/logging/logger.js +390 -0
  202. package/dist/logging/sanitize-messages.d.ts +42 -0
  203. package/dist/logging/sanitize-messages.js +150 -0
  204. package/dist/logging/session-recorder.d.ts +90 -0
  205. package/dist/logging/session-recorder.js +304 -0
  206. package/dist/lsp/client.d.ts +41 -0
  207. package/dist/lsp/client.js +166 -0
  208. package/dist/lsp/manager.d.ts +40 -0
  209. package/dist/lsp/manager.js +124 -0
  210. package/dist/lsp/servers.d.ts +16 -0
  211. package/dist/lsp/servers.js +60 -0
  212. package/dist/migrate-models.d.ts +45 -0
  213. package/dist/migrate-models.js +179 -0
  214. package/dist/onboarding.d.ts +189 -0
  215. package/dist/onboarding.js +625 -0
  216. package/dist/plugins/gitOps.d.ts +32 -0
  217. package/dist/plugins/gitOps.js +62 -0
  218. package/dist/plugins/installedPlugins.d.ts +15 -0
  219. package/dist/plugins/installedPlugins.js +56 -0
  220. package/dist/plugins/knownMarketplaces.d.ts +10 -0
  221. package/dist/plugins/knownMarketplaces.js +46 -0
  222. package/dist/plugins/loadPluginHooks.d.ts +52 -0
  223. package/dist/plugins/loadPluginHooks.js +162 -0
  224. package/dist/plugins/marketplaceManager.d.ts +49 -0
  225. package/dist/plugins/marketplaceManager.js +142 -0
  226. package/dist/plugins/parseMarketplaceInput.d.ts +16 -0
  227. package/dist/plugins/parseMarketplaceInput.js +73 -0
  228. package/dist/plugins/pluginCommandHook.d.ts +54 -0
  229. package/dist/plugins/pluginCommandHook.js +180 -0
  230. package/dist/plugins/pluginCommandsLoader.d.ts +25 -0
  231. package/dist/plugins/pluginCommandsLoader.js +114 -0
  232. package/dist/plugins/pluginInstaller.d.ts +36 -0
  233. package/dist/plugins/pluginInstaller.js +228 -0
  234. package/dist/plugins/schemas.d.ts +9 -0
  235. package/dist/plugins/schemas.js +136 -0
  236. package/dist/plugins/types.d.ts +75 -0
  237. package/dist/plugins/types.js +7 -0
  238. package/dist/plugins/varRewrite.d.ts +42 -0
  239. package/dist/plugins/varRewrite.js +129 -0
  240. package/dist/preset/index.d.ts +60 -0
  241. package/dist/preset/index.js +159 -0
  242. package/dist/product/define.d.ts +60 -0
  243. package/dist/product/define.js +123 -0
  244. package/dist/product/index.d.ts +5 -0
  245. package/dist/product/index.js +4 -0
  246. package/dist/product/types.d.ts +111 -0
  247. package/dist/product/types.js +21 -0
  248. package/dist/prompt/composer.d.ts +49 -0
  249. package/dist/prompt/composer.js +168 -0
  250. package/dist/prompt/instruction-scanner.d.ts +54 -0
  251. package/dist/prompt/instruction-scanner.js +187 -0
  252. package/dist/prompt/section-cache.d.ts +15 -0
  253. package/dist/prompt/section-cache.js +31 -0
  254. package/dist/prompt/section-loader.d.ts +26 -0
  255. package/dist/prompt/section-loader.js +53 -0
  256. package/dist/prompt/sections/base.md +35 -0
  257. package/dist/prompt/sections/coding.md +31 -0
  258. package/dist/prompt/sections/orchestration.md +16 -0
  259. package/dist/prompt/sections/tone.md +9 -0
  260. package/dist/protocol/chat-session-manager.d.ts +33 -0
  261. package/dist/protocol/chat-session-manager.js +65 -0
  262. package/dist/protocol/chat-session.d.ts +49 -0
  263. package/dist/protocol/chat-session.js +86 -0
  264. package/dist/protocol/client.d.ts +104 -0
  265. package/dist/protocol/client.js +260 -0
  266. package/dist/protocol/factories.d.ts +93 -0
  267. package/dist/protocol/factories.js +64 -0
  268. package/dist/protocol/helpers.d.ts +42 -0
  269. package/dist/protocol/helpers.js +55 -0
  270. package/dist/protocol/index.d.ts +4 -0
  271. package/dist/protocol/index.js +4 -0
  272. package/dist/protocol/server.d.ts +80 -0
  273. package/dist/protocol/server.js +855 -0
  274. package/dist/protocol/transport.d.ts +37 -0
  275. package/dist/protocol/transport.js +85 -0
  276. package/dist/protocol/types.d.ts +221 -0
  277. package/dist/protocol/types.js +61 -0
  278. package/dist/remote/bridge.d.ts +37 -0
  279. package/dist/remote/bridge.js +103 -0
  280. package/dist/run/ArtifactTracker.d.ts +38 -0
  281. package/dist/run/ArtifactTracker.js +132 -0
  282. package/dist/run/CheckpointWriter.d.ts +53 -0
  283. package/dist/run/CheckpointWriter.js +135 -0
  284. package/dist/run/EngineRunner.d.ts +72 -0
  285. package/dist/run/EngineRunner.js +119 -0
  286. package/dist/run/Evaluator.d.ts +56 -0
  287. package/dist/run/Evaluator.js +62 -0
  288. package/dist/run/FileRunStore.d.ts +39 -0
  289. package/dist/run/FileRunStore.js +176 -0
  290. package/dist/run/Heartbeat.d.ts +54 -0
  291. package/dist/run/Heartbeat.js +123 -0
  292. package/dist/run/RunApprovalBackend.d.ts +53 -0
  293. package/dist/run/RunApprovalBackend.js +85 -0
  294. package/dist/run/RunLock.d.ts +40 -0
  295. package/dist/run/RunLock.js +98 -0
  296. package/dist/run/RunManager.d.ts +82 -0
  297. package/dist/run/RunManager.js +587 -0
  298. package/dist/run/RunQueue.d.ts +27 -0
  299. package/dist/run/RunQueue.js +73 -0
  300. package/dist/run/RunStore.d.ts +25 -0
  301. package/dist/run/RunStore.js +9 -0
  302. package/dist/run/factory.d.ts +63 -0
  303. package/dist/run/factory.js +51 -0
  304. package/dist/run/index.d.ts +17 -0
  305. package/dist/run/index.js +23 -0
  306. package/dist/run/types.d.ts +136 -0
  307. package/dist/run/types.js +28 -0
  308. package/dist/runtime/safe-spawn.d.ts +118 -0
  309. package/dist/runtime/safe-spawn.js +268 -0
  310. package/dist/services/analytics.d.ts +29 -0
  311. package/dist/services/analytics.js +84 -0
  312. package/dist/services/auto-dream.d.ts +49 -0
  313. package/dist/services/auto-dream.js +124 -0
  314. package/dist/services/diagnostics.d.ts +41 -0
  315. package/dist/services/diagnostics.js +99 -0
  316. package/dist/services/extract-memories.d.ts +31 -0
  317. package/dist/services/extract-memories.js +71 -0
  318. package/dist/services/index.d.ts +11 -0
  319. package/dist/services/index.js +17 -0
  320. package/dist/services/memory-orchestrator.d.ts +60 -0
  321. package/dist/services/memory-orchestrator.js +133 -0
  322. package/dist/services/notifier.d.ts +25 -0
  323. package/dist/services/notifier.js +69 -0
  324. package/dist/services/oauth.d.ts +27 -0
  325. package/dist/services/oauth.js +153 -0
  326. package/dist/services/session-memory.d.ts +37 -0
  327. package/dist/services/session-memory.js +81 -0
  328. package/dist/session/file-history.d.ts +41 -0
  329. package/dist/session/file-history.js +115 -0
  330. package/dist/session/memory.d.ts +104 -0
  331. package/dist/session/memory.js +310 -0
  332. package/dist/session/session-manager.d.ts +41 -0
  333. package/dist/session/session-manager.js +261 -0
  334. package/dist/session/transcript.d.ts +39 -0
  335. package/dist/session/transcript.js +186 -0
  336. package/dist/settings/manager.d.ts +36 -0
  337. package/dist/settings/manager.js +187 -0
  338. package/dist/settings/schema.d.ts +1219 -0
  339. package/dist/settings/schema.js +246 -0
  340. package/dist/skills/frontmatter.d.ts +13 -0
  341. package/dist/skills/frontmatter.js +70 -0
  342. package/dist/skills/index.d.ts +7 -0
  343. package/dist/skills/index.js +6 -0
  344. package/dist/skills/scanner.d.ts +31 -0
  345. package/dist/skills/scanner.js +171 -0
  346. package/dist/state.d.ts +160 -0
  347. package/dist/state.js +267 -0
  348. package/dist/tool-system/builtin/agent-notifications.d.ts +84 -0
  349. package/dist/tool-system/builtin/agent-notifications.js +197 -0
  350. package/dist/tool-system/builtin/agent-registry.d.ts +67 -0
  351. package/dist/tool-system/builtin/agent-registry.js +126 -0
  352. package/dist/tool-system/builtin/agent-transcript-translator.d.ts +22 -0
  353. package/dist/tool-system/builtin/agent-transcript-translator.js +160 -0
  354. package/dist/tool-system/builtin/agent.d.ts +16 -0
  355. package/dist/tool-system/builtin/agent.js +364 -0
  356. package/dist/tool-system/builtin/apply-patch/applier.d.ts +26 -0
  357. package/dist/tool-system/builtin/apply-patch/applier.js +250 -0
  358. package/dist/tool-system/builtin/apply-patch/index.d.ts +20 -0
  359. package/dist/tool-system/builtin/apply-patch/index.js +101 -0
  360. package/dist/tool-system/builtin/apply-patch/parser.d.ts +17 -0
  361. package/dist/tool-system/builtin/apply-patch/parser.js +212 -0
  362. package/dist/tool-system/builtin/apply-patch/seek-sequence.d.ts +18 -0
  363. package/dist/tool-system/builtin/apply-patch/seek-sequence.js +123 -0
  364. package/dist/tool-system/builtin/apply-patch/types.d.ts +49 -0
  365. package/dist/tool-system/builtin/apply-patch/types.js +13 -0
  366. package/dist/tool-system/builtin/arena.d.ts +31 -0
  367. package/dist/tool-system/builtin/arena.js +416 -0
  368. package/dist/tool-system/builtin/ask-user.d.ts +20 -0
  369. package/dist/tool-system/builtin/ask-user.js +99 -0
  370. package/dist/tool-system/builtin/bash.d.ts +21 -0
  371. package/dist/tool-system/builtin/bash.js +149 -0
  372. package/dist/tool-system/builtin/brief.d.ts +6 -0
  373. package/dist/tool-system/builtin/brief.js +41 -0
  374. package/dist/tool-system/builtin/config.d.ts +6 -0
  375. package/dist/tool-system/builtin/config.js +67 -0
  376. package/dist/tool-system/builtin/cron.d.ts +10 -0
  377. package/dist/tool-system/builtin/cron.js +61 -0
  378. package/dist/tool-system/builtin/edit.d.ts +6 -0
  379. package/dist/tool-system/builtin/edit.js +100 -0
  380. package/dist/tool-system/builtin/file-cache.d.ts +11 -0
  381. package/dist/tool-system/builtin/file-cache.js +39 -0
  382. package/dist/tool-system/builtin/glob.d.ts +7 -0
  383. package/dist/tool-system/builtin/glob.js +76 -0
  384. package/dist/tool-system/builtin/grep.d.ts +7 -0
  385. package/dist/tool-system/builtin/grep.js +150 -0
  386. package/dist/tool-system/builtin/index.d.ts +20 -0
  387. package/dist/tool-system/builtin/index.js +449 -0
  388. package/dist/tool-system/builtin/lsp.d.ts +6 -0
  389. package/dist/tool-system/builtin/lsp.js +141 -0
  390. package/dist/tool-system/builtin/mcp-tools.d.ts +10 -0
  391. package/dist/tool-system/builtin/mcp-tools.js +102 -0
  392. package/dist/tool-system/builtin/memory.d.ts +26 -0
  393. package/dist/tool-system/builtin/memory.js +226 -0
  394. package/dist/tool-system/builtin/notebook-edit.d.ts +6 -0
  395. package/dist/tool-system/builtin/notebook-edit.js +124 -0
  396. package/dist/tool-system/builtin/plan.d.ts +15 -0
  397. package/dist/tool-system/builtin/plan.js +55 -0
  398. package/dist/tool-system/builtin/powershell.d.ts +11 -0
  399. package/dist/tool-system/builtin/powershell.js +64 -0
  400. package/dist/tool-system/builtin/read.d.ts +6 -0
  401. package/dist/tool-system/builtin/read.js +70 -0
  402. package/dist/tool-system/builtin/remote-trigger.d.ts +6 -0
  403. package/dist/tool-system/builtin/remote-trigger.js +54 -0
  404. package/dist/tool-system/builtin/repl.d.ts +11 -0
  405. package/dist/tool-system/builtin/repl.js +82 -0
  406. package/dist/tool-system/builtin/send-message.d.ts +6 -0
  407. package/dist/tool-system/builtin/send-message.js +47 -0
  408. package/dist/tool-system/builtin/skill-prompt.d.ts +20 -0
  409. package/dist/tool-system/builtin/skill-prompt.js +52 -0
  410. package/dist/tool-system/builtin/skill.d.ts +9 -0
  411. package/dist/tool-system/builtin/skill.js +55 -0
  412. package/dist/tool-system/builtin/sleep.d.ts +6 -0
  413. package/dist/tool-system/builtin/sleep.js +33 -0
  414. package/dist/tool-system/builtin/task.d.ts +48 -0
  415. package/dist/tool-system/builtin/task.js +139 -0
  416. package/dist/tool-system/builtin/tool-search.d.ts +11 -0
  417. package/dist/tool-system/builtin/tool-search.js +90 -0
  418. package/dist/tool-system/builtin/web-fetch.d.ts +10 -0
  419. package/dist/tool-system/builtin/web-fetch.js +328 -0
  420. package/dist/tool-system/builtin/web-search.d.ts +25 -0
  421. package/dist/tool-system/builtin/web-search.js +174 -0
  422. package/dist/tool-system/builtin/worktree.d.ts +10 -0
  423. package/dist/tool-system/builtin/worktree.js +95 -0
  424. package/dist/tool-system/builtin/write.d.ts +6 -0
  425. package/dist/tool-system/builtin/write.js +36 -0
  426. package/dist/tool-system/context.d.ts +165 -0
  427. package/dist/tool-system/context.js +18 -0
  428. package/dist/tool-system/executor.d.ts +49 -0
  429. package/dist/tool-system/executor.js +495 -0
  430. package/dist/tool-system/investigation-guard.d.ts +46 -0
  431. package/dist/tool-system/investigation-guard.js +155 -0
  432. package/dist/tool-system/mcp-manager.d.ts +67 -0
  433. package/dist/tool-system/mcp-manager.js +249 -0
  434. package/dist/tool-system/permission.d.ts +111 -0
  435. package/dist/tool-system/permission.js +774 -0
  436. package/dist/tool-system/registry.d.ts +32 -0
  437. package/dist/tool-system/registry.js +131 -0
  438. package/dist/tool-system/sandbox/bwrap.d.ts +13 -0
  439. package/dist/tool-system/sandbox/bwrap.js +63 -0
  440. package/dist/tool-system/sandbox/index.d.ts +78 -0
  441. package/dist/tool-system/sandbox/index.js +222 -0
  442. package/dist/tool-system/sandbox/off.d.ts +2 -0
  443. package/dist/tool-system/sandbox/off.js +8 -0
  444. package/dist/tool-system/sandbox/seatbelt.d.ts +18 -0
  445. package/dist/tool-system/sandbox/seatbelt.js +109 -0
  446. package/dist/tool-system/task-guard.d.ts +29 -0
  447. package/dist/tool-system/task-guard.js +70 -0
  448. package/dist/tool-system/validation.d.ts +5 -0
  449. package/dist/tool-system/validation.js +39 -0
  450. package/dist/types.d.ts +376 -0
  451. package/dist/types.js +4 -0
  452. package/dist/updater.d.ts +65 -0
  453. package/dist/updater.js +405 -0
  454. package/dist/utils/debug.d.ts +41 -0
  455. package/dist/utils/debug.js +110 -0
  456. package/dist/utils/earlyInput.d.ts +43 -0
  457. package/dist/utils/earlyInput.js +166 -0
  458. package/dist/utils/env.d.ts +24 -0
  459. package/dist/utils/env.js +104 -0
  460. package/dist/utils/envUtils.d.ts +51 -0
  461. package/dist/utils/envUtils.js +135 -0
  462. package/dist/utils/execFileNoThrow.d.ts +39 -0
  463. package/dist/utils/execFileNoThrow.js +74 -0
  464. package/dist/utils/format.d.ts +44 -0
  465. package/dist/utils/format.js +236 -0
  466. package/dist/utils/intl.d.ts +22 -0
  467. package/dist/utils/intl.js +83 -0
  468. package/dist/utils/lockfile.d.ts +15 -0
  469. package/dist/utils/lockfile.js +30 -0
  470. package/dist/utils/memoize.d.ts +19 -0
  471. package/dist/utils/memoize.js +14 -0
  472. package/dist/utils/semver.d.ts +6 -0
  473. package/dist/utils/semver.js +13 -0
  474. package/dist/utils/sliceAnsi.d.ts +12 -0
  475. package/dist/utils/sliceAnsi.js +62 -0
  476. package/dist/utils/systemTheme.d.ts +40 -0
  477. package/dist/utils/systemTheme.js +108 -0
  478. package/dist/utils/task-sanitizer.d.ts +6 -0
  479. package/dist/utils/task-sanitizer.js +24 -0
  480. package/dist/utils/theme.d.ts +93 -0
  481. package/dist/utils/theme.js +544 -0
  482. package/dist/utils/toolDisplay.d.ts +31 -0
  483. package/dist/utils/toolDisplay.js +188 -0
  484. package/package.json +72 -0
@@ -0,0 +1,602 @@
1
+ /**
2
+ * Turn loop — state machine implementation.
3
+ *
4
+ * Following Claude Code's po_() pattern:
5
+ * pre_check → model_call → post_check → tool_exec → context_mgmt → hook_notify → next turn
6
+ */
7
+ import { initialTurnState, newTurnId } from "./turn-state.js";
8
+ import { wrapHookMessages } from "../hooks/inject.js";
9
+ import { ContextLimitError } from "../exceptions.js";
10
+ import { logger } from "../logging/logger.js";
11
+ import { checkTokenBudget, createBudgetTracker } from "./token-budget.js";
12
+ import { StreamingToolQueue } from "./streaming-tool-queue.js";
13
+ import { estimateTokens } from "../context/compaction.js";
14
+ export class TurnLoop {
15
+ deps;
16
+ config;
17
+ turnCount = 0;
18
+ /** Tool IDs already emitted as tool_use_start during streaming (to avoid duplicates). */
19
+ streamedToolIds = new Set();
20
+ /**
21
+ * Turn-scoped child logger set at the top of each loop iteration. Class
22
+ * methods invoked inside the iteration (callModelWithFallback,
23
+ * executeToolCall, ...) read it instead of using the root `logger`, so
24
+ * every line they write is tagged with the current turn/turnId.
25
+ */
26
+ currentTurnLog = logger;
27
+ /** Last emitted ctx token estimate; used to skip no-op usage_update events. */
28
+ lastCtxEmit = -1;
29
+ constructor(deps, config) {
30
+ this.deps = deps;
31
+ this.config = config;
32
+ // Wrap onStream so a single throwing handler can't silently break
33
+ // the channel for the rest of the run. A 2026-05-25 incident saw a
34
+ // sub-agent's events stop reaching the renderer ~23s into its run —
35
+ // the engine kept executing tools, but every subsequent stream emit
36
+ // was happening into a dead pipe. With this guard the failure shows
37
+ // up in ~/.code-shell/logs/engine-*.log with the offending event
38
+ // type, instead of presenting as a frozen UI.
39
+ //
40
+ // We replace `config.onStream` so the wrap covers BOTH direct calls
41
+ // (`this.config.onStream?.({...})` inside this class) AND places
42
+ // where the reference is passed onward (ModelFacade, StreamingToolQueue).
43
+ if (this.config.onStream) {
44
+ const inner = this.config.onStream;
45
+ this.config = {
46
+ ...this.config,
47
+ onStream: (event) => {
48
+ try {
49
+ inner(event);
50
+ }
51
+ catch (err) {
52
+ // Use root logger, not currentTurnLog — this can fire from
53
+ // outside a turn iteration (e.g. ModelFacade after run() resolves).
54
+ logger.warn("stream.handler_threw", {
55
+ eventType: event.type,
56
+ error: err.message,
57
+ stack: err.stack?.split("\n").slice(0, 4).join("\n"),
58
+ });
59
+ }
60
+ },
61
+ };
62
+ }
63
+ }
64
+ /**
65
+ * Emit a lifecycle hook with isSubAgent + sessionId auto-merged into data.
66
+ * Returns the aggregated HookResult so callers can consume `messages` /
67
+ * `decision` / `stop`. Use this instead of `deps.hooks.emit` directly so
68
+ * every emit carries the same context envelope.
69
+ */
70
+ async emitHook(event, data = {}) {
71
+ return this.deps.hooks.emit(event, {
72
+ ...data,
73
+ isSubAgent: this.deps.isSubAgent === true,
74
+ sessionId: this.deps.sessionId,
75
+ });
76
+ }
77
+ /**
78
+ * Emit a usage_update so the UI ctx bar reflects current message-array
79
+ * size. Called at every point where messages mutate: after a tool_result
80
+ * is appended, after context management (which may shrink), and after an
81
+ * LLM response (which we also feed through with the provider's authoritative
82
+ * promptTokens to override our estimate).
83
+ */
84
+ emitCtxFromMessages(messages) {
85
+ if (!this.config.onStream)
86
+ return;
87
+ // Messages-only estimate is missing system prompt + tool defs (typically
88
+ // ~16k tokens). Pull the cached overhead derived from the previous LLM
89
+ // response to keep the ctx bar in the same ballpark as the real prompt.
90
+ // Without this, the bar drops to ~1k every time tool results land.
91
+ const msgsEstimate = estimateTokens(messages);
92
+ const overhead = this.deps.ctxOverheadStore.get(this.deps.sessionId);
93
+ const ctx = msgsEstimate + overhead;
94
+ this.currentTurnLog.info("debug.ctx.emit", {
95
+ src: "messages",
96
+ value: ctx,
97
+ msgsEstimate,
98
+ overhead,
99
+ msgCount: messages.length,
100
+ prev: this.lastCtxEmit,
101
+ });
102
+ if (ctx === this.lastCtxEmit)
103
+ return;
104
+ this.lastCtxEmit = ctx;
105
+ this.config.onStream({ type: "usage_update", promptTokens: ctx });
106
+ }
107
+ emitCtxFromUsage(promptTokens, messages) {
108
+ if (!this.config.onStream)
109
+ return;
110
+ // Reverse-derive overhead from this authoritative reading so the next
111
+ // estimate-based emit (post-tool-result) is calibrated.
112
+ const msgsEstimate = estimateTokens(messages);
113
+ const overhead = Math.max(0, promptTokens - msgsEstimate);
114
+ this.deps.ctxOverheadStore.set(this.deps.sessionId, overhead);
115
+ this.currentTurnLog.info("debug.ctx.emit", {
116
+ src: "usage",
117
+ value: promptTokens,
118
+ msgsEstimate,
119
+ derivedOverhead: overhead,
120
+ prev: this.lastCtxEmit,
121
+ });
122
+ if (promptTokens === this.lastCtxEmit)
123
+ return;
124
+ this.lastCtxEmit = promptTokens;
125
+ this.config.onStream({ type: "usage_update", promptTokens });
126
+ }
127
+ /**
128
+ * Run the multi-turn agent loop until completion.
129
+ */
130
+ async run(initialMessages) {
131
+ let messages = [...initialMessages];
132
+ let finalText = "";
133
+ const budgetTracker = createBudgetTracker();
134
+ while (this.turnCount < this.config.maxTurns) {
135
+ this.turnCount++;
136
+ const state = initialTurnState(this.turnCount);
137
+ // Per-turn correlation ID. Every log written through `tlog` (or any
138
+ // child derived from it) is stamped with `turn` + `turnId`, so
139
+ // `jq 'select(.turnId == "...")'` reconstructs one turn's timeline.
140
+ // Span is *not* used for the loop itself because there are 6+ early
141
+ // returns; instead, each return-causing branch logs its own terminal
142
+ // event (model_error, completed, etc.).
143
+ const turnId = newTurnId();
144
+ const tlog = logger.child({ turn: this.turnCount, turnId });
145
+ this.currentTurnLog = tlog;
146
+ const turnStartedAt = Date.now();
147
+ tlog.info("turn.start", { cat: "turn", messageCount: messages.length });
148
+ // Tag downstream tool-exec / permission lines with this turn's IDs.
149
+ this.deps.toolExecutor.setLogger(tlog);
150
+ this.config.onStream?.({ type: "stream_request_start", turnNumber: this.turnCount });
151
+ const turnStartHook = await this.emitHook("on_turn_start", {
152
+ turnNumber: this.turnCount,
153
+ });
154
+ const turnStartInjection = wrapHookMessages(turnStartHook.messages);
155
+ if (turnStartInjection) {
156
+ messages.push(turnStartInjection);
157
+ }
158
+ // Approaching max turns: inject a warning so the model can wrap up
159
+ const turnsRemaining = this.config.maxTurns - this.turnCount;
160
+ if (turnsRemaining === 2) {
161
+ messages.push({
162
+ role: "user",
163
+ content: "<system-reminder>Warning: you have only 2 turns remaining before the turn limit is reached. " +
164
+ "Start wrapping up your work and prepare a summary of what you've accomplished and what remains to be done.</system-reminder>",
165
+ });
166
+ }
167
+ else if (turnsRemaining === 0) {
168
+ messages.push({
169
+ role: "user",
170
+ content: "<system-reminder>This is your LAST turn. You MUST respond with a final text summary now. " +
171
+ "Do NOT call any tools. Summarize what you have accomplished and list any remaining work.</system-reminder>",
172
+ });
173
+ }
174
+ // Pre-check: context management (async — may trigger LLM summarization)
175
+ messages = await this.deps.contextManager.manageAsync(messages);
176
+ // No pre-llm ctx emit here: the messages-only estimate would be ~16k
177
+ // smaller than the real prompt (system + tools not included), making
178
+ // the bar visibly drop on every submit. Post-llm/post-tool-result
179
+ // events carry an accurate value; if compaction shrank the array, the
180
+ // dedicated context_compact event has already informed the UI.
181
+ // post_compact hook: ContextManager just finished a manage() pass.
182
+ // If any non-micro tier fired, give handlers a chance to inject a
183
+ // <system-reminder> ("context was compacted — recall earlier
184
+ // decisions from the transcript") into THIS turn before the model
185
+ // call. Microcompact is lossless (just clearing redundant
186
+ // tool_results) so we suppress hook emits for it to keep token
187
+ // overhead down.
188
+ const pending = this.deps.consumePendingCompactInfo?.();
189
+ if (pending && pending.strategy !== "micro") {
190
+ const compactHook = await this.emitHook("post_compact", {
191
+ strategy: pending.strategy,
192
+ beforeTokens: pending.before,
193
+ afterTokens: pending.after,
194
+ });
195
+ const compactInjection = wrapHookMessages(compactHook.messages);
196
+ if (compactInjection) {
197
+ messages.push(compactInjection);
198
+ }
199
+ }
200
+ // Model call (with streaming fallback and max_output_tokens continuation)
201
+ // Track tool IDs streamed during this turn to avoid duplicate UI events
202
+ this.streamedToolIds.clear();
203
+ // Streaming tool queue: start concurrency-safe tools during streaming
204
+ const streamingQueue = new StreamingToolQueue(this.deps.toolExecutor);
205
+ let response;
206
+ try {
207
+ response = await this.callModelWithFallback(messages);
208
+ }
209
+ catch (err) {
210
+ if (err instanceof ContextLimitError) {
211
+ // Progressive recovery: drop oldest API rounds, up to 3 retries
212
+ const { dropOldestRounds } = await import("../context/compaction.js");
213
+ let recovered = false;
214
+ for (let retry = 1; retry <= 3; retry++) {
215
+ tlog.warn("turn.ptl_recovery", { cat: "turn", retry, roundsToDrop: retry });
216
+ messages = dropOldestRounds(messages, retry);
217
+ try {
218
+ response = await this.callModelWithFallback(messages);
219
+ recovered = true;
220
+ break;
221
+ }
222
+ catch (retryErr) {
223
+ if (!(retryErr instanceof ContextLimitError)) {
224
+ this.config.onStream?.({ type: "error", error: retryErr.message });
225
+ return { text: finalText, reason: "model_error", messages };
226
+ }
227
+ }
228
+ }
229
+ if (!recovered) {
230
+ this.patchOrphanedToolUses(messages);
231
+ this.config.onStream?.({
232
+ type: "error",
233
+ error: "Context limit exceeded after 3 recovery attempts",
234
+ });
235
+ return { text: finalText, reason: "prompt_too_long", messages };
236
+ }
237
+ }
238
+ else {
239
+ this.patchOrphanedToolUses(messages);
240
+ this.config.onStream?.({ type: "error", error: err.message });
241
+ return { text: finalText, reason: "model_error", messages };
242
+ }
243
+ }
244
+ // UI ctx bar: prefer the provider's authoritative promptTokens.
245
+ if (response.usage?.promptTokens !== undefined) {
246
+ this.emitCtxFromUsage(response.usage.promptTokens, messages);
247
+ }
248
+ // Feed actual token usage back to the context manager so subsequent
249
+ // compaction decisions use hybrid (actual + delta) estimation rather than
250
+ // pure heuristics. Without this the manager falls back to char/4 estimates.
251
+ if (response.usage?.promptTokens !== undefined) {
252
+ this.deps.contextManager.recordActualUsage(response.usage.promptTokens, messages.length);
253
+ }
254
+ // Handle max_output_tokens: if response was truncated, do continuation (up to 3 times)
255
+ if (response.stopReason === "max_tokens" &&
256
+ response.toolCalls.length === 0 &&
257
+ response.text) {
258
+ let combinedText = response.text;
259
+ for (let retry = 0; retry < 3; retry++) {
260
+ tlog.info("turn.max_tokens_continuation", { cat: "turn", retry: retry + 1 });
261
+ const contMessages = [
262
+ ...messages,
263
+ { role: "assistant", content: combinedText },
264
+ {
265
+ role: "user",
266
+ content: "<system-reminder>Your previous response was truncated due to length. Please continue from where you left off.</system-reminder>",
267
+ },
268
+ ];
269
+ try {
270
+ const contResponse = await this.deps.model.call(this.deps.systemPrompt, contMessages, this.deps.tools, this.config.onStream, this.config.signal);
271
+ combinedText += contResponse.text;
272
+ if (contResponse.stopReason !== "max_tokens" || contResponse.toolCalls.length > 0) {
273
+ response = { ...contResponse, text: combinedText };
274
+ break;
275
+ }
276
+ }
277
+ catch {
278
+ break;
279
+ }
280
+ }
281
+ response = { ...response, text: combinedText };
282
+ }
283
+ // After any continuation, send latest usage so ctx bar reflects real context
284
+ if (response.usage?.promptTokens !== undefined) {
285
+ this.emitCtxFromUsage(response.usage.promptTokens, messages);
286
+ }
287
+ // Aborted?
288
+ if (this.config.signal?.aborted) {
289
+ return { text: finalText, reason: "aborted_streaming", messages };
290
+ }
291
+ // Accumulate text
292
+ if (response.text) {
293
+ finalText = response.text;
294
+ }
295
+ // Post-check: tool calls?
296
+ if (response.toolCalls.length === 0) {
297
+ // No tool use — final answer
298
+ this.config.onStream?.({
299
+ type: "assistant_message",
300
+ message: { role: "assistant", content: finalText },
301
+ });
302
+ await this.emitHook("on_turn_end", {
303
+ turnNumber: this.turnCount,
304
+ hasToolUse: false,
305
+ });
306
+ messages.push({ role: "assistant", content: finalText });
307
+ return { text: finalText, reason: "completed", messages };
308
+ }
309
+ // Tool execution phase
310
+ tlog.info("turn.tool_use", { cat: "turn", tools: response.toolCalls.map((t) => t.toolName) });
311
+ const toolCalls = response.toolCalls.slice(0, this.config.maxToolCallsPerTurn);
312
+ // Add assistant message with tool_use blocks to messages
313
+ const assistantBlocks = [];
314
+ if (response.text) {
315
+ assistantBlocks.push({ type: "text", text: response.text });
316
+ }
317
+ for (const tc of toolCalls) {
318
+ assistantBlocks.push({
319
+ type: "tool_use",
320
+ id: tc.id,
321
+ name: tc.toolName,
322
+ input: tc.args,
323
+ });
324
+ // Only emit tool_use_start if not already emitted during streaming
325
+ if (!this.streamedToolIds.has(tc.id)) {
326
+ this.config.onStream?.({ type: "tool_use_start", toolCall: tc });
327
+ }
328
+ // Record in transcript
329
+ this.deps.transcript.appendToolUse(tc.toolName, tc.id, tc.args);
330
+ }
331
+ messages.push({ role: "assistant", content: assistantBlocks });
332
+ // Execute tools — enqueue concurrency-safe tools for early start,
333
+ // drain remaining (unsafe) tools sequentially.
334
+ for (const tc of toolCalls) {
335
+ streamingQueue.enqueue(tc);
336
+ }
337
+ const results = await streamingQueue.drain();
338
+ // Record results in transcript and stream
339
+ const resultBlocks = [];
340
+ for (const result of results) {
341
+ const content = result.error ? `Error: ${result.error}` : (result.result ?? "(no output)");
342
+ resultBlocks.push({
343
+ type: "tool_result",
344
+ tool_use_id: result.id,
345
+ content,
346
+ });
347
+ this.deps.transcript.appendToolResult(result.id, result.toolName, result.result, result.error);
348
+ this.config.onStream?.({ type: "tool_result", result });
349
+ }
350
+ // Fire-and-forget tool use summary (non-blocking)
351
+ if (this.config.onStream) {
352
+ import("./tool-summary.js").then(({ generateToolUseSummary }) => {
353
+ if (!this.deps.model.summarize)
354
+ return;
355
+ generateToolUseSummary(toolCalls, results, this.deps.model.summarize).then((summary) => {
356
+ if (summary) {
357
+ this.config.onStream?.({ type: "tool_summary", summary });
358
+ }
359
+ });
360
+ });
361
+ }
362
+ messages.push({ role: "user", content: resultBlocks });
363
+ // Tool results just pushed; recompute ctx so the bar updates *before*
364
+ // the next model round-trip — large tool outputs can move it sharply.
365
+ this.emitCtxFromMessages(messages);
366
+ // Token budget check
367
+ const totalOutputTokens = this.deps.model.getOutputTokens?.() ?? 0;
368
+ const budgetDecision = checkTokenBudget(totalOutputTokens, this.config.tokenBudget ?? Infinity, budgetTracker);
369
+ if (budgetDecision === "stop") {
370
+ tlog.info("turn.budget_stop", {
371
+ cat: "turn",
372
+ outputTokens: totalOutputTokens,
373
+ budget: this.config.tokenBudget,
374
+ });
375
+ this.config.onStream?.({
376
+ type: "assistant_message",
377
+ message: { role: "assistant", content: finalText },
378
+ });
379
+ messages.push({ role: "assistant", content: finalText });
380
+ return { text: finalText, reason: "completed", messages };
381
+ }
382
+ if (budgetDecision === "nudge") {
383
+ messages.push({
384
+ role: "user",
385
+ content: "<system-reminder>You are approaching the token budget limit. Please start wrapping up your work and provide a summary.</system-reminder>",
386
+ });
387
+ }
388
+ // Investigation guard: end-of-turn check. If too many consecutive
389
+ // read-only turns went by without any user-visible text or side-effecting
390
+ // tool, inject a reminder that will land at the top of the next turn.
391
+ const guard = this.deps.toolExecutor.getInvestigationGuard();
392
+ if (guard) {
393
+ guard.noteText(response.text);
394
+ const turnReminder = guard.turnEnded(this.turnCount);
395
+ if (turnReminder) {
396
+ messages.push({ role: "user", content: turnReminder });
397
+ tlog.info("guard.silent_turn", { cat: "guard", turn: this.turnCount });
398
+ }
399
+ }
400
+ // Task guard: nudge the model if it has an in_progress task that
401
+ // hasn't moved in several turns. TaskCreate is sticky in working
402
+ // memory for the first few turns only; without this, the spinner
403
+ // runs forever on tasks the model has mentally finished.
404
+ const taskGuard = this.deps.toolExecutor.getTaskGuard();
405
+ if (taskGuard) {
406
+ const taskReminder = taskGuard.turnEnded(this.turnCount);
407
+ if (taskReminder) {
408
+ messages.push({ role: "user", content: taskReminder });
409
+ tlog.info("guard.stale_task", { cat: "guard", turn: this.turnCount });
410
+ }
411
+ }
412
+ // Hook: turn end
413
+ await this.emitHook("on_turn_end", {
414
+ turnNumber: this.turnCount,
415
+ hasToolUse: true,
416
+ toolCallCount: toolCalls.length,
417
+ });
418
+ // Record turn boundary
419
+ this.deps.transcript.appendTurnBoundary();
420
+ this.config.onTurnBoundary?.(this.turnCount);
421
+ tlog.info("turn.end", {
422
+ cat: "turn",
423
+ duration_ms: Date.now() - turnStartedAt,
424
+ outcome: "continue",
425
+ });
426
+ }
427
+ // Max turns reached — do one final summarization call (no tools)
428
+ logger.warn("turn.max_turns_reached", {
429
+ cat: "turn",
430
+ maxTurns: this.config.maxTurns,
431
+ turnCount: this.turnCount,
432
+ });
433
+ messages = this.deps.contextManager.manage(messages);
434
+ messages.push({
435
+ role: "user",
436
+ content: "<system-reminder>Turn limit reached. Provide a final summary of what you accomplished and what remains to be done. Do NOT call any tools.</system-reminder>",
437
+ });
438
+ this.emitCtxFromMessages(messages);
439
+ try {
440
+ const summaryResponse = await this.deps.model.call(this.deps.systemPrompt, messages, [], // No tools available for summary turn
441
+ this.config.onStream, this.config.signal);
442
+ if (summaryResponse.text) {
443
+ finalText = summaryResponse.text;
444
+ }
445
+ }
446
+ catch {
447
+ // If even summary fails, just return what we have
448
+ this.currentTurnLog.warn("turn.summary_failed", { cat: "turn" });
449
+ }
450
+ if (finalText) {
451
+ this.config.onStream?.({
452
+ type: "assistant_message",
453
+ message: { role: "assistant", content: finalText },
454
+ });
455
+ messages.push({ role: "assistant", content: finalText });
456
+ }
457
+ this.config.onStream?.({ type: "turn_complete", reason: "max_turns" });
458
+ return { text: finalText, reason: "max_turns", messages };
459
+ }
460
+ /**
461
+ * Call model with streaming fallback.
462
+ * If streaming fails, emit tombstone and retry non-streaming.
463
+ */
464
+ async callModelWithFallback(messages) {
465
+ // Wrap stream callback to track tool_use_start events and reactive compaction
466
+ let streamingResponseTokens = 0;
467
+ const wrappedStream = this.config.onStream
468
+ ? (event) => {
469
+ if (event.type === "tool_use_start" && event.toolCall?.id) {
470
+ this.streamedToolIds.add(event.toolCall.id);
471
+ }
472
+ // Track accumulating response size for reactive compaction
473
+ if (event.type === "text_delta" && event.text) {
474
+ streamingResponseTokens += Math.ceil(event.text.length / 4);
475
+ }
476
+ // Reactive compaction warning: if nearing context limit mid-stream,
477
+ // log a warning (actual compaction happens between turns)
478
+ if (streamingResponseTokens > 0 && streamingResponseTokens % 2000 === 0) {
479
+ if (this.deps.contextManager.shouldReactiveCompact(messages, streamingResponseTokens)) {
480
+ this.currentTurnLog.warn("turn.reactive_compact_warning", {
481
+ cat: "turn",
482
+ responseTokens: streamingResponseTokens,
483
+ turn: this.turnCount,
484
+ });
485
+ }
486
+ }
487
+ return this.config.onStream(event);
488
+ }
489
+ : undefined;
490
+ try {
491
+ return await this.deps.model.call(this.deps.systemPrompt, messages, this.deps.tools, wrappedStream, this.config.signal);
492
+ }
493
+ catch (err) {
494
+ // If it's a context or rate limit error, don't fallback — propagate
495
+ if (err instanceof ContextLimitError)
496
+ throw err;
497
+ // Streaming might have partially emitted — send tombstone to revoke
498
+ this.config.onStream?.({ type: "tombstone", messageId: `turn_${this.turnCount}` });
499
+ this.currentTurnLog.warn("turn.streaming_fallback", {
500
+ cat: "turn",
501
+ error: err.message,
502
+ });
503
+ // Retry without streaming
504
+ return await this.deps.model.callWithoutStreaming(this.deps.systemPrompt, messages, this.deps.tools, this.config.signal);
505
+ }
506
+ }
507
+ /**
508
+ * Execute tools with overlap: start concurrent-safe (read-only) tools
509
+ * immediately in parallel while sequential (write) tools run one-by-one.
510
+ * Both groups run simultaneously — we don't wait for safe tools to finish
511
+ * before starting unsafe ones.
512
+ */
513
+ async executeToolsOverlapped(calls) {
514
+ if (calls.length <= 1) {
515
+ // Single tool — no overlap needed
516
+ return this.deps.toolExecutor.executeAll(calls);
517
+ }
518
+ const safe = [];
519
+ const unsafe = [];
520
+ for (const call of calls) {
521
+ if (this.deps.toolExecutor.isConcurrencySafe(call.toolName)) {
522
+ safe.push(call);
523
+ }
524
+ else {
525
+ unsafe.push(call);
526
+ }
527
+ }
528
+ // If all same type, delegate directly
529
+ if (safe.length === 0 || unsafe.length === 0) {
530
+ return this.deps.toolExecutor.executeAll(calls);
531
+ }
532
+ // Run both groups simultaneously:
533
+ // - safe tools all in parallel
534
+ // - unsafe tools sequentially (but started at the same time as safe group)
535
+ const resultMap = new Map();
536
+ const safePromise = Promise.all(safe.map((c) => this.deps.toolExecutor.executeSingle(c)));
537
+ const unsafePromise = (async () => {
538
+ const results = [];
539
+ for (const call of unsafe) {
540
+ results.push(await this.deps.toolExecutor.executeSingle(call));
541
+ }
542
+ return results;
543
+ })();
544
+ const [safeResults, unsafeResults] = await Promise.all([safePromise, unsafePromise]);
545
+ for (const r of safeResults)
546
+ resultMap.set(r.id, r);
547
+ for (const r of unsafeResults)
548
+ resultMap.set(r.id, r);
549
+ // Return results in original call order for deterministic transcript
550
+ return calls.map((c) => resultMap.get(c.id));
551
+ }
552
+ get currentTurn() {
553
+ return this.turnCount;
554
+ }
555
+ /**
556
+ * Generate synthetic error tool_results for any dangling tool_use blocks
557
+ * that never received results (e.g. because the API call failed).
558
+ * Prevents model confusion on the next turn.
559
+ */
560
+ patchOrphanedToolUses(messages) {
561
+ // Find the last assistant message with tool_use blocks
562
+ for (let i = messages.length - 1; i >= 0; i--) {
563
+ const msg = messages[i];
564
+ if (msg.role !== "assistant" || !Array.isArray(msg.content))
565
+ continue;
566
+ const toolUseIds = [];
567
+ for (const block of msg.content) {
568
+ if (block.type === "tool_use" && block.id)
569
+ toolUseIds.push(block.id);
570
+ }
571
+ if (toolUseIds.length === 0)
572
+ continue;
573
+ // Check if all tool_use IDs have corresponding tool_results
574
+ const answeredIds = new Set();
575
+ for (let j = i + 1; j < messages.length; j++) {
576
+ const rm = messages[j];
577
+ if (!Array.isArray(rm.content))
578
+ continue;
579
+ for (const block of rm.content) {
580
+ if (block.type === "tool_result" && block.tool_use_id) {
581
+ answeredIds.add(block.tool_use_id);
582
+ }
583
+ }
584
+ }
585
+ const orphanedIds = toolUseIds.filter((id) => !answeredIds.has(id));
586
+ if (orphanedIds.length === 0)
587
+ return;
588
+ // Inject synthetic error results
589
+ const errorBlocks = orphanedIds.map((id) => ({
590
+ type: "tool_result",
591
+ tool_use_id: id,
592
+ content: "Error: Tool execution was cancelled because the previous API call failed.",
593
+ }));
594
+ messages.push({ role: "user", content: errorBlocks });
595
+ this.currentTurnLog.warn("turn.patched_orphaned_tool_uses", {
596
+ cat: "turn",
597
+ count: orphanedIds.length,
598
+ });
599
+ return; // Only patch the most recent orphaned set
600
+ }
601
+ }
602
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Turn loop state definitions.
3
+ */
4
+ import type { TurnPhase, ToolCall, LLMResponse, TerminalReason } from "../types.js";
5
+ export interface TurnState {
6
+ phase: TurnPhase;
7
+ turnNumber: number;
8
+ modelResponse?: LLMResponse;
9
+ toolCalls?: ToolCall[];
10
+ finalText?: string;
11
+ error?: Error;
12
+ terminalReason?: TerminalReason;
13
+ }
14
+ export declare function initialTurnState(turnNumber: number): TurnState;
15
+ /**
16
+ * Short, human-readable turn correlation ID. Stamped on every log entry
17
+ * written for the duration of one turn so `jq 'select(.turnId == "abc123")'`
18
+ * pulls a single turn's whole timeline out of the daily log file.
19
+ */
20
+ export declare function newTurnId(): string;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Turn loop state definitions.
3
+ */
4
+ export function initialTurnState(turnNumber) {
5
+ return {
6
+ phase: "pre_check",
7
+ turnNumber,
8
+ };
9
+ }
10
+ /**
11
+ * Short, human-readable turn correlation ID. Stamped on every log entry
12
+ * written for the duration of one turn so `jq 'select(.turnId == "abc123")'`
13
+ * pulls a single turn's whole timeline out of the daily log file.
14
+ */
15
+ export function newTurnId() {
16
+ return `t${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
17
+ }