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,40 +1,33 @@
1
1
  /**
2
- * The `/init` prompt — adapted from Claude Code's `/init`, kept provider-neutral
3
- * (One Code writes a CLAUDE.md that any Claude Code-compatible agent reads) and
4
- * self-contained (no dependency on CC-internal skills like `update-config`).
2
+ * The `/init` prompt: Claude Code's classic one, the prompt it sends by
3
+ * default, submitted verbatim as a user turn by extensions/init/index.ts.
5
4
  *
6
- * Submitted verbatim as a user turn by extensions/init/index.ts; the model does
7
- * the work with the normal tool set (subagent survey, ask_user_question, write).
5
+ * Three adaptations. The CLAUDE.md it writes is read by every Claude
6
+ * Code-compatible agent, One Code included, so the opening sentence and the
7
+ * file's header name AI coding agents rather than one product (the header is
8
+ * the one One Code's /init has always written). And the rules to fold in
9
+ * include an AGENTS.md: One Code reads a directory's AGENTS.md only when that
10
+ * directory has no CLAUDE.md, so once /init writes one, anything left in
11
+ * AGENTS.md alone would stop reaching the model.
8
12
  */
9
- export const INIT_PROMPT = `Analyze this codebase and set up a concise CLAUDE.md at the repository root, to be loaded into every future session working here. Keep it minimal — every line must earn its place: include only what an agent would get wrong or waste time rediscovering without it.
10
-
11
- ## 1. Survey the codebase
12
- Launch an Explore subagent (or read directly if the repo is small) to gather:
13
- - Build, test, and lint commands — especially non-standard scripts, flags, or how to run a *single* test.
14
- - Languages, frameworks, package manager, and whether this is a monorepo/multi-module or single project.
15
- - High-level architecture that requires reading several files to grasp (the "big picture", not a file listing).
16
- - Non-obvious gotchas, required env vars, or workflow quirks.
17
- - Existing AI-tool config to fold in: an existing CLAUDE.md, AGENTS.md, .cursor/rules or .cursorrules, .github/copilot-instructions.md, .windsurfrules, .clinerules — and the important parts of README.md.
18
-
19
- ## 2. Fill the gaps
20
- Use ask_user_question only for things the code cannot answer — branch/PR conventions, required setup, testing quirks, communication preferences. Skip anything already obvious from manifests or the README. Do not mark options as "recommended": this is how their team works, not best practices.
21
-
22
- ## 3. Write CLAUDE.md
23
- Prefix the file with:
13
+ export const INIT_PROMPT = `Please analyze this codebase and create a CLAUDE.md file, which will be given to future AI coding agents (Claude Code and compatible tools) to operate in this repository.
14
+
15
+ What to add:
16
+ 1. Commands that will be commonly used, such as how to build, lint, and run tests. Include the necessary commands to develop in this codebase, such as how to run a single test.
17
+ 2. High-level code architecture and structure so that future instances can be productive more quickly. Focus on the "big picture" architecture that requires reading multiple files to understand.
18
+
19
+ Usage notes:
20
+ - If there's already a CLAUDE.md, suggest improvements to it.
21
+ - When you make the initial CLAUDE.md, do not repeat yourself and do not include obvious instructions like "Provide helpful error messages to users", "Write unit tests for all new utilities", "Never include sensitive information (API keys, tokens) in code or commits".
22
+ - Avoid listing every component or file structure that can be easily discovered.
23
+ - Don't include generic development practices.
24
+ - If there are Cursor rules (in .cursor/rules/ or .cursorrules), Copilot rules (in .github/copilot-instructions.md) or an AGENTS.md, make sure to include the important parts.
25
+ - If there is a README.md, make sure to include the important parts.
26
+ - Do not make up information such as "Common Development Tasks", "Tips for Development", "Support and Documentation" unless this is expressly included in other files that you read.
27
+ - Be sure to prefix the file with the following text:
24
28
 
