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.
- package/README.md +16 -5
- package/agents/general-purpose.md +2 -2
- package/docs/guide/README.md +2 -1
- package/docs/guide/bring-your-claude-code-setup.md +60 -9
- package/docs/guide/configuration.md +48 -14
- package/docs/guide/differences-from-claude-code.md +40 -28
- package/docs/guide/doctor.md +20 -13
- package/docs/guide/hooks.md +18 -3
- package/docs/guide/installation.md +26 -4
- package/docs/guide/permissions-modes-and-auto-mode.md +225 -49
- package/docs/guide/providers-and-models.md +137 -54
- package/docs/guide/reference.md +27 -15
- package/docs/guide/sessions-and-context.md +69 -16
- package/docs/guide/skills-plugins-and-mcp.md +57 -20
- package/docs/guide/subagents-and-workflows.md +95 -29
- package/docs/guide/tasks-and-background-work.md +101 -26
- package/docs/guide/terminal-interface.md +24 -12
- package/docs/guide/tools.md +46 -5
- package/docs/guide/troubleshooting.md +13 -9
- package/docs/guide/windows.md +58 -31
- package/extensions/artifacts/gallery.ts +227 -0
- package/extensions/artifacts/index.ts +288 -0
- package/extensions/artifacts/store.ts +570 -0
- package/extensions/artifacts/viewer.ts +199 -0
- package/extensions/ask-user/description.ts +67 -0
- package/extensions/ask-user/dialogs.ts +80 -0
- package/extensions/ask-user/index.ts +37 -33
- package/extensions/ask-user/questions.ts +32 -7
- package/extensions/ask-user/widget.ts +45 -25
- package/extensions/auto-mode/actions.ts +6 -0
- package/extensions/auto-mode/cache.ts +88 -0
- package/extensions/auto-mode/classifier-prompt.ts +5 -4
- package/extensions/auto-mode/classifier.ts +290 -65
- package/extensions/auto-mode/config.ts +72 -20
- package/extensions/auto-mode/decision-log.ts +2 -0
- package/extensions/auto-mode/defaults.ts +1 -1
- package/extensions/auto-mode/denials.ts +124 -0
- package/extensions/auto-mode/git-checkout-programs.ts +187 -0
- package/extensions/auto-mode/git-status-meta.ts +159 -0
- package/extensions/auto-mode/history.ts +189 -0
- package/extensions/auto-mode/instructions.ts +58 -52
- package/extensions/auto-mode/model-select.ts +159 -134
- package/extensions/auto-mode/paths.ts +57 -5
- package/extensions/auto-mode/pause.ts +57 -2
- package/extensions/auto-mode/prompt.ts +37 -21
- package/extensions/auto-mode/read-only-options.ts +724 -0
- package/extensions/auto-mode/resolved-paths-meta.ts +133 -0
- package/extensions/auto-mode/rules.ts +18 -1
- package/extensions/auto-mode/safety-floor.ts +361 -18
- package/extensions/auto-mode/setup-run.ts +20 -14
- package/extensions/auto-mode/shell-analysis.ts +764 -490
- package/extensions/auto-mode/shell-parse.ts +885 -0
- package/extensions/auto-mode/system-note.ts +33 -0
- package/extensions/auto-mode/transcript.ts +80 -51
- package/extensions/background/cron.ts +642 -0
- package/extensions/background/index.ts +614 -248
- package/extensions/background/loop-fire.ts +173 -0
- package/extensions/background/loop-preamble.ts +59 -0
- package/extensions/background/loop-skill.ts +163 -0
- package/extensions/background/monitor-batch.ts +53 -0
- package/extensions/background/noop-fold.ts +128 -0
- package/extensions/background/switch-guard.ts +56 -0
- package/extensions/background/wakeup.ts +178 -55
- package/extensions/bash/background.ts +53 -17
- package/extensions/bash/child.ts +7 -0
- package/extensions/bash/description.ts +188 -0
- package/extensions/bash/guards.ts +15 -67
- package/extensions/bash/index.ts +66 -35
- package/extensions/branding/compaction-keep.ts +63 -0
- package/extensions/branding/declined.ts +22 -0
- package/extensions/branding/index.ts +133 -9
- package/extensions/branding/prompt-editor.ts +53 -6
- package/extensions/branding/queued-edit.ts +31 -0
- package/extensions/branding/replaced-builtins.ts +68 -0
- package/extensions/branding/startup.ts +33 -71
- package/extensions/btw/index.ts +250 -78
- package/extensions/btw/panel.ts +228 -100
- package/extensions/btw/prompt.ts +41 -4
- package/extensions/cache-marks/index.ts +42 -0
- package/extensions/claude-compat/index.ts +7 -11
- package/extensions/claude-context/index.ts +436 -63
- package/extensions/compaction/cut.ts +157 -0
- package/extensions/compaction/index.ts +219 -75
- package/extensions/compaction/kept-thinking.ts +62 -0
- package/extensions/compaction/prompt.ts +15 -12
- package/extensions/context-budget/budget.ts +16 -2
- package/extensions/context-budget/index.ts +35 -15
- package/extensions/context-management/index.ts +36 -28
- package/extensions/doctor/build.ts +59 -8
- package/extensions/doctor/builtins.ts +84 -0
- package/extensions/doctor/cli.ts +7 -1
- package/extensions/doctor/compat.ts +88 -23
- package/extensions/doctor/dependencies.ts +41 -5
- package/extensions/doctor/fix-prompt.ts +9 -2
- package/extensions/doctor/index.ts +102 -58
- package/extensions/doctor/models.ts +102 -63
- package/extensions/doctor/presets.ts +42 -27
- package/extensions/doctor/report.ts +19 -2
- package/extensions/doctor/viewer.ts +49 -27
- package/extensions/effort/index.ts +141 -25
- package/extensions/effort/settings.ts +21 -0
- package/extensions/effort/slider.ts +24 -20
- package/extensions/exit/index.ts +32 -1
- package/extensions/file-tracker/index.ts +420 -42
- package/extensions/file-tracker/replay.ts +137 -24
- package/extensions/file-tracker/restore.ts +66 -0
- package/extensions/file-tracker/results.ts +66 -0
- package/extensions/file-tracker/shell-reads.ts +211 -0
- package/extensions/file-tracker/tracker.ts +45 -15
- package/extensions/footer/footer-line.ts +45 -24
- package/extensions/footer/index.ts +47 -2
- package/extensions/footer/pr.ts +34 -5
- package/extensions/hooks/compaction.ts +7 -0
- package/extensions/hooks/executor.ts +32 -30
- package/extensions/hooks/index.ts +209 -58
- package/extensions/hooks/matcher.ts +3 -0
- package/extensions/hooks/prompt-context.ts +59 -0
- package/extensions/hooks/protocol.ts +41 -3
- package/extensions/hooks/settings.ts +50 -19
- package/extensions/hooks/subagent-bridge.ts +2 -0
- package/extensions/hooks/trust.ts +26 -12
- package/extensions/idle-compact/index.ts +119 -0
- package/extensions/idle-compact/policy.ts +143 -0
- package/extensions/init/index.ts +11 -10
- package/extensions/init/prompt.ts +25 -32
- package/extensions/interrupted/index.ts +13 -1
- package/extensions/kept-thinking/index.ts +27 -0
- package/extensions/ledger/index.ts +46 -0
- package/extensions/ledger/wire.ts +154 -0
- package/extensions/lib/agent-cron.ts +146 -0
- package/extensions/lib/agent-loader.ts +18 -3
- package/extensions/lib/agent-view.ts +85 -0
- package/extensions/lib/anthropic-auth.ts +98 -0
- package/extensions/lib/anthropic-payload.ts +82 -1
- package/extensions/lib/anthropic-server-call.ts +230 -0
- package/extensions/lib/anthropic-server-tools.ts +485 -0
- package/extensions/lib/app-launch.mjs +94 -0
- package/extensions/lib/argument-hints.ts +190 -0
- package/extensions/lib/atomic-write.ts +6 -2
- package/extensions/lib/auto-memory.ts +75 -0
- package/extensions/lib/bash-parser.ts +84 -0
- package/extensions/lib/btw-fork.ts +93 -0
- package/extensions/lib/child-extensions.ts +52 -0
- package/extensions/lib/child-writes.ts +45 -0
- package/extensions/lib/claude-context.ts +515 -100
- package/extensions/lib/claude-external-includes.ts +68 -0
- package/extensions/lib/claude-id.ts +22 -0
- package/extensions/lib/claude-rules.ts +323 -0
- package/extensions/lib/claude-settings.ts +76 -4
- package/extensions/lib/compaction-boundary.ts +36 -0
- package/extensions/lib/compaction-keep.mjs +83 -0
- package/extensions/lib/config-mode.ts +112 -0
- package/extensions/lib/consent-dialogs.ts +63 -0
- package/extensions/lib/consent-preview.ts +5 -1
- package/extensions/lib/consent-stores.ts +48 -0
- package/extensions/lib/context-facts.ts +97 -0
- package/extensions/lib/covered-messages.ts +23 -0
- package/extensions/lib/deferred.ts +359 -25
- package/extensions/lib/dock-rows.ts +60 -0
- package/extensions/lib/empty-tool-result.ts +27 -0
- package/extensions/lib/environment-block.ts +76 -0
- package/extensions/lib/fetch-timeout.ts +24 -6
- package/extensions/{system-prompt → lib}/git-status.ts +51 -13
- package/extensions/lib/git.ts +75 -13
- package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
- package/extensions/lib/grammars/README.md +2 -0
- package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
- package/extensions/lib/guide-docs.ts +45 -0
- package/extensions/lib/headless-output.ts +46 -0
- package/extensions/lib/hook-gate.ts +7 -7
- package/extensions/lib/interrupt.ts +20 -4
- package/extensions/lib/key-input.ts +455 -0
- package/extensions/lib/keys.ts +14 -1
- package/extensions/lib/ledger/fold-memo.ts +49 -0
- package/extensions/lib/ledger/fold.ts +265 -0
- package/extensions/lib/ledger/layout.ts +72 -0
- package/extensions/lib/ledger/plan.ts +102 -0
- package/extensions/lib/ledger/records.ts +276 -0
- package/extensions/lib/ledger/state.ts +47 -0
- package/extensions/lib/ledger/validate.ts +141 -0
- package/extensions/lib/ledger/wire.ts +632 -0
- package/extensions/lib/local-command.ts +71 -19
- package/extensions/lib/mcp-overrides.ts +1 -1
- package/extensions/lib/mcp-share.ts +46 -0
- package/extensions/lib/memory-content.ts +146 -0
- package/extensions/lib/memory-index.ts +18 -0
- package/extensions/lib/memory.ts +134 -123
- package/extensions/lib/model-catalog/huggingface.json +178 -0
- package/extensions/lib/model-catalog/models-dev.json +18533 -0
- package/extensions/lib/model-catalog/openrouter.json +5050 -0
- package/extensions/lib/model-catalog-data.ts +513 -0
- package/extensions/lib/model-catalog.ts +462 -0
- package/extensions/lib/model-choice-warnings.ts +137 -0
- package/extensions/lib/model-pairings.ts +152 -0
- package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
- package/extensions/lib/model-policy.ts +112 -15
- package/extensions/lib/model-tier.ts +283 -355
- package/extensions/lib/model-unusable.ts +1 -1
- package/extensions/lib/newer-model.ts +100 -0
- package/extensions/lib/notices.ts +87 -0
- package/extensions/lib/notifications.ts +357 -73
- package/extensions/lib/one-code-settings.ts +74 -8
- package/extensions/lib/open-browser.ts +63 -0
- package/extensions/lib/openrouter-generation.ts +94 -0
- package/extensions/lib/operation-deadline.ts +30 -0
- package/extensions/lib/original-command.ts +4 -4
- package/extensions/lib/package-version.ts +1 -1
- package/extensions/lib/panel-focus.ts +134 -0
- package/extensions/lib/paths.ts +17 -2
- package/extensions/lib/permission-gate.ts +72 -6
- package/extensions/lib/persisted-output.ts +60 -8
- package/extensions/lib/pi-ai-estimate.ts +19 -3
- package/extensions/lib/pi-version.ts +38 -2
- package/extensions/lib/plan-mode-channels.ts +42 -5
- package/extensions/lib/plugins.ts +26 -17
- package/extensions/lib/powershell-parse-script.ts +208 -0
- package/extensions/lib/powershell-parser.ts +344 -0
- package/extensions/lib/previous-mark.ts +101 -0
- package/extensions/lib/process-state.ts +48 -0
- package/extensions/lib/process-tree.ts +132 -8
- package/extensions/lib/prompt-options.ts +82 -0
- package/extensions/lib/queued-delivery.ts +44 -0
- package/extensions/lib/reminders.ts +370 -358
- package/extensions/lib/replaced-builtins.mjs +199 -0
- package/extensions/lib/replay-call.ts +70 -0
- package/extensions/lib/request-replay.ts +547 -0
- package/extensions/lib/scratchpad.ts +81 -14
- package/extensions/lib/send-now-channel.ts +7 -0
- package/extensions/lib/session-model-tier.ts +51 -0
- package/extensions/lib/session-work.ts +64 -0
- package/extensions/lib/settings-channels.ts +4 -4
- package/extensions/lib/shell-exit-semantics.ts +170 -0
- package/extensions/lib/shell-quote.ts +11 -0
- package/extensions/lib/shell-result.ts +70 -0
- package/extensions/lib/shell-spawn.ts +70 -6
- package/extensions/lib/shell-tool.ts +75 -25
- package/extensions/lib/side-call-cache.ts +124 -0
- package/extensions/lib/side-call-run.ts +96 -0
- package/extensions/lib/side-call-usage.ts +38 -0
- package/extensions/lib/side-call.ts +24 -1
- package/extensions/lib/single-flight.ts +18 -0
- package/extensions/lib/skill-body.ts +42 -0
- package/extensions/lib/skill-scan.ts +63 -24
- package/extensions/lib/spill-file.ts +140 -0
- package/extensions/lib/subagent-handback.ts +112 -0
- package/extensions/lib/system-role.ts +156 -0
- package/extensions/lib/terminal-text.ts +114 -0
- package/extensions/lib/text-width.ts +47 -16
- package/extensions/lib/timer-ops.ts +20 -0
- package/extensions/lib/tool-additions.ts +129 -0
- package/extensions/lib/tool-call-corruption.ts +134 -0
- package/extensions/lib/tool-path.ts +66 -0
- package/extensions/lib/tool-variants.ts +69 -0
- package/extensions/lib/tui-render.ts +255 -27
- package/extensions/lib/turn-budget-layout.ts +159 -0
- package/extensions/lib/update-check.mjs +15 -2
- package/extensions/lib/usage-bus.ts +1 -1
- package/extensions/lib/user-denial.ts +38 -0
- package/extensions/lib/workspace-channel.ts +16 -0
- package/extensions/lib/worktree-channel.ts +31 -3
- package/extensions/lib/worktree-isolation.ts +40 -10
- package/extensions/lsp/client.ts +8 -9
- package/extensions/lsp/index.ts +76 -12
- package/extensions/lsp/keep-alive.ts +1 -1
- package/extensions/lsp/trust.ts +119 -0
- package/extensions/lsp/watcher.ts +1 -1
- package/extensions/mcp/announce.ts +90 -43
- package/extensions/mcp/client.ts +161 -48
- package/extensions/mcp/config.ts +132 -8
- package/extensions/mcp/discovery.ts +19 -0
- package/extensions/mcp/index.ts +220 -121
- package/extensions/mcp/oauth/browser.ts +38 -17
- package/extensions/mcp/oauth/flow.ts +25 -17
- package/extensions/mcp/oauth/provider.ts +14 -8
- package/extensions/mcp/oauth/store.ts +34 -16
- package/extensions/mcp/panel/keys.ts +12 -11
- package/extensions/mcp/panel/render.ts +9 -6
- package/extensions/mcp/schema.ts +104 -5
- package/extensions/mcp/trust.ts +164 -73
- package/extensions/memory/entries.ts +23 -14
- package/extensions/memory/index.ts +134 -28
- package/extensions/memory/open-external.ts +33 -18
- package/extensions/memory/panel.ts +135 -26
- package/extensions/model-default/index.ts +86 -19
- package/extensions/newer-model/index.ts +31 -0
- package/extensions/notebook/description.ts +18 -0
- package/extensions/notebook/index.ts +6 -5
- package/extensions/notebook/notebook.ts +1 -1
- package/extensions/permissions/index.ts +1426 -398
- package/extensions/permissions/matcher.ts +425 -74
- package/extensions/permissions/modes.ts +11 -4
- package/extensions/permissions/panel/host.ts +130 -0
- package/extensions/permissions/panel/keys.ts +48 -0
- package/extensions/permissions/panel/render.ts +450 -0
- package/extensions/permissions/panel/state.ts +550 -0
- package/extensions/permissions/powershell-cmdlets.ts +198 -0
- package/extensions/permissions/powershell-paths.ts +197 -0
- package/extensions/permissions/powershell-rules.ts +207 -280
- package/extensions/permissions/powershell-tree.ts +557 -0
- package/extensions/permissions/project-trust.ts +27 -11
- package/extensions/permissions/protected-paths.ts +14 -2
- package/extensions/permissions/session-grant.ts +6 -0
- package/extensions/permissions/settings.ts +166 -31
- package/extensions/permissions/subagent-gate.ts +25 -0
- package/extensions/permissions/workspace.ts +78 -0
- package/extensions/plan-mode/index.ts +276 -92
- package/extensions/plan-mode/reminder.ts +89 -21
- package/extensions/plan-mode/run-state.ts +26 -0
- package/extensions/plan-mode/texts.ts +177 -0
- package/extensions/plan-mode/viewer.ts +92 -27
- package/extensions/plugins/counts.ts +3 -3
- package/extensions/plugins/index.ts +64 -46
- package/extensions/plugins/install/install.ts +16 -0
- package/extensions/plugins/marketplace/sync.ts +9 -3
- package/extensions/plugins/marketplace/types.ts +3 -1
- package/extensions/plugins/panel/keys.ts +21 -24
- package/extensions/plugins/panel/render.ts +12 -6
- package/extensions/plugins/shell-expand.ts +107 -0
- package/extensions/plugins/template.ts +32 -15
- package/extensions/powershell/child.ts +7 -0
- package/extensions/powershell/description.ts +38 -6
- package/extensions/powershell/guards.ts +1 -1
- package/extensions/powershell/index.ts +14 -7
- package/extensions/recap/index.ts +73 -71
- package/extensions/recap/prompt.ts +2 -2
- package/extensions/recap/scheduler.ts +3 -5
- package/extensions/search-tools/grep-merge.ts +39 -0
- package/extensions/search-tools/index.ts +0 -0
- package/extensions/send-now/chord.ts +62 -0
- package/extensions/send-now/index.ts +133 -0
- package/extensions/session-title/index.ts +3 -0
- package/extensions/session-title/title.ts +4 -1
- package/extensions/skill/announce.ts +30 -0
- package/extensions/skill/code-review.ts +97 -0
- package/extensions/skill/index.ts +469 -132
- package/extensions/skill/invoke.ts +118 -75
- package/extensions/skill/listing.ts +133 -9
- package/extensions/skill/panel/keys.ts +15 -16
- package/extensions/skill/panel/render.ts +5 -2
- package/extensions/skill/panel/state.ts +12 -1
- package/extensions/subagents/agent-args.ts +39 -0
- package/extensions/subagents/agent-description.ts +132 -0
- package/extensions/subagents/agents.ts +146 -31
- package/extensions/subagents/default-model.ts +2 -3
- package/extensions/subagents/delegation-steer.ts +4 -4
- package/extensions/subagents/fork-cache.ts +82 -0
- package/extensions/subagents/guide-agent.ts +189 -0
- package/extensions/subagents/hand-back-review.ts +3 -0
- package/extensions/subagents/index.ts +1594 -852
- package/extensions/subagents/live-runs.ts +26 -6
- package/extensions/subagents/model-select.ts +38 -25
- package/extensions/subagents/outcome.ts +5 -2
- package/extensions/subagents/panel-keys.ts +73 -31
- package/extensions/subagents/panel-render.ts +120 -96
- package/extensions/subagents/panel-widget.ts +60 -89
- package/extensions/subagents/parent-mode.ts +50 -0
- package/extensions/subagents/pending-claim.ts +18 -1
- package/extensions/subagents/runner.ts +135 -80
- package/extensions/subagents/runs.ts +44 -5
- package/extensions/subagents/send-to-main-tool.ts +1 -1
- package/extensions/subagents/session-turns.ts +32 -8
- package/extensions/subagents/shell-panel.ts +36 -178
- package/extensions/subagents/tasks-dialog.ts +170 -0
- package/extensions/subagents/viewport-mirror.ts +95 -0
- package/extensions/subagents/worktree.ts +57 -13
- package/extensions/system-prompt/environment.ts +19 -19
- package/extensions/system-prompt/idle-turn.ts +65 -0
- package/extensions/system-prompt/index.ts +165 -47
- package/extensions/system-prompt/template.ts +34 -51
- package/extensions/system-prompt/tiers/common.ts +23 -64
- package/extensions/system-prompt/tiers/frontier.ts +32 -11
- package/extensions/system-prompt/tiers/low.ts +67 -45
- package/extensions/system-prompt/tiers/mid.ts +96 -38
- package/extensions/system-reminder/index.ts +204 -70
- package/extensions/tasks/descriptions.ts +190 -0
- package/extensions/tasks/index.ts +120 -41
- package/extensions/tasks/store.ts +83 -16
- package/extensions/tool-call-corruption/child.ts +7 -0
- package/extensions/tool-call-corruption/index.ts +237 -0
- package/extensions/tool-search/index.ts +366 -144
- package/extensions/tool-style/index.ts +68 -10
- package/extensions/tool-style/write-description.ts +40 -0
- package/extensions/tool-style/write-render.ts +237 -0
- package/extensions/turn-duration/index.ts +7 -3
- package/extensions/turn-duration/line.ts +47 -4
- package/extensions/web/backends.ts +6 -9
- package/extensions/web/description.ts +50 -0
- package/extensions/web/index.ts +118 -53
- package/extensions/web/native-call.ts +15 -0
- package/extensions/web/native-results.ts +41 -0
- package/extensions/web-fetch/description.ts +42 -0
- package/extensions/web-fetch/extract.ts +53 -6
- package/extensions/web-fetch/index.ts +125 -76
- package/extensions/web-fetch/page-content.ts +19 -0
- package/extensions/web-fetch/response.ts +49 -0
- package/extensions/web-fetch/summarize.ts +9 -10
- package/extensions/workflow/agent-session.ts +108 -18
- package/extensions/workflow/args.ts +24 -0
- package/extensions/workflow/description.ts +67 -0
- package/extensions/workflow/globals.ts +17 -3
- package/extensions/workflow/index.ts +157 -162
- package/extensions/workflow/records.ts +1 -0
- package/extensions/workflow/run-manager.ts +33 -6
- package/extensions/workflow/saved-workflows.ts +3 -2
- package/extensions/workflow/types.ts +22 -1
- package/extensions/workflow/viewer.ts +54 -24
- package/extensions/workflow/widget.ts +20 -0
- package/extensions/worktree/descriptions.ts +91 -0
- package/extensions/worktree/guards.ts +316 -54
- package/extensions/worktree/index.ts +162 -44
- package/extensions/worktree/powershell-guards.ts +167 -0
- package/extensions/worktree/rewrite.ts +14 -16
- package/package.json +21 -7
- package/skills/artifact-design/SKILL.md +85 -0
- package/skills/code-review/SKILL.md +20 -20
- package/skills/code-review/cells/arguments-and-flags.md +19 -0
- package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
- package/skills/code-review/cells/high-8-inline.md +122 -0
- package/skills/code-review/cells/low-1-pass-max4.md +27 -0
- package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
- package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
- package/skills/code-review/cells/medium-3x5-verify.md +132 -0
- package/skills/code-review/cells/minimal-prompt.md +32 -0
- package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
- package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
- package/skills/fewer-permission-prompts/SKILL.md +1 -1
- package/skills/loop/SKILL.md +11 -0
- package/skills/security-review/SKILL.md +2 -5
- package/skills/simplify/SKILL.md +7 -5
- package/skills/workflow-authoring/SKILL.md +143 -0
- package/themes/onecode-light.json +1 -1
- package/themes/onecode.json +1 -1
- package/types/app.d.ts +58 -0
- package/types/community-packages.d.ts +26 -1
- package/extensions/auto-mode/recoverability.ts +0 -229
- package/extensions/lib/capability-index.ts +0 -466
- package/extensions/lib/model-facts.generated.json +0 -2675
- package/extensions/lib/model-facts.ts +0 -162
- package/extensions/tool-search/announce.ts +0 -42
|
@@ -1,40 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `/init` prompt
|
|
3
|
-
*
|
|
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
|
-
*
|
|
7
|
-
*
|
|
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 = `
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
117
|
-
*
|
|
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
|
-
|
|
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
|
+
}
|