@vellumai/assistant 0.8.4 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +2 -2
- package/docs/browser-use-architecture-phase2.md +1 -1
- package/knip.json +2 -1
- package/openapi.yaml +809 -11
- package/package.json +1 -1
- package/src/__tests__/anthropic-provider.test.ts +34 -37
- package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
- package/src/__tests__/audit-log-rotation.test.ts +70 -16
- package/src/__tests__/background-workers-disk-pressure.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +2 -3
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +3 -3
- package/src/__tests__/checker.test.ts +6 -15
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
- package/src/__tests__/computer-use-tools.test.ts +2 -4
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +197 -2
- package/src/__tests__/conversation-agent-loop.test.ts +163 -122
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
- package/src/__tests__/conversation-clear-safety.test.ts +25 -25
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-fork-crud.test.ts +178 -15
- package/src/__tests__/conversation-lifecycle.test.ts +52 -11
- package/src/__tests__/{conversation-load-cleaned-at.test.ts → conversation-load-history-stripped.test.ts} +13 -13
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
- package/src/__tests__/conversation-skill-tools.test.ts +2 -5
- package/src/__tests__/conversation-store.test.ts +1 -1
- package/src/__tests__/conversation-sync-tags.test.ts +99 -32
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
- package/src/__tests__/credential-execution-tools.test.ts +6 -6
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +2 -2
- package/src/__tests__/dynamic-page-surface.test.ts +2 -2
- package/src/__tests__/email-html-renderer.test.ts +12 -0
- package/src/__tests__/gateway-flag-listener.test.ts +237 -0
- package/src/__tests__/gemini-provider.test.ts +78 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/guardian-outbound-http.test.ts +7 -5
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
- package/src/__tests__/heartbeat-service.test.ts +4 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
- package/src/__tests__/list-messages-tool-merge.test.ts +70 -11
- package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
- package/src/__tests__/llm-resolver.test.ts +77 -9
- package/src/__tests__/llm-usage-store.test.ts +66 -0
- package/src/__tests__/logger.test.ts +89 -0
- package/src/__tests__/mcp-abort-signal.test.ts +2 -2
- package/src/__tests__/media-generate-image.test.ts +31 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
- package/src/__tests__/model-intents.test.ts +2 -4
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/onboarding-template-contract.test.ts +1 -1
- package/src/__tests__/openai-provider.test.ts +46 -0
- package/src/__tests__/openai-responses-provider.test.ts +114 -12
- package/src/__tests__/pending-interactions-resolved-event.test.ts +0 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
- package/src/__tests__/platform.test.ts +2 -2
- package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
- package/src/__tests__/plugin-bootstrap.test.ts +2 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
- package/src/__tests__/plugin-types.test.ts +3 -2
- package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
- package/src/__tests__/pricing.test.ts +12 -0
- package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
- package/src/__tests__/registry.test.ts +2 -8
- package/src/__tests__/require-fresh-approval.test.ts +2 -2
- package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
- package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
- package/src/__tests__/skill-tool-factory.test.ts +1 -1
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/sync-message-contract.test.ts +59 -0
- package/src/__tests__/system-prompt.test.ts +145 -131
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/tool-approval-handler.test.ts +1 -5
- package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
- package/src/__tests__/tool-executor.test.ts +9 -62
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
- package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
- package/src/__tests__/usage-routes.test.ts +3 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +6 -5
- package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
- package/src/acp/prepare-agent-env.ts +78 -0
- package/src/acp/session-manager.ts +1 -1
- package/src/agent/loop.ts +8 -0
- package/src/api/README.md +5 -0
- package/src/api/index.ts +4 -0
- package/src/api/package.json +10 -0
- package/src/background-wake/background-wake-routes.test.ts +233 -0
- package/src/background-wake/runtime-registry.ts +24 -0
- package/src/cli/commands/__tests__/browser.test.ts +23 -5
- package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
- package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
- package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +1 -0
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
- package/src/cli/commands/browser.ts +247 -0
- package/src/cli/commands/domain.ts +91 -41
- package/src/cli/commands/inference.ts +93 -40
- package/src/cli/commands/memory-v2-compare-render.ts +115 -0
- package/src/cli/commands/memory-v2.ts +176 -1
- package/src/cli/commands/memory-v3-render.ts +344 -0
- package/src/cli/commands/memory-v3.ts +316 -0
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +21 -9
- package/src/config/bundled-skills/document-editor/SKILL.md +11 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +18 -0
- package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/call-site-defaults.ts +7 -6
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +213 -1
- package/src/config/schemas/call-site-catalog.ts +21 -7
- package/src/config/schemas/llm.ts +12 -1
- package/src/config/schemas/memory-v2.ts +246 -0
- package/src/config/schemas/memory.ts +2 -1
- package/src/context/compactor.ts +52 -0
- package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
- package/src/conversations/message-consolidation.ts +404 -0
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -13
- package/src/daemon/conversation-agent-loop.ts +126 -76
- package/src/daemon/conversation-error.ts +31 -1
- package/src/daemon/conversation-lifecycle.ts +27 -22
- package/src/daemon/conversation-runtime-assembly.ts +10 -9
- package/src/daemon/conversation-tool-setup.ts +63 -3
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +14 -29
- package/src/daemon/disk-pressure-guard.ts +14 -2
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +11 -3
- package/src/daemon/host-browser-proxy.ts +5 -5
- package/src/daemon/host-cu-proxy.ts +4 -4
- package/src/daemon/host-file-proxy.ts +4 -4
- package/src/daemon/host-proxy-base.ts +4 -4
- package/src/daemon/host-transfer-proxy.ts +10 -10
- package/src/daemon/lifecycle.ts +23 -20
- package/src/daemon/meet-manifest-loader.ts +1 -7
- package/src/daemon/message-types/conversations.ts +6 -9
- package/src/daemon/message-types/home.ts +1 -13
- package/src/daemon/message-types/messages.ts +6 -14
- package/src/daemon/message-types/sync.ts +14 -0
- package/src/daemon/shutdown-handlers.ts +24 -5
- package/src/daemon/switch-inference-profile-tool.ts +52 -0
- package/src/daemon/tool-setup-types.ts +13 -0
- package/src/events/relationship-state-updated.ts +25 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -1
- package/src/home/home-greeting.ts +0 -9
- package/src/home/suggested-prompts.ts +0 -9
- package/src/ipc/gateway-flag-listener.ts +123 -0
- package/src/ipc/skill-routes/registries.ts +8 -12
- package/src/memory/__tests__/db-async-query.test.ts +165 -0
- package/src/memory/__tests__/db-maintenance.test.ts +115 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
- package/src/memory/__tests__/memory-retrospective-job.test.ts +7 -0
- package/src/memory/auto-analysis-enqueue.ts +5 -1
- package/src/memory/conversation-crud.ts +71 -70
- package/src/memory/conversation-starters-cadence.ts +3 -1
- package/src/memory/conversation-title-service.ts +19 -3
- package/src/memory/db-async-query.ts +214 -0
- package/src/memory/db-init.ts +10 -0
- package/src/memory/db-maintenance.ts +30 -21
- package/src/memory/graph/bootstrap.ts +8 -1
- package/src/memory/graph/capability-seed.ts +7 -3
- package/src/memory/graph/conversation-graph-memory.ts +100 -17
- package/src/memory/graph/extraction.ts +1 -5
- package/src/memory/graph/graph-search.ts +7 -1
- package/src/memory/indexer.ts +28 -18
- package/src/memory/job-handlers/cleanup.ts +76 -18
- package/src/memory/job-handlers/conversation-starters.ts +1 -4
- package/src/memory/jobs/embed-pkb-file.ts +6 -1
- package/src/memory/jobs-store.ts +14 -0
- package/src/memory/jobs-worker.ts +55 -22
- package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
- package/src/memory/llm-request-log-source-local.ts +7 -0
- package/src/memory/llm-request-log-source.ts +9 -2
- package/src/memory/llm-request-log-store.ts +43 -1
- package/src/memory/llm-usage-store.ts +24 -0
- package/src/memory/memory-retrospective-enqueue.ts +8 -1
- package/src/memory/memory-retrospective-job.ts +5 -0
- package/src/memory/memory-v2-activation-log-store.ts +15 -6
- package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
- package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
- package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
- package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
- package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
- package/src/memory/migrations/index.ts +17 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/schema/conversations.ts +1 -1
- package/src/memory/schema/infrastructure.ts +21 -0
- package/src/memory/tool-usage-store.ts +36 -8
- package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
- package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
- package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
- package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
- package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
- package/src/memory/v2/__tests__/injection.test.ts +127 -98
- package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
- package/src/memory/v2/__tests__/router.test.ts +171 -3
- package/src/memory/v2/harness/compare.ts +57 -0
- package/src/memory/v2/harness/metrics.ts +124 -0
- package/src/memory/v2/harness/oracle.ts +145 -0
- package/src/memory/v2/harness/replay-input.ts +224 -0
- package/src/memory/v2/harness/retriever.ts +74 -0
- package/src/memory/v2/harness/router-retriever.ts +43 -0
- package/src/memory/v2/harness/runner.ts +106 -0
- package/src/memory/v2/harness/trace.ts +58 -0
- package/src/memory/v2/injection.ts +21 -15
- package/src/memory/v2/prompts/router.ts +26 -1
- package/src/memory/v2/qdrant.ts +14 -2
- package/src/memory/v2/router.ts +171 -18
- package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
- package/src/memory/v3/__tests__/edges.test.ts +563 -0
- package/src/memory/v3/__tests__/filter.test.ts +512 -0
- package/src/memory/v3/__tests__/gate.test.ts +574 -0
- package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
- package/src/memory/v3/__tests__/loop.test.ts +530 -0
- package/src/memory/v3/__tests__/retriever.test.ts +226 -0
- package/src/memory/v3/__tests__/scouts.test.ts +440 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
- package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
- package/src/memory/v3/__tests__/traversal.test.ts +469 -0
- package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
- package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
- package/src/memory/v3/__tests__/validate.test.ts +245 -0
- package/src/memory/v3/auto-edges.ts +223 -0
- package/src/memory/v3/coactivation-store.ts +124 -0
- package/src/memory/v3/consolidation-job.ts +323 -0
- package/src/memory/v3/edge-learning-job.ts +160 -0
- package/src/memory/v3/edges.ts +249 -0
- package/src/memory/v3/filter.ts +281 -0
- package/src/memory/v3/gate.ts +334 -0
- package/src/memory/v3/index-composition.ts +113 -0
- package/src/memory/v3/llm-capture.ts +46 -0
- package/src/memory/v3/loop.ts +382 -0
- package/src/memory/v3/maintenance.ts +144 -0
- package/src/memory/v3/prompt-context.ts +33 -0
- package/src/memory/v3/prompts/consolidation.ts +458 -0
- package/src/memory/v3/prompts/system-prompts.ts +196 -0
- package/src/memory/v3/retriever.ts +33 -0
- package/src/memory/v3/scouts.ts +420 -0
- package/src/memory/v3/shadow-middleware.ts +305 -0
- package/src/memory/v3/traversal.ts +206 -0
- package/src/memory/v3/tree-index.ts +237 -0
- package/src/memory/v3/tree-store.ts +394 -0
- package/src/memory/v3/tree-walk.ts +351 -0
- package/src/memory/v3/types.ts +65 -0
- package/src/memory/v3/validate.ts +300 -0
- package/src/notifications/adapters/macos.ts +18 -1
- package/src/notifications/adapters/platform.ts +1 -1
- package/src/notifications/decision-engine.ts +1 -4
- package/src/notifications/emit-signal.ts +29 -49
- package/src/permissions/prompter.ts +3 -3
- package/src/permissions/question-prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +2 -2
- package/src/plugin-api/index.ts +4 -0
- package/src/plugin-api/types.ts +7 -33
- package/src/plugins/defaults/index.ts +6 -0
- package/src/plugins/defaults/injectors.ts +18 -11
- package/src/plugins/external-plugin-loader.ts +5 -68
- package/src/plugins/types.ts +11 -16
- package/src/proactive-artifact/aux-message-injector.ts +17 -4
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
- package/src/prompts/persona-resolver.ts +36 -21
- package/src/prompts/sections.ts +39 -7
- package/src/prompts/system-prompt.ts +50 -185
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +230 -8
- package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
- package/src/providers/__tests__/retry-callsite.test.ts +85 -5
- package/src/providers/anthropic/client.ts +32 -66
- package/src/providers/call-site-routing.ts +14 -2
- package/src/providers/connection-model-compat.ts +38 -0
- package/src/providers/connection-resolution.ts +16 -2
- package/src/providers/gemini/client.ts +49 -6
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/minimax/client.ts +106 -0
- package/src/providers/model-catalog.ts +43 -0
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openai/chat-completions-provider.ts +6 -3
- package/src/providers/openai/codex-models.ts +18 -0
- package/src/providers/openai/responses-provider.ts +78 -21
- package/src/providers/provider-send-message.ts +7 -1
- package/src/providers/retry.ts +34 -3
- package/src/providers/thinking-config.ts +26 -1
- package/src/providers/usage-tracking.ts +2 -0
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/agent-wake.ts +1 -0
- package/src/runtime/assistant-event-hub.ts +76 -6
- package/src/runtime/auth/route-policy.ts +36 -0
- package/src/runtime/btw-sidechain.ts +0 -6
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +10 -3
- package/src/runtime/pending-interactions.ts +0 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +106 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +25 -6
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
- package/src/runtime/routes/acp-routes.test.ts +255 -6
- package/src/runtime/routes/acp-routes.ts +8 -1
- package/src/runtime/routes/avatar-routes.ts +10 -10
- package/src/runtime/routes/background-wake-routes.ts +188 -0
- package/src/runtime/routes/browser-tabs-routes.ts +200 -0
- package/src/runtime/routes/btw-routes.ts +0 -6
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +12 -4
- package/src/runtime/routes/conversation-management-routes.ts +77 -20
- package/src/runtime/routes/conversation-query-routes.ts +142 -36
- package/src/runtime/routes/conversation-routes.ts +252 -410
- package/src/runtime/routes/conversation-starter-routes.ts +6 -3
- package/src/runtime/routes/disk-pressure-routes.ts +1 -1
- package/src/runtime/routes/domain-routes.ts +60 -10
- package/src/runtime/routes/email-routes.ts +5 -2
- package/src/runtime/routes/events-routes.ts +54 -10
- package/src/runtime/routes/group-routes.ts +24 -8
- package/src/runtime/routes/host-browser-routes.ts +10 -2
- package/src/runtime/routes/host-cu-routes.ts +2 -2
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
- package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
- package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
- package/src/runtime/routes/memory-item-routes.ts +8 -3
- package/src/runtime/routes/memory-v2-routes.ts +215 -5
- package/src/runtime/routes/memory-v3-routes.ts +316 -0
- package/src/runtime/routes/migration-routes.ts +21 -24
- package/src/runtime/routes/plugins-routes.ts +337 -0
- package/src/runtime/routes/rename-conversation-routes.ts +6 -2
- package/src/runtime/routes/secret-routes.ts +25 -5
- package/src/runtime/routes/settings-routes.ts +12 -11
- package/src/runtime/routes/slack-channel-routes.ts +5 -4
- package/src/runtime/routes/workspace-routes.ts +25 -10
- package/src/runtime/sync/resource-sync-events.ts +106 -38
- package/src/runtime/sync/sync-publisher.test.ts +49 -0
- package/src/runtime/sync/sync-publisher.ts +2 -1
- package/src/runtime/verification-outbound-actions.ts +73 -1
- package/src/telemetry/types.ts +12 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/acp/spawn.test.ts +119 -0
- package/src/tools/acp/spawn.ts +15 -2
- package/src/tools/apps/definitions.ts +2 -8
- package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
- package/src/tools/ask-question/ask-question-tool.ts +38 -45
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +70 -0
- package/src/tools/browser/browser-execution.ts +16 -3
- package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +3 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +12 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +27 -1
- package/src/tools/browser/cdp-client/factory.ts +100 -17
- package/src/tools/browser/cdp-client/local-cdp-client.ts +12 -0
- package/src/tools/browser/cdp-client/types.ts +65 -0
- package/src/tools/browser/pinned-tabs.ts +96 -40
- package/src/tools/computer-use/definitions.ts +22 -78
- package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
- package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
- package/src/tools/credentials/vault.ts +3 -9
- package/src/tools/document/document-tool.ts +59 -0
- package/src/tools/execution-target.ts +21 -23
- package/src/tools/executor.ts +6 -1
- package/src/tools/filesystem/edit.ts +3 -9
- package/src/tools/filesystem/list.ts +3 -9
- package/src/tools/filesystem/read.ts +3 -9
- package/src/tools/filesystem/write.ts +3 -9
- package/src/tools/host-filesystem/edit.ts +3 -9
- package/src/tools/host-filesystem/read.ts +3 -9
- package/src/tools/host-filesystem/transfer.ts +3 -9
- package/src/tools/host-filesystem/write.ts +3 -9
- package/src/tools/host-terminal/host-shell.ts +3 -9
- package/src/tools/mcp/mcp-tool-factory.ts +1 -8
- package/src/tools/memory/register.test.ts +1 -1
- package/src/tools/memory/register.ts +4 -9
- package/src/tools/network/web-fetch.ts +3 -9
- package/src/tools/network/web-search.ts +25 -32
- package/src/tools/registry.ts +7 -23
- package/src/tools/schema-transforms.ts +1 -1
- package/src/tools/skills/execute.ts +3 -9
- package/src/tools/skills/load.ts +3 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -8
- package/src/tools/subagent/notify-parent.ts +3 -9
- package/src/tools/system/request-permission.ts +3 -9
- package/src/tools/terminal/shell.ts +3 -9
- package/src/tools/tool-defaults.ts +94 -0
- package/src/tools/types.ts +27 -98
- package/src/tools/ui-surface/definitions.ts +6 -22
- package/src/usage/pricing.ts +23 -0
- package/src/usage/types.ts +12 -0
- package/src/util/logger.ts +16 -7
- package/src/util/platform.ts +7 -2
- package/src/util/sqlite3-runtime.ts +65 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
- package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
- package/src/__tests__/message-complete-display-id.test.ts +0 -175
- package/src/daemon/query-complexity-router.ts +0 -75
- package/src/prompts/cache-boundary.ts +0 -8
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory v3 — selection gate.
|
|
3
|
+
*
|
|
4
|
+
* The gate is the final step of one retrieval pass. After the scouts, the tree
|
|
5
|
+
* walk, the edge expansion, and the sticky carry-over have each contributed
|
|
6
|
+
* candidate page slugs, the gate makes one capable LLM call over the *unioned*
|
|
7
|
+
* candidate set and decides:
|
|
8
|
+
*
|
|
9
|
+
* - **ready** — finalize the selection and inject for the next reply, or
|
|
10
|
+
* - **more** — the candidates don't yet cover the turn; emit follow-up
|
|
11
|
+
* questions that seed the next pass. These questions are the gate's own
|
|
12
|
+
* *generated* queries (a refined sub-question), NOT a replay of the
|
|
13
|
+
* original user message — the loop feeds them back to the scouts/tree on
|
|
14
|
+
* the next iteration.
|
|
15
|
+
*
|
|
16
|
+
* The gate also returns the final ordered `selectedSlugs` (the order the model
|
|
17
|
+
* returned, with sticky slugs guaranteed present). Sticky pages are never
|
|
18
|
+
* dropped: they were injected on a prior turn and removing them mid-conversation
|
|
19
|
+
* would silently amnesia the assistant, so we union them back in even when the
|
|
20
|
+
* model omits them.
|
|
21
|
+
*
|
|
22
|
+
* Scope — brief generation is deferred. The full v3 design pairs the selection
|
|
23
|
+
* with a ~1000-token voice brief, but that brief is only consumed when v3 is
|
|
24
|
+
* actually injected (a later cutover). In shadow mode the harness injects v2
|
|
25
|
+
* and only compares selections, so this module produces the selection +
|
|
26
|
+
* `GateDecision` only — matching what the harness trace already models. The
|
|
27
|
+
* brief-generation seam is marked below; do not build voice synthesis here.
|
|
28
|
+
*
|
|
29
|
+
* Fail-safe. If no provider is configured or the provider call errors/returns
|
|
30
|
+
* an unusable response, the gate fails *open*: it returns
|
|
31
|
+
* `decision: { decision: "ready" }` and selects every candidate. A retrieval
|
|
32
|
+
* loop that can't reach the model should still inject what it found rather than
|
|
33
|
+
* inject nothing.
|
|
34
|
+
*
|
|
35
|
+
* This module is currently unwired — a later PR composes it into the loop.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { z } from "zod";
|
|
39
|
+
|
|
40
|
+
import {
|
|
41
|
+
extractToolUse,
|
|
42
|
+
getConfiguredProvider,
|
|
43
|
+
} from "../../providers/provider-send-message.js";
|
|
44
|
+
import type {
|
|
45
|
+
Message,
|
|
46
|
+
Provider,
|
|
47
|
+
ToolDefinition,
|
|
48
|
+
} from "../../providers/types.js";
|
|
49
|
+
import { getLogger } from "../../util/logger.js";
|
|
50
|
+
import type { RetrievalInput } from "../v2/harness/retriever.js";
|
|
51
|
+
import type { GateDecision } from "../v2/harness/trace.js";
|
|
52
|
+
import type { LlmCallSink } from "./llm-capture.js";
|
|
53
|
+
import { renderConversationContext } from "./prompt-context.js";
|
|
54
|
+
import {
|
|
55
|
+
GATE_SYSTEM_PROMPT,
|
|
56
|
+
resolveV3SystemPrompt,
|
|
57
|
+
} from "./prompts/system-prompts.js";
|
|
58
|
+
|
|
59
|
+
const log = getLogger("memory-v3-gate");
|
|
60
|
+
|
|
61
|
+
/** Tool name forced via `tool_choice`. Shared constant so tests can match it. */
|
|
62
|
+
const GATE_TOOL_NAME = "decide_selection";
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Arguments to one gate invocation.
|
|
66
|
+
*
|
|
67
|
+
* `candidates` is the accumulated candidate set for this pass — the union of
|
|
68
|
+
* scouts-kept, tree pages, edge-pulled, and sticky slugs. `sticky` is the
|
|
69
|
+
* subset that was injected on a prior turn and must survive: it is always a
|
|
70
|
+
* subset of `candidates` in practice, but the gate unions it back into both
|
|
71
|
+
* the prompt and the final selection defensively.
|
|
72
|
+
*/
|
|
73
|
+
export interface RunGateArgs {
|
|
74
|
+
input: RetrievalInput;
|
|
75
|
+
candidates: Set<string>;
|
|
76
|
+
sticky: Set<string>;
|
|
77
|
+
passNumber: number;
|
|
78
|
+
/**
|
|
79
|
+
* Per-candidate one-line summaries, keyed by slug. When present, candidates
|
|
80
|
+
* are rendered to the model as `slug — summary` so the gate can judge
|
|
81
|
+
* relevance on page content rather than the slug alone. Missing entries fall
|
|
82
|
+
* back to the bare slug; the forced tool's `selected_slugs` enum stays
|
|
83
|
+
* slug-only. The loop passes this only when `memory.v3.gateCandidateSummaries`
|
|
84
|
+
* is set.
|
|
85
|
+
*/
|
|
86
|
+
summaryBySlug?: ReadonlyMap<string, string>;
|
|
87
|
+
/** Optional debug sink — emits one record for the gate's LLM call. */
|
|
88
|
+
capture?: LlmCallSink;
|
|
89
|
+
/**
|
|
90
|
+
* Provider override seam for tests. Production leaves this unset and the
|
|
91
|
+
* gate resolves `getConfiguredProvider("memoryV3Gate")`. `null` is distinct
|
|
92
|
+
* from `undefined`: passing `null` simulates "no provider configured" and
|
|
93
|
+
* exercises the fail-safe path without resolving the real registry.
|
|
94
|
+
*/
|
|
95
|
+
provider?: Provider | null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface RunGateResult {
|
|
99
|
+
decision: GateDecision;
|
|
100
|
+
/** Final page slugs in the model's returned order; sticky guaranteed present. */
|
|
101
|
+
selectedSlugs: string[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Build the forced tool definition. `selected_slugs` is the ordered final
|
|
106
|
+
* selection; `decision` is the ready/more verdict; `questions` carries the
|
|
107
|
+
* generated follow-up queries on "more" (ignored on "ready"). Mirrors the
|
|
108
|
+
* forced-tool pattern of v2's `select_pages_to_inject`.
|
|
109
|
+
*/
|
|
110
|
+
function buildGateTool(candidateSlugs: readonly string[]): ToolDefinition {
|
|
111
|
+
return {
|
|
112
|
+
name: GATE_TOOL_NAME,
|
|
113
|
+
description:
|
|
114
|
+
"Decide whether the accumulated candidate pages are sufficient to answer " +
|
|
115
|
+
"the next turn. Return decision='ready' with the final ordered selection " +
|
|
116
|
+
"when the candidates cover the turn; return decision='more' with one or " +
|
|
117
|
+
"more generated follow-up questions (NOT the original message) to seed " +
|
|
118
|
+
"another retrieval pass when coverage is incomplete.",
|
|
119
|
+
input_schema: {
|
|
120
|
+
type: "object",
|
|
121
|
+
properties: {
|
|
122
|
+
decision: { type: "string", enum: ["ready", "more"] },
|
|
123
|
+
selected_slugs: {
|
|
124
|
+
type: "array",
|
|
125
|
+
items: { type: "string", enum: [...candidateSlugs] },
|
|
126
|
+
description:
|
|
127
|
+
"Final ordered page slugs to inject. Each candidate is listed as " +
|
|
128
|
+
"`slug — summary` when summaries are available; return only the slug " +
|
|
129
|
+
"(left of the em-dash), and only from the candidate set. Prefer keeping " +
|
|
130
|
+
"a plausibly-relevant page over dropping it; for a list / 'all of X' / " +
|
|
131
|
+
"breadth request, include every candidate that plausibly applies rather " +
|
|
132
|
+
"than trimming to the most prominent few.",
|
|
133
|
+
},
|
|
134
|
+
questions: {
|
|
135
|
+
type: "array",
|
|
136
|
+
items: { type: "string" },
|
|
137
|
+
description:
|
|
138
|
+
"When decision='more', the generated follow-up questions seeding the next pass.",
|
|
139
|
+
},
|
|
140
|
+
reasoning: {
|
|
141
|
+
type: "string",
|
|
142
|
+
description:
|
|
143
|
+
"One short sentence: why this ready/more verdict, and which " +
|
|
144
|
+
"candidates were kept or dropped.",
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
required: ["decision"],
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const GateToolResultSchema = z.object({
|
|
153
|
+
decision: z.enum(["ready", "more"]),
|
|
154
|
+
selected_slugs: z.array(z.string()).optional(),
|
|
155
|
+
questions: z.array(z.string()).optional(),
|
|
156
|
+
reasoning: z.string().optional(),
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Render the candidate list for the prompt. With summaries available each line
|
|
161
|
+
* is `slug — summary` so the model can judge relevance on content; without them
|
|
162
|
+
* it falls back to the bare slug. The slug (left of the em-dash) is what the
|
|
163
|
+
* `selected_slugs` enum constrains, so the model always answers in slugs.
|
|
164
|
+
*/
|
|
165
|
+
function renderCandidateLines(
|
|
166
|
+
slugs: readonly string[],
|
|
167
|
+
summaryBySlug: ReadonlyMap<string, string> | undefined,
|
|
168
|
+
): string {
|
|
169
|
+
return slugs
|
|
170
|
+
.map((slug) => {
|
|
171
|
+
const summary = summaryBySlug?.get(slug);
|
|
172
|
+
return summary ? `${slug} — ${summary}` : slug;
|
|
173
|
+
})
|
|
174
|
+
.join("\n");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Order a slug selection: keep the model's returned order, restricted to the
|
|
179
|
+
* candidate set, then append any sticky slugs the model omitted (sticky is
|
|
180
|
+
* never dropped). De-duplicates while preserving first-seen order.
|
|
181
|
+
*/
|
|
182
|
+
function orderSelection(
|
|
183
|
+
modelSlugs: readonly string[],
|
|
184
|
+
candidates: Set<string>,
|
|
185
|
+
sticky: Set<string>,
|
|
186
|
+
): string[] {
|
|
187
|
+
const seen = new Set<string>();
|
|
188
|
+
const out: string[] = [];
|
|
189
|
+
for (const slug of modelSlugs) {
|
|
190
|
+
if (!candidates.has(slug)) continue; // model can only pick from candidates
|
|
191
|
+
if (seen.has(slug)) continue;
|
|
192
|
+
seen.add(slug);
|
|
193
|
+
out.push(slug);
|
|
194
|
+
}
|
|
195
|
+
for (const slug of sticky) {
|
|
196
|
+
if (seen.has(slug)) continue;
|
|
197
|
+
seen.add(slug);
|
|
198
|
+
out.push(slug);
|
|
199
|
+
}
|
|
200
|
+
return out;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Fail-safe result: inject every candidate and declare the pass ready. Used
|
|
205
|
+
* when the provider is unavailable or the call cannot produce a usable
|
|
206
|
+
* decision. Ordering puts sticky last via `orderSelection` with an empty
|
|
207
|
+
* model selection, so candidates come first then any sticky not already in
|
|
208
|
+
* the set.
|
|
209
|
+
*/
|
|
210
|
+
function failSafe(candidates: Set<string>, sticky: Set<string>): RunGateResult {
|
|
211
|
+
return {
|
|
212
|
+
decision: { decision: "ready" },
|
|
213
|
+
selectedSlugs: orderSelection([...candidates], candidates, sticky),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Run the gate for one pass.
|
|
219
|
+
*
|
|
220
|
+
* Makes one forced-tool LLM call over the candidate set and maps the result to
|
|
221
|
+
* a `GateDecision` plus the final ordered selection. Sticky slugs are always
|
|
222
|
+
* present in the selection. Any failure (no provider, provider throw, missing
|
|
223
|
+
* tool_use, schema mismatch) falls back to selecting all candidates with a
|
|
224
|
+
* "ready" decision.
|
|
225
|
+
*/
|
|
226
|
+
export async function runGate(args: RunGateArgs): Promise<RunGateResult> {
|
|
227
|
+
const { input, candidates, sticky, passNumber } = args;
|
|
228
|
+
|
|
229
|
+
const candidateSlugs = [...candidates];
|
|
230
|
+
|
|
231
|
+
// Resolve the provider. A `provider` key in args (including explicit `null`)
|
|
232
|
+
// takes precedence so tests inject a stub; production omits it and resolves
|
|
233
|
+
// the configured `memoryV3Gate` call site.
|
|
234
|
+
const provider =
|
|
235
|
+
args.provider !== undefined
|
|
236
|
+
? args.provider
|
|
237
|
+
: await getConfiguredProvider("memoryV3Gate");
|
|
238
|
+
|
|
239
|
+
if (!provider) {
|
|
240
|
+
log.warn("memoryV3Gate provider unavailable; gate failing open (ready)");
|
|
241
|
+
return failSafe(candidates, sticky);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const systemPrompt = resolveV3SystemPrompt(
|
|
245
|
+
GATE_SYSTEM_PROMPT,
|
|
246
|
+
input.config.memory?.v3?.prompts?.gate,
|
|
247
|
+
input.workspaceDir,
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
const stickySlugs = [...sticky];
|
|
251
|
+
const userMsg: Message = {
|
|
252
|
+
role: "user",
|
|
253
|
+
content: [
|
|
254
|
+
{
|
|
255
|
+
type: "text",
|
|
256
|
+
text: renderConversationContext(input),
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
type: "text",
|
|
260
|
+
text:
|
|
261
|
+
`<pass_number>${passNumber}</pass_number>\n\n` +
|
|
262
|
+
`<sticky_slugs>\n${stickySlugs.join("\n")}\n</sticky_slugs>\n\n` +
|
|
263
|
+
`<candidates>\n` +
|
|
264
|
+
`${renderCandidateLines(candidateSlugs, args.summaryBySlug)}\n` +
|
|
265
|
+
`</candidates>`,
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const gateTool = buildGateTool(candidateSlugs);
|
|
271
|
+
|
|
272
|
+
const startedAt = Date.now();
|
|
273
|
+
let response;
|
|
274
|
+
try {
|
|
275
|
+
response = await provider.sendMessage([userMsg], [gateTool], systemPrompt, {
|
|
276
|
+
config: {
|
|
277
|
+
callSite: "memoryV3Gate" as const,
|
|
278
|
+
tool_choice: { type: "tool" as const, name: GATE_TOOL_NAME },
|
|
279
|
+
},
|
|
280
|
+
...(input.signal ? { signal: input.signal } : {}),
|
|
281
|
+
});
|
|
282
|
+
} catch (err) {
|
|
283
|
+
log.warn({ err }, "Gate provider call threw; failing open (ready)");
|
|
284
|
+
return failSafe(candidates, sticky);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
args.capture?.({
|
|
288
|
+
lane: "gate",
|
|
289
|
+
callSite: "memoryV3Gate",
|
|
290
|
+
request: { systemPrompt, messages: [userMsg], tools: [gateTool] },
|
|
291
|
+
response,
|
|
292
|
+
ms: Date.now() - startedAt,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const toolBlock = extractToolUse(response);
|
|
296
|
+
if (!toolBlock || toolBlock.name !== GATE_TOOL_NAME) {
|
|
297
|
+
log.warn(
|
|
298
|
+
{ stopReason: response.stopReason },
|
|
299
|
+
"Gate model returned no decide_selection tool_use; failing open (ready)",
|
|
300
|
+
);
|
|
301
|
+
return failSafe(candidates, sticky);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const parsed = GateToolResultSchema.safeParse(toolBlock.input);
|
|
305
|
+
if (!parsed.success) {
|
|
306
|
+
log.warn(
|
|
307
|
+
{ error: parsed.error.message },
|
|
308
|
+
"Gate tool input did not match schema; failing open (ready)",
|
|
309
|
+
);
|
|
310
|
+
return failSafe(candidates, sticky);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const selectedSlugs = orderSelection(
|
|
314
|
+
parsed.data.selected_slugs ?? [],
|
|
315
|
+
candidates,
|
|
316
|
+
sticky,
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
if (parsed.data.decision === "more") {
|
|
320
|
+
const questions = (parsed.data.questions ?? []).filter(
|
|
321
|
+
(q) => q.trim().length > 0,
|
|
322
|
+
);
|
|
323
|
+
const decision: GateDecision =
|
|
324
|
+
questions.length > 0
|
|
325
|
+
? { decision: "more", questions }
|
|
326
|
+
: { decision: "more" };
|
|
327
|
+
return { decision, selectedSlugs };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// brief generation lands at cutover (P5) — shadow mode injects v2, so this
|
|
331
|
+
// gate produces only the selection + decision. Do NOT synthesize a voice
|
|
332
|
+
// brief here.
|
|
333
|
+
return { decision: { decision: "ready" }, selectedSlugs };
|
|
334
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory v3 — Compositional index rendering.
|
|
3
|
+
*
|
|
4
|
+
* A v3 tree node has no stored "index" of its own. Instead, a parent node's
|
|
5
|
+
* index is *composed at read time* by concatenating one description line per
|
|
6
|
+
* child (a `node:` sub-node's summary or a `page:` leaf's summary) plus a thin
|
|
7
|
+
* `Routing hints:` trailer drawn from the node's own frontmatter. Nothing here
|
|
8
|
+
* is persisted — the block is generated fresh every time a descent prompt needs
|
|
9
|
+
* it, so it always reflects the current state of the children.
|
|
10
|
+
*
|
|
11
|
+
* {@link composeNodeIndex} is a **pure function** over an already-built
|
|
12
|
+
* {@link TreeIndex} (from `tree-index.ts`) and {@link PageIndex} (from
|
|
13
|
+
* `../v2/page-index.ts`). It does no I/O: the tree walk / driver PR is
|
|
14
|
+
* responsible for building those indices and feeding them in.
|
|
15
|
+
*
|
|
16
|
+
* Resolution rules, per child ref of `nodeId` (in authored order):
|
|
17
|
+
* - `kind:"node"` → look up the child in `tree.nodes`; emit
|
|
18
|
+
* `"[node:<id>] <summary>"` where summary is the child's
|
|
19
|
+
* `frontmatter.summary` if non-empty, else the first non-empty line of its
|
|
20
|
+
* body. A node with neither still emits its header (`"[node:<id>]"`).
|
|
21
|
+
* - `kind:"page"` → look up `pages.bySlug.get(ref)`; emit
|
|
22
|
+
* `"[page:<slug>] <entry.summary>"`.
|
|
23
|
+
* - Either lookup missing → emit nothing for that ref. Reporting dangling
|
|
24
|
+
* refs is validation's job, not this renderer's.
|
|
25
|
+
*
|
|
26
|
+
* The node's own `routing_hints` (when present) are appended last under a
|
|
27
|
+
* `Routing hints:` trailer. A node with no resolvable children and no routing
|
|
28
|
+
* hints composes to the empty string.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import type { PageIndex } from "../v2/page-index.js";
|
|
32
|
+
import type { TreeIndex } from "./tree-index.js";
|
|
33
|
+
import type { TreeNode } from "./types.js";
|
|
34
|
+
|
|
35
|
+
/** Trailer label introducing a node's own routing hints. */
|
|
36
|
+
const ROUTING_HINTS_LABEL = "Routing hints:";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a node's display summary: its frontmatter `summary` if non-empty,
|
|
40
|
+
* otherwise the first non-empty line of its body, otherwise the empty string.
|
|
41
|
+
* Whitespace is trimmed so a leading blank line in the body never wins.
|
|
42
|
+
*/
|
|
43
|
+
function nodeSummary(node: TreeNode): string {
|
|
44
|
+
const summary = node.frontmatter.summary?.trim();
|
|
45
|
+
if (summary) return summary;
|
|
46
|
+
for (const line of node.body.split("\n")) {
|
|
47
|
+
const trimmed = line.trim();
|
|
48
|
+
if (trimmed) return trimmed;
|
|
49
|
+
}
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Render one child ref into its index line, or `null` when the ref's target is
|
|
55
|
+
* absent from the supplied indices (validation owns reporting those).
|
|
56
|
+
*
|
|
57
|
+
* A resolvable `node:` child always yields a line — its header (`[node:<id>]`)
|
|
58
|
+
* with a trailing summary when one exists. A `page:` child yields
|
|
59
|
+
* `[page:<slug>] <summary>`; the v2 page index already truncates `summary`.
|
|
60
|
+
*/
|
|
61
|
+
function renderChild(
|
|
62
|
+
kind: "page" | "node",
|
|
63
|
+
ref: string,
|
|
64
|
+
tree: TreeIndex,
|
|
65
|
+
pages: PageIndex,
|
|
66
|
+
): string | null {
|
|
67
|
+
if (kind === "node") {
|
|
68
|
+
const child = tree.nodes.get(ref);
|
|
69
|
+
if (!child) return null;
|
|
70
|
+
const summary = nodeSummary(child);
|
|
71
|
+
return summary ? `[node:${ref}] ${summary}` : `[node:${ref}]`;
|
|
72
|
+
}
|
|
73
|
+
const entry = pages.bySlug.get(ref);
|
|
74
|
+
if (!entry) return null;
|
|
75
|
+
return `[page:${ref}] ${entry.summary}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Compose the prompt-ready index block for `nodeId` from its children's
|
|
80
|
+
* descriptions plus the node's own routing hints.
|
|
81
|
+
*
|
|
82
|
+
* Pure and deterministic: children are emitted in authored order (the order
|
|
83
|
+
* `tree.childrenByNode` preserves from the node's `children` frontmatter), refs
|
|
84
|
+
* whose targets are absent are silently skipped, and the node's
|
|
85
|
+
* `routing_hints` (if present) are appended under a {@link ROUTING_HINTS_LABEL}
|
|
86
|
+
* trailer. A node with no entry in `childrenByNode`, no resolvable children,
|
|
87
|
+
* and no routing hints composes to the empty string.
|
|
88
|
+
*
|
|
89
|
+
* The result is a plain string with no trailing newline, suitable to drop
|
|
90
|
+
* directly into an LLM descent prompt.
|
|
91
|
+
*/
|
|
92
|
+
export function composeNodeIndex(
|
|
93
|
+
nodeId: string,
|
|
94
|
+
tree: TreeIndex,
|
|
95
|
+
pages: PageIndex,
|
|
96
|
+
): string {
|
|
97
|
+
const blocks: string[] = [];
|
|
98
|
+
|
|
99
|
+
const childRefs = tree.childrenByNode.get(nodeId) ?? [];
|
|
100
|
+
for (const { kind, ref } of childRefs) {
|
|
101
|
+
const line = renderChild(kind, ref, tree, pages);
|
|
102
|
+
if (line !== null) blocks.push(line);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const routingHints = tree.nodes
|
|
106
|
+
.get(nodeId)
|
|
107
|
+
?.frontmatter.routing_hints?.trim();
|
|
108
|
+
if (routingHints) {
|
|
109
|
+
blocks.push(`${ROUTING_HINTS_LABEL} ${routingHints}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return blocks.join("\n");
|
|
113
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory v3 — LLM-call capture types.
|
|
3
|
+
*
|
|
4
|
+
* A debugging seam: when a sink is threaded into the retrieval loop, every v3
|
|
5
|
+
* LLM call (dense filter, each tree-walk descender call, the gate) emits one
|
|
6
|
+
* {@link LlmCallRecord} carrying the full input it sent and the raw response it
|
|
7
|
+
* got back. The `simulate` path collects these so an operator can inspect what
|
|
8
|
+
* each call actually saw and returned. Nothing here is persisted, and the sink
|
|
9
|
+
* is `undefined` on every non-simulate path, so production pays zero cost.
|
|
10
|
+
*
|
|
11
|
+
* Leaf module: it imports only provider types, so the lanes and the loop can
|
|
12
|
+
* depend on it without a cycle.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type {
|
|
16
|
+
Message,
|
|
17
|
+
ProviderResponse,
|
|
18
|
+
ToolDefinition,
|
|
19
|
+
} from "../../providers/types.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* One captured v3 LLM call — its full input (system prompt, messages, tool
|
|
23
|
+
* schema) and raw output (provider response). `pass` is the 1-based retrieval
|
|
24
|
+
* pass the call ran in; `node` is set only for the tree-walk descender (the
|
|
25
|
+
* node whose composed index it judged). `ms` is the provider round-trip time.
|
|
26
|
+
*/
|
|
27
|
+
export interface LlmCallRecord {
|
|
28
|
+
pass: number;
|
|
29
|
+
lane: "filter" | "descent" | "gate";
|
|
30
|
+
callSite: string;
|
|
31
|
+
node?: string;
|
|
32
|
+
request: {
|
|
33
|
+
systemPrompt: string;
|
|
34
|
+
messages: Message[];
|
|
35
|
+
tools: ToolDefinition[];
|
|
36
|
+
};
|
|
37
|
+
response: ProviderResponse;
|
|
38
|
+
ms: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The sink a lane calls to emit a capture record. Lanes don't know their pass
|
|
43
|
+
* number, so they emit without `pass` and the loop wraps the sink to stamp the
|
|
44
|
+
* current pass. `undefined` on every non-capturing path (the common case).
|
|
45
|
+
*/
|
|
46
|
+
export type LlmCallSink = (record: Omit<LlmCallRecord, "pass">) => void;
|