25
29
  \`\`\`
26
30
  # CLAUDE.md
27
31
 
28
32
  This file gives guidance to AI coding agents (Claude Code and compatible tools) working in this repository.
29
- \`\`\`
30
-
31
- Include: build/test/lint commands an agent can't guess; code-style rules that DIFFER from language defaults; testing instructions and quirks; repo etiquette (branch naming, PR/commit conventions); required env vars or setup; non-obvious gotchas and architectural decisions; the important parts of any existing AI-tool config.
32
-
33
- Exclude: file-by-file structure or component lists (discoverable by reading); standard language conventions; generic advice ("write clean code", "handle errors"); long references or API docs (link them with \`@path/to/file\` import syntax instead of inlining); anything that changes frequently (reference the source with \`@path\`).
34
-
35
- Be specific — "Use 2-space indentation in TypeScript" beats "format code properly." Do not repeat yourself and do not invent sections like "Common Development Tasks" — only include what you actually found.
36
-
37
- If a CLAUDE.md already exists: read it, propose concrete improvements as diffs, and explain each. Do NOT silently overwrite. For projects with distinct subdirectories (monorepos), mention that per-directory CLAUDE.md files are loaded automatically when working in those directories, and offer to create them.
38
-
39
- ## 4. Recap
40
- Summarize which file(s) you wrote and the key points in each. Remind the user this is a starting point to review and tweak, and that they can run \`/init\` again anytime to re-scan.`;
33
+ \`\`\``;
@@ -11,11 +11,13 @@
11
11
  * InterruptedByUser component. The paired turn-duration line suppresses itself
12
12
  * on the same signal, so an interrupted turn shows this note in place of the
13
13
  * "Cooked for …" line — CC's behaviour (its turn-duration render is gated on
14
- * `!aborted`).
14
+ * `!aborted`). A send now (ctrl+x ctrl+s, the send-now extension) aborts the
15
+ * turn too, but gets no note: the user has already said what to do instead.
15
16
  */
16
17
 
17
18
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
18
19
  import { wasInterrupted } from "../lib/interrupt.ts";
20
+ import { SEND_NOW_CHANNEL } from "../lib/send-now-channel.ts";
19
21
  import { linesComponent, safeThemePaint } from "../lib/tui-render.ts";
20
22
  import { INTERRUPTED_TEXT } from "./line.ts";
21
23
 
@@ -27,7 +29,17 @@ export default function interruptedExtension(pi: ExtensionAPI) {
27
29
  return linesComponent(() => [paint("dim", INTERRUPTED_TEXT)]);
28
30
  });
29
31
 
