@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
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
addMessage,
|
|
27
27
|
isEchoSuppressedUserMessage,
|
|
28
28
|
isHiddenMessageMetadata,
|
|
29
|
+
isSuppressedQueuedMessage,
|
|
29
30
|
provenanceFromTrustContext,
|
|
30
31
|
recordConversationPersistedSeq,
|
|
31
32
|
setConversationOriginChannelIfUnset,
|
|
@@ -61,6 +62,7 @@ import { getModelInfo } from "./handlers/config-model.js";
|
|
|
61
62
|
import { preactivateHostProxySkills } from "./host-proxy-preactivation.js";
|
|
62
63
|
import type { UserMessageAttachment } from "./message-protocol.js";
|
|
63
64
|
import { buildTransportHints } from "./transport-hints.js";
|
|
65
|
+
import { sameTrustIdentity, type TrustContext } from "./trust-context-types.js";
|
|
64
66
|
import { resolveVerificationSessionIntent } from "./verification-session-intent.js";
|
|
65
67
|
|
|
66
68
|
const log = getLogger("conversation-process");
|
|
@@ -285,9 +287,9 @@ async function buildPassthroughBatch(
|
|
|
285
287
|
break;
|
|
286
288
|
}
|
|
287
289
|
// The batched turn applies only the head's `clientOs`, so messages from a
|
|
288
|
-
// different OS surface must not coalesce.
|
|
290
|
+
// different OS surface must not coalesce. Browser, mobile, and desktop apps
|
|
289
291
|
// report `interfaceId: "web"`, so the interface check above no longer
|
|
290
|
-
// separates them
|
|
292
|
+
// separates them, so split on the reported OS explicitly.
|
|
291
293
|
if (candidate.transport?.clientOs !== head.transport?.clientOs) {
|
|
292
294
|
break;
|
|
293
295
|
}
|
|
@@ -300,6 +302,13 @@ async function buildPassthroughBatch(
|
|
|
300
302
|
if (candidate.sourceActorPrincipalId !== head.sourceActorPrincipalId) {
|
|
301
303
|
break;
|
|
302
304
|
}
|
|
305
|
+
// Channel senders carry no principal, so the check above leaves two
|
|
306
|
+
// different Slack contacts looking identical (`undefined === undefined`).
|
|
307
|
+
// The batch runs under a single trust context, so split on the sender's
|
|
308
|
+
// trust identity too or a tail executes with the head's privileges.
|
|
309
|
+
if (!sameTrustIdentity(candidate.trustContext, head.trustContext)) {
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
303
312
|
if (classifySlash(candidate.content) !== "passthrough") {
|
|
304
313
|
break;
|
|
305
314
|
}
|
|
@@ -324,10 +333,11 @@ async function buildPassthroughBatch(
|
|
|
324
333
|
return conversation.queue.shiftN(matched);
|
|
325
334
|
}
|
|
326
335
|
|
|
327
|
-
// ── Steer repair
|
|
336
|
+
// ── Steer / interrupt repair ────────────────────────────────────────
|
|
328
337
|
|
|
329
338
|
/**
|
|
330
|
-
* When a steer-to-message abort
|
|
339
|
+
* When a steer-to-message abort (or a user interrupt with messages still
|
|
340
|
+
* queued behind the stopped turn) cuts off an in-flight tool call, the
|
|
331
341
|
* conversation history may end with an assistant message containing one
|
|
332
342
|
* or more `tool_use` blocks that have no corresponding `tool_result`.
|
|
333
343
|
* LLM providers reject this sequence. This helper scans the tail of the
|
|
@@ -335,10 +345,12 @@ async function buildPassthroughBatch(
|
|
|
335
345
|
* unmatched `tool_use` blocks.
|
|
336
346
|
*/
|
|
337
347
|
function repairPendingToolUseBlocks(conversation: Conversation): void {
|
|
338
|
-
|
|
348
|
+
const steered = conversation.pendingSteerRepair;
|
|
349
|
+
if (!steered && !conversation.pendingInterruptRepair) {
|
|
339
350
|
return;
|
|
340
351
|
}
|
|
341
352
|
conversation.pendingSteerRepair = false;
|
|
353
|
+
conversation.pendingInterruptRepair = false;
|
|
342
354
|
|
|
343
355
|
const messages = conversation.messages;
|
|
344
356
|
if (messages.length === 0) {
|
|
@@ -381,15 +393,18 @@ function repairPendingToolUseBlocks(conversation: Conversation): void {
|
|
|
381
393
|
{
|
|
382
394
|
conversationId: conversation.conversationId,
|
|
383
395
|
pendingToolUseCount: pendingToolUseIds.length,
|
|
396
|
+
trigger: steered ? "steer" : "interrupt",
|
|
384
397
|
},
|
|
385
|
-
"Injecting synthetic tool_result for pending tool_use blocks
|
|
398
|
+
"Injecting synthetic tool_result for pending tool_use blocks",
|
|
386
399
|
);
|
|
387
400
|
|
|
388
401
|
// Build a single user message with tool_result blocks for all pending IDs.
|
|
389
402
|
const syntheticContent = pendingToolUseIds.map((toolUseId) => ({
|
|
390
403
|
type: "tool_result" as const,
|
|
391
404
|
tool_use_id: toolUseId,
|
|
392
|
-
content:
|
|
405
|
+
content: steered
|
|
406
|
+
? "Tool execution was interrupted by user steering."
|
|
407
|
+
: "Tool execution was interrupted by the user.",
|
|
393
408
|
is_error: true,
|
|
394
409
|
}));
|
|
395
410
|
conversation.messages.push({
|
|
@@ -405,11 +420,23 @@ function repairPendingToolUseBlocks(conversation: Conversation): void {
|
|
|
405
420
|
* we did. The flag is what makes the requeue paths able to distinguish a
|
|
406
421
|
* message clients still believe is running from one they never stopped
|
|
407
422
|
* showing as queued. See {@link requeueDrainedMessages}.
|
|
423
|
+
*
|
|
424
|
+
* Queue events come in pairs. A row with no client-visible queued counterpart
|
|
425
|
+
* ({@link isSuppressedQueuedMessage} — hidden sends and the daemon-injected
|
|
426
|
+
* subagent/ACP/wake notifications) never produced a `message_queued` ack, so
|
|
427
|
+
* releasing one produces no dequeue either: an unpaired dequeue would retire a
|
|
428
|
+
* client's counter entry for a message that is still waiting. Nothing was
|
|
429
|
+
* announced, so the flag stays false and the requeue paths correctly send
|
|
430
|
+
* nothing for it. The activity-state transition callers emit alongside this is
|
|
431
|
+
* unrelated to the queue and always fires: the turn really is starting.
|
|
408
432
|
*/
|
|
409
433
|
function announceDequeue(
|
|
410
434
|
conversation: Conversation,
|
|
411
435
|
message: QueuedMessage,
|
|
412
436
|
): void {
|
|
437
|
+
if (isSuppressedQueuedMessage(message.metadata)) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
413
440
|
message.onEvent({
|
|
414
441
|
type: "message_dequeued",
|
|
415
442
|
conversationId: conversation.conversationId,
|
|
@@ -434,7 +461,7 @@ function visibleQueuePosition(
|
|
|
434
461
|
): number | undefined {
|
|
435
462
|
let position = 0;
|
|
436
463
|
for (const item of conversation.queue.snapshot()) {
|
|
437
|
-
if (
|
|
464
|
+
if (isSuppressedQueuedMessage(item.metadata)) {
|
|
438
465
|
continue;
|
|
439
466
|
}
|
|
440
467
|
position += 1;
|
|
@@ -460,8 +487,10 @@ function visibleQueuePosition(
|
|
|
460
487
|
* `message_dequeued` and would otherwise see the row vanish until a later
|
|
461
488
|
* drain. Messages requeued before the announcement (the pre-flight
|
|
462
489
|
* processing-lock checks) get nothing, because clients never stopped showing
|
|
463
|
-
* them as queued and an event there would be noise.
|
|
464
|
-
*
|
|
490
|
+
* them as queued and an event there would be noise. Rows with no client-visible
|
|
491
|
+
* queued counterpart ({@link isSuppressedQueuedMessage} — hidden sends and
|
|
492
|
+
* daemon-injected subagent/ACP/wake notifications) are suppressed for the same
|
|
493
|
+
* reason they get no queued ack: they have no client row.
|
|
465
494
|
*/
|
|
466
495
|
function requeueDrainedMessages(
|
|
467
496
|
conversation: Conversation,
|
|
@@ -488,7 +517,7 @@ function requeueDrainedMessages(
|
|
|
488
517
|
continue;
|
|
489
518
|
}
|
|
490
519
|
message.dequeueAnnounced = false;
|
|
491
|
-
if (
|
|
520
|
+
if (isSuppressedQueuedMessage(message.metadata)) {
|
|
492
521
|
continue;
|
|
493
522
|
}
|
|
494
523
|
const position = visibleQueuePosition(conversation, message.requestId);
|
|
@@ -653,7 +682,15 @@ export async function kickQueueDrain(
|
|
|
653
682
|
},
|
|
654
683
|
"drainQueue retry rejected; queued messages remain stranded until the next drain trigger",
|
|
655
684
|
);
|
|
656
|
-
|
|
685
|
+
// Only the sends a user is waiting on get the failure notice. A stranded
|
|
686
|
+
// row with no client-visible queued counterpart (hidden machine signal,
|
|
687
|
+
// daemon-injected subagent/ACP/wake notification) has no queued bubble to
|
|
688
|
+
// explain, so a "your queued message" error would describe something the
|
|
689
|
+
// user never sent.
|
|
690
|
+
const strandedVisible = conversation.queue
|
|
691
|
+
.snapshot()
|
|
692
|
+
.filter((queued) => !isSuppressedQueuedMessage(queued.metadata));
|
|
693
|
+
for (const queued of strandedVisible) {
|
|
657
694
|
queued.onEvent({
|
|
658
695
|
type: "error",
|
|
659
696
|
conversationId: conversation.conversationId,
|
|
@@ -766,7 +803,11 @@ async function drainSingleMessage(
|
|
|
766
803
|
|
|
767
804
|
// Snapshot persona context at turn start so later tool turns can't pick up
|
|
768
805
|
// a different actor's context if a concurrent request mutates the live fields.
|
|
769
|
-
|
|
806
|
+
// Trust comes from the queued message, not the live slot: the slot holds
|
|
807
|
+
// whichever actor sent most recently, which is this sender only when nobody
|
|
808
|
+
// else sent while this message waited.
|
|
809
|
+
conversation.currentTurnTrustContext =
|
|
810
|
+
next.trustContext ?? conversation.trustContext;
|
|
770
811
|
conversation.currentTurnChannelCapabilities =
|
|
771
812
|
conversation.channelCapabilities;
|
|
772
813
|
|
|
@@ -813,7 +854,10 @@ async function drainSingleMessage(
|
|
|
813
854
|
: {}),
|
|
814
855
|
sentAt: next.sentAt,
|
|
815
856
|
};
|
|
816
|
-
const cleanUserMsg = createUserMessage(
|
|
857
|
+
const cleanUserMsg = await createUserMessage(
|
|
858
|
+
next.content,
|
|
859
|
+
next.attachments,
|
|
860
|
+
);
|
|
817
861
|
const llmUserMsg = enrichMessageWithSourcePaths(
|
|
818
862
|
cleanUserMsg,
|
|
819
863
|
next.attachments,
|
|
@@ -821,7 +865,7 @@ async function drainSingleMessage(
|
|
|
821
865
|
// When displayContent is provided (e.g. original text before recording
|
|
822
866
|
// intent stripping), persist that to DB so users see the full message.
|
|
823
867
|
// The in-memory userMessage (sent to the LLM) still uses the stripped content.
|
|
824
|
-
const contentToPersist = serializePersistedUserMessageContent(
|
|
868
|
+
const contentToPersist = await serializePersistedUserMessageContent(
|
|
825
869
|
next.content,
|
|
826
870
|
next.displayContent,
|
|
827
871
|
next.attachments,
|
|
@@ -913,11 +957,14 @@ async function drainSingleMessage(
|
|
|
913
957
|
: {}),
|
|
914
958
|
sentAt: next.sentAt,
|
|
915
959
|
};
|
|
916
|
-
const cleanUserMsg = createUserMessage(
|
|
960
|
+
const cleanUserMsg = await createUserMessage(
|
|
961
|
+
next.content,
|
|
962
|
+
next.attachments,
|
|
963
|
+
);
|
|
917
964
|
await addMessage(
|
|
918
965
|
conversation.conversationId,
|
|
919
966
|
"user",
|
|
920
|
-
serializePersistedUserMessageContent(
|
|
967
|
+
await serializePersistedUserMessageContent(
|
|
921
968
|
next.content,
|
|
922
969
|
next.displayContent,
|
|
923
970
|
next.attachments,
|
|
@@ -1003,11 +1050,14 @@ async function drainSingleMessage(
|
|
|
1003
1050
|
: {}),
|
|
1004
1051
|
sentAt: next.sentAt,
|
|
1005
1052
|
};
|
|
1006
|
-
const cleanUserMsg = createUserMessage(
|
|
1053
|
+
const cleanUserMsg = await createUserMessage(
|
|
1054
|
+
next.content,
|
|
1055
|
+
next.attachments,
|
|
1056
|
+
);
|
|
1007
1057
|
await addMessage(
|
|
1008
1058
|
conversation.conversationId,
|
|
1009
1059
|
"user",
|
|
1010
|
-
serializePersistedUserMessageContent(
|
|
1060
|
+
await serializePersistedUserMessageContent(
|
|
1011
1061
|
next.content,
|
|
1012
1062
|
next.displayContent,
|
|
1013
1063
|
next.attachments,
|
|
@@ -1097,6 +1147,12 @@ async function drainSingleMessage(
|
|
|
1097
1147
|
metadata: { ...next.metadata, sentAt: next.sentAt },
|
|
1098
1148
|
displayContent: next.displayContent,
|
|
1099
1149
|
clientMessageId: next.clientMessageId,
|
|
1150
|
+
// Attribute the stored row to the sender this turn runs as, not to
|
|
1151
|
+
// whoever happens to occupy the conversation slot at drain time.
|
|
1152
|
+
trustContext: next.trustContext,
|
|
1153
|
+
...(next.transport?.clientOs
|
|
1154
|
+
? { requestClientOs: next.transport.clientOs }
|
|
1155
|
+
: {}),
|
|
1100
1156
|
});
|
|
1101
1157
|
} catch (err) {
|
|
1102
1158
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1217,7 +1273,14 @@ async function drainSingleMessage(
|
|
|
1217
1273
|
isUserMessage?: boolean;
|
|
1218
1274
|
titleText?: string;
|
|
1219
1275
|
isHiddenPrompt?: boolean;
|
|
1220
|
-
|
|
1276
|
+
turnTrustContext?: TrustContext;
|
|
1277
|
+
} = {
|
|
1278
|
+
isUserMessage: true,
|
|
1279
|
+
// Carry the sender's trust into the run. The loop re-initializes the
|
|
1280
|
+
// per-turn snapshot on entry, so without this the stamp above is undone
|
|
1281
|
+
// and the turn reverts to the conversation's most recent actor.
|
|
1282
|
+
turnTrustContext: conversation.currentTurnTrustContext,
|
|
1283
|
+
};
|
|
1221
1284
|
if (next.isInteractive !== undefined) {
|
|
1222
1285
|
drainLoopOptions.isInteractive = next.isInteractive;
|
|
1223
1286
|
}
|
|
@@ -1342,7 +1405,12 @@ async function drainBatch(
|
|
|
1342
1405
|
|
|
1343
1406
|
// Snapshot persona context at turn start so later tool turns can't pick up
|
|
1344
1407
|
// a different actor's context if a concurrent request mutates the live fields.
|
|
1345
|
-
|
|
1408
|
+
// The head's trust governs the batch, which is sound only because
|
|
1409
|
+
// `buildPassthroughBatch` refuses to coalesce messages from different
|
|
1410
|
+
// actors; without that boundary this would run a tail under the head's
|
|
1411
|
+
// trust.
|
|
1412
|
+
conversation.currentTurnTrustContext =
|
|
1413
|
+
head.trustContext ?? conversation.trustContext;
|
|
1346
1414
|
conversation.currentTurnChannelCapabilities =
|
|
1347
1415
|
conversation.channelCapabilities;
|
|
1348
1416
|
|
|
@@ -1447,6 +1515,12 @@ async function drainBatch(
|
|
|
1447
1515
|
metadata: { ...qm.metadata, sentAt: qm.sentAt },
|
|
1448
1516
|
displayContent: qm.displayContent,
|
|
1449
1517
|
clientMessageId: qm.clientMessageId,
|
|
1518
|
+
// Same attribution rule as the single-message drain. Batch members
|
|
1519
|
+
// share one sender, so every row here names that sender.
|
|
1520
|
+
trustContext: qm.trustContext,
|
|
1521
|
+
...(qm.transport?.clientOs
|
|
1522
|
+
? { requestClientOs: qm.transport.clientOs }
|
|
1523
|
+
: {}),
|
|
1450
1524
|
};
|
|
1451
1525
|
if (i === 0) {
|
|
1452
1526
|
batchPersistResult =
|
|
@@ -1675,8 +1749,12 @@ async function drainBatch(
|
|
|
1675
1749
|
titleText?: string;
|
|
1676
1750
|
isHiddenPrompt?: boolean;
|
|
1677
1751
|
notifyUserMessageId?: string;
|
|
1752
|
+
turnTrustContext?: TrustContext;
|
|
1678
1753
|
} = {
|
|
1679
1754
|
isUserMessage: true,
|
|
1755
|
+
// Same reason as the single-message drain: the loop re-initializes the
|
|
1756
|
+
// per-turn snapshot, so the head's trust has to travel with the call.
|
|
1757
|
+
turnTrustContext: conversation.currentTurnTrustContext,
|
|
1680
1758
|
};
|
|
1681
1759
|
if (lastPushEligibleUserMessageId !== undefined) {
|
|
1682
1760
|
drainLoopOptions.notifyUserMessageId = lastPushEligibleUserMessageId;
|
|
@@ -1798,7 +1876,14 @@ export async function processMessage(
|
|
|
1798
1876
|
await conversation.ensureActorScopedHistory();
|
|
1799
1877
|
// Snapshot persona context at turn start so later tool turns can't pick up
|
|
1800
1878
|
// a different actor's context if a concurrent request mutates the live fields.
|
|
1801
|
-
|
|
1879
|
+
//
|
|
1880
|
+
// Held in a local as well as on the conversation: the field is writable
|
|
1881
|
+
// out-of-band while this turn is in flight (`agent-wake` stamps it and
|
|
1882
|
+
// restores the prior value in a `finally`), so reading it back at the agent
|
|
1883
|
+
// loop call below would reintroduce the late read this capture exists to
|
|
1884
|
+
// avoid. The local is what the loop runs under.
|
|
1885
|
+
const turnTrustContext = conversation.trustContext;
|
|
1886
|
+
conversation.currentTurnTrustContext = turnTrustContext;
|
|
1802
1887
|
conversation.currentTurnAuthContext = conversation.authContext;
|
|
1803
1888
|
conversation.currentTurnSourceActorPrincipalId =
|
|
1804
1889
|
sourceActorPrincipalId ?? conversation.authContext?.actorPrincipalId;
|
|
@@ -1873,7 +1958,7 @@ export async function processMessage(
|
|
|
1873
1958
|
: {}),
|
|
1874
1959
|
};
|
|
1875
1960
|
|
|
1876
|
-
const cleanUserMsg = createUserMessage(content, attachments);
|
|
1961
|
+
const cleanUserMsg = await createUserMessage(content, attachments);
|
|
1877
1962
|
const llmUserMsg = enrichMessageWithSourcePaths(
|
|
1878
1963
|
cleanUserMsg,
|
|
1879
1964
|
attachments,
|
|
@@ -1881,7 +1966,7 @@ export async function processMessage(
|
|
|
1881
1966
|
const persisted = await addMessage(
|
|
1882
1967
|
conversation.conversationId,
|
|
1883
1968
|
"user",
|
|
1884
|
-
serializePersistedUserMessageContent(
|
|
1969
|
+
await serializePersistedUserMessageContent(
|
|
1885
1970
|
content,
|
|
1886
1971
|
displayContent,
|
|
1887
1972
|
attachments,
|
|
@@ -1965,12 +2050,12 @@ export async function processMessage(
|
|
|
1965
2050
|
? { imageSourcePaths: pmImageSourcePaths }
|
|
1966
2051
|
: {}),
|
|
1967
2052
|
};
|
|
1968
|
-
const cleanUserMsg = createUserMessage(content, attachments);
|
|
2053
|
+
const cleanUserMsg = await createUserMessage(content, attachments);
|
|
1969
2054
|
const llmUserMsg = enrichMessageWithSourcePaths(cleanUserMsg, attachments);
|
|
1970
2055
|
// When displayContent is provided (e.g. original text before recording
|
|
1971
2056
|
// intent stripping), persist that to DB so users see the full message.
|
|
1972
2057
|
// The in-memory userMessage (sent to the LLM) still uses the stripped content.
|
|
1973
|
-
const contentToPersist = serializePersistedUserMessageContent(
|
|
2058
|
+
const contentToPersist = await serializePersistedUserMessageContent(
|
|
1974
2059
|
content,
|
|
1975
2060
|
displayContent,
|
|
1976
2061
|
attachments,
|
|
@@ -2049,11 +2134,11 @@ export async function processMessage(
|
|
|
2049
2134
|
}
|
|
2050
2135
|
: {}),
|
|
2051
2136
|
};
|
|
2052
|
-
const cleanUserMsg = createUserMessage(content, attachments);
|
|
2137
|
+
const cleanUserMsg = await createUserMessage(content, attachments);
|
|
2053
2138
|
const persisted = await addMessage(
|
|
2054
2139
|
conversation.conversationId,
|
|
2055
2140
|
"user",
|
|
2056
|
-
serializePersistedUserMessageContent(
|
|
2141
|
+
await serializePersistedUserMessageContent(
|
|
2057
2142
|
content,
|
|
2058
2143
|
displayContent,
|
|
2059
2144
|
attachments,
|
|
@@ -2097,7 +2182,10 @@ export async function processMessage(
|
|
|
2097
2182
|
throw err;
|
|
2098
2183
|
} finally {
|
|
2099
2184
|
conversation.setProcessing(false);
|
|
2100
|
-
|
|
2185
|
+
// `kickQueueDrain` never rejects, so a failed drain in this `finally`
|
|
2186
|
+
// cannot mask the error the try block is unwinding, and its retry plus
|
|
2187
|
+
// sender notification replace a silently stranded queue.
|
|
2188
|
+
await kickQueueDrain(conversation, "loop_complete", "compact_command");
|
|
2101
2189
|
}
|
|
2102
2190
|
}
|
|
2103
2191
|
|
|
@@ -2127,11 +2215,11 @@ export async function processMessage(
|
|
|
2127
2215
|
}
|
|
2128
2216
|
: {}),
|
|
2129
2217
|
};
|
|
2130
|
-
const cleanUserMsg = createUserMessage(content, attachments);
|
|
2218
|
+
const cleanUserMsg = await createUserMessage(content, attachments);
|
|
2131
2219
|
const persisted = await addMessage(
|
|
2132
2220
|
conversation.conversationId,
|
|
2133
2221
|
"user",
|
|
2134
|
-
serializePersistedUserMessageContent(
|
|
2222
|
+
await serializePersistedUserMessageContent(
|
|
2135
2223
|
content,
|
|
2136
2224
|
displayContent,
|
|
2137
2225
|
attachments,
|
|
@@ -2171,7 +2259,8 @@ export async function processMessage(
|
|
|
2171
2259
|
throw err;
|
|
2172
2260
|
} finally {
|
|
2173
2261
|
conversation.setProcessing(false);
|
|
2174
|
-
|
|
2262
|
+
// Same never-rejecting drain as the `/compact` branch above.
|
|
2263
|
+
await kickQueueDrain(conversation, "loop_complete", "clean_command");
|
|
2175
2264
|
}
|
|
2176
2265
|
}
|
|
2177
2266
|
|
|
@@ -2263,7 +2352,16 @@ export async function processMessage(
|
|
|
2263
2352
|
titleText?: string;
|
|
2264
2353
|
callSite?: LLMCallSite;
|
|
2265
2354
|
overrideProfile?: string;
|
|
2266
|
-
|
|
2355
|
+
turnTrustContext?: TrustContext;
|
|
2356
|
+
} = {
|
|
2357
|
+
isUserMessage: true,
|
|
2358
|
+
// Carry the trust captured at turn start into the run. Several awaits sit
|
|
2359
|
+
// between that capture and the loop opening, and both the conversation
|
|
2360
|
+
// slot and the per-turn field are writable throughout that window, so
|
|
2361
|
+
// reading either here would run this turn as whoever wrote last. The
|
|
2362
|
+
// local captured at turn start is the only value no other writer can move.
|
|
2363
|
+
turnTrustContext,
|
|
2364
|
+
};
|
|
2267
2365
|
if (isInteractive !== undefined) {
|
|
2268
2366
|
loopOptions.isInteractive = isInteractive;
|
|
2269
2367
|
}
|
|
@@ -14,6 +14,7 @@ import type { AuthContext } from "../runtime/auth/types.js";
|
|
|
14
14
|
import { getLogger } from "../util/logger.js";
|
|
15
15
|
import type { UserMessageAttachment } from "./message-protocol.js";
|
|
16
16
|
import type { ConversationTransportMetadata } from "./message-types/conversations.js";
|
|
17
|
+
import type { TrustContext } from "./trust-context-types.js";
|
|
17
18
|
|
|
18
19
|
const log = getLogger("conversation-queue");
|
|
19
20
|
|
|
@@ -33,6 +34,15 @@ export interface QueuedMessage {
|
|
|
33
34
|
sourceActorPrincipalId?: string;
|
|
34
35
|
/** Full auth snapshot captured at enqueue time for turn-scoped authorization decisions. */
|
|
35
36
|
authContext?: AuthContext;
|
|
37
|
+
/**
|
|
38
|
+
* Sender's trust captured at enqueue time. The conversation-level
|
|
39
|
+
* `trustContext` is a single mutable slot holding whichever actor last sent
|
|
40
|
+
* a message, so a drain that read it would run this message under a
|
|
41
|
+
* different actor's trust whenever another actor sent in between. Trust
|
|
42
|
+
* governs `trustClass`, `executionChannel`, and `requesterExternalUserId`,
|
|
43
|
+
* so reading the wrong one misroutes the whole tool-approval path.
|
|
44
|
+
*/
|
|
45
|
+
trustContext?: TrustContext;
|
|
36
46
|
/** Transport metadata snapshot captured at enqueue time, applied when this message becomes active. */
|
|
37
47
|
transport?: ConversationTransportMetadata;
|
|
38
48
|
/** Original user message text to persist to DB when recording intent stripping produced a different `content`. */
|
|
@@ -451,12 +451,18 @@ export function buildActiveDocuments(conversationId: string): Array<{
|
|
|
451
451
|
* the readable handle is its `slug` (the directory stem, frozen at creation).
|
|
452
452
|
* The app tools key off the id, so the line carries it for tool calls and the
|
|
453
453
|
* slug for everything a human or the model would recognize.
|
|
454
|
+
*
|
|
455
|
+
* The resolved directory is not part of the returned shape. A workspace app's
|
|
456
|
+
* files are reachable by joining the workspace `Root:` from the `<workspace>`
|
|
457
|
+
* block with the app-builder skill's `data/apps/<slug>/` layout and the slug
|
|
458
|
+
* above. A plugin-bundled app's files sit outside that layout and belong to
|
|
459
|
+
* the plugin, which the provenance clause marks as not the assistant's to
|
|
460
|
+
* rewrite, so no path is offered for them either.
|
|
454
461
|
*/
|
|
455
462
|
export function buildVisibleAppContext(appId: string | undefined): {
|
|
456
463
|
appId: string;
|
|
457
464
|
name: string;
|
|
458
465
|
slug: string;
|
|
459
|
-
sourceDir: string;
|
|
460
466
|
pluginName?: string;
|
|
461
467
|
} | null {
|
|
462
468
|
if (!appId) {
|
|
@@ -471,7 +477,6 @@ export function buildVisibleAppContext(appId: string | undefined): {
|
|
|
471
477
|
appId: source.id,
|
|
472
478
|
name: source.name,
|
|
473
479
|
slug: source.dirName,
|
|
474
|
-
sourceDir: source.sourceDir,
|
|
475
480
|
...(source.origin.kind === "plugin"
|
|
476
481
|
? { pluginName: source.origin.pluginName }
|
|
477
482
|
: {}),
|
|
@@ -2239,10 +2244,10 @@ export async function applyRuntimeInjections(
|
|
|
2239
2244
|
: undefined;
|
|
2240
2245
|
// OS surface reported by the client, independent of the transport interface
|
|
2241
2246
|
// above. Drives the per-turn `client_os:` line so the model knows whether it
|
|
2242
|
-
// is talking to
|
|
2247
|
+
// is talking to a browser, mobile, or desktop app (all sharing the `"web"`
|
|
2243
2248
|
// transport interface). Read the frozen per-turn snapshot (not the live
|
|
2244
2249
|
// `clientOs`) so a queued message from another surface can't perturb the
|
|
2245
|
-
// in-flight turn
|
|
2250
|
+
// in-flight turn, using the same anti-race pattern as `clientTimezone`.
|
|
2246
2251
|
const clientOs = liveConversation?.currentTurnClientOs ?? undefined;
|
|
2247
2252
|
// The app the client has on screen (app viewer or the app-editing split),
|
|
2248
2253
|
// resolved to its name and source directory so the assistant can act on it
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
ensureConversationExists,
|
|
20
20
|
getConversation,
|
|
21
21
|
} from "../persistence/conversation-crud.js";
|
|
22
|
+
import type { ConversationOrigin } from "../persistence/conversation-types.js";
|
|
22
23
|
import { wrapWithCallSiteRouting } from "../providers/call-site-routing.js";
|
|
23
24
|
import {
|
|
24
25
|
mainAgentResolutionError,
|
|
@@ -51,6 +52,37 @@ import { buildTransportHints } from "./transport-hints.js";
|
|
|
51
52
|
|
|
52
53
|
const conversationOptions = new Map<string, ConversationCreateOptions>();
|
|
53
54
|
|
|
55
|
+
/**
|
|
56
|
+
* The channel a conversation created here belongs to.
|
|
57
|
+
*
|
|
58
|
+
* A trust context is stamped per inbound message from the gateway verdict,
|
|
59
|
+
* so `sourceChannel` names the channel the message creating this
|
|
60
|
+
* conversation actually arrived on. This is the moment that fact is known,
|
|
61
|
+
* and the caller is the only party that holds it.
|
|
62
|
+
*
|
|
63
|
+
* Returns `undefined` when there is no trust context, which does NOT mean
|
|
64
|
+
* native. `handleSendMessage` materializes a conversation before the route
|
|
65
|
+
* resolves trust, so a Slack or phone send reaches here with no
|
|
66
|
+
* `sourceChannel` yet. Assuming native there would stamp a remote
|
|
67
|
+
* conversation as the guardian's own, and nothing could repair it:
|
|
68
|
+
* `setConversationOriginChannelIfUnset` only writes over NULL, and
|
|
69
|
+
* `recoverRestingTrustContext` grants INTERNAL_GUARDIAN_TRUST_CONTEXT to the
|
|
70
|
+
* native channel on every later wake and boot-resume.
|
|
71
|
+
*
|
|
72
|
+
* `transport.channelId` is not a substitute. It is the external Slack
|
|
73
|
+
* conversation id, not a {@link ChannelId}.
|
|
74
|
+
*
|
|
75
|
+
* So this states the origin only where it is genuinely known, and leaves the
|
|
76
|
+
* rest to attribution exactly as before. The remaining callers get their
|
|
77
|
+
* origin when they are migrated with real knowledge of it, not by guessing
|
|
78
|
+
* here.
|
|
79
|
+
*/
|
|
80
|
+
function originFromStoredOptions(
|
|
81
|
+
storedOptions: ConversationCreateOptions | undefined,
|
|
82
|
+
): ConversationOrigin | undefined {
|
|
83
|
+
return storedOptions?.trustContext?.sourceChannel;
|
|
84
|
+
}
|
|
85
|
+
|
|
54
86
|
/**
|
|
55
87
|
* Drops the transport fields that describe what the client had on screen for a
|
|
56
88
|
* single message rather than for the conversation as a whole.
|
|
@@ -141,9 +173,17 @@ export async function getOrCreateConversation(
|
|
|
141
173
|
);
|
|
142
174
|
}
|
|
143
175
|
|
|
176
|
+
// A stale conversation is rebuilt only once it is genuinely idle. Queued
|
|
177
|
+
// messages live in memory on the instance being disposed, and the queue
|
|
178
|
+
// drains via an async dispatch after the current turn releases, so
|
|
179
|
+
// `isProcessing()` can read false while a queued turn is still pending:
|
|
180
|
+
// rebuilding in that gap would silently drop those messages. The conversation
|
|
181
|
+
// stays stale and is rebuilt on a later call.
|
|
144
182
|
if (
|
|
145
183
|
!conversation ||
|
|
146
|
-
(conversation.isStale() &&
|
|
184
|
+
(conversation.isStale() &&
|
|
185
|
+
!conversation.isProcessing() &&
|
|
186
|
+
!conversation.hasQueuedMessages())
|
|
147
187
|
) {
|
|
148
188
|
if (conversation) {
|
|
149
189
|
// Stale rebuild: the conversation id lives on, so abort in-flight
|
|
@@ -229,9 +269,13 @@ export async function getOrCreateConversation(
|
|
|
229
269
|
createConversation({
|
|
230
270
|
id: conversationId,
|
|
231
271
|
conversationType: storedOptions.conversationType,
|
|
272
|
+
origin: originFromStoredOptions(storedOptions),
|
|
232
273
|
});
|
|
233
274
|
} else {
|
|
234
|
-
ensureConversationExists(
|
|
275
|
+
ensureConversationExists(
|
|
276
|
+
conversationId,
|
|
277
|
+
originFromStoredOptions(storedOptions),
|
|
278
|
+
);
|
|
235
279
|
}
|
|
236
280
|
}
|
|
237
281
|
|
|
@@ -353,7 +397,12 @@ export function clearAllActiveConversations(): number {
|
|
|
353
397
|
export function evictConversationsForReload(): void {
|
|
354
398
|
const subagentManager = getSubagentManager();
|
|
355
399
|
for (const [id, conversation] of conversationEntries()) {
|
|
356
|
-
|
|
400
|
+
// A conversation with queued messages is not idle: the queue drains via an
|
|
401
|
+
// async dispatch after the current turn releases, so `isProcessing()` can
|
|
402
|
+
// read false while a queued turn is still pending. Disposing in that gap
|
|
403
|
+
// would silently drop the queued messages, so mark it stale instead and
|
|
404
|
+
// let it rebuild once the queue has run.
|
|
405
|
+
if (!conversation.isProcessing() && !conversation.hasQueuedMessages()) {
|
|
357
406
|
subagentManager.abortAllForParent(id);
|
|
358
407
|
conversation.dispose();
|
|
359
408
|
deleteConversation(id);
|
|
@@ -76,6 +76,15 @@ export type CurrentTurnSurface = {
|
|
|
76
76
|
* to the client.
|
|
77
77
|
*/
|
|
78
78
|
activationMoment?: ActivationMomentParam;
|
|
79
|
+
/**
|
|
80
|
+
* Completion stamped when the user answers the surface before this turn's
|
|
81
|
+
* message lands. The snapshot is the only record of the surface at that
|
|
82
|
+
* point, so both persisted-content appenders carry these onto the
|
|
83
|
+
* `ui_surface` block they build; without them a reseed reactivates an
|
|
84
|
+
* already-answered card.
|
|
85
|
+
*/
|
|
86
|
+
completed?: boolean;
|
|
87
|
+
completionSummary?: string;
|
|
79
88
|
};
|
|
80
89
|
}[SurfaceType];
|
|
81
90
|
|