@vellumai/assistant 0.6.4 → 0.6.6
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/AGENTS.md +9 -1
- package/ARCHITECTURE.md +43 -49
- package/Dockerfile +17 -3
- package/README.md +3 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/bun.lock +8 -3
- package/docs/architecture/integrations.md +33 -59
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +19 -18
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/error-handling.md +111 -0
- package/docs/plugins.md +761 -0
- package/docs/skills.md +10 -10
- package/docs/stt-provider-onboarding.md +2 -1
- package/examples/plugins/echo/README.md +132 -0
- package/examples/plugins/echo/package.json +17 -0
- package/examples/plugins/echo/register.ts +187 -0
- 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/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +334 -78
- 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__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +36 -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__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- 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__/bundled-asset.test.ts +6 -6
- 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 +96 -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 +870 -655
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
- package/src/__tests__/compaction-events.test.ts +501 -0
- package/src/__tests__/compaction-pipeline.test.ts +210 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
- package/src/__tests__/compaction-timeout-recovery.test.ts +262 -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-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema-cmd.test.ts +11 -5
- package/src/__tests__/config-schema.test.ts +440 -114
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +72 -73
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/contacts-write.test.ts +4 -4
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-token-estimator.test.ts +191 -1
- package/src/__tests__/context-window-manager.test.ts +883 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +32 -15
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +86 -46
- package/src/__tests__/conversation-agent-loop.test.ts +435 -216
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +36 -10
- package/src/__tests__/conversation-error.test.ts +37 -6
- package/src/__tests__/conversation-history-web-search.test.ts +7 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +34 -12
- package/src/__tests__/conversation-lifecycle.test.ts +336 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +32 -15
- package/src/__tests__/conversation-process-callsite.test.ts +309 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +44 -21
- package/src/__tests__/conversation-queue.test.ts +68 -38
- package/src/__tests__/conversation-routes-disk-view.test.ts +36 -7
- package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +2877 -152
- package/src/__tests__/conversation-runtime-workspace.test.ts +35 -50
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-skill-tools.test.ts +12 -146
- package/src/__tests__/conversation-slash-queue.test.ts +39 -19
- package/src/__tests__/conversation-slash-unknown.test.ts +53 -16
- package/src/__tests__/conversation-speed-override.test.ts +36 -12
- 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 +118 -2
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -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 +4 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +33 -9
- package/src/__tests__/conversation-workspace-injection.test.ts +46 -15
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +46 -15
- package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +5 -2
- 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__/db-schedule-syntax-migration.test.ts +1 -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__/empty-response-pipeline.test.ts +305 -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 +29 -10
- package/src/__tests__/file-write-tool.test.ts +151 -1
- package/src/__tests__/filing-service.test.ts +255 -0
- package/src/__tests__/first-greeting.test.ts +247 -5
- 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__/headless-browser-mode.test.ts +57 -0
- package/src/__tests__/heartbeat-service.test.ts +96 -15
- package/src/__tests__/history-repair-pipeline.test.ts +399 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
- package/src/__tests__/host-proxy-interface.test.ts +36 -2
- package/src/__tests__/host-shell-tool.test.ts +124 -18
- package/src/__tests__/http-user-message-parity.test.ts +29 -1
- package/src/__tests__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +1 -66
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- 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__/media-generate-image.test.ts +119 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
- package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
- package/src/__tests__/messaging-skill-split.test.ts +3 -34
- package/src/__tests__/migration-import-from-url.test.ts +621 -0
- package/src/__tests__/model-intents.test.ts +11 -83
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- 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__/notification-decision-strategy.test.ts +0 -11
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +14 -12
- package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
- package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
- package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
- package/src/__tests__/oauth-providers-routes.test.ts +3 -2
- package/src/__tests__/oauth-store.test.ts +46 -78
- package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
- package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -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__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +1 -25
- package/src/__tests__/permission-mode.test.ts +16 -0
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/persona-resolver.test.ts +13 -13
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- package/src/__tests__/pkb-autoinject.test.ts +37 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
- package/src/__tests__/platform.test.ts +5 -2
- package/src/__tests__/plugin-bootstrap.test.ts +483 -0
- package/src/__tests__/plugin-registry.test.ts +273 -0
- package/src/__tests__/plugin-route-contribution.test.ts +288 -0
- package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
- package/src/__tests__/plugin-types.test.ts +320 -0
- package/src/__tests__/pricing.test.ts +93 -14
- 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 +69 -9
- package/src/__tests__/reaction-persistence.test.ts +561 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- 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__/schedule-routes.test.ts +131 -1
- package/src/__tests__/scheduler-recurrence.test.ts +14 -70
- package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
- package/src/__tests__/secret-detection-handler.test.ts +0 -10
- 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-identity.test.ts +0 -134
- 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 +259 -3
- package/src/__tests__/system-prompt.test.ts +22 -35
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-runner.test.ts +3 -1
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/test-preload.ts +11 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
- package/src/__tests__/thread-backfill.test.ts +941 -0
- package/src/__tests__/title-generate-pipeline.test.ts +224 -0
- package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
- package/src/__tests__/tool-error-pipeline.test.ts +244 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -8
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +201 -94
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- 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__/user-plugin-loader.test.ts +191 -0
- 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-046-seed-conversation-starters-callsite.test.ts +185 -0
- package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
- package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
- package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
- package/src/__tests__/workspace-policy.test.ts +22 -16
- package/src/acp/client-handler.ts +1 -2
- package/src/agent/loop.ts +545 -115
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- 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/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- 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/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- 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/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/attachment.test.ts +438 -0
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- 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 +886 -0
- package/src/cli/commands/__tests__/inference-send.test.ts +463 -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 +606 -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/backup.ts +2 -2
- package/src/cli/commands/browser.ts +350 -0
- package/src/cli/commands/cache.ts +341 -0
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -12
- package/src/cli/commands/config.ts +6 -6
- package/src/cli/commands/conversations-import.ts +347 -0
- package/src/cli/commands/conversations.ts +69 -8
- package/src/cli/commands/email.ts +234 -194
- package/src/cli/commands/image-generation.ts +299 -0
- package/src/cli/commands/inference.ts +200 -0
- package/src/cli/commands/memory.ts +127 -17
- package/src/cli/commands/notifications.ts +68 -103
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +2 -2
- package/src/cli/commands/oauth/providers.ts +176 -8
- package/src/cli/commands/oauth/status.ts +46 -36
- 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/skills.ts +3 -4
- 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 +39 -24
- package/src/cli.ts +0 -37
- package/src/config/__tests__/backup-schema.test.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +20 -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 +69 -12
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
- package/src/config/bundled-skills/schedule/SKILL.md +8 -3
- package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-tool-registry.ts +0 -190
- package/src/config/env.ts +7 -2
- package/src/config/feature-flag-registry.json +42 -10
- 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 +49 -41
- package/src/config/schemas/analysis.ts +3 -22
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/calls.ts +0 -4
- package/src/config/schemas/conversations.ts +16 -0
- 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 +317 -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 +64 -0
- package/src/config/schemas/updates.ts +1 -1
- package/src/config/schemas/workspace-git.ts +3 -40
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +96 -7
- package/src/context/__tests__/compact-prompt.test.ts +63 -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 +26 -0
- package/src/context/token-estimator.ts +61 -3
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +417 -39
- 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/credential-health/credential-health-service.ts +19 -6
- package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
- 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 -3
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/context-overflow-reducer.ts +4 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +162 -34
- package/src/daemon/conversation-agent-loop.ts +1282 -599
- package/src/daemon/conversation-attachments.ts +2 -6
- package/src/daemon/conversation-error.ts +36 -1
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +59 -17
- package/src/daemon/conversation-messaging.ts +73 -4
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +24 -11
- package/src/daemon/conversation-queue-manager.ts +3 -0
- package/src/daemon/conversation-runtime-assembly.ts +1063 -211
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +389 -1
- package/src/daemon/conversation-tool-setup.ts +51 -9
- package/src/daemon/conversation-usage.ts +1 -1
- package/src/daemon/conversation.ts +197 -64
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/external-skills-bootstrap.ts +41 -0
- package/src/daemon/first-greeting.ts +191 -14
- 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 +65 -12
- package/src/daemon/handlers/conversations.ts +9 -2
- package/src/daemon/handlers/shared.ts +39 -11
- package/src/daemon/handlers/skills.ts +7 -3
- package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
- package/src/daemon/lifecycle.ts +109 -82
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +63 -0
- package/src/daemon/message-types/messages.ts +21 -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 +122 -12
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -65
- 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/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +99 -28
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- 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 +11 -0
- package/src/home/feed-scheduler.ts +20 -4
- package/src/home/feed-types.ts +97 -4
- package/src/home/relationship-state-writer.ts +2 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- 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 +34 -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 +6 -3
- package/src/ipc/routes/attachment.ts +114 -0
- package/src/ipc/routes/browser-context.ts +63 -0
- package/src/ipc/routes/browser.ts +97 -0
- package/src/ipc/routes/cache.ts +96 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +31 -1
- package/src/ipc/routes/list-clients.ts +31 -0
- package/src/ipc/routes/merge-contacts.ts +17 -0
- package/src/ipc/routes/notification.ts +133 -0
- package/src/ipc/routes/rename-conversation.ts +59 -0
- package/src/ipc/routes/search-contacts.ts +19 -0
- 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/upsert-contact.ts +25 -0
- package/src/ipc/routes/watcher.ts +203 -0
- package/src/ipc/socket-path.ts +76 -0
- package/src/media/app-icon-generator.ts +23 -46
- package/src/media/avatar-router.ts +26 -41
- package/src/media/gemini-image-service.ts +8 -41
- package/src/media/image-credentials.ts +73 -0
- package/src/media/image-service.ts +85 -0
- package/src/media/openai-image-service.ts +131 -0
- package/src/media/types.ts +46 -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 +133 -3
- package/src/memory/conversation-group-migration.ts +38 -6
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +32 -4
- package/src/memory/db-init.ts +10 -0
- package/src/memory/embedding-backend.ts +1 -1
- package/src/memory/embedding-gemini.test.ts +41 -2
- package/src/memory/embedding-gemini.ts +6 -1
- package/src/memory/graph/bootstrap.test.ts +282 -0
- package/src/memory/graph/bootstrap.ts +8 -5
- 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 +183 -53
- package/src/memory/graph/graph-search.test.ts +93 -0
- package/src/memory/graph/graph-search.ts +4 -1
- package/src/memory/graph/inspect.ts +2 -2
- 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 +237 -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/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
- package/src/memory/migrations/149-oauth-tables.ts +1 -0
- 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/223-schedule-script-column.ts +11 -0
- package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
- package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/pkb/pkb-index.test.ts +369 -0
- package/src/memory/pkb/pkb-index.ts +255 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +252 -0
- package/src/memory/pkb/pkb-reconcile.ts +148 -0
- package/src/memory/pkb/pkb-search.test.ts +499 -0
- package/src/memory/pkb/pkb-search.ts +159 -0
- package/src/memory/pkb/types.ts +53 -0
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +147 -1
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -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 +1421 -0
- package/src/messaging/providers/slack/render-transcript.ts +501 -0
- package/src/messaging/style-analyzer.ts +5 -2
- package/src/notifications/README.md +9 -5
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/decision-engine.ts +3 -9
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/preference-extractor.ts +2 -6
- package/src/notifications/signal.ts +1 -2
- package/src/oauth/AGENTS.md +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
- package/src/oauth/connect-orchestrator.ts +8 -34
- package/src/oauth/connect-types.ts +6 -10
- package/src/oauth/manual-token-connection.ts +23 -0
- package/src/oauth/oauth-store.ts +31 -14
- package/src/oauth/platform-connection.test.ts +47 -0
- package/src/oauth/platform-connection.ts +15 -5
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -106
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +1223 -0
- package/src/permissions/approval-policy.ts +309 -0
- package/src/permissions/arg-parser.test.ts +161 -0
- package/src/permissions/arg-parser.ts +141 -0
- package/src/permissions/bash-risk-classifier.test.ts +1620 -0
- package/src/permissions/bash-risk-classifier.ts +950 -0
- package/src/permissions/checker.ts +348 -711
- package/src/permissions/command-registry.test.ts +774 -0
- package/src/permissions/command-registry.ts +1005 -0
- package/src/permissions/defaults.ts +28 -79
- package/src/permissions/file-risk-classifier.test.ts +535 -0
- package/src/permissions/file-risk-classifier.ts +274 -0
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +262 -0
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/secret-prompter.ts +53 -2
- package/src/permissions/shell-identity.ts +2 -42
- 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 +25 -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 +9 -19
- package/src/platform/client.ts +19 -1
- package/src/plugins/defaults/circuit-breaker.ts +146 -0
- package/src/plugins/defaults/compaction.ts +145 -0
- package/src/plugins/defaults/empty-response.ts +126 -0
- package/src/plugins/defaults/history-repair.ts +85 -0
- package/src/plugins/defaults/index.ts +116 -0
- package/src/plugins/defaults/injectors.ts +491 -0
- package/src/plugins/defaults/llm-call.ts +82 -0
- package/src/plugins/defaults/memory-retrieval.ts +226 -0
- package/src/plugins/defaults/overflow-reduce.ts +181 -0
- package/src/plugins/defaults/persistence.ts +129 -0
- package/src/plugins/defaults/title-generate.ts +95 -0
- package/src/plugins/defaults/token-estimate.ts +104 -0
- package/src/plugins/defaults/tool-error.ts +126 -0
- package/src/plugins/defaults/tool-execute.ts +89 -0
- package/src/plugins/defaults/tool-result-truncate.ts +88 -0
- package/src/plugins/pipeline.ts +316 -0
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/registry.ts +241 -0
- package/src/plugins/types.ts +1134 -0
- package/src/plugins/user-loader.ts +177 -0
- package/src/prompts/persona-resolver.ts +3 -3
- package/src/prompts/system-prompt.ts +19 -20
- package/src/prompts/templates/BOOTSTRAP.md +27 -77
- 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 +524 -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 +80 -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/deepgram-realtime.test.ts +61 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +57 -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 +646 -0
- package/src/providers/speech-to-text/xai-realtime.ts +821 -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 +27 -18
- package/src/runtime/__tests__/agent-wake.test.ts +43 -2
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
- package/src/runtime/__tests__/client-registry.test.ts +293 -0
- 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/client-registry.ts +261 -0
- 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 +129 -9
- package/src/runtime/http-types.ts +23 -3
- 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-builder.ts +1 -22
- 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 +78 -0
- package/src/runtime/routes/approval-routes.ts +29 -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/browser-extension-pair-routes.ts +27 -8
- 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 +351 -138
- 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 +987 -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/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +720 -127
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
- package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
- package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
- package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
- package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
- package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
- package/src/runtime/routes/playground/deps.ts +56 -0
- package/src/runtime/routes/playground/force-compact.ts +73 -0
- package/src/runtime/routes/playground/guard.ts +37 -0
- package/src/runtime/routes/playground/index.ts +28 -0
- package/src/runtime/routes/playground/inject-failures.ts +159 -0
- package/src/runtime/routes/playground/reset-circuit.ts +115 -0
- package/src/runtime/routes/playground/seed-conversation.ts +139 -0
- package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
- package/src/runtime/routes/playground/state.ts +78 -0
- package/src/runtime/routes/schedule-routes.ts +89 -8
- 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 +97 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +56 -8
- 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 +35 -9
- 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 +234 -2
- package/src/tools/browser/browser-execution.ts +150 -54
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/credentials/tool-policy.ts +39 -5
- package/src/tools/credentials/vault.ts +9 -4
- package/src/tools/executor.ts +129 -73
- 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/script-proxy/session-manager.ts +37 -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 +116 -46
- package/src/tools/policy-context.ts +29 -8
- package/src/tools/registry.ts +195 -6
- package/src/tools/schedule/create.ts +23 -8
- package/src/tools/schedule/update.ts +3 -1
- package/src/tools/secret-detection-handler.ts +0 -51
- 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/system/avatar-generator.ts +6 -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 +40 -5
- 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 +9 -4
- package/src/util/pricing.ts +41 -8
- 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/006-services-config.ts +2 -4
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
- 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 +56 -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/046-seed-conversation-starters-callsite.ts +108 -0
- package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
- package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/registry.ts +28 -0
- package/src/workspace/provider-commit-message-generator.ts +19 -38
- package/tsconfig.json +1 -1
- package/hook-templates/debug-prompt-logger/hook.json +0 -7
- package/hook-templates/debug-prompt-logger/run.sh +0 -66
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- 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__/hooks-blocking.test.ts +0 -178
- package/src/__tests__/hooks-cli.test.ts +0 -182
- package/src/__tests__/hooks-config.test.ts +0 -108
- package/src/__tests__/hooks-discovery.test.ts +0 -211
- package/src/__tests__/hooks-integration.test.ts +0 -196
- package/src/__tests__/hooks-manager.test.ts +0 -226
- package/src/__tests__/hooks-runner.test.ts +0 -175
- package/src/__tests__/hooks-settings.test.ts +0 -160
- package/src/__tests__/hooks-templates.test.ts +0 -169
- package/src/__tests__/hooks-ts-runner.test.ts +0 -170
- package/src/__tests__/hooks-watch.test.ts +0 -112
- package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
- package/src/__tests__/oauth-scope-policy.test.ts +0 -180
- 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__/send-notification-tool.test.ts +0 -83
- 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/cli/commands/shotgun.ts +0 -266
- 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/conversations/SKILL.md +0 -20
- package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -66
- 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/heartbeat/SKILL.md +0 -43
- package/src/config/bundled-skills/notifications/SKILL.md +0 -40
- package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
- package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
- 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/screen-watch/SKILL.md +0 -27
- package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
- package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
- 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/daemon/context-overflow-approval.ts +0 -52
- package/src/daemon/watch-handler.ts +0 -399
- package/src/hooks/cli.ts +0 -253
- package/src/hooks/config.ts +0 -100
- package/src/hooks/discovery.ts +0 -135
- package/src/hooks/manager.ts +0 -179
- package/src/hooks/runner.ts +0 -117
- package/src/hooks/templates.ts +0 -77
- package/src/hooks/types.ts +0 -75
- package/src/oauth/scope-policy.ts +0 -89
- 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/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- package/src/shared/provider-env-vars.ts +0 -19
- package/src/signals/shotgun.ts +0 -203
- package/src/tools/watch/screen-watch.ts +0 -144
- package/src/tools/watch/watch-state.ts +0 -142
- 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,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validator for Google Cloud Storage signed URLs.
|
|
3
|
+
*
|
|
4
|
+
* Used by the migrations import path to confirm that a URL the daemon is
|
|
5
|
+
* about to `fetch()` is, in fact, a signed GCS object URL (and not an
|
|
6
|
+
* attacker-chosen host, scheme, or path).
|
|
7
|
+
*
|
|
8
|
+
* This is a pure function — no I/O, no side effects. The caller decides
|
|
9
|
+
* what to do with the validation result.
|
|
10
|
+
*
|
|
11
|
+
* What we check:
|
|
12
|
+
* - The string parses as a URL.
|
|
13
|
+
* - The scheme is `https:` (no `http:`, `file:`, `data:`, etc.).
|
|
14
|
+
* - The hostname is exactly `storage.googleapis.com`.
|
|
15
|
+
* - No explicit port is present (the default HTTPS port is required;
|
|
16
|
+
* WHATWG URL normalizes `:443` to an empty port string for HTTPS).
|
|
17
|
+
* - The URL carries a signature query param — either `X-Goog-Signature`
|
|
18
|
+
* (V4 signing) or `Signature` (V2 signing). If neither is present
|
|
19
|
+
* the URL is not a signed URL and we refuse it.
|
|
20
|
+
* - The pathname does not contain `..` segments (traversal guard).
|
|
21
|
+
*
|
|
22
|
+
* On success we return the hostname and pathname for logging/telemetry.
|
|
23
|
+
* We deliberately do NOT return the full URL or the query string,
|
|
24
|
+
* because the signature is sensitive and should not end up in logs.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export type GcsUrlValidation =
|
|
28
|
+
| { ok: true; host: string; path: string }
|
|
29
|
+
| { ok: false; reason: string };
|
|
30
|
+
|
|
31
|
+
const EXPECTED_HOST = "storage.googleapis.com";
|
|
32
|
+
const DEFAULT_ALLOWED_HOSTS: readonly string[] = [EXPECTED_HOST];
|
|
33
|
+
|
|
34
|
+
export interface ValidateGcsSignedUrlOptions {
|
|
35
|
+
/**
|
|
36
|
+
* Allowlisted hosts. Defaults to `["storage.googleapis.com"]` — the only
|
|
37
|
+
* production value. Test-only callers can widen this to point at a local
|
|
38
|
+
* HTTP fixture (the `scheme` check also relaxes to accept `http:` for
|
|
39
|
+
* non-default hosts). Production code MUST NOT pass a wider list.
|
|
40
|
+
*/
|
|
41
|
+
allowedHosts?: readonly string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function validateGcsSignedUrl(
|
|
45
|
+
raw: string,
|
|
46
|
+
options?: ValidateGcsSignedUrlOptions,
|
|
47
|
+
): GcsUrlValidation {
|
|
48
|
+
let parsed: URL;
|
|
49
|
+
try {
|
|
50
|
+
parsed = new URL(raw);
|
|
51
|
+
} catch {
|
|
52
|
+
return { ok: false, reason: "invalid_url" };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const allowedHosts = options?.allowedHosts ?? DEFAULT_ALLOWED_HOSTS;
|
|
56
|
+
const isDefaultHostList =
|
|
57
|
+
allowedHosts.length === 1 && allowedHosts[0] === EXPECTED_HOST;
|
|
58
|
+
|
|
59
|
+
// When the allowlist is the production default (GCS only), require HTTPS.
|
|
60
|
+
// When a caller passes a non-default allowlist (tests pointing at a local
|
|
61
|
+
// HTTP server), allow http too — same-process tests cannot reasonably
|
|
62
|
+
// stand up HTTPS. We still refuse non-http(s) schemes (file:, data:, etc).
|
|
63
|
+
if (isDefaultHostList) {
|
|
64
|
+
if (parsed.protocol !== "https:") {
|
|
65
|
+
return { ok: false, reason: "scheme" };
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
69
|
+
return { ok: false, reason: "scheme" };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!allowedHosts.includes(parsed.hostname)) {
|
|
74
|
+
return { ok: false, reason: "host" };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Reject any explicit port. GCS signed URLs use the default HTTPS
|
|
78
|
+
// port (443), and WHATWG URL normalizes `:443` to an empty port
|
|
79
|
+
// string for HTTPS — so a correctly-issued signed URL always has
|
|
80
|
+
// `parsed.port === ""`. A non-empty port indicates an attacker is
|
|
81
|
+
// trying to redirect the fetch to a non-default port (SSRF vector).
|
|
82
|
+
//
|
|
83
|
+
// For the non-default (test) allowlist, an explicit port is expected
|
|
84
|
+
// (local servers bind to arbitrary ephemeral ports), so the check is
|
|
85
|
+
// skipped in that case.
|
|
86
|
+
if (isDefaultHostList && parsed.port !== "") {
|
|
87
|
+
return { ok: false, reason: "port" };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const hasV4 = parsed.searchParams.has("X-Goog-Signature");
|
|
91
|
+
const hasV2 = parsed.searchParams.has("Signature");
|
|
92
|
+
if (!hasV4 && !hasV2) {
|
|
93
|
+
return { ok: false, reason: "missing_signature" };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Defense-in-depth: reject any `..` path segment. The WHATWG URL
|
|
97
|
+
// parser silently normalizes `/bucket/../foo` to `/foo`, which would
|
|
98
|
+
// hide a traversal attempt — so inspect the *raw* input before
|
|
99
|
+
// normalization in addition to the parsed pathname.
|
|
100
|
+
if (hasTraversalSegment(parsed.pathname) || hasTraversalInRawPath(raw)) {
|
|
101
|
+
return { ok: false, reason: "path_traversal" };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { ok: true, host: parsed.hostname, path: parsed.pathname };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function hasTraversalSegment(pathname: string): boolean {
|
|
108
|
+
for (const segment of pathname.split("/")) {
|
|
109
|
+
if (segment === "..") return true;
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Look for `..` path segments in the raw input before URL normalization.
|
|
116
|
+
* We slice off the scheme+authority and stop at the first `?` or `#`,
|
|
117
|
+
* then examine each segment split on both `/` and `\` (the WHATWG URL
|
|
118
|
+
* parser treats `\` as equivalent to `/` for special schemes like
|
|
119
|
+
* `https:`, so a backslash-delimited traversal would be normalized
|
|
120
|
+
* away by `new URL()`).
|
|
121
|
+
*
|
|
122
|
+
* Inside each segment we also handle percent-encoded separators
|
|
123
|
+
* (`%2F` = `/`, `%5C` = `\`): we pre-normalize those before the
|
|
124
|
+
* top-level split, and we also re-split each decoded segment on `/`
|
|
125
|
+
* and `\` to catch cases like `%2e%2e%2fother` where the encoded
|
|
126
|
+
* slash hides a traversal within a single raw segment.
|
|
127
|
+
*/
|
|
128
|
+
function hasTraversalInRawPath(raw: string): boolean {
|
|
129
|
+
const schemeEnd = raw.indexOf("://");
|
|
130
|
+
if (schemeEnd === -1) return false;
|
|
131
|
+
const afterScheme = raw.slice(schemeEnd + 3);
|
|
132
|
+
const pathStart = afterScheme.search(/[\/\\]/);
|
|
133
|
+
if (pathStart === -1) return false;
|
|
134
|
+
let path = afterScheme.slice(pathStart);
|
|
135
|
+
const queryIdx = path.indexOf("?");
|
|
136
|
+
if (queryIdx !== -1) path = path.slice(0, queryIdx);
|
|
137
|
+
const hashIdx = path.indexOf("#");
|
|
138
|
+
if (hashIdx !== -1) path = path.slice(0, hashIdx);
|
|
139
|
+
|
|
140
|
+
// Normalize percent-encoded forms of `/` and `\` so that encoded
|
|
141
|
+
// separators participate in the segment split.
|
|
142
|
+
const normalized = path.replace(/%2[fF]/g, "/").replace(/%5[cC]/g, "\\");
|
|
143
|
+
|
|
144
|
+
for (const segment of normalized.split(/[\/\\]/)) {
|
|
145
|
+
if (segment === "..") return true;
|
|
146
|
+
// Percent-decoded forms of ".." — e.g. "%2E%2E", ".%2e", "%2e.".
|
|
147
|
+
// Also re-split on `/` and `\` to catch encoded separators that
|
|
148
|
+
// weren't covered by the top-level normalization (e.g. a decoded
|
|
149
|
+
// segment `../other`).
|
|
150
|
+
let decoded: string;
|
|
151
|
+
try {
|
|
152
|
+
decoded = decodeURIComponent(segment);
|
|
153
|
+
} catch {
|
|
154
|
+
// Ignore malformed percent-encoding; URL parser would handle it.
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
for (const sub of decoded.split(/[\/\\]/)) {
|
|
158
|
+
if (sub === "..") return true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* A .vbundle is a gzip-compressed tar archive containing:
|
|
5
5
|
* - manifest.json: metadata with schema_version, checksums, and bundle info
|
|
6
6
|
* - workspace/: the entire ~/.vellum/workspace/ directory tree (DB, config,
|
|
7
|
-
* skills,
|
|
7
|
+
* skills, prompts, attachments, etc.) — excluding large/regenerable
|
|
8
8
|
* dirs (embedding-models/, data/qdrant/)
|
|
9
9
|
* - trust/trust.json: trust rules (optional, lives in protected/ outside workspace)
|
|
10
10
|
*/
|
|
@@ -429,15 +429,6 @@ export interface BuildExportVBundleOptions {
|
|
|
429
429
|
description?: string;
|
|
430
430
|
/** Absolute path to trust.json. If provided and the file exists, it is included in the archive. */
|
|
431
431
|
trustPath?: string;
|
|
432
|
-
/**
|
|
433
|
-
* Absolute path to the hooks directory. Previously hooks lived outside the
|
|
434
|
-
* workspace at ~/.vellum/hooks/ and needed explicit inclusion. Now hooks
|
|
435
|
-
* live under workspace (~/.vellum/workspace/hooks/) and are included in
|
|
436
|
-
* the workspace walk. Only pass this for backward-compat scenarios where
|
|
437
|
-
* hooks are still outside the workspace; otherwise omit to avoid double
|
|
438
|
-
* export. Included in the archive under the "hooks/" prefix.
|
|
439
|
-
*/
|
|
440
|
-
hooksDir?: string;
|
|
441
432
|
/**
|
|
442
433
|
* Absolute path to the workspace directory (~/.vellum/workspace/).
|
|
443
434
|
* When provided and exists, the entire directory tree is walked and
|
|
@@ -479,7 +470,6 @@ export function buildExportVBundle(
|
|
|
479
470
|
checkpoint,
|
|
480
471
|
trustPath,
|
|
481
472
|
workspaceDir,
|
|
482
|
-
hooksDir,
|
|
483
473
|
credentials,
|
|
484
474
|
} = options;
|
|
485
475
|
|
|
@@ -515,11 +505,6 @@ export function buildExportVBundle(
|
|
|
515
505
|
configEntry.data = new TextEncoder().encode(sanitized);
|
|
516
506
|
}
|
|
517
507
|
|
|
518
|
-
// Include hooks directory if it exists (lives at ~/.vellum/hooks/, outside workspace).
|
|
519
|
-
if (hooksDir && existsSync(hooksDir) && lstatSync(hooksDir).isDirectory()) {
|
|
520
|
-
files.push(...walkDirectory(hooksDir, "hooks"));
|
|
521
|
-
}
|
|
522
|
-
|
|
523
508
|
// Include trust rules if the file exists (lives in protected/, outside workspace).
|
|
524
509
|
if (trustPath && existsSync(trustPath)) {
|
|
525
510
|
const trustData = new Uint8Array(readFileSync(trustPath));
|
|
@@ -820,7 +805,6 @@ export async function streamExportVBundle(
|
|
|
820
805
|
checkpoint,
|
|
821
806
|
trustPath,
|
|
822
807
|
workspaceDir,
|
|
823
|
-
hooksDir,
|
|
824
808
|
credentials,
|
|
825
809
|
} = options;
|
|
826
810
|
|
|
@@ -845,11 +829,6 @@ export async function streamExportVBundle(
|
|
|
845
829
|
);
|
|
846
830
|
}
|
|
847
831
|
|
|
848
|
-
// Include hooks directory if it exists
|
|
849
|
-
if (hooksDir && existsSync(hooksDir) && lstatSync(hooksDir).isDirectory()) {
|
|
850
|
-
allFileMetadata.push(...walkDirectoryForMetadata(hooksDir, "hooks"));
|
|
851
|
-
}
|
|
852
|
-
|
|
853
832
|
// Include trust rules if the file exists
|
|
854
833
|
if (trustPath && existsSync(trustPath)) {
|
|
855
834
|
const trustStat = lstatSync(trustPath);
|
|
@@ -28,13 +28,59 @@ import { sanitizeConfigForTransfer } from "../../config/sanitize-for-transfer.js
|
|
|
28
28
|
import { isGuardianPersonaCustomized } from "../../prompts/persona-resolver.js";
|
|
29
29
|
import { getLogger } from "../../util/logger.js";
|
|
30
30
|
import type { PathResolver } from "./vbundle-import-analyzer.js";
|
|
31
|
+
import { mergeMetadataPreservingVellum } from "./vbundle-metadata-merge.js";
|
|
31
32
|
import type { ManifestType, VBundleTarEntry } from "./vbundle-validator.js";
|
|
32
33
|
import { validateVBundle } from "./vbundle-validator.js";
|
|
33
34
|
|
|
34
35
|
const log = getLogger("vbundle-importer");
|
|
35
36
|
|
|
36
37
|
/** Archive path for the legacy guardian user persona file. */
|
|
37
|
-
const LEGACY_USER_MD_ARCHIVE_PATH = "prompts/USER.md";
|
|
38
|
+
export const LEGACY_USER_MD_ARCHIVE_PATH = "prompts/USER.md";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Archive paths recognized as JSON config files that must be run through
|
|
42
|
+
* `sanitizeConfigForTransfer` before writing to disk. Exported so the
|
|
43
|
+
* streaming importer can apply the same defense-in-depth treatment.
|
|
44
|
+
*/
|
|
45
|
+
export const CONFIG_ARCHIVE_PATHS: ReadonlySet<string> = new Set([
|
|
46
|
+
"workspace/config.json",
|
|
47
|
+
"config/settings.json",
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Archive path for the credential metadata file. On import, bundle contents
|
|
52
|
+
* must be merged with the target's live `vellum:*` entries so the gateway's
|
|
53
|
+
* `readServiceCredentials` can still locate the platform API key after a
|
|
54
|
+
* local→platform teleport. Both importers consult this constant.
|
|
55
|
+
*/
|
|
56
|
+
export const CREDENTIAL_METADATA_ARCHIVE_PATH =
|
|
57
|
+
"workspace/data/credentials/metadata.json";
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Paths inside the workspace directory that must be preserved across an
|
|
61
|
+
* import when the bundle does not carry entries for them.
|
|
62
|
+
*
|
|
63
|
+
* Each entry is a path RELATIVE to the workspace root. Two kinds of live
|
|
64
|
+
* data warrant carry-over:
|
|
65
|
+
*
|
|
66
|
+
* - `embedding-models` / `deprecated`: large local caches / quarantine
|
|
67
|
+
* directories that are never shipped inside bundles but are expensive
|
|
68
|
+
* or impossible to reconstruct from an import.
|
|
69
|
+
* - `data/db` / `data/qdrant`: user-critical state (SQLite assistant DB;
|
|
70
|
+
* Qdrant vector store). If the bundle omits them — e.g. a partial
|
|
71
|
+
* bundle covering only prompts/config — the live copies must survive.
|
|
72
|
+
*
|
|
73
|
+
* Both the buffer-based `commitImport` (which selectively clears the
|
|
74
|
+
* workspace in place) and the streaming importer (which swaps the
|
|
75
|
+
* workspace with a freshly-populated temp tree) consult this list so
|
|
76
|
+
* their behavior stays in sync.
|
|
77
|
+
*/
|
|
78
|
+
export const WORKSPACE_PRESERVE_PATHS: readonly string[] = [
|
|
79
|
+
"embedding-models",
|
|
80
|
+
"deprecated",
|
|
81
|
+
"data/db",
|
|
82
|
+
"data/qdrant",
|
|
83
|
+
];
|
|
38
84
|
|
|
39
85
|
// ---------------------------------------------------------------------------
|
|
40
86
|
// Public types
|
|
@@ -171,10 +217,19 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
171
217
|
entryMap = validation.entries;
|
|
172
218
|
}
|
|
173
219
|
|
|
174
|
-
// Directories to preserve when clearing the workspace.
|
|
175
|
-
|
|
176
|
-
//
|
|
177
|
-
const
|
|
220
|
+
// Directories to preserve when clearing the workspace. Derived from the
|
|
221
|
+
// shared WORKSPACE_PRESERVE_PATHS list so the streaming importer's
|
|
222
|
+
// carry-over logic and this in-place clear stay in sync.
|
|
223
|
+
const WORKSPACE_SKIP_DIRS = new Set<string>();
|
|
224
|
+
const DATA_SKIP_DIRS = new Set<string>();
|
|
225
|
+
for (const rel of WORKSPACE_PRESERVE_PATHS) {
|
|
226
|
+
const parts = rel.split("/");
|
|
227
|
+
if (parts.length === 1) {
|
|
228
|
+
WORKSPACE_SKIP_DIRS.add(parts[0]);
|
|
229
|
+
} else if (parts.length === 2 && parts[0] === "data") {
|
|
230
|
+
DATA_SKIP_DIRS.add(parts[1]);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
178
233
|
|
|
179
234
|
// Step 1b: Clear the workspace directory before restore if the bundle
|
|
180
235
|
// contains new-format workspace/ entries. This ensures an exact-match
|
|
@@ -195,6 +250,30 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
195
250
|
(f) => f.path.startsWith("workspace/") && !!pathResolver.resolve(f.path),
|
|
196
251
|
);
|
|
197
252
|
|
|
253
|
+
// Capture the target's credential metadata BEFORE the workspace clear
|
|
254
|
+
// runs. Step 1b wipes `data/credentials/`, so reading live metadata
|
|
255
|
+
// later (during the per-file write loop) would always miss. The merge
|
|
256
|
+
// helper needs this content to preserve the target's platform-identity
|
|
257
|
+
// (`vellum:*`) entries across the overwrite.
|
|
258
|
+
let liveCredentialMetadataJson: string | null = null;
|
|
259
|
+
const credentialMetadataDiskPath = pathResolver.resolve(
|
|
260
|
+
CREDENTIAL_METADATA_ARCHIVE_PATH,
|
|
261
|
+
);
|
|
262
|
+
if (credentialMetadataDiskPath && existsSync(credentialMetadataDiskPath)) {
|
|
263
|
+
try {
|
|
264
|
+
liveCredentialMetadataJson = readFileSync(
|
|
265
|
+
credentialMetadataDiskPath,
|
|
266
|
+
"utf-8",
|
|
267
|
+
);
|
|
268
|
+
} catch (err) {
|
|
269
|
+
log.warn(
|
|
270
|
+
{ err, path: credentialMetadataDiskPath },
|
|
271
|
+
"Failed to read live credential metadata before import; vellum:* entries may not be preserved",
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
let workspaceWasCleared = false;
|
|
198
277
|
if (hasWorkspaceEntries && workspaceDir && existsSync(workspaceDir)) {
|
|
199
278
|
try {
|
|
200
279
|
// Clear workspace contents selectively, preserving skip dirs
|
|
@@ -218,6 +297,7 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
218
297
|
rmSync(entryPath, { recursive: true, force: true });
|
|
219
298
|
}
|
|
220
299
|
}
|
|
300
|
+
workspaceWasCleared = true;
|
|
221
301
|
} catch (err) {
|
|
222
302
|
return {
|
|
223
303
|
ok: false,
|
|
@@ -358,15 +438,47 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
358
438
|
|
|
359
439
|
// Sanitize config files to strip environment-specific fields (defense-in-depth)
|
|
360
440
|
let dataToWrite: Uint8Array = archiveEntry.data;
|
|
361
|
-
if (
|
|
362
|
-
fileEntry.path === "workspace/config.json" ||
|
|
363
|
-
fileEntry.path === "config/settings.json"
|
|
364
|
-
) {
|
|
441
|
+
if (CONFIG_ARCHIVE_PATHS.has(fileEntry.path)) {
|
|
365
442
|
const configJson = new TextDecoder().decode(archiveEntry.data);
|
|
366
443
|
const sanitized = sanitizeConfigForTransfer(configJson);
|
|
367
444
|
dataToWrite = new TextEncoder().encode(sanitized);
|
|
368
445
|
}
|
|
369
446
|
|
|
447
|
+
// Preserve target's `vellum:*` metadata entries across the overwrite.
|
|
448
|
+
// Django's post-hatch provisioning writes these on the target via
|
|
449
|
+
// POST /v1/secrets; a naive overwrite of the bundle's metadata.json
|
|
450
|
+
// would wipe them and break the gateway's vellum credential read.
|
|
451
|
+
// We use the snapshot captured BEFORE the workspace clear because
|
|
452
|
+
// Step 1b may have already removed the live file.
|
|
453
|
+
if (fileEntry.path === CREDENTIAL_METADATA_ARCHIVE_PATH) {
|
|
454
|
+
const bundleJson = new TextDecoder().decode(archiveEntry.data);
|
|
455
|
+
const merged = mergeMetadataPreservingVellum(
|
|
456
|
+
bundleJson,
|
|
457
|
+
liveCredentialMetadataJson,
|
|
458
|
+
);
|
|
459
|
+
dataToWrite = new TextEncoder().encode(merged);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// If we're about to replace a SQLite main database file, remove any
|
|
463
|
+
// pre-existing `.db-wal`/`.db-shm`/`.db-journal` siblings at the
|
|
464
|
+
// target. Those auxiliary files are only valid as a pair with the
|
|
465
|
+
// exact `.db` that wrote them; leaving them alongside a replacement
|
|
466
|
+
// DB causes SQLite to replay incompatible WAL frames on the first
|
|
467
|
+
// open and report "database disk image is malformed". The exporter
|
|
468
|
+
// already checkpointed the source WAL into the main DB before the
|
|
469
|
+
// bundle was built, so dropping the sibling aux files doesn't lose
|
|
470
|
+
// data from the source workspace.
|
|
471
|
+
if (diskPath.endsWith(".db")) {
|
|
472
|
+
for (const suffix of [".db-wal", ".db-shm", ".db-journal"]) {
|
|
473
|
+
const auxPath = `${diskPath.slice(0, -".db".length)}${suffix}`;
|
|
474
|
+
try {
|
|
475
|
+
rmSync(auxPath, { force: true });
|
|
476
|
+
} catch {
|
|
477
|
+
/* best effort — if the aux file doesn't exist we're fine */
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
370
482
|
// Write the file
|
|
371
483
|
try {
|
|
372
484
|
writeFileSync(diskPath, dataToWrite);
|
|
@@ -416,6 +528,39 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
416
528
|
});
|
|
417
529
|
}
|
|
418
530
|
|
|
531
|
+
// If Step 1b actually cleared the workspace AND the bundle did not
|
|
532
|
+
// carry a metadata.json entry, the target's vellum:* entries were
|
|
533
|
+
// wiped along with the `data/credentials/` directory. Restore them by
|
|
534
|
+
// writing a minimal file containing just the preserved entries so the
|
|
535
|
+
// gateway can still locate the platform API key. When Step 1b did NOT
|
|
536
|
+
// run (e.g. workspaceDir unset) the live metadata.json is still on
|
|
537
|
+
// disk untouched — we must not rewrite it here or we would drop the
|
|
538
|
+
// non-vellum entries the caller chose to keep.
|
|
539
|
+
const bundleHadMetadata = manifest.files.some(
|
|
540
|
+
(f) => f.path === CREDENTIAL_METADATA_ARCHIVE_PATH,
|
|
541
|
+
);
|
|
542
|
+
if (
|
|
543
|
+
workspaceWasCleared &&
|
|
544
|
+
!bundleHadMetadata &&
|
|
545
|
+
liveCredentialMetadataJson &&
|
|
546
|
+
credentialMetadataDiskPath
|
|
547
|
+
) {
|
|
548
|
+
const merged = mergeMetadataPreservingVellum(
|
|
549
|
+
JSON.stringify({ version: 5, credentials: [] }),
|
|
550
|
+
liveCredentialMetadataJson,
|
|
551
|
+
);
|
|
552
|
+
try {
|
|
553
|
+
mkdirSync(dirname(credentialMetadataDiskPath), { recursive: true });
|
|
554
|
+
writeFileSync(credentialMetadataDiskPath, merged);
|
|
555
|
+
} catch (err) {
|
|
556
|
+
warnings.push(
|
|
557
|
+
`Failed to restore preserved vellum:* credential metadata: ${
|
|
558
|
+
err instanceof Error ? err.message : String(err)
|
|
559
|
+
}`,
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
419
564
|
// Build final report
|
|
420
565
|
const report: ImportCommitReport = {
|
|
421
566
|
success: true,
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge helper for `data/credentials/metadata.json` on bundle import.
|
|
3
|
+
*
|
|
4
|
+
* The credential metadata file lists the credentials known to the assistant
|
|
5
|
+
* (service, field, policy, timestamps) and is used by the gateway's
|
|
6
|
+
* `readServiceCredentials` to decide whether a service is "configured".
|
|
7
|
+
* The VELLUM spec requires all four `vellum:*` fields (`platform_base_url`,
|
|
8
|
+
* `assistant_api_key`, `platform_assistant_id`, `webhook_secret`) to be
|
|
9
|
+
* present in metadata before the gateway will even look up their values in
|
|
10
|
+
* CES.
|
|
11
|
+
*
|
|
12
|
+
* On a local→platform teleport, the bundle carries the SOURCE's metadata
|
|
13
|
+
* (no `vellum:*` entries, since the source is local), and a naive overwrite
|
|
14
|
+
* would wipe out the `vellum:*` entries that Django's post-hatch
|
|
15
|
+
* provisioning just wrote on the TARGET. This module merges bundle and live
|
|
16
|
+
* metadata with one rule:
|
|
17
|
+
*
|
|
18
|
+
* - Drop `service === "vellum"` entries the bundle tries to ship
|
|
19
|
+
* (defense-in-depth — they represent the source's identity, not the
|
|
20
|
+
* target's). This mirrors the CES-side filter in migration-routes.ts.
|
|
21
|
+
* - Preserve every `service === "vellum"` entry the target already has.
|
|
22
|
+
* - Import bundle entries for every other service normally (user OAuth,
|
|
23
|
+
* channel credentials).
|
|
24
|
+
*
|
|
25
|
+
* Malformed input (missing file, unparseable JSON, unrecognized schema) is
|
|
26
|
+
* handled by the callers: they should treat "no live metadata" as no
|
|
27
|
+
* preservation needed, and leave the bundle's file untouched if its schema
|
|
28
|
+
* can't be merged cleanly.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
const VELLUM_SERVICE = "vellum";
|
|
32
|
+
|
|
33
|
+
interface MetadataRecord {
|
|
34
|
+
credentialId: string;
|
|
35
|
+
service: string;
|
|
36
|
+
field: string;
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface MetadataFile {
|
|
41
|
+
version?: number;
|
|
42
|
+
credentials?: unknown[];
|
|
43
|
+
[key: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isRecord(value: unknown): value is MetadataRecord {
|
|
47
|
+
if (typeof value !== "object" || value == null) return false;
|
|
48
|
+
const r = value as Record<string, unknown>;
|
|
49
|
+
return (
|
|
50
|
+
typeof r.credentialId === "string" &&
|
|
51
|
+
typeof r.service === "string" &&
|
|
52
|
+
typeof r.field === "string"
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function parseMetadata(json: string | null | undefined): MetadataFile | null {
|
|
57
|
+
if (!json) return null;
|
|
58
|
+
try {
|
|
59
|
+
const parsed: unknown = JSON.parse(json);
|
|
60
|
+
if (typeof parsed !== "object" || parsed == null || Array.isArray(parsed)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return parsed as MetadataFile;
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function extractVellumRecords(file: MetadataFile | null): MetadataRecord[] {
|
|
70
|
+
if (!file || !Array.isArray(file.credentials)) return [];
|
|
71
|
+
return file.credentials
|
|
72
|
+
.filter(isRecord)
|
|
73
|
+
.filter((r) => r.service === VELLUM_SERVICE);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Merge the bundle's metadata.json content with any `vellum:*` entries
|
|
78
|
+
* present in the target's live metadata.json.
|
|
79
|
+
*
|
|
80
|
+
* Returns the merged JSON string, preserving the bundle's schema version
|
|
81
|
+
* and formatting (2-space indent). If the bundle's JSON is unparseable the
|
|
82
|
+
* original input is returned unchanged — we never want to corrupt the
|
|
83
|
+
* bundle's file by emitting an empty or restructured payload.
|
|
84
|
+
*
|
|
85
|
+
* If the live JSON is unparseable or missing, the bundle's file is returned
|
|
86
|
+
* verbatim (no preservation possible — nothing to preserve).
|
|
87
|
+
*/
|
|
88
|
+
export function mergeMetadataPreservingVellum(
|
|
89
|
+
bundleJson: string,
|
|
90
|
+
liveJson: string | null,
|
|
91
|
+
): string {
|
|
92
|
+
const bundle = parseMetadata(bundleJson);
|
|
93
|
+
if (!bundle) return bundleJson;
|
|
94
|
+
|
|
95
|
+
const preservedVellum = extractVellumRecords(parseMetadata(liveJson));
|
|
96
|
+
|
|
97
|
+
const bundleCredentials = Array.isArray(bundle.credentials)
|
|
98
|
+
? bundle.credentials.filter(isRecord)
|
|
99
|
+
: [];
|
|
100
|
+
|
|
101
|
+
// Drop any `service === "vellum"` entries from the bundle (defense-in-depth).
|
|
102
|
+
const filteredBundle = bundleCredentials.filter(
|
|
103
|
+
(r) => r.service !== VELLUM_SERVICE,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// Union: bundle non-vellum entries + target vellum entries. If the
|
|
107
|
+
// preserved list happens to collide with a bundle entry on credentialId,
|
|
108
|
+
// the preserved version wins (it belongs to the target's live identity).
|
|
109
|
+
const merged = [...filteredBundle, ...preservedVellum];
|
|
110
|
+
|
|
111
|
+
const output: MetadataFile = {
|
|
112
|
+
...bundle,
|
|
113
|
+
credentials: merged,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
return JSON.stringify(output, null, 2);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** @internal For direct use by tests. */
|
|
120
|
+
export const _internal = {
|
|
121
|
+
VELLUM_SERVICE,
|
|
122
|
+
parseMetadata,
|
|
123
|
+
extractVellumRecords,
|
|
124
|
+
};
|