32
+ // Send now aborts the turn to deliver the queued messages: the user has
33
+ // already said what to do instead, so that abort gets no note.
34
+ let sendingNow = false;
35
+ pi.events.on(SEND_NOW_CHANNEL, () => {
36
+ sendingNow = true;
37
+ });
38
+
30
39
  pi.on("agent_end", (event, ctx) => {
40
+ const skip = sendingNow;
41
+ sendingNow = false;
42
+ if (skip) return;
31
43
  if (!ctx.hasUI) return;
32
44
  if (!wasInterrupted(event.messages, ctx.signal?.aborted)) return;
33
45
  pi.appendEntry(ENTRY_TYPE);
@@ -0,0 +1,27 @@
1
+ /**
2
+ * kept-thinking extension — removes thinking from the turns a compaction keeps,
3
+ * on APIs that check a thinking block's signature against the conversation
4
+ * before it (`compaction/kept-thinking.ts` says why and which turns).
5
+ *
6
+ * The boundary comes from the session branch, not from the request's messages:
7
+ * system-reminder rewrites the compaction summary message into a plain user
8
+ * message, so a strip keyed on it found nothing (seen live on Opus 5.5: every
9
+ * request after the compaction was a 400). It is the first `context` hook in
10
+ * the package manifest, before every extension that captures the request's
11
+ * messages for a side call (skill, background, subagents, recap, btw,
12
+ * compaction), so each capture carries the stripped history the session sent.
13
+ */
14
+
15
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
16
+ import { signsThinking, withoutKeptThinking } from "../compaction/kept-thinking.ts";
17
+ import { latestCompaction } from "../lib/compaction-boundary.ts";
18
+
19
+ export default function keptThinkingExtension(pi: ExtensionAPI) {
20
+ pi.on("context", (event, ctx) => {
21
+ const api = ctx.model?.api;
22
+ // Only a signing API pays for the branch walk.
23
+ if (!signsThinking(api)) return undefined;
24
+ const messages = withoutKeptThinking(event.messages, api, latestCompaction(ctx.sessionManager.getBranch())?.time);
25
+ return messages ? { messages } : undefined;
26
+ });
27
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * ledger extension, the request side of the record-once design
3
+ * (working-docs/features/lifecycle-grid-hunt/design.md sections 2 and 5): the
4
+ * request is a pure function of the branch. Every request is recorded on the
5
+ * branch as it went on the wire (`ledger/wire.ts`, the payload side), and the
6
+ * next request inside the same epoch takes every byte above its append point
7
+ * from that record instead of from a rebuild.
8
+ *
9
+ * Its `context` hook runs right after kept-thinking, before anything injects:
10
+ *
11
+ * - it folds the branch (lib/ledger/fold.ts, through the shared memo in
12
+ * lib/ledger/fold-memo.ts) and decides whether this request
13
+ * opens an epoch (lib/ledger/plan.ts planRequest);
14
+ * - inside an epoch it cuts the messages the last request covered, so pi and
15
+ * every later `context` hook see only the tail, and the payload side puts
16
+ * the recorded list in front of what pi renders for it;
17
+ * - it publishes the plan on LEDGER_PLAN_CHANNEL for the renderer
18
+ * (system-reminder) and the payload side.
19
+ *
20
+ * Resume, `/reload`, `/fork` and `/tree` need no restore code: each folds a
21
+ * different branch. pi keeps a `/tree` move only in memory, so `session_tree`
22
+ * appends a `one-code:branch` record, which makes the selected leaf the one a
23
+ * reopened file resumes on.
24
+ */
25
+
26
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
27
+ import { sessionIdentity } from "../lib/anthropic-auth.ts";
28
+ import { foldSession } from "../lib/ledger/fold-memo.ts";
29
+ import { LEDGER_PLAN_CHANNEL, planRequest } from "../lib/ledger/plan.ts";
30
+ import { makeRecord, RECORD } from "../lib/ledger/records.ts";
31
+ import { extensionVersion } from "../lib/package-version.ts";
32
+
33
+ export default function ledgerExtension(pi: ExtensionAPI) {
34
+ const by = extensionVersion();
35
+
36
+ pi.on("session_tree", (event) => {
37
+ pi.appendEntry(RECORD.branch, makeRecord("branch", by, { fromLeafId: event.oldLeafId ?? "" }));
38
+ });
39
+
40
+ pi.on("context", (event, ctx) => {
41
+ const fold = foldSession(ctx.sessionManager, sessionIdentity(pi, ctx.model));
42
+ const { plan, tailStart } = planRequest(fold, event.messages as Array<{ role: string; timestamp?: number }>);
43
+ pi.events.emit(LEDGER_PLAN_CHANNEL, plan);
44
+ return tailStart > 0 ? { messages: event.messages.slice(tailStart) } : undefined;
45
+ });
46
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * ledger-wire extension, the payload side of the ledger (extensions/ledger/index.ts
3
+ * has the request side and the design). Its `before_provider_request` hook
4
+ * runs after every hook that shapes the tail (system-reminder's layout,
5
+ * tool-search, context-management) and before cache-marks:
6
+ *
7
+ * 1. Inside an epoch it splices: the base request's recorded list, then pi's
8
+ * rendering of the tail, under the recorded system prompt and tools, with
9
+ * tools added mid-epoch appended (lib/ledger/wire.ts spliceRequest).
10
+ * 2. It settles the layout (lib/ledger/layout.ts: no system message stranded
11
+ * before a user message) and validates the request (lib/ledger/validate.ts): no reference to an
12
+ * undeclared tool, no system message stranded before a user message. A
13
+ * problem is a bug; it is dropped and reported, so it costs one block,
14
+ * never a 400. ONE_CODE_LEDGER_STRICT=1 sends it unrepaired instead, for
15
+ * the live probes to see.
16
+ * 3. It records the request as a delta against the fold's base (the request
17
+ * the fold rebuilds it on), with the pending items it carried and what its
18
+ * new blocks told the model, and publishes it (LEDGER_SENT_CHANNEL) for
19
+ * the side calls that replay it (compaction's summarizer, btw, recap,
20
+ * forks).
21
+ *
22
+ * A pi cache warm never records or publishes: it is not a request the model
23
+ * answers. pi sends it through the same hooks with the warm cap, under the
24
+ * plan of the request it warms, in a provider call of its own, so it resends
25
+ * that request as it went out (with the warm's cap), or after a `/reload` (no
26
+ * plan here: pi's context is the tail the old instance cut) the branch's last
27
+ * request. Every other payload follows a context pass: pi's retry runs one
28
+ * too, and a second payload in the call a request went out in is pi-ai's
29
+ * strict-tools fallback, a real re-send. The cap alone cannot tell them
30
+ * apart: pi-ai clamps a request's cap to the window's room, down to 1.
31
+ */
32
+
33
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
34
+ import { sessionIdentity } from "../lib/anthropic-auth.ts";
35
+ import { notifyOrPrint } from "../lib/headless-output.ts";
36
+ import { foldSession } from "../lib/ledger/fold-memo.ts";
37
+ import { LEDGER_PLAN_CHANNEL, LEDGER_SENT_CHANNEL, type LedgerSent, type RequestPlan } from "../lib/ledger/plan.ts";
38
+ import { makeRecord, RECORD, type ToldFact } from "../lib/ledger/records.ts";
39
+ import { deliveredAfterRepair, repairRequest, validateRequest } from "../lib/ledger/validate.ts";
40
+ import { settlePayload } from "../lib/ledger/layout.ts";
41
+ import { appendPoint, encodeRequest, hasWarmCap, listKey, type RecordedRequest, recordedBody, recordedRequest, samePrefix, spliceRequest, type WirePayload, withBranchToolOrder, withRequestFields } from "../lib/ledger/wire.ts";
42
+ import { type AffinityModel, affinityHeaders } from "../lib/request-replay.ts";
43
+ import { extensionVersion } from "../lib/package-version.ts";
44
+
45
+ export default function ledgerWireExtension(pi: ExtensionAPI) {
46
+ const by = extensionVersion();
47
+ /** The plan of the last `context` pass; a warm re-runs only this hook and reuses it. */
48
+ let plan: RequestPlan | undefined;
49
+ /**
50
+ * The provider call the hook runs in: pi resolves a call's headers once
51
+ * (`prepareRequest`), before its first payload, so `before_provider_headers`
52
+ * opens each call, a warm's included.
53
+ */
54
+ let call = 0;
55
+ /** The call a request under `plan` went out in: a payload with the warm cap in a later call is a warm, in the same call pi-ai's re-send. */
56
+ let sentIn: number | undefined;
57
+ /** What this hook recorded under `plan`: the request, and the hashes it stored, so a re-send stores only new bodies. */
58
+ let recorded: { request: RecordedRequest; hashes: Set<string> } | undefined;
59
+ /** The last session request this instance sent, as it left this hook: what a warm resends. */
60
+ let lastSent: WirePayload | undefined;
61
+ /** Problems already reported this session, so a recurring bug is one notice. */
62
+ const reported = new Set<string>();
63
+
64
+ pi.events.on(LEDGER_PLAN_CHANNEL, (data) => {
65
+ plan = data as RequestPlan;
66
+ sentIn = undefined;
67
+ recorded = undefined;
68
+ });
69
+ pi.on("session_start", () => {
70
+ plan = undefined;
71
+ sentIn = undefined;
72
+ recorded = undefined;
73
+ lastSent = undefined;
74
+ reported.clear();
75
+ });
76
+
77
+ /** The session the plan's request is routed for: the one that opened the epoch, recorded with its base. */
78
+ const routingOf = (current: RequestPlan | undefined, own: string) => (current && !current.epoch ? (current.base?.routing ?? own) : own);
79
+
80
+ // A /fork's first request extends its parent's record, and the parent's cache
81
+ // lives on the host the parent's session id routed to: a gateway such as
82
+ // OpenRouter picks it by the session-affinity headers, which pi-ai builds
83
+ // from the fork's own id (R3-M22). Not on Codex, where pi-ai sets its
84
+ // session-id header after every hook (findings §75). With no plan in this
85
+ // instance (a warm after a /reload) the call resends the branch's last
86
+ // request, so it goes where that request was routed.
87
+ pi.on("before_provider_headers", (event, ctx) => {
88
+ call++;
89
+ const own = ctx.sessionManager.getSessionId();
90
+ const identity = plan ? undefined : sessionIdentity(pi, ctx.model);
91
+ const routing = plan ? routingOf(plan, own) : ((identity && foldSession(ctx.sessionManager, identity).lastRequest?.routing) ?? own);
92
+ if (routing === own) return;
93
+ Object.assign(event.headers, affinityHeaders(ctx.model as AffinityModel | undefined, routing));
94
+ });
95
+
96
+ pi.on("before_provider_request", (event, ctx) => {
97
+ const model = ctx.model;
98
+ const identity = sessionIdentity(pi, model);
99
+ if (!model || !identity) return undefined;
100
+ const api = model.api;
101
+ const live = event.payload as WirePayload;
102
+ const current = plan;
103
+
104
+ // A cache warm: the request it warms, as sent, under the warm's cap. A
105
+ // payload in the call the request went out in is pi-ai sending it again.
106
+ if ((!current || (sentIn !== undefined && sentIn !== call)) && hasWarmCap(live)) {
107
+ if (lastSent) return withRequestFields(lastSent, live);
108
+ const base = foldSession(ctx.sessionManager, identity).lastRequest;
109
+ return base ? recordedBody(base, live, api) : undefined;
110
+ }
111
+ // No context pass in this instance and not a warm: leave pi's request alone.
112
+ if (!current) return undefined;
113
+
114
+ let payload = live;
115
+ const base = current.epoch ? undefined : current.base;
116
+ const end = base ? appendPoint(base.list, api) : 0;
117
+ if (base) payload = spliceRequest(base, payload, api);
118
+ // An epoch renders pi's tools again, in the order the branch first declared them.
119
+ else payload = withBranchToolOrder(payload, current.fold.toolOrder, api);
120
+ const spliced = payload;
121
+ // The final layout pass: no system message stranded before a user message (lib/ledger/layout.ts).
122
+ payload = settlePayload(payload, api, listKey(api), end);
123
+ let delivered: { consumed: string[]; facts: ToldFact[] } = { consumed: current.consumed, facts: current.carried.flatMap((block) => block.facts) };
124
+
125
+ const problems = validateRequest(payload, api);
126
+ if (problems.length > 0) {
127
+ const fresh = problems.map((problem) => JSON.stringify(problem)).filter((key) => !reported.has(key));
128
+ for (const key of fresh) reported.add(key);
129
+ if (fresh.length > 0) notifyOrPrint(ctx, `One Code: the request had ${problems.length} problem(s) the ledger ${process.env.ONE_CODE_LEDGER_STRICT === "1" ? "left in place" : "removed"}: ${fresh.join(", ")}`, "warning");
130
+ if (process.env.ONE_CODE_LEDGER_STRICT !== "1") {
131
+ payload = repairRequest(payload, api, problems);
132
+ delivered = deliveredAfterRepair(payload, api, delivered, current.fold.pending, new Set(current.base?.hashes ?? []), current.carried);
133
+ }
134
+ }
135
+
136
+ // The recorded prefix is known, unless the layout pass or a repair moved its elements.
137
+ const request = recordedRequest(payload, identity, base && samePrefix(spliced, payload, listKey(api), end) ? { base, end } : undefined);
138
+ lastSent = payload;
139
+ sentIn = call;
140
+ // The same request again under this plan (pi-ai re-sending an identical body) records nothing new.
141
+ if (recorded && encodeRequest(recorded.request, request, { has: () => true }) === undefined) return payload === live ? undefined : payload;
142
+ const known = { has: (hash: string) => current.fold.knownHashes.has(hash) || recorded?.hashes.has(hash) === true };
143
+ const delta = encodeRequest(current.fold.lastRequest, request, known);
144
+ if (delta) {
145
+ if (current.epoch) pi.appendEntry(RECORD.epoch, makeRecord("epoch", by, { cause: current.cause ?? "first" }));
146
+ const routing = routingOf(current, ctx.sessionManager.getSessionId());
147
+ pi.appendEntry(RECORD.request, makeRecord("request", by, { ...delta, covers: current.covers, consumed: delivered.consumed, routing }));
148
+ if (delivered.facts.length > 0) pi.appendEntry(RECORD.told, makeRecord("told", by, { facts: delivered.facts }));
149
+ recorded = { request, hashes: new Set([...(recorded?.hashes ?? []), ...Object.keys(delta.blobs)]) };
150
+ pi.events.emit(LEDGER_SENT_CHANNEL, { payload, model, sessionId: ctx.sessionManager.getSessionId(), plan: current } satisfies LedgerSent);
151
+ }
152
+ return payload === live ? undefined : payload;
153
+ });
154
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Cron jobs for subagents, Claude Code's way (findings §21): a subagent's
3
+ * `cron_create` stamps its task id on the job, `cron_list` shows it only its
4
+ * own jobs, `cron_delete` refuses another's, and a fire goes to that agent
5
+ * while it lives and is dropped once it has ended.
6
+ *
7
+ * Children never load the background extension, so the subagents extension
8
+ * gives each child proxy tools (`agentCronTools`) that reach the main
9
+ * session's store over the main session's bus: an `AgentCronRequest` on
10
+ * AGENT_CRON_CHANNEL, answered synchronously by the background extension. A
11
+ * fire for an agent's job goes the other way as an `AgentCronFire` on
12
+ * AGENT_CRON_FIRE_CHANNEL; the subagents extension sets `delivered` when the
13
+ * agent took it.
14
+ *
15
+ * Pure: no pi imports.
16
+ */
17
+
18
+ import {
19
+ CRON_CREATE_DESCRIPTION,
20
+ CRON_CREATE_PARAMETERS,
21
+ CRON_DELETE_DESCRIPTION,
22
+ CRON_DELETE_PARAMETERS,
23
+ CRON_LIST_DESCRIPTION,
24
+ CRON_LIST_PARAMETERS,
25
+ } from "../background/cron.ts";
26
+
27
+ export const AGENT_CRON_CHANNEL = "one-code:agent-cron";
28
+ export const AGENT_CRON_FIRE_CHANNEL = "one-code:agent-cron-fire";
29
+
30
+ export type AgentCronRequest =
31
+ | { op: "create"; agentId: string; cwd: string; cron: string; prompt: string; recurring?: boolean; result?: AgentCronResult }
32
+ | { op: "list"; agentId: string; result?: AgentCronResult }
33
+ | { op: "delete"; agentId: string; id: string; result?: AgentCronResult };
34
+
35
+ export interface AgentCronResult {
36
+ text: string;
37
+ isError?: boolean;
38
+ details?: Record<string, unknown>;
39
+ }
40
+
41
+ /**
42
+ * The conversation a fire lands in: its branch to read, and entries to
43
+ * append to it. A no-prompt `/loop` sentinel records there what it already
44
+ * sent, so the record follows that conversation (its `/tree`, its resume,
45
+ * its own compaction), not the session that owns the job store.
46
+ */
47
+ export interface FireConversation {
48
+ getBranch(): readonly unknown[];
49
+ appendEntry(customType: string, data?: unknown): void;
50
+ }
51
+
52
+ export interface AgentCronFire {
53
+ agentId: string;
54
+ jobId: string;
55
+ /**
56
+ * The text to send, resolved against the agent's own conversation (a
57
+ * sentinel reads and records its delivery there; undefined resolves as a
58
+ * conversation that was sent nothing). Call it once, for the send.
59
+ */
60
+ text(conversation: FireConversation | undefined): string;
61
+ /** Set by the subagents extension when a live agent received it. */
62
+ delivered?: boolean;
63
+ }
64
+
65
+ /**
66
+ * cron_create's refusal in an agent that runs to completion (a nested spawn,
67
+ * or any spawn in a one-shot run): a fire reaches only a background agent,
68
+ * and this one ends first.
69
+ */
70
+ export const BLOCKING_RUN_REFUSAL =
71
+ "Nothing was scheduled: this agent runs until its task is done and then ends, so a job it schedules could never fire. Do the work now, or report back so the main session can schedule it.";
72
+
73
+ /** Claude Code's refusal when an agent deletes a job it does not own. */
74
+ export function formatNotOwner(id: string): string {
75
+ return `Cannot delete cron job '${id}': owned by another agent`;
76
+ }
77
+
78
+ interface Emitter {
79
+ emit(channel: string, data: unknown): unknown;
80
+ }
81
+
82
+ /**
83
+ * Whether `agentId` owns a pending job in the main session's store (its
84
+ * `cron_list` count). The subagents extension keeps such an agent resident,
85
+ * since a fire reaches only a live one.
86
+ */
87
+ export function agentOwnsCronJobs(events: Emitter, agentId: string): boolean {
88
+ const request: AgentCronRequest = { op: "list", agentId };
89
+ events.emit(AGENT_CRON_CHANNEL, request);
90
+ return ((request.result?.details?.jobCount as number | undefined) ?? 0) > 0;
91
+ }
92
+
93
+ /** Where a child's cron tools live: only a `resident` (background) run can receive a fire. */
94
+ export interface AgentCronOwner {
95
+ agentId: string;
96
+ cwd: string;
97
+ resident: boolean;
98
+ }
99
+
100
+ /**
101
+ * The three cron tools for a child session, bound to its owner and the MAIN
102
+ * session's bus. Same names, schemas and descriptions as the main session's
103
+ * (background/index.ts). A run that is not resident keeps all three, as in
104
+ * Claude Code, but its cron_create refuses.
105
+ */
106
+ export function agentCronTools(events: Emitter, { agentId, cwd, resident }: AgentCronOwner) {
107
+ const toolResult = (result: AgentCronResult) => ({
108
+ content: [{ type: "text" as const, text: result.text }],
109
+ details: result.details ?? {},
110
+ ...(result.isError ? { isError: true } : {}),
111
+ });
112
+ const ask = (request: AgentCronRequest) => {
113
+ events.emit(AGENT_CRON_CHANNEL, request);
114
+ return toolResult(request.result ?? { text: "The session's cron store is not loaded; nothing was scheduled.", isError: true });
115
+ };
116
+ return [
117
+ {
118
+ name: "cron_create",
119
+ label: "Cron Create",
120
+ description: CRON_CREATE_DESCRIPTION,
121
+ parameters: CRON_CREATE_PARAMETERS,
122
+ async execute(_id: string, params: { cron: string; prompt: string; recurring?: boolean; durable?: boolean }) {
123
+ if (!resident) return toolResult({ text: BLOCKING_RUN_REFUSAL, isError: true });
124
+ return ask({ op: "create", agentId, cwd, cron: params.cron, prompt: params.prompt, recurring: params.recurring });
125
+ },
126
+ },
127
+ {
128
+ name: "cron_list",
129
+ label: "Cron List",
130
+ description: CRON_LIST_DESCRIPTION,
131
+ parameters: CRON_LIST_PARAMETERS,
132
+ async execute() {
133
+ return ask({ op: "list", agentId });
134
+ },
135
+ },
136
+ {
137
+ name: "cron_delete",
138
+ label: "Cron Delete",
139
+ description: CRON_DELETE_DESCRIPTION,
140
+ parameters: CRON_DELETE_PARAMETERS,
141
+ async execute(_id: string, params: { id: string }) {
142
+ return ask({ op: "delete", agentId, id: params.id });
143
+ },
144
+ },
145
+ ];
146
+ }
@@ -31,6 +31,7 @@ import type { PermissionBridge } from "../permissions/subagent-gate.ts";
31
31
  import { withShortCacheRetention } from "./cache-retention.ts";
32
32
  import { hookGateFactory } from "./hook-gate.ts";
33
33
  import { permissionGateFactory } from "./permission-gate.ts";
34
+ import { releaseSideCalls } from "./side-call-run.ts";
34
35
  import { worktreeGuardFactory } from "./worktree-isolation.ts";
35
36
 
36
37
  /** The last assistant message's text (the value an agent run returns). */
@@ -113,8 +114,9 @@ export interface AgentLoaderOptions {
113
114
  * listener). A cached loader therefore broke the second child of the same
114
115
  * agent type, every SendMessage resume, every workflow agent after the first,
115
116
  * and any sibling still running when one was disposed
116
- * (LIFECYCLE-REVIEW-2026-09-06 H3, measured). A build costs ~3 ms once the
117
- * extension modules are in jiti's cache, so there is nothing to save.
117
+ * (LIFECYCLE-REVIEW-2026-09-06 H3, measured). A build costs ~3 ms once pi's
118
+ * factory cache holds the extensions for that directory (jiti itself runs
119
+ * with `moduleCache: false`, findings §8), so there is nothing to save.
118
120
  */
119
121
  export async function buildAgentLoader(options: AgentLoaderOptions): Promise<DefaultResourceLoader> {
120
122
  const loader = new DefaultResourceLoader({
@@ -126,7 +128,8 @@ export async function buildAgentLoader(options: AgentLoaderOptions): Promise<Def
126
128
  // Order is load-bearing, mirroring the main session's extension order:
127
129
  // hooks first (a hook's updatedInput must be what every guard judges),
128
130
  // then the worktree git-isolation guard, then the permission gate.
129
- ...(options.getHookBridge ? [hookGateFactory(options.getHookBridge, { agentTypeOf: options.agentTypeOf, neverGate: options.neverGate })] : []),
131
+ // Permission-autoallowed orchestration tools still run the user's hooks.
132
+ ...(options.getHookBridge ? [hookGateFactory(options.getHookBridge, { agentTypeOf: options.agentTypeOf })] : []),
130
133
  worktreeGuardFactory(options.cwd),
131
134
  permissionGateFactory(options.cwd, os.homedir(), options.neverGate, options.getPermissionBridge),
132
135
  ...(options.extraFactories ?? []),
@@ -181,3 +184,15 @@ export async function openChildSession(options: OpenChildSessionOptions): Promis
181
184
  await session.bindExtensions({ onError: options.onError });
182
185
  return session;
183
186
  }
187
+
188
+ /**
189
+ * Dispose a child opened by openChildSession. `dispose()` never emits
190
+ * `session_shutdown`, so the child's side-call keys (its web_fetch reader),
191
+ * which that event releases in a main session, are released here
192
+ * (lib/side-call-run.ts).
193
+ */
194
+ export function disposeChildSession(session: AgentSession): void {
195
+ const sessionId = session.sessionManager.getSessionId();
196
+ session.dispose();
197
+ releaseSideCalls(sessionId);
198
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * The agent view's shared half: while the user views an agent (the subagents
3
+ * panel's Enter-to-view), the editor targets that agent, as Claude Code
4
+ * 2.1.283's does (findings §40). A label sits on the editor's top border, the
5
+ * empty editor shows `Message @<label>…`, and what the user submits goes to
6
+ * the agent in Claude Code's mid-turn wrapper. The subagents extension owns
7
+ * the view and announces it over `AGENT_VIEW_CHANNEL`; the branding extension
8
+ * owns the editor and paints it (extensions share no module state).
9
+ */
10
+
11
+ import { fitPainted, visibleWidth } from "./text-width.ts";
12
+ import { cutPlainText } from "./tui-render.ts";
13
+
14
+ /** `{ target }` while an agent is viewed, `{}` when the view returns to main. */
15
+ export const AGENT_VIEW_CHANNEL = "one-code:agent-view";
16
+
17
+ export interface AgentViewTarget {
18
+ /** The top-border label: `@<name>` for a fork, the run's description otherwise. */
19
+ badge: string;
20
+ /** The empty editor's placeholder: `Message @<name or agent type>…`. */
21
+ placeholder: string;
22
+ }
23
+
24
+ export interface AgentViewAnnouncement {
25
+ target?: AgentViewTarget;
26
+ }
27
+
28
+ /** The slice of a live run the target is built from. */
29
+ export interface ViewedRun {
30
+ name: string;
31
+ agentType: string;
32
+ label: string;
33
+ }
34
+
35
+ /**
36
+ * The editor target for a viewed run, Claude Code's shapes: a fork shows
37
+ * `@what-word-is` on the border and `Message @what-word-is…`; a regular agent
38
+ * shows its description on the border and `Message @general-purpose…`.
39
+ */
40
+ export function agentViewTarget(run: ViewedRun, fork: boolean): AgentViewTarget {
41
+ const handle = fork ? run.name : run.agentType;
42
+ return { badge: fork ? `@${run.name}` : run.label, placeholder: `Message @${handle}…` };
43
+ }
44
+
45
+ /**
46
+ * Claude Code's wrapper for a message the user types at an agent, idle or
47
+ * running (findings §40), verbatim.
48
+ */
49
+ export function userMessageForAgent(text: string): string {
50
+ return `The user sent a new message while you were working:\n${text}\n\nThis is how Claude Code surfaces messages the user sends mid-turn — within the running turn, often alongside the next tool result, rather than as a separate conversation turn. Address the message above as you continue this turn.`;
51
+ }
52
+
53
+ const USER_MESSAGE_OPENER = "The user sent a new message while you were working:\n";
54
+ const USER_MESSAGE_CLOSER = "\n\nThis is how Claude Code surfaces messages the user sends mid-turn";
55
+
56
+ /** The user's own text inside `userMessageForAgent`'s wrapper, or undefined for any other prompt. */
57
+ export function unwrapUserMessage(prompt: string): string | undefined {
58
+ if (!prompt.startsWith(USER_MESSAGE_OPENER)) return undefined;
59
+ const end = prompt.lastIndexOf(USER_MESSAGE_CLOSER);
60
+ return end < USER_MESSAGE_OPENER.length ? undefined : prompt.slice(USER_MESSAGE_OPENER.length, end);
61
+ }
62
+
63
+ /**
64
+ * Draw `badge` (already painted, padding included, `badgeWidth` columns) at the
65
+ * right end of the editor's top border, followed by one border glyph:
66
+ * `─── @name ─`. The rest of the border, and anything pi embeds at its left
67
+ * (the working status), keeps its paint. No-op when the border is too narrow.
68
+ */
69
+ export function applyBorderBadge(lines: string[], badge: string, badgeWidth: number, borderGlyph: string): string[] {
70
+ const top = lines[0];
71
+ if (top === undefined) return lines;
72
+ const width = visibleWidth(top);
73
+ const tail = badgeWidth + 1;
74
+ if (width < tail + 4) return lines;
75
+ const head = fitPainted(top, width - tail).text;
76
+ const out = lines.slice();
77
+ out[0] = `${head}\x1b[0m${badge}${borderGlyph}`;
78
+ return out;
79
+ }
80
+
81
+ /** The label cut to fit a border of `width` columns, keeping room for the border itself. */
82
+ export function fitBadge(label: string, width: number): string {
83
+ const room = Math.floor(width / 2) - 3;
84
+ return room <= 1 ? "" : cutPlainText(label, room);
85
+ }