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
|
@@ -26,194 +26,361 @@
|
|
|
26
26
|
* and pi's event bus only delivers to listeners already registered.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
29
|
+
import type { ExtensionAPI, SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
30
30
|
import { Type } from "typebox";
|
|
31
31
|
import {
|
|
32
32
|
DEFER_CHANNEL,
|
|
33
33
|
deferredAddendumText,
|
|
34
34
|
deferredMissReminderText,
|
|
35
35
|
DeferredRegistry,
|
|
36
|
+
deferralEnabled,
|
|
36
37
|
deferredReminderText,
|
|
38
|
+
loaderUnavailableReminderText,
|
|
37
39
|
type DeferRequest,
|
|
38
40
|
resultText,
|
|
39
41
|
searchTools,
|
|
40
|
-
|
|
42
|
+
selectedRequests,
|
|
43
|
+
type ResponsesToolLoadModel,
|
|
41
44
|
stabilizeDeferredTools,
|
|
45
|
+
stabilizeResponsesToolLoads,
|
|
46
|
+
responsesToolLoadCompat,
|
|
42
47
|
supportsToolReferences,
|
|
43
48
|
toolNotFoundName,
|
|
44
49
|
toolSearchLoads,
|
|
50
|
+
toolsInUse,
|
|
51
|
+
callableDeferredTools,
|
|
52
|
+
reconcileActive,
|
|
53
|
+
WITHHOLD_CHANNEL,
|
|
54
|
+
DEFERRED_QUERY_CHANNEL,
|
|
55
|
+
type DeferredQuery,
|
|
56
|
+
deferredRemovedText,
|
|
57
|
+
withheldMissReminderText,
|
|
58
|
+
unavailableToolReminderText,
|
|
45
59
|
} from "../lib/deferred.ts";
|
|
46
|
-
import { looksLikeAnthropicRequest } from "../lib/anthropic-payload.ts";
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
60
|
+
import { looksLikeAnthropicRequest, MID_CONVERSATION_SYSTEM_BETA, withBetas } from "../lib/anthropic-payload.ts";
|
|
61
|
+
import { liftAddenda, supportsToolAdditions, TOOL_ADDITION_BETAS, withToolAdditions } from "../lib/tool-additions.ts";
|
|
62
|
+
import { inContextEntries } from "../lib/compaction-boundary.ts";
|
|
63
|
+
import { CALLABLE_TOOLS } from "../lib/ledger/fold.ts";
|
|
64
|
+
import { foldOnce, sameEntries } from "../lib/ledger/fold-memo.ts";
|
|
65
|
+
import { recordedToolNames } from "../lib/ledger/wire.ts";
|
|
66
|
+
import { MCP_TOOLS_CHANNEL } from "../lib/mcp-share.ts";
|
|
67
|
+
import { sessionIdentity } from "../lib/anthropic-auth.ts";
|
|
68
|
+
import type { EntryLike } from "../lib/ledger/records.ts";
|
|
69
|
+
import { CONTEXT_ORDER, REMINDER_CHANNEL, RENDER_CHANNEL, type RenderRequest } from "../lib/reminders.ts";
|
|
49
70
|
import { sessionAlive } from "../lib/session-lifecycle.ts";
|
|
50
71
|
import { ccToolRenderers } from "../lib/tui-render.ts";
|
|
51
72
|
import { normalizeToolName } from "../permissions/matcher.ts";
|
|
52
|
-
import { applyAnnouncement, planAnnouncement } from "./announce.ts";
|
|
53
73
|
import { registerLocalCommand } from "../lib/local-command.ts";
|
|
54
74
|
|
|
55
|
-
/**
|
|
56
|
-
const
|
|
75
|
+
/** The told mode for "this epoch's message 1 lists deferred tools this run cannot load", said once per epoch. */
|
|
76
|
+
const UNAVAILABLE_MODE = "deferred-unavailable";
|
|
77
|
+
|
|
78
|
+
/** A session manager as far as the reconciler reads it (a test double may lack `getBranch`). */
|
|
79
|
+
type SessionLike = { getBranch?: () => unknown[] };
|
|
80
|
+
const branchOf = (session: SessionLike | undefined) => (session?.getBranch?.() ?? []) as EntryLike[];
|
|
57
81
|
|
|
58
82
|
export default function toolSearchExtension(pi: ExtensionAPI) {
|
|
59
83
|
// Owned here (see lib/deferred.ts): the only instance, fed over DEFER_CHANNEL.
|
|
60
84
|
const deferredRegistry = new DeferredRegistry();
|
|
85
|
+
// Names WITHHOLD_CHANNEL removed from the registry, kept only so a direct
|
|
86
|
+
// call to one still gets an explanation (tool_execution_end below) instead
|
|
87
|
+
// of a bare "not found": the registry itself can't answer that, since
|
|
88
|
+
// removal is what makes the name stop being deferred (searchable/loadable).
|
|
89
|
+
// A re-defer (a model change bringing it back) clears the name from here.
|
|
90
|
+
const withheldNames = new Set<string>();
|
|
61
91
|
let sessionStarted = false;
|
|
62
|
-
// The deferred-tools listing is a first-prepend reminder on message 1, part of
|
|
63
|
-
// the cached prefix of every later request. Before the first request nothing
|
|
64
|
-
// is cached, so the listing is (re)written freely; after it the listing is
|
|
65
|
-
// FROZEN and later arrivals ride a one-shot addendum where the model reads
|
|
66
|
-
// next (./announce.ts — a rewrite re-cached 16.7k tokens of history on
|
|
67
|
-
// request 2 of every run whose MCP tools registered inside the debounce,
|
|
68
|
-
// measured 2026-09-04). While the initial MCP connect is still settling the
|
|
69
|
-
// addendum waits for settle, so one burst of per-tool defers is one notice.
|
|
70
|
-
let requestSent = false;
|
|
71
|
-
let mcpSettled = false;
|
|
72
|
-
let announcePending = false;
|
|
73
|
-
let announceTimer: NodeJS.Timeout | undefined;
|
|
74
92
|
// After session_shutdown every pi.* call throws (the runner is invalidated,
|
|
75
|
-
// findings §8)
|
|
76
|
-
// be an uncaught exception and take pi down (LIFECYCLE-REVIEW L1).
|
|
93
|
+
// findings §8): an event that lands then must do nothing (LIFECYCLE-REVIEW L1).
|
|
77
94
|
const alive = sessionAlive(pi);
|
|
78
|
-
/**
|
|
79
|
-
|
|
95
|
+
/** The session's manager, for folding what the current epoch has told and loaded outside a hook that carries one. */
|
|
96
|
+
let session: SessionLike | undefined;
|
|
97
|
+
/** The session's model, for a decision made outside a hook that carries one. */
|
|
98
|
+
let currentModel: { provider?: string; api?: string; id?: string; compat?: unknown } | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* The branch's fold with the live identity, so a model or auth switch
|
|
101
|
+
* counts. Shared with the ledger (lib/ledger/fold-memo.ts): the fold this
|
|
102
|
+
* turn_start takes serves the request's `context` hook, and a server's
|
|
103
|
+
* connect defers its tools in a burst with nothing appended between.
|
|
104
|
+
*/
|
|
105
|
+
const foldOf = (owner: SessionLike | undefined, branch: EntryLike[], model: typeof currentModel) => foldOnce(owner, branch, sessionIdentity(pi, model));
|
|
80
106
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
107
|
+
/** Whether tool_search is registered; a `--tools` list without it turns deferral off (lib/deferred.ts). */
|
|
108
|
+
const deferring = () => deferralEnabled(pi.getAllTools());
|
|
109
|
+
const searchableTools = (all = pi.getAllTools()) => {
|
|
110
|
+
if (!deferralEnabled(all)) return [];
|
|
111
|
+
return all
|
|
84
112
|
.filter((tool) => deferredRegistry.has(tool.name))
|
|
85
113
|
.map((tool) => ({
|
|
86
114
|
name: tool.name,
|
|
87
115
|
description: tool.description ?? "",
|
|
88
116
|
keywords: deferredRegistry.keywordsFor(tool.name),
|
|
89
117
|
}));
|
|
118
|
+
};
|
|
90
119
|
|
|
91
|
-
|
|
120
|
+
/**
|
|
121
|
+
* The deferred-tools listing as live state: what an epoch's message 1 says.
|
|
122
|
+
* Inside an epoch message 1 is the record, so a change here is announced by
|
|
123
|
+
* the render step below, never by rewriting it.
|
|
124
|
+
*/
|
|
125
|
+
const publishListing = () => {
|
|
92
126
|
if (!alive()) return;
|
|
93
127
|
const available = searchableTools();
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
scope: "every-turn",
|
|
98
|
-
key: "deferred-tools",
|
|
99
|
-
text: deferredReminderText(available),
|
|
100
|
-
placement: "first-prepend",
|
|
101
|
-
order: CONTEXT_ORDER.deferredTools,
|
|
102
|
-
});
|
|
103
|
-
} else if (plan.kind === "addendum") {
|
|
104
|
-
// Unkeyed on purpose: a keyed next-turn reminder replaces its
|
|
105
|
-
// predecessor, and an addendum replaced before delivery would lose names.
|
|
106
|
-
pi.events.emit(REMINDER_CHANNEL, { text: deferredAddendumText(plan.added) });
|
|
128
|
+
if (available.length === 0) {
|
|
129
|
+
pi.events.emit(REMINDER_CHANNEL, { key: "deferred-tools", remove: true });
|
|
130
|
+
return;
|
|
107
131
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const scheduleAnnounce = () => {
|
|
116
|
-
clearAnnounce();
|
|
117
|
-
if (!alive()) return;
|
|
118
|
-
announceTimer = setTimeout(() => {
|
|
119
|
-
announceTimer = undefined;
|
|
120
|
-
// Decided at fire time, not schedule time: a request may have gone out
|
|
121
|
-
// during the debounce (the -p race).
|
|
122
|
-
if (requestSent && !mcpSettled) {
|
|
123
|
-
announcePending = true;
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
announce();
|
|
127
|
-
}, ANNOUNCE_DEBOUNCE_MS);
|
|
128
|
-
announceTimer.unref?.();
|
|
132
|
+
pi.events.emit(REMINDER_CHANNEL, {
|
|
133
|
+
key: "deferred-tools",
|
|
134
|
+
text: deferredReminderText(available),
|
|
135
|
+
placement: "first-prepend",
|
|
136
|
+
order: CONTEXT_ORDER.deferredTools,
|
|
137
|
+
facts: [{ kind: "tools", names: available.map((tool) => tool.name) }],
|
|
138
|
+
});
|
|
129
139
|
};
|
|
130
140
|
|
|
141
|
+
/** Deferred tools this process has seen loadable: one of them missing later is known to be gone. */
|
|
142
|
+
const seenHere = new Set<string>();
|
|
143
|
+
/** True once the MCP extension published its settled tool set: an MCP tool missing then is gone. */
|
|
144
|
+
let mcpSettled = false;
|
|
131
145
|
pi.events.on(MCP_TOOLS_CHANNEL, (data) => {
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
if ((data as { settled?: boolean } | undefined)?.settled) mcpSettled = true;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Inside an epoch, a deferred tool that registered since the epoch's listing
|
|
150
|
+
// is announced on the request's tail: computed when the request goes out,
|
|
151
|
+
// from what is loadable then and what the epoch has told, so a tool withheld
|
|
152
|
+
// in the meantime is never announced (the H1 class). Where the model takes
|
|
153
|
+
// Claude Code's tool_addition, the addendum becomes one (the context and
|
|
154
|
+
// payload hooks below) and the tool is callable at once.
|
|
155
|
+
pi.events.on(RENDER_CHANNEL, (data) => {
|
|
156
|
+
const request = data as RenderRequest;
|
|
157
|
+
if (request.epoch) return;
|
|
158
|
+
const told = request.told.get("tools") ?? new Set<string>();
|
|
159
|
+
if (!deferring()) {
|
|
160
|
+
// The epoch's message 1 lists deferred tools this run cannot load.
|
|
161
|
+
if (told.size > 0 && !request.modes.has(UNAVAILABLE_MODE)) {
|
|
162
|
+
request.blocks.push({ text: loaderUnavailableReminderText(), facts: [{ kind: "mode", key: UNAVAILABLE_MODE, on: true }] });
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
// A tool told and missing now is announced gone once its absence is known
|
|
167
|
+
// (the fold then holds it untold, so a return is announced as new): this
|
|
168
|
+
// process saw it leave, or it is an MCP tool and the MCP connects have
|
|
169
|
+
// settled (a resume sends its first request before a slow server
|
|
170
|
+
// registers, and a fork never had the parent's own tools). A tool
|
|
171
|
+
// withdrawn for the model is not gone: a call to it says so.
|
|
172
|
+
const loadable = new Set(searchableTools().map((tool) => tool.name));
|
|
173
|
+
for (const name of loadable) seenHere.add(name);
|
|
174
|
+
const absent = (name: string) => seenHere.has(name) || (name.startsWith("mcp__") && mcpSettled);
|
|
175
|
+
const removed = [...told].filter((name) => !loadable.has(name) && !withheldNames.has(name) && absent(name));
|
|
176
|
+
if (removed.length > 0) request.blocks.push({ text: deferredRemovedText(removed), facts: [{ kind: "tools", names: removed, removed: true }] });
|
|
177
|
+
const added = [...loadable].filter((name) => !told.has(name));
|
|
178
|
+
if (added.length === 0) return;
|
|
179
|
+
if (!supportsToolAdditions(request.model)) {
|
|
180
|
+
request.blocks.push({ text: deferredAddendumText(added), facts: [{ kind: "tools", names: added }] });
|
|
181
|
+
return;
|
|
137
182
|
}
|
|
183
|
+
// Here an addendum is a tool_addition, callable at once: only a tool the
|
|
184
|
+
// reconciler activated before pi fixed this turn's tools (turn_start) is
|
|
185
|
+
// announced; one that arrived since waits for the next request.
|
|
186
|
+
const active = new Set(pi.getActiveTools());
|
|
187
|
+
const callable = added.filter((name) => active.has(name));
|
|
188
|
+
if (callable.length > 0) request.blocks.push({ text: deferredAddendumText(callable), facts: [{ kind: "tools", names: callable, callable: true }] });
|
|
138
189
|
});
|
|
139
190
|
|
|
140
|
-
|
|
141
|
-
|
|
191
|
+
/** The addenda the last `context` pass lifted off tool results, by call id (lib/tool-additions.ts). */
|
|
192
|
+
let addendaByCall = new Map<string, string[]>();
|
|
193
|
+
pi.on("context", (event, ctx) => {
|
|
194
|
+
addendaByCall = new Map();
|
|
195
|
+
if (!supportsToolAdditions(ctx?.model) || !Array.isArray(event?.messages)) return undefined;
|
|
196
|
+
const lifted = liftAddenda(event.messages);
|
|
197
|
+
addendaByCall = lifted.byCall;
|
|
198
|
+
return lifted.messages === event.messages ? undefined : { messages: lifted.messages };
|
|
142
199
|
});
|
|
143
200
|
|
|
144
|
-
// Every tool_search load
|
|
145
|
-
//
|
|
146
|
-
// it to reference a loaded tool from the
|
|
201
|
+
// Every tool_search load still in context (call id → names), derived from the
|
|
202
|
+
// branch by each reconcile, plus the loads this turn made that are not on the
|
|
203
|
+
// branch yet: the wire hook needs it to reference a loaded tool from the
|
|
204
|
+
// result that loaded it.
|
|
147
205
|
let loads = new Map<string, string[]>();
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// Claude Code keeps a ToolSearch-loaded tool callable across a resume (its
|
|
151
|
-
// tool_reference blocks stay in the history), so deferring them again would
|
|
152
|
-
// send the model into a "Tool <name> not found" round trip. pi restores them
|
|
153
|
-
// as active from the transcript before session_start; deferAll and the
|
|
154
|
-
// late-defer handler leave those alone. A load made during this session
|
|
155
|
-
// activates the tool directly and never needs this set.
|
|
156
|
-
let loadedBefore = new Set<string>();
|
|
157
|
-
|
|
158
|
-
// On models that take client-side tool references (first-party Claude >= 4.5,
|
|
159
|
-
// not Haiku) the `tools` array is held byte-identical to request 1's: every
|
|
160
|
-
// deferred tool rides every Anthropic request as a `defer_loading: true`
|
|
161
|
-
// definition, and a tool that tool_search loaded — which pi 0.86 promotes into
|
|
162
|
-
// the eager list, re-caching everything from the tools block down — is
|
|
163
|
-
// demoted back to deferred and loaded through `tool_reference` blocks in the
|
|
164
|
-
// tool_search result that activated it (lib/deferred.ts stabilizeDeferredTools
|
|
165
|
-
// has the rules; docs/decisions/caching.md the measurements).
|
|
166
|
-
pi.on("before_provider_request", (event, ctx) => {
|
|
167
|
-
if (!supportsToolReferences(ctx.model as { provider?: string; id?: string } | undefined)) return undefined;
|
|
168
|
-
if (!looksLikeAnthropicRequest(event.payload)) return undefined;
|
|
169
|
-
return stabilizeDeferredTools(event.payload as Record<string, unknown>, pi.getAllTools(), (name) => deferredRegistry.has(name), loads);
|
|
170
|
-
});
|
|
206
|
+
/** Loads `tool_search` made since the last turn started: their results reach the branch when the batch ends. */
|
|
207
|
+
const freshLoads = new Map<string, string[]>();
|
|
171
208
|
|
|
209
|
+
/** What the branch's in-context history loaded and uses, for the branch it was read from. */
|
|
210
|
+
let used: { entries: readonly EntryLike[]; loads: ReadonlyMap<string, string[]>; inUse: ReadonlySet<string> } | undefined;
|
|
211
|
+
/**
|
|
212
|
+
* The branch's in-context loads and tools in use, read again only when the
|
|
213
|
+
* branch changed (lib/ledger/fold-memo.ts `sameEntries`): the reconciler runs
|
|
214
|
+
* at every turn, model switch, defer and withhold, and a server's connect
|
|
215
|
+
* defers its tools in a burst with nothing appended between. Both are pure
|
|
216
|
+
* functions of the entries; the registry, the model and this turn's loads
|
|
217
|
+
* are applied on top at each run.
|
|
218
|
+
*/
|
|
219
|
+
const branchUse = (branch: EntryLike[]) => {
|
|
220
|
+
if (used && sameEntries(used.entries, branch)) return used;
|
|
221
|
+
const inContext = inContextEntries(branch) as SessionEntry[];
|
|
222
|
+
const loads = toolSearchLoads(inContext);
|
|
223
|
+
used = { entries: [...branch], loads, inUse: toolsInUse(inContext, loads) };
|
|
224
|
+
return used;
|
|
225
|
+
};
|
|
172
226
|
|
|
173
|
-
/**
|
|
174
|
-
|
|
227
|
+
/**
|
|
228
|
+
* The one place deferred tools are activated and deactivated (lib/deferred.ts
|
|
229
|
+
* callableDeferredTools): a deferred tool is active exactly when the branch
|
|
230
|
+
* gives a reason. Its history uses it (a load or a call in context), the
|
|
231
|
+
* epoch announced it callable, the recorded head the next request starts
|
|
232
|
+
* from declares it eagerly, or the next request announces it callable.
|
|
233
|
+
* Claude Code keeps a loaded tool callable across a resume (its
|
|
234
|
+
* tool_reference blocks stay in the history); a compaction returns to
|
|
235
|
+
* deferred what its kept turns no longer use, and keeps what the last
|
|
236
|
+
* request declared eagerly (removing it from `tools` would be the miss). A
|
|
237
|
+
* withheld tool is never active. Run before pi fixes a turn's tools
|
|
238
|
+
* (turn_start) and whenever the inputs change.
|
|
239
|
+
*/
|
|
240
|
+
const reconcile = (owner: SessionLike | undefined, model: typeof currentModel) => {
|
|
241
|
+
if (!alive()) return;
|
|
242
|
+
const all = pi.getAllTools();
|
|
243
|
+
const registered = new Set(all.map((tool) => tool.name));
|
|
244
|
+
if (!deferralEnabled(all)) {
|
|
245
|
+
// Nothing is deferred without the loader, but a withheld tool is still
|
|
246
|
+
// never active: a /tree switch restores a loadout from before it left.
|
|
247
|
+
const active = pi.getActiveTools();
|
|
248
|
+
const next = reconcileActive(active, new Set(), new Set(), withheldNames, registered);
|
|
249
|
+
if (next !== active) pi.setActiveTools([...next]);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const branch = branchOf(owner);
|
|
253
|
+
const use = branchUse(branch);
|
|
254
|
+
loads = new Map([...use.loads, ...freshLoads]);
|
|
175
255
|
const deferred = new Set(deferredRegistry.names);
|
|
176
|
-
|
|
256
|
+
// With nothing deferred no deferred tool is callable: the active set
|
|
257
|
+
// loses only withheld tools, and the branch's fold has nothing to decide.
|
|
258
|
+
let callable = new Set<string>();
|
|
259
|
+
if (deferred.size > 0) {
|
|
260
|
+
const fold = foldOf(owner, branch, model);
|
|
261
|
+
const inUse = new Set(use.inUse);
|
|
262
|
+
for (const names of freshLoads.values()) for (const name of names) inUse.add(name);
|
|
263
|
+
const last = fold.lastRequest;
|
|
264
|
+
const told = fold.told.get("tools") ?? new Set<string>();
|
|
265
|
+
const announcing = supportsToolAdditions(model) && !fold.opensEpoch ? new Set(searchableTools(all).map((tool) => tool.name).filter((name) => !told.has(name))) : new Set<string>();
|
|
266
|
+
callable = callableDeferredTools(deferred, {
|
|
267
|
+
inUse,
|
|
268
|
+
toldCallable: fold.told.get(CALLABLE_TOOLS) ?? new Set(),
|
|
269
|
+
declaredEager: last ? recordedToolNames(last, { eager: true }) : new Set(),
|
|
270
|
+
announcing,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
177
273
|
const active = pi.getActiveTools();
|
|
178
|
-
const next = active
|
|
179
|
-
if (next
|
|
180
|
-
announce();
|
|
274
|
+
const next = reconcileActive(active, deferred, callable, withheldNames, registered);
|
|
275
|
+
if (next !== active) pi.setActiveTools([...next]);
|
|
181
276
|
};
|
|
277
|
+
const reconcileNow = () => reconcile(session, currentModel);
|
|
278
|
+
|
|
279
|
+
// On models that take client-side tool references (first-party Claude >= 4.5,
|
|
280
|
+
// not Haiku) every deferred tool rides every Anthropic request as a
|
|
281
|
+
// `defer_loading: true` definition, and a tool that tool_search loaded — which
|
|
282
|
+
// pi promotes into the eager list — is demoted back to deferred and loaded
|
|
283
|
+
// through `tool_reference` blocks in the tool_search result that activated it
|
|
284
|
+
// (lib/deferred.ts stabilizeDeferredTools has the rules;
|
|
285
|
+
// working-docs/decisions/caching.md the measurements). On OpenAI Responses
|
|
286
|
+
// models with native tool loading (GPT-5.4 and newer on openai and
|
|
287
|
+
// openai-codex) a loaded tool leaves `tools` for pi-ai's own load item at the
|
|
288
|
+
// tool_search result instead (stabilizeResponsesToolLoads). Inside an epoch
|
|
289
|
+
// the request carries only its tail here: the ledger's payload hook, later,
|
|
290
|
+
// puts the recorded list in front and keeps the recorded tools.
|
|
291
|
+
pi.on("before_provider_request", (event, ctx) => {
|
|
292
|
+
const allTools = pi.getAllTools();
|
|
293
|
+
const enabled = deferralEnabled(allTools);
|
|
294
|
+
const toolLoadCompat = responsesToolLoadCompat(ctx.model as ResponsesToolLoadModel | undefined);
|
|
295
|
+
if (toolLoadCompat) {
|
|
296
|
+
if (!enabled) return undefined;
|
|
297
|
+
return stabilizeResponsesToolLoads(event.payload as Record<string, unknown>, (name) => deferredRegistry.has(name), loads, toolLoadCompat);
|
|
298
|
+
}
|
|
299
|
+
const original = event.payload as Record<string, unknown>;
|
|
300
|
+
const additions = supportsToolAdditions(ctx.model) && looksLikeAnthropicRequest(original);
|
|
301
|
+
// The context step lifted this request's addenda off their tool results;
|
|
302
|
+
// whatever happens below, they go out (lib/tool-additions.ts).
|
|
303
|
+
const announceAsText = () => {
|
|
304
|
+
const text = additions ? withToolAdditions(original, addendaByCall, false) : undefined;
|
|
305
|
+
return text ? withBetas(text.payload, [MID_CONVERSATION_SYSTEM_BETA]) : undefined;
|
|
306
|
+
};
|
|
307
|
+
if (!enabled || !supportsToolReferences(ctx.model as { provider?: string; id?: string } | undefined) || !looksLikeAnthropicRequest(original)) return announceAsText();
|
|
308
|
+
const isDeferred = (name: string) => deferredRegistry.has(name);
|
|
309
|
+
// A tool that arrived mid-session goes out as Claude Code's tool_addition
|
|
310
|
+
// where the model takes one, declared deferred.
|
|
311
|
+
const lifted = additions ? withToolAdditions(original, addendaByCall) : undefined;
|
|
312
|
+
const stable = stabilizeDeferredTools(lifted?.payload ?? original, allTools, isDeferred, loads, new Set(lifted?.names ?? []));
|
|
313
|
+
// Renamed (OAuth) tools cannot be referenced: the active tools stay as pi
|
|
314
|
+
// rendered them and the addition goes as text.
|
|
315
|
+
if (!stable) return announceAsText();
|
|
316
|
+
return lifted ? withBetas(stable, TOOL_ADDITION_BETAS) : stable;
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
pi.events.on(DEFERRED_QUERY_CHANNEL, (data) => {
|
|
320
|
+
const query = data as DeferredQuery;
|
|
321
|
+
for (const name of deferredRegistry.names) query.tools.push({ name, keywords: deferredRegistry.keywordsFor(name) });
|
|
322
|
+
});
|
|
182
323
|
|
|
183
324
|
pi.events.on(DEFER_CHANNEL, (data) => {
|
|
184
325
|
const request = data as DeferRequest;
|
|
185
326
|
deferredRegistry.add(request);
|
|
327
|
+
// A withheld tool brought back is deferred again.
|
|
328
|
+
if (request?.name) withheldNames.delete(request.name);
|
|
186
329
|
// A defer arriving after the session_start pass (MCP servers connect
|
|
187
330
|
// asynchronously and register their tools then) would otherwise leave the
|
|
188
|
-
// tool eager AND unlisted
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (active.includes(request.name) && !loadedBefore.has(request.name)) {
|
|
193
|
-
pi.setActiveTools(active.filter((name) => name !== request.name));
|
|
194
|
-
}
|
|
195
|
-
// The tool is searchable (keyword search reads the registry) from now;
|
|
196
|
-
// only the standing listing waits.
|
|
197
|
-
scheduleAnnounce();
|
|
331
|
+
// tool eager AND unlisted.
|
|
332
|
+
if (sessionStarted && request?.name && deferring() && alive()) {
|
|
333
|
+
reconcileNow();
|
|
334
|
+
publishListing();
|
|
198
335
|
}
|
|
199
336
|
});
|
|
200
337
|
|
|
338
|
+
pi.events.on(WITHHOLD_CHANNEL, (data) => {
|
|
339
|
+
const name = (data as { name?: string } | undefined)?.name;
|
|
340
|
+
if (!name || !deferredRegistry.remove(name)) return;
|
|
341
|
+
withheldNames.add(name);
|
|
342
|
+
if (!alive()) return;
|
|
343
|
+
reconcileNow();
|
|
344
|
+
publishListing();
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// A /tree switch keeps the process: pi restored the selected branch's
|
|
348
|
+
// loadout, and the selected branch decides which deferred tools are callable
|
|
349
|
+
// and referenced there.
|
|
350
|
+
pi.on("session_tree", (_event, ctx) => {
|
|
351
|
+
freshLoads.clear();
|
|
352
|
+
reconcile(ctx?.sessionManager, ctx?.model ?? currentModel);
|
|
353
|
+
});
|
|
354
|
+
|
|
201
355
|
pi.on("session_start", (_event, ctx) => {
|
|
202
356
|
sessionStarted = true;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
357
|
+
currentModel = ctx?.model;
|
|
358
|
+
session = ctx?.sessionManager;
|
|
359
|
+
freshLoads.clear();
|
|
360
|
+
reconcileNow();
|
|
361
|
+
publishListing();
|
|
362
|
+
});
|
|
363
|
+
pi.on("model_select", (event) => {
|
|
364
|
+
currentModel = event.model;
|
|
365
|
+
reconcileNow();
|
|
366
|
+
});
|
|
367
|
+
// Every turn's tools are fixed right after this (pi's prepareRequest): the
|
|
368
|
+
// tools the request announces callable must be active by then.
|
|
369
|
+
pi.on("turn_start", (_event, ctx) => {
|
|
370
|
+
freshLoads.clear();
|
|
371
|
+
if (ctx?.model) currentModel = ctx.model;
|
|
372
|
+
reconcileNow();
|
|
214
373
|
});
|
|
215
374
|
|
|
216
|
-
|
|
375
|
+
// A compaction opens an epoch, which lists every deferred tool again. The
|
|
376
|
+
// loads that named a loaded tool went with the summary: the tool goes back to
|
|
377
|
+
// deferred, so the new epoch declares it the way it declares the rest (Claude
|
|
378
|
+
// Code declares a deferred tool only while a reference to it is in the
|
|
379
|
+
// history), unless the reconciler keeps it.
|
|
380
|
+
pi.on("session_compact", (_event, ctx) => {
|
|
381
|
+
reconcile(ctx?.sessionManager, ctx?.model ?? currentModel);
|
|
382
|
+
publishListing();
|
|
383
|
+
});
|
|
217
384
|
|
|
218
385
|
// The every-turn deferred-tools list tells the model to load via tool_search,
|
|
219
386
|
// but if it calls a deferred tool directly anyway, pi's core dispatcher fails
|
|
@@ -228,15 +395,34 @@ export default function toolSearchExtension(pi: ExtensionAPI) {
|
|
|
228
395
|
// Map a Claude Code spelling (a direct `NotebookEdit` call) to our name so
|
|
229
396
|
// the miss is recognised and the steer names the loadable tool (M1).
|
|
230
397
|
const name = raw ? normalizeToolName(raw) : undefined;
|
|
231
|
-
if (!name
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
398
|
+
if (!name) return;
|
|
399
|
+
// Without tool_search there is nothing to steer a deferred-tool miss to.
|
|
400
|
+
if (deferredRegistry.has(name) && deferring()) {
|
|
401
|
+
pi.events.emit(REMINDER_CHANNEL, {
|
|
402
|
+
key: `deferred-miss-${name}`,
|
|
403
|
+
text: deferredMissReminderText(name),
|
|
404
|
+
});
|
|
405
|
+
} else if (withheldNames.has(name)) {
|
|
406
|
+
// A withheld tool is gone from the registry too (WITHHOLD_CHANNEL), so
|
|
407
|
+
// the ordinary steer above would send the model to tool_search for a
|
|
408
|
+
// name that will not be found there either.
|
|
409
|
+
pi.events.emit(REMINDER_CHANNEL, {
|
|
410
|
+
key: `deferred-miss-${name}`,
|
|
411
|
+
text: withheldMissReminderText(name),
|
|
412
|
+
});
|
|
413
|
+
} else if (session && recordedToolNames(foldOf(session, branchOf(session), currentModel).lastRequest ?? { head: {}, list: [] }).has(name)) {
|
|
414
|
+
// The epoch's recorded tools stay declared after the tool left the
|
|
415
|
+
// session (a setting turned it off): the call cannot be salvaged, and
|
|
416
|
+
// the model must not keep trying it.
|
|
417
|
+
pi.events.emit(REMINDER_CHANNEL, {
|
|
418
|
+
key: `deferred-miss-${name}`,
|
|
419
|
+
text: unavailableToolReminderText(name),
|
|
420
|
+
});
|
|
421
|
+
}
|
|
237
422
|
});
|
|
238
423
|
|
|
239
424
|
pi.registerTool({
|
|
425
|
+
// A literal, like every registration: agent-view-tool-labels.test.ts reads them from source.
|
|
240
426
|
name: "tool_search",
|
|
241
427
|
label: "Tool Search",
|
|
242
428
|
...ccToolRenderers("Tool Search"),
|
|
@@ -257,14 +443,34 @@ export default function toolSearchExtension(pi: ExtensionAPI) {
|
|
|
257
443
|
// silently dropped before — the model was told the rest "Loaded" and only
|
|
258
444
|
// discovered the miss later as an opaque InputValidationError. Always
|
|
259
445
|
// surface the unmatched names.
|
|
260
|
-
const requested =
|
|
261
|
-
|
|
262
|
-
|
|
446
|
+
const requested = selectedRequests(params.query);
|
|
447
|
+
// A requested tool that is already callable (an eager tool, or one loaded
|
|
448
|
+
// earlier) is a harmless no-op, as in Claude Code: the model proceeds
|
|
449
|
+
// instead of retrying a name it was told does not exist.
|
|
450
|
+
const active = pi.getActiveTools();
|
|
451
|
+
const alreadyActive = (name: string) => active.find((tool) => tool.toLowerCase() === name);
|
|
452
|
+
const unmatched = requested
|
|
453
|
+
? requested.filter((request) => !matches.some((m) => m.name.toLowerCase() === request.name))
|
|
263
454
|
: [];
|
|
455
|
+
const eager = [...new Set(unmatched.flatMap((request) => alreadyActive(request.name) ?? []))];
|
|
456
|
+
const notFound = unmatched.filter((request) => !alreadyActive(request.name));
|
|
457
|
+
// A withheld name can still sit in the frozen listing (withdrawn by a model
|
|
458
|
+
// change after the first request), so it gets "do not retry", not a
|
|
459
|
+
// spelling hint. Both notes quote the model's own spelling.
|
|
460
|
+
const withdrawn = [...new Set(notFound.filter((n) => withheldNames.has(n.name)).map((n) => n.asWritten))];
|
|
461
|
+
const missing = [...new Set(notFound.filter((n) => !withheldNames.has(n.name)).map((n) => n.asWritten))];
|
|
264
462
|
const notFoundNote =
|
|
265
|
-
|
|
266
|
-
? ` Not found (not deferred tool names — check spelling, or search by keyword instead of \`select:\`): ${
|
|
267
|
-
: ""
|
|
463
|
+
(missing.length > 0
|
|
464
|
+
? ` Not found (not deferred tool names — check spelling, or search by keyword instead of \`select:\`): ${missing.join(", ")}.`
|
|
465
|
+
: "") +
|
|
466
|
+
(withdrawn.length > 0 ? ` Withdrawn for the current model (do not retry): ${withdrawn.join(", ")}.` : "");
|
|
467
|
+
|
|
468
|
+
if (matches.length === 0 && eager.length > 0) {
|
|
469
|
+
return {
|
|
470
|
+
content: [{ type: "text", text: `Already loaded: ${eager.join(", ")}.${notFoundNote}` }],
|
|
471
|
+
details: { matches: eager, added: [] as string[], notFound, refused: [] as string[] },
|
|
472
|
+
};
|
|
473
|
+
}
|
|
268
474
|
|
|
269
475
|
if (matches.length === 0) {
|
|
270
476
|
const names = available.map((t) => t.name).join(", ") || "(none)";
|
|
@@ -272,30 +478,42 @@ export default function toolSearchExtension(pi: ExtensionAPI) {
|
|
|
272
478
|
content: [
|
|
273
479
|
{ type: "text", text: `No tools matched "${params.query}".${notFoundNote} Tools that can be loaded: ${names}` },
|
|
274
480
|
],
|
|
275
|
-
details: { matches: [] as string[], added: [] as string[], notFound },
|
|
481
|
+
details: { matches: [] as string[], added: [] as string[], notFound, refused: [] as string[] },
|
|
276
482
|
isError: true,
|
|
277
483
|
};
|
|
278
484
|
}
|
|
279
485
|
|
|
280
|
-
const
|
|
281
|
-
const
|
|
486
|
+
const matchNames = matches.map((m) => m.name);
|
|
487
|
+
const toActivate = matchNames.filter((name) => !active.includes(name));
|
|
488
|
+
if (toActivate.length > 0) pi.setActiveTools([...new Set([...active, ...toActivate])]);
|
|
489
|
+
// pi can decline to activate a tool: since 1.0.4 a `--tools` list that
|
|
490
|
+
// names no `mcp__*` entry keeps the MCP tools registered but not
|
|
491
|
+
// activatable. Report what pi actually activated, never a plausible load.
|
|
492
|
+
const now = new Set(pi.getActiveTools());
|
|
493
|
+
const added = toActivate.filter((name) => now.has(name));
|
|
494
|
+
const refused = toActivate.filter((name) => !now.has(name));
|
|
282
495
|
if (added.length > 0) {
|
|
283
|
-
|
|
496
|
+
freshLoads.set(toolCallId, added);
|
|
284
497
|
loads.set(toolCallId, added);
|
|
285
498
|
}
|
|
286
499
|
|
|
287
|
-
const loaded =
|
|
500
|
+
const loaded = [...new Set([...matchNames, ...eager])].filter((name) => now.has(name));
|
|
501
|
+
const already = loaded.filter((name) => !added.includes(name));
|
|
502
|
+
const refusedNote =
|
|
503
|
+
refused.length > 0 ? ` Could not load ${refused.join(", ")}: this session's tool list (pi's --tools) leaves them out, so they cannot be called.` : "";
|
|
288
504
|
return {
|
|
289
505
|
content: [
|
|
290
506
|
{
|
|
291
507
|
type: "text",
|
|
292
508
|
text:
|
|
293
|
-
(
|
|
294
|
-
? `Loaded ${added.join(", ")}. These tools are now callable.`
|
|
295
|
-
|
|
509
|
+
([
|
|
510
|
+
added.length > 0 ? `Loaded ${added.join(", ")}. These tools are now callable.` : "",
|
|
511
|
+
already.length > 0 ? `Already loaded: ${already.join(", ")}.` : "",
|
|
512
|
+
].filter(Boolean).join(" ") + refusedNote + notFoundNote).trim(),
|
|
296
513
|
},
|
|
297
514
|
],
|
|
298
|
-
details: { matches: loaded, added, notFound },
|
|
515
|
+
details: { matches: loaded, added, notFound, refused },
|
|
516
|
+
isError: loaded.length === 0,
|
|
299
517
|
};
|
|
300
518
|
},
|
|
301
519
|
});
|
|
@@ -303,6 +521,10 @@ export default function toolSearchExtension(pi: ExtensionAPI) {
|
|
|
303
521
|
registerLocalCommand(pi, "tools-deferred", {
|
|
304
522
|
description: "Show which tools are deferred (loadable via tool_search)",
|
|
305
523
|
handler: async (args, ctx) => {
|
|
524
|
+
if (!deferring()) {
|
|
525
|
+
ctx.ui.notify("tool_search is not registered (a --tools list leaves it out), so no tool is deferred.", "info");
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
306
528
|
const available = searchableTools();
|
|
307
529
|
const activeSet = new Set(pi.getActiveTools());
|
|
308
530
|
const lines = available.map((t) => `${activeSet.has(t.name) ? "loaded " : "deferred"} ${t.name}`);
|