@cjhyy/code-shell-core 0.5.0-rc.1 → 0.6.0-rc.1

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 (528) hide show
  1. package/README.md +13 -10
  2. package/dist/agent/agent-definition-registry.d.ts +21 -3
  3. package/dist/agent/agent-definition-registry.js +49 -18
  4. package/dist/agent/agent-definition.d.ts +32 -0
  5. package/dist/agent/agent-definition.js +49 -4
  6. package/dist/arena/arena.js +11 -12
  7. package/dist/arena/context/context-tools.js +47 -6
  8. package/dist/arena/context/within-root.d.ts +7 -0
  9. package/dist/arena/context/within-root.js +15 -0
  10. package/dist/arena/iterate/phases/argue.js +8 -2
  11. package/dist/arena/iterate/phases/revise.js +1 -1
  12. package/dist/arena/iterate/phases/tournament.js +3 -3
  13. package/dist/arena/ledger.js +9 -1
  14. package/dist/arena/phases/adjudication.js +1 -4
  15. package/dist/arena/phases/build-consensus.js +1 -4
  16. package/dist/arena/phases/cross-review.js +2 -8
  17. package/dist/arena/phases/debate-rounds.js +1 -4
  18. package/dist/arena/phases/participant-research.js +1 -4
  19. package/dist/arena/phases/planning-detail-expansion.js +8 -10
  20. package/dist/arena/planner.js +0 -1
  21. package/dist/arena/providers/docs.d.ts +1 -0
  22. package/dist/arena/providers/docs.js +5 -2
  23. package/dist/arena/providers/repo.d.ts +1 -0
  24. package/dist/arena/providers/repo.js +5 -2
  25. package/dist/arena/strategies/utils.d.ts +1 -4
  26. package/dist/arena/strategies/utils.js +7 -51
  27. package/dist/arena/transitions.d.ts +7 -2
  28. package/dist/arena/transitions.js +9 -4
  29. package/dist/arena/types.d.ts +8 -2
  30. package/dist/automation/cron-expr.d.ts +41 -0
  31. package/dist/automation/cron-expr.js +169 -0
  32. package/dist/automation/index.d.ts +41 -0
  33. package/dist/automation/index.js +39 -0
  34. package/dist/automation/runner.d.ts +84 -0
  35. package/dist/automation/runner.js +92 -0
  36. package/dist/automation/scheduler.d.ts +194 -0
  37. package/dist/automation/scheduler.js +623 -0
  38. package/dist/automation/store.d.ts +39 -0
  39. package/dist/automation/store.js +122 -0
  40. package/dist/automation/write-policy.d.ts +34 -0
  41. package/dist/automation/write-policy.js +113 -0
  42. package/dist/automation/write-run.d.ts +51 -0
  43. package/dist/automation/write-run.js +38 -0
  44. package/dist/capability-control/disabled-lists.d.ts +25 -0
  45. package/dist/capability-control/disabled-lists.js +57 -0
  46. package/dist/capability-control/index.d.ts +5 -0
  47. package/dist/capability-control/index.js +3 -0
  48. package/dist/capability-control/overlay.d.ts +64 -0
  49. package/dist/capability-control/overlay.js +116 -0
  50. package/dist/capability-control/project.d.ts +65 -0
  51. package/dist/capability-control/project.js +147 -0
  52. package/dist/capability-control/service.d.ts +70 -0
  53. package/dist/capability-control/service.js +147 -0
  54. package/dist/capability-control/types.d.ts +62 -0
  55. package/dist/capability-control/types.js +17 -0
  56. package/dist/cc-orchestrator/agent-adapter.d.ts +50 -0
  57. package/dist/cc-orchestrator/agent-adapter.js +133 -0
  58. package/dist/cc-orchestrator/cc-capability.d.ts +19 -0
  59. package/dist/cc-orchestrator/cc-capability.js +53 -0
  60. package/dist/cc-orchestrator/codex-session-discovery.d.ts +24 -0
  61. package/dist/cc-orchestrator/codex-session-discovery.js +191 -0
  62. package/dist/cc-orchestrator/codex-session-history.d.ts +25 -0
  63. package/dist/cc-orchestrator/codex-session-history.js +187 -0
  64. package/dist/cc-orchestrator/external-agent-changes.d.ts +19 -0
  65. package/dist/cc-orchestrator/external-agent-changes.js +214 -0
  66. package/dist/cc-orchestrator/external-agent-driver.d.ts +18 -0
  67. package/dist/cc-orchestrator/external-agent-driver.js +69 -0
  68. package/dist/cc-orchestrator/index.d.ts +8 -0
  69. package/dist/cc-orchestrator/index.js +8 -0
  70. package/dist/cc-orchestrator/relevance-judge.d.ts +15 -0
  71. package/dist/cc-orchestrator/relevance-judge.js +29 -0
  72. package/dist/cc-orchestrator/session-discovery.d.ts +46 -0
  73. package/dist/cc-orchestrator/session-discovery.js +125 -0
  74. package/dist/cc-orchestrator/session-history.d.ts +19 -0
  75. package/dist/cc-orchestrator/session-history.js +67 -0
  76. package/dist/cli/agent-server-stdio.d.ts +26 -1
  77. package/dist/cli/agent-server-stdio.js +221 -28
  78. package/dist/cli/agent-server-tcp.d.ts +20 -0
  79. package/dist/cli/agent-server-tcp.js +134 -0
  80. package/dist/cli/graceful-shutdown.d.ts +27 -0
  81. package/dist/cli/graceful-shutdown.js +30 -0
  82. package/dist/context/compaction.d.ts +56 -0
  83. package/dist/context/compaction.js +209 -15
  84. package/dist/context/manager.d.ts +0 -20
  85. package/dist/context/manager.js +25 -41
  86. package/dist/cost-tracker.js +5 -61
  87. package/dist/credentials/cipher.d.ts +49 -0
  88. package/dist/credentials/cipher.js +45 -0
  89. package/dist/credentials/cookie-jar.d.ts +24 -0
  90. package/dist/credentials/cookie-jar.js +40 -0
  91. package/dist/credentials/index.d.ts +7 -0
  92. package/dist/credentials/index.js +5 -0
  93. package/dist/credentials/inject-credential-tool.d.ts +20 -0
  94. package/dist/credentials/inject-credential-tool.js +130 -0
  95. package/dist/credentials/store.d.ts +72 -0
  96. package/dist/credentials/store.js +184 -0
  97. package/dist/credentials/types.d.ts +56 -0
  98. package/dist/credentials/use-credential-tool.d.ts +29 -0
  99. package/dist/credentials/use-credential-tool.js +205 -0
  100. package/dist/credentials/use-gate.d.ts +56 -0
  101. package/dist/credentials/use-gate.js +52 -0
  102. package/dist/cron/cron-runtime.d.ts +2 -0
  103. package/dist/cron/cron-runtime.js +2 -0
  104. package/dist/cron/cron-store.d.ts +2 -0
  105. package/dist/cron/cron-store.js +2 -0
  106. package/dist/cron/scheduler.d.ts +5 -32
  107. package/dist/cron/scheduler.js +5 -113
  108. package/dist/data/model-metadata.d.ts +77 -0
  109. package/dist/data/model-metadata.js +56 -0
  110. package/dist/data/model-metadata.json +216 -0
  111. package/dist/data/openrouter-models.d.ts +18 -7
  112. package/dist/data/openrouter-models.js +35 -8
  113. package/dist/data/openrouter-sync.js +1 -1
  114. package/dist/engine/aux-key.d.ts +10 -0
  115. package/dist/engine/aux-key.js +11 -0
  116. package/dist/engine/dynamic-tool-defs.d.ts +19 -0
  117. package/dist/engine/dynamic-tool-defs.js +36 -0
  118. package/dist/engine/engine.d.ts +431 -108
  119. package/dist/engine/engine.js +1630 -279
  120. package/dist/engine/friendly-error.d.ts +18 -0
  121. package/dist/engine/friendly-error.js +63 -0
  122. package/dist/engine/goal.d.ts +187 -0
  123. package/dist/engine/goal.js +196 -0
  124. package/dist/engine/image-compression.d.ts +81 -0
  125. package/dist/engine/image-compression.js +186 -0
  126. package/dist/engine/image-policy.d.ts +165 -0
  127. package/dist/engine/image-policy.js +189 -0
  128. package/dist/engine/model-connections-pool.d.ts +17 -0
  129. package/dist/engine/model-connections-pool.js +66 -0
  130. package/dist/engine/model-facade.d.ts +10 -0
  131. package/dist/engine/model-facade.js +25 -0
  132. package/dist/engine/patch-orphaned-tools.js +3 -0
  133. package/dist/engine/reactive-threshold.d.ts +13 -0
  134. package/dist/engine/reactive-threshold.js +17 -0
  135. package/dist/engine/resolve-llm-config.d.ts +16 -0
  136. package/dist/engine/resolve-llm-config.js +44 -0
  137. package/dist/engine/runtime.js +9 -1
  138. package/dist/engine/sandbox-cache-key.d.ts +10 -0
  139. package/dist/engine/sandbox-cache-key.js +9 -0
  140. package/dist/engine/sandbox-config.d.ts +31 -0
  141. package/dist/engine/sandbox-config.js +38 -0
  142. package/dist/engine/session-title.d.ts +13 -0
  143. package/dist/engine/session-title.js +34 -0
  144. package/dist/engine/session-usage.d.ts +19 -0
  145. package/dist/engine/session-usage.js +25 -0
  146. package/dist/engine/steer-queue.d.ts +32 -0
  147. package/dist/engine/steer-queue.js +26 -0
  148. package/dist/engine/streaming-tool-queue.d.ts +12 -0
  149. package/dist/engine/streaming-tool-queue.js +50 -10
  150. package/dist/engine/turn-loop.d.ts +94 -9
  151. package/dist/engine/turn-loop.js +775 -342
  152. package/dist/engine/types.d.ts +175 -0
  153. package/dist/engine/types.js +13 -0
  154. package/dist/external-agents/config.d.ts +2 -0
  155. package/dist/external-agents/config.js +15 -0
  156. package/dist/external-agents/types.d.ts +31 -0
  157. package/dist/external-agents/types.js +1 -0
  158. package/dist/git/parse-log.d.ts +13 -0
  159. package/dist/git/parse-log.js +21 -0
  160. package/dist/git/utils.d.ts +8 -6
  161. package/dist/git/utils.js +62 -26
  162. package/dist/git/worktree.d.ts +48 -0
  163. package/dist/git/worktree.js +111 -20
  164. package/dist/hooks/events.d.ts +23 -1
  165. package/dist/hooks/goal-stop-hook.d.ts +58 -0
  166. package/dist/hooks/goal-stop-hook.js +279 -0
  167. package/dist/hooks/hook-output.d.ts +33 -0
  168. package/dist/hooks/hook-output.js +79 -0
  169. package/dist/hooks/registry.d.ts +15 -0
  170. package/dist/hooks/registry.js +57 -1
  171. package/dist/hooks/shell-runner.d.ts +2 -0
  172. package/dist/hooks/shell-runner.js +48 -18
  173. package/dist/index.d.ts +79 -11
  174. package/dist/index.js +89 -6
  175. package/dist/llm/capabilities/param-specs.d.ts +14 -0
  176. package/dist/llm/capabilities/param-specs.js +62 -0
  177. package/dist/llm/capabilities/reasoning-control.d.ts +24 -0
  178. package/dist/llm/capabilities/reasoning-control.js +38 -0
  179. package/dist/llm/capabilities/rules.js +37 -3
  180. package/dist/llm/capabilities/types.d.ts +32 -2
  181. package/dist/llm/clamp-max-tokens.d.ts +11 -0
  182. package/dist/llm/clamp-max-tokens.js +17 -0
  183. package/dist/llm/client-base.d.ts +69 -5
  184. package/dist/llm/client-base.js +185 -25
  185. package/dist/llm/client-factory.d.ts +3 -3
  186. package/dist/llm/client-factory.js +2 -2
  187. package/dist/llm/model-cache.js +4 -2
  188. package/dist/llm/model-pool.d.ts +29 -11
  189. package/dist/llm/model-pool.js +36 -20
  190. package/dist/llm/provider-auth.d.ts +41 -0
  191. package/dist/llm/provider-auth.js +76 -0
  192. package/dist/llm/provider-catalog.d.ts +6 -2
  193. package/dist/llm/providers/anthropic.d.ts +32 -2
  194. package/dist/llm/providers/anthropic.js +207 -18
  195. package/dist/llm/providers/openai.d.ts +47 -2
  196. package/dist/llm/providers/openai.js +478 -71
  197. package/dist/llm/reasoning-setting.d.ts +51 -0
  198. package/dist/llm/reasoning-setting.js +40 -0
  199. package/dist/llm/stop-reason.d.ts +13 -0
  200. package/dist/llm/stop-reason.js +21 -0
  201. package/dist/llm/stream-watchdog.js +5 -1
  202. package/dist/llm/strip-vision.d.ts +32 -0
  203. package/dist/llm/strip-vision.js +94 -0
  204. package/dist/llm/token-counter.js +9 -2
  205. package/dist/llm/types.d.ts +9 -6
  206. package/dist/logging/logger.js +18 -4
  207. package/dist/logging/sanitize-messages.d.ts +10 -0
  208. package/dist/logging/sanitize-messages.js +112 -0
  209. package/dist/lsp/client.js +28 -8
  210. package/dist/lsp/manager.d.ts +1 -1
  211. package/dist/lsp/manager.js +42 -11
  212. package/dist/lsp/root-path.d.ts +9 -0
  213. package/dist/lsp/root-path.js +12 -0
  214. package/dist/model-catalog/builtin.d.ts +12 -0
  215. package/dist/model-catalog/builtin.js +407 -0
  216. package/dist/model-catalog/gen-connections.d.ts +20 -0
  217. package/dist/model-catalog/gen-connections.js +28 -0
  218. package/dist/model-catalog/index.d.ts +41 -0
  219. package/dist/model-catalog/index.js +90 -0
  220. package/dist/model-catalog/params.d.ts +20 -0
  221. package/dist/model-catalog/params.js +45 -0
  222. package/dist/model-catalog/resolve.d.ts +48 -0
  223. package/dist/model-catalog/resolve.js +33 -0
  224. package/dist/model-catalog/save-entry.d.ts +32 -0
  225. package/dist/model-catalog/save-entry.js +104 -0
  226. package/dist/model-catalog/types.d.ts +561 -0
  227. package/dist/model-catalog/types.js +93 -0
  228. package/dist/model-catalog/upsert.d.ts +9 -0
  229. package/dist/model-catalog/upsert.js +8 -0
  230. package/dist/onboarding.d.ts +12 -82
  231. package/dist/onboarding.js +75 -333
  232. package/dist/plugins/gitOps.d.ts +53 -0
  233. package/dist/plugins/gitOps.js +130 -5
  234. package/dist/plugins/installer/checkUpdate.d.ts +16 -0
  235. package/dist/plugins/installer/checkUpdate.js +32 -0
  236. package/dist/plugins/installer/codex/convertAgents.d.ts +7 -0
  237. package/dist/plugins/installer/codex/convertAgents.js +55 -0
  238. package/dist/plugins/installer/codex/convertCommands.d.ts +19 -0
  239. package/dist/plugins/installer/codex/convertCommands.js +46 -0
  240. package/dist/plugins/installer/codex/convertMcp.d.ts +11 -0
  241. package/dist/plugins/installer/codex/convertMcp.js +85 -0
  242. package/dist/plugins/installer/codex/convertSkills.d.ts +12 -0
  243. package/dist/plugins/installer/codex/convertSkills.js +33 -0
  244. package/dist/plugins/installer/detectFormat.d.ts +2 -0
  245. package/dist/plugins/installer/detectFormat.js +6 -0
  246. package/dist/plugins/installer/install.d.ts +7 -0
  247. package/dist/plugins/installer/install.js +123 -0
  248. package/dist/plugins/installer/installFromArchive.d.ts +43 -0
  249. package/dist/plugins/installer/installFromArchive.js +134 -0
  250. package/dist/plugins/installer/installFromSource.d.ts +13 -0
  251. package/dist/plugins/installer/installFromSource.js +51 -0
  252. package/dist/plugins/installer/list.d.ts +9 -0
  253. package/dist/plugins/installer/list.js +30 -0
  254. package/dist/plugins/installer/loadPluginAgents.d.ts +3 -0
  255. package/dist/plugins/installer/loadPluginAgents.js +27 -0
  256. package/dist/plugins/installer/loadPluginMcp.d.ts +14 -0
  257. package/dist/plugins/installer/loadPluginMcp.js +107 -0
  258. package/dist/plugins/installer/parseSource.d.ts +20 -0
  259. package/dist/plugins/installer/parseSource.js +74 -0
  260. package/dist/plugins/installer/paths.d.ts +5 -0
  261. package/dist/plugins/installer/paths.js +25 -0
  262. package/dist/plugins/installer/pruneDisabled.d.ts +24 -0
  263. package/dist/plugins/installer/pruneDisabled.js +73 -0
  264. package/dist/plugins/installer/types.d.ts +52 -0
  265. package/dist/plugins/installer/types.js +28 -0
  266. package/dist/plugins/installer/uninstall.d.ts +2 -0
  267. package/dist/plugins/installer/uninstall.js +18 -0
  268. package/dist/plugins/installer/unzip.d.ts +14 -0
  269. package/dist/plugins/installer/unzip.js +82 -0
  270. package/dist/plugins/installer/update.d.ts +28 -0
  271. package/dist/plugins/installer/update.js +97 -0
  272. package/dist/plugins/loadPluginHooks.d.ts +54 -1
  273. package/dist/plugins/loadPluginHooks.js +83 -1
  274. package/dist/plugins/marketplaceManager.d.ts +15 -1
  275. package/dist/plugins/marketplaceManager.js +58 -4
  276. package/dist/plugins/pluginCommandHook.js +34 -14
  277. package/dist/plugins/pluginContent.d.ts +30 -0
  278. package/dist/plugins/pluginContent.js +83 -0
  279. package/dist/plugins/pluginInstaller.d.ts +34 -0
  280. package/dist/plugins/pluginInstaller.js +168 -35
  281. package/dist/plugins/schemas.js +41 -6
  282. package/dist/plugins/types.d.ts +10 -0
  283. package/dist/preset/index.d.ts +7 -1
  284. package/dist/preset/index.js +112 -6
  285. package/dist/product/types.d.ts +1 -1
  286. package/dist/prompt/composer.d.ts +31 -0
  287. package/dist/prompt/composer.js +95 -25
  288. package/dist/prompt/instruction-scanner.js +5 -3
  289. package/dist/prompt/section-loader.js +1 -0
  290. package/dist/prompt/sections/base.md +1 -0
  291. package/dist/prompt/sections/browser.md +10 -0
  292. package/dist/prompt/sections/coding.md +4 -0
  293. package/dist/prompt/sections/orchestration.md +8 -0
  294. package/dist/protocol/chat-session-manager.d.ts +15 -2
  295. package/dist/protocol/chat-session-manager.js +46 -0
  296. package/dist/protocol/chat-session.d.ts +82 -0
  297. package/dist/protocol/chat-session.js +121 -1
  298. package/dist/protocol/client.d.ts +27 -0
  299. package/dist/protocol/client.js +38 -0
  300. package/dist/protocol/redact.d.ts +50 -0
  301. package/dist/protocol/redact.js +71 -0
  302. package/dist/protocol/server.d.ts +153 -2
  303. package/dist/protocol/server.js +807 -39
  304. package/dist/protocol/tcp-transport.d.ts +45 -0
  305. package/dist/protocol/tcp-transport.js +74 -0
  306. package/dist/protocol/transport.js +3 -2
  307. package/dist/protocol/types.d.ts +69 -0
  308. package/dist/protocol/types.js +22 -0
  309. package/dist/quota/credentials.d.ts +3 -0
  310. package/dist/quota/credentials.js +80 -0
  311. package/dist/quota/index.d.ts +36 -0
  312. package/dist/quota/index.js +155 -0
  313. package/dist/quota/types.d.ts +48 -0
  314. package/dist/quota/types.js +13 -0
  315. package/dist/remote/bridge.d.ts +11 -2
  316. package/dist/remote/bridge.js +61 -33
  317. package/dist/review/review-prompt.d.ts +28 -0
  318. package/dist/review/review-prompt.js +81 -0
  319. package/dist/run/ArtifactTracker.js +5 -4
  320. package/dist/run/EngineRunner.d.ts +27 -1
  321. package/dist/run/EngineRunner.js +46 -8
  322. package/dist/run/FileRunStore.js +40 -11
  323. package/dist/run/RunApprovalBackend.js +25 -5
  324. package/dist/run/RunLock.d.ts +28 -2
  325. package/dist/run/RunLock.js +49 -7
  326. package/dist/run/RunManager.d.ts +12 -0
  327. package/dist/run/RunManager.js +86 -3
  328. package/dist/run/RunQueue.d.ts +1 -0
  329. package/dist/run/RunQueue.js +8 -2
  330. package/dist/run/factory.d.ts +8 -1
  331. package/dist/run/factory.js +1 -0
  332. package/dist/run/index.d.ts +2 -2
  333. package/dist/run/index.js +1 -1
  334. package/dist/run/redirect-target.d.ts +7 -0
  335. package/dist/run/redirect-target.js +13 -0
  336. package/dist/run/types.js +1 -1
  337. package/dist/runtime/background-shell.d.ts +139 -0
  338. package/dist/runtime/background-shell.js +495 -0
  339. package/dist/runtime/output-clean.d.ts +24 -0
  340. package/dist/runtime/output-clean.js +41 -0
  341. package/dist/runtime/ring-file.d.ts +64 -0
  342. package/dist/runtime/ring-file.js +174 -0
  343. package/dist/runtime/safe-spawn.js +15 -34
  344. package/dist/runtime/spawn-common.d.ts +148 -0
  345. package/dist/runtime/spawn-common.js +303 -0
  346. package/dist/runtime/truncate-output.d.ts +22 -0
  347. package/dist/runtime/truncate-output.js +49 -0
  348. package/dist/runtime/utf8-cut.d.ts +11 -0
  349. package/dist/runtime/utf8-cut.js +23 -0
  350. package/dist/services/auto-dream.d.ts +4 -0
  351. package/dist/services/auto-dream.js +26 -26
  352. package/dist/services/browser-open.d.ts +13 -0
  353. package/dist/services/browser-open.js +16 -0
  354. package/dist/services/diagnostics.d.ts +1 -2
  355. package/dist/services/diagnostics.js +12 -7
  356. package/dist/services/dream-consolidation.d.ts +57 -0
  357. package/dist/services/dream-consolidation.js +151 -0
  358. package/dist/services/extract-memories.d.ts +14 -1
  359. package/dist/services/extract-memories.js +45 -6
  360. package/dist/services/memory-orchestrator.d.ts +21 -0
  361. package/dist/services/memory-orchestrator.js +125 -32
  362. package/dist/services/notifier.d.ts +8 -0
  363. package/dist/services/notifier.js +39 -25
  364. package/dist/services/oauth.js +5 -7
  365. package/dist/services/session-memory-sort.d.ts +8 -0
  366. package/dist/services/session-memory-sort.js +9 -0
  367. package/dist/services/session-memory.js +30 -19
  368. package/dist/session/file-history.d.ts +124 -1
  369. package/dist/session/file-history.js +232 -9
  370. package/dist/session/memory.d.ts +116 -2
  371. package/dist/session/memory.js +250 -28
  372. package/dist/session/session-manager.d.ts +63 -1
  373. package/dist/session/session-manager.js +186 -5
  374. package/dist/session/simple-diff.d.ts +23 -0
  375. package/dist/session/simple-diff.js +84 -0
  376. package/dist/session/transcript.d.ts +25 -1
  377. package/dist/session/transcript.js +35 -2
  378. package/dist/session/undo-target.d.ts +67 -0
  379. package/dist/session/undo-target.js +144 -0
  380. package/dist/settings/disk-defaults.d.ts +42 -0
  381. package/dist/settings/disk-defaults.js +33 -0
  382. package/dist/settings/feature-flags.d.ts +64 -0
  383. package/dist/settings/feature-flags.js +61 -0
  384. package/dist/settings/manager.d.ts +101 -1
  385. package/dist/settings/manager.js +345 -15
  386. package/dist/settings/migrate-config.d.ts +45 -0
  387. package/dist/settings/migrate-config.js +125 -0
  388. package/dist/settings/personalization.d.ts +19 -0
  389. package/dist/settings/personalization.js +7 -0
  390. package/dist/settings/schema-export.d.ts +25 -0
  391. package/dist/settings/schema-export.js +38 -0
  392. package/dist/settings/schema.d.ts +1827 -396
  393. package/dist/settings/schema.js +361 -81
  394. package/dist/skills/scanner.d.ts +9 -0
  395. package/dist/skills/scanner.js +30 -2
  396. package/dist/stt/resolve-transcribe.d.ts +31 -0
  397. package/dist/stt/resolve-transcribe.js +108 -0
  398. package/dist/stt/transcribe.d.ts +51 -0
  399. package/dist/stt/transcribe.js +65 -0
  400. package/dist/tool-system/browser-bridge.d.ts +226 -0
  401. package/dist/tool-system/browser-bridge.js +163 -0
  402. package/dist/tool-system/builtin/add-marketplace.d.ts +12 -0
  403. package/dist/tool-system/builtin/add-marketplace.js +76 -0
  404. package/dist/tool-system/builtin/agent-heartbeat.d.ts +49 -0
  405. package/dist/tool-system/builtin/agent-heartbeat.js +89 -0
  406. package/dist/tool-system/builtin/agent-notifications.d.ts +12 -3
  407. package/dist/tool-system/builtin/agent-notifications.js +9 -3
  408. package/dist/tool-system/builtin/agent-output-file.d.ts +38 -0
  409. package/dist/tool-system/builtin/agent-output-file.js +72 -0
  410. package/dist/tool-system/builtin/agent-registry.d.ts +23 -0
  411. package/dist/tool-system/builtin/agent-registry.js +16 -4
  412. package/dist/tool-system/builtin/agent.d.ts +50 -11
  413. package/dist/tool-system/builtin/agent.js +576 -73
  414. package/dist/tool-system/builtin/apply-patch/applier.js +66 -8
  415. package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +10 -0
  416. package/dist/tool-system/builtin/apply-patch/backup-targets.js +30 -0
  417. package/dist/tool-system/builtin/apply-patch/index.js +7 -3
  418. package/dist/tool-system/builtin/arena.js +6 -7
  419. package/dist/tool-system/builtin/background-jobs.d.ts +76 -0
  420. package/dist/tool-system/builtin/background-jobs.js +124 -0
  421. package/dist/tool-system/builtin/background-shell-tools.d.ts +20 -0
  422. package/dist/tool-system/builtin/background-shell-tools.js +108 -0
  423. package/dist/tool-system/builtin/background-work.d.ts +67 -0
  424. package/dist/tool-system/builtin/background-work.js +86 -0
  425. package/dist/tool-system/builtin/bash-output-style.d.ts +32 -0
  426. package/dist/tool-system/builtin/bash-output-style.js +40 -0
  427. package/dist/tool-system/builtin/bash.d.ts +5 -2
  428. package/dist/tool-system/builtin/bash.js +98 -62
  429. package/dist/tool-system/builtin/browser-tools.d.ts +33 -0
  430. package/dist/tool-system/builtin/browser-tools.js +312 -0
  431. package/dist/tool-system/builtin/cancel-goal.d.ts +31 -0
  432. package/dist/tool-system/builtin/cancel-goal.js +64 -0
  433. package/dist/tool-system/builtin/check-quota.d.ts +15 -0
  434. package/dist/tool-system/builtin/check-quota.js +34 -0
  435. package/dist/tool-system/builtin/complete-goal.d.ts +25 -0
  436. package/dist/tool-system/builtin/complete-goal.js +45 -0
  437. package/dist/tool-system/builtin/config.js +7 -0
  438. package/dist/tool-system/builtin/cron.d.ts +7 -0
  439. package/dist/tool-system/builtin/cron.js +105 -9
  440. package/dist/tool-system/builtin/drive-claude-code.d.ts +30 -0
  441. package/dist/tool-system/builtin/drive-claude-code.js +157 -0
  442. package/dist/tool-system/builtin/edit-model-catalog.d.ts +3 -0
  443. package/dist/tool-system/builtin/edit-model-catalog.js +94 -0
  444. package/dist/tool-system/builtin/edit.d.ts +2 -1
  445. package/dist/tool-system/builtin/edit.js +21 -12
  446. package/dist/tool-system/builtin/eol.d.ts +29 -0
  447. package/dist/tool-system/builtin/eol.js +37 -0
  448. package/dist/tool-system/builtin/file-cache.d.ts +6 -0
  449. package/dist/tool-system/builtin/file-cache.js +8 -0
  450. package/dist/tool-system/builtin/generate-image.d.ts +60 -0
  451. package/dist/tool-system/builtin/generate-image.js +374 -0
  452. package/dist/tool-system/builtin/generate-video.d.ts +55 -0
  453. package/dist/tool-system/builtin/generate-video.js +361 -0
  454. package/dist/tool-system/builtin/grep.js +6 -2
  455. package/dist/tool-system/builtin/image-providers.d.ts +86 -0
  456. package/dist/tool-system/builtin/image-providers.js +190 -0
  457. package/dist/tool-system/builtin/image-uploader.d.ts +33 -0
  458. package/dist/tool-system/builtin/image-uploader.js +74 -0
  459. package/dist/tool-system/builtin/index.d.ts +24 -1
  460. package/dist/tool-system/builtin/index.js +310 -25
  461. package/dist/tool-system/builtin/mcp-tools.js +23 -3
  462. package/dist/tool-system/builtin/memory.js +45 -7
  463. package/dist/tool-system/builtin/notebook-edit.d.ts +2 -1
  464. package/dist/tool-system/builtin/notebook-edit.js +1 -1
  465. package/dist/tool-system/builtin/powershell.js +4 -1
  466. package/dist/tool-system/builtin/read.d.ts +2 -1
  467. package/dist/tool-system/builtin/read.js +11 -3
  468. package/dist/tool-system/builtin/repl.js +4 -1
  469. package/dist/tool-system/builtin/skill.js +9 -0
  470. package/dist/tool-system/builtin/sleep.js +12 -5
  471. package/dist/tool-system/builtin/task.js +4 -1
  472. package/dist/tool-system/builtin/tool-search.js +25 -7
  473. package/dist/tool-system/builtin/update-automation-memory.d.ts +24 -0
  474. package/dist/tool-system/builtin/update-automation-memory.js +60 -0
  475. package/dist/tool-system/builtin/video-providers.d.ts +154 -0
  476. package/dist/tool-system/builtin/video-providers.js +235 -0
  477. package/dist/tool-system/builtin/view-image.d.ts +18 -0
  478. package/dist/tool-system/builtin/view-image.js +96 -0
  479. package/dist/tool-system/builtin/web-fetch.js +12 -2
  480. package/dist/tool-system/builtin/web-search.d.ts +4 -1
  481. package/dist/tool-system/builtin/web-search.js +57 -10
  482. package/dist/tool-system/builtin/worktree.d.ts +2 -1
  483. package/dist/tool-system/builtin/worktree.js +31 -4
  484. package/dist/tool-system/builtin/write.d.ts +2 -1
  485. package/dist/tool-system/builtin/write.js +1 -1
  486. package/dist/tool-system/context.d.ts +166 -6
  487. package/dist/tool-system/executor.d.ts +3 -2
  488. package/dist/tool-system/executor.js +197 -119
  489. package/dist/tool-system/investigation-guard.d.ts +4 -1
  490. package/dist/tool-system/investigation-guard.js +20 -4
  491. package/dist/tool-system/mcp-manager.d.ts +78 -16
  492. package/dist/tool-system/mcp-manager.js +357 -28
  493. package/dist/tool-system/path-policy.d.ts +99 -0
  494. package/dist/tool-system/path-policy.js +578 -0
  495. package/dist/tool-system/permission.d.ts +36 -1
  496. package/dist/tool-system/permission.js +322 -117
  497. package/dist/tool-system/plan-mode-allowlist.d.ts +32 -0
  498. package/dist/tool-system/plan-mode-allowlist.js +65 -0
  499. package/dist/tool-system/registry.d.ts +1 -0
  500. package/dist/tool-system/registry.js +26 -1
  501. package/dist/tool-system/sandbox/index.d.ts +8 -0
  502. package/dist/tool-system/sandbox/index.js +7 -2
  503. package/dist/tool-system/validate-tool-metadata.d.ts +36 -0
  504. package/dist/tool-system/validate-tool-metadata.js +63 -0
  505. package/dist/tool-system/validation.d.ts +10 -0
  506. package/dist/tool-system/validation.js +16 -3
  507. package/dist/types.d.ts +301 -15
  508. package/dist/updater.js +20 -9
  509. package/dist/utils/envUtils.d.ts +0 -9
  510. package/dist/utils/envUtils.js +3 -28
  511. package/dist/utils/exec.d.ts +40 -0
  512. package/dist/utils/exec.js +144 -0
  513. package/dist/utils/format.js +4 -2
  514. package/dist/utils/json.d.ts +12 -0
  515. package/dist/utils/json.js +92 -0
  516. package/dist/utils/lockfile.js +9 -2
  517. package/dist/utils/theme.d.ts +7 -1
  518. package/dist/utils/theme.js +5 -3
  519. package/dist/utils/toolDisplay.js +0 -1
  520. package/package.json +14 -7
  521. package/dist/agent/coordinator.d.ts +0 -49
  522. package/dist/agent/coordinator.js +0 -77
  523. package/dist/settings/manager.test.js +0 -73
  524. package/dist/tool-system/builtin/remote-trigger.d.ts +0 -6
  525. package/dist/tool-system/builtin/remote-trigger.js +0 -54
  526. package/dist/tool-system/builtin/send-message.d.ts +0 -6
  527. package/dist/tool-system/builtin/send-message.js +0 -47
  528. /package/dist/{settings/manager.test.d.ts → credentials/types.js} +0 -0
