one-code-extension 0.4.1 → 0.6.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 (439) hide show
  1. package/README.md +16 -5
  2. package/agents/general-purpose.md +2 -2
  3. package/docs/guide/README.md +2 -1
  4. package/docs/guide/bring-your-claude-code-setup.md +60 -9
  5. package/docs/guide/configuration.md +48 -14
  6. package/docs/guide/differences-from-claude-code.md +40 -28
  7. package/docs/guide/doctor.md +20 -13
  8. package/docs/guide/hooks.md +18 -3
  9. package/docs/guide/installation.md +26 -4
  10. package/docs/guide/permissions-modes-and-auto-mode.md +225 -49
  11. package/docs/guide/providers-and-models.md +137 -54
  12. package/docs/guide/reference.md +27 -15
  13. package/docs/guide/sessions-and-context.md +69 -16
  14. package/docs/guide/skills-plugins-and-mcp.md +57 -20
  15. package/docs/guide/subagents-and-workflows.md +95 -29
  16. package/docs/guide/tasks-and-background-work.md +101 -26
  17. package/docs/guide/terminal-interface.md +24 -12
  18. package/docs/guide/tools.md +46 -5
  19. package/docs/guide/troubleshooting.md +13 -9
  20. package/docs/guide/windows.md +58 -31
  21. package/extensions/artifacts/gallery.ts +227 -0
  22. package/extensions/artifacts/index.ts +288 -0
  23. package/extensions/artifacts/store.ts +570 -0
  24. package/extensions/artifacts/viewer.ts +199 -0
  25. package/extensions/ask-user/description.ts +67 -0
  26. package/extensions/ask-user/dialogs.ts +80 -0
  27. package/extensions/ask-user/index.ts +37 -33
  28. package/extensions/ask-user/questions.ts +32 -7
  29. package/extensions/ask-user/widget.ts +45 -25
  30. package/extensions/auto-mode/actions.ts +6 -0
  31. package/extensions/auto-mode/cache.ts +88 -0
  32. package/extensions/auto-mode/classifier-prompt.ts +5 -4
  33. package/extensions/auto-mode/classifier.ts +290 -65
  34. package/extensions/auto-mode/config.ts +72 -20
  35. package/extensions/auto-mode/decision-log.ts +2 -0
  36. package/extensions/auto-mode/defaults.ts +1 -1
  37. package/extensions/auto-mode/denials.ts +124 -0
  38. package/extensions/auto-mode/git-checkout-programs.ts +187 -0
  39. package/extensions/auto-mode/git-status-meta.ts +159 -0
  40. package/extensions/auto-mode/history.ts +189 -0
  41. package/extensions/auto-mode/instructions.ts +58 -52
  42. package/extensions/auto-mode/model-select.ts +159 -134
  43. package/extensions/auto-mode/paths.ts +57 -5
  44. package/extensions/auto-mode/pause.ts +57 -2
  45. package/extensions/auto-mode/prompt.ts +37 -21
  46. package/extensions/auto-mode/read-only-options.ts +724 -0
  47. package/extensions/auto-mode/resolved-paths-meta.ts +133 -0
  48. package/extensions/auto-mode/rules.ts +18 -1
  49. package/extensions/auto-mode/safety-floor.ts +361 -18
  50. package/extensions/auto-mode/setup-run.ts +20 -14
  51. package/extensions/auto-mode/shell-analysis.ts +764 -490
  52. package/extensions/auto-mode/shell-parse.ts +885 -0
  53. package/extensions/auto-mode/system-note.ts +33 -0
  54. package/extensions/auto-mode/transcript.ts +80 -51
  55. package/extensions/background/cron.ts +642 -0
  56. package/extensions/background/index.ts +614 -248
  57. package/extensions/background/loop-fire.ts +173 -0
  58. package/extensions/background/loop-preamble.ts +59 -0
  59. package/extensions/background/loop-skill.ts +163 -0
  60. package/extensions/background/monitor-batch.ts +53 -0
  61. package/extensions/background/noop-fold.ts +128 -0
  62. package/extensions/background/switch-guard.ts +56 -0
  63. package/extensions/background/wakeup.ts +178 -55
  64. package/extensions/bash/background.ts +53 -17
  65. package/extensions/bash/child.ts +7 -0
  66. package/extensions/bash/description.ts +188 -0
  67. package/extensions/bash/guards.ts +15 -67
  68. package/extensions/bash/index.ts +66 -35
  69. package/extensions/branding/compaction-keep.ts +63 -0
  70. package/extensions/branding/declined.ts +22 -0
  71. package/extensions/branding/index.ts +133 -9
  72. package/extensions/branding/prompt-editor.ts +53 -6
  73. package/extensions/branding/queued-edit.ts +31 -0
  74. package/extensions/branding/replaced-builtins.ts +68 -0
  75. package/extensions/branding/startup.ts +33 -71
  76. package/extensions/btw/index.ts +250 -78
  77. package/extensions/btw/panel.ts +228 -100
  78. package/extensions/btw/prompt.ts +41 -4
  79. package/extensions/cache-marks/index.ts +42 -0
  80. package/extensions/claude-compat/index.ts +7 -11
  81. package/extensions/claude-context/index.ts +436 -63
  82. package/extensions/compaction/cut.ts +157 -0
  83. package/extensions/compaction/index.ts +219 -75
  84. package/extensions/compaction/kept-thinking.ts +62 -0
  85. package/extensions/compaction/prompt.ts +15 -12
  86. package/extensions/context-budget/budget.ts +16 -2
  87. package/extensions/context-budget/index.ts +35 -15
  88. package/extensions/context-management/index.ts +36 -28
  89. package/extensions/doctor/build.ts +59 -8
  90. package/extensions/doctor/builtins.ts +84 -0
  91. package/extensions/doctor/cli.ts +7 -1
  92. package/extensions/doctor/compat.ts +88 -23
  93. package/extensions/doctor/dependencies.ts +41 -5
  94. package/extensions/doctor/fix-prompt.ts +9 -2
  95. package/extensions/doctor/index.ts +102 -58
  96. package/extensions/doctor/models.ts +102 -63
  97. package/extensions/doctor/presets.ts +42 -27
  98. package/extensions/doctor/report.ts +19 -2
  99. package/extensions/doctor/viewer.ts +49 -27
  100. package/extensions/effort/index.ts +141 -25
  101. package/extensions/effort/settings.ts +21 -0
  102. package/extensions/effort/slider.ts +24 -20
  103. package/extensions/exit/index.ts +32 -1
  104. package/extensions/file-tracker/index.ts +420 -42
  105. package/extensions/file-tracker/replay.ts +137 -24
  106. package/extensions/file-tracker/restore.ts +66 -0
  107. package/extensions/file-tracker/results.ts +66 -0
  108. package/extensions/file-tracker/shell-reads.ts +211 -0
  109. package/extensions/file-tracker/tracker.ts +45 -15
  110. package/extensions/footer/footer-line.ts +45 -24
  111. package/extensions/footer/index.ts +47 -2
  112. package/extensions/footer/pr.ts +34 -5
  113. package/extensions/hooks/compaction.ts +7 -0
  114. package/extensions/hooks/executor.ts +32 -30
  115. package/extensions/hooks/index.ts +209 -58
  116. package/extensions/hooks/matcher.ts +3 -0
  117. package/extensions/hooks/prompt-context.ts +59 -0
  118. package/extensions/hooks/protocol.ts +41 -3
  119. package/extensions/hooks/settings.ts +50 -19
  120. package/extensions/hooks/subagent-bridge.ts +2 -0
  121. package/extensions/hooks/trust.ts +26 -12
  122. package/extensions/idle-compact/index.ts +119 -0
  123. package/extensions/idle-compact/policy.ts +143 -0
  124. package/extensions/init/index.ts +11 -10
  125. package/extensions/init/prompt.ts +25 -32
  126. package/extensions/interrupted/index.ts +13 -1
  127. package/extensions/kept-thinking/index.ts +27 -0
  128. package/extensions/ledger/index.ts +46 -0
  129. package/extensions/ledger/wire.ts +154 -0
  130. package/extensions/lib/agent-cron.ts +146 -0
  131. package/extensions/lib/agent-loader.ts +18 -3
  132. package/extensions/lib/agent-view.ts +85 -0
  133. package/extensions/lib/anthropic-auth.ts +98 -0
  134. package/extensions/lib/anthropic-payload.ts +82 -1
  135. package/extensions/lib/anthropic-server-call.ts +230 -0
  136. package/extensions/lib/anthropic-server-tools.ts +485 -0
  137. package/extensions/lib/app-launch.mjs +94 -0
  138. package/extensions/lib/argument-hints.ts +190 -0
  139. package/extensions/lib/atomic-write.ts +6 -2
  140. package/extensions/lib/auto-memory.ts +75 -0
  141. package/extensions/lib/bash-parser.ts +84 -0
  142. package/extensions/lib/btw-fork.ts +93 -0
  143. package/extensions/lib/child-extensions.ts +52 -0
  144. package/extensions/lib/child-writes.ts +45 -0
  145. package/extensions/lib/claude-context.ts +515 -100
  146. package/extensions/lib/claude-external-includes.ts +68 -0
  147. package/extensions/lib/claude-id.ts +22 -0
  148. package/extensions/lib/claude-rules.ts +323 -0
  149. package/extensions/lib/claude-settings.ts +76 -4
  150. package/extensions/lib/compaction-boundary.ts +36 -0
  151. package/extensions/lib/compaction-keep.mjs +83 -0
  152. package/extensions/lib/config-mode.ts +112 -0
  153. package/extensions/lib/consent-dialogs.ts +63 -0
  154. package/extensions/lib/consent-preview.ts +5 -1
  155. package/extensions/lib/consent-stores.ts +48 -0
  156. package/extensions/lib/context-facts.ts +97 -0
  157. package/extensions/lib/covered-messages.ts +23 -0
  158. package/extensions/lib/deferred.ts +359 -25
  159. package/extensions/lib/dock-rows.ts +60 -0
  160. package/extensions/lib/empty-tool-result.ts +27 -0
  161. package/extensions/lib/environment-block.ts +76 -0
  162. package/extensions/lib/fetch-timeout.ts +24 -6
  163. package/extensions/{system-prompt → lib}/git-status.ts +51 -13
  164. package/extensions/lib/git.ts +75 -13
  165. package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
  166. package/extensions/lib/grammars/README.md +2 -0
  167. package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
  168. package/extensions/lib/guide-docs.ts +45 -0
  169. package/extensions/lib/headless-output.ts +46 -0
  170. package/extensions/lib/hook-gate.ts +7 -7
  171. package/extensions/lib/interrupt.ts +20 -4
  172. package/extensions/lib/key-input.ts +455 -0
  173. package/extensions/lib/keys.ts +14 -1
  174. package/extensions/lib/ledger/fold-memo.ts +49 -0
  175. package/extensions/lib/ledger/fold.ts +265 -0
  176. package/extensions/lib/ledger/layout.ts +72 -0
  177. package/extensions/lib/ledger/plan.ts +102 -0
  178. package/extensions/lib/ledger/records.ts +276 -0
  179. package/extensions/lib/ledger/state.ts +47 -0
  180. package/extensions/lib/ledger/validate.ts +141 -0
  181. package/extensions/lib/ledger/wire.ts +632 -0
  182. package/extensions/lib/local-command.ts +71 -19
  183. package/extensions/lib/mcp-overrides.ts +1 -1
  184. package/extensions/lib/mcp-share.ts +46 -0
  185. package/extensions/lib/memory-content.ts +146 -0
  186. package/extensions/lib/memory-index.ts +18 -0
  187. package/extensions/lib/memory.ts +134 -123
  188. package/extensions/lib/model-catalog/huggingface.json +178 -0
  189. package/extensions/lib/model-catalog/models-dev.json +18533 -0
  190. package/extensions/lib/model-catalog/openrouter.json +5050 -0
  191. package/extensions/lib/model-catalog-data.ts +513 -0
  192. package/extensions/lib/model-catalog.ts +462 -0
  193. package/extensions/lib/model-choice-warnings.ts +137 -0
  194. package/extensions/lib/model-pairings.ts +152 -0
  195. package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
  196. package/extensions/lib/model-policy.ts +112 -15
  197. package/extensions/lib/model-tier.ts +283 -355
  198. package/extensions/lib/model-unusable.ts +1 -1
  199. package/extensions/lib/newer-model.ts +100 -0
  200. package/extensions/lib/notices.ts +87 -0
  201. package/extensions/lib/notifications.ts +357 -73
  202. package/extensions/lib/one-code-settings.ts +74 -8
  203. package/extensions/lib/open-browser.ts +63 -0
  204. package/extensions/lib/openrouter-generation.ts +94 -0
  205. package/extensions/lib/operation-deadline.ts +30 -0
  206. package/extensions/lib/original-command.ts +4 -4
  207. package/extensions/lib/package-version.ts +1 -1
  208. package/extensions/lib/panel-focus.ts +134 -0
  209. package/extensions/lib/paths.ts +17 -2
  210. package/extensions/lib/permission-gate.ts +72 -6
  211. package/extensions/lib/persisted-output.ts +60 -8
  212. package/extensions/lib/pi-ai-estimate.ts +19 -3
  213. package/extensions/lib/pi-version.ts +38 -2
  214. package/extensions/lib/plan-mode-channels.ts +42 -5
  215. package/extensions/lib/plugins.ts +26 -17
  216. package/extensions/lib/powershell-parse-script.ts +208 -0
  217. package/extensions/lib/powershell-parser.ts +344 -0
  218. package/extensions/lib/previous-mark.ts +101 -0
  219. package/extensions/lib/process-state.ts +48 -0
  220. package/extensions/lib/process-tree.ts +132 -8
  221. package/extensions/lib/prompt-options.ts +82 -0
  222. package/extensions/lib/queued-delivery.ts +44 -0
  223. package/extensions/lib/reminders.ts +370 -358
  224. package/extensions/lib/replaced-builtins.mjs +199 -0
  225. package/extensions/lib/replay-call.ts +70 -0
  226. package/extensions/lib/request-replay.ts +547 -0
  227. package/extensions/lib/scratchpad.ts +81 -14
  228. package/extensions/lib/send-now-channel.ts +7 -0
  229. package/extensions/lib/session-model-tier.ts +51 -0
  230. package/extensions/lib/session-work.ts +64 -0
  231. package/extensions/lib/settings-channels.ts +4 -4
  232. package/extensions/lib/shell-exit-semantics.ts +170 -0
  233. package/extensions/lib/shell-quote.ts +11 -0
  234. package/extensions/lib/shell-result.ts +70 -0
  235. package/extensions/lib/shell-spawn.ts +70 -6
  236. package/extensions/lib/shell-tool.ts +75 -25
  237. package/extensions/lib/side-call-cache.ts +124 -0
  238. package/extensions/lib/side-call-run.ts +96 -0
  239. package/extensions/lib/side-call-usage.ts +38 -0
  240. package/extensions/lib/side-call.ts +24 -1
  241. package/extensions/lib/single-flight.ts +18 -0
  242. package/extensions/lib/skill-body.ts +42 -0
  243. package/extensions/lib/skill-scan.ts +63 -24
  244. package/extensions/lib/spill-file.ts +140 -0
  245. package/extensions/lib/subagent-handback.ts +112 -0
  246. package/extensions/lib/system-role.ts +156 -0
  247. package/extensions/lib/terminal-text.ts +114 -0
  248. package/extensions/lib/text-width.ts +47 -16
  249. package/extensions/lib/timer-ops.ts +20 -0
  250. package/extensions/lib/tool-additions.ts +129 -0
  251. package/extensions/lib/tool-call-corruption.ts +134 -0
  252. package/extensions/lib/tool-path.ts +66 -0
  253. package/extensions/lib/tool-variants.ts +69 -0
  254. package/extensions/lib/tui-render.ts +255 -27
  255. package/extensions/lib/turn-budget-layout.ts +159 -0
  256. package/extensions/lib/update-check.mjs +15 -2
  257. package/extensions/lib/usage-bus.ts +1 -1
  258. package/extensions/lib/user-denial.ts +38 -0
  259. package/extensions/lib/workspace-channel.ts +16 -0
  260. package/extensions/lib/worktree-channel.ts +31 -3
  261. package/extensions/lib/worktree-isolation.ts +40 -10
  262. package/extensions/lsp/client.ts +8 -9
  263. package/extensions/lsp/index.ts +76 -12
  264. package/extensions/lsp/keep-alive.ts +1 -1
  265. package/extensions/lsp/trust.ts +119 -0
  266. package/extensions/lsp/watcher.ts +1 -1
  267. package/extensions/mcp/announce.ts +90 -43
  268. package/extensions/mcp/client.ts +161 -48
  269. package/extensions/mcp/config.ts +132 -8
  270. package/extensions/mcp/discovery.ts +19 -0
  271. package/extensions/mcp/index.ts +220 -121
  272. package/extensions/mcp/oauth/browser.ts +38 -17
  273. package/extensions/mcp/oauth/flow.ts +25 -17
  274. package/extensions/mcp/oauth/provider.ts +14 -8
  275. package/extensions/mcp/oauth/store.ts +34 -16
  276. package/extensions/mcp/panel/keys.ts +12 -11
  277. package/extensions/mcp/panel/render.ts +9 -6
  278. package/extensions/mcp/schema.ts +104 -5
  279. package/extensions/mcp/trust.ts +164 -73
  280. package/extensions/memory/entries.ts +23 -14
  281. package/extensions/memory/index.ts +134 -28
  282. package/extensions/memory/open-external.ts +33 -18
  283. package/extensions/memory/panel.ts +135 -26
  284. package/extensions/model-default/index.ts +86 -19
  285. package/extensions/newer-model/index.ts +31 -0
  286. package/extensions/notebook/description.ts +18 -0
  287. package/extensions/notebook/index.ts +6 -5
  288. package/extensions/notebook/notebook.ts +1 -1
  289. package/extensions/permissions/index.ts +1426 -398
  290. package/extensions/permissions/matcher.ts +425 -74
  291. package/extensions/permissions/modes.ts +11 -4
  292. package/extensions/permissions/panel/host.ts +130 -0
  293. package/extensions/permissions/panel/keys.ts +48 -0
  294. package/extensions/permissions/panel/render.ts +450 -0
  295. package/extensions/permissions/panel/state.ts +550 -0
  296. package/extensions/permissions/powershell-cmdlets.ts +198 -0
  297. package/extensions/permissions/powershell-paths.ts +197 -0
  298. package/extensions/permissions/powershell-rules.ts +207 -280
  299. package/extensions/permissions/powershell-tree.ts +557 -0
  300. package/extensions/permissions/project-trust.ts +27 -11
  301. package/extensions/permissions/protected-paths.ts +14 -2
  302. package/extensions/permissions/session-grant.ts +6 -0
  303. package/extensions/permissions/settings.ts +166 -31
  304. package/extensions/permissions/subagent-gate.ts +25 -0
  305. package/extensions/permissions/workspace.ts +78 -0
  306. package/extensions/plan-mode/index.ts +276 -92
  307. package/extensions/plan-mode/reminder.ts +89 -21
  308. package/extensions/plan-mode/run-state.ts +26 -0
  309. package/extensions/plan-mode/texts.ts +177 -0
  310. package/extensions/plan-mode/viewer.ts +92 -27
  311. package/extensions/plugins/counts.ts +3 -3
  312. package/extensions/plugins/index.ts +64 -46
  313. package/extensions/plugins/install/install.ts +16 -0
  314. package/extensions/plugins/marketplace/sync.ts +9 -3
  315. package/extensions/plugins/marketplace/types.ts +3 -1
  316. package/extensions/plugins/panel/keys.ts +21 -24
  317. package/extensions/plugins/panel/render.ts +12 -6
  318. package/extensions/plugins/shell-expand.ts +107 -0
  319. package/extensions/plugins/template.ts +32 -15
  320. package/extensions/powershell/child.ts +7 -0
  321. package/extensions/powershell/description.ts +38 -6
  322. package/extensions/powershell/guards.ts +1 -1
  323. package/extensions/powershell/index.ts +14 -7
  324. package/extensions/recap/index.ts +73 -71
  325. package/extensions/recap/prompt.ts +2 -2
  326. package/extensions/recap/scheduler.ts +3 -5
  327. package/extensions/search-tools/grep-merge.ts +39 -0
  328. package/extensions/search-tools/index.ts +0 -0
  329. package/extensions/send-now/chord.ts +62 -0
  330. package/extensions/send-now/index.ts +133 -0
  331. package/extensions/session-title/index.ts +3 -0
  332. package/extensions/session-title/title.ts +4 -1
  333. package/extensions/skill/announce.ts +30 -0
  334. package/extensions/skill/code-review.ts +97 -0
  335. package/extensions/skill/index.ts +469 -132
  336. package/extensions/skill/invoke.ts +118 -75
  337. package/extensions/skill/listing.ts +133 -9
  338. package/extensions/skill/panel/keys.ts +15 -16
  339. package/extensions/skill/panel/render.ts +5 -2
  340. package/extensions/skill/panel/state.ts +12 -1
  341. package/extensions/subagents/agent-args.ts +39 -0
  342. package/extensions/subagents/agent-description.ts +132 -0
  343. package/extensions/subagents/agents.ts +146 -31
  344. package/extensions/subagents/default-model.ts +2 -3
  345. package/extensions/subagents/delegation-steer.ts +4 -4
  346. package/extensions/subagents/fork-cache.ts +82 -0
  347. package/extensions/subagents/guide-agent.ts +189 -0
  348. package/extensions/subagents/hand-back-review.ts +3 -0
  349. package/extensions/subagents/index.ts +1594 -852
  350. package/extensions/subagents/live-runs.ts +26 -6
  351. package/extensions/subagents/model-select.ts +38 -25
  352. package/extensions/subagents/outcome.ts +5 -2
  353. package/extensions/subagents/panel-keys.ts +73 -31
  354. package/extensions/subagents/panel-render.ts +120 -96
  355. package/extensions/subagents/panel-widget.ts +60 -89
  356. package/extensions/subagents/parent-mode.ts +50 -0
  357. package/extensions/subagents/pending-claim.ts +18 -1
  358. package/extensions/subagents/runner.ts +135 -80
  359. package/extensions/subagents/runs.ts +44 -5
  360. package/extensions/subagents/send-to-main-tool.ts +1 -1
  361. package/extensions/subagents/session-turns.ts +32 -8
  362. package/extensions/subagents/shell-panel.ts +36 -178
  363. package/extensions/subagents/tasks-dialog.ts +170 -0
  364. package/extensions/subagents/viewport-mirror.ts +95 -0
  365. package/extensions/subagents/worktree.ts +57 -13
  366. package/extensions/system-prompt/environment.ts +19 -19
  367. package/extensions/system-prompt/idle-turn.ts +65 -0
  368. package/extensions/system-prompt/index.ts +165 -47
  369. package/extensions/system-prompt/template.ts +34 -51
  370. package/extensions/system-prompt/tiers/common.ts +23 -64
  371. package/extensions/system-prompt/tiers/frontier.ts +32 -11
  372. package/extensions/system-prompt/tiers/low.ts +67 -45
  373. package/extensions/system-prompt/tiers/mid.ts +96 -38
  374. package/extensions/system-reminder/index.ts +204 -70
  375. package/extensions/tasks/descriptions.ts +190 -0
  376. package/extensions/tasks/index.ts +120 -41
  377. package/extensions/tasks/store.ts +83 -16
  378. package/extensions/tool-call-corruption/child.ts +7 -0
  379. package/extensions/tool-call-corruption/index.ts +237 -0
  380. package/extensions/tool-search/index.ts +366 -144
  381. package/extensions/tool-style/index.ts +68 -10
  382. package/extensions/tool-style/write-description.ts +40 -0
  383. package/extensions/tool-style/write-render.ts +237 -0
  384. package/extensions/turn-duration/index.ts +7 -3
  385. package/extensions/turn-duration/line.ts +47 -4
  386. package/extensions/web/backends.ts +6 -9
  387. package/extensions/web/description.ts +50 -0
  388. package/extensions/web/index.ts +118 -53
  389. package/extensions/web/native-call.ts +15 -0
  390. package/extensions/web/native-results.ts +41 -0
  391. package/extensions/web-fetch/description.ts +42 -0
  392. package/extensions/web-fetch/extract.ts +53 -6
  393. package/extensions/web-fetch/index.ts +125 -76
  394. package/extensions/web-fetch/page-content.ts +19 -0
  395. package/extensions/web-fetch/response.ts +49 -0
  396. package/extensions/web-fetch/summarize.ts +9 -10
  397. package/extensions/workflow/agent-session.ts +108 -18
  398. package/extensions/workflow/args.ts +24 -0
  399. package/extensions/workflow/description.ts +67 -0
  400. package/extensions/workflow/globals.ts +17 -3
  401. package/extensions/workflow/index.ts +157 -162
  402. package/extensions/workflow/records.ts +1 -0
  403. package/extensions/workflow/run-manager.ts +33 -6
  404. package/extensions/workflow/saved-workflows.ts +3 -2
  405. package/extensions/workflow/types.ts +22 -1
  406. package/extensions/workflow/viewer.ts +54 -24
  407. package/extensions/workflow/widget.ts +20 -0
  408. package/extensions/worktree/descriptions.ts +91 -0
  409. package/extensions/worktree/guards.ts +316 -54
  410. package/extensions/worktree/index.ts +162 -44
  411. package/extensions/worktree/powershell-guards.ts +167 -0
  412. package/extensions/worktree/rewrite.ts +14 -16
  413. package/package.json +21 -7
  414. package/skills/artifact-design/SKILL.md +85 -0
  415. package/skills/code-review/SKILL.md +20 -20
  416. package/skills/code-review/cells/arguments-and-flags.md +19 -0
  417. package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
  418. package/skills/code-review/cells/high-8-inline.md +122 -0
  419. package/skills/code-review/cells/low-1-pass-max4.md +27 -0
  420. package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
  421. package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
  422. package/skills/code-review/cells/medium-3x5-verify.md +132 -0
  423. package/skills/code-review/cells/minimal-prompt.md +32 -0
  424. package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
  425. package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
  426. package/skills/fewer-permission-prompts/SKILL.md +1 -1
  427. package/skills/loop/SKILL.md +11 -0
  428. package/skills/security-review/SKILL.md +2 -5
  429. package/skills/simplify/SKILL.md +7 -5
  430. package/skills/workflow-authoring/SKILL.md +143 -0
  431. package/themes/onecode-light.json +1 -1
  432. package/themes/onecode.json +1 -1
  433. package/types/app.d.ts +58 -0
  434. package/types/community-packages.d.ts +26 -1
  435. package/extensions/auto-mode/recoverability.ts +0 -229
  436. package/extensions/lib/capability-index.ts +0 -466
  437. package/extensions/lib/model-facts.generated.json +0 -2675
  438. package/extensions/lib/model-facts.ts +0 -162
  439. package/extensions/tool-search/announce.ts +0 -42
