@vellumai/assistant 0.4.52 → 0.4.54
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/ARCHITECTURE.md +2 -2
- package/bun.lock +62 -349
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/keychain-broker.md +91 -40
- package/docs/architecture/memory.md +3 -3
- package/docs/architecture/security.md +2 -2
- package/knip.json +7 -29
- package/package.json +2 -9
- package/src/__tests__/agent-loop.test.ts +1 -1
- package/src/__tests__/app-git-history.test.ts +0 -2
- package/src/__tests__/app-git-service.test.ts +1 -6
- package/src/__tests__/approval-cascade.test.ts +3 -2
- package/src/__tests__/approval-routes-http.test.ts +0 -1
- package/src/__tests__/asset-materialize-tool.test.ts +0 -1
- package/src/__tests__/asset-search-tool.test.ts +0 -1
- package/src/__tests__/assistant-events-sse-hardening.test.ts +0 -1
- package/src/__tests__/attachments-store.test.ts +0 -1
- package/src/__tests__/avatar-e2e.test.ts +5 -1
- package/src/__tests__/browser-fill-credential.test.ts +4 -6
- package/src/__tests__/btw-routes.test.ts +39 -0
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-domain.test.ts +1 -1
- package/src/__tests__/call-routes-http.test.ts +1 -3
- package/src/__tests__/canonical-guardian-store.test.ts +33 -2
- package/src/__tests__/channel-guardian.test.ts +4 -4
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +1 -1
- package/src/__tests__/checker.test.ts +13 -11
- package/src/__tests__/claude-code-skill-regression.test.ts +5 -2
- package/src/__tests__/claude-code-tool-profiles.test.ts +7 -3
- package/src/__tests__/config-loader-backfill.test.ts +1 -5
- package/src/__tests__/config-schema.test.ts +9 -46
- package/src/__tests__/config-watcher.test.ts +11 -3
- package/src/__tests__/conversation-routes-slash-commands.test.ts +0 -1
- package/src/__tests__/credential-broker-browser-fill.test.ts +27 -24
- package/src/__tests__/credential-broker-server-use.test.ts +76 -40
- package/src/__tests__/credential-security-e2e.test.ts +1 -6
- package/src/__tests__/credential-security-invariants.test.ts +27 -8
- package/src/__tests__/credential-vault-unit.test.ts +32 -16
- package/src/__tests__/credential-vault.test.ts +40 -28
- package/src/__tests__/credentials-cli.test.ts +1 -21
- package/src/__tests__/email-invite-adapter.test.ts +0 -1
- package/src/__tests__/error-handler-friendly-messages.test.ts +4 -5
- package/src/__tests__/fixtures/credential-security-fixtures.ts +3 -3
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -79
- package/src/__tests__/gateway-only-enforcement.test.ts +1 -23
- package/src/__tests__/guardian-action-conversation-turn.test.ts +8 -8
- package/src/__tests__/guardian-action-late-reply.test.ts +13 -14
- package/src/__tests__/guardian-action-store.test.ts +0 -57
- package/src/__tests__/guardian-outbound-http.test.ts +1 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -3
- package/src/__tests__/hooks-blocking.test.ts +1 -1
- package/src/__tests__/hooks-config.test.ts +5 -29
- package/src/__tests__/hooks-discovery.test.ts +1 -1
- package/src/__tests__/hooks-integration.test.ts +1 -1
- package/src/__tests__/hooks-manager.test.ts +1 -1
- package/src/__tests__/hooks-runner.test.ts +1 -23
- package/src/__tests__/hooks-settings.test.ts +1 -1
- package/src/__tests__/hooks-templates.test.ts +1 -1
- package/src/__tests__/host-shell-tool.test.ts +0 -1
- package/src/__tests__/http-user-message-parity.test.ts +19 -0
- package/src/__tests__/integration-status.test.ts +0 -1
- package/src/__tests__/invite-routes-http.test.ts +0 -3
- package/src/__tests__/list-messages-attachments.test.ts +0 -1
- package/src/__tests__/llm-usage-store.test.ts +50 -0
- package/src/__tests__/log-export-workspace.test.ts +233 -0
- package/src/__tests__/managed-proxy-context.test.ts +41 -41
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
- package/src/__tests__/media-generate-image.test.ts +9 -4
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -7
- package/src/__tests__/memory-regressions.experimental.test.ts +4 -4
- package/src/__tests__/memory-regressions.test.ts +27 -28
- package/src/__tests__/memory-retrieval.benchmark.test.ts +1 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -4
- package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1
- package/src/__tests__/migration-export-http.test.ts +0 -1
- package/src/__tests__/migration-import-commit-http.test.ts +0 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +0 -1
- package/src/__tests__/migration-validate-http.test.ts +0 -1
- package/src/__tests__/notification-decision-fallback.test.ts +1 -1
- package/src/__tests__/notification-schedule-dedup.test.ts +237 -0
- package/src/__tests__/oauth-cli.test.ts +2 -14
- package/src/__tests__/oauth-store.test.ts +3 -7
- package/src/__tests__/oauth2-gateway-transport.test.ts +5 -4
- package/src/__tests__/onboarding-starter-tasks.test.ts +1 -1
- package/src/__tests__/onboarding-template-contract.test.ts +1 -2
- package/src/__tests__/openai-provider.test.ts +7 -7
- package/src/__tests__/platform.test.ts +14 -4
- package/src/__tests__/pricing.test.ts +0 -234
- package/src/__tests__/provider-commit-message-generator.test.ts +19 -15
- package/src/__tests__/provider-fail-open-selection.test.ts +67 -62
- package/src/__tests__/provider-managed-proxy-integration.test.ts +88 -85
- package/src/__tests__/provider-registry-ollama.test.ts +10 -4
- package/src/__tests__/public-ingress-urls.test.ts +1 -1
- package/src/__tests__/recording-handler.test.ts +0 -1
- package/src/__tests__/registry.test.ts +3 -103
- package/src/__tests__/relay-server.test.ts +0 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +0 -1
- package/src/__tests__/runtime-events-sse.test.ts +0 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +2 -7
- package/src/__tests__/secret-onetime-send.test.ts +1 -6
- package/src/__tests__/secret-routes-managed-proxy.test.ts +6 -14
- package/src/__tests__/secret-scanner-executor.test.ts +0 -1
- package/src/__tests__/secure-keys.test.ts +241 -229
- package/src/__tests__/send-endpoint-busy.test.ts +0 -1
- package/src/__tests__/session-abort-tool-results.test.ts +3 -2
- package/src/__tests__/session-agent-loop-overflow.test.ts +1012 -838
- package/src/__tests__/session-agent-loop.test.ts +2 -2
- package/src/__tests__/session-confirmation-signals.test.ts +3 -2
- package/src/__tests__/session-error.test.ts +5 -4
- package/src/__tests__/session-history-web-search.test.ts +34 -9
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -7
- package/src/__tests__/session-pre-run-repair.test.ts +3 -2
- package/src/__tests__/session-provider-retry-repair.test.ts +31 -27
- package/src/__tests__/session-queue.test.ts +5 -5
- package/src/__tests__/session-runtime-assembly.test.ts +118 -0
- package/src/__tests__/session-slash-known.test.ts +31 -14
- package/src/__tests__/session-slash-queue.test.ts +3 -2
- package/src/__tests__/session-slash-unknown.test.ts +3 -2
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -1
- package/src/__tests__/session-workspace-injection.test.ts +3 -2
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -2
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
- package/src/__tests__/skillssh-registry.test.ts +21 -0
- package/src/__tests__/slack-channel-config.test.ts +1 -7
- package/src/__tests__/slack-share-routes.test.ts +1 -1
- package/src/__tests__/swarm-recursion.test.ts +4 -1
- package/src/__tests__/swarm-session-integration.test.ts +24 -14
- package/src/__tests__/swarm-tool.test.ts +4 -2
- package/src/__tests__/task-compiler.test.ts +1 -1
- package/src/__tests__/telegram-bot-username-resolution.test.ts +2 -4
- package/src/__tests__/test-support/browser-skill-harness.ts +0 -18
- package/src/__tests__/test-support/computer-use-skill-harness.ts +0 -23
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1521 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +1 -2
- package/src/__tests__/trust-store.test.ts +8 -83
- package/src/__tests__/twilio-config.test.ts +0 -1
- package/src/__tests__/twilio-provider.test.ts +0 -5
- package/src/__tests__/twilio-routes.test.ts +2 -3
- package/src/__tests__/usage-cache-backfill-migration.test.ts +10 -10
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
- package/src/__tests__/voice-quality.test.ts +2 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
- package/src/__tests__/web-search.test.ts +1 -1
- package/src/agent/loop.ts +17 -1
- package/src/bundler/app-bundler.ts +40 -24
- package/src/calls/call-controller.ts +16 -0
- package/src/calls/guardian-question-copy.ts +1 -1
- package/src/calls/relay-server.ts +29 -13
- package/src/calls/voice-control-protocol.ts +1 -0
- package/src/calls/voice-quality.ts +1 -1
- package/src/calls/voice-session-bridge.ts +9 -3
- package/src/channels/types.ts +16 -0
- package/src/cli/commands/bash.ts +173 -0
- package/src/cli/commands/doctor.ts +15 -57
- package/src/cli/commands/memory.ts +3 -5
- package/src/cli/commands/oauth/connections.ts +4 -2
- package/src/cli/commands/oauth/providers.ts +1 -13
- package/src/cli/commands/sessions.ts +1 -1
- package/src/cli/commands/usage.ts +359 -0
- package/src/cli/http-client.ts +22 -12
- package/src/cli/program.ts +4 -0
- package/src/cli/reference.ts +2 -0
- package/src/cli.ts +251 -181
- package/src/config/assistant-feature-flags.ts +0 -7
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +1 -1
- package/src/config/bundled-skills/claude-code/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +0 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +4 -3
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +3 -5
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -3
- package/src/config/bundled-skills/messaging/SKILL.md +0 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +1 -1
- package/src/config/bundled-skills/sequences/SKILL.md +0 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -6
- package/src/config/env.ts +13 -0
- package/src/config/feature-flag-registry.json +15 -39
- package/src/config/loader.ts +7 -135
- package/src/config/schema.ts +0 -6
- package/src/config/schemas/channels.ts +1 -0
- package/src/config/schemas/elevenlabs.ts +2 -2
- package/src/config/schemas/security.ts +1 -2
- package/src/config/skills.ts +1 -1
- package/src/contacts/contact-store.ts +21 -75
- package/src/contacts/contacts-write.ts +6 -6
- package/src/contacts/types.ts +2 -0
- package/src/context/token-estimator.ts +35 -2
- package/src/context/window-manager.ts +16 -2
- package/src/daemon/approved-devices-store.ts +0 -44
- package/src/daemon/classifier.ts +1 -1
- package/src/daemon/config-watcher.ts +35 -11
- package/src/daemon/context-overflow-reducer.ts +13 -2
- package/src/daemon/handlers/config-ingress.ts +25 -8
- package/src/daemon/handlers/config-model.ts +22 -16
- package/src/daemon/handlers/config-telegram.ts +18 -6
- package/src/daemon/handlers/dictation.ts +0 -429
- package/src/daemon/handlers/sessions.ts +4 -116
- package/src/daemon/handlers/skills.ts +2 -201
- package/src/daemon/lifecycle.ts +21 -20
- package/src/daemon/message-types/contacts.ts +2 -0
- package/src/daemon/message-types/integrations.ts +1 -0
- package/src/daemon/message-types/sessions.ts +2 -0
- package/src/daemon/parse-actual-tokens-from-error.test.ts +75 -0
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +42 -5
- package/src/daemon/session-agent-loop-handlers.ts +1 -1
- package/src/daemon/session-agent-loop.ts +27 -79
- package/src/daemon/session-error.ts +5 -4
- package/src/daemon/session-process.ts +17 -10
- package/src/daemon/session-runtime-assembly.ts +50 -0
- package/src/daemon/session-slash.ts +34 -22
- package/src/daemon/session.ts +1 -0
- package/src/daemon/shutdown-handlers.ts +15 -0
- package/src/daemon/watch-handler.ts +2 -2
- package/src/email/guardrails.ts +1 -1
- package/src/email/service.ts +0 -5
- package/src/events/domain-events.ts +1 -0
- package/src/hooks/templates.ts +1 -1
- package/src/media/app-icon-generator.ts +4 -3
- package/src/media/avatar-router.ts +5 -4
- package/src/media/gemini-image-service.ts +5 -5
- package/src/memory/admin.ts +2 -2
- package/src/memory/app-git-service.ts +0 -7
- package/src/memory/canonical-guardian-store.ts +25 -3
- package/src/memory/conversation-crud.ts +1 -1
- package/src/memory/conversation-title-service.ts +2 -2
- package/src/memory/db-init.ts +12 -0
- package/src/memory/embedding-backend.ts +46 -33
- package/src/memory/external-conversation-store.ts +0 -30
- package/src/memory/guardian-action-store.ts +0 -31
- package/src/memory/guardian-approvals.ts +1 -56
- package/src/memory/indexer.ts +4 -3
- package/src/memory/items-extractor.ts +1 -1
- package/src/memory/job-handlers/backfill.ts +5 -2
- package/src/memory/job-handlers/index-maintenance.ts +2 -2
- package/src/memory/job-handlers/media-processing.ts +2 -2
- package/src/memory/job-handlers/summarization.ts +1 -1
- package/src/memory/job-utils.ts +1 -2
- package/src/memory/jobs-worker.ts +2 -2
- package/src/memory/llm-usage-store.ts +57 -11
- package/src/memory/media-store.ts +4 -535
- package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +2 -2
- package/src/memory/migrations/110-channel-guardian.ts +0 -1
- package/src/memory/migrations/158-channel-interaction-columns.ts +18 -0
- package/src/memory/migrations/159-drop-contact-interaction-columns.ts +16 -0
- package/src/memory/migrations/160-drop-loopback-port-column.ts +13 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/published-pages-store.ts +0 -83
- package/src/memory/qdrant-circuit-breaker.ts +0 -8
- package/src/memory/retriever.test.ts +19 -12
- package/src/memory/retriever.ts +1 -1
- package/src/memory/schema/contacts.ts +2 -2
- package/src/memory/schema/oauth.ts +0 -1
- package/src/memory/search/semantic.ts +1 -8
- package/src/memory/shared-app-links-store.ts +0 -15
- package/src/messaging/registry.ts +0 -5
- package/src/messaging/style-analyzer.ts +1 -1
- package/src/notifications/copy-composer.ts +5 -13
- package/src/notifications/decision-engine.ts +2 -2
- package/src/notifications/deliveries-store.ts +0 -39
- package/src/notifications/guardian-question-mode.ts +6 -10
- package/src/notifications/preference-extractor.ts +1 -1
- package/src/oauth/byo-connection.test.ts +29 -20
- package/src/oauth/connect-orchestrator.ts +5 -3
- package/src/oauth/connect-types.ts +9 -2
- package/src/oauth/manual-token-connection.ts +9 -7
- package/src/oauth/oauth-store.ts +2 -8
- package/src/oauth/provider-behaviors.ts +11 -1
- package/src/oauth/seed-providers.ts +13 -5
- package/src/permissions/checker.ts +21 -2
- package/src/permissions/shell-identity.ts +0 -5
- package/src/permissions/trust-store.ts +0 -37
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +1 -1
- package/src/prompts/system-prompt.ts +5 -14
- package/src/prompts/templates/BOOTSTRAP.md +1 -3
- package/src/providers/anthropic/client.ts +16 -8
- package/src/providers/managed-proxy/constants.ts +9 -11
- package/src/providers/managed-proxy/context.ts +14 -9
- package/src/providers/provider-send-message.ts +4 -52
- package/src/providers/registry.ts +29 -57
- package/src/providers/types.ts +1 -1
- package/src/runtime/actor-token-store.ts +0 -23
- package/src/runtime/auth/route-policy.ts +4 -0
- package/src/runtime/channel-invite-transports/telegram.ts +12 -6
- package/src/runtime/channel-retry-sweep.ts +6 -0
- package/src/runtime/http-router.ts +5 -1
- package/src/runtime/http-server.ts +101 -4
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/invite-instruction-generator.ts +25 -51
- package/src/runtime/invite-service.ts +0 -20
- package/src/runtime/middleware/error-handler.ts +1 -2
- package/src/runtime/routes/app-management-routes.ts +1 -0
- package/src/runtime/routes/attachment-routes.ts +1 -1
- package/src/runtime/routes/brain-graph-routes.ts +1 -1
- package/src/runtime/routes/btw-routes.ts +20 -1
- package/src/runtime/routes/call-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +64 -24
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +2 -2
- package/src/runtime/routes/documents-routes.ts +3 -3
- package/src/runtime/routes/global-search-routes.ts +1 -1
- package/src/runtime/routes/guardian-bootstrap-routes.ts +0 -20
- package/src/runtime/routes/guardian-refresh-routes.ts +0 -20
- package/src/runtime/routes/inbound-message-handler.ts +10 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -0
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +5 -5
- package/src/runtime/routes/integrations/slack/share.ts +5 -5
- package/src/runtime/routes/log-export-routes.ts +122 -10
- package/src/runtime/routes/secret-routes.ts +4 -4
- package/src/runtime/routes/session-query-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +53 -0
- package/src/runtime/routes/trust-rules-routes.ts +1 -1
- package/src/runtime/routes/workspace-routes.ts +3 -0
- package/src/runtime/verification-templates.ts +1 -1
- package/src/security/credential-backend.ts +148 -0
- package/src/security/oauth2.ts +5 -5
- package/src/security/secret-allowlist.ts +1 -1
- package/src/security/secure-keys.ts +98 -160
- package/src/security/token-manager.ts +0 -7
- package/src/sequence/guardrails.ts +0 -4
- package/src/sequence/store.ts +1 -20
- package/src/sequence/types.ts +1 -36
- package/src/signals/bash.ts +157 -0
- package/src/signals/cancel.ts +69 -0
- package/src/signals/conversation-undo.ts +127 -0
- package/src/signals/trust-rule.ts +174 -0
- package/src/skills/clawhub.ts +5 -5
- package/src/skills/managed-store.ts +4 -4
- package/src/skills/skillssh-registry.ts +6 -1
- package/src/swarm/backend-claude-code.ts +6 -6
- package/src/swarm/worker-backend.ts +1 -1
- package/src/swarm/worker-runner.ts +1 -1
- package/src/telegram/bot-username.ts +11 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +366 -0
- package/src/telemetry/usage-telemetry-reporter.ts +181 -0
- package/src/tools/claude-code/claude-code.ts +6 -6
- package/src/tools/credentials/broker.ts +7 -5
- package/src/tools/credentials/vault.ts +11 -6
- package/src/tools/memory/handlers.test.ts +24 -26
- package/src/tools/memory/handlers.ts +1 -13
- package/src/tools/network/__tests__/web-search.test.ts +18 -86
- package/src/tools/network/web-search.ts +9 -15
- package/src/tools/registry.ts +5 -100
- package/src/tools/terminal/parser.ts +34 -4
- package/src/tools/tool-manifest.ts +0 -10
- package/src/usage/actors.ts +0 -12
- package/src/util/canonicalize-identity.ts +0 -9
- package/src/util/errors.ts +0 -3
- package/src/util/platform.ts +31 -8
- package/src/util/pricing.ts +0 -39
- package/src/watcher/constants.ts +0 -7
- package/src/watcher/providers/linear.ts +1 -1
- package/src/work-items/work-item-store.ts +4 -4
- package/src/workspace/commit-message-provider.ts +1 -1
- package/src/workspace/git-service.ts +44 -1
- package/src/workspace/provider-commit-message-generator.ts +11 -7
- package/src/__tests__/fixtures/proxy-fixtures.ts +0 -147
- package/src/browser-extension-relay/client.ts +0 -155
- package/src/contacts/index.ts +0 -18
- package/src/daemon/tls-certs.ts +0 -270
- package/src/errors.ts +0 -41
- package/src/events/index.ts +0 -18
- package/src/followups/index.ts +0 -10
- package/src/playbooks/index.ts +0 -10
- package/src/runtime/auth/index.ts +0 -44
- package/src/tasks/candidate-store.ts +0 -95
- package/src/tools/browser/api-map.ts +0 -313
- package/src/tools/browser/auto-navigate.ts +0 -469
- package/src/tools/browser/headless-browser.ts +0 -590
- package/src/tools/browser/recording-store.ts +0 -75
- package/src/tools/computer-use/registry.ts +0 -21
- package/src/tools/tasks/index.ts +0 -27
|
@@ -26,7 +26,6 @@ import type {
|
|
|
26
26
|
const mockConfig = {
|
|
27
27
|
provider: "anthropic",
|
|
28
28
|
model: "test",
|
|
29
|
-
apiKeys: {},
|
|
30
29
|
maxTokens: 4096,
|
|
31
30
|
dataDir: "/tmp",
|
|
32
31
|
timeouts: {
|
|
@@ -197,7 +196,7 @@ describe("ToolExecutor allowedToolNames gating", () => {
|
|
|
197
196
|
}
|
|
198
197
|
});
|
|
199
198
|
|
|
200
|
-
test("executes normally when allowedToolNames is not set
|
|
199
|
+
test("executes normally when allowedToolNames is not set", async () => {
|
|
201
200
|
const executor = new ToolExecutor(makePrompter());
|
|
202
201
|
const result = await executor.execute(
|
|
203
202
|
"file_read",
|
|
@@ -57,7 +57,6 @@ import {
|
|
|
57
57
|
} from "../permissions/trust-store.js";
|
|
58
58
|
|
|
59
59
|
const trustPath = join(testDir, "protected", "trust.json");
|
|
60
|
-
const legacyTrustPath = join(testDir, "trust.json");
|
|
61
60
|
const DEFAULT_TEMPLATES = getDefaultRuleTemplates();
|
|
62
61
|
const NUM_DEFAULTS = DEFAULT_TEMPLATES.length;
|
|
63
62
|
const DEFAULT_PRIORITY_BY_ID = new Map(
|
|
@@ -73,11 +72,6 @@ describe("Trust Store", () => {
|
|
|
73
72
|
} catch {
|
|
74
73
|
/* may not exist */
|
|
75
74
|
}
|
|
76
|
-
try {
|
|
77
|
-
rmSync(legacyTrustPath);
|
|
78
|
-
} catch {
|
|
79
|
-
/* may not exist */
|
|
80
|
-
}
|
|
81
75
|
});
|
|
82
76
|
|
|
83
77
|
// Intentionally do not remove `testDir` in afterAll.
|
|
@@ -1099,7 +1093,7 @@ describe("Trust Store", () => {
|
|
|
1099
1093
|
|
|
1100
1094
|
// ── default allow: browser tools ────────────────────────────
|
|
1101
1095
|
|
|
1102
|
-
test("all
|
|
1096
|
+
test("all 14 browser tools have default allow rules", () => {
|
|
1103
1097
|
const templates = getDefaultRuleTemplates();
|
|
1104
1098
|
const browserTools = [
|
|
1105
1099
|
"browser_navigate",
|
|
@@ -1109,8 +1103,12 @@ describe("Trust Store", () => {
|
|
|
1109
1103
|
"browser_click",
|
|
1110
1104
|
"browser_type",
|
|
1111
1105
|
"browser_press_key",
|
|
1106
|
+
"browser_scroll",
|
|
1107
|
+
"browser_select_option",
|
|
1108
|
+
"browser_hover",
|
|
1112
1109
|
"browser_wait_for",
|
|
1113
1110
|
"browser_extract",
|
|
1111
|
+
"browser_wait_for_download",
|
|
1114
1112
|
"browser_fill_credential",
|
|
1115
1113
|
];
|
|
1116
1114
|
|
|
@@ -1223,79 +1221,6 @@ describe("Trust Store", () => {
|
|
|
1223
1221
|
// ── loadFromDisk resilience (misc) ──────────────────────────────
|
|
1224
1222
|
|
|
1225
1223
|
describe("loadFromDisk resilience (misc)", () => {
|
|
1226
|
-
test("migrates legacy root trust file to protected path", () => {
|
|
1227
|
-
mkdirSync(dirname(legacyTrustPath), { recursive: true });
|
|
1228
|
-
writeFileSync(
|
|
1229
|
-
legacyTrustPath,
|
|
1230
|
-
JSON.stringify({
|
|
1231
|
-
version: 3,
|
|
1232
|
-
rules: [
|
|
1233
|
-
{
|
|
1234
|
-
id: "legacy-deny",
|
|
1235
|
-
tool: "host_bash",
|
|
1236
|
-
pattern: "rm -rf *",
|
|
1237
|
-
scope: "everywhere",
|
|
1238
|
-
decision: "deny",
|
|
1239
|
-
priority: 200,
|
|
1240
|
-
createdAt: 123,
|
|
1241
|
-
},
|
|
1242
|
-
],
|
|
1243
|
-
}),
|
|
1244
|
-
);
|
|
1245
|
-
|
|
1246
|
-
clearCache();
|
|
1247
|
-
const rules = getAllRules();
|
|
1248
|
-
|
|
1249
|
-
expect(rules.find((r) => r.id === "legacy-deny")).toBeDefined();
|
|
1250
|
-
expect(readFileSync(trustPath, "utf-8")).toContain("legacy-deny");
|
|
1251
|
-
expect(() => readFileSync(legacyTrustPath, "utf-8")).toThrow();
|
|
1252
|
-
});
|
|
1253
|
-
|
|
1254
|
-
test("prefers protected trust file when both protected and legacy files exist", () => {
|
|
1255
|
-
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1256
|
-
writeFileSync(
|
|
1257
|
-
trustPath,
|
|
1258
|
-
JSON.stringify({
|
|
1259
|
-
version: 3,
|
|
1260
|
-
rules: [
|
|
1261
|
-
{
|
|
1262
|
-
id: "protected-rule",
|
|
1263
|
-
tool: "bash",
|
|
1264
|
-
pattern: "protected *",
|
|
1265
|
-
scope: "/tmp",
|
|
1266
|
-
decision: "allow",
|
|
1267
|
-
priority: 100,
|
|
1268
|
-
createdAt: 1,
|
|
1269
|
-
},
|
|
1270
|
-
],
|
|
1271
|
-
}),
|
|
1272
|
-
);
|
|
1273
|
-
writeFileSync(
|
|
1274
|
-
legacyTrustPath,
|
|
1275
|
-
JSON.stringify({
|
|
1276
|
-
version: 3,
|
|
1277
|
-
rules: [
|
|
1278
|
-
{
|
|
1279
|
-
id: "legacy-rule",
|
|
1280
|
-
tool: "bash",
|
|
1281
|
-
pattern: "legacy *",
|
|
1282
|
-
scope: "/tmp",
|
|
1283
|
-
decision: "deny",
|
|
1284
|
-
priority: 200,
|
|
1285
|
-
createdAt: 2,
|
|
1286
|
-
},
|
|
1287
|
-
],
|
|
1288
|
-
}),
|
|
1289
|
-
);
|
|
1290
|
-
|
|
1291
|
-
clearCache();
|
|
1292
|
-
const rules = getAllRules();
|
|
1293
|
-
|
|
1294
|
-
expect(rules.find((r) => r.id === "protected-rule")).toBeDefined();
|
|
1295
|
-
expect(rules.find((r) => r.id === "legacy-rule")).toBeUndefined();
|
|
1296
|
-
expect(readFileSync(legacyTrustPath, "utf-8")).toContain("legacy-rule");
|
|
1297
|
-
});
|
|
1298
|
-
|
|
1299
1224
|
test("malformed file (valid JSON but null) is handled gracefully", () => {
|
|
1300
1225
|
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1301
1226
|
writeFileSync(trustPath, "null");
|
|
@@ -1536,10 +1461,10 @@ describe("Trust Store", () => {
|
|
|
1536
1461
|
});
|
|
1537
1462
|
});
|
|
1538
1463
|
|
|
1539
|
-
// ──
|
|
1464
|
+
// ── optional ctx parameter ────────────────────────────────────
|
|
1540
1465
|
|
|
1541
|
-
describe("
|
|
1542
|
-
test("
|
|
1466
|
+
describe("optional ctx parameter", () => {
|
|
1467
|
+
test("callers without ctx parameter still work", () => {
|
|
1543
1468
|
addRule("bash", "git *", "/tmp", "allow", 200);
|
|
1544
1469
|
// Calling without the 4th argument — must still match
|
|
1545
1470
|
const match = findHighestPriorityRule("bash", ["git status"], "/tmp");
|
|
@@ -41,11 +41,6 @@ mock.module("../config/loader.js", () => ({
|
|
|
41
41
|
}));
|
|
42
42
|
|
|
43
43
|
mock.module("../security/secure-keys.js", () => ({
|
|
44
|
-
getSecureKey: (key: string) => {
|
|
45
|
-
if (key === credentialKey("twilio", "auth_token")) return mockAuthToken;
|
|
46
|
-
if (key === credentialKey("twilio", "account_sid")) return mockAccountSid;
|
|
47
|
-
return undefined;
|
|
48
|
-
},
|
|
49
44
|
getSecureKeyAsync: async (key: string) => {
|
|
50
45
|
if (key === credentialKey("twilio", "auth_token")) return mockAuthToken;
|
|
51
46
|
if (key === credentialKey("twilio", "account_sid")) return mockAccountSid;
|
|
@@ -120,11 +120,10 @@ mock.module("../util/logger.js", () => ({
|
|
|
120
120
|
const mockConfigObj = {
|
|
121
121
|
model: "test",
|
|
122
122
|
provider: "test",
|
|
123
|
-
apiKeys: {},
|
|
124
123
|
memory: { enabled: false },
|
|
125
124
|
rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
|
|
126
125
|
secretDetection: { enabled: false },
|
|
127
|
-
elevenlabs: { voiceId:
|
|
126
|
+
elevenlabs: { voiceId: DEFAULT_ELEVENLABS_VOICE_ID },
|
|
128
127
|
calls: {
|
|
129
128
|
voice: {
|
|
130
129
|
language: "en-US",
|
|
@@ -144,7 +143,6 @@ mock.module("../config/loader.js", () => ({
|
|
|
144
143
|
}));
|
|
145
144
|
|
|
146
145
|
mock.module("../security/secure-keys.js", () => ({
|
|
147
|
-
getSecureKey: (key: string) => mockSecureKeyStore[key],
|
|
148
146
|
setSecureKeyAsync: async (key: string, value: string) => {
|
|
149
147
|
mockSecureKeyStore[key] = value;
|
|
150
148
|
return true;
|
|
@@ -322,6 +320,7 @@ import {
|
|
|
322
320
|
handleStatusCallback,
|
|
323
321
|
handleVoiceWebhook,
|
|
324
322
|
} from "../calls/twilio-routes.js";
|
|
323
|
+
import { DEFAULT_ELEVENLABS_VOICE_ID } from "../config/schemas/elevenlabs.js";
|
|
325
324
|
import { getDb, initializeDb, resetDb } from "../memory/db.js";
|
|
326
325
|
import { conversations } from "../memory/schema.js";
|
|
327
326
|
import {
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
import { migrateBackfillUsageCacheAccounting } from "../memory/migrations/140-backfill-usage-cache-accounting.js";
|
|
47
47
|
import type { PricingUsage } from "../usage/types.js";
|
|
48
48
|
import {
|
|
49
|
-
|
|
49
|
+
resolvePricing,
|
|
50
50
|
resolvePricingForUsageWithOverrides,
|
|
51
51
|
} from "../util/pricing.js";
|
|
52
52
|
|
|
@@ -187,7 +187,8 @@ describe("migrateBackfillUsageCacheAccounting", () => {
|
|
|
187
187
|
model,
|
|
188
188
|
inputTokens: 700,
|
|
189
189
|
outputTokens: 70,
|
|
190
|
-
estimatedCostUsd:
|
|
190
|
+
estimatedCostUsd:
|
|
191
|
+
resolvePricing("anthropic", model, 700, 70).estimatedCostUsd ?? 0,
|
|
191
192
|
});
|
|
192
193
|
insertRequestLog({
|
|
193
194
|
id: "log-prev",
|
|
@@ -201,12 +202,9 @@ describe("migrateBackfillUsageCacheAccounting", () => {
|
|
|
201
202
|
}),
|
|
202
203
|
});
|
|
203
204
|
|
|
204
|
-
const flattenedHistoricalCost =
|
|
205
|
-
3_420_218,
|
|
206
|
-
|
|
207
|
-
model,
|
|
208
|
-
"anthropic",
|
|
209
|
-
);
|
|
205
|
+
const flattenedHistoricalCost =
|
|
206
|
+
resolvePricing("anthropic", model, 3_420_218, 11_768).estimatedCostUsd ??
|
|
207
|
+
0;
|
|
210
208
|
insertUsageEvent({
|
|
211
209
|
id: "usage-target",
|
|
212
210
|
conversationId: "conv-usage-1",
|
|
@@ -245,7 +243,8 @@ describe("migrateBackfillUsageCacheAccounting", () => {
|
|
|
245
243
|
}),
|
|
246
244
|
});
|
|
247
245
|
|
|
248
|
-
const noLogCost =
|
|
246
|
+
const noLogCost =
|
|
247
|
+
resolvePricing("anthropic", model, 1_234, 56).estimatedCostUsd ?? 0;
|
|
249
248
|
insertUsageEvent({
|
|
250
249
|
id: "usage-no-logs",
|
|
251
250
|
conversationId: "conv-usage-2",
|
|
@@ -346,7 +345,8 @@ describe("migrateBackfillUsageCacheAccounting", () => {
|
|
|
346
345
|
model,
|
|
347
346
|
inputTokens: 1_200,
|
|
348
347
|
outputTokens: 80,
|
|
349
|
-
estimatedCostUsd:
|
|
348
|
+
estimatedCostUsd:
|
|
349
|
+
resolvePricing("anthropic", model, 1_200, 80).estimatedCostUsd ?? 0,
|
|
350
350
|
});
|
|
351
351
|
insertRequestLog({
|
|
352
352
|
id: "log-target",
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
buildElevenLabsVoiceSpec,
|
|
11
11
|
resolveVoiceQualityProfile,
|
|
12
12
|
} from "../calls/voice-quality.js";
|
|
13
|
+
import { DEFAULT_ELEVENLABS_VOICE_ID } from "../config/schemas/elevenlabs.js";
|
|
13
14
|
|
|
14
15
|
describe("buildElevenLabsVoiceSpec", () => {
|
|
15
16
|
test("returns bare voiceId when no model is set", () => {
|
|
@@ -63,7 +64,7 @@ describe("buildElevenLabsVoiceSpec", () => {
|
|
|
63
64
|
describe("resolveVoiceQualityProfile", () => {
|
|
64
65
|
test("always returns ElevenLabs ttsProvider", () => {
|
|
65
66
|
mockConfig = {
|
|
66
|
-
elevenlabs: { voiceId:
|
|
67
|
+
elevenlabs: { voiceId: DEFAULT_ELEVENLABS_VOICE_ID },
|
|
67
68
|
calls: {
|
|
68
69
|
voice: {
|
|
69
70
|
language: "en-US",
|
|
@@ -548,7 +548,7 @@ async function executeWebSearch(
|
|
|
548
548
|
if (!apiKey) {
|
|
549
549
|
return {
|
|
550
550
|
content:
|
|
551
|
-
"Error: No web search API key configured.
|
|
551
|
+
"Error: No web search API key configured. Set it via `keys set perplexity <key>` or `keys set brave <key>`, or configure it from the Settings page under API Keys.",
|
|
552
552
|
isError: true,
|
|
553
553
|
};
|
|
554
554
|
}
|
package/src/agent/loop.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as Sentry from "@sentry/node";
|
|
2
2
|
|
|
3
|
+
import { estimateToolsTokens } from "../context/token-estimator.js";
|
|
3
4
|
import { truncateOversizedToolResults } from "../context/tool-result-truncation.js";
|
|
4
5
|
import { getHookManager } from "../hooks/manager.js";
|
|
5
6
|
import type {
|
|
@@ -78,7 +79,7 @@ export type AgentEvent =
|
|
|
78
79
|
toolUseId: string;
|
|
79
80
|
input: Record<string, unknown>;
|
|
80
81
|
}
|
|
81
|
-
| { type: "server_tool_complete"; toolUseId: string }
|
|
82
|
+
| { type: "server_tool_complete"; toolUseId: string; isError: boolean }
|
|
82
83
|
| { type: "error"; error: Error }
|
|
83
84
|
| {
|
|
84
85
|
type: "usage";
|
|
@@ -175,6 +176,20 @@ export class AgentLoop {
|
|
|
175
176
|
this.toolExecutor = toolExecutor ?? null;
|
|
176
177
|
}
|
|
177
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Estimate token cost of the tool definitions sent to the provider.
|
|
181
|
+
*
|
|
182
|
+
* When `history` is provided and a dynamic `resolveTools` callback
|
|
183
|
+
* exists, the budget is derived from the resolved tool list for that
|
|
184
|
+
* turn — matching what `run()` actually sends. Without `history` (or
|
|
185
|
+
* without a resolver), falls back to the static `this.tools`.
|
|
186
|
+
*/
|
|
187
|
+
getToolTokenBudget(history?: Message[]): number {
|
|
188
|
+
const tools =
|
|
189
|
+
history && this.resolveTools ? this.resolveTools(history) : this.tools;
|
|
190
|
+
return estimateToolsTokens(tools);
|
|
191
|
+
}
|
|
192
|
+
|
|
178
193
|
async run(
|
|
179
194
|
messages: Message[],
|
|
180
195
|
onEvent: (event: AgentEvent) => void | Promise<void>,
|
|
@@ -318,6 +333,7 @@ export class AgentLoop {
|
|
|
318
333
|
onEvent({
|
|
319
334
|
type: "server_tool_complete",
|
|
320
335
|
toolUseId: event.toolUseId,
|
|
336
|
+
isError: event.isError,
|
|
321
337
|
});
|
|
322
338
|
}
|
|
323
339
|
},
|
|
@@ -14,7 +14,7 @@ import { join } from "node:path";
|
|
|
14
14
|
import archiver from "archiver";
|
|
15
15
|
import JSZip from "jszip";
|
|
16
16
|
|
|
17
|
-
import { getApp, getAppsDir } from "../memory/app-store.js";
|
|
17
|
+
import { getApp, getAppsDir, isMultifileApp } from "../memory/app-store.js";
|
|
18
18
|
import { computeContentId } from "../util/content-id.js";
|
|
19
19
|
import { getLogger } from "../util/logger.js";
|
|
20
20
|
import { compileApp } from "./app-compiler.js";
|
|
@@ -60,8 +60,10 @@ export async function packageApp(
|
|
|
60
60
|
const version = app.version ?? "1.0.0";
|
|
61
61
|
const contentId = computeContentId(app.name);
|
|
62
62
|
|
|
63
|
+
const multifile = isMultifileApp(app);
|
|
64
|
+
|
|
63
65
|
const manifest: AppManifest = {
|
|
64
|
-
format_version: 2,
|
|
66
|
+
format_version: multifile ? 2 : 1,
|
|
65
67
|
name: app.name,
|
|
66
68
|
...(app.description ? { description: app.description } : {}),
|
|
67
69
|
...(app.icon ? { icon: app.icon } : {}),
|
|
@@ -74,34 +76,48 @@ export async function packageApp(
|
|
|
74
76
|
content_id: contentId,
|
|
75
77
|
};
|
|
76
78
|
|
|
77
|
-
// Compile the app and bundle the
|
|
79
|
+
// Compile the app and bundle the output.
|
|
78
80
|
const compiledFiles: { name: string; data: Buffer }[] = [];
|
|
79
81
|
|
|
80
82
|
const appDir = join(getAppsDir(), appId);
|
|
81
|
-
const compileResult = await compileApp(appDir);
|
|
82
|
-
if (!compileResult.ok) {
|
|
83
|
-
const messages = compileResult.errors
|
|
84
|
-
.map((e) => {
|
|
85
|
-
const loc = e.location
|
|
86
|
-
? ` (${e.location.file}:${e.location.line}:${e.location.column})`
|
|
87
|
-
: "";
|
|
88
|
-
return `${e.text}${loc}`;
|
|
89
|
-
})
|
|
90
|
-
.join("\n");
|
|
91
|
-
throw new Error(`Compilation failed for app "${app.name}":\n${messages}`);
|
|
92
|
-
}
|
|
93
83
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
84
|
+
if (multifile) {
|
|
85
|
+
// Multi-file TSX app: compile src/ -> dist/
|
|
86
|
+
const compileResult = await compileApp(appDir);
|
|
87
|
+
if (!compileResult.ok) {
|
|
88
|
+
const messages = compileResult.errors
|
|
89
|
+
.map((e) => {
|
|
90
|
+
const loc = e.location
|
|
91
|
+
? ` (${e.location.file}:${e.location.line}:${e.location.column})`
|
|
92
|
+
: "";
|
|
93
|
+
return `${e.text}${loc}`;
|
|
94
|
+
})
|
|
95
|
+
.join("\n");
|
|
96
|
+
throw new Error(`Compilation failed for app "${app.name}":\n${messages}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const distDir = join(appDir, "dist");
|
|
100
|
+
const indexHtml = await readFile(join(distDir, "index.html"), "utf-8");
|
|
101
|
+
const mainJs = await readFile(join(distDir, "main.js"));
|
|
97
102
|
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
compiledFiles.push({ name: "index.html", data: Buffer.from(indexHtml) });
|
|
104
|
+
compiledFiles.push({ name: "main.js", data: mainJs });
|
|
100
105
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
// main.css is optional — only produced when the app imports CSS
|
|
107
|
+
const cssPath = join(distDir, "main.css");
|
|
108
|
+
if (existsSync(cssPath)) {
|
|
109
|
+
compiledFiles.push({ name: "main.css", data: await readFile(cssPath) });
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
// Single-file HTML app: bundle index.html directly
|
|
113
|
+
const indexHtmlPath = join(appDir, "index.html");
|
|
114
|
+
if (!existsSync(indexHtmlPath)) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`App "${app.name}" has no src/ directory and no index.html`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const indexHtml = await readFile(indexHtmlPath, "utf-8");
|
|
120
|
+
compiledFiles.push({ name: "index.html", data: Buffer.from(indexHtml) });
|
|
105
121
|
}
|
|
106
122
|
|
|
107
123
|
// Create the zip archive
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
ASK_GUARDIAN_CAPTURE_REGEX,
|
|
51
51
|
CALL_OPENING_ACK_MARKER,
|
|
52
52
|
CALL_OPENING_MARKER,
|
|
53
|
+
CALL_VERIFICATION_COMPLETE_MARKER,
|
|
53
54
|
couldBeControlMarker,
|
|
54
55
|
END_CALL_MARKER,
|
|
55
56
|
extractBalancedJson,
|
|
@@ -209,6 +210,21 @@ export class CallController {
|
|
|
209
210
|
await this.runTurn(CALL_OPENING_MARKER);
|
|
210
211
|
}
|
|
211
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Kick off the first utterance after the caller has completed outbound
|
|
215
|
+
* phone verification. Sends a verification-aware marker so the LLM can
|
|
216
|
+
* greet naturally with context that verification just happened.
|
|
217
|
+
*/
|
|
218
|
+
async startPostVerificationGreeting(): Promise<void> {
|
|
219
|
+
if (this.initialGreetingStarted) return;
|
|
220
|
+
if (this.state !== "idle") return;
|
|
221
|
+
|
|
222
|
+
this.initialGreetingStarted = true;
|
|
223
|
+
this.resetSilenceTimer();
|
|
224
|
+
this.lastSentWasOpener = true;
|
|
225
|
+
await this.runTurn(CALL_VERIFICATION_COMPLETE_MARKER);
|
|
226
|
+
}
|
|
227
|
+
|
|
212
228
|
/**
|
|
213
229
|
* Handle a final caller utterance from the ConversationRelay.
|
|
214
230
|
* Caller utterances always trigger normal turns, even when a guardian
|
|
@@ -52,7 +52,7 @@ export async function generateGuardianCopy(
|
|
|
52
52
|
const fallback = buildFallbackCopy(questionText);
|
|
53
53
|
|
|
54
54
|
// If no provider is configured, return fallback immediately
|
|
55
|
-
const resolved = resolveConfiguredProvider();
|
|
55
|
+
const resolved = await resolveConfiguredProvider();
|
|
56
56
|
if (!resolved) {
|
|
57
57
|
log.debug(
|
|
58
58
|
"No provider available for guardian copy generation, using fallback",
|
|
@@ -595,7 +595,7 @@ export class RelayConnection {
|
|
|
595
595
|
(resolved.actorTrust.trustClass === "guardian" ||
|
|
596
596
|
resolved.actorTrust.trustClass === "trusted_contact")
|
|
597
597
|
) {
|
|
598
|
-
touchContactInteraction(resolved.actorTrust.memberRecord.
|
|
598
|
+
touchContactInteraction(resolved.actorTrust.memberRecord.channel.id);
|
|
599
599
|
}
|
|
600
600
|
if (this.controller && resolved.actorTrust.trustClass !== "unknown") {
|
|
601
601
|
this.controller.setTrustContext(
|
|
@@ -612,7 +612,7 @@ export class RelayConnection {
|
|
|
612
612
|
resolved.actorTrust.trustClass === "trusted_contact")
|
|
613
613
|
) {
|
|
614
614
|
touchContactInteraction(
|
|
615
|
-
resolved.actorTrust.memberRecord.
|
|
615
|
+
resolved.actorTrust.memberRecord.channel.id,
|
|
616
616
|
);
|
|
617
617
|
}
|
|
618
618
|
if (this.controller && resolved.actorTrust.trustClass !== "unknown") {
|
|
@@ -992,14 +992,7 @@ export class RelayConnection {
|
|
|
992
992
|
}
|
|
993
993
|
|
|
994
994
|
if (isOutbound) {
|
|
995
|
-
|
|
996
|
-
this.sendTextToken(result.ttsMessage!, true);
|
|
997
|
-
|
|
998
|
-
updateCallSession(this.callSessionId, {
|
|
999
|
-
status: "completed",
|
|
1000
|
-
endedAt: Date.now(),
|
|
1001
|
-
});
|
|
1002
|
-
|
|
995
|
+
// Keep the pointer message back to the initiating conversation
|
|
1003
996
|
const successSession = getCallSession(this.callSessionId);
|
|
1004
997
|
if (successSession?.initiatedFromConversationId) {
|
|
1005
998
|
addPointerMessage(
|
|
@@ -1018,9 +1011,32 @@ export class RelayConnection {
|
|
|
1018
1011
|
});
|
|
1019
1012
|
}
|
|
1020
1013
|
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1014
|
+
// Update trust context on the controller so the LLM knows this is the guardian
|
|
1015
|
+
if (this.controller) {
|
|
1016
|
+
const verifiedActorTrust = resolveActorTrust({
|
|
1017
|
+
assistantId,
|
|
1018
|
+
sourceChannel: "phone",
|
|
1019
|
+
conversationExternalId: fromNumber,
|
|
1020
|
+
actorExternalId: fromNumber,
|
|
1021
|
+
});
|
|
1022
|
+
this.controller.setTrustContext(
|
|
1023
|
+
toTrustContext(verifiedActorTrust, fromNumber),
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// Mark session as in-progress and transition to guardian conversation
|
|
1028
|
+
// with verification context so the LLM greets naturally.
|
|
1029
|
+
updateCallSession(this.callSessionId, { status: "in_progress" });
|
|
1030
|
+
if (this.controller) {
|
|
1031
|
+
this.controller
|
|
1032
|
+
.startPostVerificationGreeting()
|
|
1033
|
+
.catch((err) =>
|
|
1034
|
+
log.error(
|
|
1035
|
+
{ err, callSessionId: this.callSessionId },
|
|
1036
|
+
"Failed to start post-verification greeting",
|
|
1037
|
+
),
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1024
1040
|
} else if (result.verificationType === "trusted_contact") {
|
|
1025
1041
|
this.continueCallAfterTrustedContactActivation({
|
|
1026
1042
|
assistantId,
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
export const CALL_OPENING_MARKER = "[CALL_OPENING]";
|
|
13
13
|
export const CALL_OPENING_ACK_MARKER = "[CALL_OPENING_ACK]";
|
|
14
|
+
export const CALL_VERIFICATION_COMPLETE_MARKER = "[CALL_VERIFICATION_COMPLETE]";
|
|
14
15
|
export const END_CALL_MARKER = "[END_CALL]";
|
|
15
16
|
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
@@ -44,7 +44,7 @@ export function buildElevenLabsVoiceSpec(config: {
|
|
|
44
44
|
*
|
|
45
45
|
* Always uses ElevenLabs TTS via Twilio ConversationRelay.
|
|
46
46
|
* The voice ID comes from the shared `elevenlabs.voiceId` config
|
|
47
|
-
* (defaults to
|
|
47
|
+
* (defaults to Amelia — ZF6FPAbjXT4488VcRRnw).
|
|
48
48
|
*/
|
|
49
49
|
export function resolveVoiceQualityProfile(
|
|
50
50
|
config?: ReturnType<typeof loadConfig>,
|
|
@@ -24,7 +24,10 @@ import { checkIngressForSecrets } from "../security/secret-ingress.js";
|
|
|
24
24
|
import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
|
|
25
25
|
import { IngressBlockedError } from "../util/errors.js";
|
|
26
26
|
import { getLogger } from "../util/logger.js";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
CALL_OPENING_MARKER,
|
|
29
|
+
CALL_VERIFICATION_COMPLETE_MARKER,
|
|
30
|
+
} from "./voice-control-protocol.js";
|
|
28
31
|
|
|
29
32
|
const log = getLogger("voice-session-bridge");
|
|
30
33
|
|
|
@@ -197,7 +200,8 @@ function buildVoiceCallControlPrompt(opts: {
|
|
|
197
200
|
? " However, the disclosure text from rule 0 is separate from self-introduction and must always be included in your opening greeting, even if the Task does not mention introducing yourself."
|
|
198
201
|
: "";
|
|
199
202
|
lines.push(
|
|
200
|
-
|
|
203
|
+
'7. If the latest user turn is "(verification completed — transitioning into conversation)", the caller just completed a phone verification code challenge on this call. Greet them naturally and ask if there is anything you can help with. Keep it casual and brief.',
|
|
204
|
+
`If the latest user turn is "(call connected — deliver opening greeting)", deliver your opening greeting based solely on the Task context above. The Task already describes how to open the call — follow it directly without adding any extra introduction on top. If the Task says to introduce yourself, do so once. If the Task does not mention introducing yourself, skip the introduction.${disclosureReminder} Vary the wording naturally; do not use a fixed template.`,
|
|
201
205
|
"8. If the latest user turn includes [CALL_OPENING_ACK], treat it as the callee acknowledging your opener and continue the conversation naturally without re-introducing yourself or repeating the initial check-in question.",
|
|
202
206
|
);
|
|
203
207
|
}
|
|
@@ -269,7 +273,9 @@ export async function startVoiceTurn(
|
|
|
269
273
|
const persistedContent =
|
|
270
274
|
opts.content === CALL_OPENING_MARKER
|
|
271
275
|
? "(call connected — deliver opening greeting)"
|
|
272
|
-
: opts.content
|
|
276
|
+
: opts.content === CALL_VERIFICATION_COMPLETE_MARKER
|
|
277
|
+
? "(verification completed — transitioning into conversation)"
|
|
278
|
+
: opts.content;
|
|
273
279
|
|
|
274
280
|
// Build the call-control protocol prompt so the model knows how to emit
|
|
275
281
|
// control markers (ASK_GUARDIAN, END_CALL, etc.) and recognize opener turns.
|
package/src/channels/types.ts
CHANGED
|
@@ -74,6 +74,22 @@ export function assertInterfaceId(value: unknown, field: string): InterfaceId {
|
|
|
74
74
|
return value;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Interfaces that have an SSE client capable of displaying interactive
|
|
79
|
+
* permission prompts. Channel interfaces (telegram, slack, etc.) route
|
|
80
|
+
* approvals through the guardian system and have no interactive prompter UI.
|
|
81
|
+
*/
|
|
82
|
+
export const INTERACTIVE_INTERFACES: ReadonlySet<InterfaceId> = new Set([
|
|
83
|
+
"macos",
|
|
84
|
+
"ios",
|
|
85
|
+
"cli",
|
|
86
|
+
"vellum",
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
export function isInteractiveInterface(id: InterfaceId): boolean {
|
|
90
|
+
return INTERACTIVE_INTERFACES.has(id);
|
|
91
|
+
}
|
|
92
|
+
|
|
77
93
|
export interface TurnInterfaceContext {
|
|
78
94
|
userMessageInterface: InterfaceId;
|
|
79
95
|
assistantMessageInterface: InterfaceId;
|