@@ -7,10 +7,57 @@ import { homedir } from "node:os";
7
7
  import { nanoid } from "nanoid";
8
8
  import { Transcript } from "./transcript.js";
9
9
  import { SessionError } from "../exceptions.js";
10
+ /**
11
+ * Validate a session ID before it is joined into a filesystem path.
12
+ *
13
+ * Internally generated IDs use `nanoid(16)` and are trusted by construction.
14
+ * But every public entry point (`create`'s explicitSessionId, `resume`,
15
+ * `exists`, `saveState`'s state.sessionId, `fork`'s sourceSessionId) accepts
16
+ * an ID from an outside caller — protocol clients, ChatSessionManager-driven
17
+ * cold starts, persisted state files — and join()'s it into `sessionsDir`.
18
+ * Without this check a value like "../etc/passwd" or "/tmp/x" would let the
19
+ * caller escape the sessions directory.
20
+ *
21
+ * Exported for direct unit testing.
22
+ */
23
+ export function assertSafeSessionId(sessionId) {
24
+ if (typeof sessionId !== "string" || sessionId.length === 0) {
25
+ throw new SessionError(`invalid session id: must be a non-empty string`);
26
+ }
27
+ // basename check: reject any path-shaped value. Covers absolute paths,
28
+ // POSIX and Windows separators, parent-dir tokens, and the lone "..".
29
+ if (sessionId.includes("/") || sessionId.includes("\\")) {
30
+ throw new SessionError(`invalid session id: contains path separator: ${sessionId}`);
31
+ }
32
+ if (sessionId === "." || sessionId === ".." || sessionId.includes("..")) {
33
+ throw new SessionError(`invalid session id: contains parent-dir token: ${sessionId}`);
34
+ }
35
+ // Conservative character allow-list: letters, digits, and `-_.` only.
36
+ // This matches what nanoid emits plus the dotted variants in-house code
37
+ // already uses (e.g. "tui-main", "agent.foo"). Anything else (NUL,
38
+ // newline, control chars, shell metacharacters, glob chars) is rejected.
39
+ if (!/^[A-Za-z0-9_.-]+$/.test(sessionId)) {
40
+ throw new SessionError(`invalid session id: unexpected characters: ${sessionId}`);
41
+ }
42
+ // Cap the length to keep filesystem APIs happy and avoid disk-name DoS.
43
+ if (sessionId.length > 128) {
44
+ throw new SessionError(`invalid session id: too long (max 128 chars)`);
45
+ }
46
+ }
47
+ /**
48
+ * Resolve the `.code-shell` home dir. `CODE_SHELL_HOME` overrides the default
49
+ * `~/.code-shell` — mirrors Codex's `CODEX_HOME`. Tests set it to a temp dir
50
+ * (see bunfig.toml preload) so a `new Engine()` / `new SessionManager()` with
51
+ * no explicit storageDir doesn't pollute the user's real ~/.code-shell/sessions
52
+ * with throwaway test sessions (the rm-usage/test-model sidebar junk).
53
+ */
54
+ export function codeShellHome() {
55
+ return process.env.CODE_SHELL_HOME || join(homedir(), ".code-shell");
56
+ }
10
57
  export class SessionManager {
11
58
  sessionsDir;
12
59
  constructor(storageDir) {
13
- this.sessionsDir = storageDir ?? join(homedir(), ".code-shell", "sessions");
60
+ this.sessionsDir = storageDir ?? join(codeShellHome(), "sessions");
14
61
  mkdirSync(this.sessionsDir, { recursive: true });
15
62
  }
16
63
  /**
@@ -20,7 +67,12 @@ export class SessionManager {
20
67
  * nanoid. Either way the on-disk directory is materialized and the
21
68
  * state.json + transcript.jsonl files are written before return.
22
69
  */
23
- create(cwd, model, provider, explicitSessionId) {
70
+ create(cwd, model, provider, explicitSessionId, parentSessionId, origin) {
71
+ // External callers may pass any string; nanoid output is trusted. Either
72
+ // way the ID gets joined into a filesystem path, so the public entry
73
+ // point validates before that join.
74
+ if (explicitSessionId !== undefined)
75
+ assertSafeSessionId(explicitSessionId);
24
76
  const sessionId = explicitSessionId ?? nanoid(16);
25
77
  const sessionDir = join(this.sessionsDir, sessionId);
26
78
  mkdirSync(sessionDir, { recursive: true });
@@ -34,8 +86,19 @@ export class SessionManager {
34
86
  turnCount: 0,
35
87
  invokedSkills: [],
36
88
  status: "active",
89
+ // Always write the key: a sub-agent gets its parent sid; a top-level
90
+ // session gets explicit null. This lets the desktop disk-rebuild tell a
91
+ // new top-level session (key present, null) apart from a legacy session
92
+ // (key absent) and from a sub-agent (key present, non-empty string).
93
+ parentSessionId: parentSessionId ?? null,
94
+ ...(origin ? { origin } : {}),
37
95
  };
38
- writeFileSync(join(sessionDir, "state.json"), JSON.stringify(state, null, 2), "utf-8");
96
+ // Atomic write (tmp+rename) like saveState, so a crash during this one-time
97
+ // create can't leave a torn state.json that resume() then fails to parse.
98
+ const stateTarget = join(sessionDir, "state.json");
99
+ const stateTmp = `${stateTarget}.${process.pid}.${Date.now()}.create.tmp`;
100
+ writeFileSync(stateTmp, JSON.stringify(state, null, 2), "utf-8");
101
+ renameSync(stateTmp, stateTarget);
39
102
  const transcript = new Transcript(join(sessionDir, "transcript.jsonl"));
40
103
  transcript.append("session_meta", {
41
104
  sessionId,
@@ -52,9 +115,115 @@ export class SessionManager {
52
115
  * without catching SessionError.
53
116
  */
54
117
  exists(sessionId) {
118
+ // exists() is a probe — callers use it to decide between resume and
119
+ // create-with-explicit-sid. Treat an invalid id as "not present"
120
+ // rather than letting the traversal-shaped string reach existsSync.
121
+ try {
122
+ assertSafeSessionId(sessionId);
123
+ }
124
+ catch {
125
+ return false;
126
+ }
55
127
  return existsSync(join(this.sessionsDir, sessionId));
56
128
  }
129
+ /**
130
+ * Cheap "what cwd is this session bound to?" probe — reads only state.json,
131
+ * NOT the transcript (unlike resume()). engine.run uses this to recover a
132
+ * resumed session's project directory when the caller omits options.cwd
133
+ * (e.g. a desktop host whose sidebar repo selection has drifted to null),
134
+ * so a project-bound session keeps loading its own agents/settings/memory
135
+ * instead of falling back to process.cwd(). Returns undefined for an
136
+ * unknown, malformed, or traversal-shaped id rather than throwing — the
137
+ * caller treats "no recoverable cwd" the same as "not given".
138
+ */
139
+ readCwd(sessionId) {
140
+ try {
141
+ assertSafeSessionId(sessionId);
142
+ }
143
+ catch {
144
+ return undefined;
145
+ }
146
+ const stateFile = join(this.sessionsDir, sessionId, "state.json");
147
+ if (!existsSync(stateFile))
148
+ return undefined;
149
+ try {
150
+ const state = JSON.parse(readFileSync(stateFile, "utf-8"));
151
+ return typeof state.cwd === "string" && state.cwd.length > 0 ? state.cwd : undefined;
152
+ }
153
+ catch {
154
+ return undefined;
155
+ }
156
+ }
157
+ /**
158
+ * Cheap "does this session have a persisted goal?" probe — reads only
159
+ * state.json, NOT the transcript (like readCwd). A persistent goal lives ONLY
160
+ * in state.activeGoal; it is never appended to the transcript as an event, so
161
+ * a session rebuilt from disk (e.g. localStorage wiped) can't recover the
162
+ * goal from its messages. The desktop host calls this on session load to
163
+ * re-surface the active-goal block + its Cancel button, which would otherwise
164
+ * be invisible (and thus uncancellable) after a reload of an aborted goal
165
+ * run. Returns undefined for an unknown / malformed / traversal-shaped id, or
166
+ * a session with no active goal — never throws.
167
+ */
168
+ readActiveGoal(sessionId) {
169
+ try {
170
+ assertSafeSessionId(sessionId);
171
+ }
172
+ catch {
173
+ return undefined;
174
+ }
175
+ const stateFile = join(this.sessionsDir, sessionId, "state.json");
176
+ if (!existsSync(stateFile))
177
+ return undefined;
178
+ try {
179
+ const state = JSON.parse(readFileSync(stateFile, "utf-8"));
180
+ return state.activeGoal;
181
+ }
182
+ catch {
183
+ return undefined;
184
+ }
185
+ }
186
+ /**
187
+ * Disk-only "wipe this session's persistent goal" — the counterpart to
188
+ * readActiveGoal. Reads state.json, removes state.activeGoal, and rewrites it
189
+ * atomically (via saveState). Returns true only if a goal was actually
190
+ * present (idempotent — clearing a session with no goal is a no-op returning
191
+ * false). Never throws on an unknown / malformed / traversal-shaped id
192
+ * (returns false), matching readActiveGoal's tolerance.
193
+ *
194
+ * Why it lives here and not only on Engine: a persistent goal can strand a
195
+ * session whose worker is NOT live (aborted/reloaded) — Engine.clearGoal
196
+ * needs a resumed in-RAM session and a matching in-flight hook, neither of
197
+ * which exists once the worker has exited. Hosts (the desktop bridge) call
198
+ * THIS to clear such a goal off disk without spinning up a full Engine.
199
+ * Engine.clearGoal also delegates its disk write here so the wipe logic lives
200
+ * in exactly one place.
201
+ */
202
+ clearActiveGoal(sessionId) {
203
+ try {
204
+ assertSafeSessionId(sessionId);
205
+ }
206
+ catch {
207
+ return false;
208
+ }
209
+ const stateFile = join(this.sessionsDir, sessionId, "state.json");
210
+ if (!existsSync(stateFile))
211
+ return false;
212
+ let state;
213
+ try {
214
+ state = JSON.parse(readFileSync(stateFile, "utf-8"));
215
+ }
216
+ catch {
217
+ return false;
218
+ }
219
+ if (state.activeGoal === undefined)
220
+ return false;
221
+ state.activeGoal = undefined;
222
+ this.saveState(state);
223
+ return true;
224
+ }
57
225
  resume(sessionId) {
226
+ assertSafeSessionId(sessionId);
58
227
  const sessionDir = join(this.sessionsDir, sessionId);
59
228
  if (!existsSync(sessionDir)) {
60
229
  throw new SessionError(`Session not found: ${sessionId}`);
@@ -63,13 +232,25 @@ export class SessionManager {
63
232
  if (!existsSync(stateFile)) {
64
233
  throw new SessionError(`Session state file not found: ${sessionId}`);
65
234
  }
66
- const state = JSON.parse(readFileSync(stateFile, "utf-8"));
235
+ let state;
236
+ try {
237
+ state = JSON.parse(readFileSync(stateFile, "utf-8"));
238
+ }
239
+ catch (err) {
240
+ // A corrupt state.json (external tampering, disk corruption, or a crash
241
+ // during the one-time create() write) must surface as a clean SessionError
242
+ // — not a raw SyntaxError that escapes callers expecting SessionError.
243
+ throw new SessionError(`Session state is corrupt for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
244
+ }
67
245
  const transcriptFile = join(sessionDir, "transcript.jsonl");
68
246
  const transcript = Transcript.loadFromFile(transcriptFile);
69
247
  state.status = "active";
70
248
  return { state, transcript };
71
249
  }
72
250
  saveState(state) {
251
+ // state.sessionId could come from a deserialized state.json that was
252
+ // tampered with on disk. Validate before joining.
253
+ assertSafeSessionId(state.sessionId);
73
254
  const sessionDir = join(this.sessionsDir, state.sessionId);
74
255
  mkdirSync(sessionDir, { recursive: true });
75
256
  // Atomic write: stage to .tmp, then rename. Protects against two processes
@@ -94,7 +275,7 @@ export class SessionManager {
94
275
  newBundle.state.parentSessionId = sourceSessionId;
95
276
  // Copy events up to the fork point
96
277
  for (const event of events) {
97
- if (event.type === "turn_boundary" && event.data.turnNumber > forkTurn) {
278
+ if (event.type === "turn_boundary" && (event.data.turnNumber ?? -1) > forkTurn) {
98
279
  break;
99
280
  }
100
281
  newBundle.transcript.append(event.type, event.data);
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Minimal, dependency-free unified-diff for human-readable previews (e.g. the
3
+ * /undo confirmation). NOT a patch generator — it's for showing a user "here's
4
+ * what restoring will change" before they confirm. Uses a standard LCS so the
5
+ * output reads like a normal diff (unchanged context, -removed, +added).
6
+ *
7
+ * `from` is the content currently on disk; `to` is the snapshot we'd restore.
8
+ * So the preview answers "if I undo, these lines change."
9
+ */
10
+ /** A single diff line with its marker. */
11
+ export interface DiffLine {
12
+ marker: " " | "-" | "+";
13
+ text: string;
14
+ }
15
+ /** Longest-common-subsequence table → minimal -/+ line diff (Myers-equivalent
16
+ * result via classic LCS; fine for preview-sized files). */
17
+ export declare function diffLines(from: string, to: string): DiffLine[];
18
+ /**
19
+ * Render a compact unified-diff string for a preview. Collapses long runs of
20
+ * unchanged context to at most `context` lines around each change so a small
21
+ * edit in a big file doesn't print the whole file. Returns "" when identical.
22
+ */
23
+ export declare function renderDiffPreview(from: string, to: string, context?: number): string;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Minimal, dependency-free unified-diff for human-readable previews (e.g. the
3
+ * /undo confirmation). NOT a patch generator — it's for showing a user "here's
4
+ * what restoring will change" before they confirm. Uses a standard LCS so the
5
+ * output reads like a normal diff (unchanged context, -removed, +added).
6
+ *
7
+ * `from` is the content currently on disk; `to` is the snapshot we'd restore.
8
+ * So the preview answers "if I undo, these lines change."
9
+ */
10
+ /** Longest-common-subsequence table → minimal -/+ line diff (Myers-equivalent
11
+ * result via classic LCS; fine for preview-sized files). */
12
+ export function diffLines(from, to) {
13
+ // Normalize CRLF → LF before splitting so a Windows (CRLF) snapshot vs. an
14
+ // LF edit doesn't render as every-line-changed in the /undo preview.
15
+ const a = from.replace(/\r\n/g, "\n").split("\n");
16
+ const b = to.replace(/\r\n/g, "\n").split("\n");
17
+ const n = a.length;
18
+ const m = b.length;
19
+ // LCS length table.
20
+ const lcs = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
21
+ for (let i = n - 1; i >= 0; i--) {
22
+ for (let j = m - 1; j >= 0; j--) {
23
+ lcs[i][j] = a[i] === b[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]);
24
+ }
25
+ }
26
+ const out = [];
27
+ let i = 0;
28
+ let j = 0;
29
+ while (i < n && j < m) {
30
+ if (a[i] === b[j]) {
31
+ out.push({ marker: " ", text: a[i] });
32
+ i++;
33
+ j++;
34
+ }
35
+ else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
36
+ out.push({ marker: "-", text: a[i] });
37
+ i++;
38
+ }
39
+ else {
40
+ out.push({ marker: "+", text: b[j] });
41
+ j++;
42
+ }
43
+ }
44
+ while (i < n)
45
+ out.push({ marker: "-", text: a[i++] });
46
+ while (j < m)
47
+ out.push({ marker: "+", text: b[j++] });
48
+ return out;
49
+ }
50
+ /**
51
+ * Render a compact unified-diff string for a preview. Collapses long runs of
52
+ * unchanged context to at most `context` lines around each change so a small
53
+ * edit in a big file doesn't print the whole file. Returns "" when identical.
54
+ */
55
+ export function renderDiffPreview(from, to, context = 3) {
56
+ const lines = diffLines(from, to);
57
+ if (lines.every((l) => l.marker === " "))
58
+ return "";
59
+ // Mark which lines are within `context` of a change; drop the rest, inserting
60
+ // a "⋯" gap marker where we elide.
61
+ const keep = new Array(lines.length).fill(false);
62
+ for (let k = 0; k < lines.length; k++) {
63
+ if (lines[k].marker !== " ") {
64
+ for (let d = -context; d <= context; d++) {
65
+ const idx = k + d;
66
+ if (idx >= 0 && idx < lines.length)
67
+ keep[idx] = true;
68
+ }
69
+ }
70
+ }
71
+ const rows = [];
72
+ let elided = false;
73
+ for (let k = 0; k < lines.length; k++) {
74
+ if (keep[k]) {
75
+ rows.push(`${lines[k].marker}${lines[k].text}`);
76
+ elided = false;
77
+ }
78
+ else if (!elided) {
79
+ rows.push("⋯");
80
+ elided = true;
81
+ }
82
+ }
83
+ return rows.join("\n");
84
+ }
@@ -10,10 +10,34 @@ export declare class Transcript {
10
10
  getFilePath(): string;
11
11
  constructor(filePath: string);
12
12
  append(type: TranscriptEventType, data: Record<string, unknown>): TranscriptEvent;
13
- appendMessage(role: string, content: string | ContentBlock[]): TranscriptEvent;
13
+ /**
14
+ * Append a chat message to the transcript.
15
+ *
16
+ * `injected` marks a synthetic system-reminder turn (e.g. a background-job
17
+ * completion notification) that is submitted to the model as `role:"user"`
18
+ * but is NOT the user's own input. The disk reader uses this flag to skip
19
+ * rendering it as a user bubble on replay (matching the live UI, which never
20
+ * shows it as a bubble — only the assistant's reply). Real user input and
21
+ * step-gap steering messages are left unmarked so they render normally.
22
+ */
23
+ appendMessage(role: string, content: string | ContentBlock[], opts?: {
24
+ injected?: boolean;
25
+ }): TranscriptEvent;
14
26
  appendToolUse(toolName: string, toolCallId: string, args: Record<string, unknown>): TranscriptEvent;
15
27
  appendToolResult(toolCallId: string, toolName: string, result?: string, error?: string): TranscriptEvent;
28
+ /** Anchor for a spawned sub-agent (see TranscriptEventType "subagent").
29
+ * Written at spawn time so replay can rebuild the sub-agent's card from
30
+ * sessions/<agentId>/ — agentId === the sub-agent's session id. */
31
+ appendSubagent(agentId: string, name: string | undefined, description: string): TranscriptEvent;
16
32
  appendTurnBoundary(): TranscriptEvent;
33
+ /**
34
+ * Mark the in-flight turn as user-interrupted (Stop). Persisted so a resume
35
+ * rebuilds the renderer's "stopped" marker (foldTranscript) — otherwise the
36
+ * interrupted turn folds behind the process-card header on reload.
37
+ * Idempotent: a no-op if the last event is already a turn_stopped (the loop
38
+ * can hit more than one abort return for a single Stop).
39
+ */
40
+ appendTurnStopped(): TranscriptEvent | undefined;
17
41
  appendSummary(summary: string, compactedRange: {
18
42
  fromTurn: number;
19
43
  toTurn: number;
@@ -29,8 +29,22 @@ export class Transcript {
29
29
  this.flush(event);
30
30
  return event;
31
31
  }
32
- appendMessage(role, content) {
33
- return this.append("message", { role, content });
32
+ /**
33
+ * Append a chat message to the transcript.
34
+ *
35
+ * `injected` marks a synthetic system-reminder turn (e.g. a background-job
36
+ * completion notification) that is submitted to the model as `role:"user"`
37
+ * but is NOT the user's own input. The disk reader uses this flag to skip
38
+ * rendering it as a user bubble on replay (matching the live UI, which never
39
+ * shows it as a bubble — only the assistant's reply). Real user input and
40
+ * step-gap steering messages are left unmarked so they render normally.
41
+ */
42
+ appendMessage(role, content, opts) {
43
+ return this.append("message", {
44
+ role,
45
+ content,
46
+ ...(opts?.injected ? { injected: true } : {}),
47
+ });
34
48
  }
35
49
  appendToolUse(toolName, toolCallId, args) {
36
50
  return this.append("tool_use", { toolName, toolCallId, args });
@@ -38,10 +52,29 @@ export class Transcript {
38
52
  appendToolResult(toolCallId, toolName, result, error) {
39
53
  return this.append("tool_result", { toolCallId, toolName, result, error });
40
54
  }
55
+ /** Anchor for a spawned sub-agent (see TranscriptEventType "subagent").
56
+ * Written at spawn time so replay can rebuild the sub-agent's card from
57
+ * sessions/<agentId>/ — agentId === the sub-agent's session id. */
58
+ appendSubagent(agentId, name, description) {
59
+ return this.append("subagent", { agentId, name, description });
60
+ }
41
61
  appendTurnBoundary() {
42
62
  this.currentTurn++;
43
63
  return this.append("turn_boundary", { turnNumber: this.currentTurn });
44
64
  }
65
+ /**
66
+ * Mark the in-flight turn as user-interrupted (Stop). Persisted so a resume
67
+ * rebuilds the renderer's "stopped" marker (foldTranscript) — otherwise the
68
+ * interrupted turn folds behind the process-card header on reload.
69
+ * Idempotent: a no-op if the last event is already a turn_stopped (the loop
70
+ * can hit more than one abort return for a single Stop).
71
+ */
72
+ appendTurnStopped() {
73
+ const last = this.events[this.events.length - 1];
74
+ if (last && last.type === "turn_stopped")
75
+ return undefined;
76
+ return this.append("turn_stopped", {});
77
+ }
45
78
  appendSummary(summary, compactedRange) {
46
79
  return this.append("summary", {
47
80
  summary,
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Pure selection of the "most recent file modification" to undo, given a
3
+ * FileHistory snapshot list. Kept fs-free so the "what does /undo target"
4
+ * decision is unit-testable and shared between the TUI command and any future
5
+ * desktop entry point.
6
+ *
7
+ * A snapshot is the *pre-modification* backup of a file (see file-history.ts:
8
+ * saveSnapshot runs before Write/Edit/ApplyPatch). So the newest snapshot's
9
+ * backup is the content to restore for a single-step undo: it is the state the
10
+ * file had immediately before the last edit.
11
+ */
12
+ import type { FileSnapshot, RedoRecord } from "./file-history.js";
13
+ /**
14
+ * The snapshot for the most recent modification. Returns the entry with the
15
+ * greatest `timestamp`; on a tie (same-millisecond snapshots of different
16
+ * files) the LAST such entry in array order wins — saveSnapshot pushes in
17
+ * chronological order, so the last pushed is the most recently recorded, which
18
+ * is the stable, predictable choice. Returns null for an empty history.
19
+ */
20
+ export declare function latestUndoTarget(snapshots: FileSnapshot[]): FileSnapshot | null;
21
+ /**
22
+ * For "/undo all": each tracked file's EARLIEST snapshot — its content before
23
+ * the *first* AI edit this session. Restoring all of these reverts the whole
24
+ * session's file changes (vs. latestUndoTarget, which is the single last step).
25
+ *
26
+ * Returns one snapshot per file path, ordered by that file's first-edit time
27
+ * (oldest first) so a preview reads chronologically. On a same-millisecond tie
28
+ * for a given file, the FIRST array entry wins (earliest recorded).
29
+ */
30
+ export declare function earliestSnapshotsPerFile(snapshots: FileSnapshot[]): FileSnapshot[];
31
+ /**
32
+ * For turn-level "/undo": the snapshots needed to revert just the MOST RECENT
33
+ * conversation turn (one user message = one turn, see SessionState.turnSeq).
34
+ *
35
+ * The latest turn = the greatest `turnSeq` present. A turn boundary is defined
36
+ * by turnSeq, NOT wall-clock — a clock skew must not move the boundary. Within
37
+ * that turn, each touched file is restored to its EARLIEST snapshot of the turn
38
+ * (its content before the *first* edit that turn), so re-editing a file twice
39
+ * in one turn still reverts to the pre-turn baseline — and edits from *earlier*
40
+ * turns are left intact (that's the whole point vs. earliestSnapshotsPerFile,
41
+ * which spans the whole session).
42
+ *
43
+ * Backward compatibility: snapshots written before this feature have no
44
+ * `turnSeq`. They share the `undefined` bucket. `undefined` sorts as the
45
+ * smallest "turn", so any real (tagged) turn supersedes the legacy bucket; a
46
+ * history that is ALL-legacy degrades to whole-session behaviour (each file's
47
+ * earliest) rather than throwing.
48
+ *
49
+ * Returns one snapshot per file, ordered by that file's first-edit time within
50
+ * the turn (oldest first) so a preview reads chronologically. Empty → [].
51
+ */
52
+ export declare function latestTurnUndoTargets(snapshots: FileSnapshot[]): FileSnapshot[];
53
+ /**
54
+ * For "/redo": the RedoRecord[] of the MOST RECENT undone turn — the turn that
55
+ * `redoLatestTurn` should re-apply — but ONLY when that turn is still the latest
56
+ * undone state (i.e. nothing newer happened since the undo). Otherwise [].
57
+ *
58
+ * Rule: let R = the greatest turnSeq present in `redoRecords` (a RedoRecord
59
+ * exists only for a turn that was undone and not yet redone). R is redoable iff
60
+ * NO snapshot belongs to a strictly newer LIVE (non-undone) turn — a fresh edit
61
+ * after the undo supersedes the redo and invalidates it (spec: "新轮使 redo
62
+ * 失效"). Created-only turns have no pre-turn snapshot, so the RedoRecord itself
63
+ * is the sole evidence and is honoured as long as nothing newer superseded it.
64
+ *
65
+ * Empty `redoRecords` → []. fs-free so /redo's target decision is unit-testable.
66
+ */
67
+ export declare function latestRedoTargets(redoRecords: RedoRecord[], snapshots: FileSnapshot[]): RedoRecord[];
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Pure selection of the "most recent file modification" to undo, given a
3
+ * FileHistory snapshot list. Kept fs-free so the "what does /undo target"
4
+ * decision is unit-testable and shared between the TUI command and any future
5
+ * desktop entry point.
6
+ *
7
+ * A snapshot is the *pre-modification* backup of a file (see file-history.ts:
8
+ * saveSnapshot runs before Write/Edit/ApplyPatch). So the newest snapshot's
9
+ * backup is the content to restore for a single-step undo: it is the state the
10
+ * file had immediately before the last edit.
11
+ */
12
+ /**
13
+ * The snapshot for the most recent modification. Returns the entry with the
14
+ * greatest `timestamp`; on a tie (same-millisecond snapshots of different
15
+ * files) the LAST such entry in array order wins — saveSnapshot pushes in
16
+ * chronological order, so the last pushed is the most recently recorded, which
17
+ * is the stable, predictable choice. Returns null for an empty history.
18
+ */
19
+ export function latestUndoTarget(snapshots) {
20
+ let best = null;
21
+ for (const s of snapshots) {
22
+ // `>=` so a later array entry with an equal timestamp supersedes an
23
+ // earlier one (last-pushed wins on a tie).
24
+ if (best === null || s.timestamp >= best.timestamp)
25
+ best = s;
26
+ }
27
+ return best;
28
+ }
29
+ /**
30
+ * For "/undo all": each tracked file's EARLIEST snapshot — its content before
31
+ * the *first* AI edit this session. Restoring all of these reverts the whole
32
+ * session's file changes (vs. latestUndoTarget, which is the single last step).
33
+ *
34
+ * Returns one snapshot per file path, ordered by that file's first-edit time
35
+ * (oldest first) so a preview reads chronologically. On a same-millisecond tie
36
+ * for a given file, the FIRST array entry wins (earliest recorded).
37
+ */
38
+ export function earliestSnapshotsPerFile(snapshots) {
39
+ const earliest = new Map();
40
+ for (const s of snapshots) {
41
+ // Undone snapshots belong to a reverted turn: their content is no longer on
42
+ // disk and they are not the session baseline, so skip them — otherwise an
43
+ // undone (earlier) snapshot could win the baseline and `/undo all` would
44
+ // restore stale content.
45
+ if (s.undone)
46
+ continue;
47
+ const cur = earliest.get(s.filePath);
48
+ // strict `<` so the first-seen entry wins on a tie (earliest recorded).
49
+ if (!cur || s.timestamp < cur.timestamp)
50
+ earliest.set(s.filePath, s);
51
+ }
52
+ return [...earliest.values()].sort((a, b) => a.timestamp - b.timestamp);
53
+ }
54
+ /**
55
+ * For turn-level "/undo": the snapshots needed to revert just the MOST RECENT
56
+ * conversation turn (one user message = one turn, see SessionState.turnSeq).
57
+ *
58
+ * The latest turn = the greatest `turnSeq` present. A turn boundary is defined
59
+ * by turnSeq, NOT wall-clock — a clock skew must not move the boundary. Within
60
+ * that turn, each touched file is restored to its EARLIEST snapshot of the turn
61
+ * (its content before the *first* edit that turn), so re-editing a file twice
62
+ * in one turn still reverts to the pre-turn baseline — and edits from *earlier*
63
+ * turns are left intact (that's the whole point vs. earliestSnapshotsPerFile,
64
+ * which spans the whole session).
65
+ *
66
+ * Backward compatibility: snapshots written before this feature have no
67
+ * `turnSeq`. They share the `undefined` bucket. `undefined` sorts as the
68
+ * smallest "turn", so any real (tagged) turn supersedes the legacy bucket; a
69
+ * history that is ALL-legacy degrades to whole-session behaviour (each file's
70
+ * earliest) rather than throwing.
71
+ *
72
+ * Returns one snapshot per file, ordered by that file's first-edit time within
73
+ * the turn (oldest first) so a preview reads chronologically. Empty → [].
74
+ */
75
+ export function latestTurnUndoTargets(snapshots) {
76
+ if (snapshots.length === 0)
77
+ return [];
78
+ // Find the latest turn, SKIPPING undone turns: once a turn is undone (marked,
79
+ // not deleted) its snapshots stay on disk for redo but must not be re-selected
80
+ // by undo — so the next /undo peels the prior live turn ("onion" behaviour).
81
+ // undefined (legacy) is treated as the smallest turn so it loses to any tagged
82
+ // turn; a key of `-Infinity` models that ordering.
83
+ const turnKey = (s) => s.turnSeq ?? -Infinity;
84
+ let maxTurn = -Infinity;
85
+ let found = false;
86
+ for (const s of snapshots) {
87
+ if (s.undone)
88
+ continue;
89
+ const k = turnKey(s);
90
+ if (k > maxTurn)
91
+ maxTurn = k;
92
+ found = true;
93
+ }
94
+ if (!found)
95
+ return [];
96
+ // Within the latest live turn, each file's earliest snapshot = its pre-turn
97
+ // state. Undone snapshots are skipped here too (defensive: they share neither
98
+ // the selected turn nor the baseline).
99
+ const earliest = new Map();
100
+ for (const s of snapshots) {
101
+ if (s.undone)
102
+ continue;
103
+ if (turnKey(s) !== maxTurn)
104
+ continue;
105
+ const cur = earliest.get(s.filePath);
106
+ // strict `<` so the first-seen entry wins on a same-ms tie (earliest).
107
+ if (!cur || s.timestamp < cur.timestamp)
108
+ earliest.set(s.filePath, s);
109
+ }
110
+ return [...earliest.values()].sort((a, b) => a.timestamp - b.timestamp);
111
+ }
112
+ /**
113
+ * For "/redo": the RedoRecord[] of the MOST RECENT undone turn — the turn that
114
+ * `redoLatestTurn` should re-apply — but ONLY when that turn is still the latest
115
+ * undone state (i.e. nothing newer happened since the undo). Otherwise [].
116
+ *
117
+ * Rule: let R = the greatest turnSeq present in `redoRecords` (a RedoRecord
118
+ * exists only for a turn that was undone and not yet redone). R is redoable iff
119
+ * NO snapshot belongs to a strictly newer LIVE (non-undone) turn — a fresh edit
120
+ * after the undo supersedes the redo and invalidates it (spec: "新轮使 redo
121
+ * 失效"). Created-only turns have no pre-turn snapshot, so the RedoRecord itself
122
+ * is the sole evidence and is honoured as long as nothing newer superseded it.
123
+ *
124
+ * Empty `redoRecords` → []. fs-free so /redo's target decision is unit-testable.
125
+ */
126
+ export function latestRedoTargets(redoRecords, snapshots) {
127
+ if (redoRecords.length === 0)
128
+ return [];
129
+ let maxRedoTurn = -Infinity;
130
+ for (const r of redoRecords) {
131
+ if (r.turnSeq > maxRedoTurn)
132
+ maxRedoTurn = r.turnSeq;
133
+ }
134
+ // If any LIVE snapshot belongs to a strictly newer turn, the redo turn is no
135
+ // longer the latest undone state → not redoable.
136
+ for (const s of snapshots) {
137
+ if (s.undone)
138
+ continue;
139
+ const k = s.turnSeq ?? -Infinity;
140
+ if (k > maxRedoTurn)
141
+ return [];
142
+ }
143
+ return redoRecords.filter((r) => r.turnSeq === maxRedoTurn);
144
+ }