@vellumai/assistant 0.8.6 → 0.8.7
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/AGENTS.md +4 -4
- package/Dockerfile +1 -0
- package/bun.lock +11 -2
- package/docker-entrypoint.sh +8 -6
- package/docs/plugins.md +63 -28
- package/examples/plugins/echo/register.ts +4 -7
- package/knip.json +1 -0
- package/node_modules/@vellumai/environments/bun.lock +24 -0
- package/node_modules/@vellumai/environments/package.json +18 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
- package/node_modules/@vellumai/environments/src/index.ts +11 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
- package/node_modules/@vellumai/environments/src/types.ts +70 -0
- package/node_modules/@vellumai/environments/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +3 -4
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +6 -2
- package/openapi.yaml +3735 -353
- package/package.json +7 -3
- package/scripts/generate-openapi.ts +20 -13
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
- package/src/__tests__/agent-loop-exit-reason.test.ts +240 -39
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +19 -32
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +6 -4
- package/src/__tests__/agent-loop-thinking.test.ts +17 -12
- package/src/__tests__/agent-loop.test.ts +207 -341
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +4 -2
- package/src/__tests__/agent-wake-override-profile.test.ts +22 -40
- package/src/__tests__/anthropic-provider.test.ts +201 -55
- package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
- package/src/__tests__/app-control-flow.test.ts +5 -0
- package/src/__tests__/approval-cascade.test.ts +4 -11
- package/src/__tests__/approval-routes-http.test.ts +4 -2
- package/src/__tests__/assistant-event.test.ts +15 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/avatar-e2e.test.ts +7 -37
- package/src/__tests__/avatar-generator.test.ts +12 -42
- package/src/__tests__/avatar-identity-sync.test.ts +28 -3
- package/src/__tests__/background-shell-bash.test.ts +3 -7
- package/src/__tests__/btw-routes.test.ts +7 -12
- package/src/__tests__/call-pointer-messages.test.ts +5 -3
- package/src/__tests__/call-site-routing-provider.test.ts +22 -40
- package/src/__tests__/catalog-files.test.ts +1 -0
- package/src/__tests__/channel-approval-routes.test.ts +48 -20
- package/src/__tests__/channel-approvals.test.ts +3 -1
- package/src/__tests__/channel-invite-transport.test.ts +1 -5
- package/src/__tests__/channel-readiness-routes.test.ts +0 -4
- package/src/__tests__/channel-readiness-slack-remote.test.ts +2 -7
- package/src/__tests__/channel-retry-sweep.test.ts +71 -79
- package/src/__tests__/circuit-breaker-pipeline.test.ts +3 -3
- package/src/__tests__/clawhub-files.test.ts +1 -0
- package/src/__tests__/compaction-events.test.ts +5 -17
- package/src/__tests__/compaction-pipeline.test.ts +1 -1
- package/src/__tests__/compaction-timeout-recovery.test.ts +37 -48
- package/src/__tests__/compaction-trail-store.test.ts +1 -79
- package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
- package/src/__tests__/computer-use-tools.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +28 -0
- package/src/__tests__/context-search-agent-runner.test.ts +6 -3
- package/src/__tests__/context-token-estimator.test.ts +34 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +14 -7
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +3 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +12 -27
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +430 -90
- package/src/__tests__/conversation-agent-loop.test.ts +581 -62
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clear-safety.test.ts +20 -10
- package/src/__tests__/conversation-confirmation-signals.test.ts +15 -45
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-disk-view.test.ts +10 -17
- package/src/__tests__/conversation-fork-crud.test.ts +86 -172
- package/src/__tests__/conversation-fork-route.test.ts +16 -14
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
- package/src/__tests__/conversation-lifecycle.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +3 -2
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -1
- package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
- package/src/__tests__/conversation-pairing.test.ts +34 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -0
- package/src/__tests__/conversation-process-callsite.test.ts +27 -30
- package/src/__tests__/conversation-provider-retry-repair.test.ts +53 -44
- package/src/__tests__/conversation-queue.test.ts +270 -164
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -2
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +20 -22
- package/src/__tests__/conversation-runtime-workspace.test.ts +19 -1
- package/src/__tests__/conversation-slash-queue.test.ts +37 -31
- package/src/__tests__/conversation-slash-unknown.test.ts +13 -15
- package/src/__tests__/conversation-speed-override.test.ts +8 -22
- package/src/__tests__/conversation-stream-state.test.ts +484 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +6 -15
- package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
- package/src/__tests__/conversation-surfaces-state-update.test.ts +5 -2
- package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -15
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +23 -11
- package/src/__tests__/conversation-unread-route.test.ts +14 -2
- package/src/__tests__/conversation-usage.test.ts +0 -2
- package/src/__tests__/conversation-wipe.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +3 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +48 -22
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +27 -7
- package/src/__tests__/credential-execution-tools.test.ts +1 -2
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/cross-provider-web-search.test.ts +6 -2
- package/src/__tests__/cu-unified-flow.test.ts +26 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +11 -0
- package/src/__tests__/disk-pressure-guard.test.ts +66 -0
- package/src/__tests__/disk-pressure-routes.test.ts +9 -2
- package/src/__tests__/dm-persistence.test.ts +7 -2
- package/src/__tests__/dynamic-page-surface.test.ts +68 -0
- package/src/__tests__/edit-propagation.test.ts +1 -2
- package/src/__tests__/empty-response-pipeline.test.ts +127 -5
- package/src/__tests__/filing-service.test.ts +2 -2
- package/src/__tests__/first-greeting.test.ts +55 -14
- package/src/__tests__/gemini-inline-media.test.ts +78 -0
- package/src/__tests__/gemini-provider.test.ts +351 -28
- package/src/__tests__/guardian-routing-state.test.ts +60 -71
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +9 -7
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -1
- package/src/__tests__/history-repair-hook.test.ts +161 -0
- package/src/__tests__/history-repair-observability.test.ts +1 -1
- package/src/__tests__/history-repair.test.ts +2 -1
- package/src/__tests__/host-app-control-proxy.test.ts +2 -0
- package/src/__tests__/host-cu-proxy.test.ts +2 -0
- package/src/__tests__/host-file-edit-tool.test.ts +4 -2
- package/src/__tests__/host-file-proxy.test.ts +31 -0
- package/src/__tests__/host-file-read-tool.test.ts +4 -2
- package/src/__tests__/host-file-write-tool.test.ts +9 -3
- package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
- package/src/__tests__/host-shell-tool.test.ts +9 -4
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/identity-intro-cache.test.ts +35 -14
- package/src/__tests__/inbound-slack-persistence.test.ts +7 -2
- package/src/__tests__/injector-background-turn.test.ts +1 -1
- package/src/__tests__/injector-chain.test.ts +1 -1
- package/src/__tests__/injector-disk-pressure.test.ts +1 -1
- package/src/__tests__/injector-document-comments.test.ts +1 -1
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +1 -1
- package/src/__tests__/injector-v3-suppression.test.ts +220 -0
- package/src/__tests__/list-messages-attachments.test.ts +7 -8
- package/src/__tests__/list-messages-hidden-metadata.test.ts +17 -15
- package/src/__tests__/list-messages-page-latest.test.ts +0 -1
- package/src/__tests__/list-messages-tool-merge.test.ts +36 -6
- package/src/__tests__/llm-call-pipeline.test.ts +21 -15
- package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
- package/src/__tests__/llm-resolver.test.ts +23 -47
- package/src/__tests__/llm-usage-store.test.ts +45 -0
- package/src/__tests__/log-export-routes.test.ts +59 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
- package/src/__tests__/mcp-auth-routes.test.ts +15 -10
- package/src/__tests__/mcp-health-check.test.ts +18 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +1 -1
- package/src/__tests__/memory-v2-static-injector.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +8 -4
- package/src/__tests__/migration-export-http.test.ts +12 -12
- package/src/__tests__/migration-import-commit-http.test.ts +8 -8
- package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +14 -20
- package/src/__tests__/notification-decision-identity.test.ts +9 -18
- package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
- package/src/__tests__/oauth-commands-routes.test.ts +1 -1
- package/src/__tests__/onboarding-template-contract.test.ts +10 -0
- package/src/__tests__/openai-provider.test.ts +66 -70
- package/src/__tests__/openai-responses-provider.test.ts +21 -77
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -1
- package/src/__tests__/overflow-reduce-pipeline.test.ts +2 -4
- package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
- package/src/__tests__/persistence-pipeline.test.ts +15 -26
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -1
- package/src/__tests__/pipeline-runner.test.ts +2 -3
- package/src/__tests__/plugin-bootstrap.test.ts +51 -25
- package/src/__tests__/plugin-route-contribution.test.ts +6 -16
- package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
- package/src/__tests__/plugin-tool-contribution.test.ts +10 -26
- package/src/__tests__/plugin-types.test.ts +7 -14
- package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
- package/src/__tests__/process-message-background-slack.test.ts +17 -16
- package/src/__tests__/process-message-display-content.test.ts +30 -42
- package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
- package/src/__tests__/provider-error-scenarios.test.ts +7 -6
- package/src/__tests__/provider-platform-proxy-integration.test.ts +3 -8
- package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
- package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
- package/src/__tests__/provider-usage-tracking.test.ts +0 -6
- package/src/__tests__/ratelimit.test.ts +9 -4
- package/src/__tests__/relay-server.test.ts +20 -13
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
- package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
- package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +353 -0
- package/src/__tests__/schedule-routes.test.ts +80 -10
- package/src/__tests__/schedule-store.test.ts +67 -0
- package/src/__tests__/schedule-tools.test.ts +125 -0
- package/src/__tests__/secret-ingress-http.test.ts +2 -2
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
- package/src/__tests__/secret-response-routing.test.ts +13 -11
- package/src/__tests__/send-endpoint-busy.test.ts +2 -1
- package/src/__tests__/shell-observability.test.ts +249 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +11 -11
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +10 -10
- package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
- package/src/__tests__/skillssh-files.test.ts +1 -0
- package/src/__tests__/starter-task-flow.test.ts +6 -6
- package/src/__tests__/strip-memory-injections.test.ts +102 -14
- package/src/__tests__/subagent-call-site-routing.test.ts +2 -2
- package/src/__tests__/suggestion-routes.test.ts +3 -3
- package/src/__tests__/sync-message-contract.test.ts +19 -16
- package/src/__tests__/system-prompt.test.ts +54 -0
- package/src/__tests__/terminal-tools.test.ts +3 -24
- package/src/__tests__/thread-backfill.test.ts +4 -9
- package/src/__tests__/title-generate-pipeline.test.ts +1 -1
- package/src/__tests__/token-estimate-pipeline.test.ts +2 -4
- package/src/__tests__/tool-error-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execute-pipeline.test.ts +1 -1
- package/src/__tests__/tool-preview-lifecycle.test.ts +13 -11
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +9 -12
- package/src/__tests__/tool-result-truncation.test.ts +3 -1
- package/src/__tests__/tools-audio-read.test.ts +113 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
- package/src/__tests__/turn-events-store.test.ts +11 -7
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +8 -6
- package/src/__tests__/voice-session-bridge.test.ts +13 -7
- package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
- package/src/acp/prepare-agent-env.ts +52 -11
- package/src/agent/compaction-circuit.ts +140 -0
- package/src/agent/loop.ts +409 -85
- package/src/api/README.md +19 -17
- package/src/api/constants/tool-execution.ts +21 -0
- package/src/api/events/assistant-activity-state.ts +75 -0
- package/src/api/events/assistant-outbound-attachment.ts +25 -27
- package/src/api/events/assistant-text-delta.ts +6 -8
- package/src/api/events/assistant-turn-start.ts +5 -7
- package/src/api/events/avatar-updated.ts +24 -0
- package/src/api/events/compaction-circuit-closed.ts +26 -0
- package/src/api/events/compaction-circuit-open.ts +28 -0
- package/src/api/events/confirmation-request.ts +114 -0
- package/src/api/events/contact-request.ts +33 -0
- package/src/api/events/conversation-error.ts +77 -0
- package/src/api/events/conversation-list-invalidated.ts +38 -0
- package/src/api/events/conversation-title-updated.ts +24 -0
- package/src/api/events/disk-pressure-status-changed.ts +61 -0
- package/src/api/events/document-comment-created.ts +24 -28
- package/src/api/events/document-comment-deleted.ts +6 -8
- package/src/api/events/document-comment-reopened.ts +6 -8
- package/src/api/events/document-comment-resolved.ts +8 -10
- package/src/api/events/document-editor-update.ts +27 -0
- package/src/api/events/error.ts +32 -0
- package/src/api/events/generation-cancelled.ts +4 -6
- package/src/api/events/generation-handoff.ts +13 -15
- package/src/api/events/home-feed-updated.ts +26 -0
- package/src/api/events/identity-changed.ts +32 -0
- package/src/api/events/interaction-resolved.ts +50 -0
- package/src/api/events/message-complete.ts +10 -12
- package/src/api/events/message-dequeued.ts +21 -0
- package/src/api/events/message-queued-deleted.ts +23 -0
- package/src/api/events/message-queued.ts +22 -0
- package/src/api/events/message-request-complete.ts +29 -0
- package/src/api/events/navigate-settings.ts +20 -0
- package/src/api/events/notification-intent.ts +33 -0
- package/src/api/events/open-url.ts +6 -8
- package/src/api/events/question-request.ts +67 -0
- package/src/api/events/relationship-state-updated.ts +4 -6
- package/src/api/events/secret-request.ts +42 -0
- package/src/api/events/subagent-event.ts +79 -0
- package/src/api/events/subagent-spawned.ts +40 -0
- package/src/api/events/subagent-status-changed.ts +65 -0
- package/src/api/events/sync-changed.ts +29 -0
- package/src/api/events/tool-result.ts +129 -0
- package/src/api/events/tool-use-start.ts +8 -10
- package/src/api/events/turn-profile-auto-routed.ts +28 -0
- package/src/api/events/ui-surface-complete.ts +30 -0
- package/src/api/events/ui-surface-dismiss.ts +22 -0
- package/src/api/events/ui-surface-show.ts +67 -0
- package/src/api/events/ui-surface-update.ts +26 -0
- package/src/api/events/usage-update.ts +34 -0
- package/src/api/events/user-message-echo.ts +35 -0
- package/src/api/index.ts +354 -0
- package/src/api/requests/dictation.ts +45 -0
- package/src/api/responses/disk-pressure-status.ts +26 -0
- package/src/api/responses/home.ts +217 -0
- package/src/api/responses/llm-context-response.ts +2 -0
- package/src/api/responses/memory-v3-selection-log.ts +50 -0
- package/src/api/responses/subagent-detail.ts +48 -0
- package/src/approvals/guardian-decision-primitive.ts +7 -15
- package/src/approvals/guardian-request-resolvers.ts +6 -9
- package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
- package/src/avatar/__tests__/avatar-store.test.ts +193 -0
- package/src/avatar/avatar-manifest.ts +195 -0
- package/src/avatar/avatar-store.ts +113 -0
- package/src/avatar/traits-png-sync.ts +8 -2
- package/src/background-wake/next-wake.test.ts +31 -1
- package/src/background-wake/next-wake.ts +4 -1
- package/src/calls/call-conversation-messages.ts +6 -4
- package/src/calls/guardian-action-sweep.ts +6 -4
- package/src/calls/relay-server.ts +12 -8
- package/src/calls/voice-session-bridge.ts +13 -27
- package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
- package/src/cli/commands/avatar.ts +17 -11
- package/src/cli/commands/conversations.ts +15 -1
- package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
- package/src/cli/commands/db/__tests__/status.test.ts +253 -0
- package/src/cli/commands/db/format.ts +48 -0
- package/src/cli/commands/db/index.ts +29 -0
- package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
- package/src/cli/commands/db/repair-step-integrity.ts +146 -0
- package/src/cli/commands/db/repair-steps.ts +164 -0
- package/src/cli/commands/db/repair.ts +141 -0
- package/src/cli/commands/db/status.ts +366 -0
- package/src/cli/commands/memory-v3.ts +159 -445
- package/src/cli/lib/cli-colors.ts +24 -6
- package/src/cli/program.ts +4 -5
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/assistant-feature-flags.ts +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +14 -3
- package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/call-site-defaults.ts +2 -7
- package/src/config/feature-flag-registry.json +25 -9
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -226
- package/src/config/schemas/call-site-catalog.ts +8 -15
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/memory-lifecycle.ts +24 -0
- package/src/config/schemas/memory-v2.ts +0 -253
- package/src/config/schemas/memory-v3.ts +39 -0
- package/src/config/schemas/memory.ts +6 -1
- package/src/config/schemas/timeouts.ts +3 -1
- package/src/context/compactor.ts +54 -31
- package/src/context/token-estimator.ts +19 -0
- package/src/context/tool-result-truncation.ts +1 -43
- package/src/context/window-manager.ts +138 -20
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +2 -2
- package/src/daemon/__tests__/web-search-status-text.test.ts +10 -6
- package/src/daemon/approval-generators.ts +4 -4
- package/src/daemon/config-watcher.ts +7 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +225 -88
- package/src/daemon/conversation-agent-loop.ts +284 -584
- package/src/daemon/conversation-error.ts +7 -7
- package/src/daemon/conversation-history.ts +22 -6
- package/src/daemon/conversation-launch.ts +4 -8
- package/src/daemon/conversation-lifecycle.ts +10 -38
- package/src/daemon/conversation-messaging.ts +1 -3
- package/src/daemon/conversation-notifiers.ts +7 -5
- package/src/daemon/conversation-process.ts +100 -79
- package/src/daemon/conversation-runtime-assembly.ts +47 -21
- package/src/daemon/conversation-store.ts +6 -5
- package/src/daemon/conversation-surfaces.ts +55 -69
- package/src/daemon/conversation-tool-setup.ts +3 -0
- package/src/daemon/conversation.ts +91 -126
- package/src/daemon/daemon-skill-host.ts +2 -6
- package/src/daemon/disk-pressure-guard.ts +35 -29
- package/src/daemon/external-plugins-bootstrap.ts +46 -24
- package/src/daemon/first-greeting.ts +26 -4
- package/src/daemon/guardian-action-generators.ts +2 -2
- package/src/daemon/handlers/conversations.ts +6 -22
- package/src/daemon/handlers/shared.ts +4 -0
- package/src/daemon/handlers/skills.ts +15 -14
- package/src/daemon/host-app-control-proxy.ts +54 -1
- package/src/daemon/host-cu-proxy.ts +46 -22
- package/src/daemon/host-file-proxy.ts +25 -1
- package/src/daemon/host-proxy-preactivation.ts +25 -6
- package/src/daemon/lifecycle.ts +28 -55
- package/src/daemon/message-protocol.ts +2 -3
- package/src/daemon/message-provenance.ts +49 -0
- package/src/daemon/message-types/contacts.ts +3 -20
- package/src/daemon/message-types/conversations.ts +13 -111
- package/src/daemon/message-types/documents.ts +3 -9
- package/src/daemon/message-types/home.ts +4 -17
- package/src/daemon/message-types/integrations.ts +2 -6
- package/src/daemon/message-types/messages.ts +28 -343
- package/src/daemon/message-types/notifications.ts +2 -32
- package/src/daemon/message-types/settings.ts +3 -8
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/sync.ts +12 -25
- package/src/daemon/message-types/workspace.ts +3 -11
- package/src/daemon/process-message.ts +49 -46
- package/src/daemon/server.ts +12 -0
- package/src/daemon/tool-side-effects.ts +10 -7
- package/src/daemon/trust-context.ts +13 -0
- package/src/daemon/wake-target-adapter.ts +11 -1
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -1
- package/src/heartbeat/heartbeat-run-store.ts +31 -0
- package/src/heartbeat/heartbeat-service.ts +16 -0
- package/src/home/feature-gate.ts +22 -0
- package/src/home/feed-types.ts +36 -221
- package/src/ipc/__tests__/email-ipc.test.ts +0 -9
- package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
- package/src/ipc/routes/route-adapter.ts +45 -6
- package/src/ipc/skill-routes/__tests__/memory.test.ts +18 -9
- package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
- package/src/ipc/skill-routes/__tests__/registries.test.ts +28 -18
- package/src/ipc/skill-routes/memory.ts +26 -13
- package/src/ipc/skill-routes/providers.ts +5 -6
- package/src/ipc/skill-routes/registries.ts +13 -61
- package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
- package/src/memory/__tests__/conversation-queries.test.ts +192 -8
- package/src/memory/__tests__/db-maintenance.test.ts +128 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
- package/src/memory/__tests__/memory-retrospective-job.test.ts +10 -6
- package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
- package/src/memory/context-search/agent-runner.ts +2 -4
- package/src/memory/conversation-crud.ts +39 -8
- package/src/memory/conversation-queries.ts +78 -22
- package/src/memory/db-init.ts +8 -0
- package/src/memory/db-maintenance.ts +18 -2
- package/src/memory/graph/consolidation.ts +8 -11
- package/src/memory/graph/conversation-graph-memory.ts +41 -8
- package/src/memory/graph/extraction.ts +6 -9
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.ts +20 -26
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/job-handlers/conversation-starters.ts +32 -32
- package/src/memory/job-handlers/summarization.ts +1 -2
- package/src/memory/jobs-store.ts +3 -1
- package/src/memory/jobs-worker.ts +51 -39
- package/src/memory/llm-request-log-source-clickhouse.ts +5 -31
- package/src/memory/llm-request-log-source-local.ts +0 -11
- package/src/memory/llm-request-log-source.ts +9 -25
- package/src/memory/llm-request-log-store.ts +0 -41
- package/src/memory/llm-usage-store.ts +10 -0
- package/src/memory/memory-marker.ts +17 -0
- package/src/memory/memory-retrospective-job.ts +6 -2
- package/src/memory/memory-v2-activation-log-store.ts +1 -83
- package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
- package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
- package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
- package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
- package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/schema/infrastructure.ts +11 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +124 -0
- package/src/memory/v2/__tests__/migration.test.ts +11 -3
- package/src/memory/v2/__tests__/page-index.test.ts +37 -1
- package/src/memory/v2/__tests__/router.test.ts +14 -4
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -5
- package/src/memory/v2/backfill-jobs.ts +6 -0
- package/src/memory/v2/consolidation-job.ts +89 -9
- package/src/memory/v2/migration.ts +5 -3
- package/src/memory/v2/page-index.ts +11 -0
- package/src/memory/v2/router.ts +8 -11
- package/src/memory/v2/sweep-job.ts +8 -11
- package/src/memory/v2/types.ts +1 -0
- package/src/memory/v3/__tests__/assign.test.ts +242 -0
- package/src/memory/v3/__tests__/capabilities.test.ts +118 -0
- package/src/memory/v3/__tests__/core.test.ts +39 -0
- package/src/memory/v3/__tests__/fixtures/eval-turns.json +36 -0
- package/src/memory/v3/__tests__/fixtures/live-turns.json +37 -0
- package/src/memory/v3/__tests__/health.test.ts +203 -0
- package/src/memory/v3/__tests__/live-integration.test.ts +330 -0
- package/src/memory/v3/__tests__/maintain-job.test.ts +288 -0
- package/src/memory/v3/__tests__/needle.test.ts +107 -0
- package/src/memory/v3/__tests__/orchestrate.test.ts +400 -0
- package/src/memory/v3/__tests__/reconcile.test.ts +274 -0
- package/src/memory/v3/__tests__/render-injection.test.ts +61 -0
- package/src/memory/v3/__tests__/router.test.ts +260 -0
- package/src/memory/v3/__tests__/selection-log-store.test.ts +179 -0
- package/src/memory/v3/__tests__/selector.test.ts +404 -0
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +414 -0
- package/src/memory/v3/__tests__/snapshot.test.ts +168 -0
- package/src/memory/v3/__tests__/tree.test.ts +192 -0
- package/src/memory/v3/__tests__/types.test.ts +54 -0
- package/src/memory/v3/__tests__/working-set-eviction.test.ts +106 -0
- package/src/memory/v3/__tests__/working-set-skeleton.test.ts +44 -0
- package/src/memory/v3/assign.ts +268 -0
- package/src/memory/v3/capabilities.ts +124 -0
- package/src/memory/v3/core.ts +26 -0
- package/src/memory/v3/data/README.md +84 -0
- package/src/memory/v3/data/assignments.json +5 -0
- package/src/memory/v3/data/core.json +1 -0
- package/src/memory/v3/data/leaves/domain-a/topic-x.md +9 -0
- package/src/memory/v3/data/leaves/domain-a/topic-y.md +9 -0
- package/src/memory/v3/data/leaves/domain-b/topic-z.md +9 -0
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +314 -0
- package/src/memory/v3/needle.ts +115 -0
- package/src/memory/v3/orchestrate.ts +114 -0
- package/src/memory/v3/page-content.ts +34 -0
- package/src/memory/v3/provider-blocks.ts +16 -0
- package/src/memory/v3/reconcile.ts +523 -0
- package/src/memory/v3/render-injection.ts +32 -0
- package/src/memory/v3/router.ts +184 -0
- package/src/memory/v3/selection-log-store.ts +84 -0
- package/src/memory/v3/selector.ts +211 -0
- package/src/memory/v3/shadow-plugin.ts +379 -0
- package/src/memory/v3/snapshot.ts +209 -0
- package/src/memory/v3/tree.ts +174 -0
- package/src/memory/v3/types.ts +46 -60
- package/src/memory/v3/working-set.ts +88 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
- package/src/messaging/providers/slack/render-transcript.ts +2 -2
- package/src/messaging/style-analyzer.ts +8 -11
- package/src/notifications/conversation-pairing.ts +8 -6
- package/src/notifications/decision-engine.ts +10 -13
- package/src/notifications/preference-extractor.ts +11 -14
- package/src/permissions/prompter.ts +42 -36
- package/src/permissions/question-prompter.test.ts +35 -26
- package/src/permissions/question-prompter.ts +6 -10
- package/src/plugin-api/index.ts +2 -0
- package/src/plugin-api/types.ts +25 -3
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +93 -0
- package/src/plugins/defaults/circuit-breaker/package.json +15 -0
- package/src/plugins/defaults/circuit-breaker/register.ts +39 -0
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +25 -0
- package/src/plugins/defaults/compaction/package.json +15 -0
- package/src/plugins/defaults/compaction/register.ts +35 -0
- package/src/plugins/defaults/compaction/terminal.ts +73 -0
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +22 -0
- package/src/plugins/defaults/empty-response/package.json +15 -0
- package/src/plugins/defaults/empty-response/register.ts +28 -0
- package/src/plugins/defaults/empty-response/terminal.ts +106 -0
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/history-repair/package.json +15 -0
- package/src/plugins/defaults/history-repair/register.ts +24 -0
- package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
- package/src/plugins/defaults/index.ts +29 -40
- package/src/plugins/defaults/injectors/package.json +15 -0
- package/src/plugins/defaults/{injectors.ts → injectors/register.ts} +14 -38
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +17 -0
- package/src/plugins/defaults/llm-call/package.json +15 -0
- package/src/plugins/defaults/{llm-call.ts → llm-call/register.ts} +6 -38
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +17 -0
- package/src/plugins/defaults/memory-retrieval/package.json +15 -0
- package/src/plugins/defaults/{memory-retrieval.ts → memory-retrieval/register.ts} +10 -48
- package/src/plugins/defaults/{overflow-reduce.ts → overflow-reduce/middlewares/overflowReduce.ts} +18 -77
- package/src/plugins/defaults/overflow-reduce/package.json +15 -0
- package/src/plugins/defaults/overflow-reduce/register.ts +42 -0
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +19 -0
- package/src/plugins/defaults/persistence/package.json +15 -0
- package/src/plugins/defaults/persistence/register.ts +38 -0
- package/src/plugins/defaults/persistence/terminal.ts +83 -0
- package/src/plugins/defaults/title-generate/package.json +15 -0
- package/src/plugins/defaults/title-generate/register.ts +35 -0
- package/src/plugins/defaults/title-generate/terminal.ts +31 -0
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +23 -0
- package/src/plugins/defaults/token-estimate/package.json +15 -0
- package/src/plugins/defaults/token-estimate/register.ts +34 -0
- package/src/plugins/defaults/token-estimate/terminal.ts +40 -0
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +21 -0
- package/src/plugins/defaults/tool-error/package.json +15 -0
- package/src/plugins/defaults/tool-error/register.ts +35 -0
- package/src/plugins/defaults/tool-error/terminal.ts +47 -0
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +23 -0
- package/src/plugins/defaults/tool-execute/package.json +15 -0
- package/src/plugins/defaults/{tool-execute.ts → tool-execute/register.ts} +8 -46
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +23 -0
- package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +35 -0
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +113 -0
- package/src/plugins/defaults/tool-result-truncate/types.ts +22 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/pipeline.ts +0 -12
- package/src/plugins/types.ts +51 -90
- package/src/plugins/user-loader.ts +4 -3
- package/src/proactive-artifact/aux-message-injector.ts +0 -1
- package/src/proactive-artifact/job.test.ts +20 -8
- package/src/proactive-artifact/job.ts +3 -1
- package/src/prompts/sections.ts +20 -7
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
- package/src/prompts/templates/BOOTSTRAP.md +5 -1
- package/src/prompts/templates/system-sections.ts +6 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -25
- package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
- package/src/providers/anthropic/client.ts +24 -5
- package/src/providers/call-site-routing.ts +1 -9
- package/src/providers/gemini/client.ts +152 -34
- package/src/providers/gemini/inline-media.ts +74 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +0 -2
- package/src/providers/openai/chat-completions-provider.ts +1 -4
- package/src/providers/openai/responses-provider.ts +1 -4
- package/src/providers/openrouter/client.ts +1 -6
- package/src/providers/provider-send-message.ts +6 -6
- package/src/providers/ratelimit.ts +1 -9
- package/src/providers/retry.ts +0 -5
- package/src/providers/types.ts +11 -2
- package/src/providers/usage-tracking.ts +1 -9
- package/src/runtime/__tests__/agent-wake.test.ts +131 -26
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -3
- package/src/runtime/agent-wake.ts +93 -18
- package/src/runtime/assistant-event-hub.ts +2 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
- package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
- package/src/runtime/auth/route-policy.ts +42 -1079
- package/src/runtime/background-job-runner.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -1
- package/src/runtime/channel-approvals.ts +3 -14
- package/src/runtime/channel-invite-transport.ts +5 -6
- package/src/runtime/channel-readiness-service.ts +2 -5
- package/src/runtime/channel-retry-sweep.ts +12 -16
- package/src/runtime/conversation-stream-state.ts +294 -0
- package/src/runtime/http-router.ts +19 -22
- package/src/runtime/http-types.ts +12 -6
- package/src/runtime/invite-instruction-generator.ts +3 -3
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +62 -32
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -22
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +7 -2
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
- package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
- package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
- package/src/runtime/routes/__tests__/tts-routes.test.ts +3 -3
- package/src/runtime/routes/acp-routes.test.ts +97 -75
- package/src/runtime/routes/acp-routes.ts +29 -6
- package/src/runtime/routes/app-management-routes.ts +97 -24
- package/src/runtime/routes/app-routes.ts +25 -5
- package/src/runtime/routes/approval-routes.ts +16 -4
- package/src/runtime/routes/attachment-routes.ts +25 -1
- package/src/runtime/routes/audio-routes.ts +1 -0
- package/src/runtime/routes/audit-routes.ts +5 -0
- package/src/runtime/routes/auth-routes.ts +5 -0
- package/src/runtime/routes/avatar-routes.ts +238 -59
- package/src/runtime/routes/background-tool-routes.ts +9 -0
- package/src/runtime/routes/background-wake-routes.ts +13 -3
- package/src/runtime/routes/backup-routes.ts +45 -0
- package/src/runtime/routes/bookmark-routes.ts +13 -0
- package/src/runtime/routes/brain-graph-routes.ts +9 -0
- package/src/runtime/routes/browser-routes.ts +5 -0
- package/src/runtime/routes/browser-tabs-routes.ts +5 -0
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/cache-routes.ts +13 -0
- package/src/runtime/routes/call-routes.ts +21 -10
- package/src/runtime/routes/channel-availability-routes.ts +5 -1
- package/src/runtime/routes/channel-readiness-routes.ts +37 -4
- package/src/runtime/routes/channel-route-definitions.ts +21 -0
- package/src/runtime/routes/channel-verification-routes.ts +21 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
- package/src/runtime/routes/client-routes.ts +9 -0
- package/src/runtime/routes/consolidation-routes.ts +13 -5
- package/src/runtime/routes/contact-prompt-routes.ts +9 -0
- package/src/runtime/routes/contact-routes.ts +90 -23
- package/src/runtime/routes/content-source-routes.ts +5 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +5 -1
- package/src/runtime/routes/conversation-attention-routes.ts +5 -0
- package/src/runtime/routes/conversation-cli-routes.ts +54 -7
- package/src/runtime/routes/conversation-compaction-routes.ts +54 -25
- package/src/runtime/routes/conversation-list-routes.ts +81 -12
- package/src/runtime/routes/conversation-management-routes.ts +57 -14
- package/src/runtime/routes/conversation-query-routes.ts +88 -41
- package/src/runtime/routes/conversation-routes.ts +74 -19
- package/src/runtime/routes/conversation-starter-routes.ts +22 -13
- package/src/runtime/routes/conversations-import-routes.ts +6 -1
- package/src/runtime/routes/credential-prompt-routes.ts +5 -0
- package/src/runtime/routes/credential-routes.ts +25 -6
- package/src/runtime/routes/debug-bash-routes.ts +5 -0
- package/src/runtime/routes/debug-routes.ts +11 -2
- package/src/runtime/routes/defer-routes.ts +13 -0
- package/src/runtime/routes/diagnostics-routes.ts +37 -46
- package/src/runtime/routes/disk-pressure-routes.ts +17 -31
- package/src/runtime/routes/document-comments-routes.ts +46 -27
- package/src/runtime/routes/documents-routes.ts +21 -10
- package/src/runtime/routes/domain-routes.ts +61 -28
- package/src/runtime/routes/email-routes.ts +33 -0
- package/src/runtime/routes/events-routes.ts +114 -9
- package/src/runtime/routes/filing-routes.ts +9 -4
- package/src/runtime/routes/gateway-log-routes.ts +5 -0
- package/src/runtime/routes/global-search-routes.ts +53 -50
- package/src/runtime/routes/group-routes.ts +21 -5
- package/src/runtime/routes/guardian-action-routes.ts +9 -0
- package/src/runtime/routes/guardian-approval-interception.ts +0 -31
- package/src/runtime/routes/heartbeat-routes.ts +25 -9
- package/src/runtime/routes/home-feed-routes.ts +23 -19
- package/src/runtime/routes/home-state-routes.ts +8 -40
- package/src/runtime/routes/host-app-control-routes.ts +5 -0
- package/src/runtime/routes/host-bash-routes.ts +5 -0
- package/src/runtime/routes/host-browser-routes.ts +13 -0
- package/src/runtime/routes/host-cu-routes.ts +5 -0
- package/src/runtime/routes/host-file-routes.ts +26 -6
- package/src/runtime/routes/host-transfer-routes.ts +13 -2
- package/src/runtime/routes/http-adapter.ts +1 -2
- package/src/runtime/routes/identity-intro-cache.ts +17 -6
- package/src/runtime/routes/identity-routes.ts +12 -2
- package/src/runtime/routes/image-generation-routes.ts +5 -0
- package/src/runtime/routes/inbound-message-handler.ts +15 -11
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +0 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +15 -19
- package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
- package/src/runtime/routes/inference-provider-connection-routes.ts +21 -5
- package/src/runtime/routes/inference-send-routes.ts +11 -11
- package/src/runtime/routes/integrations/a2a.ts +30 -7
- package/src/runtime/routes/integrations/slack/channel.ts +19 -3
- package/src/runtime/routes/integrations/slack/share.ts +9 -2
- package/src/runtime/routes/integrations/telegram.ts +28 -9
- package/src/runtime/routes/integrations/twilio.ts +35 -7
- package/src/runtime/routes/integrations/vercel.ts +3 -3
- package/src/runtime/routes/internal-oauth-routes.ts +5 -0
- package/src/runtime/routes/internal-twilio-routes.ts +13 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
- package/src/runtime/routes/log-export-routes.ts +28 -10
- package/src/runtime/routes/mcp-auth-routes.ts +25 -0
- package/src/runtime/routes/memory-item-routes.ts +21 -10
- package/src/runtime/routes/memory-v2-routes.ts +90 -36
- package/src/runtime/routes/memory-v3-routes.ts +273 -407
- package/src/runtime/routes/migration-rollback-routes.ts +5 -1
- package/src/runtime/routes/migration-routes.ts +29 -0
- package/src/runtime/routes/notification-routes.ts +17 -1
- package/src/runtime/routes/oauth-apps.ts +33 -11
- package/src/runtime/routes/oauth-commands-routes.ts +37 -14
- package/src/runtime/routes/oauth-connect-routes.ts +9 -0
- package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
- package/src/runtime/routes/oauth-providers.ts +35 -10
- package/src/runtime/routes/platform-routes.ts +21 -0
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +3 -2
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
- package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +0 -1
- package/src/runtime/routes/playground/inject-failures.ts +13 -8
- package/src/runtime/routes/playground/reset-circuit.ts +14 -9
- package/src/runtime/routes/playground/seed-conversation.ts +1 -1
- package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
- package/src/runtime/routes/playground/state.ts +4 -3
- package/src/runtime/routes/plugins-routes.ts +22 -19
- package/src/runtime/routes/profiler-routes.ts +17 -4
- package/src/runtime/routes/ps-routes.ts +5 -0
- package/src/runtime/routes/publish-routes.ts +13 -3
- package/src/runtime/routes/question-routes.ts +5 -0
- package/src/runtime/routes/recording-routes.ts +25 -12
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/sanity-routes.ts +9 -2
- package/src/runtime/routes/schedule-routes.ts +137 -47
- package/src/runtime/routes/secret-routes.ts +17 -4
- package/src/runtime/routes/sequence-routes.ts +33 -0
- package/src/runtime/routes/settings-routes.ts +65 -19
- package/src/runtime/routes/skills-routes.ts +133 -69
- package/src/runtime/routes/slack-channel-routes.ts +5 -0
- package/src/runtime/routes/stt-routes.ts +13 -6
- package/src/runtime/routes/subagents-routes.ts +24 -18
- package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
- package/src/runtime/routes/surface-action-routes.ts +9 -0
- package/src/runtime/routes/surface-content-routes.ts +10 -2
- package/src/runtime/routes/task-routes.ts +37 -0
- package/src/runtime/routes/telemetry-routes.ts +9 -0
- package/src/runtime/routes/trace-event-routes.ts +42 -1
- package/src/runtime/routes/trust-rules-routes.ts +5 -0
- package/src/runtime/routes/tts-routes.ts +13 -6
- package/src/runtime/routes/types.ts +17 -8
- package/src/runtime/routes/ui-request-routes.ts +5 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
- package/src/runtime/routes/usage-routes.ts +71 -3
- package/src/runtime/routes/user-routes-cli.ts +9 -0
- package/src/runtime/routes/user-routes.ts +5 -1
- package/src/runtime/routes/wake-conversation-routes.ts +5 -0
- package/src/runtime/routes/watcher-routes.ts +21 -0
- package/src/runtime/routes/webhook-routes.ts +9 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +5 -0
- package/src/runtime/routes/work-items-routes.ts +47 -19
- package/src/runtime/routes/workspace-commit-routes.ts +5 -0
- package/src/runtime/routes/workspace-routes.test.ts +42 -0
- package/src/runtime/routes/workspace-routes.ts +120 -9
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -4
- package/src/runtime/services/analyze-conversation.ts +3 -6
- package/src/runtime/services/conversation-serializer.ts +24 -2
- package/src/runtime/sync/resource-sync-events.ts +16 -2
- package/src/runtime/sync/sync-publisher.ts +2 -2
- package/src/schedule/run-script.ts +28 -3
- package/src/schedule/schedule-store.ts +8 -0
- package/src/schedule/scheduler.ts +3 -1
- package/src/signals/user-message.ts +5 -8
- package/src/skills/catalog-files.ts +4 -1
- package/src/skills/clawhub-files.ts +2 -0
- package/src/skills/skillssh-files.ts +2 -0
- package/src/subagent/manager.ts +3 -6
- package/src/telemetry/types.ts +26 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +138 -1
- package/src/telemetry/usage-telemetry-reporter.ts +31 -0
- package/src/tools/acp/spawn.test.ts +88 -38
- package/src/tools/apps/definitions.ts +8 -4
- package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
- package/src/tools/ask-question/ask-question-tool.ts +85 -90
- package/src/tools/computer-use/definitions.ts +28 -24
- package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
- package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
- package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
- package/src/tools/credentials/vault.ts +112 -111
- package/src/tools/execution-target.ts +1 -1
- package/src/tools/execution-timeout.ts +3 -4
- package/src/tools/filesystem/edit.ts +45 -42
- package/src/tools/filesystem/list.ts +33 -30
- package/src/tools/filesystem/read.ts +54 -35
- package/src/tools/filesystem/write.ts +34 -31
- package/src/tools/host-filesystem/edit.ts +44 -42
- package/src/tools/host-filesystem/read.ts +49 -35
- package/src/tools/host-filesystem/transfer.ts +121 -108
- package/src/tools/host-filesystem/write.ts +33 -31
- package/src/tools/host-terminal/host-shell.ts +50 -48
- package/src/tools/memory/register.ts +23 -24
- package/src/tools/network/web-fetch.ts +49 -46
- package/src/tools/network/web-search.ts +16 -13
- package/src/tools/registry.ts +39 -16
- package/src/tools/schedule/create.ts +11 -0
- package/src/tools/schedule/update.ts +16 -0
- package/src/tools/shared/filesystem/audio-read.ts +122 -0
- package/src/tools/shared/filesystem/image-read.ts +1 -1
- package/src/tools/skills/execute.ts +34 -31
- package/src/tools/skills/load.ts +29 -23
- package/src/tools/subagent/notify-parent.ts +35 -32
- package/src/tools/system/avatar-generator.ts +13 -22
- package/src/tools/system/request-permission.ts +30 -27
- package/src/tools/terminal/shell.ts +190 -61
- package/src/tools/tool-defaults.ts +20 -9
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +74 -23
- package/src/tools/ui-surface/definitions.ts +69 -9
- package/src/usage/types.ts +10 -0
- package/src/util/errors.ts +2 -2
- package/src/util/map-limit.ts +27 -0
- package/src/util/platform.ts +15 -12
- package/src/work-items/work-item-runner.ts +7 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
- package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
- package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
- package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
- package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
- package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
- package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/provider-commit-message-generator.ts +15 -17
- package/tsconfig.json +4 -1
- package/src/__tests__/history-repair-pipeline.test.ts +0 -396
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
- package/src/cli/commands/memory-v3-render.ts +0 -491
- package/src/daemon/message-types/disk-pressure.ts +0 -9
- package/src/email/feature-gate.ts +0 -23
- package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
- package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -466
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +0 -270
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
- package/src/memory/v3/__tests__/edges.test.ts +0 -706
- package/src/memory/v3/__tests__/filter.test.ts +0 -560
- package/src/memory/v3/__tests__/gate.test.ts +0 -637
- package/src/memory/v3/__tests__/index-composition.test.ts +0 -291
- package/src/memory/v3/__tests__/loop.test.ts +0 -775
- package/src/memory/v3/__tests__/retriever.test.ts +0 -226
- package/src/memory/v3/__tests__/scouts.test.ts +0 -489
- package/src/memory/v3/__tests__/shadow-diff.test.ts +0 -225
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -398
- package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
- package/src/memory/v3/__tests__/traversal.test.ts +0 -508
- package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
- package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
- package/src/memory/v3/__tests__/tree-walk.test.ts +0 -784
- package/src/memory/v3/__tests__/validate.test.ts +0 -277
- package/src/memory/v3/auto-edges.ts +0 -223
- package/src/memory/v3/coactivation-store.ts +0 -124
- package/src/memory/v3/consolidation-job.ts +0 -323
- package/src/memory/v3/coretrieval-seed.ts +0 -240
- package/src/memory/v3/edge-learning-job.ts +0 -160
- package/src/memory/v3/edges.ts +0 -286
- package/src/memory/v3/filter.ts +0 -286
- package/src/memory/v3/gate.ts +0 -349
- package/src/memory/v3/index-composition.ts +0 -126
- package/src/memory/v3/llm-capture.ts +0 -46
- package/src/memory/v3/loop.ts +0 -430
- package/src/memory/v3/maintenance.ts +0 -144
- package/src/memory/v3/prompt-context.ts +0 -33
- package/src/memory/v3/prompts/consolidation.ts +0 -458
- package/src/memory/v3/prompts/system-prompts.ts +0 -196
- package/src/memory/v3/retriever.ts +0 -33
- package/src/memory/v3/scouts.ts +0 -431
- package/src/memory/v3/shadow-diff.ts +0 -287
- package/src/memory/v3/shadow-middleware.ts +0 -347
- package/src/memory/v3/traversal.ts +0 -211
- package/src/memory/v3/tree-index.ts +0 -237
- package/src/memory/v3/tree-store.ts +0 -394
- package/src/memory/v3/tree-walk.ts +0 -356
- package/src/memory/v3/validate.ts +0 -323
- package/src/plugins/defaults/circuit-breaker.ts +0 -141
- package/src/plugins/defaults/compaction.ts +0 -141
- package/src/plugins/defaults/empty-response.ts +0 -124
- package/src/plugins/defaults/history-repair.ts +0 -83
- package/src/plugins/defaults/persistence.ts +0 -146
- package/src/plugins/defaults/title-generate.ts +0 -90
- package/src/plugins/defaults/token-estimate.ts +0 -101
- package/src/plugins/defaults/tool-error.ts +0 -119
- package/src/plugins/defaults/tool-result-truncate.ts +0 -84
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +0 -35
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory v3 — flag-gated shadow/live plugin.
|
|
3
|
+
*
|
|
4
|
+
* Registered as an {@link Injector} that runs the v3 orchestrator each turn and
|
|
5
|
+
* records its selection set to `memory_v3_selections`. Two flags gate its
|
|
6
|
+
* injection behavior:
|
|
7
|
+
*
|
|
8
|
+
* - `memory-v3-shadow` (and live OFF): observation-only. `produce()` returns
|
|
9
|
+
* `null`, so v2 injection is bit-for-bit identical whether the flag is on
|
|
10
|
+
* or off — the only difference is the side-effect telemetry write.
|
|
11
|
+
* - `memory-v3-live`: live injection. `produce()` additionally renders the
|
|
12
|
+
* working-set selection into a `<memory>` block via {@link renderMemoryBlock}
|
|
13
|
+
* and returns it as an injection block at v2's dynamic-memory placement
|
|
14
|
+
* (`after-memory-prefix`). Selections are still logged.
|
|
15
|
+
* - both OFF: `produce()` returns `null` and skips orchestration entirely.
|
|
16
|
+
*
|
|
17
|
+
* On each turn (either flag on):
|
|
18
|
+
* 1. Lazy-init the v3 lanes ONCE across the whole process (leaf tree, core
|
|
19
|
+
* set, BM25 needle, carry-forward working set), memoizing the init
|
|
20
|
+
* promise so concurrent first turns share a single build.
|
|
21
|
+
* 2. Build a {@link TurnContext} from the conversation's recent messages.
|
|
22
|
+
* 3. Run {@link orchestrate} and record its selection set to
|
|
23
|
+
* `memory_v3_selections` with a best-effort lane attribution.
|
|
24
|
+
*
|
|
25
|
+
* Everything after the flag read is wrapped in try/catch — any failure is
|
|
26
|
+
* logged and swallowed so it can never affect the live turn. In live mode a
|
|
27
|
+
* failure returns `null` (no v3 injection); v2 suppression keys off BOTH the
|
|
28
|
+
* flag AND whether `produce()` actually returned a block, so a v3 failure (or
|
|
29
|
+
* empty selection) falls back to v2 memory rather than dropping all memory.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
33
|
+
import { getConfig } from "../../config/loader.js";
|
|
34
|
+
import type { AssistantConfig } from "../../config/schema.js";
|
|
35
|
+
import { getMessages } from "../../memory/conversation-crud.js";
|
|
36
|
+
import { getDb, getSqliteFrom } from "../../memory/db-connection.js";
|
|
37
|
+
import { stringifyMessageContent } from "../../memory/message-content.js";
|
|
38
|
+
import {
|
|
39
|
+
type InjectionBlock,
|
|
40
|
+
type Injector,
|
|
41
|
+
type Plugin,
|
|
42
|
+
type TurnContext as PluginTurnContext,
|
|
43
|
+
} from "../../plugins/types.js";
|
|
44
|
+
import { getLogger } from "../../util/logger.js";
|
|
45
|
+
import { getWorkspaceDir } from "../../util/platform.js";
|
|
46
|
+
import { getPageIndex } from "../v2/page-index.js";
|
|
47
|
+
import { injectCapabilitiesLeaf, isCapabilitySlug } from "./capabilities.js";
|
|
48
|
+
import { loadCore } from "./core.js";
|
|
49
|
+
import type { NeedleIndex } from "./needle.js";
|
|
50
|
+
import { buildNeedleIndex } from "./needle.js";
|
|
51
|
+
import type { OrchestrateResult } from "./orchestrate.js";
|
|
52
|
+
import { orchestrate } from "./orchestrate.js";
|
|
53
|
+
import { renderV3PageContent } from "./page-content.js";
|
|
54
|
+
import { renderMemoryBlock } from "./render-injection.js";
|
|
55
|
+
import { coreSlugs, loadLeafTree, resolveDataDir } from "./tree.js";
|
|
56
|
+
import {
|
|
57
|
+
type LeafPath,
|
|
58
|
+
type LeafTree,
|
|
59
|
+
MEMORY_V3_BLOCK_ID,
|
|
60
|
+
type SelectionSource,
|
|
61
|
+
type Slug,
|
|
62
|
+
type TurnContext,
|
|
63
|
+
} from "./types.js";
|
|
64
|
+
import { WorkingSet } from "./working-set.js";
|
|
65
|
+
|
|
66
|
+
const MEMORY_V3_SHADOW = "memory-v3-shadow" as const;
|
|
67
|
+
const MEMORY_V3_LIVE = "memory-v3-live" as const;
|
|
68
|
+
|
|
69
|
+
const log = getLogger("memory-v3-shadow");
|
|
70
|
+
|
|
71
|
+
/** How many recent messages to fold into the shadow `recentContext` string. */
|
|
72
|
+
const RECENT_CONTEXT_MESSAGES = 6;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The lazily-built, process-lifetime v3 lanes. The working set is stateful
|
|
76
|
+
* (carry-forward across turns), so it is intentionally shared across every
|
|
77
|
+
* shadow turn rather than rebuilt per turn — that is the whole point of the
|
|
78
|
+
* carry-forward lane.
|
|
79
|
+
*/
|
|
80
|
+
export interface ShadowLanes {
|
|
81
|
+
tree: LeafTree;
|
|
82
|
+
core: Set<LeafPath>;
|
|
83
|
+
needle: NeedleIndex;
|
|
84
|
+
workingSet: WorkingSet;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Memoized init promise. Caching the PROMISE (not the resolved value) means
|
|
89
|
+
* concurrent first turns all await the same build instead of racing several
|
|
90
|
+
* `loadLeafTree`/`buildNeedleIndex` passes.
|
|
91
|
+
*/
|
|
92
|
+
let lanesPromise: Promise<ShadowLanes> | null = null;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Drop the memoized lanes so the NEXT `getLanes` rebuilds them from scratch
|
|
96
|
+
* (fresh topic-tree load + fresh needle index). The rebuild is lazy — this only
|
|
97
|
+
* clears the cache, so the cost is paid by the next caller, and concurrent
|
|
98
|
+
* first-callers after the invalidation still share a single build via the
|
|
99
|
+
* re-memoized promise. Call this whenever the underlying tree or needle sources
|
|
100
|
+
* change on disk.
|
|
101
|
+
*/
|
|
102
|
+
export function invalidateLanes(): void {
|
|
103
|
+
lanesPromise = null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Test-only alias for {@link invalidateLanes}. */
|
|
107
|
+
export function resetShadowLanesForTests(): void {
|
|
108
|
+
invalidateLanes();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Pull a page's summary from the existing v2 page index. Missing summaries
|
|
113
|
+
* (and any read failure) degrade to "" — the needle and L2 selector treat an
|
|
114
|
+
* empty summary as "no signal" rather than throwing.
|
|
115
|
+
*/
|
|
116
|
+
async function pageSummary(slug: Slug): Promise<string> {
|
|
117
|
+
try {
|
|
118
|
+
const index = await getPageIndex(getWorkspaceDir());
|
|
119
|
+
return index.bySlug.get(slug)?.summary ?? "";
|
|
120
|
+
} catch {
|
|
121
|
+
return "";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function initLanes(config: AssistantConfig): Promise<ShadowLanes> {
|
|
126
|
+
const dataDir = resolveDataDir();
|
|
127
|
+
const pageIndex = await getPageIndex(getWorkspaceDir());
|
|
128
|
+
const pageLeaves = new Map<Slug, LeafPath[]>();
|
|
129
|
+
for (const entry of pageIndex.entries) {
|
|
130
|
+
pageLeaves.set(entry.slug, entry.leaves);
|
|
131
|
+
}
|
|
132
|
+
const tree = await loadLeafTree(dataDir, pageLeaves);
|
|
133
|
+
const core = await loadCore(dataDir);
|
|
134
|
+
|
|
135
|
+
// Always-on synthetic capabilities leaf: skill + CLI-command rows the page
|
|
136
|
+
// index already carries (with summaries). Injecting them as leaf members puts
|
|
137
|
+
// them in the needle corpus (`tree.byPage`) and, via `core`, makes L1 always
|
|
138
|
+
// open the leaf so L2 selects the relevant subset each turn — matching v2's
|
|
139
|
+
// "always in the pool, router-selected" capability surfacing. Done before
|
|
140
|
+
// `buildNeedleIndex` so the synthetic members are indexed.
|
|
141
|
+
const capabilitySlugs = pageIndex.entries
|
|
142
|
+
.map((entry) => entry.slug)
|
|
143
|
+
.filter(isCapabilitySlug);
|
|
144
|
+
injectCapabilitiesLeaf(tree, core, capabilitySlugs);
|
|
145
|
+
|
|
146
|
+
const needle = await buildNeedleIndex(tree, pageSummary);
|
|
147
|
+
const workingSet = new WorkingSet(
|
|
148
|
+
config.memory.v3.workingSet.maxPages,
|
|
149
|
+
config.memory.v3.workingSet.evictWindow,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return { tree, core, needle, workingSet };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Lazy, memoized accessor for the shadow lanes. */
|
|
156
|
+
function getLanes(config: AssistantConfig): Promise<ShadowLanes> {
|
|
157
|
+
if (!lanesPromise) {
|
|
158
|
+
lanesPromise = initLanes(config).catch((err) => {
|
|
159
|
+
// Reset on failure so a transient init error doesn't permanently wedge
|
|
160
|
+
// the shadow lane — the next turn retries.
|
|
161
|
+
lanesPromise = null;
|
|
162
|
+
throw err;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return lanesPromise;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Build a v3 {@link TurnContext} from the conversation's persisted messages.
|
|
170
|
+
* `currentMessage` is the latest user message; `recentContext` is the tail of
|
|
171
|
+
* the recent transcript. Returns `null` when there is no user message to route
|
|
172
|
+
* on (nothing to shadow this turn).
|
|
173
|
+
*/
|
|
174
|
+
function buildShadowTurn(
|
|
175
|
+
conversationId: string,
|
|
176
|
+
turnIndex: number,
|
|
177
|
+
): TurnContext | null {
|
|
178
|
+
const rows = getMessages(conversationId);
|
|
179
|
+
if (rows.length === 0) return null;
|
|
180
|
+
|
|
181
|
+
let currentMessage = "";
|
|
182
|
+
for (let i = rows.length - 1; i >= 0; i--) {
|
|
183
|
+
if (rows[i]!.role === "user") {
|
|
184
|
+
currentMessage = stringifyMessageContent(rows[i]!.content);
|
|
185
|
+
if (currentMessage.length > 0) break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (currentMessage.length === 0) return null;
|
|
189
|
+
|
|
190
|
+
const recentContext = rows
|
|
191
|
+
.slice(-RECENT_CONTEXT_MESSAGES)
|
|
192
|
+
.map((r) => stringifyMessageContent(r.content))
|
|
193
|
+
.filter((t) => t.length > 0)
|
|
194
|
+
.join("\n");
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
conversationId,
|
|
198
|
+
turnNumber: turnIndex,
|
|
199
|
+
currentMessage,
|
|
200
|
+
recentContext,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
interface SelectionRow {
|
|
205
|
+
slug: Slug;
|
|
206
|
+
source: SelectionSource;
|
|
207
|
+
pinned: number;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Map an orchestrate result onto telemetry rows with best-effort lane
|
|
212
|
+
* attribution.
|
|
213
|
+
*
|
|
214
|
+
* - A current selection whose page belongs to a core leaf → `"core+l2"`,
|
|
215
|
+
* otherwise `"l1+l2"`.
|
|
216
|
+
* - A slug in `finalInjection` but NOT re-selected this turn → `"carry-forward"`.
|
|
217
|
+
*
|
|
218
|
+
* Precise needle attribution is a documented follow-up: the needle lane only
|
|
219
|
+
* widens the open set, so a needle-surfaced page that survives L2 selection is
|
|
220
|
+
* indistinguishable here from an L1-routed one. This coarse mapping is
|
|
221
|
+
* acceptable for v0 shadow telemetry.
|
|
222
|
+
*/
|
|
223
|
+
function attributeSelections(
|
|
224
|
+
tree: LeafTree,
|
|
225
|
+
core: Set<LeafPath>,
|
|
226
|
+
result: OrchestrateResult,
|
|
227
|
+
): SelectionRow[] {
|
|
228
|
+
const coreOwnedSlugs = coreSlugs(tree, core);
|
|
229
|
+
|
|
230
|
+
const rows: SelectionRow[] = [];
|
|
231
|
+
const seen = new Set<Slug>();
|
|
232
|
+
for (const sel of result.currentSelections) {
|
|
233
|
+
seen.add(sel.slug);
|
|
234
|
+
rows.push({
|
|
235
|
+
slug: sel.slug,
|
|
236
|
+
source: coreOwnedSlugs.has(sel.slug) ? "core+l2" : "l1+l2",
|
|
237
|
+
pinned: sel.pinned ? 1 : 0,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
for (const slug of result.finalInjection) {
|
|
241
|
+
if (seen.has(slug)) continue;
|
|
242
|
+
seen.add(slug);
|
|
243
|
+
rows.push({ slug, source: "carry-forward", pinned: 0 });
|
|
244
|
+
}
|
|
245
|
+
return rows;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Write the attributed selection rows to `memory_v3_selections`. */
|
|
249
|
+
function writeSelections(
|
|
250
|
+
conversationId: string,
|
|
251
|
+
turn: number,
|
|
252
|
+
rows: SelectionRow[],
|
|
253
|
+
): void {
|
|
254
|
+
if (rows.length === 0) return;
|
|
255
|
+
const raw = getSqliteFrom(getDb());
|
|
256
|
+
// PK is (conversation_id, turn, slug); OR REPLACE keeps the write
|
|
257
|
+
// idempotent if the same turn is observed twice (e.g. a retried turn).
|
|
258
|
+
const stmt = raw.query(/*sql*/ `
|
|
259
|
+
INSERT OR REPLACE INTO memory_v3_selections (
|
|
260
|
+
conversation_id, turn, slug, source, pinned, created_at
|
|
261
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
262
|
+
`);
|
|
263
|
+
const now = Date.now();
|
|
264
|
+
for (const row of rows) {
|
|
265
|
+
stmt.run(conversationId, turn, row.slug, row.source, row.pinned, now);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Run v3 orchestration for one turn and log the selection set, returning the
|
|
271
|
+
* orchestrate result so a live caller can render it. Never throws — all
|
|
272
|
+
* failures are logged and swallowed (returning `null`) so the live turn is
|
|
273
|
+
* unaffected. Returns `null` when there is no user message to route on.
|
|
274
|
+
*/
|
|
275
|
+
async function observeTurn(
|
|
276
|
+
conversationId: string,
|
|
277
|
+
turnIndex: number,
|
|
278
|
+
): Promise<OrchestrateResult | null> {
|
|
279
|
+
try {
|
|
280
|
+
const turn = buildShadowTurn(conversationId, turnIndex);
|
|
281
|
+
if (!turn) return null;
|
|
282
|
+
|
|
283
|
+
const lanes = await getLanes(getConfig());
|
|
284
|
+
const result = await orchestrate(turn, {
|
|
285
|
+
tree: lanes.tree,
|
|
286
|
+
core: lanes.core,
|
|
287
|
+
needle: lanes.needle,
|
|
288
|
+
workingSet: lanes.workingSet,
|
|
289
|
+
pageSummary,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const rows = attributeSelections(lanes.tree, lanes.core, result);
|
|
293
|
+
writeSelections(conversationId, turnIndex, rows);
|
|
294
|
+
return result;
|
|
295
|
+
} catch (err) {
|
|
296
|
+
log.warn(
|
|
297
|
+
{ err: err instanceof Error ? err.message : String(err), conversationId },
|
|
298
|
+
"memory-v3 orchestration failed (non-fatal)",
|
|
299
|
+
);
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Test-facing shadow wrapper: gate on the shadow flag, then run v3
|
|
306
|
+
* orchestration for one turn and log the selection set. Production injection
|
|
307
|
+
* goes through `produce()` → {@link observeTurn} directly (which checks both
|
|
308
|
+
* flags); this wrapper exists so tests can drive shadow observation in
|
|
309
|
+
* isolation. Never throws — all failures are logged and swallowed so the live
|
|
310
|
+
* turn is unaffected.
|
|
311
|
+
*/
|
|
312
|
+
export async function runShadowObservation(
|
|
313
|
+
conversationId: string,
|
|
314
|
+
turnIndex: number,
|
|
315
|
+
): Promise<void> {
|
|
316
|
+
if (!isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, getConfig())) return;
|
|
317
|
+
await observeTurn(conversationId, turnIndex);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* The v3 injector. Reads both flags:
|
|
322
|
+
* - `memory-v3-live` on → orchestrate, log, render the working-set selection
|
|
323
|
+
* into a `<memory>` block, and return it at v2's dynamic-memory placement.
|
|
324
|
+
* - `memory-v3-shadow` on (live off) → orchestrate + log only, return `null`.
|
|
325
|
+
* - both off → return `null` (no orchestration).
|
|
326
|
+
*
|
|
327
|
+
* Empty selection and any failure return `null` (no v3 injection). v2
|
|
328
|
+
* suppression keys off BOTH the flag AND this return value, so a `null` here
|
|
329
|
+
* (failure or empty selection) falls back to v2 memory rather than dropping all
|
|
330
|
+
* memory.
|
|
331
|
+
*/
|
|
332
|
+
const memoryV3Injector: Injector = {
|
|
333
|
+
name: "memory-v3-shadow",
|
|
334
|
+
// High order so it sorts last; the live `<memory>` block uses the
|
|
335
|
+
// after-memory-prefix placement so it lands at the memory boundary regardless
|
|
336
|
+
// of this sort key, which only orders content-producing injectors.
|
|
337
|
+
order: 1000,
|
|
338
|
+
async produce(ctx: PluginTurnContext): Promise<InjectionBlock | null> {
|
|
339
|
+
const config = getConfig();
|
|
340
|
+
const live = isAssistantFeatureFlagEnabled(MEMORY_V3_LIVE, config);
|
|
341
|
+
const shadow = isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config);
|
|
342
|
+
if (!live && !shadow) return null;
|
|
343
|
+
|
|
344
|
+
const result = await observeTurn(ctx.conversationId, ctx.turnIndex);
|
|
345
|
+
if (!live || !result) return null;
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
// `renderMemoryBlock` returns "" for an empty selection; inject nothing.
|
|
349
|
+
const text = await renderMemoryBlock(
|
|
350
|
+
result.finalInjection,
|
|
351
|
+
renderV3PageContent,
|
|
352
|
+
);
|
|
353
|
+
if (text.length === 0) return null;
|
|
354
|
+
return {
|
|
355
|
+
id: MEMORY_V3_BLOCK_ID,
|
|
356
|
+
text,
|
|
357
|
+
// Mirror v2's dynamic `<memory>` block placement.
|
|
358
|
+
placement: "after-memory-prefix",
|
|
359
|
+
};
|
|
360
|
+
} catch (err) {
|
|
361
|
+
log.warn(
|
|
362
|
+
{
|
|
363
|
+
err: err instanceof Error ? err.message : String(err),
|
|
364
|
+
conversationId: ctx.conversationId,
|
|
365
|
+
},
|
|
366
|
+
"memory-v3 live render failed (non-fatal) — falling back to v2",
|
|
367
|
+
);
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
export const memoryV3ShadowPlugin: Plugin = {
|
|
374
|
+
manifest: {
|
|
375
|
+
name: "memory-v3-shadow",
|
|
376
|
+
version: "0.0.1",
|
|
377
|
+
},
|
|
378
|
+
injectors: [memoryV3Injector],
|
|
379
|
+
};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Snapshot / restore for the memory-v3 data directory.
|
|
6
|
+
*
|
|
7
|
+
* The v3 data dir (`<workspace>/memory/v3/data/`) holds the tree's durable
|
|
8
|
+
* state: `leaves/**\/*.md`, `assignments.json`, and `core.json` (see
|
|
9
|
+
* `resolveDataDir` in `tree.ts`). A future tree-gardening reconciler mutates
|
|
10
|
+
* these in place; this module lets it take a snapshot first and roll back if a
|
|
11
|
+
* reconcile pass produces a bad tree.
|
|
12
|
+
*
|
|
13
|
+
* Page `leaves:` frontmatter lives outside the data dir (in the v2 page store
|
|
14
|
+
* at `<workspace>/memory/concepts/`). The reconciler can capture the prior
|
|
15
|
+
* page->leaves mapping as `pageRefs` and hand it to {@link snapshotDataDir};
|
|
16
|
+
* it is persisted alongside the data snapshot and returned by
|
|
17
|
+
* {@link restoreDataDir} so the caller can revert those external frontmatter
|
|
18
|
+
* edits too.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** Files (relative to the data dir) captured by a snapshot. */
|
|
22
|
+
const SNAPSHOT_FILES = ["assignments.json", "core.json"] as const;
|
|
23
|
+
/** Directories (relative to the data dir) captured recursively. */
|
|
24
|
+
const SNAPSHOT_DIRS = ["leaves"] as const;
|
|
25
|
+
/** Filename for the persisted page->leaves diff inside a snapshot. */
|
|
26
|
+
const PAGE_REFS_FILE = "page-refs.json";
|
|
27
|
+
/** Sibling directory (relative to the data dir) that holds snapshots. */
|
|
28
|
+
const SNAPSHOTS_DIRNAME = "v3-snapshots";
|
|
29
|
+
/** Maximum number of snapshots to retain; older ones are pruned. */
|
|
30
|
+
const MAX_RETAINED_SNAPSHOTS = 5;
|
|
31
|
+
|
|
32
|
+
function snapshotsRoot(dataDir: string): string {
|
|
33
|
+
return path.join(path.dirname(dataDir), SNAPSHOTS_DIRNAME);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function copyFileIfExists(src: string, dest: string): Promise<void> {
|
|
37
|
+
try {
|
|
38
|
+
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
39
|
+
await fs.copyFile(src, dest);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") return;
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Copy `src` into `dest` recursively. Returns false if `src` doesn't exist. */
|
|
47
|
+
async function copyDirIfExists(src: string, dest: string): Promise<boolean> {
|
|
48
|
+
let entries: import("node:fs").Dirent[];
|
|
49
|
+
try {
|
|
50
|
+
entries = await fs.readdir(src, { withFileTypes: true });
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") return false;
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
await fs.mkdir(dest, { recursive: true });
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
const from = path.join(src, entry.name);
|
|
58
|
+
const to = path.join(dest, entry.name);
|
|
59
|
+
if (entry.isDirectory()) {
|
|
60
|
+
await copyDirIfExists(from, to);
|
|
61
|
+
} else if (entry.isFile()) {
|
|
62
|
+
await fs.copyFile(from, to);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function removeIfExists(target: string): Promise<void> {
|
|
69
|
+
await fs.rm(target, { recursive: true, force: true });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function serializePageRefs(pageRefs: Map<string, string[]>): string {
|
|
73
|
+
return JSON.stringify(Object.fromEntries(pageRefs), null, 2);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function deserializePageRefs(raw: string): Map<string, string[]> {
|
|
77
|
+
const obj = JSON.parse(raw) as Record<string, string[]>;
|
|
78
|
+
return new Map(Object.entries(obj));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Copy the v3 data dir's durable state into a timestamped sibling snapshot
|
|
83
|
+
* directory and return its path.
|
|
84
|
+
*
|
|
85
|
+
* @param dataDir Absolute path to the v3 data dir.
|
|
86
|
+
* @param opts.pageRefs Optional slug->prior-leaves map describing page
|
|
87
|
+
* frontmatter that lives outside the data dir; persisted so a later restore
|
|
88
|
+
* can revert it.
|
|
89
|
+
* @param opts.label Snapshot directory name. Defaults to `Date.now()`; pass an
|
|
90
|
+
* explicit label to keep tests deterministic.
|
|
91
|
+
* @returns Absolute path to the created snapshot directory.
|
|
92
|
+
*/
|
|
93
|
+
export async function snapshotDataDir(
|
|
94
|
+
dataDir: string,
|
|
95
|
+
opts?: { pageRefs?: Map<string, string[]>; label?: string },
|
|
96
|
+
): Promise<string> {
|
|
97
|
+
const label = opts?.label ?? String(Date.now());
|
|
98
|
+
const snapshotPath = path.join(snapshotsRoot(dataDir), label);
|
|
99
|
+
await fs.mkdir(snapshotPath, { recursive: true });
|
|
100
|
+
|
|
101
|
+
for (const file of SNAPSHOT_FILES) {
|
|
102
|
+
await copyFileIfExists(
|
|
103
|
+
path.join(dataDir, file),
|
|
104
|
+
path.join(snapshotPath, file),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
for (const dir of SNAPSHOT_DIRS) {
|
|
108
|
+
await copyDirIfExists(
|
|
109
|
+
path.join(dataDir, dir),
|
|
110
|
+
path.join(snapshotPath, dir),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
if (opts?.pageRefs) {
|
|
114
|
+
await fs.writeFile(
|
|
115
|
+
path.join(snapshotPath, PAGE_REFS_FILE),
|
|
116
|
+
serializePageRefs(opts.pageRefs),
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await pruneSnapshots(dataDir);
|
|
121
|
+
return snapshotPath;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Atomically replace the v3 data dir's durable state from a snapshot.
|
|
126
|
+
*
|
|
127
|
+
* Each captured file/dir is staged next to its destination and renamed into
|
|
128
|
+
* place, so a crash mid-restore never leaves a half-copied tree.
|
|
129
|
+
*
|
|
130
|
+
* @param snapshotPath Absolute path to a directory created by
|
|
131
|
+
* {@link snapshotDataDir}.
|
|
132
|
+
* @param dataDir Absolute path to the v3 data dir to overwrite.
|
|
133
|
+
* @returns The persisted `pageRefs` map (if the snapshot captured one) so the
|
|
134
|
+
* caller can revert external page frontmatter.
|
|
135
|
+
*/
|
|
136
|
+
export async function restoreDataDir(
|
|
137
|
+
snapshotPath: string,
|
|
138
|
+
dataDir: string,
|
|
139
|
+
): Promise<{ pageRefs?: Map<string, string[]> }> {
|
|
140
|
+
await fs.mkdir(dataDir, { recursive: true });
|
|
141
|
+
|
|
142
|
+
for (const file of SNAPSHOT_FILES) {
|
|
143
|
+
const src = path.join(snapshotPath, file);
|
|
144
|
+
const dest = path.join(dataDir, file);
|
|
145
|
+
const tmp = `${dest}.restore-tmp`;
|
|
146
|
+
try {
|
|
147
|
+
await fs.copyFile(src, tmp);
|
|
148
|
+
} catch (err) {
|
|
149
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
150
|
+
// Snapshot didn't capture this file; drop any stale destination copy.
|
|
151
|
+
await removeIfExists(dest);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
156
|
+
await fs.rename(tmp, dest);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
for (const dir of SNAPSHOT_DIRS) {
|
|
160
|
+
const src = path.join(snapshotPath, dir);
|
|
161
|
+
const dest = path.join(dataDir, dir);
|
|
162
|
+
const tmp = `${dest}.restore-tmp`;
|
|
163
|
+
await removeIfExists(tmp);
|
|
164
|
+
const captured = await copyDirIfExists(src, tmp);
|
|
165
|
+
if (!captured) {
|
|
166
|
+
// Snapshot didn't capture this dir; drop any stale destination copy.
|
|
167
|
+
await removeIfExists(dest);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
await removeIfExists(dest);
|
|
171
|
+
await fs.rename(tmp, dest);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const pageRefsPath = path.join(snapshotPath, PAGE_REFS_FILE);
|
|
175
|
+
try {
|
|
176
|
+
const raw = await fs.readFile(pageRefsPath, "utf8");
|
|
177
|
+
return { pageRefs: deserializePageRefs(raw) };
|
|
178
|
+
} catch (err) {
|
|
179
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") return {};
|
|
180
|
+
throw err;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Delete all but the most recent {@link MAX_RETAINED_SNAPSHOTS} snapshot
|
|
186
|
+
* directories. Snapshots sort lexicographically by their label; numeric
|
|
187
|
+
* timestamps and zero-padded labels both order chronologically.
|
|
188
|
+
*/
|
|
189
|
+
async function pruneSnapshots(dataDir: string): Promise<void> {
|
|
190
|
+
const root = snapshotsRoot(dataDir);
|
|
191
|
+
let entries: import("node:fs").Dirent[];
|
|
192
|
+
try {
|
|
193
|
+
entries = await fs.readdir(root, { withFileTypes: true });
|
|
194
|
+
} catch (err) {
|
|
195
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") return;
|
|
196
|
+
throw err;
|
|
197
|
+
}
|
|
198
|
+
const dirs = entries
|
|
199
|
+
.filter((e) => e.isDirectory())
|
|
200
|
+
.map((e) => e.name)
|
|
201
|
+
.sort();
|
|
202
|
+
const stale = dirs.slice(
|
|
203
|
+
0,
|
|
204
|
+
Math.max(0, dirs.length - MAX_RETAINED_SNAPSHOTS),
|
|
205
|
+
);
|
|
206
|
+
for (const name of stale) {
|
|
207
|
+
await removeIfExists(path.join(root, name));
|
|
208
|
+
}
|
|
209
|
+
}
|