@@ -1,14 +1,32 @@
1
1
  /**
2
- * fetch with a hard timeout (pure, no pi imports) — the AbortController +
3
- * unref'd timer boilerplate the plugin-marketplace fetches share.
2
+ * fetch and read the response under one hard deadline (pure, no pi imports):
3
+ * the timer runs until `read` settles, so a body that stalls after the headers
4
+ * is cut off too, whatever `fetchImpl` does with the signal. `signal` aborts
5
+ * early (a session shutting down). Shared by the plugin-marketplace fetches and
6
+ * the model-catalog refresh; `fetchImpl` is the test seam, given the same signal.
4
7
  */
5
-
6
- export async function fetchWithTimeout(url: string, timeoutMs: number, init?: RequestInit): Promise<Response> {
8
+ export async function fetchWithTimeout<T>(
9
+ url: string,
10
+ timeoutMs: number,
11
+ read: (response: Response) => Promise<T>,
12
+ options: { init?: RequestInit; signal?: AbortSignal; fetchImpl?: typeof fetch } = {},
13
+ ): Promise<T> {
7
14
  const controller = new AbortController();
8
- const timer = setTimeout(() => controller.abort(), timeoutMs);
15
+ // Parsed up front: a malformed URL fails the call, never the timer callback.
16
+ const host = new URL(url).host;
17
+ const timer = setTimeout(() => controller.abort(new Error(`${host} did not answer within ${timeoutMs} ms`)), timeoutMs);
9
18
  timer.unref?.();
19
+ const signal = options.signal ? AbortSignal.any([controller.signal, options.signal]) : controller.signal;
20
+ const aborted = new Promise<never>((_, reject) => {
21
+ const fail = () => reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason ?? "aborted")));
22
+ if (signal.aborted) fail();
23
+ else signal.addEventListener("abort", fail, { once: true });
24
+ });
10
25
  try {
11
- return await fetch(url, { ...init, signal: controller.signal });
26
+ const work = (async () => read(await (options.fetchImpl ?? fetch)(url, { ...options.init, signal })))();
27
+ // Lost to the deadline, the work may still reject later: not unhandled.
28
+ work.catch(() => {});
29
+ return await Promise.race([work, aborted]);
12
30
  } finally {
13
31
  clearTimeout(timer);
14
32
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
- * Claude Code's `gitStatus:` block — appended to the very end of the system
3
- * prompt in a git repo, identical across model tiers, and a one-time snapshot:
2
+ * Claude Code's `# gitStatus` section — part of the context reminder on the
3
+ * first user message (lib/claude-context.ts buildContextBlock) in a git repo,
4
+ * identical across model tiers, and a one-time snapshot:
4
5
  * CC states it is "the git status at the start of the conversation" and "will not
5
6
  * update during the conversation", so it is computed once per session and frozen.
6
7
  *
@@ -11,11 +12,18 @@
11
12
  * the status and the log are whole-output `.trim()`ed — which is why a leading
12
13
  * `??`/` M` porcelain line renders flush-left in the capture.
13
14
  *
15
+ * Bounded the way Claude Code bounds it: the status is clipped at 2,000
16
+ * characters with Claude Code's note, a clean tree reads `(clean)`, an empty
17
+ * user name drops the `Git user:` line, and a `git status` that fails or hangs
18
+ * past the timeout drops the whole block, as Claude Code does on a failed
19
+ * snapshot.
20
+ *
14
21
  * The module is pure apart from `defaultRunner`; `collectGitStatus` takes an
15
22
  * injectable runner so the git commands can be faked in unit tests.
16
23
  */
17
24
 
18
25
  import { execFileSync } from "node:child_process";
26
+ import { HARNESS_GIT_CONFIG } from "./git.ts";
19
27
 
20
28
  export interface GitSnapshot {
21
29
  branch: string;
@@ -28,9 +36,27 @@ export interface GitSnapshot {
28
36
  }
29
37
 
30
38
  const HEADER =
31
- "gitStatus: This is the git status at the start of the conversation. " +
39
+ "# gitStatus\nThis is the git status at the start of the conversation. " +
32
40
  "Note that this status is a snapshot in time, and will not update during the conversation.";
33
41
 
42
+ /** The main session's system-prompt extension claims the snapshot on this channel; a child session never does. */
43
+ export const GIT_SNAPSHOT_OWNER_CHANNEL = "one-code:git-snapshot-owner";
44
+
45
+ /** Claude Code's limit on the status inside the block, in characters. */
46
+ export const GIT_STATUS_MAX_CHARS = 2000;
47
+
48
+ /** Each git command's time limit: a hung `git status` (a network filesystem) must not block the first turn. */
49
+ export const GIT_TIMEOUT_MS = 10_000;
50
+
51
+ /**
52
+ * The status as the block carries it: cut at 2,000 characters with Claude
53
+ * Code's note, which names the shell tool the model has.
54
+ */
55
+ export function clipGitStatus(status: string, shellTool = "bash"): string {
56
+ if (status.length <= GIT_STATUS_MAX_CHARS) return status;
57
+ return `${status.substring(0, GIT_STATUS_MAX_CHARS)}\n... (truncated because it exceeds 2k characters. If you need more information, run "git status" using ${shellTool})`;
58
+ }
59
+
34
60
  /** Assemble CC's block from a snapshot. Pure; byte-exact against the captures. */
35
61
  export function formatGitStatus(s: GitSnapshot): string {
36
62
  return [
@@ -40,10 +66,9 @@ export function formatGitStatus(s: GitSnapshot): string {
40
66
  "",
41
67
  `Main branch (you will usually use this for PRs): ${s.mainBranch}`,
42
68
  "",
43
- `Git user: ${s.user}`,
44
- "",
69
+ ...(s.user ? [`Git user: ${s.user}`, ""] : []),
45
70
  "Status:",
46
- s.status,
71
+ s.status || "(clean)",
47
72
  "",
48
73
  "Recent commits:",
49
74
  s.commits,
@@ -56,7 +81,14 @@ export type GitRunner = (args: string[]) => string | null;
56
81
  function defaultRunner(cwd: string): GitRunner {
57
82
  return (args) => {
58
83
  try {
59
- return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
84
+ return execFileSync("git", [...HARNESS_GIT_CONFIG, ...args], {
85
+ cwd,
86
+ encoding: "utf8",
87
+ stdio: ["ignore", "pipe", "ignore"],
88
+ timeout: GIT_TIMEOUT_MS,
89
+ // A status over the 1 MiB default would fail instead of being clipped.
90
+ maxBuffer: 64 * 1024 * 1024,
91
+ }).trim();
60
92
  } catch {
61
93
  return null;
62
94
  }
@@ -75,19 +107,25 @@ function resolveMainBranch(run: GitRunner, branch: string): string {
75
107
 
76
108
  /**
77
109
  * CC's `gitStatus:` block for `cwd`, or null when `cwd` is not inside a git work
78
- * tree. Individual command failures degrade to empty fields rather than dropping
79
- * the whole block, matching CC's per-field resilience.
110
+ * tree or `git status` fails (or times out): an empty status would claim a
111
+ * clean tree. Other command failures degrade to empty fields rather than
112
+ * dropping the whole block. `shellTool` is the tool the clip note tells the
113
+ * model to run `git status` with.
80
114
  */
81
- export function collectGitStatus(cwd: string, runner?: GitRunner): string | null {
115
+ export function collectGitStatus(cwd: string, runner?: GitRunner, shellTool = "bash"): string | null {
82
116
  const run = runner ?? defaultRunner(cwd);
83
117
  if (run(["rev-parse", "--is-inside-work-tree"]) !== "true") return null;
84
118
 
85
119
  const branch = run(["branch", "--show-current"]) ?? "";
120
+ const mainBranch = resolveMainBranch(run, branch);
121
+ const user = run(["config", "user.name"]) ?? "";
122
+ const status = run(["status", "--porcelain"]);
123
+ if (status === null) return null;
86
124
  return formatGitStatus({
87
125
  branch,
88
- mainBranch: resolveMainBranch(run, branch),
89
- user: run(["config", "user.name"]) ?? "",
90
- status: run(["status", "--porcelain"]) ?? "",
126
+ mainBranch,
127
+ user,
128
+ status: clipGitStatus(status, shellTool),
91
129
  commits: run(["log", "-5", "--format=%h %s"]) ?? "",
92
130
  });
93
131
  }
@@ -1,24 +1,56 @@
1
1
  /** Shared git helpers — pure functions only (safe to import across extensions). */
2
2
 
3
+ /**
4
+ * Global options for every git command the harness runs on its own (startup
5
+ * git status, the classifier's git status line, consent checks, worktree
6
+ * bookkeeping). git honours the checkout's own `.git/config`, and
7
+ * `core.fsmonitor` there names a
8
+ * program that `status` and other index reads run — so a directory whose
9
+ * `.git` came from an archive or a copied tree would run it the moment One
10
+ * Code starts in it, before any prompt (SECURITY-REVIEW-2026-09-23 M5).
11
+ * `--no-optional-locks` stops `status` rewriting the index, which would run the
12
+ * checkout's `post-index-change` hook. The model's own git commands are judged
13
+ * by the permission gate instead (auto-mode/git-checkout-programs.ts).
14
+ */
15
+ export const HARNESS_GIT_CONFIG: readonly string[] = ["-c", "core.fsmonitor=false", "--no-optional-locks"];
16
+
3
17
  import { execFile } from "node:child_process";
4
18
  import { existsSync, readFileSync, statSync } from "node:fs";
19
+ import { devNull } from "node:os";
5
20
  import { basename, dirname, isAbsolute, join, resolve } from "node:path";
21
+ import { comparablePath, tryRealpath } from "./paths.ts";
22
+
23
+ /** Harness worktree bookkeeping must not execute repository hooks before the tool permission gate. */
24
+ export const HARNESS_GIT_NO_HOOKS: readonly string[] = ["-c", `core.hooksPath=${devNull}`];
6
25
 
7
26
  /**
8
- * Whether the checkout at `cwd` has no uncommitted or untracked changes, or
9
- * undefined when git could not say (not a repo, git missing). One short
10
- * `git status --porcelain`, asynchronous so the tool_call hook it runs from
11
- * does not stall the event loop; used for the classifier transcript's
12
- * ground-truth line after a `git status` call (permissions/index.ts).
27
+ * `git status` output for `cwd` with the given arguments, or undefined when
28
+ * git could not say (not a repo, git missing, timeout). Asynchronous so the
29
+ * tool_call hook it runs from does not stall the event loop; used for the
30
+ * classifier transcript's gitStatus line (auto-mode/git-status-meta.ts).
13
31
  */
14
- export function gitStatusClean(cwd: string): Promise<boolean | undefined> {
32
+ export function gitStatusOutput(cwd: string, args: readonly string[]): Promise<string | undefined> {
15
33
  return new Promise((resolvePromise) => {
16
- execFile("git", ["status", "--porcelain"], { cwd, encoding: "utf8", timeout: 5_000, windowsHide: true }, (error, stdout) => {
17
- resolvePromise(error ? undefined : stdout.trim().length === 0);
34
+ execFile("git", [...HARNESS_GIT_CONFIG, ...args], { cwd, encoding: "utf8", timeout: 5_000, windowsHide: true }, (error, stdout) => {
35
+ resolvePromise(error ? undefined : stdout);
18
36
  });
19
37
  });
20
38
  }
21
39
 
40
+ /**
41
+ * Every worktree of the repository `cwd` belongs to (`git worktree list`),
42
+ * main checkout included, in native resolved form; undefined when git could
43
+ * not list them. The worktree guards refuse git aimed at any of them.
44
+ */
45
+ export async function repositoryWorktrees(cwd: string): Promise<string[] | undefined> {
46
+ const out = await gitStatusOutput(cwd, ["worktree", "list", "--porcelain"]);
47
+ if (out === undefined) return undefined;
48
+ return out
49
+ .split("\n")
50
+ .filter((line) => line.startsWith("worktree "))
51
+ .map((line) => resolve(line.slice("worktree ".length)));
52
+ }
53
+
22
54
  /**
23
55
  * The nearest checkout root at or above `startDir`: the directory holding a
24
56
  * `.git` entry (a directory for a main checkout, a file for a linked worktree
@@ -50,23 +82,53 @@ export function findProjectRoot(startDir: string): string | undefined {
50
82
  return linkedWorktreeMainRoot(root) ?? root;
51
83
  }
52
84
 
53
- /** For a linked worktree root, the main checkout it belongs to; undefined otherwise. Exported for tests. */
54
- export function linkedWorktreeMainRoot(root: string): string | undefined {
85
+ /**
86
+ * The git dir a checkout root's `.git` FILE points at (a linked worktree or a
87
+ * submodule: `gitdir: <path>`), absolute; undefined when `.git` is a directory,
88
+ * missing, or unreadable.
89
+ */
90
+ export function gitdirFileTarget(root: string): string | undefined {
55
91
  const dotGit = join(root, ".git");
56
- let gitdir: string;
57
92
  try {
58
93
  if (!statSync(dotGit).isFile()) return undefined;
59
94
  const match = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync(dotGit, "utf-8"));
60
95
  if (!match) return undefined;
61
- gitdir = match[1];
96
+ // resolve in both cases: git writes `C:/…` on Windows, and callers compare native paths.
97
+ return isAbsolute(match[1]) ? resolve(match[1]) : resolve(root, match[1]);
62
98
  } catch {
63
99
  return undefined;
64
100
  }
65
- const absolute = isAbsolute(gitdir) ? gitdir : resolve(root, gitdir);
101
+ }
102
+
103
+ /**
104
+ * For a linked worktree root, the main checkout it belongs to; undefined
105
+ * otherwise. Exported for tests. The `.git` file alone proves nothing: an
106
+ * archive or copied tree can carry `gitdir: /trusted/.git/worktrees/x` and so
107
+ * borrow that repository's identity (its settings and remembered consent). git
108
+ * records the way back in `<gitdir>/gitdir`, so only a worktree that record
109
+ * names is inherited.
110
+ */
111
+ export function linkedWorktreeMainRoot(root: string): string | undefined {
112
+ const absolute = gitdirFileTarget(root);
113
+ if (!absolute) return undefined;
66
114
  // <main>/.git/worktrees/<name> → <main>. A submodule's gitdir points into
67
115
  // <super>/.git/modules/<name> and is left alone.
68
116
  const worktrees = dirname(absolute);
69
117
  const mainDotGit = dirname(worktrees);
70
118
  if (basename(worktrees) !== "worktrees" || basename(mainDotGit) !== ".git") return undefined;
119
+ if (!pointsBack(absolute, root)) return undefined;
71
120
  return dirname(mainDotGit);
72
121
  }
122
+
123
+ /** Whether the worktree record `gitdir` names `<root>/.git` (absolute, or relative to the record with `worktree.useRelativePaths`). */
124
+ function pointsBack(gitdir: string, root: string): boolean {
125
+ try {
126
+ const recorded = readFileSync(join(gitdir, "gitdir"), "utf-8").trim();
127
+ if (!recorded) return false;
128
+ const target = isAbsolute(recorded) ? resolve(recorded) : resolve(gitdir, recorded);
129
+ const dotGit = join(root, ".git");
130
+ return comparablePath(tryRealpath(target) ?? target) === comparablePath(tryRealpath(dotGit) ?? dotGit);
131
+ } catch {
132
+ return false;
133
+ }
134
+ }
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Max Brunsfeld
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ tree-sitter-bash 0.25.1 release WASM (https://github.com/tree-sitter/tree-sitter-bash), unmodified.
2
+ Loaded by ../bash-parser.ts with web-tree-sitter; bump both together (the grammar ABI must be one the runtime supports).
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Where the documentation the `one-code-guide` agent reads lives on this
3
+ * machine: One Code's own user guide (`docs/guide/`, shipped in the npm
4
+ * package next to `extensions/`) and the running pi's `docs/` and `examples/`
5
+ * (pi's `getDocsPath()`/`getExamplesPath()`, which name the host pi's copies in
6
+ * both install shapes; findings §6).
7
+ *
8
+ * The permissions extension makes these directories readable without a prompt
9
+ * (never writable), so the guide agent can read them on any model tier.
10
+ */
11
+
12
+ import { existsSync } from "node:fs";
13
+ import { dirname, join } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+ import { getDocsPath, getExamplesPath } from "@earendil-works/pi-coding-agent";
16
+
17
+ /** One Code's user guide, `<package>/docs/guide`; its `README.md` is the index. */
18
+ export const ONE_CODE_GUIDE_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "docs", "guide");
19
+
20
+ export interface GuideDocs {
21
+ /** One Code's user guide. */
22
+ guide: string;
23
+ /** The running pi's docs (extensions, packages, settings, …); undefined when not found. */
24
+ piDocs?: string;
25
+ /** The running pi's examples (extensions among them); undefined when not found. */
26
+ piExamples?: string;
27
+ }
28
+
29
+ function existing(read: () => string): string | undefined {
30
+ try {
31
+ const dir = read();
32
+ return existsSync(dir) ? dir : undefined;
33
+ } catch {
34
+ return undefined;
35
+ }
36
+ }
37
+
38
+ export function guideDocs(): GuideDocs {
39
+ return { guide: ONE_CODE_GUIDE_DIR, piDocs: existing(getDocsPath), piExamples: existing(getExamplesPath) };
40
+ }
41
+
42
+ /** The directories that exist, for the permission gate's read-only roots. */
43
+ export function guideDocsDirs(docs: GuideDocs = guideDocs()): string[] {
44
+ return [existsSync(docs.guide) ? docs.guide : undefined, docs.piDocs, docs.piExamples].filter((dir): dir is string => !!dir);
45
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Output from a command in a one-shot run (`-p`, `--mode json`), where
3
+ * `ctx.ui.notify` is a no-op: pi wires no UI context there, so a message sent
4
+ * through it vanishes and the command exits silently.
5
+ */
6
+
7
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
8
+
9
+ /**
10
+ * A user-facing notice: a TUI/RPC toast where there is UI, otherwise stderr.
11
+ * `console.error` never corrupts the `--mode json` event stream on stdout.
12
+ */
13
+ export function notifyOrPrint(ctx: Pick<ExtensionContext, "hasUI" | "ui">, message: string, level: "info" | "warning" | "error"): void {
14
+ if (ctx.hasUI) ctx.ui.notify(message, level);
15
+ else console.error(message);
16
+ }
17
+
18
+ /**
19
+ * Whether the session can show a custom TUI component: a panel opened through
20
+ * `ctx.ui.custom`, or a component widget. RPC has UI but forwards only dialogs,
21
+ * notices and string-array widgets: its `custom()` resolves undefined and a
22
+ * component factory vanishes, so a command falls back to text there.
23
+ */
24
+ export function canShowCustomUi(ctx: Pick<ExtensionContext, "hasUI" | "mode">): boolean {
25
+ return ctx.hasUI && ctx.mode !== "rpc";
26
+ }
27
+
28
+ /** The line saying a command whose panel cannot open in RPC shows a read-only listing instead. */
29
+ export function rpcReadOnlyText(command: string, manage: string): string {
30
+ return `${command} in RPC is read-only; use the TUI to ${manage}.`;
31
+ }
32
+
33
+ /** In RPC, say that a command whose panel cannot open shows a read-only listing instead. */
34
+ export function notifyRpcReadOnly(ctx: Pick<ExtensionContext, "mode" | "ui">, command: string, manage: string): string | undefined {
35
+ if (ctx.mode !== "rpc") return undefined;
36
+ const text = rpcReadOnlyText(command, manage);
37
+ ctx.ui.notify(text, "info");
38
+ // Returned so a command reporting its result can include it.
39
+ return text;
40
+ }
41
+
42
+ /** A command's answer in a one-shot run: stdout in print mode, stderr in json mode (stdout is the event stream there). */
43
+ export function printAnswer(ctx: Pick<ExtensionContext, "mode">, text: string): void {
44
+ if (ctx.mode === "print") console.log(text);
45
+ else console.error(text);
46
+ }
@@ -22,28 +22,28 @@ import type { InlineExtension } from "@earendil-works/pi-coding-agent";
22
22
  import { applyPostToolUseOutcome } from "../hooks/protocol.ts";
23
23
  import type { ChildHookCall, HookBridge } from "../hooks/subagent-bridge.ts";
24
24
  import { REMINDER_CHANNEL } from "./reminders.ts";
25
+ import { SUBAGENT_HANDBACK } from "./subagent-handback.ts";
25
26
 
26
27
  /** Tools the runtime itself injects; never run hooks for them. */
27
- const DEFAULT_INTERNAL_TOOLS = new Set(["structured_output"]);
28
+ const DEFAULT_INTERNAL_TOOLS = new Set(["structured_output", SUBAGENT_HANDBACK]);
28
29
 
29
30
  export interface HookGateOptions {
30
31
  /** The child's agent type for the payload's `agent_type`, resolved per session id by the runner. */
31
32
  agentTypeOf?: (sessionId: string | undefined) => string | undefined;
32
- neverGate?: Set<string>;
33
33
  }
34
34
 
35
35
  export function hookGateFactory(getBridge: () => HookBridge | undefined, options: HookGateOptions = {}): InlineExtension {
36
- const neverGate = options.neverGate ?? DEFAULT_INTERNAL_TOOLS;
37
36
  return {
38
37
  name: "agent-hook-gate",
39
38
  hidden: true,
40
39
  factory: (pi) => {
41
- const describe = (toolName: string, input: unknown, ctx: { cwd?: string; sessionManager?: { getSessionId?: () => string; getSessionFile?: () => string | undefined } } | undefined, fallbackCwd: string): ChildHookCall => {
40
+ const describe = (toolName: string, input: unknown, ctx: { cwd?: string; signal?: AbortSignal; sessionManager?: { getSessionId?: () => string; getSessionFile?: () => string | undefined } } | undefined, fallbackCwd: string): ChildHookCall => {
42
41
  const sessionId = ctx?.sessionManager?.getSessionId?.();
43
42
  return {
44
43
  toolName,
45
44
  input: (input ?? {}) as Record<string, unknown>,
46
45
  cwd: ctx?.cwd ?? fallbackCwd,
46
+ signal: ctx?.signal,
47
47
  sessionId,
48
48
  transcriptPath: ctx?.sessionManager?.getSessionFile?.() ?? undefined,
49
49
  agentType: options.agentTypeOf?.(sessionId),
@@ -51,7 +51,7 @@ export function hookGateFactory(getBridge: () => HookBridge | undefined, options
51
51
  };
52
52
 
53
53
  pi.on("tool_call", async (event, ctx) => {
54
- if (neverGate.has(event.toolName)) return undefined;
54
+ if (DEFAULT_INTERNAL_TOOLS.has(event.toolName)) return undefined;
55
55
  try {
56
56
  const bridge = getBridge();
57
57
  if (!bridge) return undefined;
@@ -67,7 +67,7 @@ export function hookGateFactory(getBridge: () => HookBridge | undefined, options
67
67
  });
68
68
 
69
69
  pi.on("tool_result", async (event, ctx) => {
70
- if (neverGate.has(event.toolName)) return undefined;
70
+ if (DEFAULT_INTERNAL_TOOLS.has(event.toolName)) return undefined;
71
71
  try {
72
72
  const bridge = getBridge();
73
73
  if (!bridge) return undefined;
@@ -78,7 +78,7 @@ export function hookGateFactory(getBridge: () => HookBridge | undefined, options
78
78
  });
79
79
  // Same assembly as the parent's tool_result handler; the parent already
80
80
  // framed additionalContext as a <system-reminder>.
81
- const content = applyPostToolUseOutcome(event.content, outcome);
81
+ const content = applyPostToolUseOutcome(event.content, outcome, event.toolName);
82
82
  if (!content) return undefined;
83
83
  return { content, isError: event.isError };
84
84
  } catch {
@@ -16,7 +16,7 @@
16
16
  * message with EMPTY content, `stopReason: "error"` and `errorMessage: "This
17
17
  * operation was aborted"` — the fetch layer's AbortError, not pi's "aborted"
18
18
  * label (measured over RPC on pi 0.85.0, STEERING-REVIEW-2026-09-05 L1;
19
- * upstream ask docs/upstream_prs.md #18). The run's abort signal is still the
19
+ * upstream ask working-docs/upstream_prs.md #18). The run's abort signal is still the
20
20
  * ground truth, and `ctx.signal` at `agent_end` is that signal (pi's
21
21
  * `activeRun` outlives the event), so callers pass `ctx.signal?.aborted` and
22
22
  * an `error` stop on an aborted run reads as "aborted": the user stopped the
@@ -34,6 +34,19 @@
34
34
  * carry the outcome over from the runs' `agent_end`. Findings §3.
35
35
  */
36
36
 
37
+ /** A one-shot command failed before its user turn could settle (no agent_end to classify). */
38
+ export const ONE_SHOT_COMMAND_FAILED_CHANNEL = "one-code:one-shot-command-failed";
39
+
40
+ /**
41
+ * The harness stopped the current turn as a failure (a broken provider stream,
42
+ * a classifier that cannot judge). Emit it before `ctx.abort()`: the run then
43
+ * ends `aborted`, which alone reads as a user cancel and exits 0.
44
+ */
45
+ export const TURN_FAILED_CHANNEL = "one-code:turn-failed";
46
+ export interface TurnFailedEvent {
47
+ reason: string;
48
+ }
49
+
37
50
  /** How the last assistant message of a run ended. */
38
51
  export type RunOutcome = "ok" | "aborted" | "error";
39
52
 
@@ -48,7 +61,10 @@ export interface RunMessage {
48
61
  * "ok". `signalAborted` is the run's abort signal at `agent_end`
49
62
  * (`ctx.signal?.aborted`): it turns an `error` stop into "aborted" (see the
50
63
  * header) and leaves a completed stop alone — a signal aborted after the final
51
- * message landed did not interrupt anything.
64
+ * message landed did not interrupt anything. A run ends on a tool call only
65
+ * when its batch terminates the loop; with the signal aborted that is a turn
66
+ * the user stopped before the model replied (a rejection without a reason,
67
+ * permissions/index.ts), so it is "aborted" too.
52
68
  */
53
69
  export function runOutcome(messages: ReadonlyArray<RunMessage> | undefined, signalAborted = false): RunOutcome {
54
70
  if (!messages) return "ok";
@@ -56,8 +72,8 @@ export function runOutcome(messages: ReadonlyArray<RunMessage> | undefined, sign
56
72
  const message = messages[i];
57
73
  if (message.role !== "assistant") continue;
58
74
  if (message.stopReason === "aborted") return "aborted";
59
- if (message.stopReason === "error") return signalAborted ? "aborted" : "error";
60
- return "ok";
75
+ if (signalAborted && (message.stopReason === "error" || message.stopReason === "toolUse")) return "aborted";
76
+ return message.stopReason === "error" ? "error" : "ok";
61
77
  }
62
78
  return "ok";
63
79
  }