@vellumai/assistant 0.3.4 → 0.3.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/Dockerfile +2 -0
- package/README.md +88 -2
- package/eslint.config.mjs +31 -0
- package/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
- package/scripts/ipc/generate-swift.ts +31 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +438 -1
- package/src/__tests__/approval-conversation-turn.test.ts +214 -0
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
- package/src/__tests__/approval-message-composer.test.ts +253 -0
- package/src/__tests__/browser-manager.test.ts +1 -0
- package/src/__tests__/call-conversation-messages.test.ts +130 -0
- package/src/__tests__/call-domain.test.ts +12 -2
- package/src/__tests__/call-orchestrator.test.ts +799 -249
- package/src/__tests__/call-pointer-messages.test.ts +148 -0
- package/src/__tests__/call-recovery.test.ts +3 -0
- package/src/__tests__/call-routes-http.test.ts +32 -2
- package/src/__tests__/call-store.test.ts +3 -0
- package/src/__tests__/channel-approval-routes.test.ts +1277 -98
- package/src/__tests__/channel-approval.test.ts +37 -0
- package/src/__tests__/channel-approvals.test.ts +36 -50
- package/src/__tests__/channel-guardian.test.ts +630 -22
- package/src/__tests__/channel-readiness-service.test.ts +324 -0
- package/src/__tests__/checker.test.ts +14 -7
- package/src/__tests__/clarification-resolver.test.ts +44 -24
- package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
- package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
- package/src/__tests__/config-schema.test.ts +14 -8
- package/src/__tests__/context-window-manager.test.ts +30 -2
- package/src/__tests__/contradiction-checker.test.ts +20 -5
- package/src/__tests__/credential-security-invariants.test.ts +7 -2
- package/src/__tests__/daemon-lifecycle.test.ts +13 -12
- package/src/__tests__/db-migration-rollback.test.ts +752 -0
- package/src/__tests__/dictation-mode-detection.test.ts +63 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
- package/src/__tests__/entity-search.test.ts +615 -0
- package/src/__tests__/fuzzy-match-property.test.ts +5 -5
- package/src/__tests__/guardian-action-store.test.ts +123 -0
- package/src/__tests__/guardian-action-sweep.test.ts +277 -0
- package/src/__tests__/guardian-dispatch.test.ts +389 -0
- package/src/__tests__/guardian-question-copy.test.ts +47 -0
- package/src/__tests__/handlers-telegram-config.test.ts +4 -2
- package/src/__tests__/handlers-twilio-config.test.ts +533 -0
- package/src/__tests__/intent-routing.test.ts +2 -0
- package/src/__tests__/ipc-snapshot.test.ts +291 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
- package/src/__tests__/messaging-send-tool.test.ts +65 -0
- package/src/__tests__/model-intents.test.ts +96 -0
- package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
- package/src/__tests__/provider-error-scenarios.test.ts +621 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
- package/src/__tests__/qdrant-manager.test.ts +27 -20
- package/src/__tests__/relay-server.test.ts +779 -40
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +6 -0
- package/src/__tests__/run-orchestrator.test.ts +42 -4
- package/src/__tests__/runtime-runs-http.test.ts +17 -1
- package/src/__tests__/runtime-runs.test.ts +16 -0
- package/src/__tests__/schedule-store.test.ts +18 -4
- package/src/__tests__/scheduler-recurrence.test.ts +13 -4
- package/src/__tests__/session-abort-tool-results.test.ts +6 -0
- package/src/__tests__/session-agent-loop.test.ts +857 -0
- package/src/__tests__/session-conflict-gate.test.ts +6 -0
- package/src/__tests__/session-pre-run-repair.test.ts +6 -0
- package/src/__tests__/session-profile-injection.test.ts +6 -0
- package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/session-queue.test.ts +6 -0
- package/src/__tests__/session-runtime-assembly.test.ts +321 -13
- package/src/__tests__/session-slash-known.test.ts +6 -0
- package/src/__tests__/session-slash-queue.test.ts +6 -0
- package/src/__tests__/session-slash-unknown.test.ts +6 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/session-workspace-injection.test.ts +6 -0
- package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/skills.test.ts +2 -0
- package/src/__tests__/sms-messaging-provider.test.ts +126 -0
- package/src/__tests__/starter-task-flow.test.ts +2 -0
- package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +2 -0
- package/src/__tests__/task-management-tools.test.ts +2 -2
- package/src/__tests__/task-runner.test.ts +14 -4
- package/src/__tests__/terminal-tools.test.ts +25 -19
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
- package/src/__tests__/tool-executor.test.ts +23 -24
- package/src/__tests__/trust-store.test.ts +3 -3
- package/src/__tests__/twilio-rest.test.ts +29 -0
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
- package/src/__tests__/twilio-routes.test.ts +167 -11
- package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
- package/src/__tests__/user-reference.test.ts +2 -0
- package/src/__tests__/voice-quality.test.ts +222 -0
- package/src/__tests__/web-search.test.ts +46 -30
- package/src/__tests__/work-item-output.test.ts +110 -0
- package/src/agent/loop.ts +1 -1
- package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
- package/src/amazon/client.ts +1418 -0
- package/src/amazon/request-extractor.ts +135 -0
- package/src/amazon/session.ts +109 -0
- package/src/autonomy/autonomy-store.ts +5 -5
- package/src/browser-extension-relay/client.ts +124 -0
- package/src/browser-extension-relay/protocol.ts +63 -0
- package/src/browser-extension-relay/server.ts +177 -0
- package/src/bundler/app-bundler.ts +3 -3
- package/src/bundler/bundle-signer.ts +1 -1
- package/src/bundler/signature-verifier.ts +1 -1
- package/src/calls/call-conversation-messages.ts +33 -0
- package/src/calls/call-domain.ts +114 -10
- package/src/calls/call-orchestrator.ts +268 -59
- package/src/calls/call-pointer-messages.ts +53 -0
- package/src/calls/call-recovery.ts +3 -8
- package/src/calls/call-store.ts +69 -87
- package/src/calls/elevenlabs-config.ts +3 -2
- package/src/calls/guardian-action-sweep.ts +105 -0
- package/src/calls/guardian-dispatch.ts +203 -0
- package/src/calls/guardian-question-copy.ts +133 -0
- package/src/calls/relay-server.ts +466 -8
- package/src/calls/speaker-identification.ts +1 -1
- package/src/calls/twilio-config.ts +22 -14
- package/src/calls/twilio-provider.ts +6 -4
- package/src/calls/twilio-rest.ts +308 -7
- package/src/calls/twilio-routes.ts +65 -12
- package/src/calls/types.ts +3 -1
- package/src/channels/types.ts +25 -0
- package/src/cli/amazon.ts +815 -0
- package/src/cli/config-commands.ts +2 -2
- package/src/cli/core-commands.ts +4 -3
- package/src/cli/influencer.ts +244 -0
- package/src/cli/map.ts +89 -6
- package/src/cli.ts +1 -1
- package/src/config/agent-schema.ts +171 -0
- package/src/config/bundled-skills/amazon/SKILL.md +127 -0
- package/src/config/bundled-skills/amazon/icon.svg +13 -0
- package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
- package/src/config/bundled-skills/browser/SKILL.md +1 -0
- package/src/config/bundled-skills/browser/TOOLS.json +17 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
- package/src/config/bundled-skills/doordash/SKILL.md +51 -51
- package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
- package/src/config/bundled-skills/influencer/SKILL.md +144 -0
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
- package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +176 -0
- package/src/config/bundled-skills/media-processing/TOOLS.json +230 -0
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
- package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
- package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
- package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +259 -0
- package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +136 -0
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +59 -0
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +143 -0
- package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +65 -0
- package/src/config/bundled-skills/messaging/SKILL.md +33 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
- package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +88 -23
- package/src/config/bundled-skills/twitter/SKILL.md +19 -3
- package/src/config/bundled-skills/twitter/icon.svg +14 -0
- package/src/config/bundled-tool-registry.ts +310 -0
- package/src/config/calls-schema.ts +181 -0
- package/src/config/core-schema.ts +309 -0
- package/src/config/defaults.ts +28 -3
- package/src/config/env-registry.ts +162 -0
- package/src/config/env.ts +175 -0
- package/src/config/loader.ts +6 -6
- package/src/config/memory-schema.ts +528 -0
- package/src/config/sandbox-schema.ts +55 -0
- package/src/config/schema.ts +158 -1133
- package/src/config/skill-state.ts +1 -1
- package/src/config/skills-schema.ts +32 -0
- package/src/config/skills.ts +35 -24
- package/src/config/system-prompt.ts +131 -56
- package/src/config/templates/IDENTITY.md +2 -2
- package/src/config/templates/SOUL.md +1 -1
- package/src/config/types.ts +1 -0
- package/src/config/user-reference.ts +4 -9
- package/src/config/vellum-skills/catalog.json +6 -7
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +4 -3
- package/src/config/vellum-skills/sms-setup/SKILL.md +216 -0
- package/src/config/vellum-skills/twilio-setup/SKILL.md +40 -8
- package/src/context/window-manager.ts +27 -7
- package/src/daemon/approval-generators.ts +186 -0
- package/src/daemon/approved-devices-store.ts +140 -0
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/classifier.ts +35 -32
- package/src/daemon/config-watcher.ts +1 -1
- package/src/daemon/daemon-control.ts +217 -0
- package/src/daemon/handlers/apps.ts +2 -3
- package/src/daemon/handlers/config-channels.ts +158 -0
- package/src/daemon/handlers/config-inbox.ts +540 -0
- package/src/daemon/handlers/config-ingress.ts +231 -0
- package/src/daemon/handlers/config-integrations.ts +258 -0
- package/src/daemon/handlers/config-model.ts +143 -0
- package/src/daemon/handlers/config-parental.ts +163 -0
- package/src/daemon/handlers/config-scheduling.ts +172 -0
- package/src/daemon/handlers/config-slack.ts +92 -0
- package/src/daemon/handlers/config-telegram.ts +301 -0
- package/src/daemon/handlers/config-tools.ts +177 -0
- package/src/daemon/handlers/config-trust.ts +104 -0
- package/src/daemon/handlers/config-twilio.ts +1080 -0
- package/src/daemon/handlers/config.ts +53 -1689
- package/src/daemon/handlers/diagnostics.ts +1 -1
- package/src/daemon/handlers/dictation.ts +180 -0
- package/src/daemon/handlers/documents.ts +18 -32
- package/src/daemon/handlers/identity.ts +14 -23
- package/src/daemon/handlers/index.ts +11 -0
- package/src/daemon/handlers/misc.ts +3 -5
- package/src/daemon/handlers/pairing.ts +98 -0
- package/src/daemon/handlers/sessions.ts +56 -5
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/handlers/skills.ts +1 -1
- package/src/daemon/handlers/twitter-auth.ts +2 -0
- package/src/daemon/handlers/work-items.ts +17 -9
- package/src/daemon/handlers/workspace-files.ts +4 -3
- package/src/daemon/install-cli-launchers.ts +113 -0
- package/src/daemon/ipc-contract/apps.ts +356 -0
- package/src/daemon/ipc-contract/browser.ts +74 -0
- package/src/daemon/ipc-contract/computer-use.ts +151 -0
- package/src/daemon/ipc-contract/diagnostics.ts +56 -0
- package/src/daemon/ipc-contract/documents.ts +74 -0
- package/src/daemon/ipc-contract/inbox.ts +209 -0
- package/src/daemon/ipc-contract/integrations.ts +284 -0
- package/src/daemon/ipc-contract/memory.ts +48 -0
- package/src/daemon/ipc-contract/messages.ts +211 -0
- package/src/daemon/ipc-contract/pairing.ts +45 -0
- package/src/daemon/ipc-contract/parental-control.ts +95 -0
- package/src/daemon/ipc-contract/schedules.ts +97 -0
- package/src/daemon/ipc-contract/sessions.ts +315 -0
- package/src/daemon/ipc-contract/shared.ts +42 -0
- package/src/daemon/ipc-contract/skills.ts +120 -0
- package/src/daemon/ipc-contract/subagents.ts +58 -0
- package/src/daemon/ipc-contract/surfaces.ts +250 -0
- package/src/daemon/ipc-contract/trust.ts +60 -0
- package/src/daemon/ipc-contract/work-items.ts +225 -0
- package/src/daemon/ipc-contract/workspace.ts +113 -0
- package/src/daemon/ipc-contract-inventory.json +70 -0
- package/src/daemon/ipc-contract-inventory.ts +55 -29
- package/src/daemon/ipc-contract.ts +229 -2426
- package/src/daemon/ipc-protocol.ts +1 -1
- package/src/daemon/ipc-validate.ts +7 -0
- package/src/daemon/lifecycle.ts +97 -377
- package/src/daemon/pairing-store.ts +177 -0
- package/src/daemon/providers-setup.ts +43 -0
- package/src/daemon/ride-shotgun-handler.ts +68 -3
- package/src/daemon/server.ts +66 -46
- package/src/daemon/session-agent-loop-handlers.ts +421 -0
- package/src/daemon/session-agent-loop.ts +117 -275
- package/src/daemon/session-dynamic-profile.ts +1 -1
- package/src/daemon/session-history.ts +1 -1
- package/src/daemon/session-media-retry.ts +1 -1
- package/src/daemon/session-messaging.ts +37 -2
- package/src/daemon/session-notifiers.ts +5 -25
- package/src/daemon/session-process.ts +99 -59
- package/src/daemon/session-queue-manager.ts +96 -4
- package/src/daemon/session-runtime-assembly.ts +199 -10
- package/src/daemon/session-surfaces.ts +19 -4
- package/src/daemon/session-tool-setup.ts +30 -30
- package/src/daemon/session-workspace.ts +1 -1
- package/src/daemon/session.ts +35 -2
- package/src/daemon/shutdown-handlers.ts +122 -0
- package/src/daemon/trace-emitter.ts +1 -1
- package/src/daemon/watch-handler.ts +36 -33
- package/src/doordash/cart-queries.ts +787 -0
- package/src/doordash/client.ts +144 -127
- package/src/doordash/order-queries.ts +85 -0
- package/src/doordash/queries.ts +10 -1308
- package/src/doordash/search-queries.ts +203 -0
- package/src/doordash/session.ts +3 -2
- package/src/doordash/store-queries.ts +246 -0
- package/src/doordash/types.ts +367 -0
- package/src/email/providers/agentmail.ts +2 -1
- package/src/email/providers/index.ts +3 -2
- package/src/email/service.ts +3 -2
- package/src/errors.ts +43 -0
- package/src/home-base/prebuilt/seed.ts +1 -1
- package/src/hooks/cli.ts +6 -5
- package/src/hooks/config.ts +6 -8
- package/src/hooks/discovery.ts +6 -5
- package/src/hooks/manager.ts +4 -3
- package/src/hooks/runner.ts +2 -2
- package/src/hooks/templates.ts +5 -5
- package/src/inbound/public-ingress-urls.ts +6 -4
- package/src/index.ts +4 -2
- package/src/influencer/client.ts +1104 -0
- package/src/instrument.ts +4 -3
- package/src/logfire.ts +4 -3
- package/src/memory/admin.ts +25 -35
- package/src/memory/attachments-store.ts +4 -7
- package/src/memory/channel-delivery-store.ts +30 -1
- package/src/memory/channel-guardian-store.ts +202 -2
- package/src/memory/clarification-resolver.ts +37 -33
- package/src/memory/conflict-store.ts +67 -61
- package/src/memory/contradiction-checker.ts +141 -117
- package/src/memory/conversation-store.ts +335 -51
- package/src/memory/db-connection.ts +27 -4
- package/src/memory/db-init.ts +265 -4
- package/src/memory/db.ts +14 -1
- package/src/memory/embedding-backend.ts +27 -5
- package/src/memory/embedding-ollama.ts +2 -1
- package/src/memory/entity-extractor.ts +38 -35
- package/src/memory/guardian-action-store.ts +430 -0
- package/src/memory/inbox-escalation-projection.ts +59 -0
- package/src/memory/inbox-thread-store.ts +218 -0
- package/src/memory/ingress-invite-store.ts +338 -0
- package/src/memory/ingress-member-store.ts +350 -0
- package/src/memory/items-extractor.ts +91 -97
- package/src/memory/job-handlers/index-maintenance.ts +3 -3
- package/src/memory/job-handlers/media-processing.ts +69 -0
- package/src/memory/job-handlers/summarization.ts +32 -26
- package/src/memory/job-utils.ts +3 -10
- package/src/memory/jobs-store.ts +8 -10
- package/src/memory/jobs-worker.ts +55 -36
- package/src/memory/media-store.ts +759 -0
- package/src/memory/migrations/001-job-deferrals.ts +45 -0
- package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
- package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
- package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
- package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
- package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
- package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
- package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
- package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
- package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
- package/src/memory/migrations/index.ts +24 -0
- package/src/memory/migrations/registry.ts +79 -0
- package/src/memory/migrations/validate-migration-state.ts +69 -0
- package/src/memory/qdrant-manager.ts +49 -8
- package/src/memory/query-builder.ts +1 -1
- package/src/memory/raw-query.ts +119 -0
- package/src/memory/recall-cache.ts +4 -1
- package/src/memory/retriever.ts +165 -47
- package/src/memory/schema-migration.ts +25 -984
- package/src/memory/schema.ts +228 -7
- package/src/memory/search/entity.ts +205 -31
- package/src/memory/search/lexical.ts +81 -52
- package/src/memory/search/ranking.ts +27 -23
- package/src/memory/search/semantic.ts +157 -19
- package/src/memory/search/types.ts +24 -0
- package/src/memory/shared-app-links-store.ts +4 -5
- package/src/memory/validation.ts +19 -0
- package/src/messaging/draft-store.ts +5 -6
- package/src/messaging/provider-types.ts +2 -0
- package/src/messaging/providers/sms/adapter.ts +201 -0
- package/src/messaging/providers/sms/client.ts +93 -0
- package/src/messaging/providers/sms/types.ts +7 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
- package/src/messaging/providers/whatsapp/adapter.ts +136 -0
- package/src/messaging/providers/whatsapp/client.ts +67 -0
- package/src/messaging/style-analyzer.ts +5 -4
- package/src/messaging/thread-summarizer.ts +61 -69
- package/src/messaging/triage-engine.ts +62 -71
- package/src/migrations/config-merge.ts +53 -0
- package/src/migrations/data-layout.ts +68 -0
- package/src/migrations/data-merge.ts +33 -0
- package/src/migrations/hooks-merge.ts +90 -0
- package/src/migrations/index.ts +6 -0
- package/src/migrations/log.ts +23 -0
- package/src/migrations/skills-merge.ts +33 -0
- package/src/migrations/workspace-layout.ts +79 -0
- package/src/permissions/checker.ts +133 -11
- package/src/permissions/prompter.ts +14 -0
- package/src/permissions/shell-identity.ts +31 -1
- package/src/permissions/trust-store.ts +21 -1
- package/src/providers/anthropic/client.ts +4 -4
- package/src/providers/failover.ts +2 -2
- package/src/providers/model-intents.ts +70 -0
- package/src/providers/ollama/client.ts +2 -1
- package/src/providers/provider-send-message.ts +176 -0
- package/src/providers/registry.ts +71 -30
- package/src/providers/retry.ts +35 -1
- package/src/providers/types.ts +12 -1
- package/src/runtime/approval-conversation-turn.ts +97 -0
- package/src/runtime/approval-message-composer.ts +253 -0
- package/src/runtime/channel-approval-parser.ts +36 -2
- package/src/runtime/channel-approvals.ts +11 -24
- package/src/runtime/channel-guardian-service.ts +88 -21
- package/src/runtime/channel-readiness-service.ts +418 -0
- package/src/runtime/channel-readiness-types.ts +35 -0
- package/src/runtime/channel-retry-sweep.ts +184 -0
- package/src/runtime/guardian-context-resolver.ts +108 -0
- package/src/runtime/http-server.ts +275 -717
- package/src/runtime/http-types.ts +59 -3
- package/src/runtime/middleware/auth.ts +116 -0
- package/src/runtime/middleware/error-handler.ts +33 -0
- package/src/runtime/middleware/twilio-validation.ts +127 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/call-routes.ts +51 -7
- package/src/runtime/routes/channel-delivery-routes.ts +170 -0
- package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
- package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
- package/src/runtime/routes/channel-route-shared.ts +144 -0
- package/src/runtime/routes/channel-routes.ts +32 -1588
- package/src/runtime/routes/conversation-routes.ts +50 -7
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/identity-routes.ts +126 -0
- package/src/runtime/routes/pairing-routes.ts +143 -0
- package/src/runtime/routes/run-routes.ts +15 -1
- package/src/runtime/run-orchestrator.ts +86 -35
- package/src/schedule/schedule-store.ts +36 -32
- package/src/schedule/scheduler.ts +3 -3
- package/src/security/encrypted-store.ts +5 -7
- package/src/security/oauth2.ts +45 -15
- package/src/security/parental-control-store.ts +183 -0
- package/src/security/secret-allowlist.ts +4 -3
- package/src/security/secret-scanner.ts +5 -5
- package/src/security/secure-keys.ts +1 -1
- package/src/security/token-manager.ts +3 -2
- package/src/services/vercel-deploy.ts +6 -2
- package/src/skills/tool-manifest.ts +3 -3
- package/src/skills/vellum-catalog-remote.ts +75 -16
- package/src/slack/slack-webhook.ts +2 -1
- package/src/swarm/orchestrator.ts +92 -1
- package/src/swarm/router-planner.ts +6 -9
- package/src/swarm/worker-prompts.ts +9 -12
- package/src/tasks/task-compiler.ts +19 -28
- package/src/tasks/task-runner.ts +1 -1
- package/src/tools/assets/materialize.ts +2 -2
- package/src/tools/assets/search.ts +15 -14
- package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
- package/src/tools/browser/auto-navigate.ts +1 -0
- package/src/tools/browser/browser-execution.ts +10 -1
- package/src/tools/browser/browser-manager.ts +119 -4
- package/src/tools/browser/network-recorder.ts +5 -0
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/credentials/broker.ts +11 -2
- package/src/tools/credentials/metadata-store.ts +18 -14
- package/src/tools/credentials/post-connect-hooks.ts +61 -0
- package/src/tools/credentials/vault.ts +49 -23
- package/src/tools/execution-target.ts +11 -1
- package/src/tools/executor.ts +68 -9
- package/src/tools/host-terminal/cli-discover.ts +1 -1
- package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
- package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
- package/src/tools/network/script-proxy/server.ts +1 -1
- package/src/tools/network/script-proxy/session-manager.ts +6 -5
- package/src/tools/network/web-fetch.ts +18 -2
- package/src/tools/network/web-search.ts +8 -4
- package/src/tools/reminder/reminder-store.ts +14 -15
- package/src/tools/schedule/create.ts +1 -0
- package/src/tools/schedule/list.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
- package/src/tools/skills/skill-script-runner.ts +24 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -0
- package/src/tools/tasks/work-item-enqueue.ts +2 -2
- package/src/tools/terminal/evaluate-typescript.ts +21 -12
- package/src/tools/terminal/parser.ts +50 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/watcher/delete.ts +6 -0
- package/src/tools/weather/service.ts +1 -1
- package/src/twitter/client.ts +190 -24
- package/src/twitter/router.ts +1 -1
- package/src/twitter/session.ts +4 -3
- package/src/util/clipboard.ts +1 -1
- package/src/util/errors.ts +65 -8
- package/src/util/fs.ts +40 -0
- package/src/util/json.ts +10 -0
- package/src/util/log-redact.ts +189 -0
- package/src/util/logger.ts +19 -17
- package/src/util/object.ts +3 -0
- package/src/util/platform.ts +105 -363
- package/src/util/pricing.ts +1 -1
- package/src/util/promise-guard.ts +1 -1
- package/src/util/retry.ts +19 -0
- package/src/util/row-mapper.ts +79 -0
- package/src/util/silently.ts +21 -0
- package/src/watcher/engine.ts +5 -1
- package/src/watcher/provider-types.ts +20 -0
- package/src/watcher/providers/github.ts +156 -0
- package/src/watcher/providers/gmail.ts +1 -0
- package/src/watcher/providers/google-calendar.ts +1 -0
- package/src/watcher/providers/linear.ts +460 -0
- package/src/watcher/providers/slack.ts +1 -0
- package/src/work-items/work-item-runner.ts +1 -1
- package/src/workspace/git-service.ts +1 -1
- package/src/workspace/provider-commit-message-generator.ts +51 -22
- package/src/__tests__/call-bridge.test.ts +0 -517
- package/src/__tests__/session-process-bridge.test.ts +0 -244
- package/src/calls/call-bridge.ts +0 -168
- package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* before it is sent to the provider. They are pure (no side effects).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import type { ChannelId, TurnChannelContext } from '../channels/types.js';
|
|
8
9
|
import type { Message } from '../providers/types.js';
|
|
9
10
|
import { listAppFiles, getAppsDir } from '../memory/app-store.js';
|
|
10
11
|
import { statSync } from 'node:fs';
|
|
@@ -15,7 +16,7 @@ import { join } from 'node:path';
|
|
|
15
16
|
* interacting. Used to gate UI-specific references and permission asks.
|
|
16
17
|
*/
|
|
17
18
|
export interface ChannelCapabilities {
|
|
18
|
-
/** The raw channel identifier (e.g. "
|
|
19
|
+
/** The raw channel identifier (e.g. "macos", "telegram", "sms"). */
|
|
19
20
|
channel: string;
|
|
20
21
|
/** Whether this channel can render the dashboard UI (apps, dynamic pages). */
|
|
21
22
|
dashboardCapable: boolean;
|
|
@@ -25,16 +26,49 @@ export interface ChannelCapabilities {
|
|
|
25
26
|
supportsVoiceInput: boolean;
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
/** Guardian identity/trust context for external chat channels. */
|
|
30
|
+
export interface GuardianRuntimeContext {
|
|
31
|
+
sourceChannel: ChannelId;
|
|
32
|
+
actorRole: 'guardian' | 'non-guardian' | 'unverified_channel';
|
|
33
|
+
guardianChatId?: string;
|
|
34
|
+
guardianExternalUserId?: string;
|
|
35
|
+
requesterIdentifier?: string;
|
|
36
|
+
requesterExternalUserId?: string;
|
|
37
|
+
requesterChatId?: string;
|
|
38
|
+
denialReason?: 'no_binding' | 'no_identity';
|
|
39
|
+
}
|
|
40
|
+
|
|
28
41
|
/** Derive channel capabilities from a raw source channel identifier. */
|
|
29
42
|
export function resolveChannelCapabilities(sourceChannel?: string | null): ChannelCapabilities {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
// Normalise legacy pseudo-channel IDs to canonical ChannelId values.
|
|
44
|
+
let channel: string;
|
|
45
|
+
switch (sourceChannel) {
|
|
46
|
+
case null:
|
|
47
|
+
case undefined:
|
|
48
|
+
case 'dashboard':
|
|
49
|
+
case 'http-api':
|
|
50
|
+
case 'mac':
|
|
51
|
+
channel = 'macos';
|
|
52
|
+
break;
|
|
53
|
+
default:
|
|
54
|
+
channel = sourceChannel;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
switch (channel) {
|
|
58
|
+
case 'macos':
|
|
59
|
+
return { channel, dashboardCapable: true, supportsDynamicUi: true, supportsVoiceInput: true };
|
|
60
|
+
case 'ios':
|
|
61
|
+
return { channel, dashboardCapable: false, supportsDynamicUi: false, supportsVoiceInput: true };
|
|
62
|
+
case 'telegram':
|
|
63
|
+
case 'sms':
|
|
64
|
+
case 'voice':
|
|
65
|
+
case 'whatsapp':
|
|
66
|
+
case 'slack':
|
|
67
|
+
case 'email':
|
|
68
|
+
return { channel, dashboardCapable: false, supportsDynamicUi: false, supportsVoiceInput: false };
|
|
69
|
+
default:
|
|
70
|
+
return { channel, dashboardCapable: false, supportsDynamicUi: false, supportsVoiceInput: false };
|
|
71
|
+
}
|
|
38
72
|
}
|
|
39
73
|
|
|
40
74
|
/** Context about the active workspace surface, passed to applyRuntimeInjections. */
|
|
@@ -264,6 +298,110 @@ export function injectChannelCapabilityContext(message: Message, caps: ChannelCa
|
|
|
264
298
|
};
|
|
265
299
|
}
|
|
266
300
|
|
|
301
|
+
/** Channel command intent metadata (e.g. Telegram /start). */
|
|
302
|
+
export interface ChannelCommandContext {
|
|
303
|
+
type: string;
|
|
304
|
+
payload?: string;
|
|
305
|
+
languageCode?: string;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Prepend channel command context to the last user message so the
|
|
310
|
+
* model knows this turn was triggered by a channel command (e.g. /start).
|
|
311
|
+
*/
|
|
312
|
+
export function injectChannelCommandContext(message: Message, ctx: ChannelCommandContext): Message {
|
|
313
|
+
const lines: string[] = ['<channel_command_context>'];
|
|
314
|
+
lines.push(`command_type: ${ctx.type}`);
|
|
315
|
+
if (ctx.payload) {
|
|
316
|
+
lines.push(`payload: ${ctx.payload}`);
|
|
317
|
+
}
|
|
318
|
+
if (ctx.languageCode) {
|
|
319
|
+
lines.push(`language_code: ${ctx.languageCode}`);
|
|
320
|
+
}
|
|
321
|
+
lines.push('</channel_command_context>');
|
|
322
|
+
|
|
323
|
+
const block = lines.join('\n');
|
|
324
|
+
return {
|
|
325
|
+
...message,
|
|
326
|
+
content: [
|
|
327
|
+
{ type: 'text', text: block },
|
|
328
|
+
...message.content,
|
|
329
|
+
],
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ---------------------------------------------------------------------------
|
|
334
|
+
// Channel turn context injection
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
|
|
337
|
+
/** Parameters for building the channel turn context block. */
|
|
338
|
+
export interface ChannelTurnContextParams {
|
|
339
|
+
turnContext: TurnChannelContext;
|
|
340
|
+
conversationOriginChannel: ChannelId | null;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Build the `<channel_turn_context>` text block that informs the model
|
|
345
|
+
* which channels are active for the current turn and the conversation's
|
|
346
|
+
* origin channel.
|
|
347
|
+
*/
|
|
348
|
+
export function buildChannelTurnContextBlock(params: ChannelTurnContextParams): string {
|
|
349
|
+
const { turnContext, conversationOriginChannel } = params;
|
|
350
|
+
const lines: string[] = ['<channel_turn_context>'];
|
|
351
|
+
lines.push(`user_message_channel: ${turnContext.userMessageChannel}`);
|
|
352
|
+
lines.push(`assistant_message_channel: ${turnContext.assistantMessageChannel}`);
|
|
353
|
+
lines.push(`conversation_origin_channel: ${conversationOriginChannel ?? 'unknown'}`);
|
|
354
|
+
lines.push('</channel_turn_context>');
|
|
355
|
+
return lines.join('\n');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Prepend channel turn context to the last user message so the model
|
|
360
|
+
* knows which channels are involved in this turn.
|
|
361
|
+
*/
|
|
362
|
+
export function injectChannelTurnContext(message: Message, params: ChannelTurnContextParams): Message {
|
|
363
|
+
const block = buildChannelTurnContextBlock(params);
|
|
364
|
+
return {
|
|
365
|
+
...message,
|
|
366
|
+
content: [
|
|
367
|
+
{ type: 'text', text: block },
|
|
368
|
+
...message.content,
|
|
369
|
+
],
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Build the `<guardian_context>` text block used for model grounding.
|
|
375
|
+
*/
|
|
376
|
+
export function buildGuardianContextBlock(ctx: GuardianRuntimeContext): string {
|
|
377
|
+
const lines: string[] = ['<guardian_context>'];
|
|
378
|
+
lines.push(`source_channel: ${ctx.sourceChannel}`);
|
|
379
|
+
lines.push(`actor_role: ${ctx.actorRole}`);
|
|
380
|
+
lines.push(`guardian_external_user_id: ${ctx.guardianExternalUserId ?? 'unknown'}`);
|
|
381
|
+
lines.push(`guardian_chat_id: ${ctx.guardianChatId ?? 'unknown'}`);
|
|
382
|
+
lines.push(`requester_identifier: ${ctx.requesterIdentifier ?? 'unknown'}`);
|
|
383
|
+
lines.push(`requester_external_user_id: ${ctx.requesterExternalUserId ?? 'unknown'}`);
|
|
384
|
+
lines.push(`requester_chat_id: ${ctx.requesterChatId ?? 'unknown'}`);
|
|
385
|
+
lines.push(`denial_reason: ${ctx.denialReason ?? 'none'}`);
|
|
386
|
+
lines.push('</guardian_context>');
|
|
387
|
+
return lines.join('\n');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Prepend guardian trust/identity facts to the last user message so the
|
|
392
|
+
* model can reason about guardian status from deterministic runtime facts.
|
|
393
|
+
*/
|
|
394
|
+
export function injectGuardianContext(message: Message, ctx: GuardianRuntimeContext): Message {
|
|
395
|
+
const block = buildGuardianContextBlock(ctx);
|
|
396
|
+
return {
|
|
397
|
+
...message,
|
|
398
|
+
content: [
|
|
399
|
+
{ type: 'text', text: block },
|
|
400
|
+
...message.content,
|
|
401
|
+
],
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
267
405
|
// ---------------------------------------------------------------------------
|
|
268
406
|
// Prefix-based stripping primitive
|
|
269
407
|
// ---------------------------------------------------------------------------
|
|
@@ -286,7 +424,7 @@ export function stripUserTextBlocksByPrefix(messages: Message[], prefixes: strin
|
|
|
286
424
|
if (nextContent.length === message.content.length) return message;
|
|
287
425
|
if (nextContent.length === 0) return null;
|
|
288
426
|
return { ...message, content: nextContent };
|
|
289
|
-
}).filter((message): message is NonNullable<typeof message> => message
|
|
427
|
+
}).filter((message): message is NonNullable<typeof message> => message != null);
|
|
290
428
|
}
|
|
291
429
|
|
|
292
430
|
// ---------------------------------------------------------------------------
|
|
@@ -298,6 +436,11 @@ export function stripChannelCapabilityContext(messages: Message[]): Message[] {
|
|
|
298
436
|
return stripUserTextBlocksByPrefix(messages, ['<channel_capabilities>']);
|
|
299
437
|
}
|
|
300
438
|
|
|
439
|
+
/** Strip `<guardian_context>` blocks injected by `injectGuardianContext`. */
|
|
440
|
+
export function stripGuardianContext(messages: Message[]): Message[] {
|
|
441
|
+
return stripUserTextBlocksByPrefix(messages, ['<guardian_context>']);
|
|
442
|
+
}
|
|
443
|
+
|
|
301
444
|
/**
|
|
302
445
|
* Prepend workspace top-level directory context to a user message.
|
|
303
446
|
*/
|
|
@@ -355,9 +498,22 @@ export function stripActiveSurfaceContext(messages: Message[]): Message[] {
|
|
|
355
498
|
// Declarative strip pipeline
|
|
356
499
|
// ---------------------------------------------------------------------------
|
|
357
500
|
|
|
501
|
+
/** Strip `<channel_command_context>` blocks injected by `injectChannelCommandContext`. */
|
|
502
|
+
export function stripChannelCommandContext(messages: Message[]): Message[] {
|
|
503
|
+
return stripUserTextBlocksByPrefix(messages, ['<channel_command_context>']);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/** Strip `<channel_turn_context>` blocks injected by `injectChannelTurnContext`. */
|
|
507
|
+
export function stripChannelTurnContext(messages: Message[]): Message[] {
|
|
508
|
+
return stripUserTextBlocksByPrefix(messages, ['<channel_turn_context>']);
|
|
509
|
+
}
|
|
510
|
+
|
|
358
511
|
/** Prefixes stripped by the pipeline (order doesn't matter — single pass). */
|
|
359
512
|
const RUNTIME_INJECTION_PREFIXES = [
|
|
360
513
|
'<channel_capabilities>',
|
|
514
|
+
'<channel_command_context>',
|
|
515
|
+
'<channel_turn_context>',
|
|
516
|
+
'<guardian_context>',
|
|
361
517
|
'<workspace_top_level>',
|
|
362
518
|
TEMPORAL_INJECTED_PREFIX,
|
|
363
519
|
'<active_workspace>',
|
|
@@ -398,6 +554,9 @@ export function applyRuntimeInjections(
|
|
|
398
554
|
activeSurface?: ActiveSurfaceContext | null;
|
|
399
555
|
workspaceTopLevelContext?: string | null;
|
|
400
556
|
channelCapabilities?: ChannelCapabilities | null;
|
|
557
|
+
channelCommandContext?: ChannelCommandContext | null;
|
|
558
|
+
channelTurnContext?: ChannelTurnContextParams | null;
|
|
559
|
+
guardianContext?: GuardianRuntimeContext | null;
|
|
401
560
|
temporalContext?: string | null;
|
|
402
561
|
},
|
|
403
562
|
): Message[] {
|
|
@@ -433,6 +592,36 @@ export function applyRuntimeInjections(
|
|
|
433
592
|
}
|
|
434
593
|
}
|
|
435
594
|
|
|
595
|
+
if (options.channelCommandContext) {
|
|
596
|
+
const userTail = result[result.length - 1];
|
|
597
|
+
if (userTail && userTail.role === 'user') {
|
|
598
|
+
result = [
|
|
599
|
+
...result.slice(0, -1),
|
|
600
|
+
injectChannelCommandContext(userTail, options.channelCommandContext),
|
|
601
|
+
];
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (options.channelTurnContext) {
|
|
606
|
+
const userTail = result[result.length - 1];
|
|
607
|
+
if (userTail && userTail.role === 'user') {
|
|
608
|
+
result = [
|
|
609
|
+
...result.slice(0, -1),
|
|
610
|
+
injectChannelTurnContext(userTail, options.channelTurnContext),
|
|
611
|
+
];
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (options.guardianContext) {
|
|
616
|
+
const userTail = result[result.length - 1];
|
|
617
|
+
if (userTail && userTail.role === 'user') {
|
|
618
|
+
result = [
|
|
619
|
+
...result.slice(0, -1),
|
|
620
|
+
injectGuardianContext(userTail, options.guardianContext),
|
|
621
|
+
];
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
436
625
|
// Temporal context is injected before workspace top-level so it
|
|
437
626
|
// appears after workspace context in the final message content
|
|
438
627
|
// (both are prepended, so later injections appear first).
|
|
@@ -17,16 +17,13 @@ import {
|
|
|
17
17
|
getPrebuiltHomeBasePreview,
|
|
18
18
|
findSeededHomeBaseApp,
|
|
19
19
|
} from '../home-base/prebuilt/seed.js';
|
|
20
|
+
import { isPlainObject } from '../util/object.js';
|
|
20
21
|
|
|
21
22
|
const log = getLogger('session-surfaces');
|
|
22
23
|
|
|
23
24
|
const MAX_UNDO_DEPTH = 10;
|
|
24
25
|
const TASK_PROGRESS_TEMPLATE_FIELDS = ['title', 'status', 'steps'] as const;
|
|
25
26
|
|
|
26
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
27
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
27
|
function normalizeCardShowData(input: Record<string, unknown>, rawData: Record<string, unknown>): CardSurfaceData {
|
|
31
28
|
const normalized: Record<string, unknown> = { ...rawData };
|
|
32
29
|
|
|
@@ -128,6 +125,24 @@ export interface SurfaceSessionContext {
|
|
|
128
125
|
onEvent: (msg: ServerMessage) => void,
|
|
129
126
|
requestId?: string,
|
|
130
127
|
): Promise<string>;
|
|
128
|
+
/** Serialize operations on a given surface to prevent read-modify-write races. */
|
|
129
|
+
withSurface<T>(surfaceId: string, fn: () => T | Promise<T>): Promise<T>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Per-surface async mutex using Promise chaining.
|
|
134
|
+
* Operations on the same surfaceId are serialized; different surfaces run concurrently.
|
|
135
|
+
*/
|
|
136
|
+
export function createSurfaceMutex(): <T>(surfaceId: string, fn: () => T | Promise<T>) => Promise<T> {
|
|
137
|
+
const chains = new Map<string, Promise<void>>();
|
|
138
|
+
|
|
139
|
+
return <T>(surfaceId: string, fn: () => T | Promise<T>): Promise<T> => {
|
|
140
|
+
const prev = chains.get(surfaceId) ?? Promise.resolve();
|
|
141
|
+
const next = prev.then(fn, fn);
|
|
142
|
+
// Keep the chain alive but swallow errors so one failure doesn't block subsequent ops
|
|
143
|
+
chains.set(surfaceId, next.then(() => {}, () => {}));
|
|
144
|
+
return next;
|
|
145
|
+
};
|
|
131
146
|
}
|
|
132
147
|
|
|
133
148
|
/**
|
|
@@ -15,6 +15,7 @@ import type { SecretPrompter } from '../permissions/secret-prompter.js';
|
|
|
15
15
|
import { addRule, findHighestPriorityRule } from '../permissions/trust-store.js';
|
|
16
16
|
import { generateAllowlistOptions, generateScopeOptions, normalizeWebFetchUrl } from '../permissions/checker.js';
|
|
17
17
|
import { getLogger } from '../util/logger.js';
|
|
18
|
+
import { isPlainObject } from '../util/object.js';
|
|
18
19
|
|
|
19
20
|
const log = getLogger('session-tool-setup');
|
|
20
21
|
import { getAllToolDefinitions } from '../tools/registry.js';
|
|
@@ -41,6 +42,7 @@ import { projectSkillTools, type SkillProjectionCache } from './session-skill-to
|
|
|
41
42
|
*/
|
|
42
43
|
export interface ToolSetupContext extends SurfaceSessionContext {
|
|
43
44
|
readonly conversationId: string;
|
|
45
|
+
assistantId?: string;
|
|
44
46
|
currentRequestId?: string;
|
|
45
47
|
workingDir: string;
|
|
46
48
|
sandboxOverride?: boolean;
|
|
@@ -76,22 +78,21 @@ export function buildToolDefinitions(): ToolDefinition[] {
|
|
|
76
78
|
|
|
77
79
|
// ── DoorDash task_progress auto-update ────────────────────────────────
|
|
78
80
|
|
|
79
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
80
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
81
|
interface DoordashStep { label: string; status: string; detail?: string }
|
|
84
82
|
|
|
85
83
|
/**
|
|
86
|
-
* Map a `vellum doordash <subcommand>` to the
|
|
84
|
+
* Map a `doordash <subcommand>` (or `vellum doordash <subcommand>`) to the
|
|
85
|
+
* step label it corresponds to.
|
|
87
86
|
*/
|
|
88
87
|
function doordashCommandToStep(cmd: string): string | null {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
if (
|
|
93
|
-
if (
|
|
94
|
-
if (
|
|
88
|
+
// Match both standalone `doordash` and legacy `vellum doordash` prefixes
|
|
89
|
+
const dd = /(?:vellum )?doordash /;
|
|
90
|
+
if (new RegExp(dd.source + 'status\\b').test(cmd) || new RegExp(dd.source + 'refresh\\b').test(cmd) || new RegExp(dd.source + 'login\\b').test(cmd)) return 'Check session';
|
|
91
|
+
if (new RegExp(dd.source + 'search\\b').test(cmd) || new RegExp(dd.source + 'search-items\\b').test(cmd)) return 'Search restaurants';
|
|
92
|
+
if (new RegExp(dd.source + 'menu\\b').test(cmd) || new RegExp(dd.source + 'item\\b').test(cmd) || new RegExp(dd.source + 'store-search\\b').test(cmd)) return 'Browse menu';
|
|
93
|
+
if (new RegExp(dd.source + 'cart\\b').test(cmd)) return 'Add to cart';
|
|
94
|
+
if (new RegExp(dd.source + 'checkout\\b').test(cmd) || new RegExp(dd.source + 'payment-methods\\b').test(cmd)) return 'Add to cart';
|
|
95
|
+
if (new RegExp(dd.source + 'order\\b').test(cmd)) return 'Place order';
|
|
95
96
|
return null;
|
|
96
97
|
}
|
|
97
98
|
|
|
@@ -150,7 +151,8 @@ export function createToolExecutor(
|
|
|
150
151
|
// Pre-execution: mark the current DoorDash step as in_progress when command starts
|
|
151
152
|
if (name === 'bash' || name === 'host_bash') {
|
|
152
153
|
const preCmd = input.command as string | undefined;
|
|
153
|
-
|
|
154
|
+
const preStepLabel = preCmd ? doordashCommandToStep(preCmd) : null;
|
|
155
|
+
if (preStepLabel) {
|
|
154
156
|
const surfaceId = 'doordash-progress';
|
|
155
157
|
const stored = ctx.surfaceState.get(surfaceId);
|
|
156
158
|
if (stored && stored.surfaceType === 'card') {
|
|
@@ -158,23 +160,20 @@ export function createToolExecutor(
|
|
|
158
160
|
if (card.template === 'task_progress' && isPlainObject(card.templateData)) {
|
|
159
161
|
const steps = (card.templateData as Record<string, unknown>).steps;
|
|
160
162
|
if (Array.isArray(steps)) {
|
|
161
|
-
const
|
|
162
|
-
if (
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
ctx.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
data: updatedData,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
163
|
+
const stepIndex = (steps as DoordashStep[]).findIndex(s => s.label === preStepLabel);
|
|
164
|
+
if (stepIndex >= 0 && (steps as DoordashStep[])[stepIndex].status !== 'in_progress') {
|
|
165
|
+
const updatedSteps = (steps as DoordashStep[]).map((s, i) =>
|
|
166
|
+
i === stepIndex ? { ...s, status: 'in_progress' } : s
|
|
167
|
+
);
|
|
168
|
+
const updatedTemplateData = { ...card.templateData as Record<string, unknown>, steps: updatedSteps };
|
|
169
|
+
const updatedData = { ...card, templateData: updatedTemplateData };
|
|
170
|
+
stored.data = updatedData as import('./ipc-contract.js').CardSurfaceData;
|
|
171
|
+
ctx.sendToClient({
|
|
172
|
+
type: 'ui_surface_update',
|
|
173
|
+
sessionId: ctx.conversationId,
|
|
174
|
+
surfaceId,
|
|
175
|
+
data: updatedData,
|
|
176
|
+
});
|
|
178
177
|
}
|
|
179
178
|
}
|
|
180
179
|
}
|
|
@@ -186,6 +185,7 @@ export function createToolExecutor(
|
|
|
186
185
|
workingDir: ctx.workingDir,
|
|
187
186
|
sessionId: ctx.conversationId,
|
|
188
187
|
conversationId: ctx.conversationId,
|
|
188
|
+
assistantId: ctx.assistantId,
|
|
189
189
|
requestId: ctx.currentRequestId,
|
|
190
190
|
taskRunId: ctx.taskRunId,
|
|
191
191
|
onOutput,
|
|
@@ -310,7 +310,7 @@ export function createToolExecutor(
|
|
|
310
310
|
// Auto-emit task_progress card on first DoorDash CLI command
|
|
311
311
|
if (name === 'bash' || name === 'host_bash') {
|
|
312
312
|
const cmd = input.command as string | undefined;
|
|
313
|
-
if (cmd
|
|
313
|
+
if (cmd && doordashCommandToStep(cmd)) {
|
|
314
314
|
const surfaceId = 'doordash-progress';
|
|
315
315
|
|
|
316
316
|
if (!ctx.surfaceState.has(surfaceId)) {
|
|
@@ -12,7 +12,7 @@ export interface WorkspaceSessionContext {
|
|
|
12
12
|
|
|
13
13
|
/** Refresh workspace top-level directory context if needed. */
|
|
14
14
|
export function refreshWorkspaceTopLevelContextIfNeeded(ctx: WorkspaceSessionContext): void {
|
|
15
|
-
if (!ctx.workspaceTopLevelDirty && ctx.workspaceTopLevelContext
|
|
15
|
+
if (!ctx.workspaceTopLevelDirty && ctx.workspaceTopLevelContext != null) return;
|
|
16
16
|
const snapshot = scanTopLevelDirectories(ctx.workingDir);
|
|
17
17
|
ctx.workspaceTopLevelContext = renderWorkspaceTopLevelContext(snapshot);
|
|
18
18
|
ctx.workspaceTopLevelDirty = false;
|
package/src/daemon/session.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import type { Message } from '../providers/types.js';
|
|
19
|
+
import type { TurnChannelContext } from '../channels/types.js';
|
|
19
20
|
import type { ServerMessage, UsageStats, UserMessageAttachment, SurfaceType, SurfaceData } from './ipc-protocol.js';
|
|
20
21
|
import { AgentLoop } from '../agent/loop.js';
|
|
21
22
|
import type { Provider } from '../providers/types.js';
|
|
@@ -37,12 +38,13 @@ import { ContextWindowManager } from '../context/window-manager.js';
|
|
|
37
38
|
import { getHookManager } from '../hooks/manager.js';
|
|
38
39
|
import { ConflictGate } from './session-conflict-gate.js';
|
|
39
40
|
import { MessageQueue } from './session-queue-manager.js';
|
|
40
|
-
import type { QueueDrainReason } from './session-queue-manager.js';
|
|
41
|
-
import type { ChannelCapabilities } from './session-runtime-assembly.js';
|
|
41
|
+
import type { QueueDrainReason, QueueMetrics } from './session-queue-manager.js';
|
|
42
|
+
import type { ChannelCapabilities, GuardianRuntimeContext } from './session-runtime-assembly.js';
|
|
42
43
|
import type { AssistantAttachmentDraft } from './assistant-attachments.js';
|
|
43
44
|
import {
|
|
44
45
|
handleSurfaceAction as handleSurfaceActionImpl,
|
|
45
46
|
handleSurfaceUndo as handleSurfaceUndoImpl,
|
|
47
|
+
createSurfaceMutex,
|
|
46
48
|
} from './session-surfaces.js';
|
|
47
49
|
import {
|
|
48
50
|
undo as undoImpl,
|
|
@@ -127,19 +129,25 @@ export class Session {
|
|
|
127
129
|
/** @internal */ currentActiveSurfaceId?: string;
|
|
128
130
|
/** @internal */ currentPage?: string;
|
|
129
131
|
/** @internal */ channelCapabilities?: ChannelCapabilities;
|
|
132
|
+
/** @internal */ guardianContext?: GuardianRuntimeContext;
|
|
133
|
+
/** @internal */ assistantId?: string;
|
|
134
|
+
/** @internal */ commandIntent?: { type: string; payload?: string; languageCode?: string };
|
|
130
135
|
/** @internal */ pendingSurfaceActions = new Map<string, { surfaceType: SurfaceType }>();
|
|
131
136
|
/** @internal */ lastSurfaceAction = new Map<string, { actionId: string; data?: Record<string, unknown> }>();
|
|
132
137
|
/** @internal */ surfaceState = new Map<string, { surfaceType: SurfaceType; data: SurfaceData }>();
|
|
133
138
|
/** @internal */ surfaceUndoStacks = new Map<string, string[]>();
|
|
139
|
+
/** @internal */ withSurface = createSurfaceMutex();
|
|
134
140
|
/** @internal */ currentTurnSurfaces: Array<{ surfaceId: string; surfaceType: SurfaceType; title?: string; data: SurfaceData; actions?: Array<{ id: string; label: string; style?: string }>; display?: string }> = [];
|
|
135
141
|
/** @internal */ onEscalateToComputerUse?: (task: string, sourceSessionId: string) => boolean;
|
|
136
142
|
/** @internal */ workspaceTopLevelContext: string | null = null;
|
|
137
143
|
/** @internal */ workspaceTopLevelDirty = true;
|
|
138
144
|
public readonly traceEmitter: TraceEmitter;
|
|
139
145
|
public memoryPolicy: SessionMemoryPolicy;
|
|
146
|
+
/** @internal */ streamThinking: boolean;
|
|
140
147
|
/** @internal */ turnCount = 0;
|
|
141
148
|
public lastAssistantAttachments: AssistantAttachmentDraft[] = [];
|
|
142
149
|
public lastAttachmentWarnings: string[] = [];
|
|
150
|
+
/** @internal */ currentTurnChannelContext: TurnChannelContext | null = null;
|
|
143
151
|
|
|
144
152
|
constructor(
|
|
145
153
|
conversationId: string,
|
|
@@ -190,6 +198,7 @@ export class Session {
|
|
|
190
198
|
);
|
|
191
199
|
|
|
192
200
|
const config = getConfig();
|
|
201
|
+
this.streamThinking = config.thinking.streamThinking ?? false;
|
|
193
202
|
const resolveTools = createResolveToolsCallback(toolDefs, this);
|
|
194
203
|
|
|
195
204
|
this.agentLoop = new AgentLoop(
|
|
@@ -283,6 +292,10 @@ export class Session {
|
|
|
283
292
|
return this.queue.length;
|
|
284
293
|
}
|
|
285
294
|
|
|
295
|
+
getQueueMetrics(): QueueMetrics {
|
|
296
|
+
return this.queue.getMetrics();
|
|
297
|
+
}
|
|
298
|
+
|
|
286
299
|
hasQueuedMessages(): boolean {
|
|
287
300
|
return !this.queue.isEmpty;
|
|
288
301
|
}
|
|
@@ -327,6 +340,26 @@ export class Session {
|
|
|
327
340
|
this.channelCapabilities = caps ?? undefined;
|
|
328
341
|
}
|
|
329
342
|
|
|
343
|
+
setGuardianContext(ctx: GuardianRuntimeContext | null): void {
|
|
344
|
+
this.guardianContext = ctx ?? undefined;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
setAssistantId(assistantId: string | null): void {
|
|
348
|
+
this.assistantId = assistantId ?? undefined;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
setCommandIntent(intent: { type: string; payload?: string; languageCode?: string } | null): void {
|
|
352
|
+
this.commandIntent = intent ?? undefined;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
setTurnChannelContext(ctx: TurnChannelContext): void {
|
|
356
|
+
this.currentTurnChannelContext = ctx;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
getTurnChannelContext(): TurnChannelContext | null {
|
|
360
|
+
return this.currentTurnChannelContext;
|
|
361
|
+
}
|
|
362
|
+
|
|
330
363
|
persistUserMessage(
|
|
331
364
|
content: string,
|
|
332
365
|
attachments: UserMessageAttachment[],
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import * as Sentry from '@sentry/node';
|
|
2
|
+
import { getSqlite, resetDb } from '../memory/db.js';
|
|
3
|
+
import { browserManager } from '../tools/browser/browser-manager.js';
|
|
4
|
+
import { getEnrichmentService } from '../workspace/commit-message-enrichment-service.js';
|
|
5
|
+
import { getLogger } from '../util/logger.js';
|
|
6
|
+
import type { DaemonServer } from './server.js';
|
|
7
|
+
import type { RuntimeHttpServer } from '../runtime/http-server.js';
|
|
8
|
+
import type { HeartbeatService } from '../workspace/heartbeat-service.js';
|
|
9
|
+
import type { AgentHeartbeatService } from '../agent-heartbeat/agent-heartbeat-service.js';
|
|
10
|
+
import type { QdrantManager } from '../memory/qdrant-manager.js';
|
|
11
|
+
import type { HookManager } from '../hooks/manager.js';
|
|
12
|
+
|
|
13
|
+
const log = getLogger('lifecycle');
|
|
14
|
+
|
|
15
|
+
export interface ShutdownDeps {
|
|
16
|
+
server: DaemonServer;
|
|
17
|
+
heartbeat: HeartbeatService;
|
|
18
|
+
agentHeartbeat: AgentHeartbeatService;
|
|
19
|
+
hookManager: HookManager;
|
|
20
|
+
runtimeHttp: RuntimeHttpServer | null;
|
|
21
|
+
scheduler: { stop(): void };
|
|
22
|
+
memoryWorker: { stop(): void };
|
|
23
|
+
qdrantManager: QdrantManager;
|
|
24
|
+
cleanupPidFile: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function installShutdownHandlers(deps: ShutdownDeps): void {
|
|
28
|
+
let shuttingDown = false;
|
|
29
|
+
|
|
30
|
+
const shutdown = async () => {
|
|
31
|
+
if (shuttingDown) return;
|
|
32
|
+
shuttingDown = true;
|
|
33
|
+
log.info('Shutting down daemon...');
|
|
34
|
+
|
|
35
|
+
deps.hookManager.stopWatching();
|
|
36
|
+
|
|
37
|
+
// Force exit if graceful shutdown takes too long.
|
|
38
|
+
// Set this BEFORE awaiting heartbeat stop and triggering daemon-stop hooks
|
|
39
|
+
// so it covers all potentially-blocking async shutdown work.
|
|
40
|
+
const forceTimer = setTimeout(() => {
|
|
41
|
+
log.warn('Graceful shutdown timed out, forcing exit');
|
|
42
|
+
deps.cleanupPidFile();
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}, 10_000);
|
|
45
|
+
forceTimer.unref();
|
|
46
|
+
|
|
47
|
+
await deps.heartbeat.stop();
|
|
48
|
+
await deps.agentHeartbeat.stop();
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
await deps.hookManager.trigger('daemon-stop', { pid: process.pid });
|
|
52
|
+
} catch {
|
|
53
|
+
// Don't let hook failures block shutdown
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Commit any uncommitted workspace changes before stopping the server.
|
|
57
|
+
// This ensures no workspace state is lost during graceful shutdown.
|
|
58
|
+
try {
|
|
59
|
+
log.info({ phase: 'pre_stop' }, 'Committing pending workspace changes');
|
|
60
|
+
await deps.heartbeat.commitAllPending();
|
|
61
|
+
} catch (err) {
|
|
62
|
+
log.warn({ err, phase: 'pre_stop' }, 'Shutdown workspace commit failed');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await deps.server.stop();
|
|
66
|
+
|
|
67
|
+
// Final commit sweep: catch any writes that occurred during server.stop()
|
|
68
|
+
// (e.g. in-flight tool executions completing during drain).
|
|
69
|
+
try {
|
|
70
|
+
log.info({ phase: 'post_stop' }, 'Final workspace commit sweep');
|
|
71
|
+
await deps.heartbeat.commitAllPending();
|
|
72
|
+
} catch (err) {
|
|
73
|
+
log.warn({ err, phase: 'post_stop' }, 'Post-stop workspace commit failed');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Flush in-flight enrichment jobs so shutdown commit notes are not dropped.
|
|
77
|
+
// The enrichment service's shutdown() drains active jobs and discards pending ones.
|
|
78
|
+
try {
|
|
79
|
+
await getEnrichmentService().shutdown();
|
|
80
|
+
} catch (err) {
|
|
81
|
+
log.warn({ err }, 'Enrichment service shutdown failed (non-fatal)');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (deps.runtimeHttp) await deps.runtimeHttp.stop();
|
|
85
|
+
await browserManager.closeAllPages();
|
|
86
|
+
deps.scheduler.stop();
|
|
87
|
+
deps.memoryWorker.stop();
|
|
88
|
+
await deps.qdrantManager.stop();
|
|
89
|
+
|
|
90
|
+
// Checkpoint WAL and close SQLite so no writes are lost on exit.
|
|
91
|
+
// Checkpoint and close are in separate try blocks so that close()
|
|
92
|
+
// always runs even if checkpointing throws (e.g. SQLITE_BUSY).
|
|
93
|
+
try {
|
|
94
|
+
getSqlite().exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
95
|
+
} catch (err) {
|
|
96
|
+
log.warn({ err }, 'WAL checkpoint failed (non-fatal)');
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
resetDb();
|
|
100
|
+
} catch (err) {
|
|
101
|
+
log.warn({ err }, 'Database close failed (non-fatal)');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await Sentry.flush(2000);
|
|
105
|
+
clearTimeout(forceTimer);
|
|
106
|
+
deps.cleanupPidFile();
|
|
107
|
+
process.exit(0);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
process.on('SIGTERM', shutdown);
|
|
111
|
+
process.on('SIGINT', shutdown);
|
|
112
|
+
|
|
113
|
+
process.on('unhandledRejection', (reason) => {
|
|
114
|
+
log.error({ err: reason }, 'Unhandled promise rejection');
|
|
115
|
+
Sentry.captureException(reason);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
process.on('uncaughtException', (err) => {
|
|
119
|
+
log.error({ err }, 'Uncaught exception');
|
|
120
|
+
Sentry.captureException(err);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
@@ -69,7 +69,7 @@ function normalizeAttributes(
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
function normalizeValue(value: unknown): string | number | boolean | null {
|
|
72
|
-
if (value
|
|
72
|
+
if (value == null) return null;
|
|
73
73
|
if (typeof value === 'boolean' || typeof value === 'number') return value;
|
|
74
74
|
if (typeof value === 'string') return truncate(value, ATTRIBUTE_VALUE_MAX_LENGTH);
|
|
75
75
|
// Coerce non-primitives to string
|