@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,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text rendering for `assistant memory v2 compare` — turns a
|
|
3
|
+
* `ComparisonReport` (the daemon route's response) into a human-readable
|
|
4
|
+
* summary and a per-turn breakdown.
|
|
5
|
+
*
|
|
6
|
+
* Lives CLI-side because formatting for the terminal is a presentation concern
|
|
7
|
+
* (mirroring the inline rendering in the `simulate` subcommand). It imports
|
|
8
|
+
* only the response *type* from the daemon — `cli/no-daemon-internals` permits
|
|
9
|
+
* type-only imports but forbids pulling in daemon runtime modules.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ComparisonReport } from "../../memory/v2/harness/runner.js";
|
|
13
|
+
|
|
14
|
+
function sortedKs(report: ComparisonReport): number[] {
|
|
15
|
+
return [...report.ks].sort((a, b) => a - b);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Sum per-lane hit counts across every scored turn for one retriever. */
|
|
19
|
+
function laneTotals(
|
|
20
|
+
report: ComparisonReport,
|
|
21
|
+
retrieverName: string,
|
|
22
|
+
): Record<string, number> {
|
|
23
|
+
const totals: Record<string, number> = {};
|
|
24
|
+
for (const turn of report.perTurn) {
|
|
25
|
+
const ev = turn.byRetriever[retrieverName];
|
|
26
|
+
if (!ev) continue;
|
|
27
|
+
for (const [lane, count] of Object.entries(ev.hitsByLane)) {
|
|
28
|
+
totals[lane] = (totals[lane] ?? 0) + count;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return totals;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function renderComparisonReport(report: ComparisonReport): string {
|
|
35
|
+
const ks = sortedKs(report);
|
|
36
|
+
const lines: string[] = [
|
|
37
|
+
"Memory Retrieval Comparison",
|
|
38
|
+
"===========================",
|
|
39
|
+
`Turns: considered ${report.turnsConsidered}, scored ${report.turnsScored}, skipped ${report.turnsSkipped}`,
|
|
40
|
+
`recall@k cutoffs: ${ks.join(", ")}`,
|
|
41
|
+
"",
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
if (report.turnsScored === 0) {
|
|
45
|
+
lines.push(
|
|
46
|
+
"No turns scored — nothing to report.",
|
|
47
|
+
"(Every sampled turn was skipped — e.g. input reconstruction failed, or none matched the filter.)",
|
|
48
|
+
);
|
|
49
|
+
return lines.join("\n");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
for (const retriever of report.retrievers) {
|
|
53
|
+
lines.push(`Retriever: ${retriever.name}`);
|
|
54
|
+
for (const k of ks) {
|
|
55
|
+
const value = retriever.aggregate.meanRecallAtK[k];
|
|
56
|
+
lines.push(
|
|
57
|
+
` recall@${k}: ${value !== undefined ? value.toFixed(3) : "n/a"}`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
lines.push(
|
|
61
|
+
` failures: ${(retriever.aggregate.failureRate * 100).toFixed(1)}%`,
|
|
62
|
+
);
|
|
63
|
+
if (retriever.aggregate.meanCostUsd !== undefined) {
|
|
64
|
+
lines.push(` mean cost: $${retriever.aggregate.meanCostUsd.toFixed(4)}`);
|
|
65
|
+
}
|
|
66
|
+
const lanes = Object.entries(laneTotals(report, retriever.name)).sort(
|
|
67
|
+
(a, b) => b[1] - a[1],
|
|
68
|
+
);
|
|
69
|
+
const laneStr = lanes.map(([lane, count]) => `${lane}=${count}`).join(", ");
|
|
70
|
+
lines.push(` hits by lane: ${laneStr.length > 0 ? laneStr : "(none)"}`);
|
|
71
|
+
lines.push("");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return lines.join("\n").trimEnd();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function renderTurnTrace(
|
|
78
|
+
report: ComparisonReport,
|
|
79
|
+
conversationId: string,
|
|
80
|
+
turn: number,
|
|
81
|
+
): string {
|
|
82
|
+
const entry = report.perTurn.find(
|
|
83
|
+
(t) => t.conversationId === conversationId && t.turn === turn,
|
|
84
|
+
);
|
|
85
|
+
if (!entry) {
|
|
86
|
+
return (
|
|
87
|
+
`Turn ${conversationId}:${turn} not found in the report. Only scored ` +
|
|
88
|
+
`turns appear here — skipped turns (e.g. failed input reconstruction) ` +
|
|
89
|
+
`are excluded. turnsScored=${report.turnsScored}, ` +
|
|
90
|
+
`turnsSkipped=${report.turnsSkipped}.`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const ks = sortedKs(report);
|
|
95
|
+
const header = `Turn ${conversationId}:${turn}`;
|
|
96
|
+
const lines: string[] = [header, "-".repeat(header.length)];
|
|
97
|
+
|
|
98
|
+
for (const [name, ev] of Object.entries(entry.byRetriever)) {
|
|
99
|
+
const recallStr = ks
|
|
100
|
+
.map((k) => `${k}:${(ev.recallAtK[k] ?? 0).toFixed(3)}`)
|
|
101
|
+
.join(" ");
|
|
102
|
+
lines.push(
|
|
103
|
+
`Retriever: ${name}`,
|
|
104
|
+
` selected (${ev.selected.length}): ${ev.selected.join(", ") || "(none)"}`,
|
|
105
|
+
` hits (${ev.hits.length}): ${ev.hits.join(", ") || "(none)"}`,
|
|
106
|
+
` misses (${ev.misses.length}): ${ev.misses.join(", ") || "(none)"}`,
|
|
107
|
+
` extras (${ev.extras.length}): ${ev.extras.join(", ") || "(none)"}`,
|
|
108
|
+
` recall@k: ${recallStr}`,
|
|
109
|
+
" (no descent trace — tier-based retriever)",
|
|
110
|
+
"",
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return lines.join("\n").trimEnd();
|
|
115
|
+
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import type { Command } from "commander";
|
|
20
20
|
|
|
21
21
|
import { cliIpcCall } from "../../ipc/cli-client.js";
|
|
22
|
+
import type { ComparisonReport } from "../../memory/v2/harness/runner.js";
|
|
22
23
|
import type {
|
|
23
24
|
MemoryV2BackfillOp,
|
|
24
25
|
MemoryV2BackfillResult,
|
|
@@ -29,6 +30,10 @@ import type {
|
|
|
29
30
|
} from "../../runtime/routes/memory-v2-routes.js";
|
|
30
31
|
import { registerCommand } from "../lib/register-command.js";
|
|
31
32
|
import { log } from "../logger.js";
|
|
33
|
+
import {
|
|
34
|
+
renderComparisonReport,
|
|
35
|
+
renderTurnTrace,
|
|
36
|
+
} from "./memory-v2-compare-render.js";
|
|
32
37
|
|
|
33
38
|
// ---------------------------------------------------------------------------
|
|
34
39
|
// Helpers
|
|
@@ -469,7 +474,15 @@ Examples:
|
|
|
469
474
|
"memory_v2_simulate_router",
|
|
470
475
|
{
|
|
471
476
|
body: {
|
|
472
|
-
query
|
|
477
|
+
// The CLI flag is still named `--query` for backwards
|
|
478
|
+
// compatibility. It becomes the just-arrived
|
|
479
|
+
// `userMessage` of a single (empty assistant, user)
|
|
480
|
+
// pair — i.e. a first-turn scenario. nowText uses
|
|
481
|
+
// the server default (live NOW.md), preserving the
|
|
482
|
+
// existing single-turn CLI semantics.
|
|
483
|
+
recentTurnPairs: [
|
|
484
|
+
{ assistantMessage: "", userMessage: opts.query },
|
|
485
|
+
],
|
|
473
486
|
...(configOverrides ? { configOverrides } : {}),
|
|
474
487
|
},
|
|
475
488
|
},
|
|
@@ -560,6 +573,168 @@ Examples:
|
|
|
560
573
|
}
|
|
561
574
|
},
|
|
562
575
|
);
|
|
576
|
+
|
|
577
|
+
// ── compare ─────────────────────────────────────────────────────────
|
|
578
|
+
|
|
579
|
+
v2.command("compare")
|
|
580
|
+
.description(
|
|
581
|
+
"Compare retrievers against the router's logged picks over a sample of real turns (read-only)",
|
|
582
|
+
)
|
|
583
|
+
.option(
|
|
584
|
+
"--limit <n>",
|
|
585
|
+
"How many historical turns to sample (default 20). Each re-runs the router = one LLM call.",
|
|
586
|
+
)
|
|
587
|
+
.option(
|
|
588
|
+
"--strategy <recent|random>",
|
|
589
|
+
"Sampling strategy over historical turns (default recent)",
|
|
590
|
+
)
|
|
591
|
+
.option(
|
|
592
|
+
"--k <list>",
|
|
593
|
+
"Comma-separated recall@k cutoffs (default 5,10,25,50)",
|
|
594
|
+
)
|
|
595
|
+
.option(
|
|
596
|
+
"--conversation <id>",
|
|
597
|
+
"Restrict to a conversation id (repeatable)",
|
|
598
|
+
(val: string, acc: string[]) => {
|
|
599
|
+
acc.push(val);
|
|
600
|
+
return acc;
|
|
601
|
+
},
|
|
602
|
+
[] as string[],
|
|
603
|
+
)
|
|
604
|
+
.option(
|
|
605
|
+
"--trace <conversationId:turn>",
|
|
606
|
+
"Print the per-retriever breakdown for one scored turn",
|
|
607
|
+
)
|
|
608
|
+
.option(
|
|
609
|
+
"--include-not-injected",
|
|
610
|
+
"Also count router picks cut by the injection cap as ground truth",
|
|
611
|
+
)
|
|
612
|
+
.option("--json", "Emit raw JSON instead of a formatted report")
|
|
613
|
+
.addHelpText(
|
|
614
|
+
"after",
|
|
615
|
+
`
|
|
616
|
+
Runs the comparison harness read-only: samples historical 'router'-mode turns
|
|
617
|
+
from memory_v2_activation_logs, reconstructs each turn's inputs, re-runs each
|
|
618
|
+
retriever, and scores selections against the logged picks (recall@k). NO writes.
|
|
619
|
+
|
|
620
|
+
Cost: each scored turn re-runs the router (one LLM call), so --limit is the
|
|
621
|
+
cost knob — start small. Today the only retriever is the router itself, so this
|
|
622
|
+
is the harness self-test (router graded against its own logged picks); the gap
|
|
623
|
+
from 1.0 is input-reconstruction drift (NOW.md / config moved since the turn).
|
|
624
|
+
|
|
625
|
+
Examples:
|
|
626
|
+
$ assistant memory v2 compare --limit 20
|
|
627
|
+
$ assistant memory v2 compare --limit 50 --strategy random --k 5,10,25
|
|
628
|
+
$ assistant memory v2 compare --limit 20 --trace conv-abc:7
|
|
629
|
+
$ assistant memory v2 compare --limit 20 --json | jq '.retrievers[0].aggregate'`,
|
|
630
|
+
)
|
|
631
|
+
.action(
|
|
632
|
+
async (opts: {
|
|
633
|
+
limit?: string;
|
|
634
|
+
strategy?: string;
|
|
635
|
+
k?: string;
|
|
636
|
+
conversation?: string[];
|
|
637
|
+
trace?: string;
|
|
638
|
+
includeNotInjected?: boolean;
|
|
639
|
+
json?: boolean;
|
|
640
|
+
}) => {
|
|
641
|
+
let limit: number | undefined;
|
|
642
|
+
if (opts.limit !== undefined) {
|
|
643
|
+
const parsed = Number(opts.limit);
|
|
644
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
645
|
+
log.error(
|
|
646
|
+
`--limit must be a positive integer (got "${opts.limit}")`,
|
|
647
|
+
);
|
|
648
|
+
process.exitCode = 1;
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
limit = parsed;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (
|
|
655
|
+
opts.strategy !== undefined &&
|
|
656
|
+
opts.strategy !== "recent" &&
|
|
657
|
+
opts.strategy !== "random"
|
|
658
|
+
) {
|
|
659
|
+
log.error(
|
|
660
|
+
`--strategy must be "recent" or "random" (got "${opts.strategy}")`,
|
|
661
|
+
);
|
|
662
|
+
process.exitCode = 1;
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
let ks: number[] | undefined;
|
|
667
|
+
if (opts.k !== undefined) {
|
|
668
|
+
ks = opts.k.split(",").map((s) => Number(s.trim()));
|
|
669
|
+
if (ks.some((k) => !Number.isInteger(k) || k < 1)) {
|
|
670
|
+
log.error(
|
|
671
|
+
`--k must be a comma-separated list of positive integers (got "${opts.k}")`,
|
|
672
|
+
);
|
|
673
|
+
process.exitCode = 1;
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const conversationIds =
|
|
679
|
+
opts.conversation && opts.conversation.length > 0
|
|
680
|
+
? opts.conversation
|
|
681
|
+
: undefined;
|
|
682
|
+
|
|
683
|
+
const result = await cliIpcCall<ComparisonReport>(
|
|
684
|
+
"memory_v2_compare_retrievers",
|
|
685
|
+
{
|
|
686
|
+
body: {
|
|
687
|
+
...(limit !== undefined ? { limit } : {}),
|
|
688
|
+
...(opts.strategy !== undefined
|
|
689
|
+
? { strategy: opts.strategy }
|
|
690
|
+
: {}),
|
|
691
|
+
...(ks !== undefined ? { ks } : {}),
|
|
692
|
+
...(conversationIds !== undefined ? { conversationIds } : {}),
|
|
693
|
+
...(opts.includeNotInjected === true
|
|
694
|
+
? { includeNotInjected: true }
|
|
695
|
+
: {}),
|
|
696
|
+
},
|
|
697
|
+
},
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
if (!result.ok) {
|
|
701
|
+
log.error(result.error ?? "Failed to compare retrievers");
|
|
702
|
+
process.exitCode = 1;
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const payload = result.result!;
|
|
707
|
+
|
|
708
|
+
if (opts.json === true) {
|
|
709
|
+
log.info(JSON.stringify(payload, null, 2));
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
log.info(renderComparisonReport(payload));
|
|
714
|
+
|
|
715
|
+
if (opts.trace !== undefined) {
|
|
716
|
+
const sep = opts.trace.lastIndexOf(":");
|
|
717
|
+
if (sep <= 0 || sep === opts.trace.length - 1) {
|
|
718
|
+
log.error(
|
|
719
|
+
`--trace must be "<conversationId>:<turn>" (got "${opts.trace}")`,
|
|
720
|
+
);
|
|
721
|
+
process.exitCode = 1;
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
const conversationId = opts.trace.slice(0, sep);
|
|
725
|
+
const turn = Number(opts.trace.slice(sep + 1));
|
|
726
|
+
if (!Number.isInteger(turn)) {
|
|
727
|
+
log.error(
|
|
728
|
+
`--trace turn must be an integer (got "${opts.trace.slice(sep + 1)}")`,
|
|
729
|
+
);
|
|
730
|
+
process.exitCode = 1;
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
log.info("");
|
|
734
|
+
log.info(renderTurnTrace(payload, conversationId, turn));
|
|
735
|
+
}
|
|
736
|
+
},
|
|
737
|
+
);
|
|
563
738
|
},
|
|
564
739
|
});
|
|
565
740
|
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text rendering for `assistant memory v3 validate` and `... tree`.
|
|
3
|
+
*
|
|
4
|
+
* Both functions are pure presentation: they take the daemon route's response
|
|
5
|
+
* shape and return a terminal-ready string. They live CLI-side (mirroring
|
|
6
|
+
* `memory-v2-compare-render.ts`) and import only the response *types* from the
|
|
7
|
+
* daemon route — `cli/no-daemon-internals` permits type-only imports but
|
|
8
|
+
* forbids pulling in daemon runtime modules.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
DescentPass,
|
|
13
|
+
LlmCallRecord,
|
|
14
|
+
MemoryV3SimulateResult,
|
|
15
|
+
MemoryV3TreeResult,
|
|
16
|
+
MemoryV3ValidateResult,
|
|
17
|
+
} from "../../runtime/routes/memory-v3-routes.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Render a {@link MemoryV3ValidateResult} into a counts summary plus the
|
|
21
|
+
* offending ids for each non-empty category. Categories with zero entries
|
|
22
|
+
* print `none` so a clean tree reads at a glance.
|
|
23
|
+
*/
|
|
24
|
+
export function renderValidationReport(report: MemoryV3ValidateResult): string {
|
|
25
|
+
const lines: string[] = [
|
|
26
|
+
"Memory v3 Tree Validation",
|
|
27
|
+
"=========================",
|
|
28
|
+
`Dangling child refs: ${report.danglingChildRefCount || "none"}`,
|
|
29
|
+
];
|
|
30
|
+
for (const d of report.danglingChildRefs) {
|
|
31
|
+
lines.push(` - ${d.node} → ${d.kind}:${d.ref}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
lines.push(`Orphan pages: ${report.orphanPageCount || "none"}`);
|
|
35
|
+
for (const slug of report.orphanPages) {
|
|
36
|
+
lines.push(` - ${slug}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
lines.push(`Cycles: ${report.cycleCount || "none"}`);
|
|
40
|
+
for (const c of report.cycles) {
|
|
41
|
+
lines.push(` - ${c.from} → ${c.to}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
lines.push(`Stale index: ${report.staleIndexCount || "none"}`);
|
|
45
|
+
for (const s of report.staleIndex) {
|
|
46
|
+
lines.push(` - ${s.node} (older than child ${s.child})`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
lines.push(
|
|
50
|
+
`Unknown edge targets: ${report.unknownEdgeTargetCount || "none"}`,
|
|
51
|
+
);
|
|
52
|
+
for (const e of report.unknownEdgeTargets) {
|
|
53
|
+
lines.push(` - ${e.from} → ${e.to}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return lines.join("\n");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Whether the validation report has any defect in any category. The CLI uses
|
|
61
|
+
* this to set a non-zero exit code so `validate` is scriptable as a check.
|
|
62
|
+
*/
|
|
63
|
+
export function reportHasDefects(report: MemoryV3ValidateResult): boolean {
|
|
64
|
+
return (
|
|
65
|
+
report.danglingChildRefCount > 0 ||
|
|
66
|
+
report.orphanPageCount > 0 ||
|
|
67
|
+
report.cycleCount > 0 ||
|
|
68
|
+
report.staleIndexCount > 0 ||
|
|
69
|
+
report.unknownEdgeTargetCount > 0
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Render a {@link MemoryV3TreeResult} as an indented tree rooted at `view.root`,
|
|
75
|
+
* descending `node:` children depth-first. A node reached more than once
|
|
76
|
+
* (shared DAG sub-node) is printed once with a `(↑ …)` re-entry marker rather
|
|
77
|
+
* than re-expanded, which also bounds output when the structure contains a
|
|
78
|
+
* cycle. `page:` children are printed as leaves under their parent node.
|
|
79
|
+
*/
|
|
80
|
+
export function renderTree(view: MemoryV3TreeResult): string {
|
|
81
|
+
const childrenById = new Map<string, MemoryV3TreeResult["nodes"][number]>();
|
|
82
|
+
for (const node of view.nodes) {
|
|
83
|
+
childrenById.set(node.id, node);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const lines: string[] = [];
|
|
87
|
+
const expanded = new Set<string>();
|
|
88
|
+
|
|
89
|
+
const walk = (nodeId: string, depth: number): void => {
|
|
90
|
+
const indent = " ".repeat(depth);
|
|
91
|
+
const node = childrenById.get(nodeId);
|
|
92
|
+
|
|
93
|
+
if (!node) {
|
|
94
|
+
lines.push(`${indent}node:${nodeId} (missing)`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (expanded.has(nodeId)) {
|
|
99
|
+
// Shared DAG sub-node (or a cycle's back-edge): print the reference but
|
|
100
|
+
// do not re-expand, so output stays finite and the re-entry is visible.
|
|
101
|
+
lines.push(`${indent}node:${nodeId} (↑ already shown)`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
expanded.add(nodeId);
|
|
105
|
+
lines.push(`${indent}node:${nodeId}`);
|
|
106
|
+
|
|
107
|
+
for (const child of node.children) {
|
|
108
|
+
if (child.kind === "page") {
|
|
109
|
+
lines.push(`${" ".repeat(depth + 1)}page:${child.ref}`);
|
|
110
|
+
} else {
|
|
111
|
+
walk(child.ref, depth + 1);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
walk(view.root, 0);
|
|
117
|
+
|
|
118
|
+
if (lines.length === 0) {
|
|
119
|
+
lines.push("(empty tree)");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Surface nodes that exist on disk but were never reached from the root —
|
|
123
|
+
// they would otherwise be invisible in a root-anchored print.
|
|
124
|
+
const unreached = view.nodes
|
|
125
|
+
.map((n) => n.id)
|
|
126
|
+
.filter((id) => !expanded.has(id))
|
|
127
|
+
.sort();
|
|
128
|
+
if (unreached.length > 0) {
|
|
129
|
+
lines.push("", `Unreachable nodes (${unreached.length}):`);
|
|
130
|
+
for (const id of unreached) {
|
|
131
|
+
lines.push(` - node:${id}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return lines.join("\n");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Canonical print order for the loop's provenance lanes. */
|
|
139
|
+
const SIMULATE_LANE_ORDER = ["hot", "sparse", "dense", "tree", "edge"] as const;
|
|
140
|
+
|
|
141
|
+
/** Render the effective lane toggles as a one-line `on` / restricted summary. */
|
|
142
|
+
function renderEffectiveLanes(
|
|
143
|
+
lanes: MemoryV3SimulateResult["effectiveConfig"]["lanes"],
|
|
144
|
+
): string {
|
|
145
|
+
const on = Object.entries(lanes)
|
|
146
|
+
.filter(([, enabled]) => enabled)
|
|
147
|
+
.map(([name]) => name);
|
|
148
|
+
const off = Object.entries(lanes)
|
|
149
|
+
.filter(([, enabled]) => !enabled)
|
|
150
|
+
.map(([name]) => name);
|
|
151
|
+
if (off.length === 0) return on.join(", ");
|
|
152
|
+
return `${on.join(", ")} (off: ${off.join(", ")})`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Render one pass's scout / tree / edge / gate breakdown into `lines`. */
|
|
156
|
+
function renderPass(pass: DescentPass, lines: string[]): void {
|
|
157
|
+
lines.push(`Pass ${pass.passNumber}`);
|
|
158
|
+
|
|
159
|
+
if (pass.scouts && pass.scouts.length > 0) {
|
|
160
|
+
const summary = pass.scouts
|
|
161
|
+
.map((s) => `${s.lane}=${s.slugs.length}`)
|
|
162
|
+
.join(" ");
|
|
163
|
+
lines.push(` scouts: ${summary}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (pass.treeLevels && pass.treeLevels.length > 0) {
|
|
167
|
+
lines.push(` tree: ${pass.treeLevels.length} level(s)`);
|
|
168
|
+
for (const level of pass.treeLevels) {
|
|
169
|
+
const node = level.node === "" ? "[root]" : level.node;
|
|
170
|
+
lines.push(
|
|
171
|
+
` ${node}: considered ${level.considered.length}, descended ${level.descended.length}, skipped ${level.skipped.length}`,
|
|
172
|
+
);
|
|
173
|
+
if (level.descended.length > 0) {
|
|
174
|
+
lines.push(` → ${level.descended.join(", ")}`);
|
|
175
|
+
}
|
|
176
|
+
if (level.reasoning.trim().length > 0) {
|
|
177
|
+
lines.push(` reason: ${level.reasoning.trim()}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (pass.edgeExpansions && pass.edgeExpansions.length > 0) {
|
|
183
|
+
const pulled = pass.edgeExpansions.reduce((n, e) => n + e.pulled.length, 0);
|
|
184
|
+
lines.push(
|
|
185
|
+
` edges: ${pass.edgeExpansions.length} seed(s) expanded, ${pulled} pulled`,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (pass.gate) {
|
|
190
|
+
lines.push(` gate: ${pass.gate.decision}`);
|
|
191
|
+
for (const q of pass.gate.questions ?? []) {
|
|
192
|
+
lines.push(` ? ${q}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Render a {@link MemoryV3SimulateResult} into a query echo, effective config,
|
|
199
|
+
* per-pass descent breakdown, and the final selection grouped by provenance
|
|
200
|
+
* lane (in fanout order). Mirrors the grouped layout of `memory v2 simulate`.
|
|
201
|
+
*/
|
|
202
|
+
export function renderSimulation(result: MemoryV3SimulateResult): string {
|
|
203
|
+
const lines: string[] = [
|
|
204
|
+
"Memory v3 Retrieval Simulation",
|
|
205
|
+
"==============================",
|
|
206
|
+
`Query: ${JSON.stringify(result.query)}`,
|
|
207
|
+
"",
|
|
208
|
+
"Config (effective):",
|
|
209
|
+
` passCap: ${result.effectiveConfig.passCap}`,
|
|
210
|
+
` lanes: ${renderEffectiveLanes(result.effectiveConfig.lanes)}`,
|
|
211
|
+
"",
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
const passes = result.trace.passes;
|
|
215
|
+
lines.push(`Passes: ${passes.length || "none"}`);
|
|
216
|
+
for (const pass of passes) {
|
|
217
|
+
lines.push("");
|
|
218
|
+
renderPass(pass, lines);
|
|
219
|
+
}
|
|
220
|
+
lines.push("");
|
|
221
|
+
|
|
222
|
+
lines.push(`Selected: ${result.selectedSlugs.length} page(s)`);
|
|
223
|
+
const grouped = new Map<string, string[]>();
|
|
224
|
+
for (const slug of result.selectedSlugs) {
|
|
225
|
+
const lane = result.sourceBySlug[slug] ?? "unknown";
|
|
226
|
+
const bucket = grouped.get(lane) ?? [];
|
|
227
|
+
bucket.push(slug);
|
|
228
|
+
grouped.set(lane, bucket);
|
|
229
|
+
}
|
|
230
|
+
const laneRank = (lane: string): number => {
|
|
231
|
+
const i = SIMULATE_LANE_ORDER.indexOf(
|
|
232
|
+
lane as (typeof SIMULATE_LANE_ORDER)[number],
|
|
233
|
+
);
|
|
234
|
+
return i === -1 ? SIMULATE_LANE_ORDER.length : i;
|
|
235
|
+
};
|
|
236
|
+
const lanes = [...grouped.keys()].sort((a, b) => laneRank(a) - laneRank(b));
|
|
237
|
+
for (const lane of lanes) {
|
|
238
|
+
const slugs = grouped.get(lane)!;
|
|
239
|
+
lines.push(` ${lane} (${slugs.length})`);
|
|
240
|
+
for (const slug of slugs) {
|
|
241
|
+
lines.push(` - ${slug}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (result.cost.ms !== undefined) {
|
|
246
|
+
lines.push("", `Cost: ${result.cost.ms} ms`);
|
|
247
|
+
}
|
|
248
|
+
if (result.failureReason) {
|
|
249
|
+
lines.push(`Failure: ${result.failureReason}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return lines.join("\n");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ── LLM-call capture ────────────────────────────────────────────────────────
|
|
256
|
+
|
|
257
|
+
type CapturedMessage = LlmCallRecord["request"]["messages"][number];
|
|
258
|
+
type CapturedBlock = LlmCallRecord["response"]["content"][number];
|
|
259
|
+
|
|
260
|
+
/** Concatenate the text blocks of a message (non-text blocks are ignored). */
|
|
261
|
+
function messageText(message: CapturedMessage): string {
|
|
262
|
+
return message.content
|
|
263
|
+
.filter(
|
|
264
|
+
(b): b is Extract<CapturedBlock, { type: "text" }> => b.type === "text",
|
|
265
|
+
)
|
|
266
|
+
.map((b) => b.text)
|
|
267
|
+
.join("\n");
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** The forced-tool block from a response, if the model returned one. */
|
|
271
|
+
function toolUseOf(
|
|
272
|
+
call: LlmCallRecord,
|
|
273
|
+
): Extract<CapturedBlock, { type: "tool_use" }> | undefined {
|
|
274
|
+
return call.response.content.find(
|
|
275
|
+
(b): b is Extract<CapturedBlock, { type: "tool_use" }> =>
|
|
276
|
+
b.type === "tool_use",
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function truncate(s: string, max: number): string {
|
|
281
|
+
const oneLine = s.replace(/\s+/g, " ").trim();
|
|
282
|
+
return oneLine.length > max ? `${oneLine.slice(0, max - 1)}…` : oneLine;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function indent(s: string, by = " "): string {
|
|
286
|
+
return s
|
|
287
|
+
.split("\n")
|
|
288
|
+
.map((line) => by + line)
|
|
289
|
+
.join("\n");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Render the captured v3 LLM calls. Compact (default): one line per call —
|
|
294
|
+
* pass/lane/node, input size, round-trip ms, and the forced-tool summary. Full
|
|
295
|
+
* (`--show-llm`): the system prompt, every message, the tool names, and the
|
|
296
|
+
* tool_use input for each call. Grouped in call order (which is pass order).
|
|
297
|
+
*/
|
|
298
|
+
export function renderLlmCalls(
|
|
299
|
+
calls: readonly LlmCallRecord[],
|
|
300
|
+
opts: { full: boolean },
|
|
301
|
+
): string {
|
|
302
|
+
if (calls.length === 0) return "LLM calls: none";
|
|
303
|
+
|
|
304
|
+
const lines: string[] = [`LLM calls (${calls.length}):`];
|
|
305
|
+
for (const call of calls) {
|
|
306
|
+
const label =
|
|
307
|
+
`pass${call.pass} · ${call.lane}` +
|
|
308
|
+
(call.node ? ` · node=${call.node}` : "");
|
|
309
|
+
const inputChars =
|
|
310
|
+
call.request.systemPrompt.length +
|
|
311
|
+
call.request.messages.reduce((n, m) => n + messageText(m).length, 0);
|
|
312
|
+
const tool = toolUseOf(call);
|
|
313
|
+
const toolSummary = tool
|
|
314
|
+
? `${tool.name}(${truncate(JSON.stringify(tool.input), 80)})`
|
|
315
|
+
: `no tool_use (stop=${call.response.stopReason})`;
|
|
316
|
+
|
|
317
|
+
if (!opts.full) {
|
|
318
|
+
lines.push(
|
|
319
|
+
` ${label} · in ${inputChars}c · ${call.ms}ms · ${truncate(toolSummary, 100)}`,
|
|
320
|
+
);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
lines.push("", `── ${label} (${call.callSite}, ${call.ms}ms) ──`);
|
|
325
|
+
lines.push("system:", indent(call.request.systemPrompt));
|
|
326
|
+
lines.push("messages:");
|
|
327
|
+
for (const message of call.request.messages) {
|
|
328
|
+
lines.push(
|
|
329
|
+
indent(`[${message.role}]`),
|
|
330
|
+
indent(messageText(message), " "),
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
lines.push(`tools: ${call.request.tools.map((t) => t.name).join(", ")}`);
|
|
334
|
+
lines.push("output:");
|
|
335
|
+
lines.push(
|
|
336
|
+
indent(
|
|
337
|
+
tool
|
|
338
|
+
? `${tool.name} ${JSON.stringify(tool.input, null, 2)}`
|
|
339
|
+
: `(no tool_use, stop=${call.response.stopReason})`,
|
|
340
|
+
),
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
return lines.join("\n");
|
|
344
|
+
}
|