@vellumai/assistant 0.5.5 → 0.5.7
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/.env.example +16 -2
- package/ARCHITECTURE.md +6 -75
- package/Dockerfile +4 -5
- package/README.md +0 -2
- package/bun.lock +0 -414
- package/docs/architecture/keychain-broker.md +45 -240
- package/docs/architecture/security.md +0 -17
- package/docs/credential-execution-service.md +2 -2
- package/node_modules/@vellumai/ces-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
- package/node_modules/@vellumai/credential-storage/package.json +1 -0
- package/node_modules/@vellumai/egress-proxy/package.json +1 -0
- package/package.json +2 -3
- package/src/__tests__/actor-token-service.test.ts +1 -2
- package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
- package/src/__tests__/browser-skill-endstate.test.ts +6 -5
- package/src/__tests__/btw-routes.test.ts +0 -39
- package/src/__tests__/call-domain.test.ts +0 -128
- package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -5
- package/src/__tests__/channel-readiness-service.test.ts +1 -60
- package/src/__tests__/checker.test.ts +4 -2
- package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-schema.test.ts +3 -3
- package/src/__tests__/context-window-manager.test.ts +78 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-skill-tools.test.ts +0 -54
- package/src/__tests__/conversation-title-service.test.ts +117 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
- package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
- package/src/__tests__/credential-security-e2e.test.ts +0 -66
- package/src/__tests__/credential-security-invariants.test.ts +4 -45
- package/src/__tests__/credentials-cli.test.ts +78 -0
- package/src/__tests__/db-migration-rollback.test.ts +2015 -1
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +98 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
- package/src/__tests__/guardian-routing-state.test.ts +0 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -7
- package/src/__tests__/http-user-message-parity.test.ts +3 -103
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
- package/src/__tests__/intent-routing.test.ts +0 -13
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
- package/src/__tests__/keychain-broker-client.test.ts +161 -22
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
- package/src/__tests__/memory-regressions.test.ts +8 -30
- package/src/__tests__/migration-export-http.test.ts +2 -2
- package/src/__tests__/migration-import-commit-http.test.ts +2 -2
- package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
- package/src/__tests__/migration-validate-http.test.ts +2 -2
- package/src/__tests__/non-member-access-request.test.ts +0 -5
- package/src/__tests__/notification-decision-fallback.test.ts +4 -0
- package/src/__tests__/notification-decision-identity.test.ts +4 -0
- package/src/__tests__/permission-types.test.ts +1 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
- package/src/__tests__/qdrant-manager.test.ts +28 -2
- package/src/__tests__/registry.test.ts +0 -6
- package/src/__tests__/require-fresh-approval.test.ts +4 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
- package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
- package/src/__tests__/secure-keys.test.ts +83 -263
- package/src/__tests__/shell-identity.test.ts +96 -6
- package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
- package/src/__tests__/skill-feature-flags.test.ts +46 -45
- package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
- package/src/__tests__/skill-load-inline-command.test.ts +8 -12
- package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
- package/src/__tests__/skill-load-tool.test.ts +0 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
- package/src/__tests__/skills.test.ts +0 -2
- package/src/__tests__/slack-inbound-verification.test.ts +0 -4
- package/src/__tests__/suggestion-routes.test.ts +1 -32
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
- package/src/__tests__/tool-executor.test.ts +4 -0
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
- package/src/__tests__/update-bulletin.test.ts +0 -2
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
- package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
- package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
- package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
- package/src/calls/audio-store.test.ts +97 -0
- package/src/calls/audio-store.ts +205 -0
- package/src/calls/call-controller.ts +85 -7
- package/src/calls/call-domain.ts +3 -0
- package/src/calls/call-store.ts +10 -3
- package/src/calls/fish-audio-client.ts +117 -0
- package/src/calls/relay-server.ts +27 -0
- package/src/calls/twilio-routes.ts +2 -1
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-ingress-preflight.ts +0 -42
- package/src/calls/voice-quality.ts +26 -5
- package/src/calls/voice-session-bridge.ts +6 -12
- package/src/cli/commands/config.ts +1 -4
- package/src/cli/commands/conversations.ts +0 -18
- package/src/cli/commands/credentials.ts +34 -4
- package/src/cli/commands/oauth/index.ts +7 -0
- package/src/cli/commands/oauth/platform.ts +179 -0
- package/src/cli/commands/platform.ts +3 -3
- package/src/config/assistant-feature-flags.ts +186 -5
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
- package/src/config/bundled-tool-registry.ts +1 -11
- package/src/config/env-registry.ts +1 -1
- package/src/config/env.ts +16 -16
- package/src/config/feature-flag-registry.json +48 -16
- package/src/config/loader.ts +98 -31
- package/src/config/schema.ts +4 -25
- package/src/config/schemas/calls.ts +13 -0
- package/src/config/schemas/fish-audio.ts +39 -0
- package/src/config/schemas/memory.ts +0 -4
- package/src/config/schemas/platform.ts +1 -1
- package/src/config/schemas/security.ts +4 -4
- package/src/config/types.ts +0 -1
- package/src/contacts/contact-store.ts +39 -0
- package/src/contacts/types.ts +2 -0
- package/src/context/window-manager.ts +53 -2
- package/src/credential-execution/approval-bridge.ts +1 -0
- package/src/credential-execution/executable-discovery.ts +28 -4
- package/src/credential-execution/feature-gates.ts +16 -0
- package/src/credential-execution/process-manager.ts +38 -0
- package/src/daemon/assistant-attachments.ts +9 -0
- package/src/daemon/config-watcher.ts +6 -4
- package/src/daemon/conversation-agent-loop.ts +0 -60
- package/src/daemon/conversation-memory.ts +0 -117
- package/src/daemon/conversation-runtime-assembly.ts +0 -2
- package/src/daemon/conversation-tool-setup.ts +0 -105
- package/src/daemon/conversation.ts +10 -1
- package/src/daemon/handlers/config-vercel.ts +92 -0
- package/src/daemon/handlers/conversations.ts +0 -11
- package/src/daemon/handlers/skills.ts +2 -15
- package/src/daemon/install-symlink.ts +195 -0
- package/src/daemon/lifecycle.ts +229 -96
- package/src/daemon/message-types/conversations.ts +3 -4
- package/src/daemon/message-types/diagnostics.ts +3 -22
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/message-types/upgrades.ts +8 -0
- package/src/daemon/server.ts +30 -92
- package/src/events/domain-events.ts +2 -1
- package/src/followups/followup-store.ts +5 -2
- package/src/inbound/platform-callback-registration.ts +3 -3
- package/src/instrument.ts +8 -5
- package/src/memory/conversation-crud.ts +0 -236
- package/src/memory/conversation-title-service.ts +76 -11
- package/src/memory/db-init.ts +15 -11
- package/src/memory/indexer.ts +15 -106
- package/src/memory/items-extractor.ts +15 -1
- package/src/memory/job-handlers/conversation-starters.ts +4 -1
- package/src/memory/job-handlers/embedding.ts +0 -79
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +30 -13
- package/src/memory/jobs-worker.ts +31 -27
- package/src/memory/migrations/001-job-deferrals.ts +19 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
- package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
- package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
- package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
- package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
- package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
- package/src/memory/migrations/116-messages-fts.ts +106 -1
- package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
- package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
- package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
- package/src/memory/migrations/141-rename-verification-table.ts +54 -0
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
- package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
- package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
- package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
- package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
- package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
- package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
- package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
- package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
- package/src/memory/migrations/189-drop-simplified-memory.ts +42 -0
- package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
- package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
- package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
- package/src/memory/migrations/index.ts +5 -3
- package/src/memory/migrations/registry.ts +90 -0
- package/src/memory/migrations/validate-migration-state.ts +137 -11
- package/src/memory/qdrant-circuit-breaker.ts +9 -0
- package/src/memory/qdrant-client.ts +4 -6
- package/src/memory/qdrant-manager.ts +64 -7
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/conversations.ts +0 -3
- package/src/memory/schema/index.ts +0 -2
- package/src/messaging/draft-store.ts +2 -2
- package/src/notifications/decision-engine.ts +4 -1
- package/src/oauth/connection-resolver.ts +6 -4
- package/src/permissions/checker.ts +0 -38
- package/src/permissions/defaults.ts +3 -3
- package/src/permissions/shell-identity.ts +76 -22
- package/src/permissions/trust-client.ts +2 -13
- package/src/permissions/trust-store.ts +8 -3
- package/src/permissions/types.ts +4 -2
- package/src/platform/client.ts +35 -7
- package/src/prompts/persona-resolver.ts +138 -0
- package/src/prompts/system-prompt.ts +36 -4
- package/src/prompts/templates/users/default.md +1 -0
- package/src/providers/registry.ts +27 -40
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
- package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
- package/src/runtime/auth/external-assistant-id.ts +13 -59
- package/src/runtime/auth/route-policy.ts +29 -1
- package/src/runtime/auth/token-service.ts +53 -15
- package/src/runtime/channel-readiness-service.ts +1 -16
- package/src/runtime/http-server.ts +29 -2
- package/src/runtime/middleware/error-handler.ts +1 -9
- package/src/runtime/routes/audio-routes.ts +40 -0
- package/src/runtime/routes/btw-routes.ts +0 -17
- package/src/runtime/routes/conversation-management-routes.ts +0 -36
- package/src/runtime/routes/conversation-query-routes.ts +106 -2
- package/src/runtime/routes/conversation-routes.ts +4 -43
- package/src/runtime/routes/diagnostics-routes.ts +1 -477
- package/src/runtime/routes/identity-routes.ts +18 -29
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
- package/src/runtime/routes/integrations/vercel.ts +89 -0
- package/src/runtime/routes/log-export-routes.ts +5 -0
- package/src/runtime/routes/memory-item-routes.test.ts +221 -3
- package/src/runtime/routes/memory-item-routes.ts +144 -4
- package/src/runtime/routes/migration-rollback-routes.ts +209 -0
- package/src/runtime/routes/migration-routes.ts +17 -1
- package/src/runtime/routes/notification-routes.ts +58 -0
- package/src/runtime/routes/schedule-routes.ts +65 -0
- package/src/runtime/routes/settings-routes.ts +41 -1
- package/src/runtime/routes/tts-routes.ts +86 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +175 -0
- package/src/runtime/routes/workspace-commit-routes.ts +62 -0
- package/src/runtime/routes/workspace-routes.test.ts +22 -1
- package/src/runtime/routes/workspace-routes.ts +1 -1
- package/src/runtime/routes/workspace-utils.ts +86 -2
- package/src/schedule/schedule-store.ts +0 -21
- package/src/security/ces-credential-client.ts +59 -22
- package/src/security/ces-rpc-credential-backend.ts +85 -0
- package/src/security/credential-backend.ts +12 -88
- package/src/security/keychain-broker-client.ts +10 -2
- package/src/security/secure-keys.ts +94 -113
- package/src/skills/catalog-install.ts +13 -7
- package/src/skills/inline-command-render.ts +5 -1
- package/src/skills/inline-command-runner.ts +30 -2
- package/src/telemetry/usage-telemetry-reporter.ts +4 -2
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/executor.ts +0 -4
- package/src/tools/memory/handlers.ts +1 -129
- package/src/tools/network/script-proxy/session-manager.ts +19 -4
- package/src/tools/network/web-fetch.ts +3 -1
- package/src/tools/permission-checker.ts +18 -0
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +9 -2
- package/src/tools/types.ts +0 -8
- package/src/util/errors.ts +0 -12
- package/src/util/platform.ts +8 -55
- package/src/util/xml.ts +8 -0
- package/src/workspace/git-service.ts +5 -2
- package/src/workspace/heartbeat-service.ts +5 -24
- package/src/workspace/migrations/001-avatar-rename.ts +15 -0
- package/src/workspace/migrations/003-seed-device-id.ts +17 -1
- package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
- package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
- package/src/workspace/migrations/006-services-config.ts +49 -0
- package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
- package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
- package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/runner.ts +106 -2
- package/src/workspace/migrations/types.ts +4 -0
- package/src/__tests__/archive-recall.test.ts +0 -560
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
- package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
- package/src/__tests__/conversation-memory-dirty-tail.test.ts +0 -150
- package/src/__tests__/conversation-switch-memory-reduction.test.ts +0 -474
- package/src/__tests__/db-memory-archive-migration.test.ts +0 -372
- package/src/__tests__/db-memory-brief-state-migration.test.ts +0 -213
- package/src/__tests__/db-memory-reducer-checkpoints.test.ts +0 -273
- package/src/__tests__/diagnostics-export.test.ts +0 -288
- package/src/__tests__/local-gateway-health.test.ts +0 -209
- package/src/__tests__/memory-brief-open-loops.test.ts +0 -530
- package/src/__tests__/memory-brief-time.test.ts +0 -285
- package/src/__tests__/memory-brief-wrapper.test.ts +0 -311
- package/src/__tests__/memory-chunk-archive.test.ts +0 -400
- package/src/__tests__/memory-chunk-dual-write.test.ts +0 -453
- package/src/__tests__/memory-episode-archive.test.ts +0 -370
- package/src/__tests__/memory-episode-dual-write.test.ts +0 -626
- package/src/__tests__/memory-observation-archive.test.ts +0 -375
- package/src/__tests__/memory-observation-dual-write.test.ts +0 -318
- package/src/__tests__/memory-reducer-job.test.ts +0 -538
- package/src/__tests__/memory-reducer-scheduling.test.ts +0 -473
- package/src/__tests__/memory-reducer-store.test.ts +0 -728
- package/src/__tests__/memory-reducer-types.test.ts +0 -707
- package/src/__tests__/memory-reducer.test.ts +0 -704
- package/src/__tests__/memory-simplified-config.test.ts +0 -281
- package/src/__tests__/secret-ingress-handler.test.ts +0 -120
- package/src/__tests__/simplified-memory-e2e.test.ts +0 -666
- package/src/__tests__/simplified-memory-runtime.test.ts +0 -616
- package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
- package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
- package/src/__tests__/swarm-orchestrator.test.ts +0 -463
- package/src/__tests__/swarm-plan-validator.test.ts +0 -384
- package/src/__tests__/swarm-recursion.test.ts +0 -197
- package/src/__tests__/swarm-router-planner.test.ts +0 -234
- package/src/__tests__/swarm-tool.test.ts +0 -185
- package/src/__tests__/swarm-worker-backend.test.ts +0 -144
- package/src/__tests__/swarm-worker-runner.test.ts +0 -288
- package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
- package/src/commands/cc-command-registry.ts +0 -248
- package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
- package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
- package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
- package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
- package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
- package/src/config/schemas/memory-simplified.ts +0 -101
- package/src/config/schemas/swarm.ts +0 -82
- package/src/logfire.ts +0 -135
- package/src/memory/archive-recall.ts +0 -516
- package/src/memory/archive-store.ts +0 -400
- package/src/memory/brief-formatting.ts +0 -33
- package/src/memory/brief-open-loops.ts +0 -266
- package/src/memory/brief-time.ts +0 -162
- package/src/memory/brief.ts +0 -75
- package/src/memory/job-handlers/backfill-simplified-memory.ts +0 -462
- package/src/memory/job-handlers/reduce-conversation-memory.ts +0 -229
- package/src/memory/migrations/185-memory-brief-state.ts +0 -52
- package/src/memory/migrations/186-memory-archive.ts +0 -109
- package/src/memory/migrations/187-memory-reducer-checkpoints.ts +0 -19
- package/src/memory/reducer-scheduler.ts +0 -242
- package/src/memory/reducer-store.ts +0 -271
- package/src/memory/reducer-types.ts +0 -106
- package/src/memory/reducer.ts +0 -467
- package/src/memory/schema/memory-archive.ts +0 -121
- package/src/memory/schema/memory-brief.ts +0 -55
- package/src/runtime/local-gateway-health.ts +0 -275
- package/src/security/secret-ingress.ts +0 -68
- package/src/swarm/backend-claude-code.ts +0 -225
- package/src/swarm/checkpoint.ts +0 -137
- package/src/swarm/graph-utils.ts +0 -53
- package/src/swarm/index.ts +0 -55
- package/src/swarm/limits.ts +0 -66
- package/src/swarm/orchestrator.ts +0 -424
- package/src/swarm/plan-validator.ts +0 -117
- package/src/swarm/router-planner.ts +0 -162
- package/src/swarm/router-prompts.ts +0 -39
- package/src/swarm/synthesizer.ts +0 -81
- package/src/swarm/types.ts +0 -72
- package/src/swarm/worker-backend.ts +0 -131
- package/src/swarm/worker-prompts.ts +0 -80
- package/src/swarm/worker-runner.ts +0 -170
- package/src/tools/claude-code/claude-code.ts +0 -610
- package/src/tools/swarm/delegate.ts +0 -205
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Unified secure key storage — single-
|
|
2
|
+
* Unified secure key storage — single-backend routing through CredentialBackend
|
|
3
3
|
* adapters.
|
|
4
4
|
*
|
|
5
|
-
* Backend selection (`
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Backend selection (`resolveBackendAsync`) is the single async decision point:
|
|
6
|
+
* 1. CES RPC (primary) — injected via `setCesClient()`: delegates credential
|
|
7
|
+
* operations to the CES process over stdio RPC. This is the default path
|
|
8
|
+
* for all local modes (desktop app, dev, CLI).
|
|
9
|
+
* 2. CES HTTP — containerized mode (IS_CONTAINERIZED + CES_CREDENTIAL_URL):
|
|
10
|
+
* delegates to the CES sidecar over HTTP. Used in Docker/managed mode.
|
|
11
|
+
* 3. Encrypted file store (fallback) — used when CES is unavailable.
|
|
9
12
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Deletes clean up both stores regardless of mode.
|
|
13
|
+
* All operations (reads, writes, lists, deletes) go to exactly one backend.
|
|
14
|
+
* There are no cross-backend fallbacks or merges.
|
|
13
15
|
*/
|
|
14
16
|
|
|
15
17
|
import type {
|
|
@@ -19,13 +21,12 @@ import type {
|
|
|
19
21
|
|
|
20
22
|
import providerEnvVarsRegistry from "../../../meta/provider-env-vars.json" with { type: "json" };
|
|
21
23
|
import { getIsContainerized } from "../config/env-registry.js";
|
|
24
|
+
import type { CesClient } from "../credential-execution/client.js";
|
|
22
25
|
import { getLogger } from "../util/logger.js";
|
|
23
26
|
import { createCesCredentialBackend } from "./ces-credential-client.js";
|
|
27
|
+
import { CesRpcCredentialBackend } from "./ces-rpc-credential-backend.js";
|
|
24
28
|
import type { CredentialBackend, DeleteResult } from "./credential-backend.js";
|
|
25
|
-
import {
|
|
26
|
-
createEncryptedStoreBackend,
|
|
27
|
-
createKeychainBackend,
|
|
28
|
-
} from "./credential-backend.js";
|
|
29
|
+
import { createEncryptedStoreBackend } from "./credential-backend.js";
|
|
29
30
|
|
|
30
31
|
export type { DeleteResult } from "./credential-backend.js";
|
|
31
32
|
|
|
@@ -36,15 +37,24 @@ export type { DeleteResult } from "./credential-backend.js";
|
|
|
36
37
|
*/
|
|
37
38
|
export type { SecureKeyBackend, SecureKeyDeleteResult };
|
|
38
39
|
|
|
40
|
+
export interface SecureKeyResult {
|
|
41
|
+
value: string | undefined;
|
|
42
|
+
unreachable: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
39
45
|
const log = getLogger("secure-keys");
|
|
40
46
|
|
|
41
|
-
let
|
|
47
|
+
let _cesClient: CesClient | undefined;
|
|
42
48
|
let _encryptedStore: CredentialBackend | undefined;
|
|
43
49
|
let _resolvedBackend: CredentialBackend | undefined;
|
|
50
|
+
let _resolvePromise: Promise<CredentialBackend> | undefined;
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
/** Inject a CES RPC client for credential routing. Resets the resolved backend. */
|
|
53
|
+
export function setCesClient(client: CesClient | undefined): void {
|
|
54
|
+
_cesClient = client;
|
|
55
|
+
// Reset resolved backend so next call picks up CES
|
|
56
|
+
_resolvedBackend = undefined;
|
|
57
|
+
_resolvePromise = undefined;
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
function getEncryptedStoreBackend(): CredentialBackend {
|
|
@@ -53,101 +63,96 @@ function getEncryptedStoreBackend(): CredentialBackend {
|
|
|
53
63
|
}
|
|
54
64
|
|
|
55
65
|
/**
|
|
56
|
-
* Resolve the primary credential backend for this process.
|
|
66
|
+
* Resolve the primary credential backend for this process (async).
|
|
57
67
|
*
|
|
58
68
|
* Priority:
|
|
59
|
-
* 1.
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* 3. Dev mode (VELLUM_DEV=1) → encrypted file store always.
|
|
69
|
+
* 1. CES RPC client → primary path for all local modes.
|
|
70
|
+
* 2. Containerized + CES_CREDENTIAL_URL → CES HTTP client (Docker/managed).
|
|
71
|
+
* 3. Encrypted file store → fallback when CES is unavailable.
|
|
63
72
|
*
|
|
64
73
|
* Once resolved, the backend does not change during the process lifetime.
|
|
65
74
|
* Call `_resetBackend()` in tests to clear the cached resolution.
|
|
66
75
|
*/
|
|
67
|
-
function
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
async function resolveBackendAsync(): Promise<CredentialBackend> {
|
|
77
|
+
if (_resolvedBackend) return _resolvedBackend;
|
|
78
|
+
if (!_resolvePromise) {
|
|
79
|
+
_resolvePromise = doResolveBackend();
|
|
80
|
+
}
|
|
81
|
+
return _resolvePromise;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function doResolveBackend(): Promise<CredentialBackend> {
|
|
85
|
+
// 1. CES RPC — primary credential backend for all local modes
|
|
86
|
+
if (_cesClient) {
|
|
87
|
+
const cesRpc = new CesRpcCredentialBackend(_cesClient);
|
|
88
|
+
if (cesRpc.isAvailable()) {
|
|
89
|
+
_resolvedBackend = cesRpc;
|
|
90
|
+
return cesRpc;
|
|
79
91
|
}
|
|
92
|
+
log.warn("CES RPC client is set but not ready — falling back to local credential store");
|
|
93
|
+
}
|
|
80
94
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
} else {
|
|
88
|
-
_resolvedBackend = getEncryptedStoreBackend();
|
|
89
|
-
}
|
|
95
|
+
// 2. CES HTTP — containerized / Docker / managed mode
|
|
96
|
+
if (getIsContainerized() && process.env.CES_CREDENTIAL_URL) {
|
|
97
|
+
const ces = createCesCredentialBackend();
|
|
98
|
+
if (ces.isAvailable()) {
|
|
99
|
+
_resolvedBackend = ces;
|
|
100
|
+
return ces;
|
|
90
101
|
}
|
|
102
|
+
log.warn(
|
|
103
|
+
"CES_CREDENTIAL_URL is set but CES backend is not available — " +
|
|
104
|
+
"falling back to local credential store",
|
|
105
|
+
);
|
|
91
106
|
}
|
|
107
|
+
|
|
108
|
+
// 3. Encrypted file store — fallback when CES is unavailable
|
|
109
|
+
_resolvedBackend = getEncryptedStoreBackend();
|
|
92
110
|
return _resolvedBackend;
|
|
93
111
|
}
|
|
94
112
|
|
|
95
113
|
/**
|
|
96
|
-
* List all account names
|
|
97
|
-
*
|
|
98
|
-
* In CES mode, only the CES backend is queried — there are no local stores.
|
|
114
|
+
* List all account names from the resolved backend (async).
|
|
99
115
|
*
|
|
100
|
-
*
|
|
101
|
-
* and the encrypted store (for legacy keys that haven't been migrated). The
|
|
102
|
-
* result is deduplicated. When the primary backend is already the encrypted
|
|
103
|
-
* store, only that store is queried.
|
|
116
|
+
* Queries exactly one backend — no cross-store merge.
|
|
104
117
|
*/
|
|
105
118
|
export async function listSecureKeysAsync(): Promise<string[]> {
|
|
106
|
-
const backend =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// CES mode — the sidecar is the single source of truth, no local merge.
|
|
110
|
-
if (backend.name === "ces-http") return primaryKeys;
|
|
111
|
-
|
|
112
|
-
// If primary backend is NOT the encrypted store, also check
|
|
113
|
-
// the encrypted store for legacy keys that haven't been migrated.
|
|
114
|
-
if (backend !== getEncryptedStoreBackend()) {
|
|
115
|
-
const encKeys = await getEncryptedStoreBackend().list();
|
|
116
|
-
const merged = new Set([...primaryKeys, ...encKeys]);
|
|
117
|
-
return Array.from(merged);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return primaryKeys;
|
|
119
|
+
const backend = await resolveBackendAsync();
|
|
120
|
+
return backend.list();
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
// ---------------------------------------------------------------------------
|
|
124
|
-
// Async CRUD — single-
|
|
124
|
+
// Async CRUD — single-backend routing
|
|
125
125
|
// ---------------------------------------------------------------------------
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
|
-
* Retrieve a secret from secure storage
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
128
|
+
* Retrieve a secret from secure storage with richer result metadata.
|
|
129
|
+
*
|
|
130
|
+
* Returns both the value (if found) and whether the backend was
|
|
131
|
+
* unreachable. Callers that need to distinguish "not found" from
|
|
132
|
+
* "backend down" should use this instead of `getSecureKeyAsync`.
|
|
133
|
+
*
|
|
134
|
+
* Reads from exactly one backend — no cross-store fallback.
|
|
133
135
|
*/
|
|
134
|
-
export async function
|
|
136
|
+
export async function getSecureKeyResultAsync(
|
|
135
137
|
account: string,
|
|
136
|
-
): Promise<
|
|
137
|
-
const backend =
|
|
138
|
+
): Promise<SecureKeyResult> {
|
|
139
|
+
const backend = await resolveBackendAsync();
|
|
138
140
|
const result = await backend.get(account);
|
|
139
|
-
if (result != null)
|
|
140
|
-
|
|
141
|
-
// CES mode — no local fallback.
|
|
142
|
-
if (backend.name === "ces-http") return undefined;
|
|
143
|
-
|
|
144
|
-
// Legacy fallback: if primary backend is NOT the encrypted store,
|
|
145
|
-
// check the encrypted store for keys that haven't been migrated.
|
|
146
|
-
if (backend !== getEncryptedStoreBackend()) {
|
|
147
|
-
return await getEncryptedStoreBackend().get(account);
|
|
141
|
+
if (result.value != null) {
|
|
142
|
+
return { value: result.value, unreachable: false };
|
|
148
143
|
}
|
|
144
|
+
return { value: undefined, unreachable: result.unreachable };
|
|
145
|
+
}
|
|
149
146
|
|
|
150
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Retrieve a secret from secure storage. Convenience wrapper over
|
|
149
|
+
* `getSecureKeyResultAsync` that returns only the value.
|
|
150
|
+
*/
|
|
151
|
+
export async function getSecureKeyAsync(
|
|
152
|
+
account: string,
|
|
153
|
+
): Promise<string | undefined> {
|
|
154
|
+
const result = await getSecureKeyResultAsync(account);
|
|
155
|
+
return result.value;
|
|
151
156
|
}
|
|
152
157
|
|
|
153
158
|
/**
|
|
@@ -158,7 +163,7 @@ export async function setSecureKeyAsync(
|
|
|
158
163
|
account: string,
|
|
159
164
|
value: string,
|
|
160
165
|
): Promise<boolean> {
|
|
161
|
-
const backend =
|
|
166
|
+
const backend = await resolveBackendAsync();
|
|
162
167
|
const ok = await backend.set(account, value);
|
|
163
168
|
if (!ok) {
|
|
164
169
|
log.warn(
|
|
@@ -172,38 +177,13 @@ export async function setSecureKeyAsync(
|
|
|
172
177
|
/**
|
|
173
178
|
* Delete a secret from secure storage.
|
|
174
179
|
*
|
|
175
|
-
*
|
|
176
|
-
* CES backend — there are no local stores to clean up.
|
|
177
|
-
*
|
|
178
|
-
* In local mode, always attempts deletion on both the keychain backend (if
|
|
179
|
-
* available) and the encrypted store backend, regardless of routing mode.
|
|
180
|
-
* This cleans up legacy data from both stores.
|
|
180
|
+
* Deletes from exactly one backend — no cross-store cleanup.
|
|
181
181
|
*/
|
|
182
182
|
export async function deleteSecureKeyAsync(
|
|
183
183
|
account: string,
|
|
184
184
|
): Promise<DeleteResult> {
|
|
185
|
-
const backend =
|
|
186
|
-
|
|
187
|
-
// In CES mode, the sidecar is the only store — no local cleanup needed.
|
|
188
|
-
if (backend.name === "ces-http") {
|
|
189
|
-
return backend.delete(account);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const keychain = getKeychainBackend();
|
|
193
|
-
const enc = getEncryptedStoreBackend();
|
|
194
|
-
|
|
195
|
-
let keychainResult: DeleteResult = "not-found";
|
|
196
|
-
if (keychain.isAvailable()) {
|
|
197
|
-
keychainResult = await keychain.delete(account);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const encResult = await enc.delete(account);
|
|
201
|
-
|
|
202
|
-
// Return "error" if either errored
|
|
203
|
-
if (keychainResult === "error" || encResult === "error") return "error";
|
|
204
|
-
// Return "deleted" if either deleted
|
|
205
|
-
if (keychainResult === "deleted" || encResult === "deleted") return "deleted";
|
|
206
|
-
return "not-found";
|
|
185
|
+
const backend = await resolveBackendAsync();
|
|
186
|
+
return backend.delete(account);
|
|
207
187
|
}
|
|
208
188
|
|
|
209
189
|
// ---------------------------------------------------------------------------
|
|
@@ -263,7 +243,8 @@ export async function getMaskedProviderKey(
|
|
|
263
243
|
|
|
264
244
|
/** @internal Test-only: reset the cached backends so they're re-created. */
|
|
265
245
|
export function _resetBackend(): void {
|
|
266
|
-
|
|
246
|
+
_cesClient = undefined;
|
|
267
247
|
_encryptedStore = undefined;
|
|
268
248
|
_resolvedBackend = undefined;
|
|
249
|
+
_resolvePromise = undefined;
|
|
269
250
|
}
|
|
@@ -88,11 +88,7 @@ function getConfigPlatformUrl(): string | undefined {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
function getPlatformUrl(): string {
|
|
91
|
-
return (
|
|
92
|
-
process.env.VELLUM_PLATFORM_URL ??
|
|
93
|
-
getConfigPlatformUrl() ??
|
|
94
|
-
"https://platform.vellum.ai"
|
|
95
|
-
);
|
|
91
|
+
return process.env.VELLUM_PLATFORM_URL ?? getConfigPlatformUrl() ?? "";
|
|
96
92
|
}
|
|
97
93
|
|
|
98
94
|
function buildHeaders(): Record<string, string> {
|
|
@@ -107,7 +103,11 @@ function buildHeaders(): Record<string, string> {
|
|
|
107
103
|
// ─── Catalog operations ──────────────────────────────────────────────────────
|
|
108
104
|
|
|
109
105
|
export async function fetchCatalog(): Promise<CatalogSkill[]> {
|
|
110
|
-
const
|
|
106
|
+
const platformUrl = getPlatformUrl();
|
|
107
|
+
if (!platformUrl) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
const url = `${platformUrl}/v1/skills/`;
|
|
111
111
|
const response = await fetch(url, {
|
|
112
112
|
headers: buildHeaders(),
|
|
113
113
|
signal: AbortSignal.timeout(10000),
|
|
@@ -214,7 +214,13 @@ export async function fetchAndExtractSkill(
|
|
|
214
214
|
skillId: string,
|
|
215
215
|
destDir: string,
|
|
216
216
|
): Promise<void> {
|
|
217
|
-
const
|
|
217
|
+
const platformUrl = getPlatformUrl();
|
|
218
|
+
if (!platformUrl) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`Cannot fetch skill "${skillId}": VELLUM_PLATFORM_URL is not configured.`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
const url = `${platformUrl}/v1/skills/${encodeURIComponent(skillId)}/`;
|
|
218
224
|
const response = await fetch(url, {
|
|
219
225
|
headers: buildHeaders(),
|
|
220
226
|
signal: AbortSignal.timeout(15000),
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { getLogger } from "../util/logger.js";
|
|
17
|
+
import { escapeXmlContent } from "../util/xml.js";
|
|
17
18
|
import type { InlineCommandExpansion } from "./inline-command-expansions.js";
|
|
18
19
|
import type { InlineCommandResult } from "./inline-command-runner.js";
|
|
19
20
|
import { runInlineCommand } from "./inline-command-runner.js";
|
|
@@ -91,7 +92,10 @@ export async function renderInlineCommands(
|
|
|
91
92
|
|
|
92
93
|
let replacement: string;
|
|
93
94
|
if (commandResult.ok) {
|
|
94
|
-
replacement = wrapInXml(
|
|
95
|
+
replacement = wrapInXml(
|
|
96
|
+
expansion.placeholderId,
|
|
97
|
+
escapeXmlContent(commandResult.output),
|
|
98
|
+
);
|
|
95
99
|
expandedCount++;
|
|
96
100
|
} else {
|
|
97
101
|
const stub = failureReasonToStub(commandResult);
|
|
@@ -37,6 +37,15 @@ const DEFAULT_TIMEOUT_MS = 10_000;
|
|
|
37
37
|
/** Maximum output characters before truncation. */
|
|
38
38
|
const MAX_OUTPUT_CHARS = 20_000;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Maximum bytes to buffer from stdout during streaming. Once this limit is
|
|
42
|
+
* reached we stop accepting data so a long-running command (e.g. `yes`) cannot
|
|
43
|
+
* grow memory unbounded before the timeout fires. Set generously above
|
|
44
|
+
* MAX_OUTPUT_CHARS to account for multi-byte UTF-8 and ANSI sequences that will
|
|
45
|
+
* be stripped before the character-level clamp.
|
|
46
|
+
*/
|
|
47
|
+
const MAX_STDOUT_BUFFER_BYTES = MAX_OUTPUT_CHARS * 4;
|
|
48
|
+
|
|
40
49
|
/**
|
|
41
50
|
* ANSI escape sequence pattern (covers SGR, cursor movement, erase, etc.).
|
|
42
51
|
* Matches: ESC[ ... final_byte and ESC] ... ST (OSC sequences).
|
|
@@ -115,7 +124,9 @@ export async function runInlineCommand(
|
|
|
115
124
|
|
|
116
125
|
return new Promise<InlineCommandResult>((resolve) => {
|
|
117
126
|
let timedOut = false;
|
|
127
|
+
let stdoutCapped = false;
|
|
118
128
|
const stdoutChunks: Buffer[] = [];
|
|
129
|
+
let stdoutBytes = 0;
|
|
119
130
|
|
|
120
131
|
let child: ReturnType<typeof spawn>;
|
|
121
132
|
try {
|
|
@@ -140,7 +151,19 @@ export async function runInlineCommand(
|
|
|
140
151
|
child.kill("SIGKILL");
|
|
141
152
|
}, timeoutMs);
|
|
142
153
|
|
|
143
|
-
child.stdout!.on("data", (data: Buffer) =>
|
|
154
|
+
child.stdout!.on("data", (data: Buffer) => {
|
|
155
|
+
if (stdoutBytes >= MAX_STDOUT_BUFFER_BYTES) return;
|
|
156
|
+
stdoutChunks.push(data);
|
|
157
|
+
stdoutBytes += data.length;
|
|
158
|
+
if (stdoutBytes >= MAX_STDOUT_BUFFER_BYTES) {
|
|
159
|
+
// Stop reading to release backpressure on the child process.
|
|
160
|
+
// This destroys the read end of the pipe, which may cause the
|
|
161
|
+
// child to receive SIGPIPE and exit with code=null. The
|
|
162
|
+
// stdoutCapped flag lets the close handler treat this as success.
|
|
163
|
+
stdoutCapped = true;
|
|
164
|
+
child.stdout!.destroy();
|
|
165
|
+
}
|
|
166
|
+
});
|
|
144
167
|
|
|
145
168
|
child.on("close", (code) => {
|
|
146
169
|
clearTimeout(timer);
|
|
@@ -157,7 +180,12 @@ export async function runInlineCommand(
|
|
|
157
180
|
}
|
|
158
181
|
|
|
159
182
|
// ── Non-zero exit ────────────────────────────────────────────────
|
|
160
|
-
|
|
183
|
+
// When stdout was capped we destroyed the read end of the pipe,
|
|
184
|
+
// which typically causes SIGPIPE — the process is killed by the
|
|
185
|
+
// signal so the exit code is null. Only suppress the error in that
|
|
186
|
+
// specific case; a command that outputs a lot but exits with a
|
|
187
|
+
// genuine non-zero code (e.g. exit 1) should still be an error.
|
|
188
|
+
if (code !== 0 && !(stdoutCapped && code == null)) {
|
|
161
189
|
log.debug(
|
|
162
190
|
{ command, exitCode: code },
|
|
163
191
|
"Inline command exited with non-zero code",
|
|
@@ -140,7 +140,7 @@ export class UsageTelemetryReporter {
|
|
|
140
140
|
|
|
141
141
|
// Resolve auth context — skip flush when neither auth mode is viable
|
|
142
142
|
const client = await VellumPlatformClient.create();
|
|
143
|
-
if (!client && !getTelemetryAppToken()) {
|
|
143
|
+
if (!client && (!getTelemetryAppToken() || !getTelemetryPlatformUrl())) {
|
|
144
144
|
return;
|
|
145
145
|
}
|
|
146
146
|
|
|
@@ -203,7 +203,9 @@ export class UsageTelemetryReporter {
|
|
|
203
203
|
if (client) {
|
|
204
204
|
resp = await client.fetch(TELEMETRY_PATH, fetchInit);
|
|
205
205
|
} else {
|
|
206
|
-
const
|
|
206
|
+
const platformUrl = getTelemetryPlatformUrl();
|
|
207
|
+
if (!platformUrl) return;
|
|
208
|
+
const url = `${platformUrl}${TELEMETRY_PATH}`;
|
|
207
209
|
resp = await fetch(url, {
|
|
208
210
|
...fetchInit,
|
|
209
211
|
headers: {
|
package/src/tools/executor.ts
CHANGED
|
@@ -183,10 +183,6 @@ export class ToolExecutor {
|
|
|
183
183
|
);
|
|
184
184
|
// Buffer so the shell's own timeout fires first and handles cleanup
|
|
185
185
|
toolTimeoutMs = (shellTimeoutSec + 5) * 1000;
|
|
186
|
-
} else if (name === "claude_code") {
|
|
187
|
-
// Claude Code spawns a subprocess that manages its own turn limits
|
|
188
|
-
// (maxTurns). Give it a generous timeout so it isn't killed mid-task.
|
|
189
|
-
toolTimeoutMs = 10 * 60 * 1000; // 10 minutes
|
|
190
186
|
} else {
|
|
191
187
|
const rawTimeoutSec = getConfig().timeouts.toolExecutionTimeoutSec;
|
|
192
188
|
toolTimeoutMs = safeTimeoutMs(rawTimeoutSec);
|
|
@@ -2,8 +2,6 @@ import { and, eq, ne } from "drizzle-orm";
|
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
4
|
import type { AssistantConfig } from "../../config/types.js";
|
|
5
|
-
import { buildArchiveRecall } from "../../memory/archive-recall.js";
|
|
6
|
-
import { insertObservation } from "../../memory/archive-store.js";
|
|
7
5
|
import { getDb } from "../../memory/db.js";
|
|
8
6
|
import { computeMemoryFingerprint } from "../../memory/fingerprint.js";
|
|
9
7
|
import { enqueueMemoryJob } from "../../memory/jobs-store.js";
|
|
@@ -20,7 +18,7 @@ const log = getLogger("memory-tools");
|
|
|
20
18
|
|
|
21
19
|
export async function handleMemorySave(
|
|
22
20
|
args: Record<string, unknown>,
|
|
23
|
-
|
|
21
|
+
_config: AssistantConfig,
|
|
24
22
|
conversationId: string,
|
|
25
23
|
messageId: string | undefined,
|
|
26
24
|
scopeId: string = "default",
|
|
@@ -65,19 +63,6 @@ export async function handleMemorySave(
|
|
|
65
63
|
? truncate(args.subject.trim(), 80, "")
|
|
66
64
|
: inferSubjectFromStatement(statement.trim());
|
|
67
65
|
|
|
68
|
-
// When simplified memory is enabled, save directly to the simplified
|
|
69
|
-
// observation/chunk tables instead of the legacy memory_items table.
|
|
70
|
-
if (config.memory.simplified.enabled) {
|
|
71
|
-
return handleSimplifiedMemorySave(
|
|
72
|
-
kind,
|
|
73
|
-
subject,
|
|
74
|
-
statement.trim(),
|
|
75
|
-
conversationId,
|
|
76
|
-
messageId,
|
|
77
|
-
scopeId,
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
66
|
try {
|
|
82
67
|
const db = getDb();
|
|
83
68
|
const id = uuid();
|
|
@@ -290,12 +275,6 @@ export async function handleMemoryRecall(
|
|
|
290
275
|
? args.scope.trim()
|
|
291
276
|
: "default";
|
|
292
277
|
|
|
293
|
-
// When simplified memory is enabled, use the archive recall path
|
|
294
|
-
// instead of the legacy hybrid retriever.
|
|
295
|
-
if (config.memory.simplified.enabled) {
|
|
296
|
-
return handleSimplifiedMemoryRecall(query.trim(), scopeId ?? "default");
|
|
297
|
-
}
|
|
298
|
-
|
|
299
278
|
// Scope policy: "conversation" means strict (only that scope),
|
|
300
279
|
// anything else allows fallback to the default scope.
|
|
301
280
|
const scopePolicyOverride: ScopePolicyOverride | undefined = scopeId
|
|
@@ -432,113 +411,6 @@ export async function handleMemoryDelete(
|
|
|
432
411
|
}
|
|
433
412
|
}
|
|
434
413
|
|
|
435
|
-
// ── Simplified memory helpers ────────────────────────────────────────
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Save a memory item as an observation + chunk in the simplified system.
|
|
439
|
-
* This is used when simplified memory is enabled instead of writing to
|
|
440
|
-
* the legacy memory_items table.
|
|
441
|
-
*/
|
|
442
|
-
function handleSimplifiedMemorySave(
|
|
443
|
-
kind: string,
|
|
444
|
-
subject: string,
|
|
445
|
-
statement: string,
|
|
446
|
-
conversationId: string,
|
|
447
|
-
messageId: string | undefined,
|
|
448
|
-
scopeId: string,
|
|
449
|
-
): ToolExecutionResult {
|
|
450
|
-
try {
|
|
451
|
-
const trimmedStatement = truncate(statement, 500, "");
|
|
452
|
-
const content = `[${kind}] ${subject}: ${trimmedStatement}`;
|
|
453
|
-
|
|
454
|
-
const result = insertObservation({
|
|
455
|
-
conversationId,
|
|
456
|
-
messageId: messageId ?? null,
|
|
457
|
-
role: "user",
|
|
458
|
-
content,
|
|
459
|
-
scopeId,
|
|
460
|
-
modality: "text",
|
|
461
|
-
source: "tool:memory_save",
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
log.debug(
|
|
465
|
-
{
|
|
466
|
-
observationId: result.observationId,
|
|
467
|
-
chunkId: result.chunkId,
|
|
468
|
-
kind,
|
|
469
|
-
subject,
|
|
470
|
-
conversationId,
|
|
471
|
-
messageId,
|
|
472
|
-
},
|
|
473
|
-
"Memory saved via simplified system",
|
|
474
|
-
);
|
|
475
|
-
|
|
476
|
-
return {
|
|
477
|
-
content: `Saved to memory (ID: ${result.observationId}).\nKind: ${kind}\nSubject: ${subject}\nStatement: ${trimmedStatement}`,
|
|
478
|
-
isError: false,
|
|
479
|
-
};
|
|
480
|
-
} catch (err) {
|
|
481
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
482
|
-
log.error({ err }, "simplified memory_save failed");
|
|
483
|
-
return { content: `Error: Failed to save memory: ${msg}`, isError: true };
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* Recall memories using the simplified archive recall path instead of
|
|
489
|
-
* the legacy hybrid retriever.
|
|
490
|
-
*/
|
|
491
|
-
function handleSimplifiedMemoryRecall(
|
|
492
|
-
query: string,
|
|
493
|
-
scopeId: string,
|
|
494
|
-
): ToolExecutionResult {
|
|
495
|
-
try {
|
|
496
|
-
const recallResult = buildArchiveRecall(scopeId, query);
|
|
497
|
-
|
|
498
|
-
if (recallResult.bullets.length === 0) {
|
|
499
|
-
return {
|
|
500
|
-
content: JSON.stringify({
|
|
501
|
-
text: "No matching memories found.",
|
|
502
|
-
resultCount: 0,
|
|
503
|
-
degraded: false,
|
|
504
|
-
items: [],
|
|
505
|
-
sources: { semantic: 0, recency: 0 },
|
|
506
|
-
}),
|
|
507
|
-
isError: false,
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
const items = recallResult.bullets.map((b) => ({
|
|
512
|
-
id: b.sourceId,
|
|
513
|
-
type: b.source,
|
|
514
|
-
kind: b.source,
|
|
515
|
-
}));
|
|
516
|
-
|
|
517
|
-
const result = {
|
|
518
|
-
text: recallResult.text,
|
|
519
|
-
resultCount: recallResult.bullets.length,
|
|
520
|
-
degraded: false,
|
|
521
|
-
items,
|
|
522
|
-
sources: {
|
|
523
|
-
semantic: recallResult.prefetchHitCount,
|
|
524
|
-
recency: 0,
|
|
525
|
-
},
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
return {
|
|
529
|
-
content: JSON.stringify(result),
|
|
530
|
-
isError: false,
|
|
531
|
-
};
|
|
532
|
-
} catch (err) {
|
|
533
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
534
|
-
log.error({ err, query }, "simplified memory_recall failed");
|
|
535
|
-
return {
|
|
536
|
-
content: `Error: Memory recall failed: ${msg}`,
|
|
537
|
-
isError: true,
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
|
|
542
414
|
// ── Helpers ──────────────────────────────────────────────────────────
|
|
543
415
|
|
|
544
416
|
function inferSubjectFromStatement(statement: string): string {
|
|
@@ -61,10 +61,25 @@ const store = new SessionStore();
|
|
|
61
61
|
/**
|
|
62
62
|
* Host patterns that are allowed by default through the proxy policy engine,
|
|
63
63
|
* regardless of session configuration. Supports exact matches (e.g.
|
|
64
|
-
* `"localhost"`) and wildcard subdomain patterns (e.g. `"*.
|
|
65
|
-
* matches `
|
|
64
|
+
* `"localhost"`) and wildcard subdomain patterns (e.g. `"*.example.com"`
|
|
65
|
+
* matches `api.example.com`, `dev.example.com`, etc.).
|
|
66
|
+
*
|
|
67
|
+
* Additional patterns can be added via the `PROXY_ALLOWED_HOSTS` env var
|
|
68
|
+
* (comma-separated, e.g. `"*.example.com,api.foo.bar"`).
|
|
66
69
|
*/
|
|
67
|
-
const ALLOWED_HOST_PATTERNS: readonly string[] =
|
|
70
|
+
const ALLOWED_HOST_PATTERNS: readonly string[] = (() => {
|
|
71
|
+
const extra = process.env.PROXY_ALLOWED_HOSTS?.trim();
|
|
72
|
+
const defaults = ["localhost"];
|
|
73
|
+
if (extra) {
|
|
74
|
+
defaults.push(
|
|
75
|
+
...extra
|
|
76
|
+
.split(",")
|
|
77
|
+
.map((h) => h.trim())
|
|
78
|
+
.filter(Boolean),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return defaults;
|
|
82
|
+
})();
|
|
68
83
|
|
|
69
84
|
/**
|
|
70
85
|
* Returns `true` when `hostname` matches any entry in
|
|
@@ -73,7 +88,7 @@ const ALLOWED_HOST_PATTERNS: readonly string[] = ["*.vellum.ai", "localhost"];
|
|
|
73
88
|
function isAllowedHost(hostname: string): boolean {
|
|
74
89
|
for (const pattern of ALLOWED_HOST_PATTERNS) {
|
|
75
90
|
if (pattern.startsWith("*.")) {
|
|
76
|
-
const suffix = pattern.slice(1); // e.g. ".
|
|
91
|
+
const suffix = pattern.slice(1); // e.g. ".example.com"
|
|
77
92
|
if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {
|
|
78
93
|
return true;
|
|
79
94
|
}
|
|
@@ -573,7 +573,9 @@ export async function executeWebFetch(
|
|
|
573
573
|
Accept:
|
|
574
574
|
"text/markdown, text/html;q=0.9, application/xhtml+xml;q=0.9, text/plain;q=0.8, application/json;q=0.7, */*;q=0.6",
|
|
575
575
|
"Accept-Encoding": "identity",
|
|
576
|
-
"User-Agent":
|
|
576
|
+
"User-Agent":
|
|
577
|
+
process.env.HTTP_USER_AGENT ||
|
|
578
|
+
"VellumAssistant/1.0 (+https://vellum.ai)",
|
|
577
579
|
};
|
|
578
580
|
|
|
579
581
|
let currentUrl = new URL(requestedUrl);
|