@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
|
@@ -32,6 +32,13 @@ let newMessages: Array<{
|
|
|
32
32
|
metadata?: string | null;
|
|
33
33
|
}> = [];
|
|
34
34
|
|
|
35
|
+
// Recorded `getMessagesAfter` calls, so overrideCursor tests can assert which
|
|
36
|
+
// cursor value actually reached the slice read.
|
|
37
|
+
let messagesAfterCalls: Array<{
|
|
38
|
+
conversationId: string;
|
|
39
|
+
afterId: string | null;
|
|
40
|
+
}> = [];
|
|
41
|
+
|
|
35
42
|
// Prior retrospective conversation + messages. `priorRetroOwnerId` is the
|
|
36
43
|
// fork-chain conversation the prior is rooted at — `findMostRecentRetrospectiveFor`
|
|
37
44
|
// returns it so the GC ownership check can tell "this source's own prior"
|
|
@@ -97,6 +104,10 @@ let messagesByConversationId: Record<string, StubMessage[]> = {};
|
|
|
97
104
|
// the real registry's semantics for conversations not in memory.
|
|
98
105
|
let loadedConversations: Record<string, { processing: boolean }> = {};
|
|
99
106
|
|
|
107
|
+
// Persisted `processing_started_at` stamps, keyed by conversation id, backing
|
|
108
|
+
// the stale-flag override's age check. Absent ids read as null (stampless).
|
|
109
|
+
let processingStartedAtById: Record<string, number | null> = {};
|
|
110
|
+
|
|
100
111
|
const watchdogEvents: Array<{
|
|
101
112
|
checkName: string;
|
|
102
113
|
value?: number | null;
|
|
@@ -155,7 +166,10 @@ mock.module("../find-most-recent-retrospective-for.js", () => ({
|
|
|
155
166
|
}));
|
|
156
167
|
|
|
157
168
|
mock.module("../../../../persistence/conversation-crud.js", () => ({
|
|
158
|
-
getMessagesAfter: (
|
|
169
|
+
getMessagesAfter: (id: string, afterId: string | null) => {
|
|
170
|
+
messagesAfterCalls.push({ conversationId: id, afterId });
|
|
171
|
+
return newMessages;
|
|
172
|
+
},
|
|
159
173
|
getMessages: (id: string) => {
|
|
160
174
|
if (messagesByConversationId[id]) {
|
|
161
175
|
return messagesByConversationId[id];
|
|
@@ -195,6 +209,8 @@ mock.module("../../../../persistence/conversation-crud.js", () => ({
|
|
|
195
209
|
const entry = loadedConversations[id];
|
|
196
210
|
return entry?.processing ?? false;
|
|
197
211
|
},
|
|
212
|
+
getConversationProcessingStartedAt: (id: string) =>
|
|
213
|
+
processingStartedAtById[id] ?? null,
|
|
198
214
|
forkConversationForRetrospective: async (params: {
|
|
199
215
|
conversationId: string;
|
|
200
216
|
throughMessageId?: string;
|
|
@@ -328,7 +344,7 @@ import type { MemoryJob } from "../../../../persistence/jobs-store.js";
|
|
|
328
344
|
import {
|
|
329
345
|
memoryRetrospectiveJob,
|
|
330
346
|
runForkBasedRetrospective,
|
|
331
|
-
|
|
347
|
+
STALE_SOURCE_PROCESSING_OVERRIDE_MS,
|
|
332
348
|
} from "../memory-retrospective-job.js";
|
|
333
349
|
|
|
334
350
|
function makeConfig(
|
|
@@ -339,12 +355,14 @@ function makeConfig(
|
|
|
339
355
|
matchConversationProfile?: boolean;
|
|
340
356
|
promptPath?: string;
|
|
341
357
|
requireUserActivity?: boolean;
|
|
358
|
+
enabled?: boolean;
|
|
342
359
|
} = {},
|
|
343
360
|
): Parameters<typeof memoryRetrospectiveJob>[1] {
|
|
344
361
|
return {
|
|
345
362
|
memory: {
|
|
346
363
|
v2: { enabled: true },
|
|
347
364
|
retrospective: {
|
|
365
|
+
enabled: overrides.enabled ?? true,
|
|
348
366
|
keepSupersededRuns: overrides.keepSupersededRuns ?? false,
|
|
349
367
|
matchConversationProfile: overrides.matchConversationProfile ?? false,
|
|
350
368
|
promptPath: overrides.promptPath ?? null,
|
|
@@ -397,6 +415,41 @@ function persistedInstructionText(): string {
|
|
|
397
415
|
return blocks[0]!.text;
|
|
398
416
|
}
|
|
399
417
|
|
|
418
|
+
/**
|
|
419
|
+
* Default persisted run output for the wake's fork conversation: a single
|
|
420
|
+
* assistant `remember` tool_use with an empty batch payload plus its
|
|
421
|
+
* successful tool_result. Satisfies the fail-closed advancement gate (a
|
|
422
|
+
* durable memory-writing call exists AND its execution succeeded) while
|
|
423
|
+
* appending nothing to the remembered log, so pre-gate assertions about log
|
|
424
|
+
* contents hold unchanged. Tests that assert the gate itself override
|
|
425
|
+
* `messagesByConversationId` for the fork id.
|
|
426
|
+
*/
|
|
427
|
+
function defaultForkRunOutput() {
|
|
428
|
+
return [
|
|
429
|
+
{
|
|
430
|
+
role: "assistant",
|
|
431
|
+
content: JSON.stringify([
|
|
432
|
+
{
|
|
433
|
+
type: "tool_use",
|
|
434
|
+
id: "tu-run-1",
|
|
435
|
+
name: "remember",
|
|
436
|
+
input: { content: [] },
|
|
437
|
+
},
|
|
438
|
+
]),
|
|
439
|
+
createdAt: Date.parse("2026-05-11T10:20:00Z"),
|
|
440
|
+
metadata: null,
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
role: "user",
|
|
444
|
+
content: JSON.stringify([
|
|
445
|
+
{ type: "tool_result", tool_use_id: "tu-run-1", content: "Saved." },
|
|
446
|
+
]),
|
|
447
|
+
createdAt: Date.parse("2026-05-11T10:20:01Z"),
|
|
448
|
+
metadata: null,
|
|
449
|
+
},
|
|
450
|
+
];
|
|
451
|
+
}
|
|
452
|
+
|
|
400
453
|
function priorRetroMessage(rememberContents: string[]) {
|
|
401
454
|
return {
|
|
402
455
|
role: "assistant",
|
|
@@ -421,6 +474,7 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
421
474
|
{ id: "m2", createdAt: Date.parse("2026-05-11T10:05:00Z") },
|
|
422
475
|
{ id: "m3", createdAt: Date.parse("2026-05-11T10:10:00Z") },
|
|
423
476
|
];
|
|
477
|
+
messagesAfterCalls = [];
|
|
424
478
|
priorRetroId = null;
|
|
425
479
|
priorRetroOwnerId = "src-conv-1";
|
|
426
480
|
priorRetroMessages = [];
|
|
@@ -434,8 +488,9 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
434
488
|
addMessageCalls = [];
|
|
435
489
|
retrospectiveJobUpserts = [];
|
|
436
490
|
conversationOverrides = {};
|
|
437
|
-
messagesByConversationId = {};
|
|
491
|
+
messagesByConversationId = { "fork-conv-1": defaultForkRunOutput() };
|
|
438
492
|
loadedConversations = {};
|
|
493
|
+
processingStartedAtById = {};
|
|
439
494
|
mockResolvedUserSlug = "alice";
|
|
440
495
|
resolveUserSlugCalls = [];
|
|
441
496
|
mockV3TierActive = false;
|
|
@@ -460,6 +515,21 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
460
515
|
expect(forkCalls[0]!.conversationId).toBe("src-conv-1");
|
|
461
516
|
});
|
|
462
517
|
|
|
518
|
+
test("retrospectives disabled: queued row drains as a no-op without forking", async () => {
|
|
519
|
+
const outcome = await memoryRetrospectiveJob(
|
|
520
|
+
makeJob(),
|
|
521
|
+
makeConfig({ enabled: false }),
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
expect(outcome.kind).toBe("disabled");
|
|
525
|
+
// The whole point of the switch: no fork, no wake, no pointer movement,
|
|
526
|
+
// so re-enabling reviews the same window the row was queued for.
|
|
527
|
+
expect(forkCalls).toHaveLength(0);
|
|
528
|
+
expect(wakeCalls).toHaveLength(0);
|
|
529
|
+
expect(stateUpserts).toHaveLength(0);
|
|
530
|
+
expect(lastRunAtBumps).toHaveLength(0);
|
|
531
|
+
});
|
|
532
|
+
|
|
463
533
|
test("dormant source beyond the sweep lookback: completed as a no-op, both pointers untouched", async () => {
|
|
464
534
|
conversationOverrides["src-conv-1"] = {
|
|
465
535
|
source: "user",
|
|
@@ -740,6 +810,375 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
740
810
|
expect(deletedConversationIds).toEqual(["fork-conv-1"]);
|
|
741
811
|
});
|
|
742
812
|
|
|
813
|
+
// -------------------------------------------------------------------------
|
|
814
|
+
// Fail-closed usable-output gate: `invoked: true` alone must not advance
|
|
815
|
+
// the cursor. The agent loop swallows provider rejections into a normal
|
|
816
|
+
// no-output return, so a wake can report success for a run that persisted
|
|
817
|
+
// nothing; advancement requires a durable memory-writing tool call on the
|
|
818
|
+
// run's own persisted tail.
|
|
819
|
+
// -------------------------------------------------------------------------
|
|
820
|
+
|
|
821
|
+
test("invoked wake with NO persisted run output: no_usable_output, cursor and log untouched, window retryable", async () => {
|
|
822
|
+
mockWakeResult = { invoked: true };
|
|
823
|
+
// The fork persisted nothing (e.g. a swallowed provider rejection or a
|
|
824
|
+
// zero-output max-tokens stop).
|
|
825
|
+
messagesByConversationId["fork-conv-1"] = [];
|
|
826
|
+
|
|
827
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
828
|
+
|
|
829
|
+
expect(outcome.kind).toBe("no_usable_output");
|
|
830
|
+
// Cursor NOT advanced, remembered log NOT appended.
|
|
831
|
+
expect(stateUpserts).toHaveLength(0);
|
|
832
|
+
// Retryable: cooldown bump only, orphan fork cleaned up.
|
|
833
|
+
expect(lastRunAtBumps).toHaveLength(1);
|
|
834
|
+
expect(deletedConversationIds).toEqual(["fork-conv-1"]);
|
|
835
|
+
expect(
|
|
836
|
+
watchdogEvents.filter((e) => e.checkName === "memory_retrospective_run"),
|
|
837
|
+
).toEqual([
|
|
838
|
+
{
|
|
839
|
+
checkName: "memory_retrospective_run",
|
|
840
|
+
value: 1,
|
|
841
|
+
detail: {
|
|
842
|
+
outcome: "no_usable_output",
|
|
843
|
+
reason: "run persisted no memory-writing tool call",
|
|
844
|
+
},
|
|
845
|
+
},
|
|
846
|
+
]);
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
test("invoked wake whose run persisted only analysis text (no tool calls): no_usable_output, prior retrospective preserved", async () => {
|
|
850
|
+
mockWakeResult = { invoked: true };
|
|
851
|
+
priorRetroId = "prior-retro-1";
|
|
852
|
+
messagesByConversationId["fork-conv-1"] = [
|
|
853
|
+
{
|
|
854
|
+
role: "assistant",
|
|
855
|
+
content: JSON.stringify([
|
|
856
|
+
{ type: "text", text: "Nothing new worth remembering here." },
|
|
857
|
+
]),
|
|
858
|
+
createdAt: Date.parse("2026-05-11T10:20:00Z"),
|
|
859
|
+
metadata: null,
|
|
860
|
+
},
|
|
861
|
+
];
|
|
862
|
+
|
|
863
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
864
|
+
|
|
865
|
+
expect(outcome.kind).toBe("no_usable_output");
|
|
866
|
+
expect(stateUpserts).toHaveLength(0);
|
|
867
|
+
// Only this run's own fork is deleted; the prior retrospective (the
|
|
868
|
+
// dedup baseline for the retry) is preserved.
|
|
869
|
+
expect(deletedConversationIds).toEqual(["fork-conv-1"]);
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
test("run-message load failure classifies as no_usable_output (fail-closed), not success", async () => {
|
|
873
|
+
mockWakeResult = { invoked: true };
|
|
874
|
+
// Fork-kind conversation with no stamps and no leading instruction row:
|
|
875
|
+
// `loadRetrospectiveRunMessages` returns null (indeterminate).
|
|
876
|
+
conversationOverrides["fork-conv-1"] = {
|
|
877
|
+
source: "memory-retrospective-fork",
|
|
878
|
+
forkParentMessageId: null,
|
|
879
|
+
};
|
|
880
|
+
messagesByConversationId["fork-conv-1"] = [
|
|
881
|
+
{
|
|
882
|
+
role: "assistant",
|
|
883
|
+
content: JSON.stringify([{ type: "text", text: "copied row" }]),
|
|
884
|
+
createdAt: 1,
|
|
885
|
+
metadata: null,
|
|
886
|
+
},
|
|
887
|
+
];
|
|
888
|
+
|
|
889
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
890
|
+
|
|
891
|
+
expect(outcome.kind).toBe("no_usable_output");
|
|
892
|
+
expect(stateUpserts).toHaveLength(0);
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
test("checkpoint-persisted saves on a fork-kind tail count as usable output (rebase-after-live cannot fake no-output)", async () => {
|
|
896
|
+
mockWakeResult = { invoked: true };
|
|
897
|
+
conversationOverrides["fork-conv-1"] = {
|
|
898
|
+
source: "memory-retrospective-fork",
|
|
899
|
+
forkParentMessageId: null,
|
|
900
|
+
};
|
|
901
|
+
// Copied prefix rows carry forkSourceMessageId stamps; the run's own
|
|
902
|
+
// persisted tail (from checkpoint persistence, regardless of what the
|
|
903
|
+
// in-memory history looked like after any rebase) sits after the
|
|
904
|
+
// boundary and carries the durable remember call.
|
|
905
|
+
messagesByConversationId["fork-conv-1"] = [
|
|
906
|
+
{
|
|
907
|
+
role: "user",
|
|
908
|
+
content: JSON.stringify([{ type: "text", text: "copied source row" }]),
|
|
909
|
+
createdAt: 100,
|
|
910
|
+
metadata: JSON.stringify({ forkSourceMessageId: "m2" }),
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
role: "assistant",
|
|
914
|
+
content: JSON.stringify([
|
|
915
|
+
{
|
|
916
|
+
type: "tool_use",
|
|
917
|
+
id: "tu-ckpt-1",
|
|
918
|
+
name: "remember",
|
|
919
|
+
input: { content: ["fact persisted via checkpoint"] },
|
|
920
|
+
},
|
|
921
|
+
]),
|
|
922
|
+
createdAt: 200,
|
|
923
|
+
metadata: null,
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
role: "user",
|
|
927
|
+
content: JSON.stringify([
|
|
928
|
+
{ type: "tool_result", tool_use_id: "tu-ckpt-1", content: "Saved." },
|
|
929
|
+
]),
|
|
930
|
+
createdAt: 201,
|
|
931
|
+
metadata: null,
|
|
932
|
+
},
|
|
933
|
+
];
|
|
934
|
+
|
|
935
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
936
|
+
|
|
937
|
+
expect(outcome.kind).toBe("invoked");
|
|
938
|
+
expect(stateUpserts).toHaveLength(1);
|
|
939
|
+
expect(stateUpserts[0]!.rememberedLog).toEqual([
|
|
940
|
+
"fact persisted via checkpoint",
|
|
941
|
+
]);
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
test("scaffold_managed_skill counts as durable work even with zero remembers", async () => {
|
|
945
|
+
mockWakeResult = { invoked: true };
|
|
946
|
+
messagesByConversationId["fork-conv-1"] = [
|
|
947
|
+
{
|
|
948
|
+
role: "assistant",
|
|
949
|
+
content: JSON.stringify([
|
|
950
|
+
{
|
|
951
|
+
type: "tool_use",
|
|
952
|
+
id: "tu-skill-1",
|
|
953
|
+
name: "scaffold_managed_skill",
|
|
954
|
+
input: { name: "deploy-checklist" },
|
|
955
|
+
},
|
|
956
|
+
]),
|
|
957
|
+
createdAt: Date.parse("2026-05-11T10:20:00Z"),
|
|
958
|
+
metadata: null,
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
role: "user",
|
|
962
|
+
content: JSON.stringify([
|
|
963
|
+
{
|
|
964
|
+
type: "tool_result",
|
|
965
|
+
tool_use_id: "tu-skill-1",
|
|
966
|
+
content: "Skill scaffolded.",
|
|
967
|
+
},
|
|
968
|
+
]),
|
|
969
|
+
createdAt: Date.parse("2026-05-11T10:20:01Z"),
|
|
970
|
+
metadata: null,
|
|
971
|
+
},
|
|
972
|
+
];
|
|
973
|
+
|
|
974
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
975
|
+
|
|
976
|
+
expect(outcome.kind).toBe("invoked");
|
|
977
|
+
expect(stateUpserts).toHaveLength(1);
|
|
978
|
+
expect(stateUpserts[0]!.rememberedLog).toEqual([]);
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
test("a remember whose execution FAILED (is_error tool_result) is not durable evidence and its facts stay out of the log", async () => {
|
|
982
|
+
mockWakeResult = { invoked: true };
|
|
983
|
+
messagesByConversationId["fork-conv-1"] = [
|
|
984
|
+
{
|
|
985
|
+
role: "assistant",
|
|
986
|
+
content: JSON.stringify([
|
|
987
|
+
{
|
|
988
|
+
type: "tool_use",
|
|
989
|
+
id: "tu-fail-1",
|
|
990
|
+
name: "remember",
|
|
991
|
+
input: { content: "fact that never landed" },
|
|
992
|
+
},
|
|
993
|
+
]),
|
|
994
|
+
createdAt: Date.parse("2026-05-11T10:20:00Z"),
|
|
995
|
+
metadata: null,
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
role: "user",
|
|
999
|
+
content: JSON.stringify([
|
|
1000
|
+
{
|
|
1001
|
+
type: "tool_result",
|
|
1002
|
+
tool_use_id: "tu-fail-1",
|
|
1003
|
+
content: "memory buffer write failed",
|
|
1004
|
+
is_error: true,
|
|
1005
|
+
},
|
|
1006
|
+
]),
|
|
1007
|
+
createdAt: Date.parse("2026-05-11T10:20:01Z"),
|
|
1008
|
+
metadata: null,
|
|
1009
|
+
},
|
|
1010
|
+
];
|
|
1011
|
+
|
|
1012
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1013
|
+
|
|
1014
|
+
// The tool_use alone proves the model asked; the durable write happens
|
|
1015
|
+
// in the executor, and it failed. Window stays retryable, and the failed
|
|
1016
|
+
// fact must not enter <already_remembered> where it would suppress the
|
|
1017
|
+
// retry's re-save.
|
|
1018
|
+
expect(outcome.kind).toBe("no_usable_output");
|
|
1019
|
+
expect(stateUpserts).toHaveLength(0);
|
|
1020
|
+
expect(lastRunAtBumps).toHaveLength(1);
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
test("explicit no-findings reply advances the cursor without fabricating a memory write", async () => {
|
|
1024
|
+
mockWakeResult = { invoked: true };
|
|
1025
|
+
messagesByConversationId["fork-conv-1"] = [
|
|
1026
|
+
{
|
|
1027
|
+
role: "assistant",
|
|
1028
|
+
content: JSON.stringify([
|
|
1029
|
+
{ type: "text", text: "Nothing new to save." },
|
|
1030
|
+
]),
|
|
1031
|
+
createdAt: Date.parse("2026-05-11T10:20:00Z"),
|
|
1032
|
+
metadata: null,
|
|
1033
|
+
},
|
|
1034
|
+
];
|
|
1035
|
+
|
|
1036
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1037
|
+
|
|
1038
|
+
// A reviewed-and-no-findings pass is a legitimate success: the mandated
|
|
1039
|
+
// exact reply is the positive persisted artifact, the cursor advances,
|
|
1040
|
+
// and the remembered log gains nothing.
|
|
1041
|
+
expect(outcome.kind).toBe("invoked");
|
|
1042
|
+
expect(stateUpserts).toHaveLength(1);
|
|
1043
|
+
expect(stateUpserts[0]!.lastProcessedMessageId).toBe("m3");
|
|
1044
|
+
expect(stateUpserts[0]!.rememberedLog).toEqual([]);
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
test("analysis prose that merely mentions the no-findings phrase does not advance", async () => {
|
|
1048
|
+
mockWakeResult = { invoked: true };
|
|
1049
|
+
messagesByConversationId["fork-conv-1"] = [
|
|
1050
|
+
{
|
|
1051
|
+
role: "assistant",
|
|
1052
|
+
content: JSON.stringify([
|
|
1053
|
+
{
|
|
1054
|
+
type: "text",
|
|
1055
|
+
text: "I reviewed the window. Nothing new to save. Here is my reasoning about why each item was already covered by prior passes.",
|
|
1056
|
+
},
|
|
1057
|
+
]),
|
|
1058
|
+
createdAt: Date.parse("2026-05-11T10:20:00Z"),
|
|
1059
|
+
metadata: null,
|
|
1060
|
+
},
|
|
1061
|
+
];
|
|
1062
|
+
|
|
1063
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1064
|
+
|
|
1065
|
+
expect(outcome.kind).toBe("no_usable_output");
|
|
1066
|
+
expect(stateUpserts).toHaveLength(0);
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
test("no-findings reply cannot advance a run that attempted a save and failed", async () => {
|
|
1070
|
+
mockWakeResult = { invoked: true };
|
|
1071
|
+
messagesByConversationId["fork-conv-1"] = [
|
|
1072
|
+
{
|
|
1073
|
+
role: "assistant",
|
|
1074
|
+
content: JSON.stringify([
|
|
1075
|
+
{
|
|
1076
|
+
type: "tool_use",
|
|
1077
|
+
id: "tu-mixed-1",
|
|
1078
|
+
name: "remember",
|
|
1079
|
+
input: { content: "fact that failed to land" },
|
|
1080
|
+
},
|
|
1081
|
+
]),
|
|
1082
|
+
createdAt: Date.parse("2026-05-11T10:20:00Z"),
|
|
1083
|
+
metadata: null,
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
role: "user",
|
|
1087
|
+
content: JSON.stringify([
|
|
1088
|
+
{
|
|
1089
|
+
type: "tool_result",
|
|
1090
|
+
tool_use_id: "tu-mixed-1",
|
|
1091
|
+
content: "write failed",
|
|
1092
|
+
is_error: true,
|
|
1093
|
+
},
|
|
1094
|
+
]),
|
|
1095
|
+
createdAt: Date.parse("2026-05-11T10:20:01Z"),
|
|
1096
|
+
metadata: null,
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
role: "assistant",
|
|
1100
|
+
content: JSON.stringify([
|
|
1101
|
+
{ type: "text", text: "Nothing new to save." },
|
|
1102
|
+
]),
|
|
1103
|
+
createdAt: Date.parse("2026-05-11T10:20:02Z"),
|
|
1104
|
+
metadata: null,
|
|
1105
|
+
},
|
|
1106
|
+
];
|
|
1107
|
+
|
|
1108
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1109
|
+
|
|
1110
|
+
// The run demonstrably had findings (it attempted a save); the failed
|
|
1111
|
+
// write must stay retryable rather than being papered over by a
|
|
1112
|
+
// no-findings claim.
|
|
1113
|
+
expect(outcome.kind).toBe("no_usable_output");
|
|
1114
|
+
expect(stateUpserts).toHaveLength(0);
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
test("a remember tool_use with NO persisted tool_result is not durable evidence", async () => {
|
|
1118
|
+
mockWakeResult = { invoked: true };
|
|
1119
|
+
messagesByConversationId["fork-conv-1"] = [
|
|
1120
|
+
{
|
|
1121
|
+
role: "assistant",
|
|
1122
|
+
content: JSON.stringify([
|
|
1123
|
+
{
|
|
1124
|
+
type: "tool_use",
|
|
1125
|
+
id: "tu-noresult-1",
|
|
1126
|
+
name: "remember",
|
|
1127
|
+
input: { content: "fact with unproven execution" },
|
|
1128
|
+
},
|
|
1129
|
+
]),
|
|
1130
|
+
createdAt: Date.parse("2026-05-11T10:20:00Z"),
|
|
1131
|
+
metadata: null,
|
|
1132
|
+
},
|
|
1133
|
+
];
|
|
1134
|
+
|
|
1135
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1136
|
+
|
|
1137
|
+
expect(outcome.kind).toBe("no_usable_output");
|
|
1138
|
+
expect(stateUpserts).toHaveLength(0);
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
test("retry after a no_usable_output run advances only once durable output exists", async () => {
|
|
1142
|
+
mockWakeResult = { invoked: true };
|
|
1143
|
+
messagesByConversationId["fork-conv-1"] = [];
|
|
1144
|
+
|
|
1145
|
+
const first = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1146
|
+
expect(first.kind).toBe("no_usable_output");
|
|
1147
|
+
expect(stateUpserts).toHaveLength(0);
|
|
1148
|
+
|
|
1149
|
+
// Retry: same window, this time the run persists a real save and its
|
|
1150
|
+
// successful execution.
|
|
1151
|
+
messagesByConversationId["fork-conv-1"] = [
|
|
1152
|
+
{
|
|
1153
|
+
role: "assistant",
|
|
1154
|
+
content: JSON.stringify([
|
|
1155
|
+
{
|
|
1156
|
+
type: "tool_use",
|
|
1157
|
+
id: "tu-retry-1",
|
|
1158
|
+
name: "remember",
|
|
1159
|
+
input: { content: "fact captured on retry" },
|
|
1160
|
+
},
|
|
1161
|
+
]),
|
|
1162
|
+
createdAt: Date.parse("2026-05-11T10:25:00Z"),
|
|
1163
|
+
metadata: null,
|
|
1164
|
+
},
|
|
1165
|
+
{
|
|
1166
|
+
role: "user",
|
|
1167
|
+
content: JSON.stringify([
|
|
1168
|
+
{ type: "tool_result", tool_use_id: "tu-retry-1", content: "Saved." },
|
|
1169
|
+
]),
|
|
1170
|
+
createdAt: Date.parse("2026-05-11T10:25:01Z"),
|
|
1171
|
+
metadata: null,
|
|
1172
|
+
},
|
|
1173
|
+
];
|
|
1174
|
+
const second = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1175
|
+
|
|
1176
|
+
expect(second.kind).toBe("invoked");
|
|
1177
|
+
expect(stateUpserts).toHaveLength(1);
|
|
1178
|
+
expect(stateUpserts[0]!.lastProcessedMessageId).toBe("m3");
|
|
1179
|
+
expect(stateUpserts[0]!.rememberedLog).toEqual(["fact captured on retry"]);
|
|
1180
|
+
});
|
|
1181
|
+
|
|
743
1182
|
test("wake throws: lastRunAt bumped before rethrow, orphan fork deleted, error rethrown", async () => {
|
|
744
1183
|
mockWakeThrows = new Error("LLM provider 503");
|
|
745
1184
|
await expect(memoryRetrospectiveJob(makeJob(), stubConfig)).rejects.toThrow(
|
|
@@ -957,7 +1396,7 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
957
1396
|
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
958
1397
|
|
|
959
1398
|
const instructionText = persistedInstructionText();
|
|
960
|
-
expect(instructionText).toContain("
|
|
1399
|
+
expect(instructionText).toContain("<\u200B/already_remembered>");
|
|
961
1400
|
});
|
|
962
1401
|
|
|
963
1402
|
test("honors memory.retrospective.promptPath override when set", async () => {
|
|
@@ -1012,12 +1451,10 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
1012
1451
|
// Mid-turn requeue gate
|
|
1013
1452
|
// -------------------------------------------------------------------------
|
|
1014
1453
|
|
|
1015
|
-
test("source mid-turn →
|
|
1454
|
+
test("source mid-turn → source_processing outcome, state fully untouched, no self-upserted row, no fork", async () => {
|
|
1016
1455
|
loadedConversations["src-conv-1"] = { processing: true };
|
|
1017
1456
|
|
|
1018
|
-
const before = Date.now();
|
|
1019
1457
|
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1020
|
-
const after = Date.now();
|
|
1021
1458
|
|
|
1022
1459
|
expect(outcome.kind).toBe("source_processing");
|
|
1023
1460
|
// BOTH pointers untouched. `lastRunAt` must stay unbumped so the
|
|
@@ -1027,18 +1464,11 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
1027
1464
|
// forever.
|
|
1028
1465
|
expect(stateUpserts).toHaveLength(0);
|
|
1029
1466
|
expect(lastRunAtBumps).toHaveLength(0);
|
|
1030
|
-
//
|
|
1031
|
-
//
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
});
|
|
1036
|
-
expect(retrospectiveJobUpserts[0]!.runAfter).toBeGreaterThanOrEqual(
|
|
1037
|
-
before + SOURCE_PROCESSING_REQUEUE_DELAY_MS,
|
|
1038
|
-
);
|
|
1039
|
-
expect(retrospectiveJobUpserts[0]!.runAfter).toBeLessThanOrEqual(
|
|
1040
|
-
after + SOURCE_PROCESSING_REQUEUE_DELAY_MS,
|
|
1041
|
-
);
|
|
1467
|
+
// The handler no longer mints a fresh fallback row per attempt: the
|
|
1468
|
+
// worker defers the SAME job row on the bounded deferral counter (see
|
|
1469
|
+
// `resolveRetrospectiveOutcome`), so a stranded processing flag cannot
|
|
1470
|
+
// produce an unbounded stream of job rows.
|
|
1471
|
+
expect(retrospectiveJobUpserts).toHaveLength(0);
|
|
1042
1472
|
// No fork, no instruction, no wake, no cleanup side effects.
|
|
1043
1473
|
expect(forkCalls).toHaveLength(0);
|
|
1044
1474
|
expect(addMessageCalls).toHaveLength(0);
|
|
@@ -1046,6 +1476,41 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
1046
1476
|
expect(deletedConversationIds).toEqual([]);
|
|
1047
1477
|
});
|
|
1048
1478
|
|
|
1479
|
+
test("source mid-turn with a fresh processing stamp → still deferred", async () => {
|
|
1480
|
+
loadedConversations["src-conv-1"] = { processing: true };
|
|
1481
|
+
processingStartedAtById["src-conv-1"] = Date.now() - 60_000;
|
|
1482
|
+
|
|
1483
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1484
|
+
|
|
1485
|
+
expect(outcome.kind).toBe("source_processing");
|
|
1486
|
+
expect(forkCalls).toHaveLength(0);
|
|
1487
|
+
});
|
|
1488
|
+
|
|
1489
|
+
test("source mid-turn with a stamp older than the stale override → proceeds and runs normally", async () => {
|
|
1490
|
+
loadedConversations["src-conv-1"] = { processing: true };
|
|
1491
|
+
processingStartedAtById["src-conv-1"] =
|
|
1492
|
+
Date.now() - STALE_SOURCE_PROCESSING_OVERRIDE_MS - 60_000;
|
|
1493
|
+
|
|
1494
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1495
|
+
|
|
1496
|
+
// A stranded flag (swallowed turn-end clear while the daemon stayed up)
|
|
1497
|
+
// must not block memory formation indefinitely: past the override window
|
|
1498
|
+
// the run proceeds as if idle.
|
|
1499
|
+
expect(outcome.kind).toBe("invoked");
|
|
1500
|
+
expect(forkCalls).toHaveLength(1);
|
|
1501
|
+
expect(stateUpserts).toHaveLength(1);
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
test("source mid-turn with a set flag but no persisted stamp → deferred (reads as live)", async () => {
|
|
1505
|
+
loadedConversations["src-conv-1"] = { processing: true };
|
|
1506
|
+
// No processingStartedAtById entry: the stamp write may have failed, so
|
|
1507
|
+
// age cannot be established. The bounded deferral budget still caps it.
|
|
1508
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1509
|
+
|
|
1510
|
+
expect(outcome.kind).toBe("source_processing");
|
|
1511
|
+
expect(forkCalls).toHaveLength(0);
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1049
1514
|
test("source loaded but idle → normal run, no requeue upsert", async () => {
|
|
1050
1515
|
loadedConversations["src-conv-1"] = { processing: false };
|
|
1051
1516
|
|
|
@@ -1750,6 +2215,7 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
1750
2215
|
content: JSON.stringify([
|
|
1751
2216
|
{
|
|
1752
2217
|
type: "tool_use",
|
|
2218
|
+
id: "tu-tail-1",
|
|
1753
2219
|
name: "remember",
|
|
1754
2220
|
input: { content: "post-fork tail save — included" },
|
|
1755
2221
|
},
|
|
@@ -1757,6 +2223,14 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
1757
2223
|
createdAt: 2000,
|
|
1758
2224
|
metadata: null,
|
|
1759
2225
|
},
|
|
2226
|
+
{
|
|
2227
|
+
role: "user",
|
|
2228
|
+
content: JSON.stringify([
|
|
2229
|
+
{ type: "tool_result", tool_use_id: "tu-tail-1", content: "Saved." },
|
|
2230
|
+
]),
|
|
2231
|
+
createdAt: 2001,
|
|
2232
|
+
metadata: null,
|
|
2233
|
+
},
|
|
1760
2234
|
];
|
|
1761
2235
|
|
|
1762
2236
|
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
@@ -1814,6 +2288,7 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
1814
2288
|
content: JSON.stringify([
|
|
1815
2289
|
{
|
|
1816
2290
|
type: "tool_use",
|
|
2291
|
+
id: "tu-seed-1",
|
|
1817
2292
|
name: "remember",
|
|
1818
2293
|
input: { content: "this run's save" },
|
|
1819
2294
|
},
|
|
@@ -1821,6 +2296,14 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
1821
2296
|
createdAt: 2000,
|
|
1822
2297
|
metadata: null,
|
|
1823
2298
|
},
|
|
2299
|
+
{
|
|
2300
|
+
role: "user",
|
|
2301
|
+
content: JSON.stringify([
|
|
2302
|
+
{ type: "tool_result", tool_use_id: "tu-seed-1", content: "Saved." },
|
|
2303
|
+
]),
|
|
2304
|
+
createdAt: 2001,
|
|
2305
|
+
metadata: null,
|
|
2306
|
+
},
|
|
1824
2307
|
];
|
|
1825
2308
|
|
|
1826
2309
|
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
@@ -1859,6 +2342,7 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
1859
2342
|
content: JSON.stringify([
|
|
1860
2343
|
{
|
|
1861
2344
|
type: "tool_use",
|
|
2345
|
+
id: "tu-carry-1",
|
|
1862
2346
|
name: "remember",
|
|
1863
2347
|
input: { content: "fork tail save" },
|
|
1864
2348
|
},
|
|
@@ -1866,6 +2350,14 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
1866
2350
|
createdAt: 2000,
|
|
1867
2351
|
metadata: null,
|
|
1868
2352
|
},
|
|
2353
|
+
{
|
|
2354
|
+
role: "user",
|
|
2355
|
+
content: JSON.stringify([
|
|
2356
|
+
{ type: "tool_result", tool_use_id: "tu-carry-1", content: "Saved." },
|
|
2357
|
+
]),
|
|
2358
|
+
createdAt: 2001,
|
|
2359
|
+
metadata: null,
|
|
2360
|
+
},
|
|
1869
2361
|
];
|
|
1870
2362
|
|
|
1871
2363
|
await memoryRetrospectiveJob(makeJob(), stubConfig);
|