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
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The login a request goes out under, and the rest of a request's identity
|
|
3
|
+
* (lib/ledger): every input that makes pi-ai build a differently shaped
|
|
4
|
+
* request. An Anthropic OAuth login (a Claude subscription) carries pi's
|
|
5
|
+
* identity betas and Claude Code's tool names; Sign in with ChatGPT drops
|
|
6
|
+
* OpenAI's per-message `configuration_update` items and the cache fields
|
|
7
|
+
* (pi-ai openai-responses.js isChatGPTSignIn); thinking turned off drops
|
|
8
|
+
* pi-ai's per-message effort markers on a model that takes them. A change
|
|
9
|
+
* of any of them opens an epoch, so recorded history is never replayed in a
|
|
10
|
+
* shape pi would not build.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync, statSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
16
|
+
import type { RequestIdentity } from "./ledger/records.ts";
|
|
17
|
+
|
|
18
|
+
/** mtime cache: this runs on every provider request, the hottest path there is. */
|
|
19
|
+
let authCache: { mtimeMs: number; oauth: ReadonlySet<string> } | undefined;
|
|
20
|
+
|
|
21
|
+
/** The providers pi's auth.json holds an OAuth login for. */
|
|
22
|
+
function oauthProviders(): ReadonlySet<string> {
|
|
23
|
+
// getAgentDir() honours PI_CODING_AGENT_DIR, so an isolated one-code app
|
|
24
|
+
// reads its own auth.json rather than ~/.pi's.
|
|
25
|
+
const path = join(getAgentDir(), "auth.json");
|
|
26
|
+
try {
|
|
27
|
+
const mtimeMs = statSync(path).mtimeMs;
|
|
28
|
+
const cached = authCache;
|
|
29
|
+
if (cached && cached.mtimeMs === mtimeMs) return cached.oauth;
|
|
30
|
+
const auth = JSON.parse(readFileSync(path, "utf8")) as Record<string, { type?: unknown; refresh?: unknown } | undefined>;
|
|
31
|
+
const oauth = new Set(Object.entries(auth ?? {}).flatMap(([provider, entry]) => (entry?.type === "oauth" || typeof entry?.refresh === "string" ? [provider] : [])));
|
|
32
|
+
authCache = { mtimeMs, oauth };
|
|
33
|
+
return oauth;
|
|
34
|
+
} catch {
|
|
35
|
+
authCache = undefined;
|
|
36
|
+
return new Set();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** True when pi's auth.json holds an OAuth login for Anthropic. */
|
|
41
|
+
export function isAnthropicOAuth(): boolean {
|
|
42
|
+
return oauthProviders().has("anthropic");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** OpenAI's own endpoint, where pi-ai treats a login that is not an `sk-` key as Sign in with ChatGPT. */
|
|
46
|
+
const OPENAI_API = "https://api.openai.com/v1";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The auth kind a request to `provider` goes out with: "oauth" for an
|
|
50
|
+
* Anthropic subscription login, "chatgpt" for Sign in with ChatGPT on
|
|
51
|
+
* OpenAI's own endpoint, else "key".
|
|
52
|
+
*/
|
|
53
|
+
export function authKind(provider: string | undefined, baseUrl?: string): "oauth" | "chatgpt" | "key" {
|
|
54
|
+
if (provider === "anthropic") return isAnthropicOAuth() ? "oauth" : "key";
|
|
55
|
+
if (provider === "openai" && baseUrl === OPENAI_API && oauthProviders().has("openai")) return "chatgpt";
|
|
56
|
+
return "key";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface IdentityModel {
|
|
60
|
+
api?: string;
|
|
61
|
+
provider?: string;
|
|
62
|
+
id?: string;
|
|
63
|
+
baseUrl?: string;
|
|
64
|
+
reasoning?: boolean;
|
|
65
|
+
thinkingLevelMap?: Record<string, unknown>;
|
|
66
|
+
compat?: unknown;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Whether pi-ai puts its per-message effort markers in the request (an
|
|
71
|
+
* effort directive before each earlier reply, a Responses
|
|
72
|
+
* `configuration_update`): only on a model that takes them, and not while
|
|
73
|
+
* thinking is off there (pi-ai anthropic-messages.js effortMessages).
|
|
74
|
+
* Undefined on a model without them, or when the level is unknown.
|
|
75
|
+
*/
|
|
76
|
+
function effortMarkers(model: IdentityModel, thinkingLevel: string | undefined): boolean | undefined {
|
|
77
|
+
if ((model.compat as { supportsMidConvoEffort?: boolean } | undefined)?.supportsMidConvoEffort !== true || thinkingLevel === undefined) return undefined;
|
|
78
|
+
return !(thinkingLevel === "off" && model.reasoning === true && model.thinkingLevelMap?.off !== null);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Who a request to `model` goes to and how pi-ai shapes it, as the ledger
|
|
83
|
+
* records it; a change opens an epoch. `thinkingLevel` is the session's
|
|
84
|
+
* (`pi.getThinkingLevel()`). Undefined without a full model.
|
|
85
|
+
*/
|
|
86
|
+
export function requestIdentity(model: IdentityModel | undefined, thinkingLevel?: string): RequestIdentity | undefined {
|
|
87
|
+
if (!model?.api || !model.provider || !model.id) return undefined;
|
|
88
|
+
const markers = effortMarkers(model, thinkingLevel);
|
|
89
|
+
return { api: model.api, provider: model.provider, model: model.id, auth: authKind(model.provider, model.baseUrl), ...(markers !== undefined ? { effortMarkers: markers } : {}) };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The identity of a request this session sends to `model` now, under the
|
|
94
|
+
* session's thinking level (undefined where the API lacks it: a test double).
|
|
95
|
+
*/
|
|
96
|
+
export function sessionIdentity(pi: { getThinkingLevel?: () => string }, model: IdentityModel | undefined): RequestIdentity | undefined {
|
|
97
|
+
return requestIdentity(model, typeof pi.getThinkingLevel === "function" ? pi.getThinkingLevel() : undefined);
|
|
98
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Recognising an Anthropic Messages request body inside a `before_provider_request`
|
|
3
3
|
* payload (pure). Shared by context-management (clear_thinking), compaction
|
|
4
|
-
* (its replayed request)
|
|
4
|
+
* (its replayed request), tool-search (deferred tool definitions) and
|
|
5
|
+
* idle-compact (the cache lifetime).
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
/** Anthropic Messages API shape: `messages` + `max_tokens`, and not an OpenAI `input`. */
|
|
@@ -13,3 +14,83 @@ export function looksLikeAnthropicRequest(payload: unknown): boolean {
|
|
|
13
14
|
const model = typeof record.model === "string" ? record.model : "";
|
|
14
15
|
return model.includes("claude") || typeof record.max_tokens === "number";
|
|
15
16
|
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The payload with `betas` added, each once, after the ones pi set. pi-ai 1.0
|
|
20
|
+
* sends `payload.betas` as the final `anthropic-beta` header (it wins over a
|
|
21
|
+
* configured header), so a beta goes here, never into the headers (findings
|
|
22
|
+
* §54). The payload is returned unchanged when nothing is missing.
|
|
23
|
+
*/
|
|
24
|
+
export function withBetas<T extends Record<string, unknown>>(payload: T, betas: readonly string[]): T {
|
|
25
|
+
const existing: unknown[] = Array.isArray(payload.betas) ? payload.betas : [];
|
|
26
|
+
const missing = [...new Set(betas)].filter((beta) => !existing.includes(beta));
|
|
27
|
+
if (missing.length === 0) return payload;
|
|
28
|
+
return { ...payload, betas: [...existing, ...missing] };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Anthropic's beta for a `role: "system"` message after the first one. */
|
|
32
|
+
export const MID_CONVERSATION_SYSTEM_BETA = "mid-conversation-system-2026-04-07";
|
|
33
|
+
/** Anthropic's beta for a system message's `clear_at`. */
|
|
34
|
+
export const CLEAR_AT_BETA = "mid-conversation-system-clear-at-2026-08-21";
|
|
35
|
+
/** Anthropic's beta for reference-form `tool_addition` blocks. */
|
|
36
|
+
export const MID_CONVERSATION_TOOL_CHANGES_BETA = "mid-conversation-tool-changes-2026-07-01";
|
|
37
|
+
|
|
38
|
+
type Message = { role?: unknown; content?: unknown };
|
|
39
|
+
type Block = Record<string, unknown> & { cache_control?: unknown };
|
|
40
|
+
|
|
41
|
+
/** pi's directive-only effort message, which cannot carry a cache mark. */
|
|
42
|
+
export function isEmptySystemMessage(message: Message | undefined): boolean {
|
|
43
|
+
return message?.role === "system" && Array.isArray(message.content) && message.content.length === 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** A system message with a `clear_at` nudge: a string body, and Anthropic refuses a cache mark on it. */
|
|
47
|
+
function isClearAtMessage(message: Message): boolean {
|
|
48
|
+
return message?.role === "system" && "clear_at" in (message as object);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The messages with pi's conversation cache mark on the last block that can
|
|
53
|
+
* carry it. pi marks the last block of the last message it built; system
|
|
54
|
+
* messages added after it in `before_provider_request` (the context message,
|
|
55
|
+
* the per-turn budget, a tool addition) would otherwise sit outside the mark,
|
|
56
|
+
* so it moves onto the last one, as Claude Code marks its own. pi's empty
|
|
57
|
+
* effort messages and a `clear_at` nudge are skipped over. Unchanged when the
|
|
58
|
+
* mark already sits there or no added system message ends the request.
|
|
59
|
+
*/
|
|
60
|
+
export function reseatMessageMark<T extends Message>(messages: T[]): T[] {
|
|
61
|
+
let target = messages.length - 1;
|
|
62
|
+
while (target >= 0 && (isEmptySystemMessage(messages[target]) || isClearAtMessage(messages[target]))) target--;
|
|
63
|
+
const tail = messages[target];
|
|
64
|
+
if (tail?.role !== "system" || !Array.isArray(tail.content) || tail.content.length === 0) return messages;
|
|
65
|
+
const tailBlocks = tail.content as Block[];
|
|
66
|
+
if (tailBlocks[tailBlocks.length - 1].cache_control !== undefined) return messages;
|
|
67
|
+
let from = target - 1;
|
|
68
|
+
while (from >= 0 && messages[from]?.role === "system") from--;
|
|
69
|
+
const source = messages[from];
|
|
70
|
+
if (!source || !Array.isArray(source.content) || source.content.length === 0) return messages;
|
|
71
|
+
const sourceBlocks = source.content as Block[];
|
|
72
|
+
const { cache_control, ...unmarked } = sourceBlocks[sourceBlocks.length - 1];
|
|
73
|
+
if (cache_control === undefined) return messages;
|
|
74
|
+
const copy = [...messages];
|
|
75
|
+
copy[from] = { ...source, content: [...sourceBlocks.slice(0, -1), unmarked] };
|
|
76
|
+
copy[target] = { ...tail, content: [...tailBlocks.slice(0, -1), { ...tailBlocks[tailBlocks.length - 1], cache_control }] };
|
|
77
|
+
return copy;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Whether a request body caches with a one-hour TTL: a breakpoint on the
|
|
82
|
+
* system, the tools or a message carries `cache_control.ttl: "1h"` (pi's long
|
|
83
|
+
* retention on Anthropic, through pi's own compat gating, so the body is the
|
|
84
|
+
* ground truth). OpenAI bodies have no breakpoints and answer false.
|
|
85
|
+
*/
|
|
86
|
+
export function hasOneHourCache(payload: unknown): boolean {
|
|
87
|
+
if (!payload || typeof payload !== "object") return false;
|
|
88
|
+
const body = payload as { system?: unknown; tools?: unknown; messages?: unknown };
|
|
89
|
+
const list = (value: unknown): unknown[] => (Array.isArray(value) ? value : []);
|
|
90
|
+
const oneHour = (block: unknown) => (block as { cache_control?: { ttl?: unknown } } | null)?.cache_control?.ttl === "1h";
|
|
91
|
+
return (
|
|
92
|
+
list(body.system).some(oneHour) ||
|
|
93
|
+
list(body.tools).some(oneHour) ||
|
|
94
|
+
list(body.messages).some((message) => oneHour(message) || list((message as { content?: unknown } | null)?.content).some(oneHour))
|
|
95
|
+
);
|
|
96
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The I/O half of the native web tools: one streamed Messages call carrying
|
|
3
|
+
* an Anthropic server tool, folded by `anthropic-server-tools.ts`.
|
|
4
|
+
*
|
|
5
|
+
* A raw `fetch` rather than pi's `completeSimple`: pi's stream parser drops
|
|
6
|
+
* `server_tool_use` and `web_*_tool_result` blocks, and those carry what
|
|
7
|
+
* decides between the answer and the fallback. Streamed because a
|
|
8
|
+
* non-streaming call with server tools can sit past undici's five-minute
|
|
9
|
+
* headers timeout (findings §43).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { type Api, calculateCost, type Model, type Usage } from "@earendil-works/pi-ai";
|
|
13
|
+
import {
|
|
14
|
+
createServerCallAccumulator,
|
|
15
|
+
nativeEligibility,
|
|
16
|
+
type Outcome,
|
|
17
|
+
parseSseBuffer,
|
|
18
|
+
pickNativeWebModel,
|
|
19
|
+
type ServerCallResult,
|
|
20
|
+
type ThinkingFields,
|
|
21
|
+
thinkingOff,
|
|
22
|
+
thinkingRetry,
|
|
23
|
+
tokenCounts,
|
|
24
|
+
WEB_SEARCH_COST_PER_REQUEST,
|
|
25
|
+
} from "./anthropic-server-tools.ts";
|
|
26
|
+
|
|
27
|
+
import { modelSpec } from "./model-policy.ts";
|
|
28
|
+
import { runWithDeadline } from "./operation-deadline.ts";
|
|
29
|
+
|
|
30
|
+
/** Observed calls take 8 to 16 s; a stuck one gives way to the fallback. */
|
|
31
|
+
const NATIVE_CALL_TIMEOUT_MS = 120_000;
|
|
32
|
+
|
|
33
|
+
/** pi's header maps: a `null` value removes that header. */
|
|
34
|
+
type HeaderMap = Record<string, string | null>;
|
|
35
|
+
|
|
36
|
+
export interface ServerCallAuth {
|
|
37
|
+
apiKey: string;
|
|
38
|
+
headers?: HeaderMap;
|
|
39
|
+
baseUrl?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface ServerCallReply {
|
|
43
|
+
result: ServerCallResult;
|
|
44
|
+
/** pi's usage shape with the cost filled in, searches included. */
|
|
45
|
+
usage: Usage;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** pi's `Usage` for one call: token cost from the catalog price, plus Anthropic's per-search fee. */
|
|
49
|
+
export function serverCallUsage(model: Model<Api>, result: ServerCallResult): Usage {
|
|
50
|
+
const usage: Usage = { ...tokenCounts(result.usage), cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } };
|
|
51
|
+
usage.cost = calculateCost(model, usage);
|
|
52
|
+
const searches = result.usage.server_tool_use?.web_search_requests ?? 0;
|
|
53
|
+
usage.cost = { ...usage.cost, total: usage.cost.total + searches * WEB_SEARCH_COST_PER_REQUEST };
|
|
54
|
+
return usage;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Run one call. The thinking-off fields for `model` are merged into `body`; a
|
|
59
|
+
* 400 that rejects them is retried once with the fields the error names, and
|
|
60
|
+
* `learned` (per extension, keyed by `provider/id`) remembers those so later
|
|
61
|
+
* calls send them first. Throws on any other HTTP error, a timeout or a
|
|
62
|
+
* cancel; the caller falls back.
|
|
63
|
+
*/
|
|
64
|
+
export async function runServerCall(
|
|
65
|
+
model: Model<Api>,
|
|
66
|
+
auth: ServerCallAuth,
|
|
67
|
+
body: Record<string, unknown>,
|
|
68
|
+
signal: AbortSignal | undefined,
|
|
69
|
+
learned?: Map<string, ThinkingFields>,
|
|
70
|
+
): Promise<ServerCallReply> {
|
|
71
|
+
const url = `${(auth.baseUrl ?? model.baseUrl ?? "https://api.anthropic.com").replace(/\/+$/, "")}/v1/messages`;
|
|
72
|
+
const headers: Record<string, string> = {};
|
|
73
|
+
for (const [name, value] of Object.entries({ ...(model.headers as HeaderMap | undefined), ...auth.headers })) {
|
|
74
|
+
if (value !== null) headers[name] = value;
|
|
75
|
+
}
|
|
76
|
+
Object.assign(headers, {
|
|
77
|
+
"content-type": "application/json",
|
|
78
|
+
accept: "text/event-stream",
|
|
79
|
+
"anthropic-version": "2023-06-01",
|
|
80
|
+
"x-api-key": auth.apiKey,
|
|
81
|
+
});
|
|
82
|
+
const timeout = AbortSignal.timeout(NATIVE_CALL_TIMEOUT_MS);
|
|
83
|
+
const combined = signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
84
|
+
|
|
85
|
+
const key = modelSpec(model);
|
|
86
|
+
let thinking = learned?.get(key) ?? thinkingOff(model);
|
|
87
|
+
for (let attempt = 0; ; attempt++) {
|
|
88
|
+
const response = await fetch(url, { method: "POST", headers, body: JSON.stringify({ ...body, ...thinking }), signal: combined });
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
const text = await response.text();
|
|
91
|
+
const message = errorMessage(text);
|
|
92
|
+
const retry = response.status === 400 && attempt === 0 ? thinkingRetry(message) : undefined;
|
|
93
|
+
if (retry) {
|
|
94
|
+
thinking = retry;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
throw new Error(`Anthropic API error (${response.status}): ${message}`);
|
|
98
|
+
}
|
|
99
|
+
// Remembered only once the model accepted them, so an unrelated 400 that
|
|
100
|
+
// happens to name a switch cannot pin wrong fields for the session.
|
|
101
|
+
if (attempt > 0) learned?.set(key, thinking);
|
|
102
|
+
const accumulator = createServerCallAccumulator();
|
|
103
|
+
try {
|
|
104
|
+
await readStream(response, accumulator);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
// The tokens and searches streamed before the break were still billed.
|
|
107
|
+
throw new ServerCallError((error as Error).message, serverCallUsage(model, accumulator.result()), error);
|
|
108
|
+
}
|
|
109
|
+
const result = accumulator.result();
|
|
110
|
+
return { result, usage: serverCallUsage(model, result) };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** The slice of pi's model registry the resolver reads. */
|
|
115
|
+
interface RegistryLike {
|
|
116
|
+
getAvailable(): Model<Api>[];
|
|
117
|
+
getApiKeyAndHeaders(
|
|
118
|
+
model: Model<Api>,
|
|
119
|
+
): Promise<{ ok: true; apiKey?: string; headers?: HeaderMap; baseUrl?: string } | { ok: false; error: string }>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
type NativeWebTarget = { ok: true; model: Model<Api>; auth: ServerCallAuth } | { ok: false; reason: string };
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Whether this session runs the native web tools, and on which model with
|
|
126
|
+
* which credentials. Not-eligible is the normal case on every other provider,
|
|
127
|
+
* so callers use the reason only for diagnostics, never in a result.
|
|
128
|
+
*/
|
|
129
|
+
async function resolveNativeWeb(ctx: { model?: Model<Api>; modelRegistry: RegistryLike }, signal: AbortSignal): Promise<NativeWebTarget> {
|
|
130
|
+
const session = ctx.model;
|
|
131
|
+
if (!session || session.provider !== "anthropic") return { ok: false, reason: "not an Anthropic session" };
|
|
132
|
+
const sessionAuth = await ctx.modelRegistry.getApiKeyAndHeaders(session);
|
|
133
|
+
signal.throwIfAborted();
|
|
134
|
+
if (!sessionAuth.ok) return { ok: false, reason: sessionAuth.error };
|
|
135
|
+
const eligible = nativeEligibility({ sessionModel: session, baseUrl: sessionAuth.baseUrl, apiKey: sessionAuth.apiKey });
|
|
136
|
+
if (!eligible.ok) return eligible;
|
|
137
|
+
const model = pickNativeWebModel(ctx.modelRegistry.getAvailable(), session);
|
|
138
|
+
if (!model) return { ok: false, reason: "no model on this provider supports dynamic filtering (Claude 4.6 or later)" };
|
|
139
|
+
// The pick is same-provider, but its credentials are resolved on their own
|
|
140
|
+
// and checked again: a per-model base URL or key must not slip past the gate.
|
|
141
|
+
const auth = model === session ? sessionAuth : await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
142
|
+
signal.throwIfAborted();
|
|
143
|
+
if (!auth.ok || !auth.apiKey || !nativeEligibility({ sessionModel: model, baseUrl: auth.baseUrl, apiKey: auth.apiKey }).ok) {
|
|
144
|
+
return { ok: false, reason: `no usable credentials for ${model.provider}/${model.id}` };
|
|
145
|
+
}
|
|
146
|
+
return { ok: true, model, auth: { apiKey: auth.apiKey, headers: auth.headers, baseUrl: auth.baseUrl } };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export type NativeAttempt =
|
|
150
|
+
/** The session cannot use the native tools; today's path runs with no note. */
|
|
151
|
+
| { kind: "skipped" }
|
|
152
|
+
| { kind: "answered"; text: string; cutOff: boolean; spec: string; result: ServerCallResult }
|
|
153
|
+
/** The call ran and did not answer; `reason` goes into the fallback's note. */
|
|
154
|
+
| { kind: "fell-back"; reason: string }
|
|
155
|
+
| { kind: "cancelled" };
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* The native step both web tools share: resolve, call, report usage, and
|
|
159
|
+
* decide with `outcome` whether the call answered. Never throws: a failure
|
|
160
|
+
* is a `fell-back` with the reason, a cancel is `cancelled`.
|
|
161
|
+
*/
|
|
162
|
+
export async function tryNativeWeb(
|
|
163
|
+
ctx: { model?: Model<Api>; modelRegistry: RegistryLike },
|
|
164
|
+
request: {
|
|
165
|
+
body: (modelId: string) => Record<string, unknown>;
|
|
166
|
+
outcome: (result: ServerCallResult) => Outcome;
|
|
167
|
+
onUsage: (usage: Usage) => void;
|
|
168
|
+
/** Called once the native path is chosen, before the call (a progress update). */
|
|
169
|
+
onStart?: (spec: string) => void;
|
|
170
|
+
/** The caller's per-session memo of each model's thinking-off fields. */
|
|
171
|
+
learned?: Map<string, ThinkingFields>;
|
|
172
|
+
signal: AbortSignal | undefined;
|
|
173
|
+
},
|
|
174
|
+
): Promise<NativeAttempt> {
|
|
175
|
+
try {
|
|
176
|
+
const native = await runWithDeadline((signal) => resolveNativeWeb(ctx, signal), {
|
|
177
|
+
signal: request.signal,
|
|
178
|
+
timeoutMs: NATIVE_CALL_TIMEOUT_MS,
|
|
179
|
+
message: "Native web authentication did not answer within 120 seconds.",
|
|
180
|
+
});
|
|
181
|
+
if (!native.ok) return { kind: "skipped" };
|
|
182
|
+
const spec = modelSpec(native.model);
|
|
183
|
+
request.onStart?.(spec);
|
|
184
|
+
const reply = await runServerCall(native.model, native.auth, request.body(native.model.id), request.signal, request.learned);
|
|
185
|
+
request.onUsage(reply.usage);
|
|
186
|
+
const outcome = request.outcome(reply.result);
|
|
187
|
+
return outcome.ok
|
|
188
|
+
? { kind: "answered", text: outcome.text, cutOff: outcome.cutOff, spec, result: reply.result }
|
|
189
|
+
: { kind: "fell-back", reason: outcome.reason };
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if (error instanceof ServerCallError && error.usage.totalTokens > 0) request.onUsage(error.usage);
|
|
192
|
+
if (request.signal?.aborted) return { kind: "cancelled" };
|
|
193
|
+
return { kind: "fell-back", reason: `the call failed: ${(error as Error).message}` };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function errorMessage(body: string): string {
|
|
198
|
+
try {
|
|
199
|
+
const parsed = JSON.parse(body) as { error?: { message?: string } };
|
|
200
|
+
return parsed.error?.message ?? body;
|
|
201
|
+
} catch {
|
|
202
|
+
return body;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** A stream that broke after it started, with the usage it had reported by then. */
|
|
207
|
+
class ServerCallError extends Error {
|
|
208
|
+
constructor(
|
|
209
|
+
message: string,
|
|
210
|
+
readonly usage: Usage,
|
|
211
|
+
cause: unknown,
|
|
212
|
+
) {
|
|
213
|
+
super(message, { cause });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function readStream(response: Response, accumulator: ReturnType<typeof createServerCallAccumulator>): Promise<void> {
|
|
218
|
+
const reader = response.body?.getReader();
|
|
219
|
+
if (!reader) throw new Error("Anthropic API returned no response body");
|
|
220
|
+
const decoder = new TextDecoder();
|
|
221
|
+
let buffer = "";
|
|
222
|
+
while (true) {
|
|
223
|
+
const { done, value } = await reader.read();
|
|
224
|
+
buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });
|
|
225
|
+
const { events, rest } = parseSseBuffer(done ? `${buffer}\n\n` : buffer);
|
|
226
|
+
buffer = rest;
|
|
227
|
+
for (const event of events) accumulator.push(event);
|
|
228
|
+
if (done) break;
|
|
229
|
+
}
|
|
230
|
+
}
|