@vellumai/assistant 0.6.4 → 0.6.5
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/.prettierignore +5 -0
- package/ARCHITECTURE.md +32 -36
- package/Dockerfile +12 -0
- package/README.md +3 -4
- package/bun.lock +8 -3
- package/docs/architecture/integrations.md +1 -20
- package/docs/architecture/security.md +16 -16
- package/docs/error-handling.md +111 -0
- package/docs/skills.md +10 -10
- package/docs/stt-provider-onboarding.md +2 -1
- package/knip.json +9 -2
- package/node_modules/@vellumai/ces-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
- package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
- package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
- package/node_modules/@vellumai/credential-storage/package.json +2 -2
- package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
- package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
- package/node_modules/@vellumai/egress-proxy/package.json +2 -2
- package/openapi.yaml +123 -11
- package/package.json +6 -3
- package/scripts/generate-openapi.ts +50 -11
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
- package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
- package/src/__tests__/agent-loop.test.ts +112 -1
- package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
- package/src/__tests__/anthropic-provider.test.ts +171 -2
- package/src/__tests__/approval-cascade.test.ts +31 -10
- package/src/__tests__/approval-routes-http.test.ts +134 -10
- package/src/__tests__/assistant-attachments.test.ts +44 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
- package/src/__tests__/browser-skill-endstate.test.ts +51 -182
- package/src/__tests__/btw-routes.test.ts +47 -1
- package/src/__tests__/call-controller.test.ts +1 -2
- package/src/__tests__/call-site-routing-provider.test.ts +214 -0
- package/src/__tests__/catalog-cache.test.ts +27 -4
- package/src/__tests__/channel-approval-routes.test.ts +4 -4
- package/src/__tests__/channel-reply-delivery.test.ts +300 -2
- package/src/__tests__/checker.test.ts +428 -501
- package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
- package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/config-analysis.test.ts +11 -28
- package/src/__tests__/config-loader-backfill.test.ts +174 -0
- package/src/__tests__/config-loader-corrupt.test.ts +183 -0
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
- package/src/__tests__/config-schema-cmd.test.ts +11 -5
- package/src/__tests__/config-schema.test.ts +427 -114
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +72 -73
- package/src/__tests__/contacts-write.test.ts +4 -4
- package/src/__tests__/context-token-estimator.test.ts +191 -1
- package/src/__tests__/context-window-manager.test.ts +530 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +30 -16
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +61 -17
- package/src/__tests__/conversation-agent-loop.test.ts +412 -82
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +30 -9
- package/src/__tests__/conversation-error.test.ts +37 -6
- package/src/__tests__/conversation-history-web-search.test.ts +6 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +36 -0
- package/src/__tests__/conversation-lifecycle.test.ts +336 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +30 -16
- package/src/__tests__/conversation-process-callsite.test.ts +306 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -16
- package/src/__tests__/conversation-queue.test.ts +41 -26
- package/src/__tests__/conversation-routes-disk-view.test.ts +29 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +2735 -55
- package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
- package/src/__tests__/conversation-skill-tools.test.ts +12 -146
- package/src/__tests__/conversation-slash-queue.test.ts +34 -19
- package/src/__tests__/conversation-slash-unknown.test.ts +30 -16
- package/src/__tests__/conversation-speed-override.test.ts +30 -11
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -2
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
- package/src/__tests__/conversation-unread-route.test.ts +2 -2
- package/src/__tests__/conversation-usage.test.ts +3 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
- package/src/__tests__/conversation-workspace-injection.test.ts +43 -15
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +44 -16
- package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
- package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
- package/src/__tests__/credential-vault-unit.test.ts +135 -19
- package/src/__tests__/credentials-cli.test.ts +1 -9
- package/src/__tests__/cross-provider-web-search.test.ts +84 -0
- package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
- package/src/__tests__/delete-propagation.test.ts +437 -0
- package/src/__tests__/dm-backfill.test.ts +417 -0
- package/src/__tests__/dm-persistence.test.ts +227 -0
- package/src/__tests__/edit-propagation.test.ts +280 -0
- package/src/__tests__/ephemeral-permissions.test.ts +93 -3
- package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
- package/src/__tests__/estimator-calibration.test.ts +213 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +26 -7
- package/src/__tests__/file-write-tool.test.ts +151 -1
- package/src/__tests__/filing-service.test.ts +255 -0
- package/src/__tests__/gemini-provider.test.ts +0 -3
- package/src/__tests__/guardian-grant-minting.test.ts +8 -0
- package/src/__tests__/headless-browser-interactions.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +96 -15
- package/src/__tests__/host-shell-tool.test.ts +124 -18
- package/src/__tests__/http-user-message-parity.test.ts +29 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
- package/src/__tests__/intent-routing.test.ts +1 -40
- package/src/__tests__/llm-catalog-parity.test.ts +174 -0
- package/src/__tests__/llm-context-normalization.test.ts +121 -0
- package/src/__tests__/llm-resolver.test.ts +214 -0
- package/src/__tests__/llm-schema.test.ts +223 -0
- package/src/__tests__/managed-proxy-context.test.ts +6 -2
- package/src/__tests__/messaging-skill-split.test.ts +3 -34
- package/src/__tests__/migration-import-from-url.test.ts +684 -0
- package/src/__tests__/model-intents.test.ts +9 -83
- package/src/__tests__/notification-decision-fallback.test.ts +0 -10
- package/src/__tests__/notification-decision-identity.test.ts +0 -9
- package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
- package/src/__tests__/oauth-store.test.ts +10 -7
- package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
- package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
- package/src/__tests__/openai-provider.test.ts +7 -0
- package/src/__tests__/openai-responses-provider.test.ts +396 -0
- package/src/__tests__/openrouter-provider-only.test.ts +135 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
- package/src/__tests__/permission-mode.test.ts +16 -0
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/persona-resolver.test.ts +13 -13
- package/src/__tests__/pkb-autoinject.test.ts +37 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
- package/src/__tests__/pricing.test.ts +50 -3
- package/src/__tests__/profiler-routes.test.ts +1 -1
- package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
- package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
- package/src/__tests__/provider-error-scenarios.test.ts +135 -6
- package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
- package/src/__tests__/provider-registry-ollama.test.ts +1 -2
- package/src/__tests__/proxy-approval-callback.test.ts +0 -1
- package/src/__tests__/reaction-persistence.test.ts +560 -0
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
- package/src/__tests__/risk-classifier-parity.test.ts +230 -0
- package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
- package/src/__tests__/secret-ingress-http.test.ts +28 -0
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
- package/src/__tests__/secret-scanner-executor.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +29 -1
- package/src/__tests__/server-history-render.test.ts +31 -0
- package/src/__tests__/shell-parser-property.test.ts +13 -13
- package/src/__tests__/skill-cache-store.test.ts +182 -0
- package/src/__tests__/skills.test.ts +19 -33
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-skill.test.ts +3 -8
- package/src/__tests__/starter-bundle.test.ts +35 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
- package/src/__tests__/suggestion-routes.test.ts +160 -3
- package/src/__tests__/system-prompt.test.ts +22 -35
- package/src/__tests__/task-runner.test.ts +3 -1
- package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
- package/src/__tests__/thread-backfill.test.ts +941 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
- package/src/__tests__/tool-executor.test.ts +60 -94
- package/src/__tests__/trust-store.test.ts +442 -109
- package/src/__tests__/update-bulletin-job.test.ts +389 -0
- package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
- package/src/__tests__/voice-session-bridge.test.ts +39 -0
- package/src/__tests__/volume-security-guard.test.ts +3 -2
- package/src/__tests__/web-search-history.test.ts +337 -0
- package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
- package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
- package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
- package/src/__tests__/workspace-policy.test.ts +1 -13
- package/src/acp/client-handler.ts +1 -2
- package/src/agent/loop.ts +209 -17
- package/src/avatar/resvg-lazy.test.ts +136 -0
- package/src/avatar/resvg-lazy.ts +82 -9
- package/src/avatar/traits-png-sync.ts +21 -1
- package/src/browser/__tests__/operations.test.ts +163 -0
- package/src/browser/identifiers.ts +51 -0
- package/src/browser/operations.ts +660 -0
- package/src/browser/types.ts +81 -0
- package/src/calls/guardian-question-copy.ts +2 -2
- package/src/calls/telephony-stt-routing.ts +1 -1
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/__tests__/attachment.test.ts +438 -0
- package/src/cli/commands/__tests__/browser.test.ts +554 -0
- package/src/cli/commands/__tests__/cache.test.ts +623 -0
- package/src/cli/commands/__tests__/email-list.test.ts +6 -0
- package/src/cli/commands/__tests__/email-send.test.ts +93 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
- package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
- package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
- package/src/cli/commands/__tests__/task.test.ts +913 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
- package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
- package/src/cli/commands/__tests__/ui.test.ts +1215 -0
- package/src/cli/commands/__tests__/watchers.test.ts +716 -0
- package/src/cli/commands/attachment.ts +182 -0
- package/src/cli/commands/browser.ts +350 -0
- package/src/cli/commands/cache.ts +341 -0
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/config.ts +6 -6
- package/src/cli/commands/conversations-import.ts +347 -0
- package/src/cli/commands/conversations.ts +14 -1
- package/src/cli/commands/email.ts +234 -194
- package/src/cli/commands/image-generation.ts +300 -0
- package/src/cli/commands/inference.ts +200 -0
- package/src/cli/commands/memory.ts +127 -17
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
- package/src/cli/commands/stt.ts +339 -0
- package/src/cli/commands/task.ts +795 -0
- package/src/cli/commands/trust.ts +50 -19
- package/src/cli/commands/tts.ts +273 -0
- package/src/cli/commands/ui.ts +670 -0
- package/src/cli/commands/watchers.ts +509 -0
- package/src/cli/lib/daemon-credential-client.ts +0 -19
- package/src/cli/program.ts +23 -4
- package/src/cli.ts +0 -37
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +2 -2
- package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +8 -1
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-tool-registry.ts +0 -175
- package/src/config/env.ts +7 -2
- package/src/config/feature-flag-registry.json +25 -9
- package/src/config/llm-resolver.ts +128 -0
- package/src/config/loader.ts +194 -10
- package/src/config/raw-config-utils.ts +30 -2
- package/src/config/sanitize-for-transfer.ts +35 -0
- package/src/config/schema.ts +30 -41
- package/src/config/schemas/analysis.ts +3 -22
- package/src/config/schemas/calls.ts +0 -4
- package/src/config/schemas/filing.ts +2 -7
- package/src/config/schemas/heartbeat.ts +0 -5
- package/src/config/schemas/inference.ts +3 -23
- package/src/config/schemas/llm.ts +318 -0
- package/src/config/schemas/memory-processing.ts +1 -9
- package/src/config/schemas/notifications.ts +4 -11
- package/src/config/schemas/platform.ts +3 -9
- package/src/config/schemas/security.ts +33 -0
- package/src/config/schemas/services.ts +9 -4
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/schemas/tts.ts +53 -0
- package/src/config/schemas/updates.ts +1 -1
- package/src/config/schemas/workspace-git.ts +3 -40
- package/src/config/skills.ts +2 -2
- package/src/context/__tests__/compact-prompt.test.ts +45 -0
- package/src/context/__tests__/microcompact.test.ts +805 -0
- package/src/context/estimator-calibration.ts +136 -0
- package/src/context/microcompact.ts +443 -0
- package/src/context/prompts/compact.md +12 -0
- package/src/context/token-estimator.ts +61 -3
- package/src/context/window-manager.ts +229 -25
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/executable-discovery.ts +19 -8
- package/src/credential-execution/process-manager.test.ts +109 -0
- package/src/credential-execution/process-manager.ts +65 -2
- package/src/daemon/approval-generators.ts +29 -4
- package/src/daemon/assistant-attachments.ts +24 -13
- package/src/daemon/classifier.ts +2 -2
- package/src/daemon/config-watcher.ts +0 -1
- package/src/daemon/context-overflow-reducer.ts +4 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +79 -12
- package/src/daemon/conversation-agent-loop.ts +462 -80
- package/src/daemon/conversation-attachments.ts +2 -6
- package/src/daemon/conversation-error.ts +36 -1
- package/src/daemon/conversation-lifecycle.ts +30 -6
- package/src/daemon/conversation-messaging.ts +73 -4
- package/src/daemon/conversation-process.ts +10 -4
- package/src/daemon/conversation-queue-manager.ts +3 -0
- package/src/daemon/conversation-runtime-assembly.ts +760 -29
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +389 -1
- package/src/daemon/conversation-tool-setup.ts +10 -5
- package/src/daemon/conversation-usage.ts +1 -1
- package/src/daemon/conversation.ts +118 -30
- package/src/daemon/external-skills-bootstrap.ts +41 -0
- package/src/daemon/guardian-action-generators.ts +34 -14
- package/src/daemon/handlers/config-model.test.ts +86 -0
- package/src/daemon/handlers/config-model.ts +54 -12
- package/src/daemon/handlers/conversations.ts +9 -2
- package/src/daemon/handlers/shared.ts +39 -11
- package/src/daemon/handlers/skills.ts +2 -2
- package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
- package/src/daemon/lifecycle.ts +76 -14
- package/src/daemon/message-types/conversations.ts +14 -0
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/trust.ts +0 -2
- package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
- package/src/daemon/pkb-context-tracker.test.ts +169 -0
- package/src/daemon/pkb-context-tracker.ts +125 -0
- package/src/daemon/pkb-reminder-builder.test.ts +70 -0
- package/src/daemon/pkb-reminder-builder.ts +31 -0
- package/src/daemon/providers-setup.ts +6 -0
- package/src/daemon/server.ts +117 -9
- package/src/daemon/tool-side-effects.ts +0 -9
- package/src/daemon/watch-handler.ts +4 -4
- package/src/daemon/web-search-history.ts +126 -0
- package/src/events/domain-events.ts +0 -1
- package/src/filing/filing-service.ts +9 -10
- package/src/heartbeat/heartbeat-service.ts +76 -28
- package/src/home/__tests__/feed-scheduler.test.ts +39 -11
- package/src/home/__tests__/rollup-producer.test.ts +44 -0
- package/src/home/assistant-feed-authoring.ts +4 -0
- package/src/home/emit-feed-event.ts +4 -0
- package/src/home/feed-scheduler.ts +20 -4
- package/src/home/feed-types.ts +56 -2
- package/src/home/relationship-state-writer.ts +2 -2
- package/src/home/rollup-producer.ts +34 -5
- package/src/home/suggested-prompts.ts +101 -0
- package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
- package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
- package/src/ipc/__tests__/socket-path.test.ts +73 -0
- package/src/ipc/__tests__/task-ipc.test.ts +577 -0
- package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
- package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
- package/src/ipc/cli-client.ts +2 -1
- package/src/ipc/cli-server.ts +26 -8
- package/src/ipc/gateway-client.ts +4 -4
- package/src/ipc/routes/attachment.ts +114 -0
- package/src/ipc/routes/browser-context.ts +61 -0
- package/src/ipc/routes/browser.ts +96 -0
- package/src/ipc/routes/cache.ts +96 -0
- package/src/ipc/routes/index.ts +17 -1
- package/src/ipc/routes/task-queue.ts +226 -0
- package/src/ipc/routes/task.ts +173 -0
- package/src/ipc/routes/ui-request.ts +50 -0
- package/src/ipc/routes/watcher.ts +203 -0
- package/src/ipc/socket-path.ts +100 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
- package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
- package/src/memory/admin.ts +18 -0
- package/src/memory/conversation-analyze-job.ts +14 -13
- package/src/memory/conversation-attention-store.ts +13 -6
- package/src/memory/conversation-crud.ts +103 -3
- package/src/memory/conversation-group-migration.ts +38 -6
- package/src/memory/conversation-title-service.ts +7 -4
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-backend.ts +1 -1
- package/src/memory/graph/compaction.ts +299 -0
- package/src/memory/graph/consolidation.ts +4 -4
- package/src/memory/graph/conversation-graph-memory.ts +89 -29
- package/src/memory/graph/extraction.test.ts +272 -2
- package/src/memory/graph/extraction.ts +173 -51
- package/src/memory/graph/graph-search.test.ts +92 -0
- package/src/memory/graph/graph-search.ts +4 -1
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.test.ts +459 -0
- package/src/memory/graph/retriever.ts +230 -48
- package/src/memory/graph/store.ts +41 -0
- package/src/memory/graph/tool-handlers.ts +27 -0
- package/src/memory/graph/tools.ts +6 -1
- package/src/memory/indexer.ts +5 -5
- package/src/memory/job-handlers/conversation-starters.ts +23 -20
- package/src/memory/job-handlers/summarization.ts +2 -2
- package/src/memory/job-utils.ts +7 -1
- package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
- package/src/memory/jobs/embed-pkb-file.ts +54 -0
- package/src/memory/jobs-store.ts +44 -3
- package/src/memory/jobs-worker.ts +4 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-index.test.ts +368 -0
- package/src/memory/pkb/pkb-index.ts +255 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
- package/src/memory/pkb/pkb-reconcile.ts +148 -0
- package/src/memory/pkb/pkb-search.test.ts +438 -0
- package/src/memory/pkb/pkb-search.ts +137 -0
- package/src/memory/pkb/types.ts +53 -0
- package/src/memory/qdrant-client.ts +122 -1
- package/src/memory/slack-thread-store.ts +37 -0
- package/src/messaging/providers/gmail/adapter.ts +6 -16
- package/src/messaging/providers/gmail/client.ts +22 -0
- package/src/messaging/providers/gmail/types.ts +7 -0
- package/src/messaging/providers/slack/adapter.ts +14 -2
- package/src/messaging/providers/slack/backfill.test.ts +257 -0
- package/src/messaging/providers/slack/backfill.ts +101 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
- package/src/messaging/providers/slack/message-metadata.ts +123 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
- package/src/messaging/providers/slack/render-transcript.ts +443 -0
- package/src/messaging/style-analyzer.ts +5 -2
- package/src/notifications/README.md +9 -5
- package/src/notifications/decision-engine.ts +3 -9
- package/src/notifications/preference-extractor.ts +2 -6
- package/src/oauth/oauth-store.ts +1 -0
- package/src/oauth/platform-connection.test.ts +47 -0
- package/src/oauth/platform-connection.ts +15 -5
- package/src/oauth/seed-providers.ts +4 -2
- package/src/permissions/approval-policy.test.ts +948 -0
- package/src/permissions/approval-policy.ts +257 -0
- package/src/permissions/bash-risk-classifier.test.ts +1208 -0
- package/src/permissions/bash-risk-classifier.ts +707 -0
- package/src/permissions/checker.ts +217 -708
- package/src/permissions/command-registry.test.ts +535 -0
- package/src/permissions/command-registry.ts +825 -0
- package/src/permissions/defaults.ts +26 -78
- package/src/permissions/file-risk-classifier.test.ts +535 -0
- package/src/permissions/file-risk-classifier.ts +274 -0
- package/src/permissions/risk-types.ts +205 -0
- package/src/permissions/secret-prompter.ts +53 -2
- package/src/permissions/skill-risk-classifier.test.ts +311 -0
- package/src/permissions/skill-risk-classifier.ts +214 -0
- package/src/permissions/trust-client.ts +52 -25
- package/src/permissions/trust-store-interface.ts +1 -6
- package/src/permissions/trust-store.ts +161 -62
- package/src/permissions/types.ts +23 -14
- package/src/permissions/web-risk-classifier.test.ts +170 -0
- package/src/permissions/web-risk-classifier.ts +89 -0
- package/src/permissions/workspace-policy.ts +1 -16
- package/src/platform/client.ts +19 -1
- package/src/prompts/persona-resolver.ts +3 -3
- package/src/prompts/system-prompt.ts +19 -20
- package/src/prompts/templates/SOUL.md +2 -2
- package/src/prompts/update-bulletin-job.ts +190 -0
- package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
- package/src/providers/__tests__/retry-callsite.test.ts +424 -0
- package/src/providers/anthropic/client.ts +183 -14
- package/src/providers/call-site-routing.ts +71 -0
- package/src/providers/gemini/client.ts +65 -2
- package/src/providers/managed-proxy/constants.ts +2 -1
- package/src/providers/model-catalog.ts +501 -33
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/chat-completions-provider.ts +57 -1
- package/src/providers/openai/responses-provider.ts +86 -9
- package/src/providers/openrouter/client.ts +76 -9
- package/src/providers/provider-env-vars.ts +56 -0
- package/src/providers/provider-send-message.ts +22 -5
- package/src/providers/ratelimit.ts +4 -0
- package/src/providers/registry.ts +19 -8
- package/src/providers/retry.ts +174 -39
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
- package/src/providers/speech-to-text/provider-catalog.ts +17 -0
- package/src/providers/speech-to-text/resolve.ts +7 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
- package/src/providers/speech-to-text/xai-realtime.ts +796 -0
- package/src/providers/speech-to-text/xai.test.ts +155 -0
- package/src/providers/speech-to-text/xai.ts +97 -0
- package/src/providers/types.ts +93 -3
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/__tests__/agent-wake.test.ts +43 -2
- package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
- package/src/runtime/agent-wake.ts +63 -22
- package/src/runtime/auth/route-policy.ts +4 -0
- package/src/runtime/btw-sidechain.ts +13 -3
- package/src/runtime/channel-reply-delivery.ts +106 -2
- package/src/runtime/decision-token.ts +116 -0
- package/src/runtime/gateway-client.ts +2 -2
- package/src/runtime/http-router.ts +32 -0
- package/src/runtime/http-server.ts +52 -1
- package/src/runtime/http-types.ts +23 -1
- package/src/runtime/interactive-ui.ts +362 -0
- package/src/runtime/invite-instruction-generator.ts +2 -2
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
- package/src/runtime/migrations/gcs-signed-url.ts +162 -0
- package/src/runtime/migrations/vbundle-importer.ts +154 -9
- package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
- package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
- package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
- package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
- package/src/runtime/migrations/vbundle-validator.ts +15 -6
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
- package/src/runtime/routes/approval-routes.ts +12 -17
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
- package/src/runtime/routes/avatar-routes.ts +20 -4
- package/src/runtime/routes/btw-routes.ts +1 -4
- package/src/runtime/routes/conversation-management-routes.ts +20 -2
- package/src/runtime/routes/conversation-routes.ts +133 -27
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +6 -4
- package/src/runtime/routes/events-routes.ts +16 -0
- package/src/runtime/routes/guardian-approval-interception.ts +33 -3
- package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
- package/src/runtime/routes/home-feed-routes.ts +120 -2
- package/src/runtime/routes/inbound-message-handler.ts +912 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
- package/src/runtime/routes/integrations/slack/channel.ts +25 -3
- package/src/runtime/routes/llm-context-normalization.ts +23 -1
- package/src/runtime/routes/migration-routes.ts +720 -124
- package/src/runtime/routes/settings-routes.ts +4 -2
- package/src/runtime/routes/trust-rules-routes.ts +30 -14
- package/src/runtime/routes/work-items-routes.test.ts +1 -1
- package/src/runtime/routes/work-items-routes.ts +3 -2
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
- package/src/runtime/services/analyze-conversation.ts +12 -16
- package/src/runtime/skill-route-registry.ts +28 -6
- package/src/schedule/scheduler.ts +8 -0
- package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
- package/src/security/__tests__/untrusted-content.test.ts +109 -0
- package/src/security/oauth2.ts +98 -35
- package/src/security/secure-keys.ts +7 -8
- package/src/security/token-manager.ts +27 -13
- package/src/security/untrusted-content.ts +102 -0
- package/src/skills/catalog-cache.ts +26 -7
- package/src/skills/catalog-install.ts +31 -3
- package/src/skills/skill-cache-store.ts +97 -0
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
- package/src/stt/daemon-batch-transcriber.ts +33 -0
- package/src/stt/stt-stream-session.ts +8 -1
- package/src/stt/types.ts +5 -1
- package/src/subagent/manager.ts +41 -13
- package/src/tasks/ephemeral-permissions.ts +9 -4
- package/src/telemetry/usage-telemetry-reporter.ts +27 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +45 -2
- package/src/tools/browser/browser-execution.ts +65 -38
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
- package/src/tools/credentials/tool-policy.ts +39 -5
- package/src/tools/credentials/vault.ts +9 -4
- package/src/tools/executor.ts +4 -0
- package/src/tools/filesystem/write.ts +52 -0
- package/src/tools/host-terminal/host-shell.ts +45 -5
- package/src/tools/memory/register.test.ts +185 -0
- package/src/tools/memory/register.ts +3 -1
- package/src/tools/network/web-fetch.ts +20 -10
- package/src/tools/network/web-search.ts +19 -4
- package/src/tools/permission-checker.ts +36 -15
- package/src/tools/policy-context.ts +25 -8
- package/src/tools/registry.ts +55 -3
- package/src/tools/side-effects.ts +0 -11
- package/src/tools/skills/execute.ts +2 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/terminal/backends/native.ts +51 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +1 -0
- package/src/tools/tool-manifest.ts +6 -21
- package/src/tools/types.ts +12 -3
- package/src/tools/verification-control-plane-policy.ts +1 -1
- package/src/tts/__tests__/provider-adapters.test.ts +240 -13
- package/src/tts/provider-catalog.ts +18 -0
- package/src/tts/providers/index.ts +2 -0
- package/src/tts/providers/xai-provider.ts +224 -0
- package/src/tts/types.ts +46 -0
- package/src/types/tar-stream.d.ts +66 -0
- package/src/util/json.ts +17 -0
- package/src/util/platform.ts +2 -2
- package/src/util/pricing.ts +15 -5
- package/src/watcher/engine.ts +1 -1
- package/src/watcher/providers/google-calendar.ts +134 -8
- package/src/watcher/providers/outlook-calendar.ts +42 -2
- package/src/workspace/git-service.ts +23 -4
- package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
- package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
- package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
- package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
- package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
- package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/provider-commit-message-generator.ts +19 -38
- package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
- package/src/__tests__/gmail-archive-gate.test.ts +0 -246
- package/src/__tests__/gmail-preferences.test.ts +0 -117
- package/src/__tests__/outlook-attachments.test.ts +0 -301
- package/src/__tests__/outlook-automation-tools.test.ts +0 -425
- package/src/__tests__/outlook-categories.test.ts +0 -212
- package/src/__tests__/outlook-compose-tools.test.ts +0 -325
- package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
- package/src/__tests__/outlook-follow-up.test.ts +0 -196
- package/src/__tests__/outlook-trash.test.ts +0 -77
- package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
- package/src/__tests__/update-bulletin-format.test.ts +0 -181
- package/src/__tests__/update-bulletin-state.test.ts +0 -135
- package/src/__tests__/update-bulletin.test.ts +0 -478
- package/src/__tests__/update-template-contract.test.ts +0 -29
- package/src/cli/commands/doctor.ts +0 -341
- package/src/config/bundled-skills/browser/SKILL.md +0 -88
- package/src/config/bundled-skills/browser/TOOLS.json +0 -516
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
- package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
- package/src/config/bundled-skills/gmail/SKILL.md +0 -221
- package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
- package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
- package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
- package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/google-calendar/types.ts +0 -97
- package/src/config/bundled-skills/outlook/SKILL.md +0 -196
- package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
- package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
- package/src/config/bundled-skills/slack/SKILL.md +0 -108
- package/src/config/bundled-skills/tasks/SKILL.md +0 -37
- package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
- package/src/config/bundled-skills/tasks/icon.svg +0 -34
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
- package/src/config/bundled-skills/watcher/SKILL.md +0 -31
- package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
- package/src/prompts/templates/UPDATES.md +0 -50
- package/src/prompts/update-bulletin-format.ts +0 -85
- package/src/prompts/update-bulletin-state.ts +0 -58
- package/src/prompts/update-bulletin-template-path.ts +0 -13
- package/src/prompts/update-bulletin.ts +0 -139
- package/src/shared/provider-env-vars.ts +0 -19
- package/src/tools/watcher/create.ts +0 -86
- package/src/tools/watcher/delete.ts +0 -36
- package/src/tools/watcher/digest.ts +0 -54
- package/src/tools/watcher/list.ts +0 -83
- package/src/tools/watcher/update.ts +0 -71
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic UI interaction request primitive.
|
|
3
|
+
*
|
|
4
|
+
* Provides a conversation-scoped mechanism for daemon-side code (skills,
|
|
5
|
+
* IPC handlers, CLI wrappers) to present an interactive UI surface to the
|
|
6
|
+
* user and await their response. This is intentionally decoupled from the
|
|
7
|
+
* confirmation_request / guardian approval pipeline — it serves a different
|
|
8
|
+
* purpose (ad-hoc UI prompts driven by skills or CLI commands, not
|
|
9
|
+
* tool-approval gates).
|
|
10
|
+
*
|
|
11
|
+
* Architecture:
|
|
12
|
+
* - Typed {@link InteractiveUiRequest} / {@link InteractiveUiResult}
|
|
13
|
+
* contracts define the wire shape for callers and resolvers.
|
|
14
|
+
* - A module-level resolver ({@link registerInteractiveUiResolver}) is
|
|
15
|
+
* installed once at daemon startup, following the same pattern as
|
|
16
|
+
* {@link registerDefaultWakeResolver} in `runtime/agent-wake.ts`.
|
|
17
|
+
* - {@link requestInteractiveUi} is the callable entry point. It
|
|
18
|
+
* delegates to the registered resolver; when no resolver is present
|
|
19
|
+
* (e.g. headless environments, test harnesses that don't install
|
|
20
|
+
* one), it fails closed by returning a `"cancelled"` result.
|
|
21
|
+
*
|
|
22
|
+
* Concurrency:
|
|
23
|
+
* - Requests are scoped to a single conversation and identified by a
|
|
24
|
+
* unique `surfaceId` generated at request time.
|
|
25
|
+
* - Multiple concurrent requests on different conversations are
|
|
26
|
+
* independent.
|
|
27
|
+
* - The resolver is responsible for lifecycle management of the
|
|
28
|
+
* underlying surface (show, await action/timeout, dismiss).
|
|
29
|
+
*
|
|
30
|
+
* Fail-closed guarantee:
|
|
31
|
+
* - If no resolver is registered, `requestInteractiveUi` returns
|
|
32
|
+
* `{ status: "cancelled" }` immediately.
|
|
33
|
+
* - If the request times out (per `timeoutMs`), the result status is
|
|
34
|
+
* `"timed_out"`.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { getLogger } from "../util/logger.js";
|
|
38
|
+
import { mintDecisionToken } from "./decision-token.js";
|
|
39
|
+
|
|
40
|
+
const log = getLogger("interactive-ui");
|
|
41
|
+
|
|
42
|
+
// ── Reserved action IDs ──────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Action IDs reserved for internal use. These are rejected by validation
|
|
46
|
+
* in both the CLI (`parseActions`) and the IPC route (`ui_request` Zod
|
|
47
|
+
* schema) so they never appear as custom button IDs.
|
|
48
|
+
*
|
|
49
|
+
* Two categories of reservation:
|
|
50
|
+
*
|
|
51
|
+
* - **Lifecycle events** (`selection_changed`, `content_changed`,
|
|
52
|
+
* `state_update`) — intercepted by `handleSurfaceAction` in
|
|
53
|
+
* conversation-surfaces.ts as non-terminal events (early return
|
|
54
|
+
* without resolving the pending `ui_request`).
|
|
55
|
+
*
|
|
56
|
+
* - **Cancellation triggers** (`cancel`, `dismiss`) — resolve the
|
|
57
|
+
* pending `ui_request` as `cancelled` instead of `submitted`.
|
|
58
|
+
*/
|
|
59
|
+
export const RESERVED_ACTION_IDS = new Set([
|
|
60
|
+
"selection_changed",
|
|
61
|
+
"content_changed",
|
|
62
|
+
"state_update",
|
|
63
|
+
"cancel",
|
|
64
|
+
"dismiss",
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
// ── Cancellation reasons ─────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Machine-readable reason for a `"cancelled"` outcome.
|
|
71
|
+
*
|
|
72
|
+
* - `"user_dismissed"` — the user explicitly closed/dismissed the surface
|
|
73
|
+
* - `"no_interactive_surface"` — no resolver was registered (headless / test)
|
|
74
|
+
* - `"conversation_not_found"` — the target conversation could not be located
|
|
75
|
+
* - `"resolver_unavailable"` — a resolver was registered but is not currently
|
|
76
|
+
* available (e.g. the surface transport is disconnected)
|
|
77
|
+
* - `"resolver_error"` — the resolver threw an unexpected error
|
|
78
|
+
*/
|
|
79
|
+
export type CancellationReason =
|
|
80
|
+
| "user_dismissed"
|
|
81
|
+
| "no_interactive_surface"
|
|
82
|
+
| "conversation_not_found"
|
|
83
|
+
| "resolver_unavailable"
|
|
84
|
+
| "resolver_error";
|
|
85
|
+
|
|
86
|
+
// ── Request / Result contracts ───────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Describes a single action button/option presented to the user on the
|
|
90
|
+
* interactive surface.
|
|
91
|
+
*/
|
|
92
|
+
export interface InteractiveUiAction {
|
|
93
|
+
/** Unique identifier for this action within the request. */
|
|
94
|
+
id: string;
|
|
95
|
+
/** Human-readable label shown on the button/option. */
|
|
96
|
+
label: string;
|
|
97
|
+
/**
|
|
98
|
+
* Optional variant hint for the renderer.
|
|
99
|
+
* - `"primary"` — emphasized / default action
|
|
100
|
+
* - `"danger"` — destructive action (red styling)
|
|
101
|
+
* - `"secondary"` — de-emphasized / cancel-like action
|
|
102
|
+
*/
|
|
103
|
+
variant?: "primary" | "danger" | "secondary";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* A request to show an interactive UI surface to the user and await their
|
|
108
|
+
* response.
|
|
109
|
+
*/
|
|
110
|
+
export interface InteractiveUiRequest {
|
|
111
|
+
/** Conversation this interaction is scoped to. */
|
|
112
|
+
conversationId: string;
|
|
113
|
+
/**
|
|
114
|
+
* Surface type hint for the renderer.
|
|
115
|
+
* - `"confirmation"` — yes/no or approve/deny prompt
|
|
116
|
+
* - `"form"` — structured data entry (v1 placeholder)
|
|
117
|
+
*/
|
|
118
|
+
surfaceType: "confirmation" | "form";
|
|
119
|
+
/** Optional title displayed at the top of the surface. */
|
|
120
|
+
title?: string;
|
|
121
|
+
/**
|
|
122
|
+
* Arbitrary payload describing the content of the surface. The shape
|
|
123
|
+
* depends on `surfaceType` — the runtime treats it as opaque and
|
|
124
|
+
* forwards it to the renderer.
|
|
125
|
+
*/
|
|
126
|
+
data: Record<string, unknown>;
|
|
127
|
+
/** Actions (buttons) to present. When omitted, the renderer uses its default set. */
|
|
128
|
+
actions?: InteractiveUiAction[];
|
|
129
|
+
/**
|
|
130
|
+
* Maximum time (in milliseconds) to wait for a user response before
|
|
131
|
+
* the request resolves with `status: "timed_out"`. When omitted, the
|
|
132
|
+
* resolver uses its own default timeout (typically 5 minutes).
|
|
133
|
+
*/
|
|
134
|
+
timeoutMs?: number;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The result of an interactive UI request after the user has responded
|
|
139
|
+
* or the request has expired.
|
|
140
|
+
*/
|
|
141
|
+
export interface InteractiveUiResult {
|
|
142
|
+
/**
|
|
143
|
+
* Terminal status of the interaction.
|
|
144
|
+
* - `"submitted"` — the user selected an action / submitted data
|
|
145
|
+
* - `"cancelled"` — the user explicitly dismissed the surface, or the
|
|
146
|
+
* surface could not be shown (fail-closed)
|
|
147
|
+
* - `"timed_out"` — the timeout elapsed without a user response
|
|
148
|
+
*/
|
|
149
|
+
status: "submitted" | "cancelled" | "timed_out";
|
|
150
|
+
/** The `id` of the action the user selected (when `status === "submitted"`). */
|
|
151
|
+
actionId?: string;
|
|
152
|
+
/** Structured data submitted by the user (for `surfaceType: "form"`). */
|
|
153
|
+
submittedData?: Record<string, unknown>;
|
|
154
|
+
/** Optional human-readable summary of the user's response. */
|
|
155
|
+
summary?: string;
|
|
156
|
+
/** The surface identifier that was shown, for audit/correlation. */
|
|
157
|
+
surfaceId: string;
|
|
158
|
+
/**
|
|
159
|
+
* Machine-readable reason for a `"cancelled"` outcome. Present only
|
|
160
|
+
* when `status === "cancelled"`. Allows callers to distinguish
|
|
161
|
+
* user-initiated dismissals from operational fail-closed outcomes
|
|
162
|
+
* without parsing log messages.
|
|
163
|
+
*
|
|
164
|
+
* Optional for backward compatibility — existing callers that only
|
|
165
|
+
* check `status` continue to work unchanged.
|
|
166
|
+
*/
|
|
167
|
+
cancellationReason?: CancellationReason;
|
|
168
|
+
/**
|
|
169
|
+
* Short-lived informational decision token, present when
|
|
170
|
+
* `status === "submitted"` and `surfaceType === "confirmation"`.
|
|
171
|
+
*
|
|
172
|
+
* Non-authoritative — carries metadata about the decision for audit
|
|
173
|
+
* and correlation purposes only. Does not grant any capability.
|
|
174
|
+
* Verification/replay enforcement is out of scope for v1.
|
|
175
|
+
*/
|
|
176
|
+
decisionToken?: string;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ── Resolver type ────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* A function that presents an interactive UI surface and resolves when
|
|
183
|
+
* the user responds or the timeout elapses.
|
|
184
|
+
*/
|
|
185
|
+
export type InteractiveUiResolver = (
|
|
186
|
+
request: InteractiveUiRequest,
|
|
187
|
+
) => Promise<InteractiveUiResult>;
|
|
188
|
+
|
|
189
|
+
// ── Module-level resolver registration ───────────────────────────────
|
|
190
|
+
//
|
|
191
|
+
// Same pattern as `runtime/agent-wake.ts`: a module-level default
|
|
192
|
+
// resolver that the daemon installs once at startup. Callers that use
|
|
193
|
+
// `requestInteractiveUi()` get the daemon-wired resolver automatically.
|
|
194
|
+
// Tests can register a mock resolver and reset via the test-only helper.
|
|
195
|
+
|
|
196
|
+
let _resolver: InteractiveUiResolver | null = null;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Install the process-wide interactive UI resolver. Called once at
|
|
200
|
+
* daemon startup (see `DaemonServer.start()`) with a function that
|
|
201
|
+
* knows how to show a surface on a live conversation and await the
|
|
202
|
+
* user's response.
|
|
203
|
+
*
|
|
204
|
+
* Calling this more than once replaces the prior resolver — the daemon
|
|
205
|
+
* startup path should call it exactly once.
|
|
206
|
+
*/
|
|
207
|
+
export function registerInteractiveUiResolver(
|
|
208
|
+
resolver: InteractiveUiResolver,
|
|
209
|
+
): void {
|
|
210
|
+
_resolver = resolver;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Reset the process-wide resolver. Test-only.
|
|
215
|
+
*
|
|
216
|
+
* @internal
|
|
217
|
+
*/
|
|
218
|
+
export function resetInteractiveUiResolverForTests(): void {
|
|
219
|
+
_resolver = null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ── Surface ID generation ────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
let _surfaceIdCounter = 0;
|
|
225
|
+
|
|
226
|
+
function generateSurfaceId(): string {
|
|
227
|
+
_surfaceIdCounter++;
|
|
228
|
+
return `ui-interaction-${Date.now()}-${_surfaceIdCounter}`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Reset the surface ID counter. Test-only.
|
|
233
|
+
*
|
|
234
|
+
* @internal
|
|
235
|
+
*/
|
|
236
|
+
export function resetSurfaceIdCounterForTests(): void {
|
|
237
|
+
_surfaceIdCounter = 0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ── Audit logging ────────────────────────────────────────────────────
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Emit a structured audit log entry for an interactive UI decision.
|
|
244
|
+
* Keyed by conversation/surface/request IDs so downstream consumers
|
|
245
|
+
* can correlate decisions across the system.
|
|
246
|
+
*/
|
|
247
|
+
function emitAuditLog(
|
|
248
|
+
request: InteractiveUiRequest,
|
|
249
|
+
result: InteractiveUiResult,
|
|
250
|
+
): void {
|
|
251
|
+
log.info(
|
|
252
|
+
{
|
|
253
|
+
event: "interactive_ui_decision",
|
|
254
|
+
conversationId: request.conversationId,
|
|
255
|
+
surfaceId: result.surfaceId,
|
|
256
|
+
surfaceType: request.surfaceType,
|
|
257
|
+
status: result.status,
|
|
258
|
+
actionId: result.actionId,
|
|
259
|
+
timestamp: new Date().toISOString(),
|
|
260
|
+
},
|
|
261
|
+
"interactive-ui: decision recorded",
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ── Public API ───────────────────────────────────────────────────────
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Present an interactive UI surface to the user and await their
|
|
269
|
+
* response.
|
|
270
|
+
*
|
|
271
|
+
* Fails closed: when no resolver is registered (headless, tests without
|
|
272
|
+
* setup), returns `{ status: "cancelled", surfaceId }` immediately.
|
|
273
|
+
*
|
|
274
|
+
* When the surface type is `"confirmation"` and the user selects the
|
|
275
|
+
* `"confirm"` action, a short-lived informational decision token is
|
|
276
|
+
* minted and attached to the result. Deny actions and other non-confirm
|
|
277
|
+
* outcomes do not receive a token. If token minting fails, the user's
|
|
278
|
+
* decision is still returned as `submitted` (the token is best-effort).
|
|
279
|
+
* The token is non-authoritative — see {@link mintDecisionToken} for
|
|
280
|
+
* details.
|
|
281
|
+
*
|
|
282
|
+
* Structured audit logs are emitted for all terminal outcomes
|
|
283
|
+
* (`submitted`, `cancelled`, `timed_out`).
|
|
284
|
+
*
|
|
285
|
+
* @param request - The interaction request describing the surface.
|
|
286
|
+
* @returns The user's response or a fail-closed cancellation.
|
|
287
|
+
*/
|
|
288
|
+
export async function requestInteractiveUi(
|
|
289
|
+
request: InteractiveUiRequest,
|
|
290
|
+
): Promise<InteractiveUiResult> {
|
|
291
|
+
const surfaceId = generateSurfaceId();
|
|
292
|
+
|
|
293
|
+
if (!_resolver) {
|
|
294
|
+
log.warn(
|
|
295
|
+
{
|
|
296
|
+
conversationId: request.conversationId,
|
|
297
|
+
surfaceType: request.surfaceType,
|
|
298
|
+
},
|
|
299
|
+
"interactive-ui: no resolver registered; failing closed",
|
|
300
|
+
);
|
|
301
|
+
const failResult: InteractiveUiResult = {
|
|
302
|
+
status: "cancelled",
|
|
303
|
+
surfaceId,
|
|
304
|
+
cancellationReason: "no_interactive_surface",
|
|
305
|
+
};
|
|
306
|
+
emitAuditLog(request, failResult);
|
|
307
|
+
return failResult;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
const resolverResult = await _resolver(request);
|
|
312
|
+
// Ensure the surfaceId is consistent — the resolver may or may not
|
|
313
|
+
// populate it, but the contract guarantees it is always present.
|
|
314
|
+
const finalSurfaceId = resolverResult.surfaceId || surfaceId;
|
|
315
|
+
|
|
316
|
+
const result: InteractiveUiResult = {
|
|
317
|
+
...resolverResult,
|
|
318
|
+
surfaceId: finalSurfaceId,
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// Mint an informational decision token only for affirmative
|
|
322
|
+
// confirmation actions. The token is short-lived (5 minutes) and
|
|
323
|
+
// non-authoritative in v1. Deny/cancel/timeout do not receive tokens.
|
|
324
|
+
if (
|
|
325
|
+
result.status === "submitted" &&
|
|
326
|
+
request.surfaceType === "confirmation" &&
|
|
327
|
+
result.actionId === "confirm"
|
|
328
|
+
) {
|
|
329
|
+
try {
|
|
330
|
+
result.decisionToken = mintDecisionToken({
|
|
331
|
+
conversationId: request.conversationId,
|
|
332
|
+
surfaceId: finalSurfaceId,
|
|
333
|
+
action: result.actionId,
|
|
334
|
+
});
|
|
335
|
+
} catch (tokenErr) {
|
|
336
|
+
log.warn(
|
|
337
|
+
{ err: tokenErr, surfaceId: finalSurfaceId },
|
|
338
|
+
"interactive-ui: failed to mint decision token; continuing without it",
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
emitAuditLog(request, result);
|
|
344
|
+
return result;
|
|
345
|
+
} catch (err) {
|
|
346
|
+
log.error(
|
|
347
|
+
{
|
|
348
|
+
err,
|
|
349
|
+
conversationId: request.conversationId,
|
|
350
|
+
surfaceType: request.surfaceType,
|
|
351
|
+
},
|
|
352
|
+
"interactive-ui: resolver threw; failing closed",
|
|
353
|
+
);
|
|
354
|
+
const failResult: InteractiveUiResult = {
|
|
355
|
+
status: "cancelled",
|
|
356
|
+
surfaceId,
|
|
357
|
+
cancellationReason: "resolver_error",
|
|
358
|
+
};
|
|
359
|
+
emitAuditLog(request, failResult);
|
|
360
|
+
return failResult;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
@@ -70,7 +70,7 @@ export async function generateInviteInstruction(params: {
|
|
|
70
70
|
? `Send ${contact} this link: ${params.shareUrl} — or tell them to message me${handle} with the code below.`
|
|
71
71
|
: `Tell ${contact} to message me${handle} with the code below.`;
|
|
72
72
|
|
|
73
|
-
const resolved = await resolveConfiguredProvider();
|
|
73
|
+
const resolved = await resolveConfiguredProvider("inviteInstructionGenerator");
|
|
74
74
|
if (!resolved) {
|
|
75
75
|
log.debug(
|
|
76
76
|
"No provider available for invite instruction generation, using fallback",
|
|
@@ -123,7 +123,7 @@ export async function generateInviteInstruction(params: {
|
|
|
123
123
|
[userMessage(prompt)],
|
|
124
124
|
undefined,
|
|
125
125
|
undefined,
|
|
126
|
-
{ signal, config: {
|
|
126
|
+
{ signal, config: { callSite: "inviteInstructionGenerator" } },
|
|
127
127
|
);
|
|
128
128
|
|
|
129
129
|
const text = extractText(response).trim();
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the GCS signed-URL validator.
|
|
3
|
+
*
|
|
4
|
+
* Covers:
|
|
5
|
+
* - A valid V4-signed URL (`X-Goog-Signature=...`) is accepted and the
|
|
6
|
+
* returned host/path match the input.
|
|
7
|
+
* - A valid V2-signed URL (`Signature=...`) is accepted.
|
|
8
|
+
* - `http://` is rejected with reason `scheme`.
|
|
9
|
+
* - A non-GCS host is rejected with reason `host`.
|
|
10
|
+
* - A GCS URL with no signature query param is rejected with reason
|
|
11
|
+
* `missing_signature`.
|
|
12
|
+
* - A GCS URL with a non-default explicit port (e.g. `:1234`, `:80`) is
|
|
13
|
+
* rejected with reason `port`. A URL with `:443` is accepted because
|
|
14
|
+
* WHATWG URL normalizes the default HTTPS port to an empty port
|
|
15
|
+
* string.
|
|
16
|
+
* - Path traversal (`/bucket/../foo`) is rejected with reason
|
|
17
|
+
* `path_traversal`, even though the URL parser would normalize it.
|
|
18
|
+
* - Encoded-slash traversal (`%2e%2e%2fother`), backslash-separator
|
|
19
|
+
* traversal (`\..\secret`), uppercase percent encoding, and an
|
|
20
|
+
* encoded dot-dot at the start of the path are all rejected.
|
|
21
|
+
* - A malformed URL string is rejected with reason `invalid_url`.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { describe, expect, test } from "bun:test";
|
|
25
|
+
|
|
26
|
+
import { validateGcsSignedUrl } from "../gcs-signed-url.js";
|
|
27
|
+
|
|
28
|
+
describe("validateGcsSignedUrl", () => {
|
|
29
|
+
test("accepts a V4-signed URL", () => {
|
|
30
|
+
const url =
|
|
31
|
+
"https://storage.googleapis.com/my-bucket/path/to/object.tgz" +
|
|
32
|
+
"?X-Goog-Algorithm=GOOG4-RSA-SHA256" +
|
|
33
|
+
"&X-Goog-Credential=service-account%40project.iam.gserviceaccount.com" +
|
|
34
|
+
"&X-Goog-Date=20260420T000000Z" +
|
|
35
|
+
"&X-Goog-Expires=3600" +
|
|
36
|
+
"&X-Goog-SignedHeaders=host" +
|
|
37
|
+
"&X-Goog-Signature=deadbeef";
|
|
38
|
+
|
|
39
|
+
const result = validateGcsSignedUrl(url);
|
|
40
|
+
expect(result.ok).toBe(true);
|
|
41
|
+
if (result.ok) {
|
|
42
|
+
expect(result.host).toBe("storage.googleapis.com");
|
|
43
|
+
expect(result.path).toBe("/my-bucket/path/to/object.tgz");
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("accepts a V2-signed URL", () => {
|
|
48
|
+
const url =
|
|
49
|
+
"https://storage.googleapis.com/my-bucket/object.tgz" +
|
|
50
|
+
"?GoogleAccessId=service-account%40project.iam.gserviceaccount.com" +
|
|
51
|
+
"&Expires=1700000000" +
|
|
52
|
+
"&Signature=abc123";
|
|
53
|
+
|
|
54
|
+
const result = validateGcsSignedUrl(url);
|
|
55
|
+
expect(result.ok).toBe(true);
|
|
56
|
+
if (result.ok) {
|
|
57
|
+
expect(result.host).toBe("storage.googleapis.com");
|
|
58
|
+
expect(result.path).toBe("/my-bucket/object.tgz");
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("rejects http:// scheme", () => {
|
|
63
|
+
const url =
|
|
64
|
+
"http://storage.googleapis.com/my-bucket/object.tgz" +
|
|
65
|
+
"?X-Goog-Signature=deadbeef";
|
|
66
|
+
|
|
67
|
+
const result = validateGcsSignedUrl(url);
|
|
68
|
+
expect(result).toEqual({ ok: false, reason: "scheme" });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("rejects a non-GCS host", () => {
|
|
72
|
+
const url =
|
|
73
|
+
"https://evil.com/my-bucket/object.tgz" + "?X-Goog-Signature=deadbeef";
|
|
74
|
+
|
|
75
|
+
const result = validateGcsSignedUrl(url);
|
|
76
|
+
expect(result).toEqual({ ok: false, reason: "host" });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("rejects a GCS URL with no signature", () => {
|
|
80
|
+
const url = "https://storage.googleapis.com/bucket/key";
|
|
81
|
+
|
|
82
|
+
const result = validateGcsSignedUrl(url);
|
|
83
|
+
expect(result).toEqual({ ok: false, reason: "missing_signature" });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("rejects a GCS URL with a non-default port (1234)", () => {
|
|
87
|
+
const url =
|
|
88
|
+
"https://storage.googleapis.com:1234/bucket/key" +
|
|
89
|
+
"?X-Goog-Signature=deadbeef";
|
|
90
|
+
|
|
91
|
+
const result = validateGcsSignedUrl(url);
|
|
92
|
+
expect(result).toEqual({ ok: false, reason: "port" });
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("rejects a GCS URL with HTTP default port (:80)", () => {
|
|
96
|
+
const url =
|
|
97
|
+
"https://storage.googleapis.com:80/bucket/key" +
|
|
98
|
+
"?X-Goog-Signature=deadbeef";
|
|
99
|
+
|
|
100
|
+
const result = validateGcsSignedUrl(url);
|
|
101
|
+
expect(result).toEqual({ ok: false, reason: "port" });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("accepts a GCS URL with explicit :443 (normalized to empty port)", () => {
|
|
105
|
+
// WHATWG URL normalizes `:443` to an empty port string for HTTPS,
|
|
106
|
+
// so the port check (`parsed.port !== ""`) does not reject it.
|
|
107
|
+
// A correctly-issued signed URL with an explicit default port is
|
|
108
|
+
// therefore unaffected by the port guard.
|
|
109
|
+
const url =
|
|
110
|
+
"https://storage.googleapis.com:443/my-bucket/object.tgz" +
|
|
111
|
+
"?X-Goog-Signature=deadbeef";
|
|
112
|
+
|
|
113
|
+
// Sanity-check the WHATWG normalization assumption.
|
|
114
|
+
expect(new URL(url).port).toBe("");
|
|
115
|
+
|
|
116
|
+
const result = validateGcsSignedUrl(url);
|
|
117
|
+
expect(result.ok).toBe(true);
|
|
118
|
+
if (result.ok) {
|
|
119
|
+
expect(result.host).toBe("storage.googleapis.com");
|
|
120
|
+
expect(result.path).toBe("/my-bucket/object.tgz");
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("rejects path traversal", () => {
|
|
125
|
+
const url =
|
|
126
|
+
"https://storage.googleapis.com/bucket/../foo" +
|
|
127
|
+
"?X-Goog-Signature=deadbeef";
|
|
128
|
+
|
|
129
|
+
const result = validateGcsSignedUrl(url);
|
|
130
|
+
expect(result).toEqual({ ok: false, reason: "path_traversal" });
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("rejects path traversal with encoded slash (%2e%2e%2fother)", () => {
|
|
134
|
+
const url =
|
|
135
|
+
"https://storage.googleapis.com/bucket/%2e%2e%2fother" +
|
|
136
|
+
"?X-Goog-Signature=deadbeef";
|
|
137
|
+
|
|
138
|
+
const result = validateGcsSignedUrl(url);
|
|
139
|
+
expect(result).toEqual({ ok: false, reason: "path_traversal" });
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("rejects path traversal with backslash separators", () => {
|
|
143
|
+
// Note: the WHATWG URL parser normalizes `\` to `/` for special
|
|
144
|
+
// schemes like `https:`, so `new URL()` would collapse this to
|
|
145
|
+
// `/secret`. The raw-path guard must still reject it.
|
|
146
|
+
const url =
|
|
147
|
+
"https://storage.googleapis.com/bucket\\..\\secret" +
|
|
148
|
+
"?X-Goog-Signature=deadbeef";
|
|
149
|
+
|
|
150
|
+
const result = validateGcsSignedUrl(url);
|
|
151
|
+
expect(result).toEqual({ ok: false, reason: "path_traversal" });
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("rejects path traversal with uppercase percent encoding", () => {
|
|
155
|
+
const url =
|
|
156
|
+
"https://storage.googleapis.com/bucket/%2E%2E/other" +
|
|
157
|
+
"?X-Goog-Signature=deadbeef";
|
|
158
|
+
|
|
159
|
+
const result = validateGcsSignedUrl(url);
|
|
160
|
+
expect(result).toEqual({ ok: false, reason: "path_traversal" });
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("rejects encoded dot-dot at the start of the path", () => {
|
|
164
|
+
const url =
|
|
165
|
+
"https://storage.googleapis.com/%2e%2e/bucket/key" +
|
|
166
|
+
"?X-Goog-Signature=deadbeef";
|
|
167
|
+
|
|
168
|
+
const result = validateGcsSignedUrl(url);
|
|
169
|
+
expect(result).toEqual({ ok: false, reason: "path_traversal" });
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("rejects a malformed URL", () => {
|
|
173
|
+
const result = validateGcsSignedUrl("not a url at all");
|
|
174
|
+
expect(result).toEqual({ ok: false, reason: "invalid_url" });
|
|
175
|
+
});
|
|
176
|
+
});
|