@vellumai/assistant 0.6.3 → 0.6.4
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 +273 -10
- package/Dockerfile +2 -3
- package/bun.lock +5 -13
- package/docs/backup-troubleshooting.md +52 -0
- package/docs/browser-use-architecture-phase2.md +174 -0
- package/docs/stt-provider-onboarding.md +120 -0
- package/knip.json +12 -2
- package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
- package/node_modules/@vellumai/ces-contracts/package.json +3 -3
- package/openapi.yaml +982 -72
- package/package.json +4 -6
- package/scripts/generate-openapi.ts +0 -1
- package/scripts/test.sh +73 -18
- package/src/__tests__/agent-image-optimize.test.ts +28 -0
- package/src/__tests__/agent-loop.test.ts +123 -0
- package/src/__tests__/anthropic-provider.test.ts +263 -10
- package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
- package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
- package/src/__tests__/browser-fill-credential.test.ts +11 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/browser-skill-endstate.test.ts +31 -7
- package/src/__tests__/btw-routes.test.ts +7 -0
- package/src/__tests__/call-controller.test.ts +581 -20
- package/src/__tests__/catalog-files.test.ts +138 -0
- package/src/__tests__/channel-invite-transport.test.ts +2 -2
- package/src/__tests__/channel-readiness-routes.test.ts +16 -20
- package/src/__tests__/channel-readiness-service.test.ts +12 -7
- package/src/__tests__/checker.test.ts +157 -10
- package/src/__tests__/clawhub-files.test.ts +347 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
- package/src/__tests__/config-analysis.test.ts +100 -0
- package/src/__tests__/config-schema.test.ts +1013 -66
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- package/src/__tests__/context-window-manager.test.ts +88 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +98 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-history-web-search.test.ts +11 -4
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
- package/src/__tests__/conversation-list-source.test.ts +145 -0
- package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +901 -60
- package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
- package/src/__tests__/conversation-skill-tools.test.ts +7 -4
- package/src/__tests__/conversation-slash-commands.test.ts +33 -0
- package/src/__tests__/conversation-slash-queue.test.ts +89 -18
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/credential-vault-unit.test.ts +379 -3
- package/src/__tests__/credentials-cli.test.ts +40 -16
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
- package/src/__tests__/device-id.test.ts +112 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
- package/src/__tests__/email-html-renderer.test.ts +71 -0
- package/src/__tests__/email-invite-adapter.test.ts +36 -32
- package/src/__tests__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
- package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +64 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
- package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
- package/src/__tests__/gmail-archive-gate.test.ts +246 -0
- package/src/__tests__/gmail-preferences.test.ts +117 -0
- package/src/__tests__/headless-browser-interactions.test.ts +43 -0
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +142 -5
- package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
- package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
- package/src/__tests__/heartbeat-service.test.ts +70 -17
- package/src/__tests__/home-state-routes.test.ts +162 -0
- package/src/__tests__/host-bash-proxy.test.ts +0 -5
- package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
- package/src/__tests__/host-cu-proxy.test.ts +0 -5
- package/src/__tests__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- package/src/__tests__/llm-context-normalization.test.ts +488 -0
- package/src/__tests__/llm-context-route-provider.test.ts +86 -5
- package/src/__tests__/llm-usage-store.test.ts +363 -0
- package/src/__tests__/media-stream-output.test.ts +555 -0
- package/src/__tests__/media-stream-parser.test.ts +374 -0
- package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
- package/src/__tests__/media-stream-stt-session.test.ts +588 -0
- package/src/__tests__/media-turn-detector.test.ts +440 -0
- package/src/__tests__/message-queue.test.ts +125 -0
- package/src/__tests__/migration-export-http.test.ts +6 -6
- package/src/__tests__/migration-import-commit-http.test.ts +8 -6
- package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +151 -0
- package/src/__tests__/model-intents.test.ts +2 -2
- package/src/__tests__/oauth-apps-routes.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +2 -0
- package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
- package/src/__tests__/oauth-providers-routes.test.ts +2 -0
- package/src/__tests__/oauth-store.test.ts +85 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
- package/src/__tests__/onboarding-template-contract.test.ts +6 -13
- package/src/__tests__/openai-provider.test.ts +176 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
- package/src/__tests__/openai-responses-provider.test.ts +1105 -0
- package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -0
- package/src/__tests__/qdrant-manager.test.ts +29 -8
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
- package/src/__tests__/relationship-state-contract.test.ts +175 -0
- package/src/__tests__/relay-server.test.ts +423 -5
- package/src/__tests__/search-skills-unified.test.ts +118 -0
- package/src/__tests__/secret-scanner-executor.test.ts +4 -0
- package/src/__tests__/secure-keys.test.ts +107 -0
- package/src/__tests__/send-endpoint-busy.test.ts +5 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- package/src/__tests__/settings-routes.test.ts +201 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
- package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
- package/src/__tests__/skills.test.ts +5 -2
- package/src/__tests__/skillssh-files.test.ts +446 -0
- package/src/__tests__/slack-block-formatting.test.ts +110 -0
- package/src/__tests__/slack-channel-config.test.ts +564 -1
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +112 -26
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +18 -7
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
- package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +33 -24
- package/src/__tests__/tool-result-truncation.test.ts +36 -0
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- package/src/__tests__/tts-catalog-parity.test.ts +345 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
- package/src/__tests__/twilio-routes.test.ts +376 -0
- package/src/__tests__/unicode.test.ts +293 -0
- package/src/__tests__/update-bulletin-format.test.ts +59 -0
- package/src/__tests__/update-bulletin.test.ts +206 -5
- package/src/__tests__/usage-routes.test.ts +25 -4
- package/src/__tests__/user-reference.test.ts +46 -61
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
- package/src/__tests__/voice-config-update.test.ts +403 -0
- package/src/__tests__/voice-quality.test.ts +434 -19
- package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
- package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
- package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
- package/src/__tests__/workspace-migration-meets.test.ts +244 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
- package/src/__tests__/workspace-policy.test.ts +2 -0
- package/src/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +43 -3
- package/src/backup/__tests__/backup-key.test.ts +152 -0
- package/src/backup/__tests__/backup-worker.test.ts +767 -0
- package/src/backup/__tests__/list-snapshots.test.ts +87 -0
- package/src/backup/__tests__/local-writer.test.ts +218 -0
- package/src/backup/__tests__/offsite-writer.test.ts +641 -0
- package/src/backup/__tests__/paths.test.ts +300 -0
- package/src/backup/__tests__/restore.test.ts +498 -0
- package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
- package/src/backup/__tests__/stream-crypt.test.ts +228 -0
- package/src/backup/backup-key.ts +137 -0
- package/src/backup/backup-worker.ts +459 -0
- package/src/backup/list-snapshots.ts +147 -0
- package/src/backup/local-writer.ts +133 -0
- package/src/backup/offsite-writer.ts +222 -0
- package/src/backup/paths.ts +226 -0
- package/src/backup/restore.ts +322 -0
- package/src/backup/snapshot-lock.ts +431 -0
- package/src/backup/stream-crypt.ts +263 -0
- package/src/bundler/package-resolver.ts +4 -0
- package/src/calls/audio-store.ts +11 -5
- package/src/calls/call-controller.ts +226 -71
- package/src/calls/call-domain.ts +9 -0
- package/src/calls/call-speech-output.ts +190 -0
- package/src/calls/call-transport.ts +77 -0
- package/src/calls/media-stream-audio-transcode.ts +173 -0
- package/src/calls/media-stream-output.ts +660 -0
- package/src/calls/media-stream-parser.ts +300 -0
- package/src/calls/media-stream-protocol.ts +166 -0
- package/src/calls/media-stream-server.ts +592 -0
- package/src/calls/media-stream-stt-session.ts +460 -0
- package/src/calls/media-turn-detector.ts +230 -0
- package/src/calls/relay-server.ts +90 -75
- package/src/calls/resolve-call-tts-provider.ts +136 -0
- package/src/calls/telephony-stt-routing.ts +145 -0
- package/src/calls/tts-call-strategy.ts +161 -0
- package/src/calls/tts-text-sanitizer.ts +32 -16
- package/src/calls/twilio-routes.ts +281 -17
- package/src/calls/voice-quality.ts +78 -35
- package/src/calls/voice-session-bridge.ts +8 -1
- package/src/channels/types.ts +16 -0
- package/src/cli/__tests__/run-assistant-command.ts +11 -1
- package/src/cli/commands/__tests__/backup.test.ts +1165 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
- package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
- package/src/cli/commands/__tests__/email-download.test.ts +16 -1
- package/src/cli/commands/__tests__/email-list.test.ts +22 -4
- package/src/cli/commands/__tests__/email-register.test.ts +4 -4
- package/src/cli/commands/__tests__/email-send.test.ts +37 -4
- package/src/cli/commands/__tests__/email-status.test.ts +5 -1
- package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
- package/src/cli/commands/backup.ts +993 -0
- package/src/cli/commands/conversations.ts +77 -0
- package/src/cli/commands/credentials.ts +0 -1
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +255 -3
- package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
- package/src/cli/commands/oauth/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +15 -0
- package/src/cli/commands/oauth/shared.ts +2 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
- package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
- package/src/cli/program.ts +30 -4
- package/src/config/__tests__/backup-schema.test.ts +134 -0
- package/src/config/assistant-feature-flags.ts +61 -62
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
- package/src/config/bundled-skills/browser/SKILL.md +30 -5
- package/src/config/bundled-skills/browser/TOOLS.json +123 -0
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
- package/src/config/bundled-skills/contacts/SKILL.md +2 -2
- package/src/config/bundled-skills/gmail/SKILL.md +53 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
- package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
- package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
- package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/outlook/SKILL.md +2 -2
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
- package/src/config/bundled-skills/slack/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
- package/src/config/bundled-tool-registry.ts +8 -0
- package/src/config/env-registry.ts +24 -0
- package/src/config/env.ts +34 -10
- package/src/config/feature-flag-registry.json +46 -14
- package/src/config/loader.ts +26 -12
- package/src/config/schema.ts +35 -10
- package/src/config/schemas/__tests__/stt.test.ts +43 -0
- package/src/config/schemas/analysis.ts +51 -0
- package/src/config/schemas/backup.ts +72 -0
- package/src/config/schemas/calls.ts +1 -26
- package/src/config/schemas/elevenlabs.ts +0 -59
- package/src/config/schemas/filing.ts +47 -7
- package/src/config/schemas/heartbeat.ts +27 -5
- package/src/config/schemas/host-browser.ts +47 -1
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/services.ts +44 -0
- package/src/config/schemas/stt.ts +59 -0
- package/src/config/schemas/tts.ts +230 -0
- package/src/config/schemas/updates.ts +14 -0
- package/src/config/skills.ts +4 -0
- package/src/config/types.ts +4 -0
- package/src/contacts/contact-store.ts +56 -11
- package/src/contacts/contacts-write.ts +38 -1
- package/src/context/post-turn-tool-result-truncation.ts +3 -2
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +45 -12
- package/src/credential-execution/executable-discovery.ts +12 -2
- package/src/credential-execution/process-manager.ts +33 -2
- package/src/credential-health/credential-health-service.ts +366 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
- package/src/daemon/conversation-agent-loop.ts +101 -24
- package/src/daemon/conversation-error.ts +11 -0
- package/src/daemon/conversation-history.ts +40 -6
- package/src/daemon/conversation-launch.ts +220 -0
- package/src/daemon/conversation-lifecycle.ts +59 -9
- package/src/daemon/conversation-messaging.ts +37 -3
- package/src/daemon/conversation-notifiers.ts +5 -0
- package/src/daemon/conversation-process.ts +581 -19
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +11 -1
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +94 -4
- package/src/daemon/conversation-tool-setup.ts +25 -0
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation.ts +86 -28
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +4 -1
- package/src/daemon/handlers/shared.ts +22 -0
- package/src/daemon/handlers/skills.ts +321 -77
- package/src/daemon/host-browser-proxy.ts +2 -1
- package/src/daemon/lifecycle.ts +122 -25
- package/src/daemon/message-protocol.ts +6 -0
- package/src/daemon/message-types/conversations.ts +34 -1
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +14 -0
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +16 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +347 -2
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +9 -0
- package/src/email/html-renderer.ts +76 -0
- package/src/heartbeat/heartbeat-service.ts +93 -7
- package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
- package/src/home/__tests__/emit-feed-event.test.ts +169 -0
- package/src/home/__tests__/feed-scheduler.test.ts +194 -0
- package/src/home/__tests__/feed-types.test.ts +275 -0
- package/src/home/__tests__/feed-writer.test.ts +688 -0
- package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
- package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
- package/src/home/__tests__/progress-formula.test.ts +213 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
- package/src/home/__tests__/rollup-producer.test.ts +398 -0
- package/src/home/assistant-feed-authoring.ts +124 -0
- package/src/home/emit-feed-event.ts +158 -0
- package/src/home/feed-scheduler.ts +247 -0
- package/src/home/feed-types.ts +181 -0
- package/src/home/feed-writer.ts +469 -0
- package/src/home/platform-gmail-digest.ts +163 -0
- package/src/home/progress-formula.ts +86 -0
- package/src/home/relationship-state-writer.ts +824 -0
- package/src/home/relationship-state.ts +143 -0
- package/src/home/rollup-producer.ts +384 -0
- package/src/hooks/runner.ts +7 -0
- package/src/inbound/platform-callback-registration.ts +12 -3
- package/src/inbound/public-ingress-urls.ts +12 -0
- package/src/instrument.ts +1 -1
- package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
- package/src/ipc/cli-client.ts +151 -0
- package/src/ipc/cli-server.ts +234 -0
- package/src/ipc/gateway-client.ts +180 -0
- package/src/ipc/routes/index.ts +5 -0
- package/src/ipc/routes/wake-conversation.ts +19 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
- package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
- package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
- package/src/memory/app-store.ts +1 -1
- package/src/memory/attachments-store.ts +70 -0
- package/src/memory/auto-analysis-enqueue.ts +127 -0
- package/src/memory/auto-analysis-guard.ts +27 -0
- package/src/memory/cleanup-schedule-state.ts +37 -0
- package/src/memory/conversation-analyze-job.ts +73 -0
- package/src/memory/conversation-crud.ts +99 -0
- package/src/memory/conversation-disk-view.ts +7 -0
- package/src/memory/conversation-group-migration.ts +34 -2
- package/src/memory/conversation-queries.ts +6 -5
- package/src/memory/db-init.ts +6 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -0
- package/src/memory/graph/conversation-graph-memory.ts +15 -0
- package/src/memory/graph/extraction.test.ts +23 -0
- package/src/memory/graph/extraction.ts +8 -0
- package/src/memory/graph/retriever.ts +27 -18
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/group-crud.ts +6 -1
- package/src/memory/indexer.ts +95 -16
- package/src/memory/job-handlers/cleanup.ts +11 -8
- package/src/memory/job-handlers/conversation-starters.ts +16 -10
- package/src/memory/jobs-store.ts +64 -4
- package/src/memory/jobs-worker.ts +22 -9
- package/src/memory/llm-usage-store.ts +92 -56
- package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
- package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +2 -0
- package/src/memory/schema/oauth.ts +3 -0
- package/src/memory/usage-buckets.ts +396 -0
- package/src/messaging/providers/gmail/client.ts +57 -6
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
- package/src/messaging/providers/slack/adapter.ts +143 -38
- package/src/messaging/providers/slack/client.ts +16 -0
- package/src/messaging/providers/slack/types.ts +4 -0
- package/src/notifications/decision-engine.ts +3 -3
- package/src/notifications/signal.ts +5 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
- package/src/oauth/byo-connection.test.ts +18 -1
- package/src/oauth/byo-connection.ts +3 -1
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.ts +6 -2
- package/src/oauth/connection.ts +2 -0
- package/src/oauth/oauth-store.ts +9 -0
- package/src/oauth/platform-connection.test.ts +98 -0
- package/src/oauth/platform-connection.ts +52 -31
- package/src/oauth/seed-providers.ts +7 -0
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/trust-store.ts +3 -3
- package/src/permissions/workspace-policy.ts +3 -0
- package/src/platform/client.test.ts +10 -0
- package/src/platform/sync-identity.ts +129 -0
- package/src/prompts/persona-resolver.ts +126 -2
- package/src/prompts/system-prompt.ts +59 -18
- package/src/prompts/templates/BOOTSTRAP.md +5 -5
- package/src/prompts/templates/SOUL.md +3 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/templates/channels/slack.md +20 -0
- package/src/prompts/update-bulletin-format.ts +26 -9
- package/src/prompts/update-bulletin.ts +34 -23
- package/src/prompts/user-reference.ts +20 -17
- package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
- package/src/providers/anthropic/client.ts +157 -61
- package/src/providers/fireworks/client.ts +2 -2
- package/src/providers/gemini/client.ts +9 -1
- package/src/providers/model-catalog.ts +6 -0
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/ollama/client.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +474 -0
- package/src/providers/openai/client.ts +25 -440
- package/src/providers/openai/responses-provider.ts +502 -0
- package/src/providers/openrouter/client.ts +101 -4
- package/src/providers/provider-secret-catalog.ts +139 -0
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +14 -3
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
- package/src/providers/speech-to-text/deepgram.test.ts +332 -0
- package/src/providers/speech-to-text/deepgram.ts +115 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
- package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
- package/src/providers/speech-to-text/google-gemini.ts +101 -0
- package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
- package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
- package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
- package/src/providers/speech-to-text/openai-whisper.ts +63 -33
- package/src/providers/speech-to-text/provider-catalog.ts +306 -0
- package/src/providers/speech-to-text/resolve.ts +386 -6
- package/src/providers/types.ts +9 -0
- package/src/runtime/AGENTS.md +43 -1
- package/src/runtime/__tests__/agent-wake.test.ts +831 -0
- package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
- package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
- package/src/runtime/agent-wake.ts +512 -0
- package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
- package/src/runtime/auth/route-policy.ts +30 -5
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +10 -10
- package/src/runtime/channel-invite-transport.ts +1 -1
- package/src/runtime/channel-invite-transports/email.ts +14 -6
- package/src/runtime/channel-readiness-service.ts +12 -22
- package/src/runtime/chrome-extension-registry.ts +38 -2
- package/src/runtime/http-server.ts +395 -10
- package/src/runtime/http-types.ts +6 -2
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +1 -0
- package/src/runtime/migrations/migration-wizard.ts +1 -0
- package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
- package/src/runtime/migrations/vbundle-importer.ts +34 -0
- package/src/runtime/pending-interactions.ts +0 -11
- package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
- package/src/runtime/routes/app-management-routes.ts +12 -18
- package/src/runtime/routes/attachment-routes.test.ts +9 -3
- package/src/runtime/routes/attachment-routes.ts +216 -17
- package/src/runtime/routes/backup-routes.ts +519 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
- package/src/runtime/routes/btw-routes.ts +8 -6
- package/src/runtime/routes/contact-routes.test.ts +298 -0
- package/src/runtime/routes/contact-routes.ts +132 -5
- package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
- package/src/runtime/routes/conversation-management-routes.ts +115 -0
- package/src/runtime/routes/conversation-routes.ts +367 -146
- package/src/runtime/routes/filing-routes.ts +93 -0
- package/src/runtime/routes/home-feed-routes.ts +334 -0
- package/src/runtime/routes/home-state-routes.ts +138 -0
- package/src/runtime/routes/host-browser-routes.ts +3 -14
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +3 -17
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
- package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
- package/src/runtime/routes/integrations/slack/channel.ts +11 -3
- package/src/runtime/routes/integrations/slack/share.ts +45 -7
- package/src/runtime/routes/llm-context-normalization.ts +303 -0
- package/src/runtime/routes/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/migration-routes.ts +40 -5
- package/src/runtime/routes/settings-routes.ts +22 -5
- package/src/runtime/routes/skills-routes.ts +76 -7
- package/src/runtime/routes/stt-routes.ts +233 -0
- package/src/runtime/routes/surface-action-routes.ts +41 -2
- package/src/runtime/routes/tts-routes.ts +108 -24
- package/src/runtime/routes/usage-routes.ts +30 -2
- package/src/runtime/routes/user-route-dispatcher.ts +50 -5
- package/src/runtime/routes/user-routes.ts +13 -1
- package/src/runtime/routes/work-items-routes.ts +8 -1
- package/src/runtime/runtime-mode.ts +33 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
- package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
- package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
- package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
- package/src/runtime/services/analyze-conversation.ts +344 -0
- package/src/runtime/services/analyze-deps-singleton.ts +32 -0
- package/src/runtime/services/auto-analysis-prompt.ts +55 -0
- package/src/runtime/skill-route-registry.ts +49 -0
- package/src/runtime/slack-block-formatting.ts +437 -10
- package/src/schedule/scheduler.ts +50 -0
- package/src/security/oauth2.ts +26 -4
- package/src/security/secure-keys.ts +25 -2
- package/src/security/token-manager.ts +8 -0
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +64 -2
- package/src/skills/category-inference.ts +122 -0
- package/src/skills/clawhub-files.ts +213 -0
- package/src/skills/clawhub.ts +84 -23
- package/src/skills/skill-file-provider.ts +40 -0
- package/src/skills/skillssh-files.ts +395 -0
- package/src/skills/skillssh-registry.ts +4 -4
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
- package/src/stt/__tests__/types.test.ts +89 -0
- package/src/stt/daemon-batch-transcriber.ts +195 -0
- package/src/stt/stt-stream-session.ts +499 -0
- package/src/stt/types.ts +330 -0
- package/src/stt/wav-encoder.test.ts +373 -0
- package/src/stt/wav-encoder.ts +175 -0
- package/src/subagent/manager.ts +38 -14
- package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
- package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
- package/src/tools/browser/browser-execution.ts +1163 -23
- package/src/tools/browser/browser-manager.ts +45 -0
- package/src/tools/browser/browser-mode-constants.ts +12 -0
- package/src/tools/browser/browser-mode.ts +92 -0
- package/src/tools/browser/browser-status-constants.ts +33 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
- package/src/tools/browser/cdp-client/errors.ts +15 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
- package/src/tools/browser/cdp-client/factory.ts +797 -87
- package/src/tools/browser/cdp-client/index.ts +16 -2
- package/src/tools/browser/cdp-client/types.ts +68 -0
- package/src/tools/credentials/vault.ts +35 -6
- package/src/tools/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- package/src/tools/shared/shell-output.ts +3 -1
- package/src/tools/side-effects.ts +2 -0
- package/src/tools/skills/sandbox-runner.ts +3 -2
- package/src/tools/terminal/safe-env.ts +10 -2
- package/src/tools/terminal/shell.ts +15 -4
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +17 -0
- package/src/tools/ui-surface/definitions.ts +6 -1
- package/src/tts/__tests__/provider-adapters.test.ts +834 -0
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
- package/src/tts/__tests__/provider-catalog.test.ts +183 -0
- package/src/tts/__tests__/provider-registry.test.ts +90 -0
- package/src/tts/provider-catalog.ts +201 -0
- package/src/tts/provider-registry.ts +73 -0
- package/src/tts/providers/deepgram-provider.ts +219 -0
- package/src/tts/providers/elevenlabs-provider.ts +211 -0
- package/src/tts/providers/fish-audio-provider.ts +183 -0
- package/src/tts/providers/index.ts +42 -0
- package/src/tts/providers/register-builtins.ts +130 -0
- package/src/tts/synthesize-text.ts +110 -0
- package/src/tts/tts-config-resolver.ts +78 -0
- package/src/tts/types.ts +153 -0
- package/src/types/onboarding-context.ts +7 -0
- package/src/util/abort-reasons.ts +58 -0
- package/src/util/device-id.ts +32 -16
- package/src/util/errors.ts +9 -1
- package/src/util/platform.ts +54 -10
- package/src/util/pricing.ts +66 -3
- package/src/util/spawn.ts +1 -1
- package/src/util/truncate.ts +4 -2
- package/src/util/unicode.ts +201 -0
- package/src/version.ts +19 -24
- package/src/watcher/engine.ts +23 -0
- package/src/watcher/watcher-store.ts +31 -0
- package/src/workspace/migrations/003-seed-device-id.ts +9 -3
- package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
- package/src/workspace/migrations/029-seed-pkb.ts +1 -1
- package/src/workspace/migrations/031-drop-user-md.ts +317 -0
- package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
- package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
- package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
- package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
- package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
- package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
- package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/top-level-renderer.ts +13 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- package/src/cli/commands/browser-relay.ts +0 -466
- package/src/email/guardrails.ts +0 -221
- package/src/email/provider.ts +0 -117
- package/src/email/providers/agentmail.ts +0 -361
- package/src/email/providers/index.ts +0 -65
- package/src/email/service.ts +0 -384
- package/src/email/types.ts +0 -126
- package/src/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/runtime/routes/browser-cdp-routes.ts +0 -229
|
@@ -19,10 +19,7 @@ import { z } from "zod";
|
|
|
19
19
|
import { getConfig } from "../../config/loader.js";
|
|
20
20
|
import { readNowScratchpad } from "../../daemon/conversation-runtime-assembly.js";
|
|
21
21
|
import { getConversationByKey } from "../../memory/conversation-key-store.js";
|
|
22
|
-
import {
|
|
23
|
-
resolveChannelPersona,
|
|
24
|
-
resolveGuardianPersona,
|
|
25
|
-
} from "../../prompts/persona-resolver.js";
|
|
22
|
+
import { resolvePersonaContext } from "../../prompts/persona-resolver.js";
|
|
26
23
|
import { getLogger } from "../../util/logger.js";
|
|
27
24
|
import { getWorkspacePromptPath } from "../../util/platform.js";
|
|
28
25
|
import type { AuthContext } from "../auth/types.js";
|
|
@@ -168,14 +165,19 @@ async function handleBtw(
|
|
|
168
165
|
try {
|
|
169
166
|
const isIntroRequest = conversationKey === IDENTITY_INTRO_KEY;
|
|
170
167
|
const isGreeting = conversationKey === GREETING_KEY;
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
// Resolve guardian persona context (undefined trustContext triggers
|
|
169
|
+
// the guardian lookup path in persona-resolver). Thread userSlug
|
|
170
|
+
// through so buildSystemPrompt's BOOTSTRAP.md placeholder never
|
|
171
|
+
// falls back to "default.md" if excludeBootstrap is ever flipped.
|
|
172
|
+
const { userPersona, userSlug, channelPersona } =
|
|
173
|
+
resolvePersonaContext(undefined, undefined);
|
|
173
174
|
const result = await runBtwSidechain({
|
|
174
175
|
content: effectiveContent,
|
|
175
176
|
conversation,
|
|
176
177
|
signal: req.signal,
|
|
177
178
|
userPersona,
|
|
178
179
|
channelPersona,
|
|
180
|
+
userSlug,
|
|
179
181
|
...(isGreeting
|
|
180
182
|
? { modelIntent: getConfig().ui.greetingModelIntent }
|
|
181
183
|
: {}),
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for POST /v1/contacts/guardian/channel endpoint.
|
|
3
|
+
*/
|
|
4
|
+
import { beforeAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
5
|
+
|
|
6
|
+
mock.module("../../util/logger.js", () => ({
|
|
7
|
+
getLogger: () =>
|
|
8
|
+
new Proxy({} as Record<string, unknown>, {
|
|
9
|
+
get: () => () => {},
|
|
10
|
+
}),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
mock.module("../../config/loader.js", () => ({
|
|
14
|
+
loadConfig: () => ({}),
|
|
15
|
+
getConfig: () => ({}),
|
|
16
|
+
invalidateConfigCache: () => {},
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
mock.module("../../config/env.js", () => ({
|
|
20
|
+
isHttpAuthDisabled: () => true,
|
|
21
|
+
getAssistantDomain: () => "vellum.me",
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
import { and, eq } from "drizzle-orm";
|
|
25
|
+
|
|
26
|
+
import { getDb, initializeDb } from "../../memory/db.js";
|
|
27
|
+
import { contactChannels, contacts } from "../../memory/schema.js";
|
|
28
|
+
import type { AuthContext } from "../auth/types.js";
|
|
29
|
+
import { handleAddGuardianChannel } from "./contact-routes.js";
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Helpers
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
function makeRequest(body: unknown): Request {
|
|
36
|
+
return new Request("http://localhost/v1/contacts/guardian/channel", {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: { "Content-Type": "application/json" },
|
|
39
|
+
body: JSON.stringify(body),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function makeServiceAuthContext(
|
|
44
|
+
overrides: Partial<AuthContext> = {},
|
|
45
|
+
): AuthContext {
|
|
46
|
+
return {
|
|
47
|
+
subject: "platform-service",
|
|
48
|
+
principalType: "svc_gateway",
|
|
49
|
+
assistantId: "test-assistant",
|
|
50
|
+
actorPrincipalId: undefined,
|
|
51
|
+
scopeProfile: "gateway_service_v1",
|
|
52
|
+
scopes: new Set(),
|
|
53
|
+
policyEpoch: 0,
|
|
54
|
+
...overrides,
|
|
55
|
+
} as AuthContext;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function makeActorAuthContext(
|
|
59
|
+
overrides: Partial<AuthContext> = {},
|
|
60
|
+
): AuthContext {
|
|
61
|
+
return {
|
|
62
|
+
subject: "test-subject",
|
|
63
|
+
principalType: "actor",
|
|
64
|
+
assistantId: "test-assistant",
|
|
65
|
+
actorPrincipalId: "guardian-principal-001",
|
|
66
|
+
scopeProfile: "actor_client_v1",
|
|
67
|
+
scopes: new Set(),
|
|
68
|
+
policyEpoch: 0,
|
|
69
|
+
...overrides,
|
|
70
|
+
} as AuthContext;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function seedGuardian(
|
|
74
|
+
displayName = "Test Guardian",
|
|
75
|
+
principalId = "guardian-principal-001",
|
|
76
|
+
): { contactId: string } {
|
|
77
|
+
const db = getDb();
|
|
78
|
+
const contactId = "guardian-001";
|
|
79
|
+
db.insert(contacts)
|
|
80
|
+
.values({
|
|
81
|
+
id: contactId,
|
|
82
|
+
displayName,
|
|
83
|
+
role: "guardian",
|
|
84
|
+
contactType: "human",
|
|
85
|
+
principalId,
|
|
86
|
+
createdAt: Date.now(),
|
|
87
|
+
updatedAt: Date.now(),
|
|
88
|
+
})
|
|
89
|
+
.run();
|
|
90
|
+
|
|
91
|
+
// Add a pre-existing channel (e.g. Telegram) so the guardian is verified
|
|
92
|
+
db.insert(contactChannels)
|
|
93
|
+
.values({
|
|
94
|
+
id: "ch-telegram-001",
|
|
95
|
+
contactId,
|
|
96
|
+
type: "telegram",
|
|
97
|
+
address: "@testguardian",
|
|
98
|
+
externalUserId: principalId,
|
|
99
|
+
status: "active",
|
|
100
|
+
createdAt: Date.now(),
|
|
101
|
+
updatedAt: Date.now(),
|
|
102
|
+
})
|
|
103
|
+
.run();
|
|
104
|
+
|
|
105
|
+
return { contactId };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Tests
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
describe("POST /v1/contacts/guardian/channel", () => {
|
|
113
|
+
beforeAll(() => {
|
|
114
|
+
initializeDb();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
beforeEach(() => {
|
|
118
|
+
const db = getDb();
|
|
119
|
+
db.delete(contactChannels).run();
|
|
120
|
+
db.delete(contacts).run();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ── Service token (platform) calls — the only permitted path ────────────
|
|
124
|
+
|
|
125
|
+
test("adds an email channel to an existing guardian (service auth)", async () => {
|
|
126
|
+
const { contactId } = seedGuardian();
|
|
127
|
+
|
|
128
|
+
const res = await handleAddGuardianChannel(
|
|
129
|
+
makeRequest({
|
|
130
|
+
type: "email",
|
|
131
|
+
address: "owner@example.com",
|
|
132
|
+
externalUserId: "owner@example.com",
|
|
133
|
+
}),
|
|
134
|
+
makeServiceAuthContext(),
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(res.status).toBe(200);
|
|
138
|
+
const body = (await res.json()) as {
|
|
139
|
+
ok: boolean;
|
|
140
|
+
contact: { id: string };
|
|
141
|
+
};
|
|
142
|
+
expect(body.ok).toBe(true);
|
|
143
|
+
expect(body.contact.id).toBe(contactId);
|
|
144
|
+
|
|
145
|
+
// Verify channel was persisted
|
|
146
|
+
const db = getDb();
|
|
147
|
+
const rows = db
|
|
148
|
+
.select()
|
|
149
|
+
.from(contactChannels)
|
|
150
|
+
.where(
|
|
151
|
+
and(
|
|
152
|
+
eq(contactChannels.contactId, contactId),
|
|
153
|
+
eq(contactChannels.type, "email"),
|
|
154
|
+
),
|
|
155
|
+
)
|
|
156
|
+
.all();
|
|
157
|
+
expect(rows.length).toBe(1);
|
|
158
|
+
expect(rows[0].address).toBe("owner@example.com");
|
|
159
|
+
expect(rows[0].status).toBe("active");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("returns 404 when no guardian exists (service auth)", async () => {
|
|
163
|
+
const res = await handleAddGuardianChannel(
|
|
164
|
+
makeRequest({
|
|
165
|
+
type: "email",
|
|
166
|
+
address: "owner@example.com",
|
|
167
|
+
externalUserId: "owner@example.com",
|
|
168
|
+
}),
|
|
169
|
+
makeServiceAuthContext(),
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
expect(res.status).toBe(404);
|
|
173
|
+
const body = (await res.json()) as {
|
|
174
|
+
error: { code: string; message: string };
|
|
175
|
+
};
|
|
176
|
+
expect(body.error.message).toContain("No guardian contact exists");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("returns 400 when type is missing (service auth)", async () => {
|
|
180
|
+
seedGuardian();
|
|
181
|
+
|
|
182
|
+
const res = await handleAddGuardianChannel(
|
|
183
|
+
makeRequest({ address: "owner@example.com" }),
|
|
184
|
+
makeServiceAuthContext(),
|
|
185
|
+
);
|
|
186
|
+
expect(res.status).toBe(400);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("returns 400 when address is missing (service auth)", async () => {
|
|
190
|
+
seedGuardian();
|
|
191
|
+
|
|
192
|
+
const res = await handleAddGuardianChannel(
|
|
193
|
+
makeRequest({ type: "email", externalUserId: "owner@example.com" }),
|
|
194
|
+
makeServiceAuthContext(),
|
|
195
|
+
);
|
|
196
|
+
expect(res.status).toBe(400);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("returns 400 when externalUserId is missing (service auth)", async () => {
|
|
200
|
+
seedGuardian();
|
|
201
|
+
|
|
202
|
+
const res = await handleAddGuardianChannel(
|
|
203
|
+
makeRequest({ type: "email", address: "owner@example.com" }),
|
|
204
|
+
makeServiceAuthContext(),
|
|
205
|
+
);
|
|
206
|
+
expect(res.status).toBe(400);
|
|
207
|
+
const body = (await res.json()) as {
|
|
208
|
+
error: { code: string; message: string };
|
|
209
|
+
};
|
|
210
|
+
expect(body.error.message).toContain("externalUserId is required");
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("preserves existing channels when adding new ones (service auth)", async () => {
|
|
214
|
+
const { contactId } = seedGuardian();
|
|
215
|
+
|
|
216
|
+
await handleAddGuardianChannel(
|
|
217
|
+
makeRequest({
|
|
218
|
+
type: "email",
|
|
219
|
+
address: "owner@example.com",
|
|
220
|
+
externalUserId: "owner@example.com",
|
|
221
|
+
}),
|
|
222
|
+
makeServiceAuthContext(),
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// Guardian should still have the telegram channel + new email channel
|
|
226
|
+
const db = getDb();
|
|
227
|
+
const rows = db
|
|
228
|
+
.select()
|
|
229
|
+
.from(contactChannels)
|
|
230
|
+
.where(eq(contactChannels.contactId, contactId))
|
|
231
|
+
.all();
|
|
232
|
+
|
|
233
|
+
const types = rows.map((r) => r.type).sort();
|
|
234
|
+
expect(types).toEqual(["email", "telegram"]);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test("defaults channel status to active (service auth)", async () => {
|
|
238
|
+
seedGuardian();
|
|
239
|
+
|
|
240
|
+
const res = await handleAddGuardianChannel(
|
|
241
|
+
makeRequest({
|
|
242
|
+
type: "email",
|
|
243
|
+
address: "owner@example.com",
|
|
244
|
+
externalUserId: "owner@example.com",
|
|
245
|
+
}),
|
|
246
|
+
makeServiceAuthContext(),
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
expect(res.status).toBe(200);
|
|
250
|
+
const body = (await res.json()) as {
|
|
251
|
+
ok: boolean;
|
|
252
|
+
contact: { channels: { type: string; status: string }[] };
|
|
253
|
+
};
|
|
254
|
+
const emailChannel = body.contact.channels.find(
|
|
255
|
+
(ch) => ch.type === "email",
|
|
256
|
+
);
|
|
257
|
+
expect(emailChannel?.status).toBe("active");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// ── Actor calls — all rejected (security fix ATL-102) ──────────────────
|
|
261
|
+
|
|
262
|
+
test("rejects actor calls with 403 (guardian takeover prevention)", async () => {
|
|
263
|
+
seedGuardian();
|
|
264
|
+
|
|
265
|
+
const res = await handleAddGuardianChannel(
|
|
266
|
+
makeRequest({
|
|
267
|
+
type: "email",
|
|
268
|
+
address: "owner@example.com",
|
|
269
|
+
externalUserId: "owner@example.com",
|
|
270
|
+
}),
|
|
271
|
+
makeActorAuthContext(),
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
expect(res.status).toBe(403);
|
|
275
|
+
const body = (await res.json()) as {
|
|
276
|
+
error: { code: string; message: string };
|
|
277
|
+
};
|
|
278
|
+
expect(body.error.code).toBe("FORBIDDEN");
|
|
279
|
+
expect(body.error.message).toContain("restricted to platform service");
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("rejects actor calls even from the bound guardian", async () => {
|
|
283
|
+
const guardianPrincipalId = "guardian-principal-001";
|
|
284
|
+
seedGuardian("Guardian", guardianPrincipalId);
|
|
285
|
+
|
|
286
|
+
// Caller IS the guardian — but actor calls are still rejected
|
|
287
|
+
const res = await handleAddGuardianChannel(
|
|
288
|
+
makeRequest({
|
|
289
|
+
type: "email",
|
|
290
|
+
address: "guardian@example.com",
|
|
291
|
+
externalUserId: "guardian@example.com",
|
|
292
|
+
}),
|
|
293
|
+
makeActorAuthContext({ actorPrincipalId: guardianPrincipalId }),
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
expect(res.status).toBe(403);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Route handlers for contact management endpoints.
|
|
3
3
|
*
|
|
4
|
-
* GET /v1/contacts
|
|
5
|
-
* POST /v1/contacts
|
|
6
|
-
* GET /v1/contacts/:id
|
|
7
|
-
* DELETE /v1/contacts/:id
|
|
8
|
-
* POST /v1/contacts/merge
|
|
4
|
+
* GET /v1/contacts — list contacts
|
|
5
|
+
* POST /v1/contacts — create or update a contact
|
|
6
|
+
* GET /v1/contacts/:id — get a contact by ID
|
|
7
|
+
* DELETE /v1/contacts/:id — delete a contact
|
|
8
|
+
* POST /v1/contacts/merge — merge two contacts
|
|
9
|
+
* POST /v1/contacts/guardian/channel — add a channel to the guardian contact
|
|
9
10
|
* PATCH /v1/contact-channels/:contactChannelId — update a contact channel's status/policy
|
|
10
11
|
*/
|
|
11
12
|
|
|
@@ -13,6 +14,7 @@ import { z } from "zod";
|
|
|
13
14
|
|
|
14
15
|
import {
|
|
15
16
|
deleteContact,
|
|
17
|
+
findGuardianContact,
|
|
16
18
|
getAssistantContactMetadata,
|
|
17
19
|
getChannelById,
|
|
18
20
|
getContact,
|
|
@@ -32,6 +34,7 @@ import type {
|
|
|
32
34
|
ContactType,
|
|
33
35
|
} from "../../contacts/types.js";
|
|
34
36
|
import { resolveGuardianName } from "../../prompts/user-reference.js";
|
|
37
|
+
import type { AuthContext } from "../auth/types.js";
|
|
35
38
|
import { httpError } from "../http-errors.js";
|
|
36
39
|
import type { RouteDefinition } from "../http-router.js";
|
|
37
40
|
|
|
@@ -410,6 +413,100 @@ export async function handleUpdateContactChannel(
|
|
|
410
413
|
});
|
|
411
414
|
}
|
|
412
415
|
|
|
416
|
+
/**
|
|
417
|
+
* POST /v1/contacts/guardian/channel
|
|
418
|
+
*
|
|
419
|
+
* Add a single channel to the existing guardian contact.
|
|
420
|
+
* If no guardian contact exists, returns 404.
|
|
421
|
+
* If the caller is not the guardian, returns 403.
|
|
422
|
+
*
|
|
423
|
+
* Used by the guardian to auto-verify their own channel.
|
|
424
|
+
*/
|
|
425
|
+
export async function handleAddGuardianChannel(
|
|
426
|
+
req: Request,
|
|
427
|
+
authContext: AuthContext,
|
|
428
|
+
): Promise<Response> {
|
|
429
|
+
// This endpoint is restricted to gateway service tokens only — the
|
|
430
|
+
// platform calls it during email registration to auto-verify the owner's
|
|
431
|
+
// email as a guardian channel. Direct actor/local calls are not permitted
|
|
432
|
+
// because the endpoint bypasses normal channel verification (no code sent,
|
|
433
|
+
// no confirmation) and would allow guardian channel takeover (ATL-102).
|
|
434
|
+
if (authContext.principalType !== "svc_gateway") {
|
|
435
|
+
return httpError(
|
|
436
|
+
"FORBIDDEN",
|
|
437
|
+
"This endpoint is restricted to platform service calls",
|
|
438
|
+
403,
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const body = (await req.json()) as {
|
|
443
|
+
type: string;
|
|
444
|
+
address: string;
|
|
445
|
+
externalUserId?: string;
|
|
446
|
+
externalChatId?: string;
|
|
447
|
+
status?: string;
|
|
448
|
+
policy?: string;
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
if (!body.type || !body.address) {
|
|
452
|
+
return httpError("BAD_REQUEST", "type and address are required", 400);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (!body.externalUserId) {
|
|
456
|
+
return httpError(
|
|
457
|
+
"BAD_REQUEST",
|
|
458
|
+
"externalUserId is required for trust resolution",
|
|
459
|
+
400,
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (body.status !== undefined && !isChannelStatus(body.status)) {
|
|
464
|
+
return httpError(
|
|
465
|
+
"BAD_REQUEST",
|
|
466
|
+
`Invalid channel status "${body.status}". Must be one of: ${VALID_CHANNEL_STATUSES.join(", ")}`,
|
|
467
|
+
400,
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (body.policy !== undefined && !isChannelPolicy(body.policy)) {
|
|
472
|
+
return httpError(
|
|
473
|
+
"BAD_REQUEST",
|
|
474
|
+
`Invalid channel policy "${body.policy}". Must be one of: ${VALID_CHANNEL_POLICIES.join(", ")}`,
|
|
475
|
+
400,
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const guardian = findGuardianContact();
|
|
480
|
+
if (!guardian) {
|
|
481
|
+
return httpError(
|
|
482
|
+
"NOT_FOUND",
|
|
483
|
+
"No guardian contact exists. The guardian must be verified on at least one channel first.",
|
|
484
|
+
404,
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Upsert the guardian with the new channel added.
|
|
489
|
+
const updated = upsertContact({
|
|
490
|
+
id: guardian.id,
|
|
491
|
+
displayName: guardian.displayName,
|
|
492
|
+
role: "guardian",
|
|
493
|
+
channels: [
|
|
494
|
+
{
|
|
495
|
+
...body,
|
|
496
|
+
status: (body.status as ChannelStatus) ?? "active",
|
|
497
|
+
policy: body.policy as ChannelPolicy | undefined,
|
|
498
|
+
verifiedAt: Date.now(),
|
|
499
|
+
verifiedVia: "guardian_channel_endpoint",
|
|
500
|
+
},
|
|
501
|
+
],
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
return Response.json(
|
|
505
|
+
{ ok: true, contact: withGuardianNameOverride(updated) },
|
|
506
|
+
{ status: 200 },
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
|
|
413
510
|
// ---------------------------------------------------------------------------
|
|
414
511
|
// Route definitions
|
|
415
512
|
// ---------------------------------------------------------------------------
|
|
@@ -476,6 +573,36 @@ export function contactRouteDefinitions(): RouteDefinition[] {
|
|
|
476
573
|
}),
|
|
477
574
|
handler: async ({ req }) => handleMergeContacts(req),
|
|
478
575
|
},
|
|
576
|
+
{
|
|
577
|
+
endpoint: "contacts/guardian/channel",
|
|
578
|
+
method: "POST",
|
|
579
|
+
policyKey: "contacts",
|
|
580
|
+
summary: "Add a channel to the guardian contact",
|
|
581
|
+
description: "Used by the guardian to auto-verify their own channel.",
|
|
582
|
+
tags: ["contacts"],
|
|
583
|
+
requestBody: z.object({
|
|
584
|
+
type: z.string().describe("Channel type, e.g. 'email'"),
|
|
585
|
+
address: z
|
|
586
|
+
.string()
|
|
587
|
+
.describe("Channel address, e.g. 'user@example.com'"),
|
|
588
|
+
externalUserId: z
|
|
589
|
+
.string()
|
|
590
|
+
.describe("External user ID for trust resolution"),
|
|
591
|
+
status: z
|
|
592
|
+
.string()
|
|
593
|
+
.optional()
|
|
594
|
+
.describe("Channel status (default: active)"),
|
|
595
|
+
}),
|
|
596
|
+
responseBody: z.object({
|
|
597
|
+
ok: z.boolean(),
|
|
598
|
+
contact: z
|
|
599
|
+
.object({})
|
|
600
|
+
.passthrough()
|
|
601
|
+
.describe("Updated guardian contact"),
|
|
602
|
+
}),
|
|
603
|
+
handler: async ({ req, authContext }) =>
|
|
604
|
+
handleAddGuardianChannel(req, authContext),
|
|
605
|
+
},
|
|
479
606
|
{
|
|
480
607
|
endpoint: "contact-channels/:contactChannelId",
|
|
481
608
|
method: "PATCH",
|
|
@@ -3,35 +3,22 @@
|
|
|
3
3
|
*
|
|
4
4
|
* POST /v1/conversations/:id/analyze — analyze a conversation via a new
|
|
5
5
|
* agent loop that produces a structured self-assessment.
|
|
6
|
+
*
|
|
7
|
+
* The heavy lifting lives in `services/analyze-conversation.ts`. This module
|
|
8
|
+
* is thin glue: map the route params to the service, translate service
|
|
9
|
+
* errors into HTTP errors, and build the success response.
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
|
-
import type
|
|
9
|
-
import {
|
|
10
|
-
addMessage,
|
|
11
|
-
createConversation,
|
|
12
|
-
getConversation,
|
|
13
|
-
getMessages,
|
|
14
|
-
} from "../../memory/conversation-crud.js";
|
|
15
|
-
import { resolveConversationId } from "../../memory/conversation-key-store.js";
|
|
16
|
-
import { getLogger } from "../../util/logger.js";
|
|
17
|
-
import { buildAssistantEvent } from "../assistant-event.js";
|
|
18
|
-
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
|
|
19
|
-
import { httpError } from "../http-errors.js";
|
|
12
|
+
import { httpError, type HttpErrorCode } from "../http-errors.js";
|
|
20
13
|
import type { RouteDefinition } from "../http-router.js";
|
|
21
|
-
import
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
// Dependency types — injected by the daemon at wiring time
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
14
|
+
import {
|
|
15
|
+
analyzeConversation,
|
|
16
|
+
type ConversationAnalysisDeps,
|
|
17
|
+
} from "../services/analyze-conversation.js";
|
|
28
18
|
|
|
29
|
-
export
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
id: string,
|
|
33
|
-
) => Record<string, unknown> | null;
|
|
34
|
-
}
|
|
19
|
+
// Re-export the dependency type so existing callers can continue importing it
|
|
20
|
+
// from this module.
|
|
21
|
+
export type { ConversationAnalysisDeps };
|
|
35
22
|
|
|
36
23
|
// ---------------------------------------------------------------------------
|
|
37
24
|
// Route definitions
|
|
@@ -50,132 +37,25 @@ export function conversationAnalysisRouteDefinitions(
|
|
|
50
37
|
"Create a new conversation with a structured self-assessment of an existing conversation.",
|
|
51
38
|
tags: ["conversations"],
|
|
52
39
|
handler: async ({ params }) => {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return httpError(
|
|
57
|
-
"NOT_FOUND",
|
|
58
|
-
`Conversation ${params.id} not found`,
|
|
59
|
-
404,
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// b. Load the conversation
|
|
64
|
-
const conversation = getConversation(resolvedId);
|
|
65
|
-
if (!conversation) {
|
|
66
|
-
return httpError(
|
|
67
|
-
"NOT_FOUND",
|
|
68
|
-
`Conversation ${resolvedId} not found`,
|
|
69
|
-
404,
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// c. Reject private conversations
|
|
74
|
-
if (conversation.conversationType === "private") {
|
|
75
|
-
return httpError(
|
|
76
|
-
"FORBIDDEN",
|
|
77
|
-
"Private conversations cannot be analyzed",
|
|
78
|
-
403,
|
|
79
|
-
);
|
|
80
|
-
}
|
|
40
|
+
const result = await analyzeConversation(params.id, deps, {
|
|
41
|
+
trigger: "manual",
|
|
42
|
+
});
|
|
81
43
|
|
|
82
|
-
|
|
83
|
-
const existingMessages = getMessages(resolvedId);
|
|
84
|
-
if (existingMessages.length === 0) {
|
|
44
|
+
if ("error" in result) {
|
|
85
45
|
return httpError(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
46
|
+
result.error.kind as HttpErrorCode,
|
|
47
|
+
result.error.message,
|
|
48
|
+
result.error.status,
|
|
89
49
|
);
|
|
90
50
|
}
|
|
91
51
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
await import("../../export/transcript-formatter.js");
|
|
95
|
-
const transcript = buildAnalysisTranscript(resolvedId);
|
|
96
|
-
|
|
97
|
-
// f. Create a new conversation for the analysis
|
|
98
|
-
const newConv = createConversation({
|
|
99
|
-
title: `Analysis: ${conversation.title ?? "Untitled"}`,
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// g. Build the analysis prompt
|
|
103
|
-
const prompt = `<transcript>
|
|
104
|
-
${transcript}
|
|
105
|
-
</transcript>
|
|
106
|
-
|
|
107
|
-
Analyze the conversation above. Provide a structured self-assessment:
|
|
108
|
-
|
|
109
|
-
1. **Summary**: What was the user trying to accomplish? What was the outcome?
|
|
110
|
-
2. **What went well**: Effective tool usage, good reasoning, helpful responses, problem-solving patterns.
|
|
111
|
-
3. **What went wrong**: Errors, unnecessary tool calls, incorrect assumptions, wasted turns, misunderstandings.
|
|
112
|
-
4. **Root causes**: Why did failures happen? Missing context? Wrong approach? Tool limitations?
|
|
113
|
-
5. **Recommendations**: Specific, actionable improvements for similar conversations next time.
|
|
114
|
-
6. **Code & tooling changes**: Are there any changes to files you should make based on these learnings? Are there any skills or scripts that are worth creating or modifying? Don't make these changes yet — just provide your analysis.
|
|
115
|
-
|
|
116
|
-
Be honest and specific. Reference particular moments in the transcript. Focus on patterns that generalize beyond this specific conversation.
|
|
117
|
-
|
|
118
|
-
Do not use tools during analysis. If you identify insights worth remembering for future conversations, include them in the response as explicit memory candidates instead of saving them directly.`;
|
|
119
|
-
|
|
120
|
-
// h. Persist the user message
|
|
121
|
-
const message = await addMessage(
|
|
122
|
-
newConv.id,
|
|
123
|
-
"user",
|
|
124
|
-
JSON.stringify([{ type: "text", text: prompt }]),
|
|
125
|
-
{ provenanceTrustClass: "unknown" as const },
|
|
52
|
+
const detail = deps.buildConversationDetailResponse(
|
|
53
|
+
result.analysisConversationId,
|
|
126
54
|
);
|
|
127
|
-
const messageId = message.id;
|
|
128
|
-
|
|
129
|
-
// i. Load the conversation into memory with untrusted analysis context
|
|
130
|
-
const analysisConversation =
|
|
131
|
-
await deps.sendMessageDeps.getOrCreateConversation(newConv.id);
|
|
132
|
-
analysisConversation.setTrustContext({
|
|
133
|
-
trustClass: "unknown",
|
|
134
|
-
sourceChannel: "vellum",
|
|
135
|
-
});
|
|
136
|
-
await analysisConversation.ensureActorScopedHistory();
|
|
137
|
-
// Analysis runs over attacker-influenced transcript content, so do not
|
|
138
|
-
// expose any tools, even when a live client is available.
|
|
139
|
-
analysisConversation.setSubagentAllowedTools(new Set<string>());
|
|
140
|
-
|
|
141
|
-
const hasLiveSubscriber =
|
|
142
|
-
deps.sendMessageDeps.assistantEventHub.hasSubscribersForEvent({
|
|
143
|
-
assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
|
|
144
|
-
conversationId: newConv.id,
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// j. Build onEvent using inline hub publisher
|
|
148
|
-
const onEvent = (msg: ServerMessage) => {
|
|
149
|
-
deps.sendMessageDeps.assistantEventHub.publish(
|
|
150
|
-
buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, msg, newConv.id),
|
|
151
|
-
);
|
|
152
|
-
};
|
|
153
|
-
analysisConversation.updateClient(onEvent, !hasLiveSubscriber);
|
|
154
|
-
|
|
155
|
-
// k. Set up processing state (required by runAgentLoop guard)
|
|
156
|
-
analysisConversation.processing = true;
|
|
157
|
-
analysisConversation.abortController = new AbortController();
|
|
158
|
-
analysisConversation.currentRequestId = crypto.randomUUID();
|
|
159
|
-
|
|
160
|
-
// l. Fire-and-forget the agent loop
|
|
161
|
-
analysisConversation
|
|
162
|
-
.runAgentLoop(prompt, messageId, onEvent, {
|
|
163
|
-
isInteractive: false,
|
|
164
|
-
isUserMessage: true,
|
|
165
|
-
})
|
|
166
|
-
.catch((err) => {
|
|
167
|
-
log.error(
|
|
168
|
-
{ err, conversationId: newConv.id },
|
|
169
|
-
"Analysis agent loop failed",
|
|
170
|
-
);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// m. Return the new conversation detail
|
|
174
|
-
const detail = deps.buildConversationDetailResponse(newConv.id);
|
|
175
55
|
if (!detail) {
|
|
176
56
|
return httpError(
|
|
177
57
|
"INTERNAL_ERROR",
|
|
178
|
-
`Analysis conversation ${
|
|
58
|
+
`Analysis conversation ${result.analysisConversationId} could not be loaded`,
|
|
179
59
|
500,
|
|
180
60
|
);
|
|
181
61
|
}
|