@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
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* double typed as `Conversation` (`makeWakeConversation`) that stubs only the
|
|
8
8
|
* handful of members the wake touches — `getMessages`, `messages.push`,
|
|
9
9
|
* `isProcessing`/`setProcessing`/`waitForIdle`, `currentTurnTrustContext`,
|
|
10
|
-
* `setSubagentAllowedTools`, `
|
|
10
|
+
* `setSubagentAllowedTools`, `kickDrainQueue`, `maybeCompact`,
|
|
11
11
|
* `contextWindowManager.estimateInputTokens`, and a scripted `agentLoop.run()`.
|
|
12
12
|
*
|
|
13
13
|
* The wake's side effects flow through the daemon boundary, so the
|
|
@@ -62,7 +62,7 @@ interface WakeConversationProbe {
|
|
|
62
62
|
processingToggles: boolean[];
|
|
63
63
|
/** Tail messages persisted via `addMessage`, in call order. */
|
|
64
64
|
persistedTailCalls: Message[];
|
|
65
|
-
/** Number of times `
|
|
65
|
+
/** Number of times `kickDrainQueue` was invoked. */
|
|
66
66
|
drainQueueCalls: number;
|
|
67
67
|
/**
|
|
68
68
|
* Cross-hook call sequence tag. Each push/persist/drain (and the
|
|
@@ -71,7 +71,7 @@ interface WakeConversationProbe {
|
|
|
71
71
|
*/
|
|
72
72
|
callSequence: string[];
|
|
73
73
|
/**
|
|
74
|
-
* Snapshot of the processing flag at the moment `
|
|
74
|
+
* Snapshot of the processing flag at the moment `kickDrainQueue` was
|
|
75
75
|
* invoked. Lets tests prove drain ran AFTER setProcessing(false),
|
|
76
76
|
* rather than just inferring it from the order of recorded toggles.
|
|
77
77
|
*/
|
|
@@ -380,7 +380,7 @@ function makeWakeConversation(options: {
|
|
|
380
380
|
scriptedTail?: Message[];
|
|
381
381
|
scriptedEvents?: AgentEvent[];
|
|
382
382
|
isProcessing?: boolean;
|
|
383
|
-
/** When true, omit `
|
|
383
|
+
/** When true, omit `kickDrainQueue` so we can verify the wake handles its absence. */
|
|
384
384
|
omitDrainQueue?: boolean;
|
|
385
385
|
initialAllowedTools?: Set<string>;
|
|
386
386
|
/** Replaces the default scripted `agentLoop.run` body entirely. */
|
|
@@ -471,7 +471,7 @@ function makeWakeConversation(options: {
|
|
|
471
471
|
|
|
472
472
|
const runBody = options.runImpl ?? defaultRun;
|
|
473
473
|
|
|
474
|
-
const
|
|
474
|
+
const kickDrainQueue = options.omitDrainQueue
|
|
475
475
|
? undefined
|
|
476
476
|
: async () => {
|
|
477
477
|
probe.drainQueueCalls += 1;
|
|
@@ -597,7 +597,7 @@ function makeWakeConversation(options: {
|
|
|
597
597
|
},
|
|
598
598
|
buildCurrentSystemPrompt: () => "mock-system-prompt",
|
|
599
599
|
modelOverride: undefined,
|
|
600
|
-
...(
|
|
600
|
+
...(kickDrainQueue ? { kickDrainQueue } : {}),
|
|
601
601
|
};
|
|
602
602
|
|
|
603
603
|
return conversation as unknown as WakeConversation;
|
|
@@ -2297,11 +2297,11 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
2297
2297
|
expect(conversation.persistedTailCalls).toHaveLength(0);
|
|
2298
2298
|
});
|
|
2299
2299
|
|
|
2300
|
-
test("
|
|
2300
|
+
test("kickDrainQueue is called in finally after a successful run", async () => {
|
|
2301
2301
|
// Verifies Gap 1 fix: messages queued during a wake (because the
|
|
2302
2302
|
// wake set `processing = true`) must be picked up after the wake
|
|
2303
2303
|
// completes. Mirrors the canonical user-turn `finally` path which
|
|
2304
|
-
// sets `processing = false` then calls `
|
|
2304
|
+
// sets `processing = false` then calls `kickDrainQueue`.
|
|
2305
2305
|
const conversation = makeWakeConversation({
|
|
2306
2306
|
baseline: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
2307
2307
|
scriptedAssistant: {
|
|
@@ -2333,7 +2333,7 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
2333
2333
|
expect(conversation.isProcessing()).toBe(false);
|
|
2334
2334
|
});
|
|
2335
2335
|
|
|
2336
|
-
test("
|
|
2336
|
+
test("kickDrainQueue is called in finally even when the agent loop throws", async () => {
|
|
2337
2337
|
// Verifies the drain is in the finally block, not just on success.
|
|
2338
2338
|
// A wake that crashes mid-run must still flush queued messages —
|
|
2339
2339
|
// otherwise a transient LLM error strands every concurrent send.
|
|
@@ -2361,7 +2361,7 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
2361
2361
|
expect(conversation.processingToggles).toEqual([true, false]);
|
|
2362
2362
|
});
|
|
2363
2363
|
|
|
2364
|
-
test("missing
|
|
2364
|
+
test("missing kickDrainQueue hook is tolerated (no-op fallback)", async () => {
|
|
2365
2365
|
// The hook is intentionally optional so test stubs without a queue
|
|
2366
2366
|
// can omit it. Production daemon always wires it.
|
|
2367
2367
|
const conversation = makeWakeConversation({
|
|
@@ -2387,7 +2387,7 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
2387
2387
|
expect(conversation.drainQueueCalls).toBe(0);
|
|
2388
2388
|
});
|
|
2389
2389
|
|
|
2390
|
-
test("
|
|
2390
|
+
test("kickDrainQueue rejection does not propagate from the wake", async () => {
|
|
2391
2391
|
// Defense in depth: if the queue drain throws (e.g. a poisoned
|
|
2392
2392
|
// message), the wake itself must still resolve normally — the
|
|
2393
2393
|
// drain failure is logged but never surfaced.
|
|
@@ -2398,7 +2398,7 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
2398
2398
|
content: [{ type: "text", text: "reply" }],
|
|
2399
2399
|
},
|
|
2400
2400
|
});
|
|
2401
|
-
conversation.
|
|
2401
|
+
conversation.kickDrainQueue = async () => {
|
|
2402
2402
|
throw new Error("drain blew up");
|
|
2403
2403
|
};
|
|
2404
2404
|
|
|
@@ -2461,7 +2461,7 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
2461
2461
|
});
|
|
2462
2462
|
|
|
2463
2463
|
test(
|
|
2464
|
-
"tail messages are pushed and persisted BEFORE
|
|
2464
|
+
"tail messages are pushed and persisted BEFORE the queue drain runs " +
|
|
2465
2465
|
"(so dequeued turns see updated history)",
|
|
2466
2466
|
async () => {
|
|
2467
2467
|
// Locks in the round-3 fix: a user message queued during the wake
|
|
@@ -2474,7 +2474,7 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
2474
2474
|
//
|
|
2475
2475
|
// Mirrors the canonical user-turn pattern in
|
|
2476
2476
|
// conversation-agent-loop.ts: messages updated →
|
|
2477
|
-
// processing=false →
|
|
2477
|
+
// processing=false → kickDrainQueue.
|
|
2478
2478
|
const firstAssistant: Message = {
|
|
2479
2479
|
role: "assistant",
|
|
2480
2480
|
content: [
|
|
@@ -2536,13 +2536,13 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
2536
2536
|
);
|
|
2537
2537
|
|
|
2538
2538
|
test(
|
|
2539
|
-
"silent no-op:
|
|
2539
|
+
"silent no-op: the queue drain still runs (in finally) but nothing is " +
|
|
2540
2540
|
"pushed, persisted, or emitted",
|
|
2541
2541
|
async () => {
|
|
2542
2542
|
// The wake's silent-no-op semantics must be preserved by the
|
|
2543
2543
|
// round-3 reordering: an empty assistant reply produces no
|
|
2544
2544
|
// visible text and no tool calls, so no push/persist/emit should
|
|
2545
|
-
// happen.
|
|
2545
|
+
// happen. The queue drain must still run in the finally block so a
|
|
2546
2546
|
// racy queued message is not stranded.
|
|
2547
2547
|
const conversation = makeWakeConversation({
|
|
2548
2548
|
baseline: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
@@ -96,8 +96,6 @@ mock.module("../pre-first-message-gate.js", () => ({
|
|
|
96
96
|
|
|
97
97
|
// Import after mocks are in place.
|
|
98
98
|
const { runBackgroundJob } = await import("../background-job-runner.js");
|
|
99
|
-
const { bufferIfDeferred, resetDeferredForTest } =
|
|
100
|
-
await import("../../notifications/deferred-emit.js");
|
|
101
99
|
|
|
102
100
|
// ── Shared fixtures ──────────────────────────────────────────────────
|
|
103
101
|
|
|
@@ -125,7 +123,6 @@ beforeEach(() => {
|
|
|
125
123
|
processMessageCalls.length = 0;
|
|
126
124
|
emitCalls.length = 0;
|
|
127
125
|
addMessageCalls.length = 0;
|
|
128
|
-
resetDeferredForTest();
|
|
129
126
|
preFirstMessageGateOpen = true;
|
|
130
127
|
processMessageImpl = async () => ({ messageId: "msg-1" });
|
|
131
128
|
emitImpl = async () => ({
|
|
@@ -290,22 +287,6 @@ describe("runBackgroundJob", () => {
|
|
|
290
287
|
).toBe("model_provider");
|
|
291
288
|
});
|
|
292
289
|
|
|
293
|
-
test("non-throwing turn failure with deferNotifications drops buffered notifications", async () => {
|
|
294
|
-
processMessageImpl = async () => ({
|
|
295
|
-
messageId: "msg-failed-turn",
|
|
296
|
-
turnFailure: { failureCode: "provider_error" },
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
const result = await runBackgroundJob(
|
|
300
|
-
baseOpts({ deferNotifications: true }),
|
|
301
|
-
);
|
|
302
|
-
|
|
303
|
-
expect(result.ok).toBe(false);
|
|
304
|
-
// Only the runner's failure signal emits — no committed "success" signal.
|
|
305
|
-
expect(emitCalls).toHaveLength(1);
|
|
306
|
-
expect(emitCalls[0].sourceEventName).toBe("activity.failed");
|
|
307
|
-
});
|
|
308
|
-
|
|
309
290
|
test("timeout: returns ok=false with errorKind=timeout and emits activity.failed", async () => {
|
|
310
291
|
// Never resolve — force timeout to win the race.
|
|
311
292
|
processMessageImpl = () => new Promise(() => {});
|
|
@@ -464,129 +445,4 @@ describe("runBackgroundJob", () => {
|
|
|
464
445
|
expect(processMessageCalls).toHaveLength(1);
|
|
465
446
|
});
|
|
466
447
|
});
|
|
467
|
-
|
|
468
|
-
describe("deferNotifications", () => {
|
|
469
|
-
function buildSkillNotificationParams(message: string) {
|
|
470
|
-
return {
|
|
471
|
-
sourceEventName: "assistant.share",
|
|
472
|
-
sourceChannel: "assistant_tool" as const,
|
|
473
|
-
sourceContextId: "skill-ctx",
|
|
474
|
-
contextPayload: { requestedMessage: message },
|
|
475
|
-
attentionHints: {
|
|
476
|
-
requiresAction: false,
|
|
477
|
-
urgency: "low" as const,
|
|
478
|
-
isAsyncBackground: false,
|
|
479
|
-
visibleInSourceNow: false,
|
|
480
|
-
},
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
test("success path commits buffered in-band notifications", async () => {
|
|
485
|
-
processMessageImpl = async () => {
|
|
486
|
-
// Stand in for the IPC route's bufferIfDeferred call when the model
|
|
487
|
-
// invokes `notifications send` mid-turn.
|
|
488
|
-
const buffered = bufferIfDeferred(
|
|
489
|
-
STUB_CONVERSATION_ID,
|
|
490
|
-
buildSkillNotificationParams("all green"),
|
|
491
|
-
);
|
|
492
|
-
expect(buffered).not.toBeNull();
|
|
493
|
-
expect(buffered!.dispatched).toBe(false);
|
|
494
|
-
return { messageId: "msg-success" };
|
|
495
|
-
};
|
|
496
|
-
|
|
497
|
-
const result = await runBackgroundJob(
|
|
498
|
-
baseOpts({ deferNotifications: true }),
|
|
499
|
-
);
|
|
500
|
-
|
|
501
|
-
expect(result.ok).toBe(true);
|
|
502
|
-
// Commit flushed the buffered notification through emitNotificationSignal.
|
|
503
|
-
const successEmits = emitCalls.filter(
|
|
504
|
-
(e) => e.sourceEventName !== "activity.failed",
|
|
505
|
-
);
|
|
506
|
-
expect(successEmits).toHaveLength(1);
|
|
507
|
-
expect(successEmits[0].sourceEventName).toBe("assistant.share");
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
// Regression for the PR #31216 Codex P1 finding: a heartbeat that calls
|
|
511
|
-
// the notifications skill and then times out must not leave a "success"
|
|
512
|
-
// notification standing alongside the runner's `activity.failed` emit.
|
|
513
|
-
test("timeout drops buffered in-band notifications; only activity.failed emits", async () => {
|
|
514
|
-
processMessageImpl = () => {
|
|
515
|
-
bufferIfDeferred(
|
|
516
|
-
STUB_CONVERSATION_ID,
|
|
517
|
-
buildSkillNotificationParams("premature success"),
|
|
518
|
-
);
|
|
519
|
-
return new Promise(() => {});
|
|
520
|
-
};
|
|
521
|
-
|
|
522
|
-
const result = await runBackgroundJob(
|
|
523
|
-
baseOpts({ deferNotifications: true, timeoutMs: 30 }),
|
|
524
|
-
);
|
|
525
|
-
|
|
526
|
-
expect(result.ok).toBe(false);
|
|
527
|
-
expect(result.errorKind).toBe("timeout");
|
|
528
|
-
// Only the runner's failure signal makes it out — the buffered
|
|
529
|
-
// "success" notification is discarded.
|
|
530
|
-
expect(emitCalls).toHaveLength(1);
|
|
531
|
-
expect(emitCalls[0].sourceEventName).toBe("activity.failed");
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
test("thrown exception also drops buffered notifications", async () => {
|
|
535
|
-
processMessageImpl = async () => {
|
|
536
|
-
bufferIfDeferred(
|
|
537
|
-
STUB_CONVERSATION_ID,
|
|
538
|
-
buildSkillNotificationParams("doomed"),
|
|
539
|
-
);
|
|
540
|
-
throw new Error("kaboom");
|
|
541
|
-
};
|
|
542
|
-
|
|
543
|
-
const result = await runBackgroundJob(
|
|
544
|
-
baseOpts({ deferNotifications: true }),
|
|
545
|
-
);
|
|
546
|
-
|
|
547
|
-
expect(result.ok).toBe(false);
|
|
548
|
-
expect(emitCalls).toHaveLength(1);
|
|
549
|
-
expect(emitCalls[0].sourceEventName).toBe("activity.failed");
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
// Regression: after timeout, `processMessage` keeps running and may
|
|
553
|
-
// emit a late skill call. The tombstone must swallow it instead of
|
|
554
|
-
// letting it bypass the buffer and reach the dispatch pipeline.
|
|
555
|
-
test("late skill call after timeout is swallowed by the tombstone", async () => {
|
|
556
|
-
processMessageImpl = () => new Promise(() => {});
|
|
557
|
-
|
|
558
|
-
const result = await runBackgroundJob(
|
|
559
|
-
baseOpts({ deferNotifications: true, timeoutMs: 20 }),
|
|
560
|
-
);
|
|
561
|
-
expect(result.ok).toBe(false);
|
|
562
|
-
|
|
563
|
-
const late = bufferIfDeferred(
|
|
564
|
-
STUB_CONVERSATION_ID,
|
|
565
|
-
buildSkillNotificationParams("post-timeout"),
|
|
566
|
-
);
|
|
567
|
-
expect(late).not.toBeNull();
|
|
568
|
-
expect(late!.dispatched).toBe(false);
|
|
569
|
-
expect(late!.reason).toMatch(/did not complete/);
|
|
570
|
-
|
|
571
|
-
// Only the runner's failure signal made it out.
|
|
572
|
-
expect(emitCalls).toHaveLength(1);
|
|
573
|
-
expect(emitCalls[0].sourceEventName).toBe("activity.failed");
|
|
574
|
-
});
|
|
575
|
-
|
|
576
|
-
test("without deferNotifications, bufferIfDeferred is a no-op", async () => {
|
|
577
|
-
processMessageImpl = async () => {
|
|
578
|
-
const buffered = bufferIfDeferred(
|
|
579
|
-
STUB_CONVERSATION_ID,
|
|
580
|
-
buildSkillNotificationParams("immediate"),
|
|
581
|
-
);
|
|
582
|
-
// Buffer was never armed, so the call returns null and the IPC
|
|
583
|
-
// handler would emit directly.
|
|
584
|
-
expect(buffered).toBeNull();
|
|
585
|
-
return { messageId: "msg-success" };
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
const result = await runBackgroundJob(baseOpts());
|
|
589
|
-
expect(result.ok).toBe(true);
|
|
590
|
-
});
|
|
591
|
-
});
|
|
592
448
|
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the desktop presence policy.
|
|
3
|
+
*
|
|
4
|
+
* The invariant under test is fail-open: only a fresh `active` report from a
|
|
5
|
+
* `macos` client answers `true`. Every other shape (stale, idle, away, no
|
|
6
|
+
* presence, wrong interface, no clients, hub throw) answers `false` so the
|
|
7
|
+
* push is sent. Scoping the read to an actor principal narrows what counts as
|
|
8
|
+
* attendance, so it can only ever turn a `true` into a `false`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { afterEach, describe, expect, spyOn, test } from "bun:test";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
clearHubClients,
|
|
15
|
+
registerHubClient,
|
|
16
|
+
type RegisterHubClientArgs,
|
|
17
|
+
} from "../../__tests__/helpers/hub-clients.js";
|
|
18
|
+
import { assistantEventHub } from "../assistant-event-hub.js";
|
|
19
|
+
import {
|
|
20
|
+
isDesktopAttended,
|
|
21
|
+
PRESENCE_STALE_AFTER_MS,
|
|
22
|
+
} from "../desktop-presence.js";
|
|
23
|
+
|
|
24
|
+
// ── Test helpers ──────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/** A `now` far enough past every report to push all of them out of the window. */
|
|
27
|
+
function afterStaleness(): Date {
|
|
28
|
+
return new Date(Date.now() + PRESENCE_STALE_AFTER_MS + 1_000);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The policy reads the process singleton, so every client registers there. */
|
|
32
|
+
function registerClient(args: Omit<RegisterHubClientArgs, "hub">): void {
|
|
33
|
+
registerHubClient({ ...args, hub: assistantEventHub });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
clearHubClients(assistantEventHub);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ── Tests ─────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
describe("isDesktopAttended", () => {
|
|
43
|
+
test("fresh active macos client is attended", () => {
|
|
44
|
+
registerClient({ clientId: "mac-1", presence: "active" });
|
|
45
|
+
expect(isDesktopAttended()).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("active report older than the staleness bound is not attended", () => {
|
|
49
|
+
registerClient({ clientId: "mac-1", presence: "active" });
|
|
50
|
+
expect(isDesktopAttended({ now: afterStaleness() })).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("idle is not attended", () => {
|
|
54
|
+
registerClient({ clientId: "mac-1", presence: "idle" });
|
|
55
|
+
expect(isDesktopAttended()).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("away is not attended", () => {
|
|
59
|
+
registerClient({ clientId: "mac-1", presence: "away" });
|
|
60
|
+
expect(isDesktopAttended()).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("client that never reported presence is not attended", () => {
|
|
64
|
+
registerClient({ clientId: "mac-1" });
|
|
65
|
+
expect(isDesktopAttended()).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("active web client does not count as desktop presence", () => {
|
|
69
|
+
registerClient({
|
|
70
|
+
clientId: "web-1",
|
|
71
|
+
interfaceId: "web",
|
|
72
|
+
presence: "active",
|
|
73
|
+
});
|
|
74
|
+
expect(isDesktopAttended()).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("no connected clients is not attended", () => {
|
|
78
|
+
expect(isDesktopAttended()).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("a hub read that throws is not attended", () => {
|
|
82
|
+
const spy = spyOn(
|
|
83
|
+
assistantEventHub,
|
|
84
|
+
"listClientsByInterface",
|
|
85
|
+
).mockImplementation(() => {
|
|
86
|
+
throw new Error("hub exploded");
|
|
87
|
+
});
|
|
88
|
+
try {
|
|
89
|
+
expect(isDesktopAttended()).toBe(false);
|
|
90
|
+
} finally {
|
|
91
|
+
spy.mockRestore();
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("one active macos client among several is enough", () => {
|
|
96
|
+
registerClient({ clientId: "mac-1", presence: "away" });
|
|
97
|
+
registerClient({ clientId: "mac-2", presence: "active" });
|
|
98
|
+
expect(isDesktopAttended()).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe("isDesktopAttended scoped to an actor principal", () => {
|
|
103
|
+
test("matching actor principal is attended", () => {
|
|
104
|
+
registerClient({
|
|
105
|
+
clientId: "mac-1",
|
|
106
|
+
presence: "active",
|
|
107
|
+
actorPrincipalId: "actor-a",
|
|
108
|
+
});
|
|
109
|
+
expect(isDesktopAttended({ actorPrincipalId: "actor-a" })).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("another actor's attended mac does not count", () => {
|
|
113
|
+
registerClient({
|
|
114
|
+
clientId: "mac-1",
|
|
115
|
+
presence: "active",
|
|
116
|
+
actorPrincipalId: "actor-a",
|
|
117
|
+
});
|
|
118
|
+
expect(isDesktopAttended({ actorPrincipalId: "actor-b" })).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("client with no principal does not match a supplied principal", () => {
|
|
122
|
+
registerClient({ clientId: "mac-1", presence: "active" });
|
|
123
|
+
expect(isDesktopAttended({ actorPrincipalId: "actor-a" })).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("omitting the principal counts any attended macos client", () => {
|
|
127
|
+
registerClient({
|
|
128
|
+
clientId: "mac-1",
|
|
129
|
+
presence: "active",
|
|
130
|
+
actorPrincipalId: "actor-a",
|
|
131
|
+
});
|
|
132
|
+
expect(isDesktopAttended()).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("target actor's stale report is not attended", () => {
|
|
136
|
+
registerClient({
|
|
137
|
+
clientId: "mac-1",
|
|
138
|
+
presence: "active",
|
|
139
|
+
actorPrincipalId: "actor-a",
|
|
140
|
+
});
|
|
141
|
+
expect(
|
|
142
|
+
isDesktopAttended({
|
|
143
|
+
actorPrincipalId: "actor-a",
|
|
144
|
+
now: afterStaleness(),
|
|
145
|
+
}),
|
|
146
|
+
).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("only the non-target actor is active among several clients", () => {
|
|
150
|
+
registerClient({
|
|
151
|
+
clientId: "mac-1",
|
|
152
|
+
presence: "active",
|
|
153
|
+
actorPrincipalId: "actor-a",
|
|
154
|
+
});
|
|
155
|
+
registerClient({
|
|
156
|
+
clientId: "mac-2",
|
|
157
|
+
presence: "away",
|
|
158
|
+
actorPrincipalId: "actor-b",
|
|
159
|
+
});
|
|
160
|
+
expect(isDesktopAttended({ actorPrincipalId: "actor-b" })).toBe(false);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -86,6 +86,7 @@ import { getConfig } from "../config/loader.js";
|
|
|
86
86
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
87
87
|
import { conversationSupportsDynamicUi } from "../daemon/channel-ui-capability.js";
|
|
88
88
|
import type { Conversation } from "../daemon/conversation.js";
|
|
89
|
+
import type { QueueDrainReason } from "../daemon/conversation-queue-manager.js";
|
|
89
90
|
import { recordUsage } from "../daemon/conversation-usage.js";
|
|
90
91
|
import { getDiskPressureStatus } from "../daemon/disk-pressure-guard.js";
|
|
91
92
|
import {
|
|
@@ -534,6 +535,47 @@ async function defaultResolveTarget(
|
|
|
534
535
|
}
|
|
535
536
|
}
|
|
536
537
|
|
|
538
|
+
// ── Queue drain kick ──────────────────────────────────────────────────
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* The queue-drain surface the wake kicks on its way out.
|
|
542
|
+
*
|
|
543
|
+
* The hook is optional because the wake runs against whatever
|
|
544
|
+
* {@link WakeDeps.resolveTarget} hands it, which in tests is a partial
|
|
545
|
+
* conversation double. Production `Conversation` always provides it.
|
|
546
|
+
*/
|
|
547
|
+
interface WakeDrainTarget {
|
|
548
|
+
kickDrainQueue?: (
|
|
549
|
+
reason?: QueueDrainReason,
|
|
550
|
+
origin?: string,
|
|
551
|
+
) => Promise<void>;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Drain the target's message queue once the wake has released the
|
|
556
|
+
* conversation.
|
|
557
|
+
*
|
|
558
|
+
* `Conversation.kickDrainQueue` retries a failed drain once and notifies the
|
|
559
|
+
* queued senders when the retry also fails, so a stalled wake-tail drain is
|
|
560
|
+
* visible instead of silently stranding the queue, and it never rejects. An
|
|
561
|
+
* injected target can still omit the hook or reject from it, and neither may
|
|
562
|
+
* fail the wake, whose own work is already complete by this point.
|
|
563
|
+
*/
|
|
564
|
+
async function kickWakeDrainQueue(
|
|
565
|
+
conversation: WakeDrainTarget,
|
|
566
|
+
origin: string,
|
|
567
|
+
logContext: { conversationId: string; source: string },
|
|
568
|
+
): Promise<void> {
|
|
569
|
+
try {
|
|
570
|
+
await conversation.kickDrainQueue?.("loop_complete", origin);
|
|
571
|
+
} catch (err) {
|
|
572
|
+
log.warn(
|
|
573
|
+
{ ...logContext, err },
|
|
574
|
+
"agent-wake: kickDrainQueue threw; continuing",
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
537
579
|
// ── Per-conversation single-flight lock ───────────────────────────────
|
|
538
580
|
//
|
|
539
581
|
// When a wake arrives and another run is in flight for the same
|
|
@@ -813,7 +855,7 @@ export async function wakeAgentForOpportunity(
|
|
|
813
855
|
// Apply the caller's persona override for the duration of the run. The
|
|
814
856
|
// prompt is built once before `agentLoop.run()` (via
|
|
815
857
|
// `conversation.buildCurrentSystemPrompt()`), which reads this field;
|
|
816
|
-
// cleared (below, before
|
|
858
|
+
// cleared (below, before the queue drain) so a queued user turn never builds
|
|
817
859
|
// its prompt under the wake's override. Assigned only AFTER the
|
|
818
860
|
// profile/config reads above — those can throw, and they run before the
|
|
819
861
|
// try/finally that clears the override, so an earlier assignment would
|
|
@@ -1411,10 +1453,14 @@ export async function wakeAgentForOpportunity(
|
|
|
1411
1453
|
// user turn or a later background read never inherits the wake's stamps.
|
|
1412
1454
|
const priorCallSite = conversation.currentCallSite;
|
|
1413
1455
|
const priorTurnOverrideProfile = conversation.currentTurnOverrideProfile;
|
|
1456
|
+
const priorTurnCronRunId = conversation.currentTurnCronRunId;
|
|
1414
1457
|
const priorHasNoClient = conversation.hasNoClient;
|
|
1415
1458
|
const priorTurnTrust = conversation.currentTurnTrustContext;
|
|
1416
1459
|
conversation.currentCallSite = callSite;
|
|
1417
1460
|
conversation.currentTurnOverrideProfile = overrideProfile;
|
|
1461
|
+
// Same reason as the stamps above: a wake triggered by a schedule firing
|
|
1462
|
+
// delegates work to subagents whose usage must attribute to that firing.
|
|
1463
|
+
conversation.currentTurnCronRunId = opts.cronRunId ?? null;
|
|
1418
1464
|
if (opts.clientless) {
|
|
1419
1465
|
conversation.hasNoClient = true;
|
|
1420
1466
|
}
|
|
@@ -1503,6 +1549,7 @@ export async function wakeAgentForOpportunity(
|
|
|
1503
1549
|
// at the start of the next normal turn regardless.)
|
|
1504
1550
|
conversation.currentCallSite = priorCallSite;
|
|
1505
1551
|
conversation.currentTurnOverrideProfile = priorTurnOverrideProfile;
|
|
1552
|
+
conversation.currentTurnCronRunId = priorTurnCronRunId;
|
|
1506
1553
|
conversation.hasNoClient = priorHasNoClient;
|
|
1507
1554
|
conversation.currentTurnTrustContext = priorTurnTrust;
|
|
1508
1555
|
}
|
|
@@ -1519,7 +1566,7 @@ export async function wakeAgentForOpportunity(
|
|
|
1519
1566
|
// Run completed cleanly. The canonical user-turn pattern
|
|
1520
1567
|
// (conversation-agent-loop.ts:1860, 2106-2126) updates
|
|
1521
1568
|
// `ctx.messages` first, then clears the flag via `ctx.setProcessing(false)`, then
|
|
1522
|
-
// calls `ctx.
|
|
1569
|
+
// calls `ctx.kickDrainQueue(...)`. We mirror that order so a message
|
|
1523
1570
|
// queued during the wake dequeues against an already-updated
|
|
1524
1571
|
// history — otherwise `drainSingleMessage` reads `ctx.messages`
|
|
1525
1572
|
// mid-tail and writes a DB row that lands out of chronological
|
|
@@ -1590,7 +1637,7 @@ export async function wakeAgentForOpportunity(
|
|
|
1590
1637
|
// Silent no-op: drop buffered events, push nothing, persist
|
|
1591
1638
|
// nothing, emit nothing. (No checkpoint fired during the run
|
|
1592
1639
|
// since checkpoints only fire after tool turns and there were
|
|
1593
|
-
// none.) The finally still
|
|
1640
|
+
// none.) The finally still kicks the queue drain so a racy queued
|
|
1594
1641
|
// message isn't stranded.
|
|
1595
1642
|
return { invoked: true, producedToolCalls: false };
|
|
1596
1643
|
}
|
|
@@ -1621,25 +1668,21 @@ export async function wakeAgentForOpportunity(
|
|
|
1621
1668
|
"agent-wake: setProcessing(false) threw; continuing",
|
|
1622
1669
|
);
|
|
1623
1670
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
{ conversationId, source, err },
|
|
1629
|
-
"agent-wake: drainQueue threw; continuing",
|
|
1630
|
-
);
|
|
1631
|
-
}
|
|
1671
|
+
await kickWakeDrainQueue(conversation, "agent_wake_tail", {
|
|
1672
|
+
conversationId,
|
|
1673
|
+
source,
|
|
1674
|
+
});
|
|
1632
1675
|
drainedInTry = true;
|
|
1633
1676
|
|
|
1634
1677
|
return { invoked: true, producedToolCalls };
|
|
1635
1678
|
} finally {
|
|
1636
1679
|
// Put the conversation's resting trust back on every exit path.
|
|
1637
1680
|
restorePersistentWakeTrust();
|
|
1638
|
-
// The success path (above) already called setProcessing(false)
|
|
1639
|
-
//
|
|
1640
|
-
// error and early-return paths where no tail was produced
|
|
1641
|
-
//
|
|
1642
|
-
//
|
|
1681
|
+
// The success path (above) already called setProcessing(false) and
|
|
1682
|
+
// kicked the queue drain after tail persist. This catch-all handles the
|
|
1683
|
+
// error and early-return paths where no tail was produced: those exit
|
|
1684
|
+
// the try body before reaching the drain block, so `drainedInTry` is
|
|
1685
|
+
// still false.
|
|
1643
1686
|
if (!drainedInTry) {
|
|
1644
1687
|
restoreWakeAllowedTools();
|
|
1645
1688
|
clearWakePersonaOverride();
|
|
@@ -1651,14 +1694,10 @@ export async function wakeAgentForOpportunity(
|
|
|
1651
1694
|
"agent-wake: setProcessing(false) threw; continuing",
|
|
1652
1695
|
);
|
|
1653
1696
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
{ conversationId, source, err },
|
|
1659
|
-
"agent-wake: drainQueue threw; continuing",
|
|
1660
|
-
);
|
|
1661
|
-
}
|
|
1697
|
+
await kickWakeDrainQueue(conversation, "agent_wake_cleanup", {
|
|
1698
|
+
conversationId,
|
|
1699
|
+
source,
|
|
1700
|
+
});
|
|
1662
1701
|
}
|
|
1663
1702
|
|
|
1664
1703
|
const durationMs = nowFn() - startedAt;
|