@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,523 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory v3 — deterministic tree reconciler.
|
|
3
|
+
*
|
|
4
|
+
* After a maintainer restructures the leaf tree on disk (renames a leaf, moves
|
|
5
|
+
* it under a new domain, splits one leaf into several, or deletes a leaf), the
|
|
6
|
+
* page→leaf assignment store (each page's `leaves:` frontmatter) and the
|
|
7
|
+
* always-on `core.json` set still point at the OLD leaf paths. This module
|
|
8
|
+
* reconciles those references against the CURRENT on-disk tree, diffing leaves
|
|
9
|
+
* by their stable `id` so a rename is distinguishable from a delete+add:
|
|
10
|
+
*
|
|
11
|
+
* - id in prev and current with a changed path → RENAME/MOVE. Every page
|
|
12
|
+
* `leaves:` ref and every `core.json` entry pointing at the old path is
|
|
13
|
+
* rewritten to the new path. Assignments are preserved verbatim.
|
|
14
|
+
* - id in prev but absent from current → DELETE. The deleted leaf's
|
|
15
|
+
* member pages are first RE-HOMED across the surviving tree via
|
|
16
|
+
* {@link assignPages} (union semantics — no page is left orphaned), then the
|
|
17
|
+
* dangling ref to the old path is dropped from page frontmatter and core.
|
|
18
|
+
* - id in current but absent from prev → ADD. Nothing to migrate.
|
|
19
|
+
*
|
|
20
|
+
* Splits are expressed as a removed `from` leaf whose members re-home across the
|
|
21
|
+
* remaining/new leaves. Pass `splits` to restrict a from-leaf's members to a
|
|
22
|
+
* specific target set; otherwise a removed leaf's members re-home across the
|
|
23
|
+
* whole surviving tree (the generic delete path).
|
|
24
|
+
*
|
|
25
|
+
* Fail-closed: the whole operation runs against a snapshot taken BEFORE any
|
|
26
|
+
* mutation. After rewrites, the reconciler VALIDATES that no page `leaves:` ref
|
|
27
|
+
* and no `core.json` entry points at a leaf path absent from the current tree.
|
|
28
|
+
* If any dangling reference survives, it restores the data dir and reverts the
|
|
29
|
+
* affected page frontmatter from the snapshot, then re-throws — leaving the
|
|
30
|
+
* workspace exactly as it was before the call.
|
|
31
|
+
*
|
|
32
|
+
* On a successful reconcile it invalidates the v3 lanes ({@link invalidateLanes})
|
|
33
|
+
* so the next turn rebuilds the tree/needle from the reconciled state.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
37
|
+
import { join, relative, sep } from "node:path";
|
|
38
|
+
|
|
39
|
+
import { parse as parseYaml } from "yaml";
|
|
40
|
+
|
|
41
|
+
import type { Provider } from "../../providers/types.js";
|
|
42
|
+
import { getLogger } from "../../util/logger.js";
|
|
43
|
+
import { listPages, readPage, writePage } from "../v2/page-store.js";
|
|
44
|
+
import { assignPages } from "./assign.js";
|
|
45
|
+
import { loadCore } from "./core.js";
|
|
46
|
+
import { invalidateLanes } from "./shadow-plugin.js";
|
|
47
|
+
import { restoreDataDir, snapshotDataDir } from "./snapshot.js";
|
|
48
|
+
import { loadLeafTree } from "./tree.js";
|
|
49
|
+
import type { LeafPath, LeafTree, Slug } from "./types.js";
|
|
50
|
+
|
|
51
|
+
const log = getLogger("memory-v3-reconcile");
|
|
52
|
+
|
|
53
|
+
/** A leaf as seen at a point in time: its stable id (if any) and its path. */
|
|
54
|
+
export interface LeafRef {
|
|
55
|
+
id?: string;
|
|
56
|
+
path: LeafPath;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Optional split directive: re-home `fromId`'s member pages across `toPaths`
|
|
61
|
+
* only (instead of the whole surviving tree). `toPaths` must be leaf paths
|
|
62
|
+
* present in the current tree.
|
|
63
|
+
*/
|
|
64
|
+
export interface SplitDirective {
|
|
65
|
+
fromId: string;
|
|
66
|
+
toPaths: LeafPath[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ReconcileArgs {
|
|
70
|
+
/** The leaf set as it was BEFORE the maintainer's on-disk restructuring. */
|
|
71
|
+
prevLeaves: LeafRef[];
|
|
72
|
+
/** Absolute path to the v3 data dir (`<workspace>/memory/v3/data`). */
|
|
73
|
+
dataDir: string;
|
|
74
|
+
/** Workspace root; pages live under `<workspaceDir>/memory/concepts/`. */
|
|
75
|
+
workspaceDir: string;
|
|
76
|
+
/** Optional split directives (see {@link SplitDirective}). */
|
|
77
|
+
splits?: SplitDirective[];
|
|
78
|
+
/** Provider override forwarded to {@link assignPages} for re-homing. */
|
|
79
|
+
provider?: Provider;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface ReconcileResult {
|
|
83
|
+
/**
|
|
84
|
+
* Continuity map for telemetry/eval: each entry maps a leaf's prior identity
|
|
85
|
+
* (`id` when known, else its old path) to its new path. Renames record the
|
|
86
|
+
* new path; deletes are omitted (the leaf has no new path).
|
|
87
|
+
*/
|
|
88
|
+
idToNewPath: Map<string, LeafPath>;
|
|
89
|
+
/** Leaf paths that were renamed/moved (old → new). */
|
|
90
|
+
renames: Array<{ id?: string; oldPath: LeafPath; newPath: LeafPath }>;
|
|
91
|
+
/** Leaf paths that were deleted (present in prev, absent from current). */
|
|
92
|
+
deleted: LeafPath[];
|
|
93
|
+
/** `core.json` entries pruned because they pointed outside the current tree. */
|
|
94
|
+
prunedCore: LeafPath[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// On-disk leaf reading (mirrors the parse approach in tree.ts; the helpers
|
|
99
|
+
// there are not exported, so we replicate the minimal slice we need here).
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
/** Recursively collect every `*.md` file under `dir`. */
|
|
103
|
+
async function collectMarkdownFiles(dir: string): Promise<string[]> {
|
|
104
|
+
let entries: import("node:fs").Dirent[];
|
|
105
|
+
try {
|
|
106
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
107
|
+
} catch (err) {
|
|
108
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") return [];
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
const files: string[] = [];
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
const full = join(dir, entry.name);
|
|
114
|
+
if (entry.isDirectory()) {
|
|
115
|
+
files.push(...(await collectMarkdownFiles(full)));
|
|
116
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
117
|
+
files.push(full);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return files;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function parseLeafRef(yaml: string, leavesDir: string, file: string): LeafRef {
|
|
124
|
+
const parsed = parseYaml(yaml) as unknown;
|
|
125
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
126
|
+
throw new Error(`leaf ${file} frontmatter is not a mapping`);
|
|
127
|
+
}
|
|
128
|
+
const record = parsed as Record<string, unknown>;
|
|
129
|
+
// Prefer the frontmatter `path`; fall back to the file location so a leaf with
|
|
130
|
+
// a missing/empty path still resolves to its on-disk identity.
|
|
131
|
+
const path =
|
|
132
|
+
typeof record.path === "string" && record.path.length > 0
|
|
133
|
+
? record.path
|
|
134
|
+
: relative(leavesDir, file).replace(/\.md$/, "").split(sep).join("/");
|
|
135
|
+
return {
|
|
136
|
+
path,
|
|
137
|
+
...(typeof record.id === "string" ? { id: record.id } : {}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Read the CURRENT leaf set (id + path) from `<dataDir>/leaves/**`. */
|
|
142
|
+
async function readCurrentLeaves(dataDir: string): Promise<LeafRef[]> {
|
|
143
|
+
const leavesDir = join(dataDir, "leaves");
|
|
144
|
+
const files = await collectMarkdownFiles(leavesDir);
|
|
145
|
+
return Promise.all(
|
|
146
|
+
files.map(async (file) => {
|
|
147
|
+
const raw = await readFile(file, "utf8");
|
|
148
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(
|
|
149
|
+
raw.replace(/^/, ""),
|
|
150
|
+
);
|
|
151
|
+
if (!match) throw new Error(`leaf ${file} is missing YAML frontmatter`);
|
|
152
|
+
return parseLeafRef(match[1], leavesDir, file);
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
// Diff
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
interface TreeDiff {
|
|
162
|
+
/** old path → new path for leaves whose id persisted but path changed. */
|
|
163
|
+
renames: Map<LeafPath, LeafPath>;
|
|
164
|
+
/** leaves present in prev but absent from current (by id, or by path when no id). */
|
|
165
|
+
deleted: LeafRef[];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Diff `prev` against `current` BY id. Leaves without an id fall back to a
|
|
170
|
+
* path-keyed comparison (legacy leaves predating stable ids): an id-less prev
|
|
171
|
+
* leaf is "deleted" only if its path is absent from current.
|
|
172
|
+
*/
|
|
173
|
+
function diffByIdThenPath(prev: LeafRef[], current: LeafRef[]): TreeDiff {
|
|
174
|
+
const currentById = new Map<string, LeafRef>();
|
|
175
|
+
const currentPaths = new Set<LeafPath>();
|
|
176
|
+
for (const leaf of current) {
|
|
177
|
+
if (leaf.id) currentById.set(leaf.id, leaf);
|
|
178
|
+
currentPaths.add(leaf.path);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const renames = new Map<LeafPath, LeafPath>();
|
|
182
|
+
const deleted: LeafRef[] = [];
|
|
183
|
+
|
|
184
|
+
for (const prevLeaf of prev) {
|
|
185
|
+
if (prevLeaf.id) {
|
|
186
|
+
const match = currentById.get(prevLeaf.id);
|
|
187
|
+
if (!match) {
|
|
188
|
+
deleted.push(prevLeaf);
|
|
189
|
+
} else if (match.path !== prevLeaf.path) {
|
|
190
|
+
renames.set(prevLeaf.path, match.path);
|
|
191
|
+
}
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
// No id: identity is the path. Deleted iff the path is gone.
|
|
195
|
+
if (!currentPaths.has(prevLeaf.path)) deleted.push(prevLeaf);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return { renames, deleted };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
// Page-frontmatter ref rewriting
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
|
|
205
|
+
/** Load every page's `leaves:` frontmatter as a slug → leaf-paths map. */
|
|
206
|
+
async function loadPageRefs(
|
|
207
|
+
workspaceDir: string,
|
|
208
|
+
): Promise<Map<Slug, LeafPath[]>> {
|
|
209
|
+
const slugs = await listPages(workspaceDir);
|
|
210
|
+
const refs = new Map<Slug, LeafPath[]>();
|
|
211
|
+
for (const slug of slugs) {
|
|
212
|
+
const page = await readPage(workspaceDir, slug);
|
|
213
|
+
if (page) refs.set(slug, page.frontmatter.leaves ?? []);
|
|
214
|
+
}
|
|
215
|
+
return refs;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Apply a leaf-path transform to a single page's `leaves:` frontmatter and
|
|
220
|
+
* persist it iff the result changed. `transform` returns the rewritten list
|
|
221
|
+
* (renamed/dropped entries already applied), de-duplicated by the caller.
|
|
222
|
+
*/
|
|
223
|
+
async function rewritePageRefs(
|
|
224
|
+
workspaceDir: string,
|
|
225
|
+
slug: Slug,
|
|
226
|
+
transform: (leaves: LeafPath[]) => LeafPath[],
|
|
227
|
+
): Promise<void> {
|
|
228
|
+
const page = await readPage(workspaceDir, slug);
|
|
229
|
+
if (!page) return;
|
|
230
|
+
const before = page.frontmatter.leaves ?? [];
|
|
231
|
+
const after = transform(before);
|
|
232
|
+
if (sameLeaves(before, after)) return;
|
|
233
|
+
await writePage(workspaceDir, {
|
|
234
|
+
...page,
|
|
235
|
+
frontmatter: { ...page.frontmatter, leaves: after },
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function sameLeaves(a: LeafPath[], b: LeafPath[]): boolean {
|
|
240
|
+
if (a.length !== b.length) return false;
|
|
241
|
+
return a.every((v, i) => v === b[i]);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** De-duplicate a leaf-path list, preserving first-seen order. */
|
|
245
|
+
function dedupe(leaves: LeafPath[]): LeafPath[] {
|
|
246
|
+
const out: LeafPath[] = [];
|
|
247
|
+
for (const leaf of leaves) if (!out.includes(leaf)) out.push(leaf);
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
// Reconcile
|
|
253
|
+
// ---------------------------------------------------------------------------
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Reconcile page + core references against the current on-disk leaf tree.
|
|
257
|
+
* See the module docstring for the full contract. Fail-closed: throws (after
|
|
258
|
+
* restoring from snapshot) if any dangling reference survives validation.
|
|
259
|
+
*/
|
|
260
|
+
export async function reconcileTree(
|
|
261
|
+
args: ReconcileArgs,
|
|
262
|
+
): Promise<ReconcileResult> {
|
|
263
|
+
const { prevLeaves, dataDir, workspaceDir, splits, provider } = args;
|
|
264
|
+
|
|
265
|
+
const current = await readCurrentLeaves(dataDir);
|
|
266
|
+
const currentPaths = new Set<LeafPath>(current.map((l) => l.path));
|
|
267
|
+
const { renames, deleted } = diffByIdThenPath(prevLeaves, current);
|
|
268
|
+
|
|
269
|
+
// Capture the prior page->leaves map so a failed reconcile can revert the
|
|
270
|
+
// external page frontmatter the same way the snapshot reverts the data dir.
|
|
271
|
+
const priorPageRefs = await loadPageRefs(workspaceDir);
|
|
272
|
+
const snapshotPath = await snapshotDataDir(dataDir, {
|
|
273
|
+
pageRefs: priorPageRefs,
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
const result = await applyReconcile({
|
|
278
|
+
current,
|
|
279
|
+
currentPaths,
|
|
280
|
+
renames,
|
|
281
|
+
deleted,
|
|
282
|
+
splits: splits ?? [],
|
|
283
|
+
workspaceDir,
|
|
284
|
+
dataDir,
|
|
285
|
+
provider,
|
|
286
|
+
});
|
|
287
|
+
await validateNoDangling(workspaceDir, dataDir, currentPaths);
|
|
288
|
+
invalidateLanes();
|
|
289
|
+
return result;
|
|
290
|
+
} catch (err) {
|
|
291
|
+
log.warn(
|
|
292
|
+
{ err: err instanceof Error ? err.message : String(err) },
|
|
293
|
+
"reconcile failed validation — restoring from snapshot",
|
|
294
|
+
);
|
|
295
|
+
await revertFromSnapshot(snapshotPath, dataDir, workspaceDir);
|
|
296
|
+
throw err;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
interface ApplyArgs {
|
|
301
|
+
current: LeafRef[];
|
|
302
|
+
currentPaths: Set<LeafPath>;
|
|
303
|
+
renames: Map<LeafPath, LeafPath>;
|
|
304
|
+
deleted: LeafRef[];
|
|
305
|
+
splits: SplitDirective[];
|
|
306
|
+
workspaceDir: string;
|
|
307
|
+
dataDir: string;
|
|
308
|
+
provider?: Provider;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function applyReconcile(args: ApplyArgs): Promise<ReconcileResult> {
|
|
312
|
+
const {
|
|
313
|
+
current,
|
|
314
|
+
currentPaths,
|
|
315
|
+
renames,
|
|
316
|
+
deleted,
|
|
317
|
+
splits,
|
|
318
|
+
workspaceDir,
|
|
319
|
+
dataDir,
|
|
320
|
+
provider,
|
|
321
|
+
} = args;
|
|
322
|
+
|
|
323
|
+
// The set of leaf paths a ref may legitimately resolve to: the current tree
|
|
324
|
+
// plus every rename TARGET (a renamed leaf's new path is in `current`, but we
|
|
325
|
+
// include the map's values defensively). Any page ref or core entry that does
|
|
326
|
+
// NOT resolve into this set is dangling and must converge to a valid state.
|
|
327
|
+
const validPaths = new Set<LeafPath>(currentPaths);
|
|
328
|
+
for (const newPath of renames.values()) validPaths.add(newPath);
|
|
329
|
+
|
|
330
|
+
// A ref is "dangling" when, after rename resolution, it points outside the
|
|
331
|
+
// current tree. This covers BOTH reconciler-driven deletes (leaves in `prev`
|
|
332
|
+
// but not `current`) AND out-of-band deletes/renames a maintainer made on disk
|
|
333
|
+
// without a `prevLeaves` entry — the v1 convergence/prune case. We drop such
|
|
334
|
+
// refs in step 2; first (step 1) we re-home any page that would otherwise be
|
|
335
|
+
// orphaned so no page is left with zero leaves.
|
|
336
|
+
const isDangling = (resolved: LeafPath): boolean => !validPaths.has(resolved);
|
|
337
|
+
|
|
338
|
+
// 1. Re-home members of dangling leaves BEFORE dropping any refs, so a page
|
|
339
|
+
// that would lose its only leaf gains a surviving one (no orphan). We
|
|
340
|
+
// re-home first, against the still-intact assignments, then drop the dead
|
|
341
|
+
// path. Reconciler-tracked deletes may carry a split directive; out-of-band
|
|
342
|
+
// dangling leaves (discovered from page refs) re-home across the whole tree.
|
|
343
|
+
const splitById = new Map<string, SplitDirective>(
|
|
344
|
+
splits.map((s) => [s.fromId, s]),
|
|
345
|
+
);
|
|
346
|
+
const slugs = await listPages(workspaceDir);
|
|
347
|
+
|
|
348
|
+
// Collect every dangling leaf path: explicit deletes + any leaf referenced by
|
|
349
|
+
// a page that resolves outside the current tree.
|
|
350
|
+
const danglingLeafToMembers = new Map<LeafPath, Slug[]>();
|
|
351
|
+
for (const leaf of deleted) danglingLeafToMembers.set(leaf.path, []);
|
|
352
|
+
for (const slug of slugs) {
|
|
353
|
+
const page = await readPage(workspaceDir, slug);
|
|
354
|
+
if (!page) continue;
|
|
355
|
+
for (const ref of page.frontmatter.leaves ?? []) {
|
|
356
|
+
const resolved = renames.get(ref) ?? ref;
|
|
357
|
+
if (!isDangling(resolved)) continue;
|
|
358
|
+
const members = danglingLeafToMembers.get(ref) ?? [];
|
|
359
|
+
members.push(slug);
|
|
360
|
+
danglingLeafToMembers.set(ref, members);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const deletedPaths = new Set<LeafPath>(danglingLeafToMembers.keys());
|
|
365
|
+
for (const [leafPath, members] of danglingLeafToMembers) {
|
|
366
|
+
if (members.length === 0) continue;
|
|
367
|
+
const id = deleted.find((l) => l.path === leafPath)?.id;
|
|
368
|
+
const split = id ? splitById.get(id) : undefined;
|
|
369
|
+
await rehomeMembers(workspaceDir, dataDir, members, split, provider);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// 2. Rewrite renamed paths and drop dangling paths from page frontmatter.
|
|
373
|
+
for (const slug of slugs) {
|
|
374
|
+
await rewritePageRefs(workspaceDir, slug, (leaves) =>
|
|
375
|
+
dedupe(
|
|
376
|
+
leaves.map((p) => renames.get(p) ?? p).filter((p) => !isDangling(p)),
|
|
377
|
+
),
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// 3. Rewrite renamed paths, drop deleted paths, and PRUNE any core entry that
|
|
382
|
+
// points outside the current tree.
|
|
383
|
+
const core = await loadCore(dataDir);
|
|
384
|
+
const prunedCore: LeafPath[] = [];
|
|
385
|
+
const nextCore: LeafPath[] = [];
|
|
386
|
+
for (const path of core) {
|
|
387
|
+
const renamed = renames.get(path);
|
|
388
|
+
const resolved = renamed ?? path;
|
|
389
|
+
if (deletedPaths.has(resolved) || !currentPaths.has(resolved)) {
|
|
390
|
+
prunedCore.push(path);
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (!nextCore.includes(resolved)) nextCore.push(resolved);
|
|
394
|
+
}
|
|
395
|
+
await writeCore(dataDir, nextCore);
|
|
396
|
+
|
|
397
|
+
// 4. Build the continuity map (renames → new path; deletes have no new path).
|
|
398
|
+
const idToNewPath = new Map<string, LeafPath>();
|
|
399
|
+
const renameList: ReconcileResult["renames"] = [];
|
|
400
|
+
for (const [oldPath, newPath] of renames) {
|
|
401
|
+
const id = current.find((l) => l.path === newPath)?.id;
|
|
402
|
+
renameList.push({ id, oldPath, newPath });
|
|
403
|
+
idToNewPath.set(id ?? oldPath, newPath);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
idToNewPath,
|
|
408
|
+
renames: renameList,
|
|
409
|
+
deleted: deleted.map((l) => l.path),
|
|
410
|
+
prunedCore,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Re-home a deleted leaf's member pages onto the surviving tree via
|
|
416
|
+
* {@link assignPages} (union semantics — never drops a page's other leaves).
|
|
417
|
+
* When a {@link SplitDirective} is supplied, the classifier is restricted to a
|
|
418
|
+
* sub-tree containing only the split's `toPaths` so members redistribute across
|
|
419
|
+
* the explicit targets; otherwise it runs against the whole current tree.
|
|
420
|
+
*/
|
|
421
|
+
async function rehomeMembers(
|
|
422
|
+
workspaceDir: string,
|
|
423
|
+
dataDir: string,
|
|
424
|
+
members: Slug[],
|
|
425
|
+
split: SplitDirective | undefined,
|
|
426
|
+
provider?: Provider,
|
|
427
|
+
): Promise<void> {
|
|
428
|
+
const fullTree: LeafTree = await loadLeafTreeForReconcile(
|
|
429
|
+
workspaceDir,
|
|
430
|
+
dataDir,
|
|
431
|
+
);
|
|
432
|
+
const tree =
|
|
433
|
+
split && split.toPaths.length > 0
|
|
434
|
+
? restrictTree(fullTree, split.toPaths)
|
|
435
|
+
: fullTree;
|
|
436
|
+
if (tree.leaves.size === 0) return;
|
|
437
|
+
await assignPages({
|
|
438
|
+
tree,
|
|
439
|
+
workspaceDir,
|
|
440
|
+
slugs: members,
|
|
441
|
+
provider,
|
|
442
|
+
throttleMs: 0,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** Load the current tree with page-frontmatter assignments folded in. */
|
|
447
|
+
async function loadLeafTreeForReconcile(
|
|
448
|
+
workspaceDir: string,
|
|
449
|
+
dataDir: string,
|
|
450
|
+
): Promise<LeafTree> {
|
|
451
|
+
const pageRefs = await loadPageRefs(workspaceDir);
|
|
452
|
+
return loadLeafTree(dataDir, pageRefs);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/** A view of `tree` containing only the leaves in `keep`. */
|
|
456
|
+
function restrictTree(tree: LeafTree, keep: LeafPath[]): LeafTree {
|
|
457
|
+
const keepSet = new Set(keep);
|
|
458
|
+
const leaves = new Map(
|
|
459
|
+
[...tree.leaves].filter(([path]) => keepSet.has(path)),
|
|
460
|
+
);
|
|
461
|
+
return { leaves, byPage: tree.byPage };
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** Persist the always-on core set back to `<dataDir>/core.json`. */
|
|
465
|
+
async function writeCore(dataDir: string, alwaysOn: LeafPath[]): Promise<void> {
|
|
466
|
+
await writeFile(
|
|
467
|
+
join(dataDir, "core.json"),
|
|
468
|
+
`${JSON.stringify({ alwaysOn }, null, 2)}\n`,
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Fail-closed validation: after all rewrites, NO page `leaves:` ref and NO
|
|
474
|
+
* core entry may point at a leaf path absent from the current tree. Throws on
|
|
475
|
+
* the first dangling reference found.
|
|
476
|
+
*/
|
|
477
|
+
async function validateNoDangling(
|
|
478
|
+
workspaceDir: string,
|
|
479
|
+
dataDir: string,
|
|
480
|
+
currentPaths: Set<LeafPath>,
|
|
481
|
+
): Promise<void> {
|
|
482
|
+
const pageRefs = await loadPageRefs(workspaceDir);
|
|
483
|
+
for (const [slug, leaves] of pageRefs) {
|
|
484
|
+
for (const leaf of leaves) {
|
|
485
|
+
if (!currentPaths.has(leaf)) {
|
|
486
|
+
throw new Error(
|
|
487
|
+
`reconcile validation failed: page "${slug}" references missing leaf "${leaf}"`,
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const core = await loadCore(dataDir);
|
|
493
|
+
for (const leaf of core) {
|
|
494
|
+
if (!currentPaths.has(leaf)) {
|
|
495
|
+
throw new Error(
|
|
496
|
+
`reconcile validation failed: core references missing leaf "${leaf}"`,
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Restore the data dir from the snapshot AND revert external page frontmatter
|
|
504
|
+
* from the snapshot's captured `pageRefs`, so a failed reconcile leaves the
|
|
505
|
+
* workspace byte-for-byte as it was before the call.
|
|
506
|
+
*/
|
|
507
|
+
async function revertFromSnapshot(
|
|
508
|
+
snapshotPath: string,
|
|
509
|
+
dataDir: string,
|
|
510
|
+
workspaceDir: string,
|
|
511
|
+
): Promise<void> {
|
|
512
|
+
const { pageRefs } = await restoreDataDir(snapshotPath, dataDir);
|
|
513
|
+
if (!pageRefs) return;
|
|
514
|
+
for (const [slug, leaves] of pageRefs) {
|
|
515
|
+
const page = await readPage(workspaceDir, slug);
|
|
516
|
+
if (!page) continue;
|
|
517
|
+
if (sameLeaves(page.frontmatter.leaves ?? [], leaves)) continue;
|
|
518
|
+
await writePage(workspaceDir, {
|
|
519
|
+
...page,
|
|
520
|
+
frontmatter: { ...page.frontmatter, leaves },
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { wrapMemoryBlock } from "../memory-marker.js";
|
|
2
|
+
import { Slug } from "./types.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Render a v3 working-set selection into a single `<memory>…</memory>` text
|
|
6
|
+
* block.
|
|
7
|
+
*
|
|
8
|
+
* Pure and side-effect-free: all I/O is delegated to the injected
|
|
9
|
+
* `pageContent` resolver, which is awaited once per slug in input order. The
|
|
10
|
+
* resulting block uses the shared {@link wrapMemoryBlock} marker — the same
|
|
11
|
+
* wrapper the v2 graph-memory path emits — so the existing strip machinery in
|
|
12
|
+
* `conversation-graph-memory.ts` already recognizes (and evicts) it.
|
|
13
|
+
*
|
|
14
|
+
* Resolution is concurrent; the rendered block preserves `finalInjection`
|
|
15
|
+
* order regardless of which fetch settles first.
|
|
16
|
+
*
|
|
17
|
+
* @param finalInjection - The working-set selection for the turn, in the order
|
|
18
|
+
* it should appear in the rendered block.
|
|
19
|
+
* @param pageContent - Resolver returning a page's rendered content for a slug.
|
|
20
|
+
* @returns The wrapped `<memory>` block, or `""` for an empty selection (the
|
|
21
|
+
* caller injects nothing).
|
|
22
|
+
*/
|
|
23
|
+
export async function renderMemoryBlock(
|
|
24
|
+
finalInjection: Slug[],
|
|
25
|
+
pageContent: (slug: Slug) => Promise<string>,
|
|
26
|
+
): Promise<string> {
|
|
27
|
+
if (finalInjection.length === 0) return "";
|
|
28
|
+
|
|
29
|
+
const contents = await Promise.all(finalInjection.map(pageContent));
|
|
30
|
+
|
|
31
|
+
return wrapMemoryBlock(contents.join("\n"));
|
|
32
|
+
}
|