@vellumai/assistant 0.11.2 → 0.11.3-staging.2
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 +58 -3
- package/docs/trusted-contact-access.md +9 -5
- package/docs/vellum-doctor.md +195 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
- package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
- package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
- package/openapi.yaml +589 -30
- package/package.json +1 -1
- package/src/__tests__/agent-image-optimize.test.ts +4 -4
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
- package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
- package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/answered-question-persistence.test.ts +240 -0
- package/src/__tests__/anthropic-provider.test.ts +165 -30
- package/src/__tests__/app-control-flow.test.ts +9 -8
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
- package/src/__tests__/attachments-store.test.ts +130 -68
- package/src/__tests__/attachments.test.ts +36 -36
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
- package/src/__tests__/canned-reply-release.test.ts +86 -0
- package/src/__tests__/channel-guardian.test.ts +59 -29
- package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
- package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
- package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
- package/src/__tests__/consult-deadline.test.ts +65 -0
- package/src/__tests__/conversation-agent-loop.test.ts +438 -2
- package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
- package/src/__tests__/conversation-disk-view.test.ts +4 -4
- package/src/__tests__/conversation-error.test.ts +22 -3
- package/src/__tests__/conversation-fork-crud.test.ts +5 -1
- package/src/__tests__/conversation-fork-referential.test.ts +352 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
- package/src/__tests__/conversation-group-tools.test.ts +32 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
- package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
- package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
- package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
- package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
- package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
- package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
- package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
- package/src/__tests__/conversation-queue.test.ts +433 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
- package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
- package/src/__tests__/conversation-store.test.ts +26 -10
- package/src/__tests__/conversation-summarize-route.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
- package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
- package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
- package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
- package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
- package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
- package/src/__tests__/credential-prompt-route.test.ts +57 -7
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +89 -28
- package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
- package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
- package/src/__tests__/disk-pressure-guard.test.ts +55 -55
- package/src/__tests__/disk-pressure-routes.test.ts +10 -10
- package/src/__tests__/disk-pressure-tools.test.ts +2 -2
- package/src/__tests__/disk-usage.test.ts +35 -20
- package/src/__tests__/document-sync-tags.test.ts +379 -0
- package/src/__tests__/document-update-default-surface.test.ts +28 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
- package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
- package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
- package/src/__tests__/file-list-tool.test.ts +12 -10
- package/src/__tests__/file-ops-service.test.ts +151 -44
- package/src/__tests__/file-read-container-boundary.test.ts +137 -0
- package/src/__tests__/file-read-tool.test.ts +21 -9
- package/src/__tests__/file-url-path-guard.test.ts +83 -0
- package/src/__tests__/filesystem-tools.test.ts +82 -66
- package/src/__tests__/guardian-outbound-http.test.ts +8 -8
- package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
- package/src/__tests__/helpers/hub-clients.ts +42 -0
- package/src/__tests__/helpers/mock-conversation.ts +46 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
- package/src/__tests__/history-repair.test.ts +112 -12
- package/src/__tests__/host-bash-proxy.test.ts +10 -5
- package/src/__tests__/host-bash-routes.test.ts +67 -0
- package/src/__tests__/host-browser-proxy.test.ts +2 -2
- package/src/__tests__/host-cu-proxy.test.ts +3 -7
- package/src/__tests__/host-file-proxy.test.ts +3 -7
- package/src/__tests__/host-file-read-tool.test.ts +5 -1
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
- package/src/__tests__/http-conversation-lineage.test.ts +2 -5
- package/src/__tests__/http-user-message-parity.test.ts +44 -0
- package/src/__tests__/identity-routes.test.ts +13 -13
- package/src/__tests__/image-conversion.test.ts +29 -26
- package/src/__tests__/image-source-path-reinject.test.ts +2 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
- package/src/__tests__/introduction-card-resolver.test.ts +6 -1
- package/src/__tests__/list-messages-attachments.test.ts +56 -32
- package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
- package/src/__tests__/list-messages-page-latest.test.ts +61 -61
- package/src/__tests__/list-messages-provider-error.test.ts +6 -6
- package/src/__tests__/list-messages-queued.test.ts +49 -6
- package/src/__tests__/list-messages-system-card.test.ts +4 -4
- package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
- package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
- package/src/__tests__/llm-usage-store.test.ts +55 -0
- package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
- package/src/__tests__/media-stream-server-integration.test.ts +36 -0
- package/src/__tests__/migration-export-http.test.ts +1 -1
- package/src/__tests__/migration-import-commit-http.test.ts +1 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
- package/src/__tests__/migration-validate-http.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +65 -3
- package/src/__tests__/notification-platform-adapter.test.ts +104 -0
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
- package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
- package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
- package/src/__tests__/path-policy.test.ts +164 -1
- package/src/__tests__/persist-media-references.test.ts +2 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
- package/src/__tests__/persist-unsendable-image.test.ts +23 -23
- package/src/__tests__/persona-resolver.test.ts +154 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
- package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
- package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
- package/src/__tests__/profiler-routes.test.ts +13 -13
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
- package/src/__tests__/proxy-approval-callback.test.ts +3 -3
- package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
- package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
- package/src/__tests__/resolve-trust-class.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
- package/src/__tests__/run-due-schedules.test.ts +174 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
- package/src/__tests__/schedule-routes.test.ts +730 -8
- package/src/__tests__/schedule-store.test.ts +540 -0
- package/src/__tests__/schedule-tools.test.ts +103 -0
- package/src/__tests__/scheduler-wake.test.ts +5 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
- package/src/__tests__/secure-keys.test.ts +91 -0
- package/src/__tests__/server-history-render.test.ts +97 -7
- package/src/__tests__/size-guard.test.ts +8 -8
- package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
- package/src/__tests__/skill-tool-factory.test.ts +247 -0
- package/src/__tests__/skills-install-staging.test.ts +15 -6
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
- package/src/__tests__/slack-inbound-verification.test.ts +4 -1
- package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
- package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
- package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
- package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
- package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
- package/src/__tests__/subagent-disposal.test.ts +10 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
- package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
- package/src/__tests__/subagent-manager-notify.test.ts +148 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/subagent-persistence.test.ts +141 -0
- package/src/__tests__/subagent-role-registry.test.ts +172 -63
- package/src/__tests__/subagent-role-resolution.test.ts +88 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
- package/src/__tests__/subagent-terminal-message.test.ts +88 -1
- package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
- package/src/__tests__/subagent-tools.test.ts +1901 -108
- package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
- package/src/__tests__/system-prompt.test.ts +82 -7
- package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
- package/src/__tests__/tool-result-spool.test.ts +74 -18
- package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
- package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
- package/src/__tests__/tools-audio-read.test.ts +10 -10
- package/src/__tests__/tools-get-route.test.ts +85 -12
- package/src/__tests__/tools-list-cli.test.ts +171 -0
- package/src/__tests__/top-level-renderer.test.ts +54 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
- package/src/__tests__/turn-tail-chain.test.ts +135 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
- package/src/__tests__/ui-visual-surface.test.ts +5 -4
- package/src/__tests__/ui-work-result-surface.test.ts +5 -4
- package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
- package/src/__tests__/usage-routes.test.ts +62 -0
- package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
- package/src/__tests__/visible-app-context.test.ts +20 -4
- package/src/__tests__/web-search-history.test.ts +96 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
- package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
- package/src/acp/failure-error.ts +3 -5
- package/src/agent/attachments.ts +26 -24
- package/src/agent/history-repair/history-repair.ts +27 -26
- package/src/agent/image-optimize.ts +6 -6
- package/src/agent/loop.ts +40 -22
- package/src/agent/message-types.ts +3 -3
- package/src/api/constants/document-tools.ts +55 -0
- package/src/api/events/question-answered.ts +53 -0
- package/src/api/events/tool-result.ts +7 -0
- package/src/api/index.ts +13 -0
- package/src/api/responses/conversation-message.ts +11 -0
- package/src/api/surfaces-oauth-connect.test.ts +96 -0
- package/src/api/surfaces.ts +36 -8
- package/src/approvals/guardian-channel-delivery.ts +83 -0
- package/src/approvals/guardian-expiry-notifier.ts +13 -11
- package/src/approvals/guardian-request-resolvers.ts +222 -135
- package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
- package/src/calls/media-stream-server.ts +9 -1
- package/src/calls/voice-session-bridge.ts +38 -0
- package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
- package/src/channels/config.ts +7 -2
- package/src/channels/gateway-verification-sessions.ts +7 -2
- package/src/channels/types.ts +7 -1
- package/src/cli/commands/__tests__/cache.test.ts +7 -4
- package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
- package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
- package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
- package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
- package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
- package/src/cli/commands/__tests__/plugins.test.ts +692 -0
- package/src/cli/commands/__tests__/schedules.test.ts +136 -37
- package/src/cli/commands/conversations.ts +7 -3
- package/src/cli/commands/credentials.help.ts +7 -0
- package/src/cli/commands/inference-profiles.ts +9 -3
- package/src/cli/commands/inference.help.ts +16 -2
- package/src/cli/commands/mcp.help.ts +4 -2
- package/src/cli/commands/memory/memory-retrospective.ts +18 -0
- package/src/cli/commands/notifications.ts +3 -6
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
- package/src/cli/commands/oauth/connect.test.ts +26 -1
- package/src/cli/commands/oauth/connect.ts +4 -1
- package/src/cli/commands/oauth/index.help.ts +3 -1
- package/src/cli/commands/oauth/providers.ts +3 -0
- package/src/cli/commands/plugins.help.ts +25 -2
- package/src/cli/commands/plugins.ts +289 -10
- package/src/cli/commands/schedules.help.ts +14 -12
- package/src/cli/commands/schedules.ts +19 -5
- package/src/cli/commands/tools.help.ts +4 -2
- package/src/cli/commands/tools.ts +38 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
- package/src/cli/lib/install-from-github.ts +61 -0
- package/src/cli/lib/install-from-platform.ts +13 -2
- package/src/cli/lib/plugin-surfaces.ts +124 -4
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/cli.ts +1 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
- package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
- package/src/config/__tests__/profile-tool-support.test.ts +131 -0
- package/src/config/bundled-skills/AGENTS.md +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
- package/src/config/bundled-skills/schedule/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
- package/src/config/bundled-skills/subagent/SKILL.md +53 -22
- package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
- package/src/config/bundled-skills/visualize/SKILL.md +1 -1
- package/src/config/default-profile-catalog.ts +6 -2
- package/src/config/feature-flag-registry.json +25 -17
- package/src/config/llm-resolver.ts +45 -15
- package/src/config/loader.ts +70 -1
- package/src/config/profile-tool-support.ts +110 -0
- package/src/config/sanitize-for-transfer.ts +11 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/stt.test.ts +20 -5
- package/src/config/schemas/channels.ts +13 -0
- package/src/config/schemas/memory-retrospective.ts +25 -0
- package/src/config/schemas/plugin-updates.ts +55 -0
- package/src/config/schemas/services.ts +8 -4
- package/src/config/schemas/stt.ts +17 -5
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
- package/src/contacts/guardian-delivery-reader.ts +29 -2
- package/src/context/compactor.ts +4 -4
- package/src/context/post-turn-tool-result-truncation.ts +32 -16
- package/src/context/strip-injections.ts +42 -0
- package/src/context/tool-result-spool.ts +32 -18
- package/src/credential-execution/ces-runtime.ts +3 -0
- package/src/credential-execution/client.ts +22 -1
- package/src/credential-execution/process-manager.test.ts +99 -0
- package/src/credential-execution/process-manager.ts +62 -16
- package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
- package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
- package/src/daemon/channel-ui-capability.ts +4 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
- package/src/daemon/conversation-agent-loop.ts +318 -148
- package/src/daemon/conversation-attachments.ts +1 -1
- package/src/daemon/conversation-error.ts +8 -1
- package/src/daemon/conversation-initial-prompt.ts +16 -38
- package/src/daemon/conversation-lifecycle.ts +154 -32
- package/src/daemon/conversation-messaging.ts +106 -43
- package/src/daemon/conversation-process.ts +131 -33
- package/src/daemon/conversation-queue-manager.ts +10 -0
- package/src/daemon/conversation-runtime-assembly.ts +9 -4
- package/src/daemon/conversation-store.ts +52 -3
- package/src/daemon/conversation-surface-state.ts +9 -0
- package/src/daemon/conversation-surfaces.ts +403 -210
- package/src/daemon/conversation-tool-setup.ts +56 -7
- package/src/daemon/conversation-turn-finalize.ts +134 -89
- package/src/daemon/conversation.ts +111 -27
- package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
- package/src/daemon/disk-pressure-guard.ts +3 -3
- package/src/daemon/doordash-steps.ts +5 -5
- package/src/daemon/external-plugins-bootstrap.ts +7 -1
- package/src/daemon/handlers/config-channels.ts +48 -5
- package/src/daemon/handlers/conversation-history.ts +1 -1
- package/src/daemon/handlers/conversations.ts +62 -17
- package/src/daemon/handlers/shared.ts +12 -0
- package/src/daemon/handlers/skills.ts +11 -1
- package/src/daemon/identity-helpers.ts +38 -10
- package/src/daemon/install-assistant-command.ts +322 -0
- package/src/daemon/lifecycle.ts +14 -3
- package/src/daemon/message-types/conversations.ts +6 -7
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/persist-media-references.ts +23 -14
- package/src/daemon/process-message.ts +6 -6
- package/src/daemon/tool-setup-types.ts +19 -122
- package/src/daemon/tool-side-effects.ts +17 -94
- package/src/daemon/trust-context-types.ts +39 -0
- package/src/daemon/trust-context.ts +14 -6
- package/src/daemon/turn-tail-chain.ts +73 -0
- package/src/daemon/web-search-history.ts +47 -33
- package/src/heartbeat/heartbeat-service.ts +6 -6
- package/src/ipc/__tests__/socket-path.test.ts +21 -0
- package/src/ipc/assistant-server.ts +17 -10
- package/src/ipc/cli-client.ts +10 -1
- package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
- package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
- package/src/ipc/socket-cleanup.ts +9 -7
- package/src/ipc/socket-path.ts +4 -96
- package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
- package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
- package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
- package/src/live-voice/__tests__/protocol.test.ts +52 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
- package/src/live-voice/live-voice-archive.ts +13 -13
- package/src/live-voice/live-voice-connection.ts +7 -1
- package/src/live-voice/live-voice-photo.ts +169 -0
- package/src/live-voice/live-voice-session.ts +108 -4
- package/src/live-voice/protocol.ts +89 -1
- package/src/mcp/client.ts +10 -0
- package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
- package/src/messaging/providers/callback-routing.ts +1 -0
- package/src/messaging/providers/discord/api.ts +312 -0
- package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
- package/src/messaging/providers/discord/render.test.ts +204 -0
- package/src/messaging/providers/discord/render.ts +195 -0
- package/src/messaging/providers/discord/send.test.ts +179 -0
- package/src/messaging/providers/discord/send.ts +212 -0
- package/src/messaging/providers/discord/transport.ts +81 -0
- package/src/messaging/providers/index.ts +3 -1
- package/src/messaging/providers/retry-policy.test.ts +247 -0
- package/src/messaging/providers/retry-policy.ts +206 -0
- package/src/messaging/providers/slack/mention-source.test.ts +285 -0
- package/src/messaging/providers/slack/mention-source.ts +287 -0
- package/src/messaging/providers/telegram-bot/api.ts +47 -130
- package/src/messaging/providers/whatsapp/api.ts +47 -136
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
- package/src/monitoring/db-integrity-sample.ts +6 -1
- package/src/monitoring/plugin-auto-update.ts +359 -0
- package/src/monitoring/resource-sampler.ts +21 -5
- package/src/monitoring/worker.ts +13 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
- package/src/notifications/__tests__/broadcaster.test.ts +37 -1
- package/src/notifications/__tests__/copy-composer.test.ts +165 -0
- package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
- package/src/notifications/__tests__/notification-utils.test.ts +249 -0
- package/src/notifications/adapters/platform.ts +31 -2
- package/src/notifications/assistant-reply-producer.ts +79 -32
- package/src/notifications/broadcaster.ts +30 -10
- package/src/notifications/copy-composer.ts +69 -0
- package/src/notifications/emit-signal.ts +82 -15
- package/src/notifications/events-store.ts +10 -2
- package/src/notifications/home-feed-side-effect.ts +6 -3
- package/src/notifications/notification-utils.ts +173 -5
- package/src/notifications/signal.ts +12 -0
- package/src/oauth/AGENTS.md +4 -2
- package/src/oauth/byo-connection.ts +6 -1
- package/src/oauth/credential-token-resolver.ts +3 -10
- package/src/oauth/manual-token-connection.ts +29 -64
- package/src/oauth/manual-token-providers.ts +62 -0
- package/src/oauth/provider-serializer.ts +13 -0
- package/src/oauth/seed-providers.ts +62 -11
- package/src/onboarding/onboarding-events-store.ts +91 -1
- package/src/permissions/question-prompter.test.ts +4 -0
- package/src/permissions/question-prompter.ts +21 -4
- package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
- package/src/persistence/attachments-store.ts +15 -15
- package/src/persistence/conversation-attention-store.ts +30 -10
- package/src/persistence/conversation-bootstrap.ts +17 -0
- package/src/persistence/conversation-crud.ts +363 -97
- package/src/persistence/conversation-group-migration.ts +1 -1
- package/src/persistence/conversation-key-store.ts +38 -10
- package/src/persistence/conversation-lineage.ts +226 -0
- package/src/persistence/conversation-plugin-facade.ts +28 -3
- package/src/persistence/conversation-queries.ts +207 -33
- package/src/persistence/conversation-types.ts +107 -1
- package/src/persistence/jobs-store.ts +32 -8
- package/src/persistence/llm-usage-store.ts +22 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
- package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
- package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
- package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
- package/src/persistence/schema/conversations.ts +32 -0
- package/src/persistence/schema/infrastructure.ts +3 -0
- package/src/persistence/steps.ts +30 -0
- package/src/persistence/subagent-store.ts +148 -6
- package/src/platform/managed-speech.test.ts +20 -0
- package/src/platform/managed-speech.ts +9 -0
- package/src/plugin-api/index.ts +8 -0
- package/src/plugin-api/webhook-url.ts +151 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
- package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
- package/src/plugins/collect-source-versions.ts +4 -27
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
- package/src/plugins/defaults/exploration-drift/package.json +1 -1
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
- package/src/plugins/defaults/image-recovery/recover.ts +67 -57
- package/src/plugins/defaults/index.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
- package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
- package/src/plugins/defaults/memory/job-handlers.ts +87 -5
- package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
- package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
- package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
- package/src/plugins/defaults/memory/v2/router.ts +8 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
- package/src/plugins/external-plugin-loader.ts +17 -8
- package/src/plugins/installed-plugin-dirs.ts +101 -0
- package/src/plugins/mtime-cache.ts +34 -22
- package/src/plugins/types.ts +69 -8
- package/src/prompts/persona-resolver.ts +6 -2
- package/src/prompts/system-prompt.ts +72 -17
- package/src/providers/__tests__/inference.test.ts +40 -0
- package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
- package/src/providers/__tests__/retry-callsite.test.ts +67 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
- package/src/providers/anthropic/client.ts +112 -95
- package/src/providers/connection-resolution.ts +35 -1
- package/src/providers/gemini/client.ts +5 -5
- package/src/providers/inference/adapter-factory.ts +16 -10
- package/src/providers/inference/auth.ts +16 -0
- package/src/providers/inference/connection-availability.ts +164 -12
- package/src/providers/inference/connections.ts +25 -1
- package/src/providers/media-resolve.ts +21 -11
- package/src/providers/model-catalog.ts +15 -0
- package/src/providers/openai/chat-completions-provider.ts +4 -4
- package/src/providers/openai/responses-provider.ts +12 -25
- package/src/providers/registry.ts +9 -4
- package/src/providers/retry.ts +25 -0
- package/src/providers/server-tool-pairing.ts +134 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
- package/src/providers/speech-to-text/deepgram.ts +1 -0
- package/src/providers/speech-to-text/resolve.ts +80 -10
- package/src/providers/speech-to-text/vellum-managed.ts +7 -1
- package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
- package/src/providers/types.ts +26 -6
- package/src/providers/vellum-model-routing.ts +6 -7
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/agent-wake.test.ts +16 -16
- package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
- package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
- package/src/runtime/agent-wake.ts +63 -24
- package/src/runtime/assistant-event-hub.ts +87 -12
- package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
- package/src/runtime/auth/same-actor.ts +56 -8
- package/src/runtime/auth/scopes.ts +12 -0
- package/src/runtime/auth/types.ts +2 -0
- package/src/runtime/background-job-runner.ts +5 -27
- package/src/runtime/channel-readiness-service.ts +59 -3
- package/src/runtime/channel-readiness-types.ts +18 -0
- package/src/runtime/desktop-presence.ts +69 -0
- package/src/runtime/http-server.ts +12 -0
- package/src/runtime/local-actor-identity.ts +11 -9
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
- package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
- package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
- package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
- package/src/runtime/routes/archive-utils.ts +16 -12
- package/src/runtime/routes/attachment-routes.ts +13 -6
- package/src/runtime/routes/canned-reply-release.ts +54 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -27
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
- package/src/runtime/routes/client-routes.ts +114 -2
- package/src/runtime/routes/conversation-cli-routes.ts +10 -12
- package/src/runtime/routes/conversation-list-routes.ts +59 -10
- package/src/runtime/routes/conversation-management-routes.ts +47 -18
- package/src/runtime/routes/conversation-query-routes.ts +32 -53
- package/src/runtime/routes/conversation-routes.ts +360 -293
- package/src/runtime/routes/credential-prompt-routes.ts +23 -2
- package/src/runtime/routes/debug-routes.ts +4 -1
- package/src/runtime/routes/default-provider-routes.ts +7 -11
- package/src/runtime/routes/documents-routes.ts +44 -1
- package/src/runtime/routes/events-routes.ts +28 -1
- package/src/runtime/routes/host-app-control-routes.ts +2 -0
- package/src/runtime/routes/host-bash-routes.ts +2 -0
- package/src/runtime/routes/host-browser-routes.ts +2 -0
- package/src/runtime/routes/host-cu-routes.ts +2 -0
- package/src/runtime/routes/host-file-routes.ts +2 -0
- package/src/runtime/routes/host-transfer-routes.ts +4 -0
- package/src/runtime/routes/identity-routes.ts +6 -6
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
- package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
- package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
- package/src/runtime/routes/inference-profiles-routes.ts +152 -73
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
- package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
- package/src/runtime/routes/log-export-routes.ts +1 -1
- package/src/runtime/routes/notification-routes.ts +0 -14
- package/src/runtime/routes/oauth-providers.ts +1 -0
- package/src/runtime/routes/plugins-routes.ts +106 -1
- package/src/runtime/routes/profiler-routes.ts +4 -2
- package/src/runtime/routes/question-routes.ts +28 -10
- package/src/runtime/routes/retrospective-routes.ts +9 -3
- package/src/runtime/routes/schedule-routes.ts +210 -12
- package/src/runtime/routes/secret-routes.ts +40 -23
- package/src/runtime/routes/settings-routes.ts +41 -13
- package/src/runtime/routes/skills-routes.ts +79 -0
- package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
- package/src/runtime/routes/wake-conversation-routes.ts +23 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +24 -9
- package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
- package/src/runtime/sync/resource-sync-events.ts +79 -1
- package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -6
- package/src/runtime/verification-outbound-actions.ts +250 -222
- package/src/runtime/verification-templates.ts +41 -30
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
- package/src/schedule/inference-profile.ts +64 -3
- package/src/schedule/plugin-schedule-declarations.ts +518 -0
- package/src/schedule/plugin-schedule-reconciler.ts +605 -0
- package/src/schedule/plugin-schedules-gate.ts +23 -0
- package/src/schedule/schedule-store.ts +520 -26
- package/src/schedule/scheduler.ts +38 -0
- package/src/security/credential-backend.ts +51 -4
- package/src/security/encrypted-store.ts +64 -42
- package/src/security/secure-keys.ts +55 -7
- package/src/skills/catalog-install.ts +20 -7
- package/src/skills/inline-command-runner.ts +2 -7
- package/src/skills/skill-history.test.ts +269 -0
- package/src/skills/skill-history.ts +250 -0
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/stt/daemon-batch-transcriber.ts +8 -6
- package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
- package/src/subagent/consult-prompt.ts +3 -1
- package/src/subagent/consult-transcript.ts +1 -1
- package/src/subagent/manager.ts +304 -44
- package/src/subagent/progress-events.ts +35 -0
- package/src/subagent/role-resolution.ts +88 -0
- package/src/subagent/types.ts +302 -67
- package/src/subagent/validate-allowlists.ts +3 -4
- package/src/telegram/__tests__/webhook-health.test.ts +73 -1
- package/src/telegram/webhook-health.ts +101 -3
- package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
- package/src/telemetry/activation-funnel.ts +6 -1
- package/src/telemetry/live-voice-funnel.ts +75 -0
- package/src/telemetry/telemetry-event-sources.ts +6 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +2 -0
- package/src/telemetry/types.ts +23 -4
- package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
- package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
- package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
- package/src/tools/ask-question/ask-question-tool.ts +56 -2
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/calls/call-start.ts +8 -5
- package/src/tools/conversation-groups/move_to_group.ts +10 -12
- package/src/tools/document/document-tool.ts +11 -4
- package/src/tools/execution-timeout.ts +9 -2
- package/src/tools/executor.ts +7 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +7 -13
- package/src/tools/filesystem/search.ts +23 -21
- package/src/tools/filesystem/write.ts +4 -1
- package/src/tools/host-filesystem/edit.ts +7 -2
- package/src/tools/host-filesystem/read.ts +21 -8
- package/src/tools/host-filesystem/write.ts +10 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/mcp/mcp-tool-factory.ts +25 -1
- package/src/tools/permission-checker.ts +8 -3
- package/src/tools/schedule/delete.ts +13 -1
- package/src/tools/schedule/list.ts +14 -3
- package/src/tools/schedule/update.ts +56 -33
- package/src/tools/shared/filesystem/audio-read.ts +6 -4
- package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
- package/src/tools/shared/filesystem/image-read.ts +12 -12
- package/src/tools/shared/filesystem/path-policy.ts +70 -5
- package/src/tools/shared/filesystem/size-guard.ts +6 -6
- package/src/tools/shared/input-misuse.ts +72 -0
- package/src/tools/skills/scaffold-managed.ts +82 -0
- package/src/tools/skills/skill-tool-factory.ts +14 -1
- package/src/tools/subagent/consult-deadline.ts +12 -7
- package/src/tools/subagent/message.ts +3 -1
- package/src/tools/subagent/read.ts +66 -4
- package/src/tools/subagent/spawn.ts +500 -78
- package/src/tools/tool-approval-handler.ts +45 -4
- package/src/tools/types.ts +22 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
- package/src/tools/ui-surface/visual-validation.ts +2 -0
- package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
- package/src/usage/subagent-attribution.ts +108 -0
- package/src/util/__tests__/sqlite-retry.test.ts +112 -0
- package/src/util/abort-reasons.ts +16 -0
- package/src/util/ansi.ts +44 -0
- package/src/util/disk-usage.ts +17 -13
- package/src/util/image-conversion.ts +20 -13
- package/src/util/schedule-source-key.ts +23 -0
- package/src/util/worker-process.ts +7 -1
- package/src/workspace/git-service.ts +33 -9
- package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
- package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +15 -2
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
- package/src/daemon/install-symlink.ts +0 -207
- package/src/notifications/deferred-emit.ts +0 -147
|
@@ -79,12 +79,33 @@ const ERROR_RECENCY_WINDOW_MS = 15 * 60_000;
|
|
|
79
79
|
// ── Types ─────────────────────────────────────────────────────────────
|
|
80
80
|
|
|
81
81
|
export type TelegramWebhookHealthStatus =
|
|
82
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Telegram holds the URL the reconciler last registered, and reports no
|
|
84
|
+
* recent delivery error. This is the only status that may be presented as
|
|
85
|
+
* confirmed delivery.
|
|
86
|
+
*/
|
|
83
87
|
| "healthy"
|
|
84
88
|
/** Telegram holds no webhook URL at all — the channel is definitively dark. */
|
|
85
89
|
| "not_registered"
|
|
90
|
+
/**
|
|
91
|
+
* Telegram holds a webhook URL, but not the one the reconciler registered.
|
|
92
|
+
* A stale tunnel address or another deployment's callback delivers to
|
|
93
|
+
* somewhere this assistant is not listening, which reads identically to a
|
|
94
|
+
* healthy channel until someone sends a message. This is the case
|
|
95
|
+
* `last_error_date` cannot see on a quiet channel (see the module header's
|
|
96
|
+
* known limitation).
|
|
97
|
+
*/
|
|
98
|
+
| "url_mismatch"
|
|
86
99
|
/** Telegram reported a delivery error inside the recency window. */
|
|
87
100
|
| "delivery_failing"
|
|
101
|
+
/**
|
|
102
|
+
* A webhook is registered and not erroring, but the reconciler has not
|
|
103
|
+
* recorded which URL it registered, so ownership cannot be established.
|
|
104
|
+
* Expected transiently between the credential save and reconciliation, and
|
|
105
|
+
* persistently for deployments whose last registration predates recording.
|
|
106
|
+
* Distinct from `healthy` because "no fault found" is not "verified".
|
|
107
|
+
*/
|
|
108
|
+
| "unverified"
|
|
88
109
|
/** Preconditions unmet (no bot token / no webhook routing) — nothing to check. */
|
|
89
110
|
| "skipped"
|
|
90
111
|
/** Telegram was unreachable or answered unusably — health is unknown. */
|
|
@@ -97,6 +118,10 @@ export interface TelegramWebhookHealthResult {
|
|
|
97
118
|
lastErrorMessage?: string;
|
|
98
119
|
lastErrorDate?: number;
|
|
99
120
|
pendingUpdateCount?: number;
|
|
121
|
+
/** The URL Telegram currently holds, when one is registered. */
|
|
122
|
+
registeredUrl?: string;
|
|
123
|
+
/** The URL the reconciler recorded, when it has recorded one. */
|
|
124
|
+
expectedUrl?: string;
|
|
100
125
|
}
|
|
101
126
|
|
|
102
127
|
/**
|
|
@@ -133,6 +158,28 @@ function telegramApiBaseUrl(): string {
|
|
|
133
158
|
}
|
|
134
159
|
}
|
|
135
160
|
|
|
161
|
+
/**
|
|
162
|
+
* The webhook URL the gateway reconciler recorded on its last successful
|
|
163
|
+
* `setWebhook`, or undefined when it has never recorded one.
|
|
164
|
+
*
|
|
165
|
+
* This is deliberately a stored fact rather than a re-derivation. The gateway
|
|
166
|
+
* and the assistant already maintain mirrored copies of the routing tier order
|
|
167
|
+
* (`resolveExpectedTelegramWebhookUrl` and `hasWebhookRoutingConfigured`, which
|
|
168
|
+
* cross-reference each other in comments after LUM-2899), and a third copy here
|
|
169
|
+
* would be a third thing to keep in agreement. It also lets the managed path be
|
|
170
|
+
* checked at all: resolving a platform callback URL means POSTing a route
|
|
171
|
+
* registration, which a read-only health check must not do.
|
|
172
|
+
*/
|
|
173
|
+
function recordedWebhookUrl(): string | undefined {
|
|
174
|
+
try {
|
|
175
|
+
const recorded = getConfig().telegram.registeredWebhookUrl;
|
|
176
|
+
const trimmed = recorded?.trim();
|
|
177
|
+
return trimmed ? trimmed : undefined;
|
|
178
|
+
} catch {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
136
183
|
/** Name the channel concretely when the bot username is known. */
|
|
137
184
|
function channelLabel(): string {
|
|
138
185
|
const username = getTelegramBotUsername();
|
|
@@ -246,6 +293,27 @@ export async function checkTelegramWebhookHealth(): Promise<TelegramWebhookHealt
|
|
|
246
293
|
};
|
|
247
294
|
}
|
|
248
295
|
|
|
296
|
+
const registeredUrl = info.url.trim();
|
|
297
|
+
const expectedUrl = recordedWebhookUrl();
|
|
298
|
+
|
|
299
|
+
// Ownership is checked before delivery errors on purpose. When Telegram is
|
|
300
|
+
// pointed somewhere we never registered, any error it reports describes
|
|
301
|
+
// delivery to that other destination, so reporting the error would name a
|
|
302
|
+
// symptom while the mismatch is the cause, and the two have different fixes
|
|
303
|
+
// (re-run reconciliation vs. investigate a reachable ingress).
|
|
304
|
+
if (expectedUrl && registeredUrl !== expectedUrl) {
|
|
305
|
+
return {
|
|
306
|
+
status: "url_mismatch",
|
|
307
|
+
detail:
|
|
308
|
+
`${label} is registered with Telegram at a different address than this assistant ` +
|
|
309
|
+
`last registered, so its messages are being delivered somewhere this assistant is not ` +
|
|
310
|
+
`listening. ${fixPath(usesManagedCallbacks)}`,
|
|
311
|
+
registeredUrl,
|
|
312
|
+
expectedUrl,
|
|
313
|
+
...(pendingUpdateCount !== undefined ? { pendingUpdateCount } : {}),
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
249
317
|
const lastErrorDate = info.last_error_date;
|
|
250
318
|
const errorIsRecent =
|
|
251
319
|
lastErrorDate !== undefined &&
|
|
@@ -270,9 +338,28 @@ export async function checkTelegramWebhookHealth(): Promise<TelegramWebhookHealt
|
|
|
270
338
|
};
|
|
271
339
|
}
|
|
272
340
|
|
|
341
|
+
if (!expectedUrl) {
|
|
342
|
+
// A webhook is registered and quiet, but nothing recorded which URL we
|
|
343
|
+
// registered, so this cannot be distinguished from a stranger's webhook
|
|
344
|
+
// that happens not to be erroring. Reporting "healthy" here is the
|
|
345
|
+
// overclaim this status exists to prevent.
|
|
346
|
+
return {
|
|
347
|
+
status: "unverified",
|
|
348
|
+
detail:
|
|
349
|
+
`${label} has a webhook registered with Telegram and reports no recent delivery errors, ` +
|
|
350
|
+
`but this assistant has no record of registering it, so it cannot confirm the messages ` +
|
|
351
|
+
`reach here. Re-running setup records the registration.`,
|
|
352
|
+
registeredUrl,
|
|
353
|
+
...(lastErrorDate !== undefined ? { lastErrorDate } : {}),
|
|
354
|
+
...(pendingUpdateCount !== undefined ? { pendingUpdateCount } : {}),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
273
358
|
return {
|
|
274
359
|
status: "healthy",
|
|
275
|
-
detail: `${label}
|
|
360
|
+
detail: `${label} is registered at the address this assistant last set, and Telegram reports no recent delivery errors`,
|
|
361
|
+
registeredUrl,
|
|
362
|
+
expectedUrl,
|
|
276
363
|
...(lastErrorDate !== undefined ? { lastErrorDate } : {}),
|
|
277
364
|
...(pendingUpdateCount !== undefined ? { pendingUpdateCount } : {}),
|
|
278
365
|
};
|
|
@@ -385,7 +472,18 @@ export async function runTelegramWebhookHealthCheck(): Promise<TelegramWebhookHe
|
|
|
385
472
|
// Neither evidence of health nor of failure — leave the latch as-is so an
|
|
386
473
|
// unreachable Telegram (or a channel that isn't set up) can't either raise a
|
|
387
474
|
// false alarm or silently clear a real one.
|
|
388
|
-
|
|
475
|
+
//
|
|
476
|
+
// `unverified` belongs here rather than with the failures below: a webhook
|
|
477
|
+
// that is registered and quiet, on a deployment that never recorded its
|
|
478
|
+
// registration, is unproven, not broken. Alerting on it would page the
|
|
479
|
+
// guardian about every install whose last reconciliation predates URL
|
|
480
|
+
// recording. Note the asymmetry with `url_mismatch`, which falls through to
|
|
481
|
+
// the alert: there we know the address is wrong, which is a fault.
|
|
482
|
+
if (
|
|
483
|
+
result.status === "skipped" ||
|
|
484
|
+
result.status === "unknown" ||
|
|
485
|
+
result.status === "unverified"
|
|
486
|
+
) {
|
|
389
487
|
log.debug({ status: result.status }, result.detail);
|
|
390
488
|
return result;
|
|
391
489
|
}
|
|
@@ -43,6 +43,8 @@ const llmUsage: LlmUsageTelemetryEvent = {
|
|
|
43
43
|
// Zero is legitimate (wire bound is min(0)): a subagent can be spawned
|
|
44
44
|
// before the parent's first real user turn.
|
|
45
45
|
parent_turn_index: 0,
|
|
46
|
+
subagent_role: "advisor",
|
|
47
|
+
subagent_spawn_mode: "advisor_consult",
|
|
46
48
|
provider: "anthropic",
|
|
47
49
|
model: "claude-fable-5",
|
|
48
50
|
input_tokens: 1200,
|
|
@@ -157,10 +157,15 @@ export function activationStepIndex(stepName: ActivationStepName): number {
|
|
|
157
157
|
* `funnel_version` so the id stays stable across a version bump — otherwise
|
|
158
158
|
* queued/offline v1 rows would be keyed with the new binary's version and
|
|
159
159
|
* stop collapsing with already-ingested v1 rows from the same session.
|
|
160
|
+
*
|
|
161
|
+
* `stepName` is a plain string rather than an `ActivationStepName`: every
|
|
162
|
+
* funnel riding the onboarding substrate shares this keying (the live-voice
|
|
163
|
+
* session funnel is the other one today), and the dedup property depends only
|
|
164
|
+
* on the triple being stable per row, not on which funnel produced it.
|
|
160
165
|
*/
|
|
161
166
|
export function buildActivationDaemonEventId(
|
|
162
167
|
sessionId: string,
|
|
163
|
-
stepName:
|
|
168
|
+
stepName: string,
|
|
164
169
|
funnelVersion: string = ACTIVATION_FUNNEL_VERSION,
|
|
165
170
|
): string {
|
|
166
171
|
return `${funnelVersion}:${sessionId}:${stepName}`;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live-voice session telemetry vocabulary: the single source of truth for the
|
|
3
|
+
* step names, funnel version, and end-reason values the live-voice session
|
|
4
|
+
* emitter uses.
|
|
5
|
+
*
|
|
6
|
+
* Rides the existing onboarding telemetry substrate (`type: "onboarding"`),
|
|
7
|
+
* exactly as `activation-funnel.ts` does: the backend stores `step_name` and
|
|
8
|
+
* `funnel_version` as open strings, so this funnel needs no platform serializer
|
|
9
|
+
* and no wire-contract change.
|
|
10
|
+
*
|
|
11
|
+
* **Duration is not a field.** The onboarding event shape carries no numeric
|
|
12
|
+
* slot for it, so the two events below are keyed by the live-voice `sessionId`
|
|
13
|
+
* in `session_id` and the warehouse subtracts their `recorded_at` stamps. That
|
|
14
|
+
* yields exact percentiles rather than the bucketed-string dimension the tips
|
|
15
|
+
* and tour funnels pack into `screen`.
|
|
16
|
+
*
|
|
17
|
+
* **Turn count is not a field either.** It comes from the `turn` telemetry
|
|
18
|
+
* events voice turns already produce, which carry this session's id in their
|
|
19
|
+
* `client` bag (see `voice-session-bridge.ts`), so a turn count is a count of
|
|
20
|
+
* those rows rather than a number restated here.
|
|
21
|
+
*
|
|
22
|
+
* A session that starts and never ends is **censored, not infinite**: a daemon
|
|
23
|
+
* crash or kill emits no end event, so downstream duration math must drop
|
|
24
|
+
* unmatched `started` rows instead of treating them as open-ended sessions.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type { LiveVoiceSessionCloseReason } from "../live-voice/live-voice-session-manager.js";
|
|
28
|
+
import type { LiveVoiceProtocolErrorCode } from "../live-voice/protocol.js";
|
|
29
|
+
|
|
30
|
+
/** Funnel version stamped on every live-voice session event. */
|
|
31
|
+
export const LIVE_VOICE_FUNNEL_VERSION = "live_voice_v1_2026_08";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Live-voice session funnel steps. `stepName` is the wire value; `stepIndex`
|
|
35
|
+
* is the ordinal position.
|
|
36
|
+
*
|
|
37
|
+
* `sessionStarted` fires for every *attempted* session, before the credential
|
|
38
|
+
* preflight that can reject it. A session that fails to connect is exactly the
|
|
39
|
+
* one worth counting, and gating the start event on `ready` would hide it from
|
|
40
|
+
* the failure rate entirely.
|
|
41
|
+
*/
|
|
42
|
+
export const LIVE_VOICE_STEPS = {
|
|
43
|
+
sessionStarted: { stepName: "live_voice_session_started", stepIndex: 0 },
|
|
44
|
+
sessionEnded: { stepName: "live_voice_session_ended", stepIndex: 1 },
|
|
45
|
+
} as const;
|
|
46
|
+
|
|
47
|
+
export type LiveVoiceStepName =
|
|
48
|
+
(typeof LIVE_VOICE_STEPS)[keyof typeof LIVE_VOICE_STEPS]["stepName"];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* How a session ended, stamped as `outcome`.
|
|
52
|
+
*
|
|
53
|
+
* `failed` means the session died on an error: a credential preflight
|
|
54
|
+
* rejection before `ready`, or an utterance-arm failure after it. Everything
|
|
55
|
+
* else, including a dropped socket, is `completed`: the session ran and then
|
|
56
|
+
* stopped, and the close reason on `screen` says how.
|
|
57
|
+
*/
|
|
58
|
+
export type LiveVoiceSessionOutcome = "completed" | "failed";
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The `screen` dimension for an ended session: how it closed, plus the
|
|
62
|
+
* protocol error code when a failure is what closed it.
|
|
63
|
+
*
|
|
64
|
+
* Both halves are the daemon's own vocabulary verbatim
|
|
65
|
+
* (`LiveVoiceSessionCloseReason`, `LiveVoiceProtocolErrorCode`) rather than a
|
|
66
|
+
* mapping, so a rename on either side surfaces here as a compile error instead
|
|
67
|
+
* of a silently-empty dashboard facet. The longest pair this can produce is
|
|
68
|
+
* well inside the wire field's 64-char bound.
|
|
69
|
+
*/
|
|
70
|
+
export function liveVoiceEndScreen(
|
|
71
|
+
reason: LiveVoiceSessionCloseReason,
|
|
72
|
+
failureCode?: LiveVoiceProtocolErrorCode | null,
|
|
73
|
+
): string {
|
|
74
|
+
return failureCode ? `ended_${reason}:${failureCode}` : `ended_${reason}`;
|
|
75
|
+
}
|
|
@@ -281,6 +281,12 @@ const usageSource = simpleSource(
|
|
|
281
281
|
turn_index: e.turnIndex,
|
|
282
282
|
parent_conversation_id: e.parentConversationId,
|
|
283
283
|
parent_turn_index: e.parentTurnIndex,
|
|
284
|
+
// Delegated-work decomposition. Every subagent variety shares
|
|
285
|
+
// `llm_call_site = "subagentSpawn"`; these two orthogonal dimensions are
|
|
286
|
+
// what make advisor consults, forks, and regular spawns separable. Null
|
|
287
|
+
// for the vast majority of calls, which are not delegated at all.
|
|
288
|
+
subagent_role: e.subagentRole,
|
|
289
|
+
subagent_spawn_mode: e.subagentSpawnMode,
|
|
284
290
|
provider: e.provider,
|
|
285
291
|
model: e.model,
|
|
286
292
|
input_tokens: e.inputTokens,
|
|
@@ -95,6 +95,8 @@ export const llmUsageTelemetryEventSchema = z.object({
|
|
|
95
95
|
.nullable()
|
|
96
96
|
.optional(),
|
|
97
97
|
parent_turn_index: z.number().int().min(0).nullable().optional(),
|
|
98
|
+
subagent_role: z.string().trim().min(1).max(64).nullable().optional(),
|
|
99
|
+
subagent_spawn_mode: z.string().trim().min(1).max(64).nullable().optional(),
|
|
98
100
|
});
|
|
99
101
|
export type LlmUsageTelemetryEvent = z.infer<
|
|
100
102
|
typeof llmUsageTelemetryEventSchema
|
package/src/telemetry/types.ts
CHANGED
|
@@ -114,6 +114,26 @@ export interface LlmUsageTelemetryEvent extends TelemetryEventBase {
|
|
|
114
114
|
* parent conversation.
|
|
115
115
|
*/
|
|
116
116
|
parent_turn_index: number | null;
|
|
117
|
+
/**
|
|
118
|
+
* Role the subagent that owns this event's conversation was spawned with
|
|
119
|
+
* (`researcher`, `builder`, `advisor`; a spawn that named no role records
|
|
120
|
+
* the default, `builder`). An open string set: older rows carry role names
|
|
121
|
+
* nothing emits any more.
|
|
122
|
+
* The advisor is a ROLE, not an `LLMCallSiteEnum` value, so this is the only
|
|
123
|
+
* thing that tells an advisor consult apart from a regular subagent, since both
|
|
124
|
+
* emit under `llm_call_site = "subagentSpawn"`. Null when the LLM call did
|
|
125
|
+
* not run inside a subagent conversation.
|
|
126
|
+
*/
|
|
127
|
+
subagent_role: string | null;
|
|
128
|
+
/**
|
|
129
|
+
* How that subagent was spawned, one of the modes described on
|
|
130
|
+
* `SubagentSpawnMode` in `subagent/types.ts`, carried here as an open string
|
|
131
|
+
* set. Orthogonal to `subagent_role`: the role selects the child's
|
|
132
|
+
* capabilities, the spawn mode selects its context inheritance and lifecycle
|
|
133
|
+
* (a fork's inherited transcript dominates its input tokens regardless of
|
|
134
|
+
* role). Null under the same condition as `subagent_role`.
|
|
135
|
+
*/
|
|
136
|
+
subagent_spawn_mode: string | null;
|
|
117
137
|
provider: string;
|
|
118
138
|
model: string;
|
|
119
139
|
input_tokens: number;
|
|
@@ -164,10 +184,9 @@ export interface TurnTelemetryClientInfo {
|
|
|
164
184
|
browser_version?: string;
|
|
165
185
|
/**
|
|
166
186
|
* OS surface reported by the client at message time
|
|
167
|
-
* ("web" | "ios" | "macos" | "android").
|
|
168
|
-
* run the same
|
|
169
|
-
*
|
|
170
|
-
* this field is the only per-platform attribution in turn telemetry.
|
|
187
|
+
* ("web" | "ios" | "macos" | "windows" | "android"). Browser, mobile, and
|
|
188
|
+
* desktop apps all run the same renderer and report `interface_id: "web"`.
|
|
189
|
+
* This field is the only per-platform attribution in turn telemetry.
|
|
171
190
|
*/
|
|
172
191
|
os?: string;
|
|
173
192
|
/**
|
|
@@ -260,6 +260,8 @@ type UnreportedUsageEventFixture = UsageEvent & {
|
|
|
260
260
|
turnIndex: number | null;
|
|
261
261
|
parentConversationId: string | null;
|
|
262
262
|
parentTurnIndex: number | null;
|
|
263
|
+
subagentRole: string | null;
|
|
264
|
+
subagentSpawnMode: string | null;
|
|
263
265
|
};
|
|
264
266
|
|
|
265
267
|
function makeUsageEvent(
|
|
@@ -290,6 +292,8 @@ function makeUsageEvent(
|
|
|
290
292
|
turnIndex: 1,
|
|
291
293
|
parentConversationId: null,
|
|
292
294
|
parentTurnIndex: null,
|
|
295
|
+
subagentRole: null,
|
|
296
|
+
subagentSpawnMode: null,
|
|
293
297
|
llmCallCount: 1,
|
|
294
298
|
...overrides,
|
|
295
299
|
};
|
|
@@ -48,7 +48,7 @@ const CASES: {
|
|
|
48
48
|
fork: "deliberate `=== true` coercion owns malformed values",
|
|
49
49
|
send_result_to_user:
|
|
50
50
|
"deliberate `=== true` / `!== false` coercions own malformed values",
|
|
51
|
-
role: "
|
|
51
|
+
role: "resolveSubagentRole is total: every string lands on a type, so there is nothing to reject",
|
|
52
52
|
},
|
|
53
53
|
},
|
|
54
54
|
{
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
+
QuestionPromptOutcome,
|
|
4
5
|
QuestionPromptParams,
|
|
5
6
|
QuestionPromptResult,
|
|
6
7
|
} from "../../permissions/question-prompter.js";
|
|
@@ -25,16 +26,22 @@ function setNextResult(result: QuestionPromptResult): void {
|
|
|
25
26
|
|
|
26
27
|
mock.module("../../permissions/question-prompter.js", () => ({
|
|
27
28
|
QuestionPrompter: class {
|
|
28
|
-
async prompt(params: QuestionPromptParams): Promise<
|
|
29
|
+
async prompt(params: QuestionPromptParams): Promise<QuestionPromptOutcome> {
|
|
29
30
|
calls.push(params);
|
|
30
|
-
|
|
31
|
+
// Mirror the real prompter: it mints the request id and assigns the
|
|
32
|
+
// per-question `q1..qN` ids, then returns them alongside the resolution.
|
|
33
|
+
return {
|
|
34
|
+
requestId: "req-stub",
|
|
35
|
+
questions: params.questions.map((q, i) => ({ id: `q${i + 1}`, ...q })),
|
|
36
|
+
...nextResult,
|
|
37
|
+
};
|
|
31
38
|
}
|
|
32
39
|
},
|
|
33
40
|
}));
|
|
34
41
|
|
|
35
42
|
// Import after the mock so the tool's `import { QuestionPrompter }` binds
|
|
36
43
|
// to the stub class above.
|
|
37
|
-
const { askQuestionTool, formatQuestionsAsTextFallback } =
|
|
44
|
+
const { askQuestionTool, formatQuestionsAsTextFallback, toAnsweredQuestion } =
|
|
38
45
|
await import("./ask-question-tool.js");
|
|
39
46
|
|
|
40
47
|
type PromptParams = QuestionPromptParams;
|
|
@@ -710,3 +717,111 @@ describe("askQuestionTool definition (batched schema)", () => {
|
|
|
710
717
|
expect(schema.properties.options).toBeUndefined();
|
|
711
718
|
});
|
|
712
719
|
});
|
|
720
|
+
|
|
721
|
+
describe("answered-question record", () => {
|
|
722
|
+
test("carries the option the user chose, keyed to the questions as asked", async () => {
|
|
723
|
+
setNextResult({
|
|
724
|
+
entries: [{ questionId: "q1", decision: "option", optionId: "b" }],
|
|
725
|
+
overall: "completed",
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
const result = await askQuestionTool.execute(validInput, makeContext());
|
|
729
|
+
|
|
730
|
+
expect(result.answeredQuestion).toEqual({
|
|
731
|
+
requestId: "req-stub",
|
|
732
|
+
questions: [{ id: "q1", ...singleQ }],
|
|
733
|
+
responses: [{ questionId: "q1", decision: "option", optionId: "b" }],
|
|
734
|
+
overall: "completed",
|
|
735
|
+
});
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
test("carries a free-text answer", async () => {
|
|
739
|
+
setNextResult({
|
|
740
|
+
entries: [{ questionId: "q1", decision: "free_text", text: "Cherry" }],
|
|
741
|
+
overall: "completed",
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
const result = await askQuestionTool.execute(validInput, makeContext());
|
|
745
|
+
|
|
746
|
+
expect(result.answeredQuestion?.responses).toEqual([
|
|
747
|
+
{ questionId: "q1", decision: "free_text", text: "Cherry" },
|
|
748
|
+
]);
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
test("records a closed card as every question skipped", async () => {
|
|
752
|
+
setNextResult({
|
|
753
|
+
entries: [
|
|
754
|
+
{ questionId: "q1", decision: "skipped" },
|
|
755
|
+
{ questionId: "q2", decision: "skipped" },
|
|
756
|
+
],
|
|
757
|
+
overall: "closed",
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
const result = await askQuestionTool.execute(
|
|
761
|
+
{ questions: [singleQ, singleQ] },
|
|
762
|
+
makeContext(),
|
|
763
|
+
);
|
|
764
|
+
|
|
765
|
+
expect(result.answeredQuestion?.overall).toBe("closed");
|
|
766
|
+
expect(result.answeredQuestion?.responses).toEqual([
|
|
767
|
+
{ questionId: "q1", decision: "skipped" },
|
|
768
|
+
{ questionId: "q2", decision: "skipped" },
|
|
769
|
+
]);
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
test("keeps per-question skips inside an otherwise completed batch", async () => {
|
|
773
|
+
setNextResult({
|
|
774
|
+
entries: [
|
|
775
|
+
{ questionId: "q1", decision: "option", optionId: "a" },
|
|
776
|
+
{ questionId: "q2", decision: "skipped" },
|
|
777
|
+
],
|
|
778
|
+
overall: "completed",
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
const result = await askQuestionTool.execute(
|
|
782
|
+
{ questions: [singleQ, singleQ] },
|
|
783
|
+
makeContext(),
|
|
784
|
+
);
|
|
785
|
+
|
|
786
|
+
expect(result.answeredQuestion?.responses).toEqual([
|
|
787
|
+
{ questionId: "q1", decision: "option", optionId: "a" },
|
|
788
|
+
{ questionId: "q2", decision: "skipped" },
|
|
789
|
+
]);
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
test("records nothing for a prompt that timed out or was aborted", async () => {
|
|
793
|
+
for (const overall of ["timed_out", "aborted"] as const) {
|
|
794
|
+
setNextResult({
|
|
795
|
+
entries: [{ questionId: "q1", decision: overall }],
|
|
796
|
+
overall,
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
const result = await askQuestionTool.execute(validInput, makeContext());
|
|
800
|
+
|
|
801
|
+
expect(result.isError).toBe(true);
|
|
802
|
+
expect(result.answeredQuestion).toBeUndefined();
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
test("records nothing when the channel degrades to the text fallback", async () => {
|
|
807
|
+
const result = await askQuestionTool.execute(
|
|
808
|
+
validInput,
|
|
809
|
+
makeContext({ supportsDynamicUi: false }),
|
|
810
|
+
);
|
|
811
|
+
|
|
812
|
+
// No card was ever shown, so there is no answered state to keep.
|
|
813
|
+
expect(result.answeredQuestion).toBeUndefined();
|
|
814
|
+
expect(calls).toHaveLength(0);
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
test("toAnsweredQuestion is undefined for non-user outcomes", () => {
|
|
818
|
+
expect(
|
|
819
|
+
toAnsweredQuestion({
|
|
820
|
+
requestId: "req-1",
|
|
821
|
+
questions: [],
|
|
822
|
+
entries: [],
|
|
823
|
+
overall: "timed_out",
|
|
824
|
+
}),
|
|
825
|
+
).toBeUndefined();
|
|
826
|
+
});
|
|
827
|
+
});
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import type {
|
|
4
|
+
AnsweredQuestion,
|
|
5
|
+
AnsweredQuestionResponse,
|
|
6
|
+
} from "../../api/events/question-answered.js";
|
|
7
|
+
import {
|
|
8
|
+
QuestionPrompter,
|
|
9
|
+
type QuestionPromptOutcome,
|
|
10
|
+
} from "../../permissions/question-prompter.js";
|
|
4
11
|
import { RiskLevel } from "../../permissions/types.js";
|
|
5
12
|
import {
|
|
6
13
|
invalidToolInputResult,
|
|
@@ -161,6 +168,47 @@ export function formatQuestionsAsTextFallback(
|
|
|
161
168
|
return [header, "", blocks.join("\n\n")].join("\n");
|
|
162
169
|
}
|
|
163
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Project a settled prompt into the durable answered-question record the
|
|
173
|
+
* daemon persists on the tool call. Returns undefined for outcomes that carry
|
|
174
|
+
* no user decision (timeout, abort), which have nothing to show in history.
|
|
175
|
+
*/
|
|
176
|
+
export function toAnsweredQuestion(
|
|
177
|
+
outcome: QuestionPromptOutcome,
|
|
178
|
+
): AnsweredQuestion | undefined {
|
|
179
|
+
if (outcome.overall !== "completed" && outcome.overall !== "closed") {
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
const responses: AnsweredQuestionResponse[] = [];
|
|
183
|
+
for (const entry of outcome.entries) {
|
|
184
|
+
if (entry.decision === "option") {
|
|
185
|
+
responses.push({
|
|
186
|
+
questionId: entry.questionId,
|
|
187
|
+
decision: "option",
|
|
188
|
+
optionId: entry.optionId,
|
|
189
|
+
});
|
|
190
|
+
} else if (entry.decision === "free_text") {
|
|
191
|
+
responses.push({
|
|
192
|
+
questionId: entry.questionId,
|
|
193
|
+
decision: "free_text",
|
|
194
|
+
text: entry.text,
|
|
195
|
+
});
|
|
196
|
+
} else {
|
|
197
|
+
// A `completed` batch can still carry per-question skips, and a `closed`
|
|
198
|
+
// card reports every question as skipped. `timed_out` / `aborted` cannot
|
|
199
|
+
// reach here: they only ever appear on a batch-wide outcome of the same
|
|
200
|
+
// name, which is filtered above.
|
|
201
|
+
responses.push({ questionId: entry.questionId, decision: "skipped" });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
requestId: outcome.requestId,
|
|
206
|
+
questions: outcome.questions,
|
|
207
|
+
responses,
|
|
208
|
+
overall: outcome.overall,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
164
212
|
// ── Tool ────────────────────────────────────────────────────────────
|
|
165
213
|
|
|
166
214
|
/**
|
|
@@ -254,15 +302,21 @@ export const askQuestionTool = {
|
|
|
254
302
|
return `${prefix} Skipped`;
|
|
255
303
|
});
|
|
256
304
|
|
|
305
|
+
// The user's decision belongs in the transcript, not just in the string
|
|
306
|
+
// handed to the model: the daemon persists this on the tool call so the
|
|
307
|
+
// answered card survives a conversation switch, reload, or history reopen.
|
|
308
|
+
const answeredQuestion = toAnsweredQuestion(result);
|
|
309
|
+
|
|
257
310
|
switch (result.overall) {
|
|
258
311
|
case "completed":
|
|
259
|
-
return { content: lines.join("\n"), isError: false };
|
|
312
|
+
return { content: lines.join("\n"), isError: false, answeredQuestion };
|
|
260
313
|
case "closed": {
|
|
261
314
|
const summary =
|
|
262
315
|
"User closed the question card without answering. All questions skipped.";
|
|
263
316
|
return {
|
|
264
317
|
content: [summary, ...lines].join("\n"),
|
|
265
318
|
isError: false,
|
|
319
|
+
answeredQuestion,
|
|
266
320
|
};
|
|
267
321
|
}
|
|
268
322
|
case "timed_out":
|
|
@@ -1410,7 +1410,7 @@ export async function executeBrowserScreenshot(
|
|
|
1410
1410
|
// capture of a tall or high-DPI page can exceed Anthropic's 5 MB
|
|
1411
1411
|
// per-image cap, which would otherwise persist into the conversation
|
|
1412
1412
|
// history inside this tool_result and reject every subsequent turn.
|
|
1413
|
-
const { data: base64Data, mediaType } = optimizeImageForTransport(
|
|
1413
|
+
const { data: base64Data, mediaType } = await optimizeImageForTransport(
|
|
1414
1414
|
rawBase64,
|
|
1415
1415
|
"image/jpeg",
|
|
1416
1416
|
);
|
|
@@ -48,11 +48,14 @@ export async function executeCallStart(
|
|
|
48
48
|
|
|
49
49
|
const requestedPhone = normalizePhoneNumber(parsed.phone_number);
|
|
50
50
|
if (requestedPhone) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
// Scoped to the number being called. Voice sessions bind
|
|
52
|
+
// `expectedExternalUserId` to the E.164 number, and several can be live
|
|
53
|
+
// at once, so reading the latest and comparing would miss a verification
|
|
54
|
+
// for this number whenever another one started more recently.
|
|
55
|
+
const activeVoiceVerification = await findActiveSession("phone", {
|
|
56
|
+
expectedExternalUserId: requestedPhone,
|
|
57
|
+
});
|
|
58
|
+
if (activeVoiceVerification) {
|
|
56
59
|
return {
|
|
57
60
|
content: [
|
|
58
61
|
"Error: A guardian voice verification call is already active for this number.",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
batchSetConversationPlacement,
|
|
5
5
|
getConversation,
|
|
6
6
|
getDisplayMetaForConversations,
|
|
7
7
|
} from "../../persistence/conversation-crud.js";
|
|
@@ -73,15 +73,7 @@ export async function executeConversationMoveToGroup(
|
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
// group assignment changes.
|
|
78
|
-
batchSetDisplayOrders([
|
|
79
|
-
{
|
|
80
|
-
id: conversationId,
|
|
81
|
-
displayOrder: meta?.displayOrder ?? null,
|
|
82
|
-
groupId: group.id,
|
|
83
|
-
},
|
|
84
|
-
]);
|
|
76
|
+
batchSetConversationPlacement([{ id: conversationId, groupId: group.id }]);
|
|
85
77
|
publishConversationListAndMetadataChanged("reordered", [conversationId]);
|
|
86
78
|
|
|
87
79
|
const notes: string[] = [];
|
|
@@ -93,9 +85,15 @@ export async function executeConversationMoveToGroup(
|
|
|
93
85
|
`Moving into ${group.name} demotes it out of the Recents listing.`,
|
|
94
86
|
);
|
|
95
87
|
}
|
|
96
|
-
|
|
88
|
+
// Only Pinned and custom groups surface a hidden conversation. Recents is a
|
|
89
|
+
// removal target, and the Scheduled/Background groups are demotions, so
|
|
90
|
+
// claiming any of them made the conversation visible would misreport the
|
|
91
|
+
// outcome to the model and the user.
|
|
92
|
+
const surfacesConversation =
|
|
93
|
+
group.id === "system:pinned" || !group.id.startsWith("system:");
|
|
94
|
+
if (conversation.conversationType !== "standard" && surfacesConversation) {
|
|
97
95
|
notes.push(
|
|
98
|
-
`Note: this is a ${conversation.conversationType} conversation,
|
|
96
|
+
`Note: this is a ${conversation.conversationType} conversation, so filing it here also surfaces it into the sidebar.`,
|
|
99
97
|
);
|
|
100
98
|
}
|
|
101
99
|
|