@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
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical URL normalization for web tool inputs, shared by the gateway's
|
|
3
|
+
* risk classifiers and the daemon's URL safety checks.
|
|
4
|
+
*
|
|
5
|
+
* A URL a model passes to `web_fetch` / `network_request` reaches more than
|
|
6
|
+
* one consumer, and they must agree on what counts as the same target: the
|
|
7
|
+
* gateway builds the trust-rule ladder from it, and anything that later
|
|
8
|
+
* compares a URL to a saved rule has to fold the same spellings together. Two
|
|
9
|
+
* normalizations would mean two answers, so it lives here rather than in
|
|
10
|
+
* either process.
|
|
11
|
+
*
|
|
12
|
+
* Data-only in spirit: no imports, no config, no I/O.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Whether a bare `host:port` (or `[v6]:port`) shorthand was written. */
|
|
16
|
+
export function looksLikeHostPortShorthand(value: string): boolean {
|
|
17
|
+
if (/^\[[0-9a-fA-F:.%]+\]:\d+(?:[/?#]|$)/.test(value)) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
return /^[^/?#@\s:]+:\d+(?:[/?#]|$)/.test(value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Whether the input is a path, query, or fragment rather than a URL. */
|
|
24
|
+
export function looksLikePathOnlyInput(value: string): boolean {
|
|
25
|
+
return (
|
|
26
|
+
value.startsWith("/") ||
|
|
27
|
+
value.startsWith("./") ||
|
|
28
|
+
value.startsWith("../") ||
|
|
29
|
+
value.startsWith("?") ||
|
|
30
|
+
value.startsWith("#")
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Strip the parts of a URL that must not affect a trust decision, and fold
|
|
36
|
+
* the encodings that would otherwise let one URL wear two spellings.
|
|
37
|
+
*
|
|
38
|
+
* Percent-escaped path segments are decoded (`/%70rivate` and `/private` are
|
|
39
|
+
* one path, so a path-scoped rule cannot be bypassed by escaping), a trailing
|
|
40
|
+
* root dot is dropped from the hostname, and fragment and userinfo are
|
|
41
|
+
* removed: the fragment never reaches the server, and credentials in the
|
|
42
|
+
* authority must never end up in a saved rule.
|
|
43
|
+
*/
|
|
44
|
+
export function canonicalizeWebUrl(parsed: URL): URL {
|
|
45
|
+
parsed.hash = "";
|
|
46
|
+
parsed.username = "";
|
|
47
|
+
parsed.password = "";
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
parsed.pathname = decodeURI(parsed.pathname);
|
|
51
|
+
} catch {
|
|
52
|
+
// Keep the URL parser's canonical form when decoding fails.
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (parsed.hostname.endsWith(".")) {
|
|
56
|
+
parsed.hostname = parsed.hostname.replace(/\.+$/, "");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return parsed;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Parse and canonicalize a web tool's `url` input, or `null` when it is not a
|
|
64
|
+
* URL this system will fetch.
|
|
65
|
+
*
|
|
66
|
+
* Accepts `https://host/path`, `http://…`, bare `host/path`, and `host:port`
|
|
67
|
+
* shorthand. Rejects path-only input and any other scheme (`file:`, `data:`,
|
|
68
|
+
* `javascript:`), so a non-http target can never acquire a web trust rule.
|
|
69
|
+
*/
|
|
70
|
+
export function normalizeWebUrl(rawUrl: string): URL | null {
|
|
71
|
+
const trimmed = rawUrl.trim();
|
|
72
|
+
if (!trimmed) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (looksLikeHostPortShorthand(trimmed)) {
|
|
77
|
+
try {
|
|
78
|
+
return canonicalizeWebUrl(new URL(`https://${trimmed}`));
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const parsed = new URL(trimmed);
|
|
86
|
+
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
|
|
87
|
+
return canonicalizeWebUrl(parsed);
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
} catch {
|
|
91
|
+
// Not an absolute URL; fall through to the shorthand forms.
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (looksLikePathOnlyInput(trimmed)) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
return canonicalizeWebUrl(new URL(`https://${trimmed}`));
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"./remote-web-pairing": "./src/remote-web-pairing.ts",
|
|
15
15
|
"./twilio-ingress": "./src/twilio-ingress.ts",
|
|
16
16
|
"./trust-rules": "./src/trust-rules.ts",
|
|
17
|
+
"./url-normalization": "./src/url-normalization.ts",
|
|
17
18
|
"./handles": "./src/handles.ts",
|
|
18
19
|
"./rpc": "./src/rpc.ts",
|
|
19
20
|
"./attachment-naming": "./src/attachment-naming.ts",
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
canonicalizeWebUrl,
|
|
5
|
+
looksLikeHostPortShorthand,
|
|
6
|
+
looksLikePathOnlyInput,
|
|
7
|
+
normalizeWebUrl,
|
|
8
|
+
} from "../url-normalization.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* These are the rules a trust rule is saved and matched under. A change here
|
|
12
|
+
* changes which saved rules still match, so each case states the target the
|
|
13
|
+
* two spellings must agree on.
|
|
14
|
+
*/
|
|
15
|
+
describe("normalizeWebUrl", () => {
|
|
16
|
+
test("keeps an ordinary https URL as-is", () => {
|
|
17
|
+
expect(normalizeWebUrl("https://example.com/docs/page")?.href).toBe(
|
|
18
|
+
"https://example.com/docs/page",
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("drops the fragment: it never reaches the server", () => {
|
|
23
|
+
expect(normalizeWebUrl("https://example.com/docs#section")?.href).toBe(
|
|
24
|
+
"https://example.com/docs",
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("drops userinfo so credentials cannot land in a saved rule", () => {
|
|
29
|
+
const credentialed = new URL("https://example.com/docs/page");
|
|
30
|
+
credentialed.username = "demo";
|
|
31
|
+
credentialed.password = ["c", "r", "e", "d", "1", "2", "3"].join("");
|
|
32
|
+
|
|
33
|
+
const normalized = normalizeWebUrl(credentialed.href);
|
|
34
|
+
expect(normalized?.href).toBe("https://example.com/docs/page");
|
|
35
|
+
expect(normalized?.username).toBe("");
|
|
36
|
+
expect(normalized?.password).toBe("");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("strips a trailing root dot from the hostname", () => {
|
|
40
|
+
expect(normalizeWebUrl("https://example.com./docs/page")?.href).toBe(
|
|
41
|
+
"https://example.com/docs/page",
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("decodes escaped path segments so one path has one spelling", () => {
|
|
46
|
+
// Without this, a rule scoped to /private is bypassed by /%70rivate.
|
|
47
|
+
expect(normalizeWebUrl("https://example.com/%70rivate")?.href).toBe(
|
|
48
|
+
"https://example.com/private",
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("reads scheme-less input as https", () => {
|
|
53
|
+
expect(normalizeWebUrl("example.com/docs")?.href).toBe(
|
|
54
|
+
"https://example.com/docs",
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("reads host:port shorthand as an https origin, not a scheme", () => {
|
|
59
|
+
expect(normalizeWebUrl("example.com:8443/status")?.origin).toBe(
|
|
60
|
+
"https://example.com:8443",
|
|
61
|
+
);
|
|
62
|
+
expect(normalizeWebUrl("[2001:db8::1]:8443/status")?.origin).toBe(
|
|
63
|
+
"https://[2001:db8::1]:8443",
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("rejects path-only input rather than coercing it to a host", () => {
|
|
68
|
+
for (const input of ["/etc/passwd", "./rel", "../up", "?q=1", "#frag"]) {
|
|
69
|
+
expect(normalizeWebUrl(input)).toBeNull();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("rejects every non-http scheme", () => {
|
|
74
|
+
for (const input of [
|
|
75
|
+
"file:///etc/passwd",
|
|
76
|
+
"data:text/html,<script>",
|
|
77
|
+
"javascript:alert(1)",
|
|
78
|
+
"ftp://example.com/f",
|
|
79
|
+
]) {
|
|
80
|
+
expect(normalizeWebUrl(input)).toBeNull();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("rejects empty and whitespace-only input", () => {
|
|
85
|
+
expect(normalizeWebUrl("")).toBeNull();
|
|
86
|
+
expect(normalizeWebUrl(" ")).toBeNull();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("trims surrounding whitespace", () => {
|
|
90
|
+
expect(normalizeWebUrl(" https://example.com/a ")?.href).toBe(
|
|
91
|
+
"https://example.com/a",
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("returns null rather than throwing on an unparseable authority", () => {
|
|
96
|
+
expect(normalizeWebUrl("https://")).toBeNull();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("canonicalizeWebUrl", () => {
|
|
101
|
+
test("keeps the parser's form when the path is not decodable, without throwing", () => {
|
|
102
|
+
// `%zz` and a truncated UTF-8 sequence are not valid escapes; the path
|
|
103
|
+
// must survive unchanged rather than the call throwing.
|
|
104
|
+
expect(canonicalizeWebUrl(new URL("https://example.com/100%zz")).href).toBe(
|
|
105
|
+
"https://example.com/100%zz",
|
|
106
|
+
);
|
|
107
|
+
expect(
|
|
108
|
+
canonicalizeWebUrl(new URL("https://example.com/%E0%A4%A")).href,
|
|
109
|
+
).toBe("https://example.com/%E0%A4%A");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("decodes an escaped percent, so `%25` and `%` are one path", () => {
|
|
113
|
+
expect(canonicalizeWebUrl(new URL("https://example.com/100%25")).href).toBe(
|
|
114
|
+
"https://example.com/100%",
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("input shape predicates", () => {
|
|
120
|
+
test("host:port shorthand is recognized, scheme-prefixed input is not", () => {
|
|
121
|
+
expect(looksLikeHostPortShorthand("example.com:8443/x")).toBe(true);
|
|
122
|
+
expect(looksLikeHostPortShorthand("[2001:db8::1]:443")).toBe(true);
|
|
123
|
+
expect(looksLikeHostPortShorthand("https://example.com/x")).toBe(false);
|
|
124
|
+
expect(looksLikeHostPortShorthand("example.com/x")).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("path-only input is recognized", () => {
|
|
128
|
+
expect(looksLikePathOnlyInput("/abs")).toBe(true);
|
|
129
|
+
expect(looksLikePathOnlyInput("./rel")).toBe(true);
|
|
130
|
+
expect(looksLikePathOnlyInput("../up")).toBe(true);
|
|
131
|
+
expect(looksLikePathOnlyInput("?q=1")).toBe(true);
|
|
132
|
+
expect(looksLikePathOnlyInput("#frag")).toBe(true);
|
|
133
|
+
expect(looksLikePathOnlyInput("example.com/x")).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
* assistant through (Slack, Telegram, WhatsApp, phone, …) plus a couple of
|
|
7
7
|
* internal ids (`vellum` for native app conversations, `platform` for the
|
|
8
8
|
* internal control plane). This is the single source of truth for that set:
|
|
9
|
+
*
|
|
10
|
+
* One id, `plugin`, does not name a surface: it names *every* surface a plugin
|
|
11
|
+
* brings. A plugin channel's real identity is the plugin, which is workspace
|
|
12
|
+
* state and cannot be a compile-time union member, so the plugin name travels
|
|
13
|
+
* in `sourceMetadata.plugin` and is prefixed onto every external id the gateway
|
|
14
|
+
* forwards (`imessage:+15551234567`). Two plugins therefore share a channel
|
|
15
|
+
* row — one admission floor, one set of channel-wide defaults — while their
|
|
16
|
+
* conversations, contacts, and trust records stay disjoint. See
|
|
17
|
+
* `gateway/src/channels/plugin-inbound.ts` for what that concedes.
|
|
18
|
+
*
|
|
9
19
|
* the assistant adopts it wholesale as its `ChannelId`, and the gateway
|
|
10
20
|
* asserts its own (narrower) inbound list is a subset of it so the two sides
|
|
11
21
|
* cannot silently drift.
|
|
@@ -30,6 +40,7 @@ export const CHANNEL_IDS = [
|
|
|
30
40
|
"platform",
|
|
31
41
|
"a2a",
|
|
32
42
|
"discord",
|
|
43
|
+
"plugin",
|
|
33
44
|
] as const;
|
|
34
45
|
|
|
35
46
|
export type ChannelId = (typeof CHANNEL_IDS)[number];
|
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
* (`gateway/src/http/routes/remote-web-pairing-verification.ts`)
|
|
12
12
|
* - `POST /v1/remote-web/pairing-token` poll + exchange device code
|
|
13
13
|
* (`gateway/src/http/routes/remote-web-pairing-token.ts`)
|
|
14
|
+
* - `GET /v1/remote-web/pairing-requests` list pending challenges
|
|
15
|
+
* (loopback-only)
|
|
16
|
+
* - `POST /v1/remote-web/pairing-requests/approve` approve by request id
|
|
17
|
+
* (loopback-only)
|
|
18
|
+
* - `POST /v1/remote-web/pairing-requests/deny` deny (delete) by request id
|
|
19
|
+
* (loopback-only)
|
|
14
20
|
*
|
|
15
21
|
* These shapes mirror those handlers' request/response bodies exactly so the
|
|
16
22
|
* gateway, the `vellum pair` CLI (`cli/src/commands/pair.ts`), and the web SPA
|
|
@@ -68,6 +74,60 @@ export interface RemoteWebPairingVerificationResponse {
|
|
|
68
74
|
expiresAt: string;
|
|
69
75
|
}
|
|
70
76
|
|
|
77
|
+
/**
|
|
78
|
+
* One pending challenge as shown on a host approval surface.
|
|
79
|
+
*
|
|
80
|
+
* The requesting device already sees the plaintext `userCode` in its own
|
|
81
|
+
* challenge response ({@link RemoteWebPairingChallengeResponse.userCode});
|
|
82
|
+
* the loopback-gated list route is the only host-side re-exposure. Displaying
|
|
83
|
+
* it there is what lets the approver match the code against the requesting
|
|
84
|
+
* device's screen: the device-flow anti-phishing binding.
|
|
85
|
+
*/
|
|
86
|
+
export interface RemoteWebPairingRequestSummary {
|
|
87
|
+
/** Opaque server-side id used to approve or deny this request. */
|
|
88
|
+
requestId: string;
|
|
89
|
+
/** The human-readable code the requesting device is displaying (e.g. "ABCD-EFGH"). */
|
|
90
|
+
userCode: string;
|
|
91
|
+
/** Public base URL the challenge was minted for. */
|
|
92
|
+
publicBaseUrl: string;
|
|
93
|
+
/** ISO-8601 instant the challenge was minted. */
|
|
94
|
+
requestedAt: string;
|
|
95
|
+
/** ISO-8601 instant the challenge expires. */
|
|
96
|
+
expiresAt: string;
|
|
97
|
+
/**
|
|
98
|
+
* Client IP of the mint request: the loopback/host address when minted
|
|
99
|
+
* locally, or the edge-observed client address when the mint arrived
|
|
100
|
+
* through the nginx tunnel edge (which stamps it via `proxy_set_header`,
|
|
101
|
+
* so a remote client cannot smuggle a value).
|
|
102
|
+
*/
|
|
103
|
+
requesterIp: string;
|
|
104
|
+
/** User-Agent header of the mint request, or null when absent. */
|
|
105
|
+
requesterUserAgent: string | null;
|
|
106
|
+
/**
|
|
107
|
+
* Whether the mint arrived through the public tunnel edge rather than the
|
|
108
|
+
* host itself.
|
|
109
|
+
*/
|
|
110
|
+
viaEdgeProxy: boolean;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** `GET /v1/remote-web/pairing-requests` success response body (200). */
|
|
114
|
+
export interface RemoteWebPairingRequestListResponse {
|
|
115
|
+
requests: RemoteWebPairingRequestSummary[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Request body for the pairing-request approve and deny routes. The approve
|
|
120
|
+
* route's success body reuses {@link RemoteWebPairingVerificationResponse}.
|
|
121
|
+
*/
|
|
122
|
+
export interface RemoteWebPairingRequestActionRequest {
|
|
123
|
+
requestId: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** `POST /v1/remote-web/pairing-requests/deny` success response body (200). */
|
|
127
|
+
export interface RemoteWebPairingRequestDenyResponse {
|
|
128
|
+
status: "denied";
|
|
129
|
+
}
|
|
130
|
+
|
|
71
131
|
/** `POST /v1/remote-web/pairing-token` request body. */
|
|
72
132
|
export interface RemoteWebPairingTokenRequest {
|
|
73
133
|
/** The `deviceCode` from the challenge. */
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical URL normalization for web tool inputs, shared by the gateway's
|
|
3
|
+
* risk classifiers and the daemon's URL safety checks.
|
|
4
|
+
*
|
|
5
|
+
* A URL a model passes to `web_fetch` / `network_request` reaches more than
|
|
6
|
+
* one consumer, and they must agree on what counts as the same target: the
|
|
7
|
+
* gateway builds the trust-rule ladder from it, and anything that later
|
|
8
|
+
* compares a URL to a saved rule has to fold the same spellings together. Two
|
|
9
|
+
* normalizations would mean two answers, so it lives here rather than in
|
|
10
|
+
* either process.
|
|
11
|
+
*
|
|
12
|
+
* Data-only in spirit: no imports, no config, no I/O.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Whether a bare `host:port` (or `[v6]:port`) shorthand was written. */
|
|
16
|
+
export function looksLikeHostPortShorthand(value: string): boolean {
|
|
17
|
+
if (/^\[[0-9a-fA-F:.%]+\]:\d+(?:[/?#]|$)/.test(value)) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
return /^[^/?#@\s:]+:\d+(?:[/?#]|$)/.test(value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Whether the input is a path, query, or fragment rather than a URL. */
|
|
24
|
+
export function looksLikePathOnlyInput(value: string): boolean {
|
|
25
|
+
return (
|
|
26
|
+
value.startsWith("/") ||
|
|
27
|
+
value.startsWith("./") ||
|
|
28
|
+
value.startsWith("../") ||
|
|
29
|
+
value.startsWith("?") ||
|
|
30
|
+
value.startsWith("#")
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Strip the parts of a URL that must not affect a trust decision, and fold
|
|
36
|
+
* the encodings that would otherwise let one URL wear two spellings.
|
|
37
|
+
*
|
|
38
|
+
* Percent-escaped path segments are decoded (`/%70rivate` and `/private` are
|
|
39
|
+
* one path, so a path-scoped rule cannot be bypassed by escaping), a trailing
|
|
40
|
+
* root dot is dropped from the hostname, and fragment and userinfo are
|
|
41
|
+
* removed: the fragment never reaches the server, and credentials in the
|
|
42
|
+
* authority must never end up in a saved rule.
|
|
43
|
+
*/
|
|
44
|
+
export function canonicalizeWebUrl(parsed: URL): URL {
|
|
45
|
+
parsed.hash = "";
|
|
46
|
+
parsed.username = "";
|
|
47
|
+
parsed.password = "";
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
parsed.pathname = decodeURI(parsed.pathname);
|
|
51
|
+
} catch {
|
|
52
|
+
// Keep the URL parser's canonical form when decoding fails.
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (parsed.hostname.endsWith(".")) {
|
|
56
|
+
parsed.hostname = parsed.hostname.replace(/\.+$/, "");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return parsed;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Parse and canonicalize a web tool's `url` input, or `null` when it is not a
|
|
64
|
+
* URL this system will fetch.
|
|
65
|
+
*
|
|
66
|
+
* Accepts `https://host/path`, `http://…`, bare `host/path`, and `host:port`
|
|
67
|
+
* shorthand. Rejects path-only input and any other scheme (`file:`, `data:`,
|
|
68
|
+
* `javascript:`), so a non-http target can never acquire a web trust rule.
|
|
69
|
+
*/
|
|
70
|
+
export function normalizeWebUrl(rawUrl: string): URL | null {
|
|
71
|
+
const trimmed = rawUrl.trim();
|
|
72
|
+
if (!trimmed) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (looksLikeHostPortShorthand(trimmed)) {
|
|
77
|
+
try {
|
|
78
|
+
return canonicalizeWebUrl(new URL(`https://${trimmed}`));
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const parsed = new URL(trimmed);
|
|
86
|
+
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
|
|
87
|
+
return canonicalizeWebUrl(parsed);
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
} catch {
|
|
91
|
+
// Not an absolute URL; fall through to the shorthand forms.
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (looksLikePathOnlyInput(trimmed)) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
return canonicalizeWebUrl(new URL(`https://${trimmed}`));
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
|
|
13
|
+
import type { TrustClass } from "./trust-verdict-contract.js";
|
|
14
|
+
|
|
13
15
|
/**
|
|
14
16
|
* Per-channel inbound admission policy — ordered from most-restrictive
|
|
15
17
|
* (`no_one`, hard kill switch) to most-permissive (`strangers`, admits any
|
|
@@ -101,3 +103,35 @@ export function isAdmissionPolicy(value: unknown): value is AdmissionPolicy {
|
|
|
101
103
|
(ADMISSION_POLICY_VALUES as readonly string[]).includes(value)
|
|
102
104
|
);
|
|
103
105
|
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Trust-class ordinal compared against {@link ADMISSION_FLOOR} to make the
|
|
109
|
+
* admission decision. Higher rank = more trusted. Blocked and revoked members
|
|
110
|
+
* never reach this comparison, short-circuiting to deny on member status, so
|
|
111
|
+
* they carry no rank.
|
|
112
|
+
*/
|
|
113
|
+
export const TRUST_CLASS_RANK: Record<TrustClass, number> = {
|
|
114
|
+
guardian: 4,
|
|
115
|
+
trusted_contact: 3,
|
|
116
|
+
unverified_contact: 2,
|
|
117
|
+
unknown: 1,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Whether a sender of this trust class clears a channel's admission floor.
|
|
122
|
+
*
|
|
123
|
+
* The two halves of the check live together because they are meaningless
|
|
124
|
+
* apart: this compares a table keyed by {@link TrustClass} against one keyed
|
|
125
|
+
* by {@link AdmissionPolicy}, and a floor added to one without a rank in the
|
|
126
|
+
* other silently admits or denies everyone.
|
|
127
|
+
*
|
|
128
|
+
* Both enforcement points read this. The runtime's admission stage answers for
|
|
129
|
+
* every channel it receives; the gateway answers for a channel it delivers
|
|
130
|
+
* somewhere other than the runtime, where there is no later stage to ask.
|
|
131
|
+
*/
|
|
132
|
+
export function meetsAdmissionFloor(
|
|
133
|
+
policy: AdmissionPolicy,
|
|
134
|
+
trustClass: TrustClass,
|
|
135
|
+
): boolean {
|
|
136
|
+
return TRUST_CLASS_RANK[trustClass] >= ADMISSION_FLOOR[policy];
|
|
137
|
+
}
|
|
@@ -63,11 +63,16 @@ export type TrustRulesListIpcParams = z.infer<
|
|
|
63
63
|
typeof TrustRulesListIpcParamsSchema
|
|
64
64
|
>;
|
|
65
65
|
|
|
66
|
+
/** The three risk levels a trust rule can set and a registry default can carry. */
|
|
67
|
+
export const RISK_LEVEL_VALUES = ["low", "medium", "high"] as const;
|
|
68
|
+
export type RiskLevelValue = (typeof RISK_LEVEL_VALUES)[number];
|
|
69
|
+
export const RiskLevelValueSchema = z.enum(RISK_LEVEL_VALUES);
|
|
70
|
+
|
|
66
71
|
export const TrustRuleSchema = z.object({
|
|
67
72
|
id: z.string(),
|
|
68
73
|
tool: z.string(),
|
|
69
74
|
pattern: z.string(),
|
|
70
|
-
risk:
|
|
75
|
+
risk: RiskLevelValueSchema,
|
|
71
76
|
description: z.string(),
|
|
72
77
|
origin: z.enum(["default", "user_defined"]),
|
|
73
78
|
userModified: z.boolean(),
|
|
@@ -341,3 +346,163 @@ export const GetGuardianContactIpcResponseSchema = z.object({
|
|
|
341
346
|
export type GetGuardianContactIpcResponse = z.infer<
|
|
342
347
|
typeof GetGuardianContactIpcResponseSchema
|
|
343
348
|
>;
|
|
349
|
+
|
|
350
|
+
// ── classify_risk ────────────────────────────────────────────────────────────
|
|
351
|
+
// Risk classification is gateway-owned; the assistant sends one request per
|
|
352
|
+
// tool invocation and reads the whole answer back. The gateway validates the
|
|
353
|
+
// request against `ClassifyRiskIpcParamsSchema`; the assistant validates the
|
|
354
|
+
// response against `ClassifyRiskIpcResponseSchema` and fails closed on a
|
|
355
|
+
// mismatch.
|
|
356
|
+
|
|
357
|
+
/** A classified risk; `unknown` is the classifier's own "not in registry". */
|
|
358
|
+
export const ClassifiedRiskSchema = z.enum([...RISK_LEVEL_VALUES, "unknown"]);
|
|
359
|
+
export type ClassifiedRisk = z.infer<typeof ClassifiedRiskSchema>;
|
|
360
|
+
|
|
361
|
+
/** How a risk was determined: a user trust rule, the registry, or neither. */
|
|
362
|
+
export const RiskMatchTypeSchema = z.enum(["user_rule", "registry", "unknown"]);
|
|
363
|
+
export type RiskMatchType = z.infer<typeof RiskMatchTypeSchema>;
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* File classifier context the assistant pre-resolves (it owns the workspace
|
|
367
|
+
* filesystem) so the gateway can classify file tools without assistant path
|
|
368
|
+
* helpers. Directories are canonicalized (symlinks resolved) by the sender.
|
|
369
|
+
*/
|
|
370
|
+
export const ClassifyRiskFileContextSchema = z.object({
|
|
371
|
+
protectedDir: z.string(),
|
|
372
|
+
deprecatedDir: z.string(),
|
|
373
|
+
hooksDir: z.string(),
|
|
374
|
+
pluginsDir: z.string().optional(),
|
|
375
|
+
toolsDir: z.string().optional(),
|
|
376
|
+
routesDir: z.string().optional(),
|
|
377
|
+
workflowsDir: z.string().optional(),
|
|
378
|
+
/** Monitoring data dir: the sentinel lives here, writes are code-injection risk. */
|
|
379
|
+
monitoringDir: z.string().optional(),
|
|
380
|
+
actorTokenSigningKeyPath: z.string(),
|
|
381
|
+
skillSourceDirs: z.array(z.string()),
|
|
382
|
+
});
|
|
383
|
+
export type ClassifyRiskFileContext = z.infer<
|
|
384
|
+
typeof ClassifyRiskFileContextSchema
|
|
385
|
+
>;
|
|
386
|
+
|
|
387
|
+
/** Skill metadata the assistant pre-resolves for skill-load classification. */
|
|
388
|
+
export const ClassifyRiskSkillMetadataSchema = z.object({
|
|
389
|
+
skillId: z.string(),
|
|
390
|
+
selector: z.string(),
|
|
391
|
+
versionHash: z.string(),
|
|
392
|
+
transitiveHash: z.string().optional(),
|
|
393
|
+
hasInlineExpansions: z.boolean(),
|
|
394
|
+
isDynamic: z.boolean(),
|
|
395
|
+
});
|
|
396
|
+
export type ClassifyRiskSkillMetadata = z.infer<
|
|
397
|
+
typeof ClassifyRiskSkillMetadataSchema
|
|
398
|
+
>;
|
|
399
|
+
|
|
400
|
+
export const ClassifyRiskIpcParamsSchema = z.object({
|
|
401
|
+
tool: z.string().min(1),
|
|
402
|
+
command: z.string().optional(),
|
|
403
|
+
url: z.string().optional(),
|
|
404
|
+
path: z.string().optional(),
|
|
405
|
+
/**
|
|
406
|
+
* The file tool's target path with symlinks resolved by the assistant. The
|
|
407
|
+
* gateway's security escalation prefix checks use it so a symlink cannot
|
|
408
|
+
* mask a write into a protected directory; falls back to lexical resolution
|
|
409
|
+
* of `path` when absent.
|
|
410
|
+
*/
|
|
411
|
+
resolvedPath: z.string().optional(),
|
|
412
|
+
/**
|
|
413
|
+
* The sandbox file tool's working directory with symlinks resolved. Paired
|
|
414
|
+
* with `resolvedPath` for the workspace-boundary check so a symlinked
|
|
415
|
+
* workspace prefix (macOS `/var` → `/private/var`) does not read as an
|
|
416
|
+
* escape. Absent for host tools.
|
|
417
|
+
*/
|
|
418
|
+
resolvedWorkingDir: z.string().optional(),
|
|
419
|
+
skill: z.string().optional(),
|
|
420
|
+
mode: z.string().optional(),
|
|
421
|
+
script: z.string().optional(),
|
|
422
|
+
workingDir: z.string().optional(),
|
|
423
|
+
allowPrivateNetwork: z.boolean().optional(),
|
|
424
|
+
networkMode: z.string().optional(),
|
|
425
|
+
isContainerized: z.boolean().optional(),
|
|
426
|
+
workspaceRoot: z.string().optional(),
|
|
427
|
+
fileContext: ClassifyRiskFileContextSchema.optional(),
|
|
428
|
+
skillMetadata: ClassifyRiskSkillMetadataSchema.optional(),
|
|
429
|
+
/**
|
|
430
|
+
* The tool's registry default risk, for tools with no dedicated classifier.
|
|
431
|
+
* The gateway answers with it (`matchType: "registry"`); absent, the tool is
|
|
432
|
+
* unknown and answers `medium`.
|
|
433
|
+
*/
|
|
434
|
+
registryDefaultRisk: RiskLevelValueSchema.optional(),
|
|
435
|
+
/** Number of credential references attached to this tool invocation. */
|
|
436
|
+
credentialRefCount: z.number().int().nonnegative().optional(),
|
|
437
|
+
/**
|
|
438
|
+
* For `host_file_transfer` to_sandbox: the workspace-side destination path
|
|
439
|
+
* and the sandbox working directory it resolves against, so a transfer that
|
|
440
|
+
* lands an executable file in a code-injection sink (tools, routes, hooks,
|
|
441
|
+
* plugins, skills) escalates even though `path` carries the host-side source.
|
|
442
|
+
*/
|
|
443
|
+
transferSandboxDestPath: z.string().optional(),
|
|
444
|
+
transferSandboxWorkingDir: z.string().optional(),
|
|
445
|
+
/** `transferSandboxDestPath` with symlinks resolved by the assistant. */
|
|
446
|
+
resolvedTransferDestPath: z.string().optional(),
|
|
447
|
+
});
|
|
448
|
+
export type ClassifyRiskIpcParams = z.infer<typeof ClassifyRiskIpcParamsSchema>;
|
|
449
|
+
|
|
450
|
+
/** Regex ladder shown when the user saves a classification (display only). */
|
|
451
|
+
export const RiskPatternScopeOptionSchema = z.object({
|
|
452
|
+
pattern: z.string(),
|
|
453
|
+
label: z.string(),
|
|
454
|
+
});
|
|
455
|
+
export type RiskPatternScopeOption = z.infer<
|
|
456
|
+
typeof RiskPatternScopeOptionSchema
|
|
457
|
+
>;
|
|
458
|
+
|
|
459
|
+
/** Minimatch ladder for the "always allow" prompt option (what a rule matches). */
|
|
460
|
+
export const RiskAllowlistOptionSchema = z.object({
|
|
461
|
+
label: z.string(),
|
|
462
|
+
description: z.string(),
|
|
463
|
+
pattern: z.string(),
|
|
464
|
+
});
|
|
465
|
+
export type RiskAllowlistOption = z.infer<typeof RiskAllowlistOptionSchema>;
|
|
466
|
+
|
|
467
|
+
/** Directory ladder for filesystem operations; `scope` is a path glob or `everywhere`. */
|
|
468
|
+
export const RiskDirectoryScopeOptionSchema = z.object({
|
|
469
|
+
scope: z.string(),
|
|
470
|
+
label: z.string(),
|
|
471
|
+
});
|
|
472
|
+
export type RiskDirectoryScopeOption = z.infer<
|
|
473
|
+
typeof RiskDirectoryScopeOptionSchema
|
|
474
|
+
>;
|
|
475
|
+
|
|
476
|
+
export const RiskDangerousPatternSchema = z.object({
|
|
477
|
+
type: z.string(),
|
|
478
|
+
description: z.string(),
|
|
479
|
+
text: z.string(),
|
|
480
|
+
});
|
|
481
|
+
export type RiskDangerousPattern = z.infer<typeof RiskDangerousPatternSchema>;
|
|
482
|
+
|
|
483
|
+
export const ClassifyRiskIpcResponseSchema = z.object({
|
|
484
|
+
risk: ClassifiedRiskSchema,
|
|
485
|
+
reason: z.string(),
|
|
486
|
+
matchType: RiskMatchTypeSchema,
|
|
487
|
+
scopeOptions: z.array(RiskPatternScopeOptionSchema),
|
|
488
|
+
allowlistOptions: z.array(RiskAllowlistOptionSchema).optional(),
|
|
489
|
+
actionKeys: z.array(z.string()).optional(),
|
|
490
|
+
commandCandidates: z.array(z.string()).optional(),
|
|
491
|
+
dangerousPatterns: z.array(RiskDangerousPatternSchema).optional(),
|
|
492
|
+
opaqueConstructs: z.boolean().optional(),
|
|
493
|
+
isComplexSyntax: z.boolean().optional(),
|
|
494
|
+
sandboxAutoApprove: z.boolean().optional(),
|
|
495
|
+
/**
|
|
496
|
+
* Lexically-resolved path arguments from sandbox-auto-approve-eligible bash
|
|
497
|
+
* segments. The gateway has no filesystem access; the assistant resolves
|
|
498
|
+
* these through symlinks and revokes `sandboxAutoApprove` if any escapes
|
|
499
|
+
* the workspace.
|
|
500
|
+
*/
|
|
501
|
+
sandboxPathArgs: z.array(z.string()).optional(),
|
|
502
|
+
directoryScopeOptions: z.array(RiskDirectoryScopeOptionSchema).optional(),
|
|
503
|
+
/** Fully resolved filesystem path arguments, for directory-scoped rule matching. */
|
|
504
|
+
resolvedPaths: z.array(z.string()).optional(),
|
|
505
|
+
});
|
|
506
|
+
export type ClassifyRiskIpcResponse = z.infer<
|
|
507
|
+
typeof ClassifyRiskIpcResponseSchema
|
|
508
|
+
>;
|