@vellumai/assistant 0.11.3 → 0.11.4-dev.202608190019.b94dbf2
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/AGENTS.md +9 -3
- package/ARCHITECTURE.md +32 -10
- package/docs/architecture/memory.md +43 -5
- package/docs/architecture/security.md +96 -152
- package/docs/architecture/turn-actor.md +70 -0
- package/docs/browser-use-architecture-phase2.md +128 -56
- package/docs/flux-turn-detection-spike.md +248 -0
- package/docs/guardian-request-flow.md +35 -0
- package/docs/stt-provider-onboarding.md +3 -1
- package/knip.json +3 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/openapi.yaml +289 -113
- package/package.json +1 -1
- package/scripts/voice-ttft-spike.ts +3 -3
- package/scripts/write-plugin-api-shim.ts +10 -0
- package/src/__tests__/app-compiler.test.ts +38 -3
- package/src/__tests__/app-control-flow.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +2 -2
- package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
- package/src/__tests__/attachments-store.test.ts +21 -12
- package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
- package/src/__tests__/call-site-routing-provider.test.ts +1 -1
- package/src/__tests__/channel-availability-routes.test.ts +14 -1
- package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
- package/src/__tests__/channel-delivery-store.test.ts +14 -14
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +4 -4
- package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
- package/src/__tests__/checker.test.ts +20 -314
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
- package/src/__tests__/compaction-events.test.ts +8 -10
- package/src/__tests__/config-loader-backfill.test.ts +3 -3
- package/src/__tests__/config-schema.test.ts +25 -10
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop.test.ts +32 -21
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-attention-store.test.ts +63 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
- package/src/__tests__/conversation-fork-crud.test.ts +69 -0
- package/src/__tests__/conversation-fork-referential.test.ts +67 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +209 -0
- package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
- package/src/__tests__/conversation-queue.test.ts +154 -4
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +187 -102
- package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
- package/src/__tests__/conversation-slash-queue.test.ts +3 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +7 -10
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/custom-profile-ensure.test.ts +5 -1
- package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
- package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
- package/src/__tests__/document-append-idempotency.test.ts +233 -0
- package/src/__tests__/document-sync-tags.test.ts +0 -75
- package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
- package/src/__tests__/edit-propagation.test.ts +107 -11
- package/src/__tests__/file-ops-service.test.ts +163 -30
- package/src/__tests__/filesystem-tools.test.ts +23 -24
- package/src/__tests__/gateway-only-guard.test.ts +2 -5
- package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
- package/src/__tests__/helpers/mock-actor-context.ts +49 -0
- package/src/__tests__/helpers/mock-conversation.ts +13 -1
- package/src/__tests__/host-file-read-tool.test.ts +16 -19
- package/src/__tests__/http-user-message-parity.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
- package/src/__tests__/injector-chain.test.ts +63 -41
- package/src/__tests__/injector-disk-pressure.test.ts +11 -23
- package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
- package/src/__tests__/llm-context-resolution.test.ts +73 -1
- package/src/__tests__/llm-schema.test.ts +5 -2
- package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
- package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
- package/src/__tests__/media-generate-image.test.ts +131 -21
- package/src/__tests__/memory-retrieval-hook.test.ts +100 -7
- package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
- package/src/__tests__/mtime-cache.test.ts +3 -1
- package/src/__tests__/non-member-access-request.test.ts +0 -20
- package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
- package/src/__tests__/plugin-api-store-credential.test.ts +261 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
- package/src/__tests__/plugin-disabled-state.test.ts +2 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
- package/src/__tests__/plugin-execution-context.test.ts +73 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -4
- package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
- package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
- package/src/__tests__/proxy-approval-callback.test.ts +1 -0
- package/src/__tests__/qdrant-manager.test.ts +14 -1
- package/src/__tests__/reaction-persistence.test.ts +200 -7
- package/src/__tests__/require-fresh-approval.test.ts +0 -4
- package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +460 -105
- package/src/__tests__/run-due-schedules.test.ts +21 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
- package/src/__tests__/schedule-routes.test.ts +23 -0
- package/src/__tests__/schedule-store.test.ts +17 -0
- package/src/__tests__/scoped-approval-grants.test.ts +11 -6
- package/src/__tests__/secret-ingress-channel.test.ts +0 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +3 -3
- package/src/__tests__/skills.test.ts +32 -0
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
- package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
- package/src/__tests__/subagent-spawn-and-await.test.ts +18 -17
- package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
- package/src/__tests__/subagent-tools.test.ts +81 -101
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
- package/src/__tests__/tool-executor.test.ts +68 -39
- package/src/__tests__/turn-events-store.test.ts +43 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
- package/src/__tests__/ui-shape-teaching.test.ts +33 -0
- package/src/__tests__/ui-visual-surface.test.ts +1 -1
- package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
- package/src/__tests__/ui-work-result-surface.test.ts +1 -1
- package/src/__tests__/user-plugin-loader.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/visible-app-context.test.ts +16 -9
- package/src/__tests__/voice-config-update.test.ts +40 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
- package/src/__tests__/voice-session-bridge.test.ts +85 -29
- package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
- package/src/__tests__/worker-plugin-surface.test.ts +77 -0
- package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
- package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
- package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
- package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
- package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
- package/src/acp/__tests__/auth-required.test.ts +161 -0
- package/src/acp/acp-claude-oauth.ts +19 -2
- package/src/acp/agent-process.test.ts +100 -0
- package/src/acp/agent-process.ts +29 -26
- package/src/acp/auth-required.ts +102 -0
- package/src/acp/session-manager.test.ts +119 -0
- package/src/acp/session-manager.ts +76 -3
- package/src/api/events/acp-auth-required.ts +55 -0
- package/src/api/events/host-file.ts +2 -2
- package/src/api/index.ts +7 -0
- package/src/api/surfaces.ts +12 -3
- package/src/apps/app-store.ts +3 -0
- package/src/bundler/package-resolver.ts +2 -30
- package/src/calls/__tests__/voice-session-bridge.test.ts +194 -11
- package/src/calls/__tests__/voice-triage-escalate.test.ts +102 -0
- package/src/calls/call-controller.ts +19 -3
- package/src/calls/call-setup-flow.ts +0 -1
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/calls/voice-session-bridge.ts +115 -41
- package/src/calls/voice-triage-escalate.ts +105 -2
- package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
- package/src/channels/config.ts +13 -0
- package/src/channels/plugin-channel-declarations.ts +108 -0
- package/src/channels/types.ts +30 -0
- package/src/cli/AGENTS.md +5 -2
- package/src/cli/bundled-modules.ts +29 -0
- package/src/cli/commands/credentials.help.ts +2 -2
- package/src/cli/commands/db/repair.ts +4 -8
- package/src/cli/commands/domain.ts +6 -3
- package/src/cli/commands/email.ts +6 -3
- package/src/cli/commands/inference-providers.ts +1 -1
- package/src/cli/commands/keys.ts +8 -3
- package/src/cli/commands/mcp.help.ts +13 -4
- package/src/cli/commands/mcp.ts +9 -0
- package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
- package/src/cli/commands/memory/index.help.ts +84 -28
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/memory-v2.ts +58 -54
- package/src/cli/commands/memory/memory-v3.ts +64 -0
- package/src/cli/commands/memory/memory-validate.ts +18 -0
- package/src/cli/commands/plugins.ts +85 -36
- package/src/cli/commands/schedules.ts +35 -1
- package/src/cli/commands/stt.help.ts +27 -2
- package/src/cli/commands/trust.ts +3 -14
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
- package/src/cli/lib/bundled-marketplace.json +14 -1
- package/src/cli/lib/upgrade-plugin.ts +42 -0
- package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
- package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
- package/src/config/__tests__/default-provider.test.ts +6 -1
- package/src/config/__tests__/profile-materialization.test.ts +75 -19
- package/src/config/assistant-feature-flags.ts +36 -15
- package/src/config/balanced-model-experiment.ts +35 -0
- package/src/config/bundled-skills/acp/SKILL.md +6 -7
- package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
- package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -12
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
- package/src/config/call-site-defaults.ts +11 -5
- package/src/config/default-profile-catalog.ts +179 -16
- package/src/config/default-profile-names.ts +4 -1
- package/src/config/default-provider-resolution.ts +4 -0
- package/src/config/feature-flag-registry.json +12 -12
- package/src/config/llm-context-resolution.ts +11 -3
- package/src/config/llm-resolver.ts +28 -1
- package/src/config/profile-materialization.ts +70 -22
- package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
- package/src/config/schemas/call-site-catalog.ts +4 -4
- package/src/config/schemas/live-voice.ts +57 -23
- package/src/config/schemas/llm.ts +59 -32
- package/src/config/schemas/mcp.ts +23 -0
- package/src/config/schemas/plugin-updates.ts +6 -2
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/skills.ts +9 -2
- package/src/context/outbound-sanitize.ts +96 -1
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
- package/src/daemon/conversation-agent-loop.ts +31 -19
- package/src/daemon/conversation-messaging.ts +5 -1
- package/src/daemon/conversation-notifiers.ts +20 -10
- package/src/daemon/conversation-process.ts +20 -6
- package/src/daemon/conversation-runtime-assembly.ts +12 -6
- package/src/daemon/conversation-store.ts +14 -4
- package/src/daemon/conversation-surfaces.ts +75 -26
- package/src/daemon/conversation-tool-setup.ts +3 -6
- package/src/daemon/conversation.ts +154 -51
- package/src/daemon/doordash-steps.ts +2 -2
- package/src/daemon/lifecycle.ts +14 -1
- package/src/daemon/mcp-reload-service.ts +36 -6
- package/src/daemon/process-message.ts +13 -26
- package/src/daemon/providers-setup.ts +6 -3
- package/src/daemon/trust-context-types.ts +29 -0
- package/src/daemon/trust-context.ts +12 -7
- package/src/daemon/wake-conversation-ops.ts +3 -2
- package/src/daemon/windows-compiled-entry.ts +4 -0
- package/src/documents/document-store.ts +143 -240
- package/src/hooks/hook-loader.ts +3 -3
- package/src/hooks/registry.ts +103 -45
- package/src/hooks/types.ts +5 -0
- package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
- package/src/inbound/oauth-callback-url.ts +61 -0
- package/src/ipc/gateway-client.test.ts +1 -1
- package/src/ipc/gateway-client.ts +14 -21
- package/src/ipc/gateway-flag-listener.ts +17 -3
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
- package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +1050 -0
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
- package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
- package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
- package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
- package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
- package/src/live-voice/live-voice-archive.ts +2 -0
- package/src/live-voice/live-voice-manager.ts +16 -3
- package/src/live-voice/live-voice-metrics.ts +57 -32
- package/src/live-voice/live-voice-photo.ts +1 -2
- package/src/live-voice/live-voice-session.ts +598 -323
- package/src/live-voice/progress-narration.ts +277 -0
- package/src/live-voice/protocol.ts +21 -1
- package/src/live-voice/windows-compiled-live-voice.ts +4 -0
- package/src/mcp/__tests__/effective-config.test.ts +238 -0
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
- package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
- package/src/mcp/client.ts +16 -11
- package/src/mcp/effective-config.ts +113 -0
- package/src/mcp/manager.ts +11 -6
- package/src/mcp/mcp-auth-orchestrator.ts +13 -22
- package/src/mcp/mcp-oauth-provider.ts +205 -240
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
- package/src/monitoring/control.ts +1 -0
- package/src/monitoring/db-integrity-sample.ts +4 -5
- package/src/monitoring/plugin-auto-update.ts +128 -24
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/approval-card-data.ts +33 -0
- package/src/notifications/signal.ts +1 -0
- package/src/permissions/AGENTS.md +16 -0
- package/src/permissions/checker.test.ts +75 -122
- package/src/permissions/checker.ts +44 -560
- package/src/permissions/confirmation-guardian-request.test.ts +15 -11
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/prompter.ts +1 -5
- package/src/permissions/question-guardian-request.test.ts +14 -6
- package/src/permissions/question-guardian-request.ts +1 -2
- package/src/persistence/attachments-store.ts +8 -1
- package/src/persistence/bookmark-crud.ts +3 -7
- package/src/persistence/conversation-attention-store.ts +16 -45
- package/src/persistence/conversation-crud.ts +33 -4
- package/src/persistence/conversation-lineage.ts +9 -0
- package/src/persistence/conversation-queries.ts +182 -52
- package/src/persistence/delivery-crud.ts +150 -36
- package/src/persistence/embeddings/qdrant-manager.ts +84 -49
- package/src/persistence/external-conversation-store.ts +32 -4
- package/src/persistence/llm-request-log-store.ts +4 -10
- package/src/persistence/llm-usage-store.ts +8 -3
- package/src/persistence/message-reads.test.ts +197 -0
- package/src/persistence/message-reads.ts +211 -0
- package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
- package/src/persistence/real-user-turn-filter.ts +27 -3
- package/src/persistence/schema/documents.ts +4 -4
- package/src/persistence/steps.ts +9 -0
- package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
- package/src/plugin-api/constants.ts +8 -0
- package/src/plugin-api/conversation-turn.ts +195 -5
- package/src/plugin-api/index.ts +38 -6
- package/src/plugin-api/resolve-credential.ts +3 -2
- package/src/plugin-api/store-credential.ts +146 -0
- package/src/plugin-api/vision-support.test.ts +1 -1
- package/src/plugin-api/webhook-url.ts +13 -11
- package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
- package/src/plugins/defaults/main.ts +21 -7
- package/src/plugins/defaults/memory/AGENTS.md +15 -3
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
- package/src/plugins/defaults/memory/buffer-format.ts +165 -0
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
- package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
- package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
- package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +17 -24
- package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
- package/src/plugins/defaults/memory/indexer.ts +3 -1
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
- package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
- package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +147 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
- package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
- package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
- package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
- package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +195 -58
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +93 -24
- package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
- package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
- package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
- package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
- package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
- package/src/plugins/defaults/memory/substrate/static-context.ts +160 -33
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
- package/src/plugins/defaults/memory/v3/card.ts +9 -9
- package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
- package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
- package/src/plugins/defaults/memory/v3/edge.ts +13 -57
- package/src/plugins/defaults/memory/v3/injector.ts +8 -0
- package/src/plugins/defaults/memory/v3/prune.ts +2 -0
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
- package/src/plugins/defaults/memory/worker-control.ts +1 -0
- package/src/plugins/defaults/memory/worker.ts +6 -3
- package/src/plugins/defaults/worker-entrypoints.ts +3 -0
- package/src/plugins/external-plugin-loader.ts +47 -0
- package/src/plugins/mcp-servers.ts +361 -0
- package/src/plugins/mtime-cache.ts +37 -46
- package/src/plugins/pipeline.ts +14 -7
- package/src/plugins/plugin-execution-context.ts +35 -11
- package/src/plugins/worker-plugin-surface.ts +33 -0
- package/src/prompts/templates/system-sections.ts +0 -7
- package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
- package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
- package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -2
- package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
- package/src/providers/anthropic/client.ts +332 -241
- package/src/providers/call-site-routing.ts +30 -3
- package/src/providers/connection-resolution.ts +194 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +4 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/inference/auth.ts +6 -0
- package/src/providers/inference/connection-availability.ts +24 -2
- package/src/providers/inference/connections.ts +2 -0
- package/src/providers/model-catalog.ts +3 -3
- package/src/providers/model-intents.ts +28 -8
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +163 -40
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
- package/src/providers/openai/chat-completions-provider.ts +133 -36
- package/src/providers/openai/codex-models.ts +2 -1
- package/src/providers/provider-send-message.ts +32 -3
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +625 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +679 -0
- package/src/providers/speech-to-text/provider-catalog.ts +99 -8
- package/src/providers/speech-to-text/resolve.ts +25 -2
- package/src/routes/control.ts +1 -0
- package/src/routes/route-host-client.ts +1 -0
- package/src/routes/route-host-protocol.ts +7 -0
- package/src/routes/worker.ts +48 -15
- package/src/runtime/AGENTS.md +16 -17
- package/src/runtime/access-request-helper.ts +9 -12
- package/src/runtime/agent-wake.ts +18 -15
- package/src/runtime/effective-capabilities.test.ts +43 -0
- package/src/runtime/effective-capabilities.ts +27 -0
- package/src/runtime/guardian-reply-router.ts +6 -2
- package/src/runtime/pre-first-message-gate.ts +4 -0
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +522 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +132 -35
- package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +133 -1
- package/src/runtime/routes/channel-availability-routes.ts +32 -14
- package/src/runtime/routes/channel-route-shared.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
- package/src/runtime/routes/conversation-list-routes.ts +181 -23
- package/src/runtime/routes/conversation-management-routes.ts +17 -12
- package/src/runtime/routes/conversation-query-routes.ts +40 -27
- package/src/runtime/routes/conversation-routes.ts +43 -29
- package/src/runtime/routes/credential-prompt-routes.ts +4 -7
- package/src/runtime/routes/credential-routes.ts +26 -82
- package/src/runtime/routes/default-provider-routes.ts +15 -0
- package/src/runtime/routes/documents-routes.ts +3 -222
- package/src/runtime/routes/inbound-message-handler.ts +21 -45
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
- package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -79
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -7
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -79
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
- package/src/runtime/routes/inference-profiles-routes.ts +20 -11
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/mcp-auth-routes.ts +148 -57
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
- package/src/runtime/routes/playground/inject-failures.ts +2 -2
- package/src/runtime/routes/playground/reset-circuit.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +38 -8
- package/src/runtime/routes/schedule-routes.ts +94 -7
- package/src/runtime/routes/settings-routes.ts +6 -6
- package/src/runtime/routes/stt-routes.ts +31 -25
- package/src/runtime/routes/surface-action-routes.ts +20 -19
- package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
- package/src/runtime/routes/user-route-dispatcher.ts +69 -15
- package/src/runtime/routes/user-route-import.ts +108 -0
- package/src/runtime/routes/user-route-resolution.ts +18 -1
- package/src/runtime/routes/workspace-routes.ts +0 -9
- package/src/runtime/routes/workspace-utils.ts +3 -13
- package/src/runtime/services/conversation-serializer.ts +7 -2
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
- package/src/schedule/plugin-schedule-availability.ts +58 -0
- package/src/schedule/plugin-schedule-declarations.ts +23 -27
- package/src/schedule/plugin-schedule-reconciler.ts +12 -3
- package/src/schedule/schedule-store.ts +5 -1
- package/src/schedule/scheduler.ts +9 -4
- package/src/schedule/worker-control.ts +1 -0
- package/src/schedule/worker.ts +6 -0
- package/src/security/oauth2.ts +6 -22
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
- package/src/stt/__tests__/types.test.ts +94 -0
- package/src/stt/daemon-batch-transcriber.ts +10 -0
- package/src/stt/stt-stream-session.ts +8 -4
- package/src/stt/types.ts +103 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
- package/src/subagent/consult-context.ts +11 -11
- package/src/subagent/consult-prompt.ts +26 -35
- package/src/subagent/manager.ts +21 -40
- package/src/subagent/notify.ts +7 -1
- package/src/subagent/types.ts +15 -12
- package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
- package/src/tools/acp/spawn.test.ts +97 -0
- package/src/tools/acp/spawn.ts +38 -4
- package/src/tools/credentials/ref-parse.ts +35 -0
- package/src/tools/credentials/resolve.ts +4 -10
- package/src/tools/credentials/store.ts +168 -0
- package/src/tools/document/document-tool.ts +12 -3
- package/src/tools/executor.ts +53 -15
- package/src/tools/filesystem/read.ts +27 -10
- package/src/tools/host-filesystem/read.ts +27 -15
- package/src/tools/network/url-safety.ts +9 -16
- package/src/tools/permission-checker.ts +43 -52
- package/src/tools/registry.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
- package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
- package/src/tools/shared/filesystem/types.ts +5 -5
- package/src/tools/skills/scaffold-managed.ts +25 -7
- package/src/tools/subagent/spawn.ts +28 -88
- package/src/tools/tool-types.ts +26 -13
- package/src/tools/types.ts +6 -6
- package/src/tools/ui-surface/surface-shape-docs.ts +12 -1
- package/src/tools/workflows/run-workflow.ts +1 -2
- package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
- package/src/tts/reasoning-tag-filter.ts +89 -0
- package/src/util/__tests__/worker-process-command.test.ts +37 -0
- package/src/util/logger.ts +16 -0
- package/src/util/think-tag-stream.ts +95 -0
- package/src/util/worker-process.ts +37 -4
- package/src/windows-compiled-cli.ts +32 -0
- package/src/windows-compiled-entry.ts +4 -0
- package/src/windows-compiled-logger.ts +6 -0
- package/src/windows-compiled-worker-entry.ts +29 -0
- package/src/workspace/byok-default-profile-ensure.ts +76 -24
- package/src/workspace/custom-profile-ensure.ts +4 -24
- package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
- package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
- package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
- package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
- package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/provider-commit-message-generator.ts +7 -5
- package/src/__tests__/document-workspace-file.test.ts +0 -467
- package/src/live-voice/__tests__/front-decision.test.ts +0 -645
- package/src/live-voice/front-decision.ts +0 -476
- package/src/permissions/ipc-risk-types.ts +0 -143
- package/src/permissions/risk-types.ts +0 -76
- package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
- package/src/subagent/consult-transcript.ts +0 -90
|
@@ -147,6 +147,11 @@ const { _resetAdapterInstallCacheForTests } =
|
|
|
147
147
|
await import("../../acp/auto-install.js");
|
|
148
148
|
const { ACP_CLAUDE_OAUTH_MISSING_CODE } =
|
|
149
149
|
await import("../../acp/prepare-agent-env.js");
|
|
150
|
+
const { ACP_CLAUDE_AUTH_REQUIRED_CODE, AcpAuthRequiredError } =
|
|
151
|
+
await import("../../acp/auth-required.js");
|
|
152
|
+
const { hasAcpConnectCardRaised } =
|
|
153
|
+
await import("../../acp/acp-connect-card-state.js");
|
|
154
|
+
const { AcpAgentProcess } = await import("../../acp/agent-process.js");
|
|
150
155
|
|
|
151
156
|
// ---------------------------------------------------------------------------
|
|
152
157
|
// Helpers
|
|
@@ -547,3 +552,95 @@ describe("executeAcpSpawn — CLAUDE_CODE_OAUTH_TOKEN injection", () => {
|
|
|
547
552
|
expect(result.errorCode).toBe(ACP_CLAUDE_OAUTH_MISSING_CODE);
|
|
548
553
|
});
|
|
549
554
|
});
|
|
555
|
+
|
|
556
|
+
// ---------------------------------------------------------------------------
|
|
557
|
+
// Pre-spawn Claude auth rejection
|
|
558
|
+
// ---------------------------------------------------------------------------
|
|
559
|
+
|
|
560
|
+
describe("pre-spawn Claude auth rejection", () => {
|
|
561
|
+
test("raises the recovery surface: errorCode, card guidance, registry mark", async () => {
|
|
562
|
+
// The adapter can reject session creation with auth_required (a stored
|
|
563
|
+
// credential the server refuses). The error thrown here is produced by
|
|
564
|
+
// the REAL conversion seam, not hand-built: a wire-shaped JSON-RPC
|
|
565
|
+
// auth_required rejection driven through AcpAgentProcess.createSession's
|
|
566
|
+
// actual retry path, so this test fails if that seam stops yielding the
|
|
567
|
+
// typed error the tool boundary maps to the recovery surface.
|
|
568
|
+
const proc = new AcpAgentProcess(
|
|
569
|
+
"claude",
|
|
570
|
+
{ command: "claude-agent-acp", args: [] },
|
|
571
|
+
(() => ({})) as never,
|
|
572
|
+
);
|
|
573
|
+
(proc as unknown as { connection: unknown }).connection = {
|
|
574
|
+
newSession: () =>
|
|
575
|
+
Promise.reject({ code: -32000, message: "Authentication required" }),
|
|
576
|
+
};
|
|
577
|
+
(proc as unknown as { initializeResponse: unknown }).initializeResponse = {
|
|
578
|
+
authMethods: [
|
|
579
|
+
{
|
|
580
|
+
id: "claude-ai-login",
|
|
581
|
+
name: "Claude Subscription",
|
|
582
|
+
type: "terminal",
|
|
583
|
+
},
|
|
584
|
+
],
|
|
585
|
+
};
|
|
586
|
+
(proc as unknown as { spawnedEnv: unknown }).spawnedEnv = {};
|
|
587
|
+
const realSeamError: unknown = await proc.createSession("/tmp").then(
|
|
588
|
+
() => null,
|
|
589
|
+
(e: unknown) => e,
|
|
590
|
+
);
|
|
591
|
+
expect(realSeamError).toBeInstanceOf(AcpAuthRequiredError);
|
|
592
|
+
|
|
593
|
+
spawnMock.mockImplementationOnce(async () => {
|
|
594
|
+
throw realSeamError;
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
const context = { ...makeContext(), conversationId: "conv-prespawn-auth" };
|
|
598
|
+
const result = await executeAcpSpawn(
|
|
599
|
+
{ agent: "claude", task: "do something" },
|
|
600
|
+
context,
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
expect(result.isError).toBe(true);
|
|
604
|
+
expect(result.errorCode).toBe(ACP_CLAUDE_AUTH_REQUIRED_CODE);
|
|
605
|
+
expect(result.content).toContain("Connect Claude Code");
|
|
606
|
+
expect(hasAcpConnectCardRaised("conv-prespawn-auth")).toBe(true);
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
test("a raw wire auth_required object (unwrapped initialize path) gets the same surface", async () => {
|
|
610
|
+
// initialize() cannot ride withAuthRetry (its response advertises the
|
|
611
|
+
// methods the retry selects from), so its rejection arrives as the plain
|
|
612
|
+
// JSON-RPC object rather than the typed error. The gate must recognize
|
|
613
|
+
// both shapes or this path silently flattens to a generic failure.
|
|
614
|
+
spawnMock.mockImplementationOnce(async () => {
|
|
615
|
+
throw { code: -32000, message: "Authentication required" };
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
const context = { ...makeContext(), conversationId: "conv-prespawn-raw" };
|
|
619
|
+
const result = await executeAcpSpawn(
|
|
620
|
+
{ agent: "claude", task: "do something" },
|
|
621
|
+
context,
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
expect(result.isError).toBe(true);
|
|
625
|
+
expect(result.errorCode).toBe(ACP_CLAUDE_AUTH_REQUIRED_CODE);
|
|
626
|
+
expect(result.content).toContain("Connect Claude Code");
|
|
627
|
+
expect(hasAcpConnectCardRaised("conv-prespawn-raw")).toBe(true);
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
test("a non-auth spawn failure keeps the plain flattened result", async () => {
|
|
631
|
+
spawnMock.mockImplementationOnce(async () => {
|
|
632
|
+
throw new Error("adapter exploded");
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
const context = { ...makeContext(), conversationId: "conv-prespawn-boom" };
|
|
636
|
+
const result = await executeAcpSpawn(
|
|
637
|
+
{ agent: "claude", task: "do something" },
|
|
638
|
+
context,
|
|
639
|
+
);
|
|
640
|
+
|
|
641
|
+
expect(result.isError).toBe(true);
|
|
642
|
+
expect(result.errorCode).toBeUndefined();
|
|
643
|
+
expect(result.content).toContain("adapter exploded");
|
|
644
|
+
expect(hasAcpConnectCardRaised("conv-prespawn-boom")).toBe(false);
|
|
645
|
+
});
|
|
646
|
+
});
|
package/src/tools/acp/spawn.ts
CHANGED
|
@@ -3,6 +3,12 @@ import { basename } from "node:path";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
5
|
import { markAcpConnectCardRaised } from "../../acp/acp-connect-card-state.js";
|
|
6
|
+
import {
|
|
7
|
+
ACP_AUTH_RECOVERY_GUIDANCE,
|
|
8
|
+
ACP_CLAUDE_AUTH_REQUIRED_CODE,
|
|
9
|
+
CLAUDE_ACP_COMMAND,
|
|
10
|
+
isAcpAuthRequired,
|
|
11
|
+
} from "../../acp/auth-required.js";
|
|
6
12
|
import { resolveAgentWithAutoInstall } from "../../acp/auto-install.js";
|
|
7
13
|
import { getAcpSessionManager } from "../../acp/index.js";
|
|
8
14
|
import {
|
|
@@ -64,10 +70,12 @@ export async function executeAcpSpawn(
|
|
|
64
70
|
return { content: '"task" is required.', isError: true };
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
// Pure precondition:
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
73
|
+
// Pure precondition: the session streams its results through the
|
|
74
|
+
// conversation's event sink, so a context with no sink at all (a tool run
|
|
75
|
+
// outside any conversation, e.g. the standalone CLI runner) cannot host a
|
|
76
|
+
// spawn. Checked BEFORE any side effects (auto-install mutates the host via
|
|
77
|
+
// a `bun` global install and can block for up to the install timeout).
|
|
78
|
+
// Inside a conversation the sink always exists and is always live.
|
|
71
79
|
const sendToClient = getSendToClient(context);
|
|
72
80
|
if (!sendToClient) {
|
|
73
81
|
return {
|
|
@@ -147,6 +155,32 @@ export async function executeAcpSpawn(
|
|
|
147
155
|
|
|
148
156
|
return { content: payload, isError: false };
|
|
149
157
|
} catch (err) {
|
|
158
|
+
// A pre-spawn rejection of the stored Claude credential (the adapter
|
|
159
|
+
// raises auth_required during session creation) gets the same recovery
|
|
160
|
+
// surface as the missing-token preflight: the errorCode raises the inline
|
|
161
|
+
// Connect card off this failed tool call, the registry mark dedups a
|
|
162
|
+
// redundant secure prompt, and the guidance keeps the model pointed at
|
|
163
|
+
// the card. Adapter-gated: the card repairs Claude credentials only.
|
|
164
|
+
// `isAcpAuthRequired` rather than an instanceof: it also recognizes the
|
|
165
|
+
// raw JSON-RPC auth_required object, which is how a rejection reaches
|
|
166
|
+
// this catch from the one protocol call `withAuthRetry` cannot wrap
|
|
167
|
+
// (`initialize`, whose response is what advertises the auth methods the
|
|
168
|
+
// retry would need).
|
|
169
|
+
if (
|
|
170
|
+
isAcpAuthRequired(err) &&
|
|
171
|
+
basename(agentConfig.command) === CLAUDE_ACP_COMMAND
|
|
172
|
+
) {
|
|
173
|
+
const message =
|
|
174
|
+
err instanceof Error
|
|
175
|
+
? err.message
|
|
176
|
+
: String((err as { message?: unknown }).message ?? err);
|
|
177
|
+
markAcpConnectCardRaised(context.conversationId);
|
|
178
|
+
return {
|
|
179
|
+
content: `${message}\n\n${ACP_AUTH_RECOVERY_GUIDANCE}`,
|
|
180
|
+
isError: true,
|
|
181
|
+
errorCode: ACP_CLAUDE_AUTH_REQUIRED_CODE,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
150
184
|
const msg =
|
|
151
185
|
err instanceof Error
|
|
152
186
|
? err.message
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential reference parsing. Pure string work, no store access.
|
|
3
|
+
*
|
|
4
|
+
* `"service/field"` is the shared vocabulary of every credential entry point:
|
|
5
|
+
* the CLI, the reveal and set routes, and the plugin-facing read and write
|
|
6
|
+
* APIs all name credentials this way. The rules for what counts as a valid
|
|
7
|
+
* reference live here so each path applies the same ones.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately separate from {@link ./resolve}, which reads the metadata store:
|
|
10
|
+
* a caller that only needs to split a reference should not import a module
|
|
11
|
+
* whose behavior depends on what is stored.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse a `"service/field"` credential reference into its parts.
|
|
16
|
+
*
|
|
17
|
+
* Returns undefined for anything that is not exactly one non-empty segment on
|
|
18
|
+
* each side of a single slash: no slash, a leading or trailing slash, or more
|
|
19
|
+
* than one slash as in `"fal/api/key"`.
|
|
20
|
+
*/
|
|
21
|
+
export function parseServiceFieldRef(
|
|
22
|
+
ref: string,
|
|
23
|
+
): { service: string; field: string } | undefined {
|
|
24
|
+
const slashIndex = ref.indexOf("/");
|
|
25
|
+
if (slashIndex <= 0 || slashIndex >= ref.length - 1) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
if (ref.indexOf("/", slashIndex + 1) !== -1) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
service: ref.slice(0, slashIndex),
|
|
33
|
+
field: ref.slice(slashIndex + 1),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
listCredentialMetadata,
|
|
17
17
|
} from "./metadata-store.js";
|
|
18
18
|
import type { CredentialInjectionTemplate } from "./policy-types.js";
|
|
19
|
+
import { parseServiceFieldRef } from "./ref-parse.js";
|
|
19
20
|
|
|
20
21
|
export interface ResolvedCredential {
|
|
21
22
|
credentialId: string;
|
|
@@ -95,18 +96,11 @@ export function resolveCredentialRef(
|
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
// Try as service/field
|
|
98
|
-
const
|
|
99
|
-
if (
|
|
99
|
+
const parsed = parseServiceFieldRef(ref);
|
|
100
|
+
if (!parsed) {
|
|
100
101
|
return undefined;
|
|
101
102
|
}
|
|
102
|
-
|
|
103
|
-
if (ref.indexOf("/", slashIndex + 1) !== -1) {
|
|
104
|
-
return undefined;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const service = ref.slice(0, slashIndex);
|
|
108
|
-
const field = ref.slice(slashIndex + 1);
|
|
109
|
-
return resolveByServiceField(service, field);
|
|
103
|
+
return resolveByServiceField(parsed.service, parsed.field);
|
|
110
104
|
}
|
|
111
105
|
|
|
112
106
|
/**
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential write path. The single sequence every "store a credential"
|
|
3
|
+
* caller runs.
|
|
4
|
+
*
|
|
5
|
+
* Storing a credential is not one write: the plaintext goes to the secure
|
|
6
|
+
* backend, the value is scrubbed from recent transcripts, the metadata record
|
|
7
|
+
* is upserted, and manual-token providers reconcile their connection row.
|
|
8
|
+
* Order matters (see {@link storeCredentialValue}), so the sequence lives here
|
|
9
|
+
* rather than being re-typed per entry point. The `credentials/set` route and
|
|
10
|
+
* the plugin-facing {@link ../../plugin-api/store-credential.storeCredential}
|
|
11
|
+
* both compose it.
|
|
12
|
+
*
|
|
13
|
+
* The transcript scrub and the connection sync are imported lazily, at the
|
|
14
|
+
* point of use. Both pull heavy graphs behind them (the scrub reaches the
|
|
15
|
+
* conversation database and registry; the sync reaches the OAuth store), and
|
|
16
|
+
* this module sits under `@vellumai/plugin-api`, so a static edge would drag
|
|
17
|
+
* that machinery into every plugin-api consumer for a write most of them never
|
|
18
|
+
* perform.
|
|
19
|
+
*
|
|
20
|
+
* Callers own their own transport-level argument validation and error mapping;
|
|
21
|
+
* this module throws {@link InvalidCredentialInputError} for a value it will
|
|
22
|
+
* not store and {@link CredentialStorageError} when the secure backend write
|
|
23
|
+
* fails.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import {
|
|
27
|
+
ACP_SERVICE,
|
|
28
|
+
assertAcpCredentialFormat,
|
|
29
|
+
} from "../../acp/acp-credentials.js";
|
|
30
|
+
import { credentialKey } from "../../security/credential-key.js";
|
|
31
|
+
import { normalizeSecretValue } from "../../security/secret-normalize.js";
|
|
32
|
+
import {
|
|
33
|
+
getActiveBackendName,
|
|
34
|
+
setSecureKeyAsync,
|
|
35
|
+
} from "../../security/secure-keys.js";
|
|
36
|
+
import { getLogger } from "../../util/logger.js";
|
|
37
|
+
import {
|
|
38
|
+
assertMetadataWritable,
|
|
39
|
+
upsertCredentialMetadata,
|
|
40
|
+
} from "./metadata-store.js";
|
|
41
|
+
import type { CredentialInjectionTemplate } from "./policy-types.js";
|
|
42
|
+
|
|
43
|
+
const log = getLogger("credential-store");
|
|
44
|
+
|
|
45
|
+
/** Raised for a value the store refuses before writing anything. */
|
|
46
|
+
export class InvalidCredentialInputError extends Error {
|
|
47
|
+
constructor(message: string) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.name = "InvalidCredentialInputError";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Raised when the secure backend write itself fails. */
|
|
54
|
+
export class CredentialStorageError extends Error {
|
|
55
|
+
constructor(message: string) {
|
|
56
|
+
super(message);
|
|
57
|
+
this.name = "CredentialStorageError";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface StoreCredentialValueInput {
|
|
62
|
+
service: string;
|
|
63
|
+
field: string;
|
|
64
|
+
/** Plaintext value. Edge whitespace is trimmed before storage. */
|
|
65
|
+
value: string;
|
|
66
|
+
/** Human-friendly alias shown in credential listings. */
|
|
67
|
+
alias?: string;
|
|
68
|
+
/** What the credential is for, surfaced to the agent. */
|
|
69
|
+
usageDescription?: string;
|
|
70
|
+
allowedTools?: string[];
|
|
71
|
+
allowedDomains?: string[];
|
|
72
|
+
injectionTemplates?: CredentialInjectionTemplate[];
|
|
73
|
+
/**
|
|
74
|
+
* Skip the retroactive transcript scrub. Set it only when the plaintext
|
|
75
|
+
* provably never transited a conversation (a token minted by an OAuth
|
|
76
|
+
* refresh, say), since the scrub is a bounded but real sweep of recent
|
|
77
|
+
* history.
|
|
78
|
+
*/
|
|
79
|
+
skipTranscriptScrub?: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface StoredCredential {
|
|
83
|
+
credentialId: string;
|
|
84
|
+
service: string;
|
|
85
|
+
field: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Store a credential's plaintext and reconcile everything that hangs off it.
|
|
90
|
+
*
|
|
91
|
+
* The order is load-bearing:
|
|
92
|
+
* 1. Validate the value (and the ACP format guard) so a rejected write leaves
|
|
93
|
+
* no side effects.
|
|
94
|
+
* 2. Fail early when credential metadata is not writable.
|
|
95
|
+
* 3. Write the plaintext to the secure backend.
|
|
96
|
+
* 4. Scrub the value from recent transcripts. This runs BEFORE the metadata
|
|
97
|
+
* upsert and the connection sync, because those can throw and a
|
|
98
|
+
* stored-but-unscrubbed secret must not depend on them succeeding. The
|
|
99
|
+
* credential IS stored at this point, so the scrub is best-effort hygiene
|
|
100
|
+
* and stays invisible to the caller.
|
|
101
|
+
* 5. Upsert metadata and reconcile the manual-token connection row.
|
|
102
|
+
*
|
|
103
|
+
* @throws {InvalidCredentialInputError} when the value is empty or malformed
|
|
104
|
+
* for its service.
|
|
105
|
+
* @throws {CredentialStorageError} when the secure backend rejects the write.
|
|
106
|
+
*/
|
|
107
|
+
export async function storeCredentialValue(
|
|
108
|
+
input: StoreCredentialValueInput,
|
|
109
|
+
): Promise<StoredCredential> {
|
|
110
|
+
const { service, field } = input;
|
|
111
|
+
|
|
112
|
+
const value = normalizeSecretValue(input.value);
|
|
113
|
+
if (value.length === 0) {
|
|
114
|
+
throw new InvalidCredentialInputError("value is required");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Reject an Anthropic API key pasted into the ACP OAuth-token field (a 401
|
|
118
|
+
// footgun) before it reaches storage.
|
|
119
|
+
if (service === ACP_SERVICE) {
|
|
120
|
+
assertAcpCredentialFormat(field, value);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
assertMetadataWritable();
|
|
124
|
+
|
|
125
|
+
const stored = await setSecureKeyAsync(credentialKey(service, field), value);
|
|
126
|
+
if (!stored) {
|
|
127
|
+
throw new CredentialStorageError(
|
|
128
|
+
`Failed to store credential in secure storage (backend: ${getActiveBackendName()})`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// The stored plaintext may already sit in recent transcripts: the user
|
|
133
|
+
// message that pasted it, the persisted tool_use input, the tool result
|
|
134
|
+
// echoing the command. This is the scrub seam, not setSecureKeyAsync, which
|
|
135
|
+
// also fires on OAuth refresh rotations and MCP header writes whose values
|
|
136
|
+
// never transited a transcript.
|
|
137
|
+
if (!input.skipTranscriptScrub) {
|
|
138
|
+
const { isNonSecretPlatformField, scrubStoredCredentialFromTranscripts } =
|
|
139
|
+
await import("../../daemon/credential-transcript-scrub.js");
|
|
140
|
+
if (!isNonSecretPlatformField(service, field)) {
|
|
141
|
+
try {
|
|
142
|
+
const scrubbed = await scrubStoredCredentialFromTranscripts(value);
|
|
143
|
+
log.info(
|
|
144
|
+
{ service, field, ...scrubbed },
|
|
145
|
+
"Credential stored; scrubbed value from recent transcripts",
|
|
146
|
+
);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
log.warn(
|
|
149
|
+
{ err, service, field },
|
|
150
|
+
"Credential stored, but transcript scrub failed",
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const metadata = upsertCredentialMetadata(service, field, {
|
|
157
|
+
alias: input.alias,
|
|
158
|
+
usageDescription: input.usageDescription,
|
|
159
|
+
allowedTools: input.allowedTools,
|
|
160
|
+
allowedDomains: input.allowedDomains,
|
|
161
|
+
injectionTemplates: input.injectionTemplates,
|
|
162
|
+
});
|
|
163
|
+
const { syncManualTokenConnection } =
|
|
164
|
+
await import("../../oauth/manual-token-connection.js");
|
|
165
|
+
await syncManualTokenConnection(service);
|
|
166
|
+
|
|
167
|
+
return { credentialId: metadata.credentialId, service, field };
|
|
168
|
+
}
|
|
@@ -426,13 +426,22 @@ export function executeDocumentUpdate(
|
|
|
426
426
|
};
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
+
// The store drops append content that merely restates the document's tail,
|
|
430
|
+
// so the client renders what actually landed rather than the submission.
|
|
431
|
+
const applied = result.appliedMarkdown;
|
|
432
|
+
const message = result.duplicateLeadingContentSkipped
|
|
433
|
+
? applied.length === 0
|
|
434
|
+
? "Nothing appended: the content is already at the end of the document"
|
|
435
|
+
: "Document content updated (leading content already at the end of the document was skipped)"
|
|
436
|
+
: "Document content updated";
|
|
437
|
+
|
|
429
438
|
// Send document_editor_update message to update the built-in RTE
|
|
430
439
|
if (context.sendToClient) {
|
|
431
440
|
context.sendToClient({
|
|
432
441
|
type: "document_editor_update",
|
|
433
442
|
conversationId: context.conversationId,
|
|
434
443
|
surfaceId,
|
|
435
|
-
markdown:
|
|
444
|
+
markdown: applied,
|
|
436
445
|
mode,
|
|
437
446
|
});
|
|
438
447
|
|
|
@@ -441,7 +450,7 @@ export function executeDocumentUpdate(
|
|
|
441
450
|
success: true,
|
|
442
451
|
surface_id: surfaceId,
|
|
443
452
|
mode,
|
|
444
|
-
message
|
|
453
|
+
message,
|
|
445
454
|
}),
|
|
446
455
|
isError: false,
|
|
447
456
|
};
|
|
@@ -458,7 +467,7 @@ export function executeDocumentUpdate(
|
|
|
458
467
|
success: true,
|
|
459
468
|
surface_id: surfaceId,
|
|
460
469
|
mode,
|
|
461
|
-
message:
|
|
470
|
+
message: `${message} (no client connected to render it)`,
|
|
462
471
|
}),
|
|
463
472
|
isError: false,
|
|
464
473
|
};
|
package/src/tools/executor.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
|
|
3
|
+
import type {
|
|
4
|
+
RiskAllowlistOption,
|
|
5
|
+
RiskDirectoryScopeOption,
|
|
6
|
+
RiskPatternScopeOption,
|
|
7
|
+
} from "@vellumai/gateway-client";
|
|
8
|
+
|
|
3
9
|
import { getConfig } from "../config/loader.js";
|
|
10
|
+
import {
|
|
11
|
+
classifyRisk,
|
|
12
|
+
type RiskClassificationWithMeta,
|
|
13
|
+
} from "../permissions/checker.js";
|
|
4
14
|
import { PermissionPrompter } from "../permissions/prompter.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
15
|
+
import {
|
|
16
|
+
runInPluginContext,
|
|
17
|
+
runOutsidePluginContext,
|
|
18
|
+
} from "../plugins/plugin-execution-context.js";
|
|
7
19
|
import { TokenExpiredError } from "../security/token-manager.js";
|
|
8
20
|
import {
|
|
9
21
|
recordToolError,
|
|
@@ -30,6 +42,7 @@ import { MAX_FILE_SIZE_BYTES } from "./shared/filesystem/size-guard.js";
|
|
|
30
42
|
import { ToolApprovalHandler } from "./tool-approval-handler.js";
|
|
31
43
|
import { resolveToolInvocationAlias } from "./tool-name-aliases.js";
|
|
32
44
|
import { recordToolCompletion } from "./tool-profiler.js";
|
|
45
|
+
import { RISK_LEVEL_UNCLASSIFIED } from "./tool-types.js";
|
|
33
46
|
import { type ToolContext, type ToolExecutionResult } from "./types.js";
|
|
34
47
|
|
|
35
48
|
export class ToolExecutor {
|
|
@@ -60,18 +73,34 @@ export class ToolExecutor {
|
|
|
60
73
|
): Promise<ToolExecutionResult> {
|
|
61
74
|
const startTime = Date.now();
|
|
62
75
|
let decision = "allow";
|
|
63
|
-
|
|
76
|
+
// The invocation's one classification, taken before the gates so every
|
|
77
|
+
// audit row this call can produce (gate denial, gate error, grant-consumed
|
|
78
|
+
// execution, permission decision) records the risk of what the model asked
|
|
79
|
+
// for, then handed down to `checkPermission`. A classification that does
|
|
80
|
+
// not complete (aborted, gateway unreachable) is recorded as such rather
|
|
81
|
+
// than as a level; the permission check still requires one and fails
|
|
82
|
+
// closed on its own.
|
|
83
|
+
let classification: RiskClassificationWithMeta | undefined;
|
|
84
|
+
try {
|
|
85
|
+
classification = await classifyRisk(
|
|
86
|
+
name,
|
|
87
|
+
input,
|
|
88
|
+
context.workingDir,
|
|
89
|
+
undefined,
|
|
90
|
+
undefined,
|
|
91
|
+
context.signal,
|
|
92
|
+
);
|
|
93
|
+
} catch {
|
|
94
|
+
// Stays undefined; the audit row records RISK_LEVEL_UNCLASSIFIED.
|
|
95
|
+
}
|
|
96
|
+
let riskLevel: string = classification?.level ?? RISK_LEVEL_UNCLASSIFIED;
|
|
64
97
|
let permRiskMeta:
|
|
65
98
|
| {
|
|
66
99
|
riskLevel: string;
|
|
67
100
|
riskReason: string;
|
|
68
|
-
riskScopeOptions:
|
|
69
|
-
riskAllowlistOptions?:
|
|
70
|
-
|
|
71
|
-
description: string;
|
|
72
|
-
pattern: string;
|
|
73
|
-
}>;
|
|
74
|
-
riskDirectoryScopeOptions?: Array<{ scope: string; label: string }>;
|
|
101
|
+
riskScopeOptions: RiskPatternScopeOption[];
|
|
102
|
+
riskAllowlistOptions?: RiskAllowlistOption[];
|
|
103
|
+
riskDirectoryScopeOptions?: RiskDirectoryScopeOption[];
|
|
75
104
|
isContainerized?: boolean;
|
|
76
105
|
}
|
|
77
106
|
| undefined;
|
|
@@ -106,6 +135,11 @@ export class ToolExecutor {
|
|
|
106
135
|
// consumed; substitute the parsed value (with `.catch()` recoveries
|
|
107
136
|
// applied) so validation and execution see the same input.
|
|
108
137
|
if (gateResult.parsedInput) {
|
|
138
|
+
// The permission decision is about the input that runs, so a parse that
|
|
139
|
+
// reshaped it (a `.catch()` recovery) gets its own classification.
|
|
140
|
+
if (!Bun.deepEquals(input, gateResult.parsedInput)) {
|
|
141
|
+
classification = undefined;
|
|
142
|
+
}
|
|
109
143
|
input = gateResult.parsedInput;
|
|
110
144
|
}
|
|
111
145
|
|
|
@@ -189,6 +223,7 @@ export class ToolExecutor {
|
|
|
189
223
|
context,
|
|
190
224
|
startTime,
|
|
191
225
|
computePreviewDiff,
|
|
226
|
+
classification,
|
|
192
227
|
);
|
|
193
228
|
|
|
194
229
|
riskLevel = permResult.riskLevel;
|
|
@@ -235,15 +270,18 @@ export class ToolExecutor {
|
|
|
235
270
|
const execContext = context;
|
|
236
271
|
|
|
237
272
|
// Mark the owning plugin as in context (via AsyncLocalStorage) so host
|
|
238
|
-
// APIs the tool reaches
|
|
273
|
+
// APIs the tool reaches, e.g. resolveCredential, can scope to it. The
|
|
239
274
|
// context must be established around the `execute()` call itself so the
|
|
240
|
-
// returned promise carries the binding across its awaits.
|
|
241
|
-
//
|
|
275
|
+
// returned promise carries the binding across its awaits. A non-plugin
|
|
276
|
+
// tool (default/skill/mcp/workspace) runs with the context explicitly
|
|
277
|
+
// cleared rather than merely unset: the turn may have been started by a
|
|
278
|
+
// plugin (a route handler calling `runConversationTurn`), and this tool
|
|
279
|
+
// is host code that must not inherit that plugin's identity.
|
|
242
280
|
const owner = getToolOwner(name);
|
|
243
281
|
const execPromise =
|
|
244
282
|
owner?.kind === "plugin"
|
|
245
283
|
? runInPluginContext(owner.id, () => tool.execute(input, execContext))
|
|
246
|
-
: tool.execute(input, execContext);
|
|
284
|
+
: runOutsidePluginContext(() => tool.execute(input, execContext));
|
|
247
285
|
|
|
248
286
|
let execResult: ToolExecutionResult = await executeWithTimeout(
|
|
249
287
|
execPromise,
|
|
@@ -295,7 +333,7 @@ export class ToolExecutor {
|
|
|
295
333
|
safeResult.isError,
|
|
296
334
|
);
|
|
297
335
|
|
|
298
|
-
// Merge
|
|
336
|
+
// Merge the classification's risk metadata onto the
|
|
299
337
|
// tool result so downstream consumers (AgentEvent → handleToolResult →
|
|
300
338
|
// ToolResult SSE message) can forward it to the client.
|
|
301
339
|
if (permRiskMeta) {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
IMAGE_EXTENSIONS,
|
|
13
13
|
readImageFile,
|
|
14
14
|
} from "../shared/filesystem/image-read.js";
|
|
15
|
+
import { legacyReadArgsError } from "../shared/filesystem/legacy-read-args.js";
|
|
15
16
|
import { sandboxReadPolicy } from "../shared/filesystem/path-policy.js";
|
|
16
17
|
import {
|
|
17
18
|
invalidToolInputResult,
|
|
@@ -25,8 +26,8 @@ import type {
|
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Model-input schema, the single source for both runtime validation (via
|
|
28
|
-
* `TOOL_INPUT_SCHEMAS`) and the advertised `input_schema` below. `
|
|
29
|
-
* `
|
|
29
|
+
* `TOOL_INPUT_SCHEMAS`) and the advertised `input_schema` below. `start_index`
|
|
30
|
+
* and `max_chars` catch to `undefined` because the tool ignores non-numeric
|
|
30
31
|
* values rather than failing the read.
|
|
31
32
|
*/
|
|
32
33
|
export const fileReadInputSchema = z.looseObject({
|
|
@@ -36,14 +37,16 @@ export const fileReadInputSchema = z.looseObject({
|
|
|
36
37
|
.describe(
|
|
37
38
|
"The path to the file to read (absolute or relative to working directory)",
|
|
38
39
|
),
|
|
39
|
-
|
|
40
|
+
start_index: z
|
|
40
41
|
.number()
|
|
41
|
-
.describe("
|
|
42
|
+
.describe("Character to start reading from (0-indexed). Text files only.")
|
|
42
43
|
.optional()
|
|
43
44
|
.catch(undefined),
|
|
44
|
-
|
|
45
|
+
max_chars: z
|
|
45
46
|
.number()
|
|
46
|
-
.describe(
|
|
47
|
+
.describe(
|
|
48
|
+
"Maximum number of characters to read. Defaults to 20000, which is also the ceiling. Text files only.",
|
|
49
|
+
)
|
|
47
50
|
.optional()
|
|
48
51
|
.catch(undefined),
|
|
49
52
|
activity: z
|
|
@@ -58,7 +61,7 @@ export const fileReadInputSchema = z.looseObject({
|
|
|
58
61
|
export const fileReadTool = {
|
|
59
62
|
name: "file_read",
|
|
60
63
|
description:
|
|
61
|
-
"Read the contents of a file on your own machine. Text reads return the first
|
|
64
|
+
"Read the contents of a file on your own machine. Text reads return the first 20000 characters unless you pass `max_chars`; when a read stops short the result says so, and `start_index` pages on from there. To find where something is in a large file, code_search is cheaper than paging through it. For image files (JPEG, PNG, GIF, WebP), returns the image for visual analysis. For audio files (MP3, WAV, OGG, FLAC, AAC, M4A), returns the audio for listening. Use host_file_read for files on your guardian's device instead.",
|
|
62
65
|
category: "filesystem",
|
|
63
66
|
executionTarget: "sandbox",
|
|
64
67
|
defaultRiskLevel: RiskLevel.Low,
|
|
@@ -75,9 +78,14 @@ export const fileReadTool = {
|
|
|
75
78
|
if (!parsed.success) {
|
|
76
79
|
return invalidToolInputResult("file_read", parsed.error);
|
|
77
80
|
}
|
|
78
|
-
const {
|
|
81
|
+
const {
|
|
82
|
+
path: rawPath,
|
|
83
|
+
start_index: startIndex,
|
|
84
|
+
max_chars: maxChars,
|
|
85
|
+
} = parsed.data;
|
|
79
86
|
|
|
80
|
-
// For image files, delegate to the shared image reader.
|
|
87
|
+
// For image files, delegate to the shared image reader. Media reads carry
|
|
88
|
+
// no window, so the legacy-argument guard below does not apply to them.
|
|
81
89
|
const ext = extname(rawPath).toLowerCase();
|
|
82
90
|
if (IMAGE_EXTENSIONS.has(ext)) {
|
|
83
91
|
const pathCheck = sandboxReadPolicy(rawPath, context.workingDir);
|
|
@@ -102,11 +110,20 @@ export const fileReadTool = {
|
|
|
102
110
|
return readAudioFile(pathCheck.resolved);
|
|
103
111
|
}
|
|
104
112
|
|
|
113
|
+
const legacyArgs = legacyReadArgsError("file_read", input);
|
|
114
|
+
if (legacyArgs !== undefined) {
|
|
115
|
+
return { content: legacyArgs, isError: true };
|
|
116
|
+
}
|
|
117
|
+
|
|
105
118
|
const ops = new FileSystemOps((path, opts) =>
|
|
106
119
|
sandboxReadPolicy(path, context.workingDir, opts),
|
|
107
120
|
);
|
|
108
121
|
|
|
109
|
-
const result = await ops.readFileSafe({
|
|
122
|
+
const result = await ops.readFileSafe({
|
|
123
|
+
path: rawPath,
|
|
124
|
+
startIndex,
|
|
125
|
+
maxChars,
|
|
126
|
+
});
|
|
110
127
|
|
|
111
128
|
if (!result.ok) {
|
|
112
129
|
const { error } = result;
|