@vellumai/assistant 0.5.6 → 0.5.8
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 +3 -2
- package/README.md +0 -2
- package/bun.lock +0 -414
- package/docker-entrypoint.sh +9 -0
- package/docs/architecture/keychain-broker.md +45 -240
- package/docs/architecture/memory.md +13 -11
- 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/error.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/grants.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +120 -1
- 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 +0 -114
- package/src/__tests__/approval-cascade.test.ts +0 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/browser-skill-endstate.test.ts +6 -5
- package/src/__tests__/btw-routes.test.ts +0 -39
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-domain.test.ts +0 -128
- package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
- package/src/__tests__/ces-startup-timeout.test.ts +40 -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 -2
- package/src/__tests__/config-schema.test.ts +3 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop.test.ts +2 -4
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -1
- package/src/__tests__/conversation-error.test.ts +15 -1
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-messaging-secret-redirect.test.ts +1 -1
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -1
- package/src/__tests__/conversation-queue.test.ts +0 -1
- package/src/__tests__/conversation-skill-tools.test.ts +0 -54
- package/src/__tests__/conversation-slash-queue.test.ts +0 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -1
- package/src/__tests__/conversation-title-service.test.ts +87 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -1
- package/src/__tests__/credential-execution-client.test.ts +5 -2
- package/src/__tests__/credential-execution-feature-gates.test.ts +59 -30
- package/src/__tests__/credential-execution-managed-contract.test.ts +35 -20
- package/src/__tests__/credential-security-e2e.test.ts +1 -67
- package/src/__tests__/credential-security-invariants.test.ts +6 -50
- package/src/__tests__/credentials-cli.test.ts +82 -3
- package/src/__tests__/daemon-credential-client.test.ts +123 -0
- package/src/__tests__/db-migration-rollback.test.ts +2015 -1
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
- package/src/__tests__/gateway-client-managed-outbound.test.ts +79 -1
- 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__/journal-context.test.ts +335 -0
- package/src/__tests__/keychain-broker-client.test.ts +161 -22
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -3
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +70 -25
- package/src/__tests__/memory-recall-quality.test.ts +48 -17
- package/src/__tests__/memory-regressions.test.ts +408 -363
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -3
- 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 +2 -7
- package/src/__tests__/notification-decision-fallback.test.ts +4 -0
- package/src/__tests__/notification-decision-identity.test.ts +4 -0
- package/src/__tests__/notification-decision-strategy.test.ts +71 -0
- package/src/__tests__/oauth-cli.test.ts +5 -1
- package/src/__tests__/permission-types.test.ts +1 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +0 -37
- package/src/__tests__/provider-error-scenarios.test.ts +0 -267
- package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
- package/src/__tests__/provider-streaming.benchmark.test.ts +2 -81
- package/src/__tests__/qdrant-manager.test.ts +28 -2
- package/src/__tests__/registry.test.ts +0 -6
- package/src/__tests__/relay-server.test.ts +1 -2
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
- package/src/__tests__/script-proxy-injection-runtime.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -1
- package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
- package/src/__tests__/secure-keys.test.ts +95 -272
- 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-memory.test.ts +17 -3
- 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__/stale-approval-dedup.test.ts +171 -0
- package/src/__tests__/stt-hints.test.ts +437 -0
- package/src/__tests__/suggestion-routes.test.ts +1 -32
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/task-memory-cleanup.test.ts +14 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
- package/src/__tests__/twilio-routes-twiml.test.ts +139 -1
- 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-quality.test.ts +58 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -7
- 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 +220 -0
- package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
- package/src/acp/agent-process.ts +9 -1
- package/src/agent/loop.ts +1 -1
- package/src/approvals/guardian-request-resolvers.ts +164 -38
- package/src/calls/__tests__/tts-text-sanitizer.test.ts +254 -0
- package/src/calls/audio-store.test.ts +97 -0
- package/src/calls/audio-store.ts +205 -0
- package/src/calls/call-controller.ts +90 -8
- package/src/calls/call-domain.ts +3 -0
- package/src/calls/call-store.ts +10 -3
- package/src/calls/fish-audio-client.ts +129 -0
- package/src/calls/relay-server.ts +27 -0
- package/src/calls/stt-hints.ts +189 -0
- package/src/calls/tts-text-sanitizer.ts +61 -0
- package/src/calls/twilio-routes.ts +34 -5
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-ingress-preflight.ts +0 -42
- package/src/calls/voice-quality.ts +38 -5
- package/src/calls/voice-session-bridge.ts +7 -12
- package/src/cli/commands/avatar.ts +2 -2
- package/src/cli/commands/config.ts +1 -4
- package/src/cli/commands/credentials.ts +128 -82
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/keys.ts +7 -7
- package/src/cli/commands/memory.ts +1 -1
- package/src/cli/commands/oauth/connections.ts +11 -29
- package/src/cli/commands/oauth/index.ts +7 -0
- package/src/cli/commands/oauth/platform.ts +525 -0
- package/src/cli/commands/platform.ts +3 -3
- package/src/cli/lib/daemon-credential-client.ts +284 -0
- package/src/cli.ts +1 -1
- package/src/config/assistant-feature-flags.ts +186 -5
- package/src/config/bundled-skills/AGENTS.md +34 -0
- package/src/config/bundled-skills/acp/SKILL.md +10 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +0 -4
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
- package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +15 -2
- package/src/config/bundled-skills/settings/TOOLS.json +47 -2
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +59 -0
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +80 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
- package/src/config/bundled-skills/slack/SKILL.md +1 -1
- package/src/config/bundled-tool-registry.ts +5 -11
- package/src/config/defaults.ts +0 -2
- package/src/config/env-registry.ts +5 -5
- package/src/config/env.ts +21 -14
- package/src/config/feature-flag-registry.json +49 -9
- package/src/config/loader.ts +106 -42
- package/src/config/schema.ts +9 -29
- package/src/config/schemas/calls.ts +30 -0
- package/src/config/schemas/fish-audio.ts +39 -0
- package/src/config/schemas/inference.ts +2 -2
- package/src/config/schemas/journal.ts +16 -0
- package/src/config/schemas/memory-processing.ts +2 -2
- package/src/config/schemas/security.ts +0 -4
- package/src/config/types.ts +1 -1
- package/src/contacts/contact-store.ts +39 -0
- package/src/contacts/types.ts +2 -0
- 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/credential-execution/startup-timeout.ts +36 -0
- package/src/daemon/approval-generators.ts +3 -9
- package/src/daemon/assistant-attachments.ts +9 -0
- package/src/daemon/config-watcher.ts +5 -0
- package/src/daemon/conversation-error.ts +13 -1
- package/src/daemon/conversation-memory.ts +1 -2
- package/src/daemon/conversation-process.ts +18 -1
- package/src/daemon/conversation-surfaces.ts +30 -1
- package/src/daemon/conversation-tool-setup.ts +0 -105
- package/src/daemon/conversation.ts +21 -1
- package/src/daemon/guardian-action-generators.ts +3 -9
- package/src/daemon/handlers/config-vercel.ts +92 -0
- package/src/daemon/handlers/skills.ts +2 -15
- package/src/daemon/install-symlink.ts +195 -0
- package/src/daemon/lifecycle.ts +234 -51
- package/src/daemon/message-types/conversations.ts +4 -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 +32 -95
- package/src/events/domain-events.ts +2 -1
- package/src/inbound/platform-callback-registration.ts +3 -3
- package/src/instrument.ts +8 -5
- package/src/memory/app-store.ts +31 -0
- package/src/memory/conversation-title-service.ts +50 -1
- package/src/memory/db-init.ts +16 -0
- package/src/memory/indexer.ts +19 -10
- package/src/memory/items-extractor.ts +328 -321
- package/src/memory/job-handlers/conversation-starters.ts +4 -1
- package/src/memory/job-handlers/summarization.ts +26 -16
- package/src/memory/jobs-store.ts +63 -6
- package/src/memory/jobs-worker.ts +31 -7
- package/src/memory/journal-memory.ts +214 -0
- 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/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/193-add-source-type-columns.ts +81 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/migrations/registry.ts +98 -0
- package/src/memory/migrations/validate-migration-state.ts +137 -11
- package/src/memory/qdrant-circuit-breaker.ts +9 -0
- package/src/memory/qdrant-manager.ts +64 -7
- package/src/memory/retriever.test.ts +37 -25
- package/src/memory/retriever.ts +24 -49
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/memory-core.ts +2 -0
- package/src/memory/search/formatting.ts +7 -44
- package/src/memory/search/staleness.ts +4 -0
- package/src/memory/search/tier-classifier.ts +10 -2
- package/src/memory/search/types.ts +2 -5
- package/src/memory/task-memory-cleanup.ts +4 -3
- package/src/notifications/adapters/slack.ts +168 -6
- package/src/notifications/broadcaster.ts +1 -0
- package/src/notifications/copy-composer.ts +59 -2
- package/src/notifications/decision-engine.ts +4 -1
- package/src/notifications/signal.ts +2 -0
- package/src/notifications/types.ts +2 -0
- package/src/oauth/connection-resolver.ts +6 -4
- package/src/permissions/checker.ts +0 -38
- package/src/permissions/shell-identity.ts +76 -22
- package/src/permissions/types.ts +4 -2
- package/src/platform/client.ts +35 -7
- package/src/prompts/journal-context.ts +133 -0
- package/src/prompts/persona-resolver.ts +194 -0
- package/src/prompts/system-prompt.ts +44 -4
- package/src/prompts/templates/SOUL.md +10 -0
- package/src/prompts/templates/users/default.md +1 -0
- package/src/providers/provider-send-message.ts +3 -32
- package/src/providers/registry.ts +29 -179
- package/src/providers/types.ts +1 -1
- package/src/runtime/access-request-helper.ts +4 -0
- 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/__tests__/guard-tests.test.ts +9 -50
- package/src/runtime/auth/external-assistant-id.ts +13 -59
- package/src/runtime/auth/route-policy.ts +17 -1
- package/src/runtime/auth/token-service.ts +43 -138
- package/src/runtime/channel-readiness-service.ts +1 -16
- package/src/runtime/gateway-client.ts +47 -4
- package/src/runtime/guardian-decision-types.ts +45 -4
- package/src/runtime/http-server.ts +31 -3
- package/src/runtime/middleware/error-handler.ts +1 -9
- package/src/runtime/routes/access-request-decision.ts +2 -2
- package/src/runtime/routes/app-management-routes.ts +2 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +219 -30
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +37 -14
- package/src/runtime/routes/audio-routes.ts +40 -0
- package/src/runtime/routes/btw-routes.ts +0 -17
- package/src/runtime/routes/channel-readiness-routes.ts +9 -4
- package/src/runtime/routes/conversation-query-routes.ts +63 -1
- package/src/runtime/routes/conversation-routes.ts +4 -44
- package/src/runtime/routes/debug-routes.ts +12 -9
- package/src/runtime/routes/diagnostics-routes.ts +1 -477
- package/src/runtime/routes/guardian-approval-interception.ts +168 -11
- package/src/runtime/routes/guardian-approval-prompt.ts +6 -1
- package/src/runtime/routes/guardian-approval-reply-helpers.ts +103 -21
- package/src/runtime/routes/identity-routes.ts +19 -30
- package/src/runtime/routes/inbound-message-handler.ts +31 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +64 -5
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +52 -40
- 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/twilio.ts +52 -10
- 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 +3 -3
- package/src/runtime/routes/memory-item-routes.ts +46 -14
- 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/secret-routes.ts +141 -10
- package/src/runtime/routes/settings-routes.ts +41 -1
- package/src/runtime/routes/tts-routes.ts +96 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
- 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/security/ces-credential-client.ts +75 -29
- package/src/security/ces-rpc-credential-backend.ts +86 -0
- package/src/security/credential-backend.ts +22 -92
- package/src/security/keychain-broker-client.ts +10 -2
- package/src/security/secure-keys.ts +113 -115
- package/src/skills/catalog-install.ts +6 -32
- package/src/skills/skill-memory.ts +1 -0
- package/src/subagent/manager.ts +2 -5
- package/src/telemetry/usage-telemetry-reporter.ts +4 -2
- package/src/tools/acp/spawn.ts +78 -1
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/credentials/vault.ts +5 -3
- package/src/tools/executor.ts +0 -4
- package/src/tools/memory/definitions.ts +3 -2
- package/src/tools/memory/handlers.ts +10 -7
- package/src/tools/network/script-proxy/session-manager.ts +19 -4
- package/src/tools/network/web-fetch.ts +3 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/types.ts +0 -8
- package/src/util/browser.ts +15 -0
- package/src/util/errors.ts +0 -12
- package/src/util/platform.ts +4 -51
- package/src/workspace/git-service.ts +5 -2
- 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 +96 -0
- package/src/workspace/migrations/018-rekey-compound-credential-keys.ts +184 -0
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +103 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +27 -5
- package/src/workspace/migrations/registry.ts +12 -0
- package/src/workspace/migrations/runner.ts +106 -2
- package/src/workspace/migrations/types.ts +4 -0
- package/src/workspace/provider-commit-message-generator.ts +12 -21
- 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__/diagnostics-export.test.ts +0 -288
- package/src/__tests__/local-gateway-health.test.ts +0 -209
- package/src/__tests__/provider-fail-open-selection.test.ts +0 -271
- package/src/__tests__/provider-failover-actual-provider.test.ts +0 -66
- package/src/__tests__/secret-ingress-handler.test.ts +0 -120
- 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/swarm.ts +0 -82
- package/src/logfire.ts +0 -135
- package/src/memory/search/lexical.ts +0 -48
- package/src/providers/failover.ts +0 -186
- 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
|
@@ -7,6 +7,7 @@ import { applyGuardianDecision } from "../../../approvals/guardian-decision-prim
|
|
|
7
7
|
import type { ChannelId } from "../../../channels/types.js";
|
|
8
8
|
import {
|
|
9
9
|
getAllPendingApprovalsByGuardianChat,
|
|
10
|
+
getApprovalRequestById,
|
|
10
11
|
getPendingApprovalByRequestAndGuardianChat,
|
|
11
12
|
type GuardianApprovalRequest,
|
|
12
13
|
} from "../../../memory/guardian-approvals.js";
|
|
@@ -42,6 +43,17 @@ import {
|
|
|
42
43
|
|
|
43
44
|
const log = getLogger("runtime-http");
|
|
44
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Resolve the Slack ephemeral user ID when the source channel is Slack.
|
|
48
|
+
* Returns `undefined` for non-Slack channels.
|
|
49
|
+
*/
|
|
50
|
+
function slackEphemeralUserId(
|
|
51
|
+
sourceChannel: ChannelId,
|
|
52
|
+
userId: string | undefined,
|
|
53
|
+
): string | undefined {
|
|
54
|
+
return sourceChannel === "slack" && userId ? userId : undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
export interface GuardianCallbackDecisionParams {
|
|
46
58
|
content: string;
|
|
47
59
|
callbackData?: string;
|
|
@@ -53,6 +65,8 @@ export interface GuardianCallbackDecisionParams {
|
|
|
53
65
|
assistantId: string;
|
|
54
66
|
approvalCopyGenerator?: ApprovalCopyGenerator;
|
|
55
67
|
approvalConversationGenerator?: ApprovalConversationGenerator;
|
|
68
|
+
/** Original approval message timestamp (Slack ts) for editing after resolution. */
|
|
69
|
+
approvalMessageTs?: string;
|
|
56
70
|
}
|
|
57
71
|
|
|
58
72
|
export interface ApprovalInterceptionResult {
|
|
@@ -84,6 +98,7 @@ export async function handleGuardianCallbackDecision(
|
|
|
84
98
|
assistantId,
|
|
85
99
|
approvalCopyGenerator,
|
|
86
100
|
approvalConversationGenerator,
|
|
101
|
+
approvalMessageTs,
|
|
87
102
|
} = params;
|
|
88
103
|
|
|
89
104
|
// Callback/button path: deterministic and takes priority.
|
|
@@ -129,6 +144,7 @@ export async function handleGuardianCallbackDecision(
|
|
|
129
144
|
"Failed to deliver stale callback disambiguation notice",
|
|
130
145
|
extraContext: { pendingCount: allPending.length },
|
|
131
146
|
errorLogContext: { conversationExternalId },
|
|
147
|
+
ephemeralUserId: slackEphemeralUserId(sourceChannel, actorExternalId),
|
|
132
148
|
});
|
|
133
149
|
return { handled: true, type: "stale_ignored" };
|
|
134
150
|
}
|
|
@@ -181,6 +197,7 @@ export async function handleGuardianCallbackDecision(
|
|
|
181
197
|
logger: log,
|
|
182
198
|
errorLogMessage: "Failed to deliver guardian identity rejection notice",
|
|
183
199
|
errorLogContext: { conversationExternalId },
|
|
200
|
+
ephemeralUserId: slackEphemeralUserId(sourceChannel, actorExternalId),
|
|
184
201
|
});
|
|
185
202
|
return { handled: true, type: "guardian_decision_applied" };
|
|
186
203
|
}
|
|
@@ -195,6 +212,7 @@ export async function handleGuardianCallbackDecision(
|
|
|
195
212
|
assistantId,
|
|
196
213
|
bearerToken,
|
|
197
214
|
approvalCopyGenerator,
|
|
215
|
+
approvalMessageTs,
|
|
198
216
|
});
|
|
199
217
|
}
|
|
200
218
|
|
|
@@ -247,15 +265,20 @@ export async function handleGuardianCallbackDecision(
|
|
|
247
265
|
{},
|
|
248
266
|
approvalCopyGenerator,
|
|
249
267
|
);
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
268
|
+
const fallbackPayload: Parameters<typeof deliverChannelReply>[1] = {
|
|
269
|
+
chatId: conversationExternalId,
|
|
270
|
+
text,
|
|
271
|
+
assistantId,
|
|
272
|
+
};
|
|
273
|
+
const guardianFallbackEphemeral = slackEphemeralUserId(
|
|
274
|
+
sourceChannel,
|
|
275
|
+
actorExternalId,
|
|
258
276
|
);
|
|
277
|
+
if (guardianFallbackEphemeral) {
|
|
278
|
+
fallbackPayload.ephemeral = true;
|
|
279
|
+
fallbackPayload.user = guardianFallbackEphemeral;
|
|
280
|
+
}
|
|
281
|
+
await deliverChannelReply(replyCallbackUrl, fallbackPayload, bearerToken);
|
|
259
282
|
} catch (err) {
|
|
260
283
|
log.error(
|
|
261
284
|
{ err, conversationExternalId },
|
|
@@ -282,6 +305,7 @@ async function handleCallbackDecision(params: {
|
|
|
282
305
|
assistantId: string;
|
|
283
306
|
bearerToken?: string;
|
|
284
307
|
approvalCopyGenerator?: ApprovalCopyGenerator;
|
|
308
|
+
approvalMessageTs?: string;
|
|
285
309
|
}): Promise<ApprovalInterceptionResult> {
|
|
286
310
|
const {
|
|
287
311
|
guardianApproval,
|
|
@@ -292,6 +316,7 @@ async function handleCallbackDecision(params: {
|
|
|
292
316
|
assistantId,
|
|
293
317
|
bearerToken,
|
|
294
318
|
approvalCopyGenerator,
|
|
319
|
+
approvalMessageTs,
|
|
295
320
|
} = params;
|
|
296
321
|
|
|
297
322
|
// Access request approvals don't have a pending interaction in the
|
|
@@ -326,10 +351,12 @@ async function handleCallbackDecision(params: {
|
|
|
326
351
|
callbackDecision.action === "approve_always"
|
|
327
352
|
? "approve_once"
|
|
328
353
|
: callbackDecision.action;
|
|
354
|
+
const decisionOutcome: "approved" | "denied" =
|
|
355
|
+
effectiveAction === "reject" ? "denied" : "approved";
|
|
329
356
|
const outcomeText = await composeApprovalMessageGenerative(
|
|
330
357
|
{
|
|
331
358
|
scenario: "guardian_decision_outcome",
|
|
332
|
-
decision:
|
|
359
|
+
decision: decisionOutcome,
|
|
333
360
|
toolName: guardianApproval.toolName,
|
|
334
361
|
channel: sourceChannel,
|
|
335
362
|
},
|
|
@@ -337,15 +364,20 @@ async function handleCallbackDecision(params: {
|
|
|
337
364
|
approvalCopyGenerator,
|
|
338
365
|
);
|
|
339
366
|
try {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
367
|
+
const outcomePayload: Parameters<typeof deliverChannelReply>[1] = {
|
|
368
|
+
chatId: guardianApproval.requesterChatId,
|
|
369
|
+
text: outcomeText,
|
|
370
|
+
assistantId,
|
|
371
|
+
};
|
|
372
|
+
const requesterEphemeral = slackEphemeralUserId(
|
|
373
|
+
sourceChannel,
|
|
374
|
+
guardianApproval.requesterExternalUserId,
|
|
348
375
|
);
|
|
376
|
+
if (requesterEphemeral) {
|
|
377
|
+
outcomePayload.ephemeral = true;
|
|
378
|
+
outcomePayload.user = requesterEphemeral;
|
|
379
|
+
}
|
|
380
|
+
await deliverChannelReply(replyCallbackUrl, outcomePayload, bearerToken);
|
|
349
381
|
} catch (err) {
|
|
350
382
|
log.error(
|
|
351
383
|
{ err, conversationId: guardianApproval.conversationId },
|
|
@@ -353,12 +385,71 @@ async function handleCallbackDecision(params: {
|
|
|
353
385
|
);
|
|
354
386
|
}
|
|
355
387
|
|
|
388
|
+
// Edit the original Slack approval message to show the decision and
|
|
389
|
+
// remove stale action buttons. This prevents users from clicking
|
|
390
|
+
// buttons that have already been resolved.
|
|
391
|
+
if (sourceChannel === "slack" && approvalMessageTs) {
|
|
392
|
+
editSlackApprovalMessage({
|
|
393
|
+
replyCallbackUrl,
|
|
394
|
+
chatId: guardianApproval.guardianChatId,
|
|
395
|
+
messageTs: approvalMessageTs,
|
|
396
|
+
decision: decisionOutcome,
|
|
397
|
+
assistantId,
|
|
398
|
+
bearerToken,
|
|
399
|
+
conversationId: guardianApproval.conversationId,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
356
403
|
// Post-decision delivery is handled by the onEvent callback
|
|
357
404
|
// in the session that registered the pending interaction.
|
|
358
405
|
return { handled: true, type: "guardian_decision_applied" };
|
|
359
406
|
}
|
|
360
407
|
|
|
361
408
|
// Race condition: callback arrived after request was already resolved.
|
|
409
|
+
// On Slack, edit the original message to show it's resolved and remove
|
|
410
|
+
// stale buttons so the guardian isn't left with actionable UI that does
|
|
411
|
+
// nothing. Also send an ephemeral error message for visibility.
|
|
412
|
+
if (sourceChannel === "slack" && approvalMessageTs) {
|
|
413
|
+
// Re-read the approval from DB to get the actual resolved status.
|
|
414
|
+
// The in-memory `guardianApproval` was loaded via a pending-status
|
|
415
|
+
// filter and is still "pending" even though it was resolved by
|
|
416
|
+
// another process.
|
|
417
|
+
const refreshed = getApprovalRequestById(guardianApproval.id);
|
|
418
|
+
const resolvedStatus =
|
|
419
|
+
refreshed?.status === "approved" ? "approved" : "denied";
|
|
420
|
+
editSlackApprovalMessage({
|
|
421
|
+
replyCallbackUrl,
|
|
422
|
+
chatId: guardianApproval.guardianChatId,
|
|
423
|
+
messageTs: approvalMessageTs,
|
|
424
|
+
decision: resolvedStatus,
|
|
425
|
+
assistantId,
|
|
426
|
+
bearerToken,
|
|
427
|
+
conversationId: guardianApproval.conversationId,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Deliver a visible ephemeral error so the user sees feedback (JARVIS-299).
|
|
432
|
+
if (sourceChannel === "slack") {
|
|
433
|
+
try {
|
|
434
|
+
await deliverChannelReply(
|
|
435
|
+
replyCallbackUrl,
|
|
436
|
+
{
|
|
437
|
+
chatId: guardianApproval.guardianChatId,
|
|
438
|
+
text: "This approval request has already been resolved.",
|
|
439
|
+
assistantId,
|
|
440
|
+
ephemeral: true,
|
|
441
|
+
user: actorExternalId,
|
|
442
|
+
},
|
|
443
|
+
bearerToken,
|
|
444
|
+
);
|
|
445
|
+
} catch (err) {
|
|
446
|
+
log.error(
|
|
447
|
+
{ err, conversationId: guardianApproval.conversationId },
|
|
448
|
+
"Failed to deliver stale approval ephemeral notice",
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
362
453
|
return { handled: true, type: "stale_ignored" };
|
|
363
454
|
}
|
|
364
455
|
|
|
@@ -415,13 +506,22 @@ async function handleConversationalDecision(params: {
|
|
|
415
506
|
if (engineResult.disposition === "keep_pending") {
|
|
416
507
|
// Non-decision follow-up (clarification, disambiguation, etc.)
|
|
417
508
|
try {
|
|
509
|
+
const keepPendingPayload: Parameters<typeof deliverChannelReply>[1] = {
|
|
510
|
+
chatId: conversationExternalId,
|
|
511
|
+
text: engineResult.replyText,
|
|
512
|
+
assistantId,
|
|
513
|
+
};
|
|
514
|
+
const guardianEphemeral = slackEphemeralUserId(
|
|
515
|
+
sourceChannel,
|
|
516
|
+
actorExternalId,
|
|
517
|
+
);
|
|
518
|
+
if (guardianEphemeral) {
|
|
519
|
+
keepPendingPayload.ephemeral = true;
|
|
520
|
+
keepPendingPayload.user = guardianEphemeral;
|
|
521
|
+
}
|
|
418
522
|
await deliverChannelReply(
|
|
419
523
|
replyCallbackUrl,
|
|
420
|
-
|
|
421
|
-
chatId: conversationExternalId,
|
|
422
|
-
text: engineResult.replyText,
|
|
423
|
-
assistantId,
|
|
424
|
-
},
|
|
524
|
+
keepPendingPayload,
|
|
425
525
|
bearerToken,
|
|
426
526
|
);
|
|
427
527
|
} catch (err) {
|
|
@@ -481,6 +581,7 @@ async function handleConversationalDecision(params: {
|
|
|
481
581
|
errorLogMessage:
|
|
482
582
|
"Failed to deliver guardian identity mismatch notice for engine target",
|
|
483
583
|
errorLogContext: { conversationExternalId },
|
|
584
|
+
ephemeralUserId: slackEphemeralUserId(sourceChannel, actorExternalId),
|
|
484
585
|
});
|
|
485
586
|
return { handled: true, type: "guardian_decision_applied" };
|
|
486
587
|
}
|
|
@@ -528,13 +629,23 @@ async function handleConversationalDecision(params: {
|
|
|
528
629
|
approvalCopyGenerator,
|
|
529
630
|
);
|
|
530
631
|
try {
|
|
531
|
-
|
|
532
|
-
replyCallbackUrl,
|
|
632
|
+
const requesterOutcomePayload: Parameters<typeof deliverChannelReply>[1] =
|
|
533
633
|
{
|
|
534
634
|
chatId: targetApproval.requesterChatId,
|
|
535
635
|
text: outcomeText,
|
|
536
636
|
assistantId,
|
|
537
|
-
}
|
|
637
|
+
};
|
|
638
|
+
const requesterEphemeral = slackEphemeralUserId(
|
|
639
|
+
sourceChannel,
|
|
640
|
+
targetApproval.requesterExternalUserId,
|
|
641
|
+
);
|
|
642
|
+
if (requesterEphemeral) {
|
|
643
|
+
requesterOutcomePayload.ephemeral = true;
|
|
644
|
+
requesterOutcomePayload.user = requesterEphemeral;
|
|
645
|
+
}
|
|
646
|
+
await deliverChannelReply(
|
|
647
|
+
replyCallbackUrl,
|
|
648
|
+
requesterOutcomePayload,
|
|
538
649
|
bearerToken,
|
|
539
650
|
);
|
|
540
651
|
} catch (err) {
|
|
@@ -546,13 +657,22 @@ async function handleConversationalDecision(params: {
|
|
|
546
657
|
|
|
547
658
|
// Deliver the engine's reply to the guardian
|
|
548
659
|
try {
|
|
660
|
+
const guardianReplyPayload: Parameters<typeof deliverChannelReply>[1] = {
|
|
661
|
+
chatId: conversationExternalId,
|
|
662
|
+
text: engineResult.replyText,
|
|
663
|
+
assistantId,
|
|
664
|
+
};
|
|
665
|
+
const guardianEphemeral = slackEphemeralUserId(
|
|
666
|
+
sourceChannel,
|
|
667
|
+
actorExternalId,
|
|
668
|
+
);
|
|
669
|
+
if (guardianEphemeral) {
|
|
670
|
+
guardianReplyPayload.ephemeral = true;
|
|
671
|
+
guardianReplyPayload.user = guardianEphemeral;
|
|
672
|
+
}
|
|
549
673
|
await deliverChannelReply(
|
|
550
674
|
replyCallbackUrl,
|
|
551
|
-
|
|
552
|
-
chatId: conversationExternalId,
|
|
553
|
-
text: engineResult.replyText,
|
|
554
|
-
assistantId,
|
|
555
|
-
},
|
|
675
|
+
guardianReplyPayload,
|
|
556
676
|
bearerToken,
|
|
557
677
|
);
|
|
558
678
|
} catch (err) {
|
|
@@ -578,11 +698,80 @@ async function handleConversationalDecision(params: {
|
|
|
578
698
|
logger: log,
|
|
579
699
|
errorLogMessage: "Failed to deliver stale guardian approval notice",
|
|
580
700
|
errorLogContext: { conversationId: targetApproval.conversationId },
|
|
701
|
+
ephemeralUserId: slackEphemeralUserId(sourceChannel, actorExternalId),
|
|
581
702
|
});
|
|
582
703
|
|
|
583
704
|
return { handled: true, type: "stale_ignored" };
|
|
584
705
|
}
|
|
585
706
|
|
|
707
|
+
// ---------------------------------------------------------------------------
|
|
708
|
+
// Slack approval message edit helper
|
|
709
|
+
// ---------------------------------------------------------------------------
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Fire-and-forget: edit the original Slack approval message to show the
|
|
713
|
+
* decision outcome and remove stale action buttons. Uses `chat.update` via
|
|
714
|
+
* the gateway deliver endpoint with `messageTs`.
|
|
715
|
+
*
|
|
716
|
+
* The status line replaces the inline buttons so users see the result
|
|
717
|
+
* inline without any actionable UI remaining.
|
|
718
|
+
*/
|
|
719
|
+
function editSlackApprovalMessage(params: {
|
|
720
|
+
replyCallbackUrl: string;
|
|
721
|
+
chatId: string;
|
|
722
|
+
messageTs: string;
|
|
723
|
+
decision: "approved" | "denied";
|
|
724
|
+
assistantId: string;
|
|
725
|
+
bearerToken?: string;
|
|
726
|
+
conversationId: string;
|
|
727
|
+
}): void {
|
|
728
|
+
const {
|
|
729
|
+
replyCallbackUrl,
|
|
730
|
+
chatId,
|
|
731
|
+
messageTs,
|
|
732
|
+
decision,
|
|
733
|
+
assistantId,
|
|
734
|
+
bearerToken,
|
|
735
|
+
conversationId,
|
|
736
|
+
} = params;
|
|
737
|
+
|
|
738
|
+
const statusEmoji = decision === "approved" ? "\u2713" : "\u2717";
|
|
739
|
+
const statusLabel = decision === "approved" ? "Approved" : "Denied";
|
|
740
|
+
const statusText = `${statusEmoji} ${statusLabel}`;
|
|
741
|
+
|
|
742
|
+
// Build Block Kit blocks matching the resolved approval layout:
|
|
743
|
+
// a section with the status text and a context line with the decision.
|
|
744
|
+
// This replaces the original approval prompt's action buttons with a
|
|
745
|
+
// read-only status display.
|
|
746
|
+
const blocks = [
|
|
747
|
+
{
|
|
748
|
+
type: "section",
|
|
749
|
+
text: { type: "mrkdwn", text: statusText },
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
type: "context",
|
|
753
|
+
elements: [{ type: "mrkdwn", text: `${statusEmoji} ${statusLabel}` }],
|
|
754
|
+
},
|
|
755
|
+
];
|
|
756
|
+
|
|
757
|
+
deliverChannelReply(
|
|
758
|
+
replyCallbackUrl,
|
|
759
|
+
{
|
|
760
|
+
chatId,
|
|
761
|
+
text: statusText,
|
|
762
|
+
blocks,
|
|
763
|
+
messageTs,
|
|
764
|
+
assistantId,
|
|
765
|
+
},
|
|
766
|
+
bearerToken,
|
|
767
|
+
).catch((err) => {
|
|
768
|
+
log.error(
|
|
769
|
+
{ err, conversationId, messageTs },
|
|
770
|
+
"Failed to edit Slack approval message after resolution",
|
|
771
|
+
);
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
|
|
586
775
|
// ---------------------------------------------------------------------------
|
|
587
776
|
// Access request decision helper
|
|
588
777
|
// ---------------------------------------------------------------------------
|
|
@@ -22,6 +22,17 @@ import { deliverStaleApprovalReply } from "../guardian-approval-reply-helpers.js
|
|
|
22
22
|
|
|
23
23
|
const log = getLogger("runtime-http");
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the Slack ephemeral user ID when the source channel is Slack.
|
|
27
|
+
* Returns `undefined` for non-Slack channels.
|
|
28
|
+
*/
|
|
29
|
+
function slackEphemeralUserId(
|
|
30
|
+
sourceChannel: ChannelId,
|
|
31
|
+
userId: string | undefined,
|
|
32
|
+
): string | undefined {
|
|
33
|
+
return sourceChannel === "slack" && userId ? userId : undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
25
36
|
export interface TextEngineDecisionParams {
|
|
26
37
|
conversationId: string;
|
|
27
38
|
conversationExternalId: string;
|
|
@@ -36,6 +47,8 @@ export interface TextEngineDecisionParams {
|
|
|
36
47
|
pending: Array<{ requestId: string; toolName: string }>;
|
|
37
48
|
/** Allowed actions from the pending prompt. */
|
|
38
49
|
allowedActions: string[];
|
|
50
|
+
/** External user ID of the actor (for Slack ephemeral routing). */
|
|
51
|
+
actorExternalId?: string;
|
|
39
52
|
}
|
|
40
53
|
|
|
41
54
|
/**
|
|
@@ -58,6 +71,7 @@ export async function handleGuardianTextEngineDecision(
|
|
|
58
71
|
approvalConversationGenerator,
|
|
59
72
|
pending,
|
|
60
73
|
allowedActions,
|
|
74
|
+
actorExternalId,
|
|
61
75
|
} = params;
|
|
62
76
|
|
|
63
77
|
const engineContext: ApprovalConversationContext = {
|
|
@@ -79,13 +93,19 @@ export async function handleGuardianTextEngineDecision(
|
|
|
79
93
|
if (engineResult.disposition === "keep_pending") {
|
|
80
94
|
// Non-decision follow-up — deliver the engine's reply and keep the request pending
|
|
81
95
|
try {
|
|
96
|
+
const keepPendingPayload: Parameters<typeof deliverChannelReply>[1] = {
|
|
97
|
+
chatId: conversationExternalId,
|
|
98
|
+
text: engineResult.replyText,
|
|
99
|
+
assistantId,
|
|
100
|
+
};
|
|
101
|
+
const ephemeral = slackEphemeralUserId(sourceChannel, actorExternalId);
|
|
102
|
+
if (ephemeral) {
|
|
103
|
+
keepPendingPayload.ephemeral = true;
|
|
104
|
+
keepPendingPayload.user = ephemeral;
|
|
105
|
+
}
|
|
82
106
|
await deliverChannelReply(
|
|
83
107
|
replyCallbackUrl,
|
|
84
|
-
|
|
85
|
-
chatId: conversationExternalId,
|
|
86
|
-
text: engineResult.replyText,
|
|
87
|
-
assistantId,
|
|
88
|
-
},
|
|
108
|
+
keepPendingPayload,
|
|
89
109
|
bearerToken,
|
|
90
110
|
);
|
|
91
111
|
} catch (err) {
|
|
@@ -112,15 +132,17 @@ export async function handleGuardianTextEngineDecision(
|
|
|
112
132
|
if (result.applied) {
|
|
113
133
|
// Deliver the engine's reply text to the user
|
|
114
134
|
try {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
135
|
+
const decisionPayload: Parameters<typeof deliverChannelReply>[1] = {
|
|
136
|
+
chatId: conversationExternalId,
|
|
137
|
+
text: engineResult.replyText,
|
|
138
|
+
assistantId,
|
|
139
|
+
};
|
|
140
|
+
const ephemeral = slackEphemeralUserId(sourceChannel, actorExternalId);
|
|
141
|
+
if (ephemeral) {
|
|
142
|
+
decisionPayload.ephemeral = true;
|
|
143
|
+
decisionPayload.user = ephemeral;
|
|
144
|
+
}
|
|
145
|
+
await deliverChannelReply(replyCallbackUrl, decisionPayload, bearerToken);
|
|
124
146
|
} catch (err) {
|
|
125
147
|
log.error(
|
|
126
148
|
{ err, conversationId },
|
|
@@ -145,6 +167,7 @@ export async function handleGuardianTextEngineDecision(
|
|
|
145
167
|
logger: log,
|
|
146
168
|
errorLogMessage: "Failed to deliver stale approval notice",
|
|
147
169
|
errorLogContext: { conversationId },
|
|
170
|
+
ephemeralUserId: slackEphemeralUserId(sourceChannel, actorExternalId),
|
|
148
171
|
});
|
|
149
172
|
|
|
150
173
|
return { handled: true, type: "stale_ignored" };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP route handler for serving synthesized TTS audio.
|
|
3
|
+
*
|
|
4
|
+
* GET /v1/audio/:audioId — retrieve a previously stored audio segment.
|
|
5
|
+
*
|
|
6
|
+
* This endpoint is unauthenticated because Twilio fetches audio URLs
|
|
7
|
+
* directly; the audioId itself is an unguessable UUID that acts as a
|
|
8
|
+
* capability token.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { getAudio } from "../../calls/audio-store.js";
|
|
12
|
+
import { httpError } from "../http-errors.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Handle GET /v1/audio/:audioId.
|
|
16
|
+
*
|
|
17
|
+
* Returns the audio with its stored Content-Type. For complete audio,
|
|
18
|
+
* includes Content-Length for efficient playback. For in-progress
|
|
19
|
+
* streaming entries, uses chunked transfer encoding.
|
|
20
|
+
*/
|
|
21
|
+
export function handleGetAudio(audioId: string): Response {
|
|
22
|
+
const entry = getAudio(audioId);
|
|
23
|
+
if (!entry) {
|
|
24
|
+
return httpError("NOT_FOUND", "Audio not found", 404);
|
|
25
|
+
}
|
|
26
|
+
if (entry.type === "buffer") {
|
|
27
|
+
return new Response(new Uint8Array(entry.buffer), {
|
|
28
|
+
status: 200,
|
|
29
|
+
headers: {
|
|
30
|
+
"Content-Type": entry.contentType,
|
|
31
|
+
"Content-Length": entry.buffer.length.toString(),
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// Streaming — Content-Length unknown, chunked transfer encoding
|
|
36
|
+
return new Response(entry.stream, {
|
|
37
|
+
status: 200,
|
|
38
|
+
headers: { "Content-Type": entry.contentType },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
import { existsSync, readFileSync } from "node:fs";
|
|
16
16
|
|
|
17
17
|
import { getConversationByKey } from "../../memory/conversation-key-store.js";
|
|
18
|
-
import { checkIngressForSecrets } from "../../security/secret-ingress.js";
|
|
19
18
|
import { getLogger } from "../../util/logger.js";
|
|
20
19
|
import { getWorkspacePromptPath } from "../../util/platform.js";
|
|
21
20
|
import type { AuthContext } from "../auth/types.js";
|
|
@@ -89,22 +88,6 @@ async function handleBtw(
|
|
|
89
88
|
}
|
|
90
89
|
|
|
91
90
|
const trimmedContent = content.trim();
|
|
92
|
-
const ingressCheck = checkIngressForSecrets(trimmedContent);
|
|
93
|
-
if (ingressCheck.blocked) {
|
|
94
|
-
log.warn(
|
|
95
|
-
{ detectedTypes: ingressCheck.detectedTypes },
|
|
96
|
-
"Blocked /v1/btw message containing secrets",
|
|
97
|
-
);
|
|
98
|
-
return Response.json(
|
|
99
|
-
{
|
|
100
|
-
accepted: false,
|
|
101
|
-
error: "secret_blocked",
|
|
102
|
-
message: ingressCheck.userNotice,
|
|
103
|
-
detectedTypes: ingressCheck.detectedTypes,
|
|
104
|
-
},
|
|
105
|
-
{ status: 422 },
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
91
|
|
|
109
92
|
// ----- Identity intro fast-path -----
|
|
110
93
|
// When the client requests the identity intro, check SOUL.md first (persisted
|
|
@@ -63,10 +63,15 @@ export async function handleGetChannelReadiness(url: URL): Promise<Response> {
|
|
|
63
63
|
export async function handleRefreshChannelReadiness(
|
|
64
64
|
req: Request,
|
|
65
65
|
): Promise<Response> {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
66
|
+
let body: { channel?: ChannelId; includeRemote?: boolean };
|
|
67
|
+
try {
|
|
68
|
+
body = (await req.json()) as typeof body;
|
|
69
|
+
} catch {
|
|
70
|
+
return Response.json(
|
|
71
|
+
{ success: false, error: "Invalid JSON in request body" },
|
|
72
|
+
{ status: 400 },
|
|
73
|
+
);
|
|
74
|
+
}
|
|
70
75
|
|
|
71
76
|
const service = getReadinessService();
|
|
72
77
|
|
|
@@ -12,13 +12,20 @@
|
|
|
12
12
|
* PUT /v1/config/embeddings — set embedding provider/model
|
|
13
13
|
* GET /v1/config/permissions/skip — dangerouslySkipPermissions status
|
|
14
14
|
* PUT /v1/config/permissions/skip — toggle dangerouslySkipPermissions
|
|
15
|
+
* GET /v1/config — full raw workspace config
|
|
16
|
+
* PATCH /v1/config — deep-merge partial config
|
|
15
17
|
* GET /v1/conversations/search — search conversations
|
|
16
18
|
* GET /v1/messages/:id/content — full message content
|
|
17
19
|
* GET /v1/messages/:id/llm-context — LLM request logs for a message
|
|
18
20
|
* DELETE /v1/messages/queued/:id — delete queued message
|
|
19
21
|
*/
|
|
20
22
|
|
|
21
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
deepMergeOverwrite,
|
|
25
|
+
getConfig,
|
|
26
|
+
loadRawConfig,
|
|
27
|
+
saveRawConfig,
|
|
28
|
+
} from "../../config/loader.js";
|
|
22
29
|
import { VALID_MEMORY_EMBEDDING_PROVIDERS } from "../../config/schemas/memory-storage.js";
|
|
23
30
|
import { VALID_INFERENCE_PROVIDERS } from "../../config/schemas/services.js";
|
|
24
31
|
import {
|
|
@@ -292,6 +299,61 @@ export function conversationQueryRouteDefinitions(
|
|
|
292
299
|
},
|
|
293
300
|
},
|
|
294
301
|
|
|
302
|
+
// ── Full config read ─────────────────────────────────────────────
|
|
303
|
+
{
|
|
304
|
+
endpoint: "config",
|
|
305
|
+
method: "GET",
|
|
306
|
+
policyKey: "config",
|
|
307
|
+
handler: () => {
|
|
308
|
+
try {
|
|
309
|
+
const raw = loadRawConfig();
|
|
310
|
+
return Response.json(raw);
|
|
311
|
+
} catch (err) {
|
|
312
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
313
|
+
return httpError(
|
|
314
|
+
"INTERNAL_ERROR",
|
|
315
|
+
`Failed to read config: ${message}`,
|
|
316
|
+
500,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
// ── Generic config patch ──────────────────────────────────────────
|
|
323
|
+
{
|
|
324
|
+
endpoint: "config",
|
|
325
|
+
method: "PATCH",
|
|
326
|
+
policyKey: "config",
|
|
327
|
+
handler: async ({ req }) => {
|
|
328
|
+
const body = (await req.json()) as Record<string, unknown>;
|
|
329
|
+
if (
|
|
330
|
+
body == null ||
|
|
331
|
+
typeof body !== "object" ||
|
|
332
|
+
Array.isArray(body) ||
|
|
333
|
+
Object.keys(body).length === 0
|
|
334
|
+
) {
|
|
335
|
+
return httpError(
|
|
336
|
+
"BAD_REQUEST",
|
|
337
|
+
"Body must be a non-empty JSON object",
|
|
338
|
+
400,
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
const raw = loadRawConfig();
|
|
343
|
+
deepMergeOverwrite(raw, body);
|
|
344
|
+
saveRawConfig(raw);
|
|
345
|
+
return Response.json({ ok: true });
|
|
346
|
+
} catch (err) {
|
|
347
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
348
|
+
return httpError(
|
|
349
|
+
"INTERNAL_ERROR",
|
|
350
|
+
`Failed to patch config: ${message}`,
|
|
351
|
+
500,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
|
|
295
357
|
// ── Conversation search ───────────────────────────────────────────
|
|
296
358
|
{
|
|
297
359
|
endpoint: "conversations/search",
|