@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
|
@@ -105,8 +105,10 @@ export async function analyzeShellCommand(
|
|
|
105
105
|
* - action:gh pr
|
|
106
106
|
* - action:gh
|
|
107
107
|
*
|
|
108
|
-
*
|
|
109
|
-
* action keys.
|
|
108
|
+
* Simple actions (optional setup prefix + one action) and pipelines get
|
|
109
|
+
* action keys. Both are marked non-simple when they involve pipes, but
|
|
110
|
+
* pipelines extract keys from the first segment before the first pipe.
|
|
111
|
+
* Complex chains (semicolons, ||, &, newlines) get no action keys.
|
|
110
112
|
*/
|
|
111
113
|
export function deriveShellActionKeys(
|
|
112
114
|
analysis: ShellIdentityAnalysis,
|
|
@@ -117,18 +119,22 @@ export function deriveShellActionKeys(
|
|
|
117
119
|
return { keys: [], isSimpleAction: false };
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
// For multi-segment commands,
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
// separators (;, newline, &) and leaves them as empty operators.
|
|
122
|
+
// For multi-segment commands, check operators to determine the command shape.
|
|
123
|
+
// Pipes (|) get special handling — we can extract action keys from the first
|
|
124
|
+
// segment before the pipe. Other complex operators (||, ;, &, empty/missing)
|
|
125
|
+
// are truly opaque and get no action keys.
|
|
125
126
|
if (segments.length > 1) {
|
|
127
|
+
let hasPipe = false;
|
|
128
|
+
|
|
126
129
|
for (const seg of segments) {
|
|
127
130
|
const op = seg.operator;
|
|
128
|
-
// Non-empty operator that isn't && → definitely complex
|
|
129
|
-
if (op && op !== "&&") {
|
|
131
|
+
// Non-empty operator that isn't && or | → definitely complex, no keys
|
|
132
|
+
if (op && op !== "&&" && op !== "|") {
|
|
130
133
|
return { keys: [], isSimpleAction: false };
|
|
131
134
|
}
|
|
135
|
+
if (op === "|") {
|
|
136
|
+
hasPipe = true;
|
|
137
|
+
}
|
|
132
138
|
}
|
|
133
139
|
// Also check: if there are multiple segments but no operators at all
|
|
134
140
|
// between them (e.g. newline-separated), that's suspicious.
|
|
@@ -140,6 +146,42 @@ export function deriveShellActionKeys(
|
|
|
140
146
|
return { keys: [], isSimpleAction: false };
|
|
141
147
|
}
|
|
142
148
|
}
|
|
149
|
+
|
|
150
|
+
// For pipelines, extract action keys from the first non-setup-prefix segment
|
|
151
|
+
// before the first pipe. This enables broader "Any pdftotext command" rules
|
|
152
|
+
// that match pipelines like "pdftotext file | head -100".
|
|
153
|
+
if (hasPipe) {
|
|
154
|
+
const firstPipeIndex = segments.findIndex((s) => s.operator === "|");
|
|
155
|
+
if (firstPipeIndex > 0) {
|
|
156
|
+
const preSegments = segments.slice(0, firstPipeIndex);
|
|
157
|
+
const actionSegs = preSegments.filter(
|
|
158
|
+
(s) => !SETUP_PREFIX_PROGRAMS.has(s.program),
|
|
159
|
+
);
|
|
160
|
+
if (actionSegs.length === 1) {
|
|
161
|
+
const seg = actionSegs[0];
|
|
162
|
+
const tokens: string[] = [seg.program];
|
|
163
|
+
for (const arg of seg.args) {
|
|
164
|
+
if (tokens.length >= MAX_ACTION_KEY_DEPTH) break;
|
|
165
|
+
if (arg.startsWith("-")) continue;
|
|
166
|
+
if (arg.includes("/") || arg.startsWith(".")) continue;
|
|
167
|
+
if (/^\d+$/.test(arg)) continue;
|
|
168
|
+
if (arg.includes("$") || arg.includes('"') || arg.includes("'"))
|
|
169
|
+
continue;
|
|
170
|
+
tokens.push(arg);
|
|
171
|
+
}
|
|
172
|
+
const keys: ShellActionKey[] = [];
|
|
173
|
+
for (let depth = tokens.length; depth >= 1; depth--) {
|
|
174
|
+
keys.push({
|
|
175
|
+
key: `action:${tokens.slice(0, depth).join(" ")}`,
|
|
176
|
+
depth,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return { keys, isSimpleAction: false, primarySegment: seg };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Pipeline but couldn't extract a single primary action — no keys
|
|
183
|
+
return { keys: [], isSimpleAction: false };
|
|
184
|
+
}
|
|
143
185
|
}
|
|
144
186
|
|
|
145
187
|
// Separate setup-prefix segments from action segments
|
|
@@ -190,9 +232,10 @@ export function deriveShellActionKeys(
|
|
|
190
232
|
* Candidate ordering:
|
|
191
233
|
* 1. Raw command (most specific match — the full command as written)
|
|
192
234
|
* 2. Canonical primary command (if simple action) — the full primary segment text
|
|
193
|
-
* 3. Action keys from narrowest to broadest (if simple action)
|
|
235
|
+
* 3. Action keys from narrowest to broadest (if simple action or pipeline)
|
|
194
236
|
*
|
|
195
|
-
* Complex commands (
|
|
237
|
+
* Complex non-pipeline commands (multi-action chains, semicolons, etc.) only
|
|
238
|
+
* return the raw candidate.
|
|
196
239
|
*/
|
|
197
240
|
export async function buildShellCommandCandidates(
|
|
198
241
|
command: string,
|
|
@@ -206,14 +249,15 @@ export async function buildShellCommandCandidates(
|
|
|
206
249
|
|
|
207
250
|
const candidates: string[] = [trimmed];
|
|
208
251
|
|
|
209
|
-
if (
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (
|
|
213
|
-
|
|
252
|
+
// Add action keys as candidates if available (simple actions AND pipelines)
|
|
253
|
+
if (actionResult.keys.length > 0) {
|
|
254
|
+
// For simple actions, also add the canonical primary command text
|
|
255
|
+
if (actionResult.isSimpleAction && actionResult.primarySegment) {
|
|
256
|
+
const canonical = actionResult.primarySegment.command;
|
|
257
|
+
if (canonical !== trimmed) {
|
|
258
|
+
candidates.push(canonical);
|
|
259
|
+
}
|
|
214
260
|
}
|
|
215
|
-
|
|
216
|
-
// Add action keys
|
|
217
261
|
for (const actionKey of actionResult.keys) {
|
|
218
262
|
candidates.push(actionKey.key);
|
|
219
263
|
}
|
|
@@ -231,8 +275,9 @@ export async function buildShellCommandCandidates(
|
|
|
231
275
|
* 2. Deepest action key (e.g. "action:gh pr view")
|
|
232
276
|
* 3. Broader action keys (e.g. "action:gh pr", "action:gh")
|
|
233
277
|
*
|
|
234
|
-
* For
|
|
235
|
-
*
|
|
278
|
+
* For pipelines, the exact command plus action-key-based broader options
|
|
279
|
+
* are offered. For other complex commands (multi-action chains, semicolons,
|
|
280
|
+
* etc.), only the exact command is offered.
|
|
236
281
|
*/
|
|
237
282
|
export async function buildShellAllowlistOptions(
|
|
238
283
|
command: string,
|
|
@@ -244,14 +289,23 @@ export async function buildShellAllowlistOptions(
|
|
|
244
289
|
const actionResult = deriveShellActionKeys(analysis);
|
|
245
290
|
|
|
246
291
|
if (!actionResult.isSimpleAction || !actionResult.primarySegment) {
|
|
247
|
-
|
|
248
|
-
return [
|
|
292
|
+
const options: AllowlistOption[] = [
|
|
249
293
|
{
|
|
250
294
|
label: trimmed,
|
|
251
295
|
description: "This exact compound command",
|
|
252
296
|
pattern: trimmed,
|
|
253
297
|
},
|
|
254
298
|
];
|
|
299
|
+
// If pipeline action keys were extracted, offer them as broader options
|
|
300
|
+
for (const actionKey of actionResult.keys) {
|
|
301
|
+
const keyTokens = actionKey.key.replace(/^action:/, "");
|
|
302
|
+
options.push({
|
|
303
|
+
label: `${keyTokens} *`,
|
|
304
|
+
description: `Any "${keyTokens}" command`,
|
|
305
|
+
pattern: actionKey.key,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
return options;
|
|
255
309
|
}
|
|
256
310
|
|
|
257
311
|
const options: AllowlistOption[] = [];
|
|
@@ -33,17 +33,6 @@ export interface AcceptStarterBundleResult {
|
|
|
33
33
|
// Helpers
|
|
34
34
|
// ---------------------------------------------------------------------------
|
|
35
35
|
|
|
36
|
-
/**
|
|
37
|
-
* Resolve the gateway base URL for trust rule requests.
|
|
38
|
-
*
|
|
39
|
-
* Prefers the `GATEWAY_INTERNAL_URL` env var (set in Docker environments
|
|
40
|
-
* where the gateway runs in a separate container), falling back to the
|
|
41
|
-
* existing `getGatewayInternalBaseUrl()` helper for local deployments.
|
|
42
|
-
*/
|
|
43
|
-
function getBaseUrl(): string {
|
|
44
|
-
return process.env.GATEWAY_INTERNAL_URL ?? getGatewayInternalBaseUrl();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
36
|
function authHeaders(): Record<string, string> {
|
|
48
37
|
return {
|
|
49
38
|
Authorization: `Bearer ${mintDaemonDeliveryToken()}`,
|
|
@@ -60,7 +49,7 @@ async function request<T>(
|
|
|
60
49
|
path: string,
|
|
61
50
|
body?: unknown,
|
|
62
51
|
): Promise<T> {
|
|
63
|
-
const url = `${
|
|
52
|
+
const url = `${getGatewayInternalBaseUrl()}${path}`;
|
|
64
53
|
const options: RequestInit = {
|
|
65
54
|
method,
|
|
66
55
|
headers: authHeaders(),
|
|
@@ -102,7 +91,7 @@ async function request<T>(
|
|
|
102
91
|
* Write operations are user-initiated and infrequent, so blocking is acceptable.
|
|
103
92
|
*/
|
|
104
93
|
function requestSync<T>(method: string, path: string, body?: unknown): T {
|
|
105
|
-
const url = `${
|
|
94
|
+
const url = `${getGatewayInternalBaseUrl()}${path}`;
|
|
106
95
|
const headers = authHeaders();
|
|
107
96
|
const args: string[] = [
|
|
108
97
|
"curl",
|
|
@@ -185,9 +185,10 @@ function backfillDefaults(rules: TrustRule[]): boolean {
|
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
// Migrate existing default rules whose priority, pattern, decision,
|
|
189
|
-
// allowHighRisk has changed in the template (e.g. host_bash pattern
|
|
190
|
-
// from '*' to '**', host tool priorities changed from 1000 to 50
|
|
188
|
+
// Migrate existing default rules whose priority, pattern, scope, decision,
|
|
189
|
+
// or allowHighRisk has changed in the template (e.g. host_bash pattern
|
|
190
|
+
// changed from '*' to '**', host tool priorities changed from 1000 to 50,
|
|
191
|
+
// workspace scope changed from getRootDir()+workspace to getWorkspaceDir()).
|
|
191
192
|
for (const template of getDefaultRuleTemplates()) {
|
|
192
193
|
if (existingIds.has(template.id)) {
|
|
193
194
|
const rule = rules.find((r) => r.id === template.id);
|
|
@@ -195,6 +196,7 @@ function backfillDefaults(rules: TrustRule[]): boolean {
|
|
|
195
196
|
rule &&
|
|
196
197
|
(rule.priority !== template.priority ||
|
|
197
198
|
rule.pattern !== template.pattern ||
|
|
199
|
+
rule.scope !== template.scope ||
|
|
198
200
|
rule.decision !== template.decision ||
|
|
199
201
|
rule.allowHighRisk !== template.allowHighRisk)
|
|
200
202
|
) {
|
|
@@ -205,11 +207,14 @@ function backfillDefaults(rules: TrustRule[]): boolean {
|
|
|
205
207
|
newPriority: template.priority,
|
|
206
208
|
oldPattern: rule.pattern,
|
|
207
209
|
newPattern: template.pattern,
|
|
210
|
+
oldScope: rule.scope,
|
|
211
|
+
newScope: template.scope,
|
|
208
212
|
},
|
|
209
213
|
"Migrated default rule to updated template values",
|
|
210
214
|
);
|
|
211
215
|
rule.priority = template.priority;
|
|
212
216
|
rule.pattern = template.pattern;
|
|
217
|
+
rule.scope = template.scope;
|
|
213
218
|
rule.decision = template.decision;
|
|
214
219
|
if (template.allowHighRisk != null) {
|
|
215
220
|
rule.allowHighRisk = template.allowHighRisk;
|
package/src/permissions/types.ts
CHANGED
|
@@ -24,7 +24,8 @@ export type UserDecision =
|
|
|
24
24
|
| "always_allow_high_risk"
|
|
25
25
|
| "deny"
|
|
26
26
|
| "always_deny"
|
|
27
|
-
| "temporary_override"
|
|
27
|
+
| "temporary_override"
|
|
28
|
+
| "dangerously_skip_permissions";
|
|
28
29
|
|
|
29
30
|
/** Returns true for any allow-variant decision. Centralizes the check to prevent omissions when new allow variants are added. */
|
|
30
31
|
export function isAllowDecision(decision: UserDecision): boolean {
|
|
@@ -34,7 +35,8 @@ export function isAllowDecision(decision: UserDecision): boolean {
|
|
|
34
35
|
decision === "allow_conversation" ||
|
|
35
36
|
decision === "always_allow" ||
|
|
36
37
|
decision === "always_allow_high_risk" ||
|
|
37
|
-
decision === "temporary_override"
|
|
38
|
+
decision === "temporary_override" ||
|
|
39
|
+
decision === "dangerously_skip_permissions"
|
|
38
40
|
);
|
|
39
41
|
}
|
|
40
42
|
|
package/src/platform/client.ts
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import { getPlatformAssistantId } from "../config/env.js";
|
|
9
9
|
import { resolveManagedProxyContext } from "../providers/managed-proxy/context.js";
|
|
10
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
11
|
+
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
10
12
|
|
|
11
13
|
export class VellumPlatformClient {
|
|
12
14
|
private readonly platformBaseUrl: string;
|
|
@@ -26,21 +28,47 @@ export class VellumPlatformClient {
|
|
|
26
28
|
/**
|
|
27
29
|
* Create a platform client by resolving managed proxy context.
|
|
28
30
|
*
|
|
31
|
+
* First tries the in-memory managed proxy context (available when the daemon
|
|
32
|
+
* has rehydrated env overrides). Falls back to reading platform credentials
|
|
33
|
+
* directly from the secure keychain so that standalone CLI invocations work
|
|
34
|
+
* without the daemon having run its rehydration step.
|
|
35
|
+
*
|
|
29
36
|
* Returns `null` when auth prerequisites are missing (not logged in, no API
|
|
30
37
|
* key). The assistant ID is resolved but not required — callers that need it
|
|
31
38
|
* should check `platformAssistantId` themselves.
|
|
32
39
|
*/
|
|
33
40
|
static async create(): Promise<VellumPlatformClient | null> {
|
|
34
41
|
const ctx = await resolveManagedProxyContext();
|
|
35
|
-
if (!ctx.enabled) return null;
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
let baseUrl = ctx.enabled ? ctx.platformBaseUrl : "";
|
|
44
|
+
let apiKey = ctx.enabled ? ctx.assistantApiKey : "";
|
|
45
|
+
let assistantId = getPlatformAssistantId();
|
|
46
|
+
|
|
47
|
+
// Fall back to keychain for values not yet rehydrated (standalone CLI).
|
|
48
|
+
if (!baseUrl) {
|
|
49
|
+
baseUrl =
|
|
50
|
+
(await getSecureKeyAsync(
|
|
51
|
+
credentialKey("vellum", "platform_base_url"),
|
|
52
|
+
)) ?? "";
|
|
53
|
+
}
|
|
54
|
+
if (!apiKey) {
|
|
55
|
+
apiKey =
|
|
56
|
+
(await getSecureKeyAsync(
|
|
57
|
+
credentialKey("vellum", "assistant_api_key"),
|
|
58
|
+
)) ?? "";
|
|
59
|
+
}
|
|
60
|
+
if (!assistantId) {
|
|
61
|
+
assistantId =
|
|
62
|
+
(
|
|
63
|
+
await getSecureKeyAsync(
|
|
64
|
+
credentialKey("vellum", "platform_assistant_id"),
|
|
65
|
+
)
|
|
66
|
+
)?.trim() ?? "";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!baseUrl || !apiKey) return null;
|
|
38
70
|
|
|
39
|
-
return new VellumPlatformClient(
|
|
40
|
-
ctx.platformBaseUrl,
|
|
41
|
-
ctx.assistantApiKey,
|
|
42
|
-
assistantId,
|
|
43
|
-
);
|
|
71
|
+
return new VellumPlatformClient(baseUrl, apiKey, assistantId);
|
|
44
72
|
}
|
|
45
73
|
|
|
46
74
|
/**
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
findContactByChannelExternalId,
|
|
6
|
+
listGuardianChannels,
|
|
7
|
+
} from "../contacts/contact-store.js";
|
|
8
|
+
import type {
|
|
9
|
+
ChannelCapabilities,
|
|
10
|
+
TrustContext,
|
|
11
|
+
} from "../daemon/conversation-runtime-assembly.js";
|
|
12
|
+
import { getLogger } from "../util/logger.js";
|
|
13
|
+
import { getWorkspaceDir } from "../util/platform.js";
|
|
14
|
+
import { stripCommentLines } from "./system-prompt.js";
|
|
15
|
+
|
|
16
|
+
const log = getLogger("persona-resolver");
|
|
17
|
+
|
|
18
|
+
// ── Types ──────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
export interface PersonaContext {
|
|
21
|
+
userPersona: string | null;
|
|
22
|
+
channelPersona: string | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ── Helpers ────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Read a persona file from disk, apply comment stripping, and return
|
|
29
|
+
* the content. Returns null if the file does not exist or is empty
|
|
30
|
+
* after stripping.
|
|
31
|
+
*/
|
|
32
|
+
function readPersonaFile(filePath: string): string | null {
|
|
33
|
+
if (!existsSync(filePath)) return null;
|
|
34
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
35
|
+
const content = stripCommentLines(raw).trim();
|
|
36
|
+
return content.length > 0 ? content : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ── User persona ───────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the per-user persona file for the current actor.
|
|
43
|
+
*
|
|
44
|
+
* - If `trustContext` is undefined (desktop/native), looks up the guardian
|
|
45
|
+
* contact and reads their user file.
|
|
46
|
+
* - If `trustContext` is defined and carries a `requesterExternalUserId`,
|
|
47
|
+
* looks up the contact by channel + external user ID.
|
|
48
|
+
* - Falls back to `users/default.md` when no contact is found or the
|
|
49
|
+
* contact has no `userFile` set.
|
|
50
|
+
* - Logs a debug warning when a contact's `userFile` is set but the
|
|
51
|
+
* corresponding file is missing on disk.
|
|
52
|
+
*/
|
|
53
|
+
export function resolveUserPersona(
|
|
54
|
+
trustContext: TrustContext | undefined,
|
|
55
|
+
): string | null {
|
|
56
|
+
const usersDir = join(getWorkspaceDir(), "users");
|
|
57
|
+
const defaultPath = join(usersDir, "default.md");
|
|
58
|
+
|
|
59
|
+
let filename: string | null = null;
|
|
60
|
+
|
|
61
|
+
if (trustContext === undefined) {
|
|
62
|
+
// Desktop / native — resolve via guardian contact
|
|
63
|
+
const guardian = listGuardianChannels();
|
|
64
|
+
if (guardian) {
|
|
65
|
+
filename = guardian.contact.userFile ?? null;
|
|
66
|
+
}
|
|
67
|
+
} else if (trustContext.requesterExternalUserId) {
|
|
68
|
+
// Channel-routed request — look up contact by channel identity
|
|
69
|
+
const contactWithChannels = findContactByChannelExternalId(
|
|
70
|
+
trustContext.sourceChannel,
|
|
71
|
+
trustContext.requesterExternalUserId,
|
|
72
|
+
);
|
|
73
|
+
if (contactWithChannels) {
|
|
74
|
+
filename = contactWithChannels.userFile ?? null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Resolve file path
|
|
79
|
+
if (filename) {
|
|
80
|
+
const filePath = join(usersDir, filename);
|
|
81
|
+
if (existsSync(filePath)) {
|
|
82
|
+
return readPersonaFile(filePath);
|
|
83
|
+
}
|
|
84
|
+
// userFile is set but the file doesn't exist on disk
|
|
85
|
+
log.debug(
|
|
86
|
+
{ userFile: filename },
|
|
87
|
+
"Contact has userFile set but file is missing on disk; falling back to default.md",
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Fall back to default.md
|
|
92
|
+
return readPersonaFile(defaultPath);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── Channel persona ────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Resolve the per-channel persona file based on channel capabilities.
|
|
99
|
+
*
|
|
100
|
+
* Reads from `channels/<channel>.md` in the workspace directory.
|
|
101
|
+
* Defaults to `"vellum"` when no channel capabilities are provided.
|
|
102
|
+
* Returns null if the channel file does not exist.
|
|
103
|
+
*/
|
|
104
|
+
export function resolveChannelPersona(
|
|
105
|
+
channelCapabilities: ChannelCapabilities | undefined,
|
|
106
|
+
): string | null {
|
|
107
|
+
const channel = channelCapabilities?.channel ?? "vellum";
|
|
108
|
+
const filePath = join(getWorkspaceDir(), "channels", channel + ".md");
|
|
109
|
+
return readPersonaFile(filePath);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── Combined resolver ──────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Resolve both user and channel persona context in a single call.
|
|
116
|
+
*/
|
|
117
|
+
export function resolvePersonaContext(
|
|
118
|
+
trustContext: TrustContext | undefined,
|
|
119
|
+
channelCapabilities: ChannelCapabilities | undefined,
|
|
120
|
+
): PersonaContext {
|
|
121
|
+
return {
|
|
122
|
+
userPersona: resolveUserPersona(trustContext),
|
|
123
|
+
channelPersona: resolveChannelPersona(channelCapabilities),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── Guardian convenience ───────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Resolve the guardian's user persona.
|
|
131
|
+
*
|
|
132
|
+
* This is a convenience wrapper for background subsystems that need
|
|
133
|
+
* the guardian's persona without a full trust context. Passing
|
|
134
|
+
* `undefined` triggers the guardian lookup path in `resolveUserPersona`.
|
|
135
|
+
*/
|
|
136
|
+
export function resolveGuardianPersona(): string | null {
|
|
137
|
+
return resolveUserPersona(undefined);
|
|
138
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { copyFileSync, existsSync, readFileSync } from "node:fs";
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
@@ -9,7 +9,11 @@ import { loadSkillCatalog, type SkillSummary } from "../config/skills.js";
|
|
|
9
9
|
import { listConnections } from "../oauth/oauth-store.js";
|
|
10
10
|
import { resolveBundledDir } from "../util/bundled-asset.js";
|
|
11
11
|
import { getLogger } from "../util/logger.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
getWorkspaceDir,
|
|
14
|
+
getWorkspacePromptPath,
|
|
15
|
+
isMacOS,
|
|
16
|
+
} from "../util/platform.js";
|
|
13
17
|
import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "./cache-boundary.js";
|
|
14
18
|
|
|
15
19
|
export { SYSTEM_PROMPT_CACHE_BOUNDARY };
|
|
@@ -78,6 +82,26 @@ export function ensurePromptFiles(): void {
|
|
|
78
82
|
}
|
|
79
83
|
}
|
|
80
84
|
}
|
|
85
|
+
|
|
86
|
+
// Seed users/default.md persona template
|
|
87
|
+
try {
|
|
88
|
+
const usersDir = join(getWorkspaceDir(), "users");
|
|
89
|
+
mkdirSync(usersDir, { recursive: true });
|
|
90
|
+
const defaultPersonaSrc = join(templatesDir, "users", "default.md");
|
|
91
|
+
const defaultPersonaDest = join(usersDir, "default.md");
|
|
92
|
+
if (!existsSync(defaultPersonaDest) && existsSync(defaultPersonaSrc)) {
|
|
93
|
+
copyFileSync(defaultPersonaSrc, defaultPersonaDest);
|
|
94
|
+
log.info(
|
|
95
|
+
{ file: "users/default.md", dest: defaultPersonaDest },
|
|
96
|
+
"Created default persona file from template",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
} catch (err) {
|
|
100
|
+
log.warn(
|
|
101
|
+
{ err, file: "users/default.md" },
|
|
102
|
+
"Failed to create default persona file from template",
|
|
103
|
+
);
|
|
104
|
+
}
|
|
81
105
|
}
|
|
82
106
|
|
|
83
107
|
/**
|
|
@@ -93,6 +117,8 @@ export function ensurePromptFiles(): void {
|
|
|
93
117
|
export interface BuildSystemPromptOptions {
|
|
94
118
|
hasNoClient?: boolean;
|
|
95
119
|
excludeBootstrap?: boolean;
|
|
120
|
+
userPersona?: string | null;
|
|
121
|
+
channelPersona?: string | null;
|
|
96
122
|
}
|
|
97
123
|
|
|
98
124
|
/**
|
|
@@ -159,7 +185,9 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
|
|
|
159
185
|
dynamicParts.push(identity);
|
|
160
186
|
}
|
|
161
187
|
if (soul) dynamicParts.push(soul);
|
|
162
|
-
if (
|
|
188
|
+
if (options?.userPersona) dynamicParts.push(options.userPersona);
|
|
189
|
+
if (options?.channelPersona) dynamicParts.push(options.channelPersona);
|
|
190
|
+
if (user && !userIsTemplate && !options?.userPersona) dynamicParts.push(user);
|
|
163
191
|
if (includeBootstrap) {
|
|
164
192
|
dynamicParts.push(
|
|
165
193
|
"# First-Run Ritual\n\n" +
|
|
@@ -377,7 +405,9 @@ function readPromptFile(path: string): string | null {
|
|
|
377
405
|
* This is useful for injecting identity context into subsystems (e.g. memory
|
|
378
406
|
* extraction) that run outside the main system prompt pipeline.
|
|
379
407
|
*/
|
|
380
|
-
export function buildCoreIdentityContext(
|
|
408
|
+
export function buildCoreIdentityContext(
|
|
409
|
+
opts?: { userPersona?: string | null },
|
|
410
|
+
): string | null {
|
|
381
411
|
const parts: string[] = [];
|
|
382
412
|
for (const file of PROMPT_FILES) {
|
|
383
413
|
const content = readPromptFile(getWorkspacePromptPath(file));
|
|
@@ -386,8 +416,10 @@ export function buildCoreIdentityContext(): string | null {
|
|
|
386
416
|
// before onboarding completes. Only skip IDENTITY.md and USER.md when
|
|
387
417
|
// they are still unmodified templates (matching buildSystemPrompt).
|
|
388
418
|
if (file !== "SOUL.md" && isTemplateContent(content, file)) continue;
|
|
419
|
+
if (file === "USER.md" && opts?.userPersona) continue;
|
|
389
420
|
parts.push(content);
|
|
390
421
|
}
|
|
422
|
+
if (opts?.userPersona) parts.push(opts.userPersona);
|
|
391
423
|
return parts.length > 0 ? parts.join("\n\n") : null;
|
|
392
424
|
}
|
|
393
425
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { wrapWithLogfire } from "../logfire.js";
|
|
2
1
|
import { getProviderKeyAsync } from "../security/secure-keys.js";
|
|
3
2
|
import { ConfigError, ProviderNotConfiguredError } from "../util/errors.js";
|
|
4
3
|
import { AnthropicProvider } from "./anthropic/client.js";
|
|
@@ -293,15 +292,13 @@ export async function initializeProviders(
|
|
|
293
292
|
registerProvider(
|
|
294
293
|
"anthropic",
|
|
295
294
|
new RetryProvider(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}),
|
|
304
|
-
),
|
|
295
|
+
new AnthropicProvider(anthropicCreds.apiKey, model, {
|
|
296
|
+
useNativeWebSearch,
|
|
297
|
+
streamTimeoutMs,
|
|
298
|
+
...(anthropicCreds.baseURL
|
|
299
|
+
? { baseURL: anthropicCreds.baseURL }
|
|
300
|
+
: {}),
|
|
301
|
+
}),
|
|
305
302
|
),
|
|
306
303
|
);
|
|
307
304
|
routingSources.set("anthropic", anthropicCreds.source);
|
|
@@ -314,12 +311,10 @@ export async function initializeProviders(
|
|
|
314
311
|
registerProvider(
|
|
315
312
|
"openai",
|
|
316
313
|
new RetryProvider(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}),
|
|
322
|
-
),
|
|
314
|
+
new OpenAIProvider(openaiCreds.apiKey, model, {
|
|
315
|
+
streamTimeoutMs,
|
|
316
|
+
...(openaiCreds.baseURL ? { baseURL: openaiCreds.baseURL } : {}),
|
|
317
|
+
}),
|
|
323
318
|
),
|
|
324
319
|
);
|
|
325
320
|
routingSources.set("openai", openaiCreds.source);
|
|
@@ -332,14 +327,12 @@ export async function initializeProviders(
|
|
|
332
327
|
registerProvider(
|
|
333
328
|
"gemini",
|
|
334
329
|
new RetryProvider(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
}),
|
|
342
|
-
),
|
|
330
|
+
new GeminiProvider(geminiCreds.apiKey, model, {
|
|
331
|
+
streamTimeoutMs,
|
|
332
|
+
...(geminiCreds.baseURL
|
|
333
|
+
? { managedBaseUrl: geminiCreds.baseURL }
|
|
334
|
+
: {}),
|
|
335
|
+
}),
|
|
343
336
|
),
|
|
344
337
|
);
|
|
345
338
|
routingSources.set("gemini", geminiCreds.source);
|
|
@@ -352,12 +345,10 @@ export async function initializeProviders(
|
|
|
352
345
|
registerProvider(
|
|
353
346
|
"ollama",
|
|
354
347
|
new RetryProvider(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}),
|
|
360
|
-
),
|
|
348
|
+
new OllamaProvider(model, {
|
|
349
|
+
apiKey: ollamaKey ?? undefined,
|
|
350
|
+
streamTimeoutMs,
|
|
351
|
+
}),
|
|
361
352
|
),
|
|
362
353
|
);
|
|
363
354
|
routingSources.set("ollama", "user-key");
|
|
@@ -370,11 +361,9 @@ export async function initializeProviders(
|
|
|
370
361
|
registerProvider(
|
|
371
362
|
"fireworks",
|
|
372
363
|
new RetryProvider(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
}),
|
|
377
|
-
),
|
|
364
|
+
new FireworksProvider(fireworksKey, model, {
|
|
365
|
+
streamTimeoutMs,
|
|
366
|
+
}),
|
|
378
367
|
),
|
|
379
368
|
);
|
|
380
369
|
routingSources.set("fireworks", "user-key");
|
|
@@ -387,11 +376,9 @@ export async function initializeProviders(
|
|
|
387
376
|
registerProvider(
|
|
388
377
|
"openrouter",
|
|
389
378
|
new RetryProvider(
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}),
|
|
394
|
-
),
|
|
379
|
+
new OpenRouterProvider(openrouterKey, model, {
|
|
380
|
+
streamTimeoutMs,
|
|
381
|
+
}),
|
|
395
382
|
),
|
|
396
383
|
);
|
|
397
384
|
routingSources.set("openrouter", "user-key");
|
|
@@ -17,7 +17,6 @@ mock.module("../../../util/platform.js", () => ({
|
|
|
17
17
|
getDataDir: () => testDir,
|
|
18
18
|
getDbPath: () => join(testDir, "test.db"),
|
|
19
19
|
normalizeAssistantId: (id: string) => (id === "self" ? "self" : id),
|
|
20
|
-
readLockfile: () => ({ assistants: [{ assistantId: "vellum-test-eel" }] }),
|
|
21
20
|
isMacOS: () => process.platform === "darwin",
|
|
22
21
|
isLinux: () => process.platform === "linux",
|
|
23
22
|
isWindows: () => process.platform === "win32",
|