create-walle 0.9.21 → 0.9.23
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/README.md +27 -5
- package/package.json +2 -2
- package/template/CLAUDE.md +2 -2
- package/template/LICENSE +1 -1
- package/template/bin/ctm-dev-cleanup.js +24 -3
- package/template/bin/ctm-launch.sh +13 -0
- package/template/bin/dev.sh +156 -18
- package/template/bin/node-bin.sh +84 -0
- package/template/bin/pin-node.sh +51 -0
- package/template/claude-task-manager/api-prompts.js +1203 -182
- package/template/claude-task-manager/api-reviews.js +109 -15
- package/template/claude-task-manager/approval-agent.js +1360 -280
- package/template/claude-task-manager/bin/restart-ctm.sh +64 -23
- package/template/claude-task-manager/bin/storage-migration-supervisor.js +338 -0
- package/template/claude-task-manager/db.js +4417 -295
- package/template/claude-task-manager/docs/app-update-refresh-protocol.md +69 -0
- package/template/claude-task-manager/docs/approval-ai-refinement.md +138 -0
- package/template/claude-task-manager/docs/approval-rescue-loop.md +74 -0
- package/template/claude-task-manager/docs/codex-operational-warning-health.md +107 -0
- package/template/claude-task-manager/docs/codex-resume-state-guard-design.md +17 -12
- package/template/claude-task-manager/docs/codex-terminal-render-controller-handoff.md +311 -0
- package/template/claude-task-manager/docs/coding-agent-hooks-architecture.md +418 -0
- package/template/claude-task-manager/docs/conversation-import-freshness.md +20 -0
- package/template/claude-task-manager/docs/google-workspace-auth-health.md +77 -0
- package/template/claude-task-manager/docs/image-paste-ux.md +13 -0
- package/template/claude-task-manager/docs/ipad-web-preview.md +88 -0
- package/template/claude-task-manager/docs/main-loop-offload-architecture.md +66 -0
- package/template/claude-task-manager/docs/microsoft-dev-tunnel-phone-access-design.md +274 -519
- package/template/claude-task-manager/docs/mobile-live-streaming.md +27 -5
- package/template/claude-task-manager/docs/mobile-remote-submission-lifecycle.md +69 -0
- package/template/claude-task-manager/docs/phone-access-design.md +53 -15
- package/template/claude-task-manager/docs/phone-passkey-identity.md +122 -0
- package/template/claude-task-manager/docs/phone-setup.md +3 -0
- package/template/claude-task-manager/docs/prompt-editing-tree-design.md +25 -1
- package/template/claude-task-manager/docs/remote-desktop-access-design.md +268 -0
- package/template/claude-task-manager/docs/restart-lifecycle-architecture.md +95 -0
- package/template/claude-task-manager/docs/runtime-work-control-plane.md +53 -0
- package/template/claude-task-manager/docs/session-interactive-wait-surfaces.md +38 -0
- package/template/claude-task-manager/docs/session-needs-you-dismissal.md +84 -0
- package/template/claude-task-manager/docs/session-render-state-management-design.md +91 -3
- package/template/claude-task-manager/docs/session-standup-command-center-design.md +25 -1
- package/template/claude-task-manager/docs/session-title-authority.md +32 -0
- package/template/claude-task-manager/docs/session-workspace-binding.md +33 -0
- package/template/claude-task-manager/docs/skill-intent-resolution-design.md +72 -0
- package/template/claude-task-manager/docs/walle-mcp-supervisor-health.md +86 -0
- package/template/claude-task-manager/docs/walle-relay-phone-access-design.md +24 -15
- package/template/claude-task-manager/docs/walle-session-history-hydration.md +114 -0
- package/template/claude-task-manager/docs/walle-session-input-queue.md +104 -0
- package/template/claude-task-manager/docs/walle-session-model-catalog.md +90 -0
- package/template/claude-task-manager/docs/walle-session-model-preferences.md +15 -6
- package/template/claude-task-manager/git-utils.js +897 -27
- package/template/claude-task-manager/lib/agent-capabilities.js +33 -0
- package/template/claude-task-manager/lib/agent-cli-cache.js +37 -7
- package/template/claude-task-manager/lib/agent-hooks-installer.js +26 -2
- package/template/claude-task-manager/lib/agent-presets.js +17 -1
- package/template/claude-task-manager/lib/all-sessions-query.js +108 -0
- package/template/claude-task-manager/lib/approval-ai-refinement.js +488 -0
- package/template/claude-task-manager/lib/approval-self-adapt.js +168 -0
- package/template/claude-task-manager/lib/async-semaphore.js +44 -0
- package/template/claude-task-manager/lib/auth-context.js +5 -0
- package/template/claude-task-manager/lib/auth-rate-limit.js +47 -4
- package/template/claude-task-manager/lib/auth-rules.js +29 -2
- package/template/claude-task-manager/lib/auto-approval-verifier.js +129 -16
- package/template/claude-task-manager/lib/background-llm.js +144 -17
- package/template/claude-task-manager/lib/branch-inventory.js +212 -0
- package/template/claude-task-manager/lib/claude-desktop-sessions.js +15 -3
- package/template/claude-task-manager/lib/coalesce-sync-frames.js +151 -0
- package/template/claude-task-manager/lib/codex-launch-health.js +762 -0
- package/template/claude-task-manager/lib/codex-transcript-pager.js +51 -0
- package/template/claude-task-manager/lib/codex-zst.js +124 -0
- package/template/claude-task-manager/lib/coding-agent-models.js +233 -30
- package/template/claude-task-manager/lib/connection-health.js +232 -0
- package/template/claude-task-manager/lib/conversation-blob-parser.js +42 -0
- package/template/claude-task-manager/lib/conversation-tail-merge.js +89 -26
- package/template/claude-task-manager/lib/ctm-session-context-api.js +39 -10
- package/template/claude-task-manager/lib/cursor-conversation-store.js +354 -0
- package/template/claude-task-manager/lib/db-owner-worker-client.js +315 -0
- package/template/claude-task-manager/lib/document-review.js +141 -6
- package/template/claude-task-manager/lib/escalation-review.js +152 -0
- package/template/claude-task-manager/lib/graceful-shutdown.js +159 -0
- package/template/claude-task-manager/lib/headless-term-service.js +678 -0
- package/template/claude-task-manager/lib/heavy-worker-fallback.js +38 -0
- package/template/claude-task-manager/lib/jsonl-conversation-parser.js +542 -0
- package/template/claude-task-manager/lib/jsonl-range-reader.js +112 -0
- package/template/claude-task-manager/lib/main-db-census.js +216 -0
- package/template/claude-task-manager/lib/message-pagination.js +106 -4
- package/template/claude-task-manager/lib/microsoft-dev-tunnel-setup.js +750 -26
- package/template/claude-task-manager/lib/mobile-auth-api.js +274 -7
- package/template/claude-task-manager/lib/mobile-auth-store.js +592 -10
- package/template/claude-task-manager/lib/mobile-notification-dispatcher.js +15 -0
- package/template/claude-task-manager/lib/model-overview-brain-fallback.js +311 -0
- package/template/claude-task-manager/lib/model-overview-cache.js +141 -0
- package/template/claude-task-manager/lib/models-health-routing-notice.js +126 -0
- package/template/claude-task-manager/lib/node-pin-guard.js +93 -0
- package/template/claude-task-manager/lib/perf-tracker.js +242 -6
- package/template/claude-task-manager/lib/permission-match.js +76 -0
- package/template/claude-task-manager/lib/permission-sync.js +133 -20
- package/template/claude-task-manager/lib/process-title.js +35 -0
- package/template/claude-task-manager/lib/prompt-executions-query.js +25 -0
- package/template/claude-task-manager/lib/prompt-index-disk-cache.js +44 -0
- package/template/claude-task-manager/lib/prompt-intent.js +132 -0
- package/template/claude-task-manager/lib/provider-user-context.js +34 -0
- package/template/claude-task-manager/lib/read-pool-client.js +313 -0
- package/template/claude-task-manager/lib/readpool-breaker.js +31 -0
- package/template/claude-task-manager/lib/recent-sessions-breaker.js +12 -0
- package/template/claude-task-manager/lib/remote-feedback-client.js +72 -0
- package/template/claude-task-manager/lib/remote-relay-protocol.js +37 -4
- package/template/claude-task-manager/lib/remote-relay-store.js +159 -0
- package/template/claude-task-manager/lib/remote-submission-observer.js +278 -0
- package/template/claude-task-manager/lib/restart-guard.js +109 -0
- package/template/claude-task-manager/lib/restore-interruption-detector.js +439 -0
- package/template/claude-task-manager/lib/restore-policy.js +13 -0
- package/template/claude-task-manager/lib/restore-resume-batch.js +74 -0
- package/template/claude-task-manager/lib/restore-runtime.js +68 -0
- package/template/claude-task-manager/lib/restore-storm.js +34 -0
- package/template/claude-task-manager/lib/resume-cwd.js +36 -0
- package/template/claude-task-manager/lib/resume-preflight.js +313 -0
- package/template/claude-task-manager/lib/runtime-work-registry.js +444 -0
- package/template/claude-task-manager/lib/sanitize-openai-auth.js +31 -0
- package/template/claude-task-manager/lib/scheduler.js +21 -1
- package/template/claude-task-manager/lib/scrollback-snapshot-store.js +159 -0
- package/template/claude-task-manager/lib/serial-task-queue.js +64 -0
- package/template/claude-task-manager/lib/server-listeners.js +239 -0
- package/template/claude-task-manager/lib/session-capture.js +42 -7
- package/template/claude-task-manager/lib/session-content-backfill.js +131 -0
- package/template/claude-task-manager/lib/session-history.js +388 -43
- package/template/claude-task-manager/lib/session-host-manager.js +287 -0
- package/template/claude-task-manager/lib/session-image-refs.js +209 -0
- package/template/claude-task-manager/lib/session-jobs.js +399 -59
- package/template/claude-task-manager/lib/session-prompt-index.js +137 -0
- package/template/claude-task-manager/lib/session-restore.js +53 -0
- package/template/claude-task-manager/lib/session-standup.js +123 -23
- package/template/claude-task-manager/lib/session-state-bus.js +14 -0
- package/template/claude-task-manager/lib/session-stream.js +64 -16
- package/template/claude-task-manager/lib/session-timeline-summary.js +260 -0
- package/template/claude-task-manager/lib/session-token-usage.js +494 -0
- package/template/claude-task-manager/lib/session-workspace-binding.js +356 -0
- package/template/claude-task-manager/lib/setup-network-config.js +9 -0
- package/template/claude-task-manager/lib/size-cap.js +45 -0
- package/template/claude-task-manager/lib/size-cap.test.js +62 -0
- package/template/claude-task-manager/lib/skill-autocomplete.js +180 -1
- package/template/claude-task-manager/lib/skill-intent-resolver.js +304 -0
- package/template/claude-task-manager/lib/sqlite-driver.js +19 -3
- package/template/claude-task-manager/lib/standup-attention.js +7 -3
- package/template/claude-task-manager/lib/status-authority.js +39 -0
- package/template/claude-task-manager/lib/status-hooks.js +4 -0
- package/template/claude-task-manager/lib/storage-migration.js +235 -0
- package/template/claude-task-manager/lib/structured-capture.js +298 -0
- package/template/claude-task-manager/lib/sync-io-census.js +163 -0
- package/template/claude-task-manager/lib/tailscale-setup.js +6 -0
- package/template/claude-task-manager/lib/terminal-activity-evidence.js +33 -0
- package/template/claude-task-manager/lib/terminal-choice.js +364 -0
- package/template/claude-task-manager/lib/terminal-control-sanitize.js +17 -0
- package/template/claude-task-manager/lib/terminal-fingerprint.js +48 -0
- package/template/claude-task-manager/lib/terminal-output-flush.js +84 -0
- package/template/claude-task-manager/lib/timeline-order.js +122 -0
- package/template/claude-task-manager/lib/transcript-store.js +348 -43
- package/template/claude-task-manager/lib/transport-security.js +84 -1
- package/template/claude-task-manager/lib/wait-state.js +184 -0
- package/template/claude-task-manager/lib/walle-client.js +47 -5
- package/template/claude-task-manager/lib/walle-ctm-history.js +564 -4
- package/template/claude-task-manager/lib/walle-external-actions.js +135 -16
- package/template/claude-task-manager/lib/walle-history-hydration.js +46 -0
- package/template/claude-task-manager/lib/walle-native-health.js +403 -0
- package/template/claude-task-manager/lib/walle-repair.js +701 -0
- package/template/claude-task-manager/lib/walle-session-cache.js +109 -0
- package/template/claude-task-manager/lib/walle-session-context.js +57 -21
- package/template/claude-task-manager/lib/walle-session-model-catalog.js +34 -0
- package/template/claude-task-manager/lib/walle-supervisor.js +539 -63
- package/template/claude-task-manager/lib/walle-transcript.js +52 -0
- package/template/claude-task-manager/lib/worktree-active-sync.js +11 -7
- package/template/claude-task-manager/lib/worktree-cwd.js +32 -1
- package/template/claude-task-manager/package.json +1 -1
- package/template/claude-task-manager/prompt-harvest.js +89 -66
- package/template/claude-task-manager/providers/claude-code.js +51 -3
- package/template/claude-task-manager/providers/cursor.js +140 -45
- package/template/claude-task-manager/public/css/reviews.css +551 -61
- package/template/claude-task-manager/public/css/setup.css +191 -0
- package/template/claude-task-manager/public/css/walle-session.css +865 -10
- package/template/claude-task-manager/public/css/walle.css +154 -0
- package/template/claude-task-manager/public/designs/ai-providers-consolidation-v2.html +830 -0
- package/template/claude-task-manager/public/index.html +18516 -2058
- package/template/claude-task-manager/public/ipad.html +363 -0
- package/template/claude-task-manager/public/js/document-review-links.js +301 -0
- package/template/claude-task-manager/public/js/image-normalize.js +69 -36
- package/template/claude-task-manager/public/js/message-renderer.js +1265 -77
- package/template/claude-task-manager/public/js/prompts.js +66 -29
- package/template/claude-task-manager/public/js/reviews.js +901 -133
- package/template/claude-task-manager/public/js/session-activity-utils.js +11 -1
- package/template/claude-task-manager/public/js/session-search-utils.js +94 -10
- package/template/claude-task-manager/public/js/session-status-precedence.js +23 -5
- package/template/claude-task-manager/public/js/setup.js +1273 -176
- package/template/claude-task-manager/public/js/stream-view.js +691 -73
- package/template/claude-task-manager/public/js/terminal-reconciler.js +210 -0
- package/template/claude-task-manager/public/js/walle-session.js +2455 -158
- package/template/claude-task-manager/public/js/walle.js +455 -28
- package/template/claude-task-manager/public/m/app.css +2909 -262
- package/template/claude-task-manager/public/m/app.js +6601 -398
- package/template/claude-task-manager/public/m/claim.html +224 -17
- package/template/claude-task-manager/public/m/index.html +117 -21
- package/template/claude-task-manager/public/m/sw.js +3 -1
- package/template/claude-task-manager/public/manifest.json +2 -2
- package/template/claude-task-manager/public/prompts.html +30 -14
- package/template/claude-task-manager/queue-engine.js +507 -28
- package/template/claude-task-manager/scripts/repair-claude-session-images.js +27 -8
- package/template/claude-task-manager/server.js +14341 -2197
- package/template/claude-task-manager/session-integrity.js +160 -18
- package/template/claude-task-manager/session-search-ranking.js +1 -0
- package/template/claude-task-manager/session-utils.js +25 -5
- package/template/claude-task-manager/workers/approval-blocklist.js +96 -6
- package/template/claude-task-manager/workers/approval-widget-validator.js +14 -8
- package/template/claude-task-manager/workers/conversation-import-worker.js +11 -50
- package/template/claude-task-manager/workers/db-owner-worker.js +386 -0
- package/template/claude-task-manager/workers/harvest-worker.js +9 -55
- package/template/claude-task-manager/workers/headless-term-worker.js +9 -530
- package/template/claude-task-manager/workers/read-pool-worker.js +387 -0
- package/template/claude-task-manager/workers/scrollback-worker.js +11 -72
- package/template/claude-task-manager/workers/session-host-process.js +146 -0
- package/template/claude-task-manager/workers/session-integrity-worker.js +10 -54
- package/template/claude-task-manager/workers/state-detectors/base.js +18 -1
- package/template/claude-task-manager/workers/state-detectors/claude-code.js +182 -9
- package/template/claude-task-manager/workers/state-detectors/codex.js +150 -2
- package/template/claude-task-manager/workers/state-detectors/cursor.js +127 -0
- package/template/claude-task-manager/workers/state-detectors/gemini.js +21 -0
- package/template/claude-task-manager/workers/state-detectors/index.js +29 -0
- package/template/claude-task-manager/workers/state-detectors/opencode.js +103 -0
- package/template/docs/design/markdown-review-pane.md +206 -0
- package/template/docs/designs/2026-05-17-portkey-gateway-provider-ux.md +129 -38
- package/template/docs/designs/2026-05-20-mobile-worktree-finish-command.md +27 -0
- package/template/docs/designs/2026-05-22-ai-configuration-consolidation.md +248 -0
- package/template/docs/designs/ai-configuration-consolidation-mock.html +812 -0
- package/template/docs/private-memory-and-pii-policy.md +69 -0
- package/template/package.json +2 -1
- package/template/scripts/check-private-data.js +201 -0
- package/template/shared/sqlite-owner-guard.js +30 -0
- package/template/shared/sqlite-owner-write-queue.js +225 -0
- package/template/shared/sqlite-storage-policy.js +111 -0
- package/template/shared/sqlite-write-lock.js +428 -0
- package/template/wall-e/agent-runners/claude-code.js +5 -0
- package/template/wall-e/agent.js +166 -22
- package/template/wall-e/api-walle.js +524 -70
- package/template/wall-e/auth/provider-flows.js +11 -1
- package/template/wall-e/bin/walle-mcp-stdio.js +341 -17
- package/template/wall-e/brain.js +1614 -141
- package/template/wall-e/chat/attachment-blocks.js +96 -0
- package/template/wall-e/chat/attachments.js +2 -1
- package/template/wall-e/chat/capability-resolver.js +7 -7
- package/template/wall-e/chat/context-messages.js +28 -0
- package/template/wall-e/chat/conversation-frame.js +630 -0
- package/template/wall-e/chat/provider-messages.js +125 -0
- package/template/wall-e/chat.js +1002 -233
- package/template/wall-e/coding/acceptance-contract.js +170 -0
- package/template/wall-e/coding/acp-adapter.js +1 -1
- package/template/wall-e/coding/agent-catalog.js +3 -0
- package/template/wall-e/coding/artifact-store.js +93 -0
- package/template/wall-e/coding/capability-router.js +120 -0
- package/template/wall-e/coding/coding-run-controller.js +423 -0
- package/template/wall-e/coding/compaction-service.js +157 -12
- package/template/wall-e/coding/frontend-verification.js +258 -0
- package/template/wall-e/coding/lifecycle-hooks.js +75 -0
- package/template/wall-e/coding/local-preview-contract.js +157 -0
- package/template/wall-e/coding/permission-service.js +57 -13
- package/template/wall-e/coding/prompt-bundle.js +19 -1
- package/template/wall-e/coding/prompt-section-registry.js +227 -0
- package/template/wall-e/coding/provider-compat.js +15 -0
- package/template/wall-e/coding/runtime-events.js +224 -0
- package/template/wall-e/coding/runtime-mode.js +3 -0
- package/template/wall-e/coding/side-git-snapshot.js +160 -4
- package/template/wall-e/coding/snapshot-service.js +143 -1
- package/template/wall-e/coding/stream-processor.js +388 -34
- package/template/wall-e/coding/task-tool.js +141 -4
- package/template/wall-e/coding/tool-execution-controller.js +365 -0
- package/template/wall-e/coding/tool-registry.js +43 -5
- package/template/wall-e/coding/user-hooks.js +217 -0
- package/template/wall-e/coding-orchestrator.js +1330 -221
- package/template/wall-e/coding-prompts.js +20 -4
- package/template/wall-e/context/context-builder.js +15 -2
- package/template/wall-e/decision/confidence.js +1 -1
- package/template/wall-e/docs/coding-acceptance-contract.md +41 -0
- package/template/wall-e/docs/external-action-controller.md +26 -6
- package/template/wall-e/docs/telemetry-lifecycle.md +8 -2
- package/template/wall-e/embeddings.js +591 -53
- package/template/wall-e/external-action-controller.js +12 -0
- package/template/wall-e/http/auth.js +1 -0
- package/template/wall-e/http/chat-api.js +46 -11
- package/template/wall-e/http/model-admin.js +836 -34
- package/template/wall-e/lib/boot-profile.js +88 -0
- package/template/wall-e/lib/event-loop-monitor.js +93 -0
- package/template/wall-e/lib/service-health.js +194 -0
- package/template/wall-e/llm/anthropic.js +130 -5
- package/template/wall-e/llm/client.js +266 -63
- package/template/wall-e/llm/default-fallback.js +382 -0
- package/template/wall-e/llm/health.js +19 -0
- package/template/wall-e/llm/message-guard.js +78 -0
- package/template/wall-e/llm/model-catalog.js +252 -1
- package/template/wall-e/llm/openai.js +26 -4
- package/template/wall-e/llm/portkey-sync.js +654 -0
- package/template/wall-e/llm/provider-error.js +30 -2
- package/template/wall-e/llm/registry.js +5 -1
- package/template/wall-e/llm/request-compat.js +67 -0
- package/template/wall-e/loops/backfill.js +79 -23
- package/template/wall-e/loops/brain-optimize.js +67 -0
- package/template/wall-e/loops/ingest.js +25 -10
- package/template/wall-e/loops/question-digest.js +160 -0
- package/template/wall-e/loops/reflect.js +6 -4
- package/template/wall-e/loops/think.js +39 -12
- package/template/wall-e/mcp-server.js +318 -36
- package/template/wall-e/memory/ctm-context-client.js +52 -14
- package/template/wall-e/memory/ctm-operational-context.js +237 -0
- package/template/wall-e/memory/ctm-prompt-executions-client.js +128 -0
- package/template/wall-e/memory/ctm-session-context.js +111 -63
- package/template/wall-e/prompts/coding/deepseek.txt +3 -0
- package/template/wall-e/prompts/coding/gemini.txt +6 -0
- package/template/wall-e/prompts/coding/gpt.txt +6 -0
- package/template/wall-e/prompts/coding/local.txt +7 -0
- package/template/wall-e/runtime/decision-hooks.js +115 -0
- package/template/wall-e/runtime/devbox-gateway.js +82 -8
- package/template/wall-e/runtime/prompt-manifest.js +86 -0
- package/template/wall-e/runtime/tool-executor.js +269 -0
- package/template/wall-e/runtime/tool-result-envelope.js +138 -0
- package/template/wall-e/runtime/transcript-projection.js +60 -0
- package/template/wall-e/runtime/walle-runtime.js +224 -0
- package/template/wall-e/scripts/db-optimize/migrate.js +162 -0
- package/template/wall-e/scripts/db-optimize/recall-eval.js +117 -0
- package/template/wall-e/server.js +15 -0
- package/template/wall-e/session-files.js +9 -0
- package/template/wall-e/skills/_bundled/google-calendar/run.js +1 -1
- package/template/wall-e/skills/_bundled/gws-workspace/run.js +1 -1
- package/template/wall-e/skills/_bundled/slack-mentions/run.js +76 -6
- package/template/wall-e/skills/claude-code-reader.js +7 -3
- package/template/wall-e/skills/script-skill-runner.js +10 -0
- package/template/wall-e/skills/skill-planner.js +38 -0
- package/template/wall-e/tools/builtin-middleware.js +19 -9
- package/template/wall-e/tools/local-tools.js +1428 -16
- package/template/wall-e/tools/permission-checker.js +73 -5
- package/template/wall-e/tools/question-manager.js +117 -7
- package/template/wall-e/training/harvester.js +12 -28
- package/template/wall-e/training/replay.js +25 -80
- package/template/website/index.html +10 -10
- package/template/wall-e/eval/ab-test.js +0 -203
- package/template/wall-e/eval/agent-runner.js +0 -772
- package/template/wall-e/eval/agent-scorer.js +0 -461
- package/template/wall-e/eval/aggregator.js +0 -414
- package/template/wall-e/eval/allowed-test-commands.js +0 -34
- package/template/wall-e/eval/benchmark-generator.js +0 -113
- package/template/wall-e/eval/benchmarks/chat-eval.json +0 -1662
- package/template/wall-e/eval/benchmarks/chat.json +0 -82
- package/template/wall-e/eval/benchmarks/coding-agent-real.json +0 -1
- package/template/wall-e/eval/benchmarks/coding-agent.json +0 -1581
- package/template/wall-e/eval/benchmarks/coding.json +0 -122
- package/template/wall-e/eval/benchmarks/memory-retrieval.json +0 -234
- package/template/wall-e/eval/benchmarks/reasoning.json +0 -82
- package/template/wall-e/eval/benchmarks/swebench-lite-30.json +0 -212
- package/template/wall-e/eval/benchmarks.js +0 -669
- package/template/wall-e/eval/cc-replay.js +0 -719
- package/template/wall-e/eval/chat-eval.js +0 -525
- package/template/wall-e/eval/check-keys.js +0 -15
- package/template/wall-e/eval/check-providers.js +0 -42
- package/template/wall-e/eval/codex-cli-baseline.js +0 -669
- package/template/wall-e/eval/coding-agent-real.js +0 -570
- package/template/wall-e/eval/context-compactor.js +0 -251
- package/template/wall-e/eval/debug-agent003.js +0 -68
- package/template/wall-e/eval/diagnostics.js +0 -216
- package/template/wall-e/eval/eval-orchestrator.js +0 -642
- package/template/wall-e/eval/evaluate.js +0 -202
- package/template/wall-e/eval/evaluator.js +0 -373
- package/template/wall-e/eval/exporter.js +0 -212
- package/template/wall-e/eval/fixtures/express-basic/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-basic/server.js +0 -115
- package/template/wall-e/eval/fixtures/express-basic/test.js +0 -83
- package/template/wall-e/eval/fixtures/express-buggy/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-buggy/server.js +0 -113
- package/template/wall-e/eval/fixtures/express-buggy/test.js +0 -83
- package/template/wall-e/eval/fixtures/express-buggy-items/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-buggy-items/server.js +0 -112
- package/template/wall-e/eval/fixtures/express-buggy-items/test.js +0 -83
- package/template/wall-e/eval/fixtures/express-buggy-search/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-buggy-search/server.js +0 -121
- package/template/wall-e/eval/fixtures/express-buggy-search/test.js +0 -83
- package/template/wall-e/eval/fixtures/express-rename-data/data.js +0 -34
- package/template/wall-e/eval/fixtures/express-rename-data/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-rename-data/server.js +0 -97
- package/template/wall-e/eval/fixtures/express-rename-data/test.js +0 -88
- package/template/wall-e/eval/fixtures/express-xss/package.json +0 -12
- package/template/wall-e/eval/fixtures/express-xss/server.js +0 -90
- package/template/wall-e/eval/fixtures/express-xss/test.js +0 -67
- package/template/wall-e/eval/fixtures/express-xss/views/profile.ejs +0 -9
- package/template/wall-e/eval/fixtures/fullstack-app/config/default.js +0 -9
- package/template/wall-e/eval/fixtures/fullstack-app/config/test.js +0 -13
- package/template/wall-e/eval/fixtures/fullstack-app/package.json +0 -11
- package/template/wall-e/eval/fixtures/fullstack-app/public/css/style.css +0 -137
- package/template/wall-e/eval/fixtures/fullstack-app/public/index.html +0 -46
- package/template/wall-e/eval/fixtures/fullstack-app/public/js/app.js +0 -121
- package/template/wall-e/eval/fixtures/fullstack-app/public/js/auth.js +0 -71
- package/template/wall-e/eval/fixtures/fullstack-app/public/js/items.js +0 -80
- package/template/wall-e/eval/fixtures/fullstack-app/public/js/users.js +0 -46
- package/template/wall-e/eval/fixtures/fullstack-app/public/login.html +0 -45
- package/template/wall-e/eval/fixtures/fullstack-app/public/register.html +0 -38
- package/template/wall-e/eval/fixtures/fullstack-app/scripts/migrate.js +0 -23
- package/template/wall-e/eval/fixtures/fullstack-app/scripts/seed.js +0 -46
- package/template/wall-e/eval/fixtures/fullstack-app/server/db.js +0 -99
- package/template/wall-e/eval/fixtures/fullstack-app/server/index.js +0 -94
- package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/auth.js +0 -19
- package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/logger.js +0 -19
- package/template/wall-e/eval/fixtures/fullstack-app/server/router.js +0 -50
- package/template/wall-e/eval/fixtures/fullstack-app/server/routes/auth.js +0 -69
- package/template/wall-e/eval/fixtures/fullstack-app/server/routes/health.js +0 -23
- package/template/wall-e/eval/fixtures/fullstack-app/server/routes/items.js +0 -88
- package/template/wall-e/eval/fixtures/fullstack-app/server/routes/users.js +0 -75
- package/template/wall-e/eval/fixtures/fullstack-app/server/test.js +0 -198
- package/template/wall-e/eval/fixtures/fullstack-app/server/utils/response.js +0 -34
- package/template/wall-e/eval/fixtures/fullstack-app/server/utils/validate.js +0 -26
- package/template/wall-e/eval/fixtures/fullstack-app/server.js +0 -8
- package/template/wall-e/eval/fixtures/fullstack-app/test.js +0 -12
- package/template/wall-e/eval/fixtures/monorepo-basic/package.json +0 -8
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/data.js +0 -58
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/middleware.js +0 -46
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/package.json +0 -8
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/routes.js +0 -64
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/server.js +0 -56
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/test.js +0 -116
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/commands.js +0 -61
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/index.js +0 -62
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/output.js +0 -43
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/package.json +0 -11
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/test.js +0 -44
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/formatters.js +0 -43
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/index.js +0 -12
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/package.json +0 -5
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/test.js +0 -55
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/validators.js +0 -29
- package/template/wall-e/eval/fixtures/monorepo-basic/test.js +0 -46
- package/template/wall-e/eval/fixtures/node-cli/index.js +0 -78
- package/template/wall-e/eval/fixtures/node-cli/package.json +0 -10
- package/template/wall-e/eval/fixtures/node-cli/test.js +0 -57
- package/template/wall-e/eval/fixtures/node-typed/package.json +0 -8
- package/template/wall-e/eval/fixtures/node-typed/src/handlers.js +0 -31
- package/template/wall-e/eval/fixtures/node-typed/src/utils.js +0 -33
- package/template/wall-e/eval/fixtures/node-typed/test.js +0 -36
- package/template/wall-e/eval/fixtures/python-flask/app.py +0 -14
- package/template/wall-e/eval/fixtures/python-flask/requirements.txt +0 -2
- package/template/wall-e/eval/fixtures/python-flask/test_app.py +0 -25
- package/template/wall-e/eval/fixtures/wall-e-subset/brain.js +0 -105
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/aggregator.js +0 -101
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/chat.json +0 -20
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/coding.json +0 -32
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks.js +0 -64
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/package.json +0 -6
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/server.js +0 -31
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/test.js +0 -18
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/utils.js +0 -34
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/runner.js +0 -104
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/scorer.js +0 -73
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/test.js +0 -134
- package/template/wall-e/eval/fixtures/wall-e-subset/llm/client.js +0 -99
- package/template/wall-e/eval/fixtures/wall-e-subset/llm/providers.js +0 -63
- package/template/wall-e/eval/fixtures/wall-e-subset/llm/test.js +0 -70
- package/template/wall-e/eval/fixtures/wall-e-subset/package.json +0 -10
- package/template/wall-e/eval/fixtures/wall-e-subset/test.js +0 -86
- package/template/wall-e/eval/harvester.js +0 -685
- package/template/wall-e/eval/head-to-head.js +0 -388
- package/template/wall-e/eval/humaneval-adapter.js +0 -321
- package/template/wall-e/eval/list-models.js +0 -31
- package/template/wall-e/eval/livecodebench-adapter.js +0 -291
- package/template/wall-e/eval/mail-integration.js +0 -443
- package/template/wall-e/eval/manifest.js +0 -186
- package/template/wall-e/eval/meta-harness/adapters/coding-agent.js +0 -57
- package/template/wall-e/eval/meta-harness/bootstrap-snapshot.js +0 -149
- package/template/wall-e/eval/meta-harness/candidate-store.js +0 -117
- package/template/wall-e/eval/meta-harness/cli.js +0 -86
- package/template/wall-e/eval/meta-harness/domain-spec.js +0 -154
- package/template/wall-e/eval/meta-harness/domains/coding-agent.domain.json +0 -84
- package/template/wall-e/eval/meta-harness/examples/env-bootstrap-candidate.js +0 -29
- package/template/wall-e/eval/meta-harness/experience-store.js +0 -174
- package/template/wall-e/eval/meta-harness/frontier.js +0 -96
- package/template/wall-e/eval/meta-harness/harness-interface.js +0 -90
- package/template/wall-e/eval/meta-harness/leakage-guard.js +0 -80
- package/template/wall-e/eval/meta-harness/optimizer.js +0 -207
- package/template/wall-e/eval/meta-harness/proposer-runner.js +0 -110
- package/template/wall-e/eval/meta-harness/reporting.js +0 -58
- package/template/wall-e/eval/meta-harness/telemetry.js +0 -27
- package/template/wall-e/eval/meta-harness/validation.js +0 -81
- package/template/wall-e/eval/promoter.js +0 -228
- package/template/wall-e/eval/provider-normalizer.js +0 -33
- package/template/wall-e/eval/replay.js +0 -395
- package/template/wall-e/eval/run-agent-benchmarks.js +0 -386
- package/template/wall-e/eval/run-codex-cli-baseline.js +0 -177
- package/template/wall-e/eval/run-coding-agent-real.js +0 -187
- package/template/wall-e/eval/run-eval.js +0 -435
- package/template/wall-e/eval/run-model-comparison.js +0 -142
- package/template/wall-e/eval/session-evaluator.js +0 -187
- package/template/wall-e/eval/session-miner.js +0 -207
- package/template/wall-e/eval/session-retrieval-benchmark.js +0 -150
- package/template/wall-e/eval/session-transcripts.js +0 -509
- package/template/wall-e/eval/shadow.js +0 -161
- package/template/wall-e/eval/swebench-adapter.js +0 -345
- package/template/wall-e/eval/swebench-docker.js +0 -192
- package/template/wall-e/eval/train.py +0 -320
- package/template/wall-e/eval/trainer.js +0 -232
- package/template/wall-e/eval/weekly-eval-loop.js +0 -241
|
@@ -65,39 +65,163 @@ const EMBEDDING_MODELS = {
|
|
|
65
65
|
'voyage-3-lite': { dimensions: 512, provider: 'voyage', maxBatch: 128, key: 'voyage', label: 'Voyage AI', cost: '$0.02/1M', desc: 'Anthropic recommended' },
|
|
66
66
|
'text-embedding-3-small': { dimensions: 1536, provider: 'openai', maxBatch: 2048, key: 'openai', label: 'OpenAI', cost: '$0.02/1M', desc: 'text-embedding-3-small' },
|
|
67
67
|
'text-embedding-3-large': { dimensions: 3072, provider: 'openai', maxBatch: 2048, key: 'openai_lg', label: 'OpenAI Large', cost: '$0.13/1M', desc: 'text-embedding-3-large' },
|
|
68
|
-
'nomic-embed-text': { dimensions: 768, provider: 'ollama', maxBatch: 32, key: 'ollama', label: 'Ollama (local)', cost: 'Free', desc: 'nomic-embed-text, private' },
|
|
68
|
+
'nomic-embed-text': { dimensions: 768, provider: 'ollama', maxBatch: 32, key: 'ollama', label: 'Ollama (local)', cost: 'Free', desc: 'nomic-embed-text, private', quantize: 'int8' },
|
|
69
69
|
'mxbai-embed-large': { dimensions: 1024, provider: 'ollama', maxBatch: 32, key: 'ollama_lg', label: 'Ollama Large', cost: 'Free', desc: 'mxbai-embed-large, private' },
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
const EMBEDDING_POLICY = {
|
|
73
|
+
LOCAL_FIRST: 'local_first',
|
|
74
|
+
LOCAL_ONLY: 'local_only',
|
|
75
|
+
CLOUD_ALLOWED: 'cloud_allowed',
|
|
76
|
+
OFF: 'off',
|
|
77
|
+
};
|
|
78
|
+
const DEFAULT_EMBEDDING_POLICY = EMBEDDING_POLICY.LOCAL_FIRST;
|
|
79
|
+
const EMBEDDING_POLICY_VALUES = new Set(Object.values(EMBEDDING_POLICY));
|
|
80
|
+
const EMBEDDING_LOCAL_MODELS = ['nomic-embed-text', 'mxbai-embed-large'];
|
|
81
|
+
const EMBEDDING_CLOUD_MODELS = ['text-embedding-004', 'text-embedding-3-small', 'voyage-3-lite'];
|
|
82
|
+
const EMBEDDING_AUTO_MODEL_ORDER = [...EMBEDDING_LOCAL_MODELS, ...EMBEDDING_CLOUD_MODELS];
|
|
83
|
+
|
|
84
|
+
function _getKv(key) {
|
|
85
|
+
try { return _getAdapter().getKv(key); } catch { return null; }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function _setKv(key, value) {
|
|
89
|
+
try { _getAdapter().setKv(key, value); return true; } catch { return false; }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function _normalizeEmbeddingPolicy(value) {
|
|
93
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
94
|
+
return EMBEDDING_POLICY_VALUES.has(normalized) ? normalized : DEFAULT_EMBEDDING_POLICY;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getEmbeddingPolicy() {
|
|
98
|
+
return _normalizeEmbeddingPolicy(_getKv('embedding:auto_policy'));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function _getEmbeddingPolicyRecord() {
|
|
102
|
+
const raw = _getKv('embedding:auto_policy');
|
|
103
|
+
return {
|
|
104
|
+
policy: _normalizeEmbeddingPolicy(raw),
|
|
105
|
+
source: raw && EMBEDDING_POLICY_VALUES.has(String(raw).trim().toLowerCase()) ? 'saved' : 'default',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
80
108
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
109
|
+
function _isLocalProvider(provider) {
|
|
110
|
+
return provider === 'ollama';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function _isCloudProvider(provider) {
|
|
114
|
+
return Boolean(provider) && !_isLocalProvider(provider);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function _policyAllowsConfig(config, policy) {
|
|
118
|
+
if (!config || policy === EMBEDDING_POLICY.OFF) return false;
|
|
119
|
+
if (policy === EMBEDDING_POLICY.CLOUD_ALLOWED) return true;
|
|
120
|
+
return _isLocalProvider(config.provider);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function _providerReachableForModel(modelName) {
|
|
124
|
+
const config = EMBEDDING_MODELS[modelName];
|
|
125
|
+
return config ? _hasApiKey(config.provider) : false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function _embeddingCountForModel(modelName) {
|
|
129
|
+
try {
|
|
130
|
+
const db = _getAdapter().getDb();
|
|
131
|
+
const row = db.prepare('SELECT count(*) as c FROM embedding_map WHERE model = ?').get(modelName);
|
|
132
|
+
return row?.c || 0;
|
|
133
|
+
} catch {
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function _findExistingIndexedModel(policy, policySource) {
|
|
85
139
|
try {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
140
|
+
const db = _getAdapter().getDb();
|
|
141
|
+
const rows = db.prepare('SELECT model, count(*) as c FROM embedding_map GROUP BY model ORDER BY c DESC').all();
|
|
142
|
+
for (const row of rows) {
|
|
143
|
+
const config = EMBEDDING_MODELS[row.model];
|
|
144
|
+
if (!config) continue;
|
|
145
|
+
// Backward compatibility: installs that already built a cloud index before
|
|
146
|
+
// the policy setting existed should keep using it until the user changes
|
|
147
|
+
// policy or manually switches providers.
|
|
148
|
+
if (policySource === 'default' || _policyAllowsConfig(config, policy)) return row.model;
|
|
89
149
|
}
|
|
90
150
|
} catch {}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
91
153
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
154
|
+
function _firstAvailableModel(models, policy) {
|
|
155
|
+
for (const model of models) {
|
|
156
|
+
const config = EMBEDDING_MODELS[model];
|
|
157
|
+
if (!config || !_policyAllowsConfig(config, policy)) continue;
|
|
158
|
+
if (_hasApiKey(config.provider)) return model;
|
|
159
|
+
}
|
|
98
160
|
return null;
|
|
99
161
|
}
|
|
100
162
|
|
|
163
|
+
function _selectAutoCandidate(policy) {
|
|
164
|
+
if (policy === EMBEDDING_POLICY.OFF) return null;
|
|
165
|
+
return _firstAvailableModel(
|
|
166
|
+
policy === EMBEDDING_POLICY.CLOUD_ALLOWED ? EMBEDDING_AUTO_MODEL_ORDER : EMBEDDING_LOCAL_MODELS,
|
|
167
|
+
policy
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function getActiveEmbeddingModelInfo() {
|
|
172
|
+
const explicit = process.env.WALLE_EMBEDDING_MODEL;
|
|
173
|
+
if (explicit) {
|
|
174
|
+
return EMBEDDING_MODELS[explicit]
|
|
175
|
+
? { model: explicit, source: 'env', policy: getEmbeddingPolicy(), policySource: 'env' }
|
|
176
|
+
: { model: null, source: 'invalid_env', policy: getEmbeddingPolicy(), policySource: 'env' };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const policyRecord = _getEmbeddingPolicyRecord();
|
|
180
|
+
if (policyRecord.policy === EMBEDDING_POLICY.OFF) {
|
|
181
|
+
return { model: null, source: 'off', policy: policyRecord.policy, policySource: policyRecord.source };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const pref = _getKv('embedding:active_model');
|
|
185
|
+
const selectionMode = _getKv('embedding:selection_mode') || (pref ? 'manual' : 'auto');
|
|
186
|
+
if (pref && EMBEDDING_MODELS[pref]) {
|
|
187
|
+
const config = EMBEDDING_MODELS[pref];
|
|
188
|
+
const blockedByLocalOnly = policyRecord.source === 'saved'
|
|
189
|
+
&& policyRecord.policy === EMBEDDING_POLICY.LOCAL_ONLY
|
|
190
|
+
&& !_policyAllowsConfig(config, policyRecord.policy);
|
|
191
|
+
if (blockedByLocalOnly) {
|
|
192
|
+
// The explicit Local only policy is stronger than an older cloud manual
|
|
193
|
+
// choice. The saved model is left in kv_store for audit/history, but it is
|
|
194
|
+
// not effective while this policy is active.
|
|
195
|
+
} else if (selectionMode !== 'auto') {
|
|
196
|
+
return { model: pref, source: selectionMode, policy: policyRecord.policy, policySource: policyRecord.source };
|
|
197
|
+
} else if (_policyAllowsConfig(config, policyRecord.policy) && _providerReachableForModel(pref)) {
|
|
198
|
+
return { model: pref, source: 'auto', policy: policyRecord.policy, policySource: policyRecord.source };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const indexedModel = _findExistingIndexedModel(policyRecord.policy, policyRecord.source);
|
|
203
|
+
if (indexedModel) {
|
|
204
|
+
return { model: indexedModel, source: 'existing_index', policy: policyRecord.policy, policySource: policyRecord.source };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const candidate = _selectAutoCandidate(policyRecord.policy);
|
|
208
|
+
return {
|
|
209
|
+
model: candidate,
|
|
210
|
+
source: candidate ? 'auto_candidate' : 'none',
|
|
211
|
+
policy: policyRecord.policy,
|
|
212
|
+
policySource: policyRecord.source,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Determine the active embedding model.
|
|
218
|
+
* Priority: explicit env var > manual/saved preference > compatible existing
|
|
219
|
+
* index > auto policy candidate. Cloud candidates require explicit opt-in.
|
|
220
|
+
*/
|
|
221
|
+
function getEmbeddingModel() {
|
|
222
|
+
return getActiveEmbeddingModelInfo().model || null;
|
|
223
|
+
}
|
|
224
|
+
|
|
101
225
|
function _hasApiKey(provider) {
|
|
102
226
|
// Check process.env first, then fall back to model_providers table in brain DB
|
|
103
227
|
switch (provider) {
|
|
@@ -137,6 +261,142 @@ function _isOllamaAvailable() {
|
|
|
137
261
|
}
|
|
138
262
|
}
|
|
139
263
|
|
|
264
|
+
// --- Telemetry ---
|
|
265
|
+
|
|
266
|
+
const EMBEDDING_SETUP_STATUS_TELEMETRY_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
267
|
+
const EMBEDDING_USAGE_TELEMETRY_INTERVAL_MS = 60 * 60 * 1000;
|
|
268
|
+
let _lastSetupStatusTelemetry = { key: '', ts: 0 };
|
|
269
|
+
let _lastUsageTelemetryByKey = new Map();
|
|
270
|
+
|
|
271
|
+
function _countBucket(value) {
|
|
272
|
+
const n = Number(value) || 0;
|
|
273
|
+
if (n <= 0) return '0';
|
|
274
|
+
if (n < 10) return '1-9';
|
|
275
|
+
if (n < 100) return '10-99';
|
|
276
|
+
if (n < 1000) return '100-999';
|
|
277
|
+
if (n < 10000) return '1k-9k';
|
|
278
|
+
if (n < 100000) return '10k-99k';
|
|
279
|
+
return '100k+';
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function _safeTelemetry() {
|
|
283
|
+
try { return require('./telemetry'); } catch { return null; }
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function _modelTelemetryFields(modelName) {
|
|
287
|
+
const cfg = modelName && EMBEDDING_MODELS[modelName] ? EMBEDDING_MODELS[modelName] : null;
|
|
288
|
+
return {
|
|
289
|
+
model: cfg ? modelName : 'none',
|
|
290
|
+
provider: cfg ? cfg.provider : 'none',
|
|
291
|
+
model_key: cfg ? cfg.key : 'none',
|
|
292
|
+
cost_tier: cfg ? (cfg.cost === 'Free' ? 'free' : 'paid') : 'none',
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function _embeddingTelemetrySnapshot() {
|
|
297
|
+
const info = getActiveEmbeddingModelInfo();
|
|
298
|
+
let model = info.model || null;
|
|
299
|
+
let providerReachable = false;
|
|
300
|
+
let stats = { total: 0, memories: 0, knowledge: 0 };
|
|
301
|
+
try {
|
|
302
|
+
const cfg = model && EMBEDDING_MODELS[model] ? EMBEDDING_MODELS[model] : null;
|
|
303
|
+
providerReachable = cfg ? !!_hasApiKey(cfg.provider) : false;
|
|
304
|
+
} catch {}
|
|
305
|
+
try {
|
|
306
|
+
if (model) stats = getEmbeddingStats();
|
|
307
|
+
} catch {}
|
|
308
|
+
|
|
309
|
+
let state = 'ready';
|
|
310
|
+
if (!_vecLoaded) state = 'sqlite_vec_unavailable';
|
|
311
|
+
else if (!model) state = 'no_provider';
|
|
312
|
+
else if (!providerReachable) state = 'provider_unreachable';
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
state,
|
|
316
|
+
enabled: state === 'ready' ? 1 : 0,
|
|
317
|
+
sqlite_vec: _vecLoaded ? 1 : 0,
|
|
318
|
+
provider_reachable: providerReachable ? 1 : 0,
|
|
319
|
+
policy: info.policy || DEFAULT_EMBEDDING_POLICY,
|
|
320
|
+
selection_source: info.source || 'none',
|
|
321
|
+
..._modelTelemetryFields(model),
|
|
322
|
+
embedded_bucket: _countBucket(stats.total),
|
|
323
|
+
memory_bucket: _countBucket(stats.memories),
|
|
324
|
+
knowledge_bucket: _countBucket(stats.knowledge),
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function trackEmbeddingStatus(reason = 'boot', options = {}) {
|
|
329
|
+
const telemetry = options.telemetry || _safeTelemetry();
|
|
330
|
+
if (!telemetry || typeof telemetry.track !== 'function') return;
|
|
331
|
+
telemetry.track('embedding_status', {
|
|
332
|
+
reason,
|
|
333
|
+
..._embeddingTelemetrySnapshot(),
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function trackEmbeddingSetupView() {
|
|
338
|
+
const snapshot = _embeddingTelemetrySnapshot();
|
|
339
|
+
const key = [
|
|
340
|
+
snapshot.state,
|
|
341
|
+
snapshot.provider,
|
|
342
|
+
snapshot.model,
|
|
343
|
+
snapshot.embedded_bucket,
|
|
344
|
+
snapshot.memory_bucket,
|
|
345
|
+
].join('|');
|
|
346
|
+
const now = Date.now();
|
|
347
|
+
if (_lastSetupStatusTelemetry.key === key && now - _lastSetupStatusTelemetry.ts < EMBEDDING_SETUP_STATUS_TELEMETRY_INTERVAL_MS) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
_lastSetupStatusTelemetry = { key, ts: now };
|
|
351
|
+
trackEmbeddingStatus('setup_view');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function _switchFailureReason(result) {
|
|
355
|
+
const message = String(result && result.message || '').toLowerCase();
|
|
356
|
+
if (message.includes('unknown model')) return 'unknown_model';
|
|
357
|
+
if (message.includes('no api key')) return 'missing_key';
|
|
358
|
+
return result && result.ok ? 'success' : 'failed';
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function _trackEmbeddingProviderSwitch(previousModel, requestedModel, result) {
|
|
362
|
+
const telemetry = _safeTelemetry();
|
|
363
|
+
if (!telemetry || typeof telemetry.track !== 'function') return;
|
|
364
|
+
const fromFields = _modelTelemetryFields(previousModel);
|
|
365
|
+
const toFields = _modelTelemetryFields(requestedModel);
|
|
366
|
+
telemetry.track('embedding_provider_switch', {
|
|
367
|
+
ok: result && result.ok ? 1 : 0,
|
|
368
|
+
reason: _switchFailureReason(result),
|
|
369
|
+
from_provider: fromFields.provider,
|
|
370
|
+
from_model: fromFields.model,
|
|
371
|
+
to_provider: toFields.provider,
|
|
372
|
+
to_model: toFields.model,
|
|
373
|
+
same_provider: fromFields.provider === toFields.provider ? 1 : 0,
|
|
374
|
+
});
|
|
375
|
+
if (result && result.ok) trackEmbeddingStatus('provider_switch', { telemetry });
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function _trackEmbeddingUsage(operation, resultCount, status = 'ok') {
|
|
379
|
+
const telemetry = _safeTelemetry();
|
|
380
|
+
if (!telemetry || typeof telemetry.track !== 'function') return;
|
|
381
|
+
const snapshot = _embeddingTelemetrySnapshot();
|
|
382
|
+
const safeOperation = String(operation || 'unknown').replace(/[^a-z0-9_-]/gi, '_').slice(0, 64) || 'unknown';
|
|
383
|
+
const safeStatus = String(status || 'ok').replace(/[^a-z0-9_-]/gi, '_').slice(0, 32) || 'ok';
|
|
384
|
+
const resultBucket = _countBucket(resultCount);
|
|
385
|
+
const key = [safeOperation, safeStatus, snapshot.provider, snapshot.model, resultBucket].join('|');
|
|
386
|
+
const now = Date.now();
|
|
387
|
+
const last = _lastUsageTelemetryByKey.get(key) || 0;
|
|
388
|
+
if (now - last < EMBEDDING_USAGE_TELEMETRY_INTERVAL_MS) return;
|
|
389
|
+
_lastUsageTelemetryByKey.set(key, now);
|
|
390
|
+
telemetry.track('embedding_usage', {
|
|
391
|
+
operation: safeOperation,
|
|
392
|
+
status: safeStatus,
|
|
393
|
+
result_bucket: resultBucket,
|
|
394
|
+
state: snapshot.state,
|
|
395
|
+
provider: snapshot.provider,
|
|
396
|
+
model: snapshot.model,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
140
400
|
function getModelConfig() {
|
|
141
401
|
const model = getEmbeddingModel();
|
|
142
402
|
return model ? { name: model, ...EMBEDDING_MODELS[model] } : null;
|
|
@@ -154,7 +414,9 @@ function getDimensions() {
|
|
|
154
414
|
* Used by the setup page UI.
|
|
155
415
|
*/
|
|
156
416
|
function getProviderStatus() {
|
|
157
|
-
const
|
|
417
|
+
const activeInfo = getActiveEmbeddingModelInfo();
|
|
418
|
+
const activeModel = activeInfo.model;
|
|
419
|
+
const policy = activeInfo.policy || getEmbeddingPolicy();
|
|
158
420
|
const db = _getAdapter().getDb();
|
|
159
421
|
|
|
160
422
|
return Object.entries(EMBEDDING_MODELS).map(([model, config]) => {
|
|
@@ -186,12 +448,171 @@ function getProviderStatus() {
|
|
|
186
448
|
hasKey,
|
|
187
449
|
isActive,
|
|
188
450
|
reachable,
|
|
451
|
+
selectionSource: isActive ? activeInfo.source : undefined,
|
|
452
|
+
autoEligible: _policyAllowsConfig(config, policy),
|
|
453
|
+
cloudRequiresOptIn: _isCloudProvider(config.provider) && policy !== EMBEDDING_POLICY.CLOUD_ALLOWED,
|
|
189
454
|
embeddingCount: count,
|
|
190
455
|
sizeBytes: sizeEstimate,
|
|
191
456
|
};
|
|
192
457
|
});
|
|
193
458
|
}
|
|
194
459
|
|
|
460
|
+
function _resetBackfillForModelChange() {
|
|
461
|
+
try {
|
|
462
|
+
_setKv('backfill:embed:cursor', '0');
|
|
463
|
+
_setKv('backfill:done', null);
|
|
464
|
+
} catch {}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function _ensureVecTableForModel(modelName) {
|
|
468
|
+
const config = EMBEDDING_MODELS[modelName];
|
|
469
|
+
if (!config || !ensureVecLoaded()) return false;
|
|
470
|
+
const db = _getAdapter().getDb();
|
|
471
|
+
const vecTable = _vecTableName(config.key);
|
|
472
|
+
const existing = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name=?`).get(vecTable);
|
|
473
|
+
if (!existing) {
|
|
474
|
+
db.prepare(`CREATE VIRTUAL TABLE "${vecTable}" USING vec0(embedding ${_vecColType(config)})`).run();
|
|
475
|
+
}
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function _trackEmbeddingAutoSelect(reason, result) {
|
|
480
|
+
const telemetry = _safeTelemetry();
|
|
481
|
+
if (!telemetry || typeof telemetry.track !== 'function') return;
|
|
482
|
+
telemetry.track('embedding_auto_select', {
|
|
483
|
+
reason,
|
|
484
|
+
ok: result.ok ? 1 : 0,
|
|
485
|
+
status: result.status || '',
|
|
486
|
+
policy: result.policy || getEmbeddingPolicy(),
|
|
487
|
+
model: result.model || 'none',
|
|
488
|
+
provider: result.provider || 'none',
|
|
489
|
+
selection_source: result.selectionSource || 'none',
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function autoSelectProvider(reason = 'auto') {
|
|
494
|
+
const info = getActiveEmbeddingModelInfo();
|
|
495
|
+
const config = info.model ? EMBEDDING_MODELS[info.model] : null;
|
|
496
|
+
if (!info.model || !config) {
|
|
497
|
+
const result = {
|
|
498
|
+
ok: false,
|
|
499
|
+
status: info.source === 'off' ? 'off' : 'waiting',
|
|
500
|
+
reason: info.source,
|
|
501
|
+
policy: info.policy || getEmbeddingPolicy(),
|
|
502
|
+
selectionSource: info.source || 'none',
|
|
503
|
+
};
|
|
504
|
+
_trackEmbeddingAutoSelect(reason, result);
|
|
505
|
+
return result;
|
|
506
|
+
}
|
|
507
|
+
if (!['auto_candidate', 'auto'].includes(info.source)) {
|
|
508
|
+
return {
|
|
509
|
+
ok: false,
|
|
510
|
+
status: 'preserved',
|
|
511
|
+
reason: info.source,
|
|
512
|
+
model: info.model,
|
|
513
|
+
provider: config.provider,
|
|
514
|
+
policy: info.policy,
|
|
515
|
+
selectionSource: info.source,
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const previous = _getKv('embedding:active_model');
|
|
520
|
+
_setKv('embedding:active_model', info.model);
|
|
521
|
+
_setKv('embedding:selection_mode', 'auto');
|
|
522
|
+
_ensureVecTableForModel(info.model);
|
|
523
|
+
if (previous !== info.model) _resetBackfillForModelChange();
|
|
524
|
+
|
|
525
|
+
const result = {
|
|
526
|
+
ok: true,
|
|
527
|
+
status: previous === info.model ? 'unchanged' : 'selected',
|
|
528
|
+
model: info.model,
|
|
529
|
+
provider: config.provider,
|
|
530
|
+
policy: info.policy,
|
|
531
|
+
selectionSource: 'auto',
|
|
532
|
+
};
|
|
533
|
+
_trackEmbeddingAutoSelect(reason, result);
|
|
534
|
+
if (previous !== info.model) trackEmbeddingStatus('auto_select');
|
|
535
|
+
return result;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function getEmbeddingSetupState() {
|
|
539
|
+
const policyRecord = _getEmbeddingPolicyRecord();
|
|
540
|
+
const activeInfo = getActiveEmbeddingModelInfo();
|
|
541
|
+
const activeConfig = activeInfo.model ? EMBEDDING_MODELS[activeInfo.model] : null;
|
|
542
|
+
const localAvailable = Boolean(_firstAvailableModel(EMBEDDING_LOCAL_MODELS, EMBEDDING_POLICY.LOCAL_ONLY));
|
|
543
|
+
const cloudAvailableModel = _firstAvailableModel(EMBEDDING_CLOUD_MODELS, EMBEDDING_POLICY.CLOUD_ALLOWED);
|
|
544
|
+
const cloudAvailable = Boolean(cloudAvailableModel);
|
|
545
|
+
const cloudOptInAvailable = !activeInfo.model
|
|
546
|
+
&& cloudAvailable
|
|
547
|
+
&& policyRecord.policy !== EMBEDDING_POLICY.CLOUD_ALLOWED
|
|
548
|
+
&& policyRecord.policy !== EMBEDDING_POLICY.OFF;
|
|
549
|
+
|
|
550
|
+
let status = 'waiting_for_provider';
|
|
551
|
+
let message = 'Memory Search will turn on automatically when a local embedding provider is available.';
|
|
552
|
+
if (policyRecord.policy === EMBEDDING_POLICY.OFF) {
|
|
553
|
+
status = 'off';
|
|
554
|
+
message = 'Memory Search is off.';
|
|
555
|
+
} else if (activeInfo.model) {
|
|
556
|
+
status = activeInfo.source === 'manual' ? 'manual_active' : 'active';
|
|
557
|
+
message = `${activeConfig.label} is powering Memory Search.`;
|
|
558
|
+
} else if (cloudOptInAvailable) {
|
|
559
|
+
status = 'cloud_opt_in_available';
|
|
560
|
+
message = 'A cloud embedding provider is available. Opt in to enable semantic Memory Search.';
|
|
561
|
+
} else if (!localAvailable && !cloudAvailable) {
|
|
562
|
+
status = 'no_provider';
|
|
563
|
+
message = 'Add a local embedding provider or cloud API key to enable semantic Memory Search.';
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return {
|
|
567
|
+
policy: policyRecord.policy,
|
|
568
|
+
policySource: policyRecord.source,
|
|
569
|
+
status,
|
|
570
|
+
message,
|
|
571
|
+
activeModel: activeInfo.model || null,
|
|
572
|
+
activeProvider: activeConfig ? activeConfig.provider : null,
|
|
573
|
+
activeLabel: activeConfig ? activeConfig.label : null,
|
|
574
|
+
selectionSource: activeInfo.source || 'none',
|
|
575
|
+
localAvailable,
|
|
576
|
+
cloudAvailable,
|
|
577
|
+
cloudAvailableModel,
|
|
578
|
+
cloudOptInAvailable,
|
|
579
|
+
totalIndexed: activeInfo.model ? _embeddingCountForModel(activeInfo.model) : 0,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function setEmbeddingPolicy(policy) {
|
|
584
|
+
const normalized = String(policy || '').trim().toLowerCase();
|
|
585
|
+
if (!EMBEDDING_POLICY_VALUES.has(normalized)) {
|
|
586
|
+
return { ok: false, message: `Unknown Memory Search policy: ${policy}` };
|
|
587
|
+
}
|
|
588
|
+
const previousPolicy = getEmbeddingPolicy();
|
|
589
|
+
const previousModel = _getKv('embedding:active_model');
|
|
590
|
+
const previousConfig = previousModel && EMBEDDING_MODELS[previousModel] ? EMBEDDING_MODELS[previousModel] : null;
|
|
591
|
+
_setKv('embedding:auto_policy', normalized);
|
|
592
|
+
if (normalized !== EMBEDDING_POLICY.OFF && _getKv('embedding:selection_mode') === 'auto') {
|
|
593
|
+
_setKv('embedding:active_model', null);
|
|
594
|
+
}
|
|
595
|
+
if ((normalized === EMBEDDING_POLICY.LOCAL_ONLY || normalized === EMBEDDING_POLICY.OFF)
|
|
596
|
+
&& previousConfig
|
|
597
|
+
&& _isCloudProvider(previousConfig.provider)) {
|
|
598
|
+
_setKv('embedding:active_model', null);
|
|
599
|
+
_setKv('embedding:selection_mode', 'auto');
|
|
600
|
+
_resetBackfillForModelChange();
|
|
601
|
+
}
|
|
602
|
+
const auto = autoSelectProvider('policy_update');
|
|
603
|
+
const setup = getEmbeddingSetupState();
|
|
604
|
+
const telemetry = _safeTelemetry();
|
|
605
|
+
try {
|
|
606
|
+
telemetry?.track?.('embedding_policy_update', {
|
|
607
|
+
from: previousPolicy,
|
|
608
|
+
to: normalized,
|
|
609
|
+
status: setup.status,
|
|
610
|
+
active_provider: setup.activeProvider || 'none',
|
|
611
|
+
});
|
|
612
|
+
} catch {}
|
|
613
|
+
return { ok: true, policy: normalized, auto, setup };
|
|
614
|
+
}
|
|
615
|
+
|
|
195
616
|
// --- Embedding computation ---
|
|
196
617
|
|
|
197
618
|
async function computeEmbedding(text) {
|
|
@@ -425,6 +846,33 @@ function _vecTableName(modelKey) {
|
|
|
425
846
|
return `embedding_vec_${modelKey}`;
|
|
426
847
|
}
|
|
427
848
|
|
|
849
|
+
// vec0 column type for a model: int8[N] for models that opt into quantization
|
|
850
|
+
// (config.quantize==='int8'), float[N] otherwise. int8 stores 1 byte/dim vs 4 — ~6x
|
|
851
|
+
// smaller on disk + faster KNN, at ~95% recall@10 for unit-normalized vectors (nomic).
|
|
852
|
+
function _vecColType(config) {
|
|
853
|
+
return `${config.quantize === 'int8' ? 'int8' : 'float'}[${config.dimensions}]`;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// The bind expression that turns a JSON-array param into the stored/queried vector.
|
|
857
|
+
// Derived from the ACTUAL declared table type (not just config) so the code is correct
|
|
858
|
+
// whether the table is still float32 (pre-migration) or already int8 (post-migration) —
|
|
859
|
+
// no fragile coupling between the schema and a flag. 'unit' assumes inputs in [-1,1]
|
|
860
|
+
// (true for L2-normalized models like nomic-embed-text), the only mode sqlite-vec supports.
|
|
861
|
+
function _declaredVecType(db, table) {
|
|
862
|
+
try {
|
|
863
|
+
const r = db.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name=?").get(table);
|
|
864
|
+
if (!r || !r.sql) return null;
|
|
865
|
+
if (/\bint8\s*\[/.test(r.sql)) return 'int8';
|
|
866
|
+
if (/\bfloat\s*\[/.test(r.sql)) return 'float';
|
|
867
|
+
} catch {}
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
function _vecBindExpr(db, config) {
|
|
871
|
+
return _declaredVecType(db, _vecTableName(config.key)) === 'int8'
|
|
872
|
+
? "vec_quantize_int8(vec_f32(?),'unit')"
|
|
873
|
+
: 'vec_f32(?)';
|
|
874
|
+
}
|
|
875
|
+
|
|
428
876
|
/**
|
|
429
877
|
* Create embedding tables for the active model.
|
|
430
878
|
* Each model gets its own vec table. The mapping table is shared.
|
|
@@ -454,8 +902,8 @@ function createEmbeddingTables() {
|
|
|
454
902
|
const vecTable = _vecTableName(config.key);
|
|
455
903
|
const existing = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name=?`).get(vecTable);
|
|
456
904
|
if (!existing) {
|
|
457
|
-
db.prepare(`CREATE VIRTUAL TABLE "${vecTable}" USING vec0(embedding
|
|
458
|
-
console.log(`[embeddings] Created vec table: ${vecTable} (${config
|
|
905
|
+
db.prepare(`CREATE VIRTUAL TABLE "${vecTable}" USING vec0(embedding ${_vecColType(config)})`).run();
|
|
906
|
+
console.log(`[embeddings] Created vec table: ${vecTable} (${_vecColType(config)})`);
|
|
459
907
|
}
|
|
460
908
|
|
|
461
909
|
// Migrate from old single-table layout if it exists
|
|
@@ -543,7 +991,7 @@ function storeEmbedding(entityId, entityType, embedding) {
|
|
|
543
991
|
rowid = info.lastInsertRowid;
|
|
544
992
|
}
|
|
545
993
|
|
|
546
|
-
db.prepare(`INSERT INTO "${vecTable}"(rowid, embedding) VALUES (?,
|
|
994
|
+
db.prepare(`INSERT INTO "${vecTable}"(rowid, embedding) VALUES (?, ${_vecBindExpr(db, config)})`).run(
|
|
547
995
|
BigInt(rowid), JSON.stringify(embedding)
|
|
548
996
|
);
|
|
549
997
|
});
|
|
@@ -632,6 +1080,75 @@ function deleteEmbeddingsForModel(modelName) {
|
|
|
632
1080
|
}
|
|
633
1081
|
}
|
|
634
1082
|
|
|
1083
|
+
// Delete all embeddings for one entity (its embedding_map rows across models + the
|
|
1084
|
+
// matching vec-table rows keyed by rowid). Call this whenever an entity is hard-deleted
|
|
1085
|
+
// so we don't leak orphaned vectors (the source of the audited 47,649 orphans — skills
|
|
1086
|
+
// that re-sync by `DELETE FROM memories WHERE id = ?` left the embeddings behind).
|
|
1087
|
+
function deleteEmbeddingsForEntity(entityId) {
|
|
1088
|
+
if (!entityId) return { deleted: 0 };
|
|
1089
|
+
// Load vec0 into this connection if not already (skill subprocesses may not have
|
|
1090
|
+
// touched the embeddings module yet, but their brain.initDb() opened the DB).
|
|
1091
|
+
if (!ensureVecLoaded()) return { deleted: 0 };
|
|
1092
|
+
const db = _getAdapter().getDb();
|
|
1093
|
+
try {
|
|
1094
|
+
const rows = db.prepare('SELECT rowid, model FROM embedding_map WHERE entity_id = ?').all(entityId);
|
|
1095
|
+
if (!rows.length) return { deleted: 0 };
|
|
1096
|
+
db.transaction(() => {
|
|
1097
|
+
for (const r of rows) {
|
|
1098
|
+
const cfg = EMBEDDING_MODELS[r.model];
|
|
1099
|
+
if (cfg) {
|
|
1100
|
+
try { db.prepare(`DELETE FROM "${_vecTableName(cfg.key)}" WHERE rowid = ?`).run(BigInt(r.rowid)); } catch {}
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
db.prepare('DELETE FROM embedding_map WHERE entity_id = ?').run(entityId);
|
|
1104
|
+
})();
|
|
1105
|
+
return { deleted: rows.length };
|
|
1106
|
+
} catch (err) {
|
|
1107
|
+
return { deleted: 0, error: err.message };
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// One-time / periodic sweep: delete embeddings whose entity no longer exists. Only sweeps
|
|
1112
|
+
// entity types whose source table we can verify (memory→memories, knowledge→knowledge);
|
|
1113
|
+
// other types are left untouched. Batched per-transaction so each holds the cross-process
|
|
1114
|
+
// write lock only briefly (lock-friendly, per the CTM lesson). entity_type is parameterized;
|
|
1115
|
+
// the source-table names are internal constants (no user input → no injection).
|
|
1116
|
+
const _ORPHAN_ENTITY_TABLES = { memory: 'memories', knowledge: 'knowledge' };
|
|
1117
|
+
function pruneOrphanEmbeddings({ batchSize = 200 } = {}) {
|
|
1118
|
+
if (!ensureVecLoaded()) return { deleted: 0 };
|
|
1119
|
+
const db = _getAdapter().getDb();
|
|
1120
|
+
const batch = Math.max(50, Math.min(2000, Number(batchSize) || 200));
|
|
1121
|
+
let deleted = 0;
|
|
1122
|
+
for (const [etype, table] of Object.entries(_ORPHAN_ENTITY_TABLES)) {
|
|
1123
|
+
// Guard: only sweep if the source table exists, else skip (never mass-delete blind).
|
|
1124
|
+
const hasTable = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table);
|
|
1125
|
+
if (!hasTable) continue;
|
|
1126
|
+
for (let i = 0; i < 5000; i += 1) { // batch backstop
|
|
1127
|
+
const orphans = db.prepare(
|
|
1128
|
+
`SELECT em.rowid AS rowid, em.model AS model
|
|
1129
|
+
FROM embedding_map em
|
|
1130
|
+
WHERE em.entity_type = ?
|
|
1131
|
+
AND NOT EXISTS (SELECT 1 FROM ${table} t WHERE t.id = em.entity_id)
|
|
1132
|
+
LIMIT ?`
|
|
1133
|
+
).all(etype, batch);
|
|
1134
|
+
if (!orphans.length) break;
|
|
1135
|
+
db.transaction(() => {
|
|
1136
|
+
for (const o of orphans) {
|
|
1137
|
+
const cfg = EMBEDDING_MODELS[o.model];
|
|
1138
|
+
if (cfg) {
|
|
1139
|
+
try { db.prepare(`DELETE FROM "${_vecTableName(cfg.key)}" WHERE rowid = ?`).run(BigInt(o.rowid)); } catch {}
|
|
1140
|
+
}
|
|
1141
|
+
db.prepare('DELETE FROM embedding_map WHERE rowid = ?').run(o.rowid);
|
|
1142
|
+
}
|
|
1143
|
+
})();
|
|
1144
|
+
deleted += orphans.length;
|
|
1145
|
+
if (orphans.length < batch) break;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
if (deleted) console.log(`[embeddings] pruneOrphanEmbeddings removed ${deleted} orphaned embeddings`);
|
|
1149
|
+
return { deleted };
|
|
1150
|
+
}
|
|
1151
|
+
|
|
635
1152
|
// --- Vector search ---
|
|
636
1153
|
|
|
637
1154
|
function searchSimilarMemories(queryEmbedding, limit = 10, filters = {}) {
|
|
@@ -649,7 +1166,7 @@ function searchSimilarMemories(queryEmbedding, limit = 10, filters = {}) {
|
|
|
649
1166
|
FROM "${vecTable}" v
|
|
650
1167
|
JOIN embedding_map em ON em.rowid = v.rowid
|
|
651
1168
|
JOIN memories m ON m.id = em.entity_id
|
|
652
|
-
WHERE v.embedding MATCH
|
|
1169
|
+
WHERE v.embedding MATCH ${_vecBindExpr(db, config)} AND k = ?
|
|
653
1170
|
AND em.entity_type = 'memory' AND em.model = ?
|
|
654
1171
|
`;
|
|
655
1172
|
const params = [JSON.stringify(queryEmbedding), kFetch, config.name];
|
|
@@ -661,9 +1178,12 @@ function searchSimilarMemories(queryEmbedding, limit = 10, filters = {}) {
|
|
|
661
1178
|
sql += ' ORDER BY v.distance LIMIT ?';
|
|
662
1179
|
params.push(limit);
|
|
663
1180
|
|
|
664
|
-
|
|
1181
|
+
const rows = db.prepare(sql).all(...params);
|
|
1182
|
+
_trackEmbeddingUsage('memory_search', rows.length);
|
|
1183
|
+
return rows;
|
|
665
1184
|
} catch (err) {
|
|
666
1185
|
console.error('[embeddings] Vector search failed:', err.message);
|
|
1186
|
+
_trackEmbeddingUsage('memory_search', 0, 'error');
|
|
667
1187
|
return [];
|
|
668
1188
|
}
|
|
669
1189
|
}
|
|
@@ -678,19 +1198,22 @@ function searchSimilarKnowledge(queryEmbedding, limit = 10) {
|
|
|
678
1198
|
const vecTable = _vecTableName(config.key);
|
|
679
1199
|
const kFetch = Math.min(limit * 3, 100);
|
|
680
1200
|
|
|
681
|
-
|
|
1201
|
+
const rows = db.prepare(`
|
|
682
1202
|
SELECT kn.*, v.distance
|
|
683
1203
|
FROM "${vecTable}" v
|
|
684
1204
|
JOIN embedding_map em ON em.rowid = v.rowid
|
|
685
1205
|
JOIN knowledge kn ON kn.id = em.entity_id
|
|
686
|
-
WHERE v.embedding MATCH
|
|
1206
|
+
WHERE v.embedding MATCH ${_vecBindExpr(db, config)} AND k = ?
|
|
687
1207
|
AND em.entity_type = 'knowledge' AND em.model = ?
|
|
688
1208
|
AND kn.status = 'active'
|
|
689
1209
|
ORDER BY v.distance
|
|
690
1210
|
LIMIT ?
|
|
691
1211
|
`).all(JSON.stringify(queryEmbedding), kFetch, config.name, limit);
|
|
1212
|
+
_trackEmbeddingUsage('knowledge_search', rows.length);
|
|
1213
|
+
return rows;
|
|
692
1214
|
} catch (err) {
|
|
693
1215
|
console.error('[embeddings] Knowledge vector search failed:', err.message);
|
|
1216
|
+
_trackEmbeddingUsage('knowledge_search', 0, 'error');
|
|
694
1217
|
return [];
|
|
695
1218
|
}
|
|
696
1219
|
}
|
|
@@ -714,7 +1237,7 @@ function searchSimilarEntities(queryEmbedding, limit = 10, entityType = null) {
|
|
|
714
1237
|
SELECT em.entity_id, em.entity_type, v.distance
|
|
715
1238
|
FROM "${vecTable}" v
|
|
716
1239
|
JOIN embedding_map em ON em.rowid = v.rowid
|
|
717
|
-
WHERE v.embedding MATCH
|
|
1240
|
+
WHERE v.embedding MATCH ${_vecBindExpr(db, config)} AND k = ?
|
|
718
1241
|
AND em.model = ?
|
|
719
1242
|
`;
|
|
720
1243
|
const params = [JSON.stringify(queryEmbedding), kFetch, config.name];
|
|
@@ -724,9 +1247,12 @@ function searchSimilarEntities(queryEmbedding, limit = 10, entityType = null) {
|
|
|
724
1247
|
sql += ' ORDER BY v.distance LIMIT ?';
|
|
725
1248
|
params.push(limit);
|
|
726
1249
|
|
|
727
|
-
|
|
1250
|
+
const rows = db.prepare(sql).all(...params);
|
|
1251
|
+
_trackEmbeddingUsage(entityType ? `${entityType}_entity_search` : 'entity_search', rows.length);
|
|
1252
|
+
return rows;
|
|
728
1253
|
} catch (err) {
|
|
729
1254
|
console.error('[embeddings] Entity vector search failed:', err.message);
|
|
1255
|
+
_trackEmbeddingUsage(entityType ? `${entityType}_entity_search` : 'entity_search', 0, 'error');
|
|
730
1256
|
return [];
|
|
731
1257
|
}
|
|
732
1258
|
}
|
|
@@ -765,51 +1291,57 @@ function reciprocalRankFusion(lexicalResults, vectorResults, limit, k = 60) {
|
|
|
765
1291
|
* @returns {{ ok: boolean, message: string }}
|
|
766
1292
|
*/
|
|
767
1293
|
function switchProvider(modelName) {
|
|
1294
|
+
const previousModel = getEmbeddingModel();
|
|
768
1295
|
const config = EMBEDDING_MODELS[modelName];
|
|
769
|
-
if (!config)
|
|
770
|
-
|
|
1296
|
+
if (!config) {
|
|
1297
|
+
const result = { ok: false, message: `Unknown model: ${modelName}` };
|
|
1298
|
+
_trackEmbeddingProviderSwitch(previousModel, modelName, result);
|
|
1299
|
+
return result;
|
|
1300
|
+
}
|
|
1301
|
+
if (!_hasApiKey(config.provider)) {
|
|
1302
|
+
const result = { ok: false, message: `No API key for ${config.label}` };
|
|
1303
|
+
_trackEmbeddingProviderSwitch(previousModel, modelName, result);
|
|
1304
|
+
return result;
|
|
1305
|
+
}
|
|
771
1306
|
|
|
772
1307
|
// Save preference
|
|
773
|
-
|
|
1308
|
+
_setKv('embedding:active_model', modelName);
|
|
1309
|
+
_setKv('embedding:selection_mode', 'manual');
|
|
774
1310
|
|
|
775
1311
|
// Reset cached dimensions so they're recalculated
|
|
776
1312
|
_dimensions = null;
|
|
777
1313
|
|
|
778
1314
|
// Create the new vec table if needed
|
|
779
|
-
|
|
780
|
-
const db = _getAdapter().getDb();
|
|
781
|
-
const vecTable = _vecTableName(config.key);
|
|
782
|
-
const existing = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name=?`).get(vecTable);
|
|
783
|
-
if (!existing) {
|
|
784
|
-
db.prepare(`CREATE VIRTUAL TABLE "${vecTable}" USING vec0(embedding float[${config.dimensions}])`).run();
|
|
785
|
-
}
|
|
786
|
-
}
|
|
1315
|
+
_ensureVecTableForModel(modelName);
|
|
787
1316
|
|
|
788
1317
|
// Reset backfill cursors so the new model gets backfilled
|
|
789
|
-
|
|
790
|
-
_getAdapter().setKv('backfill:embed:cursor', '0');
|
|
791
|
-
_getAdapter().setKv('backfill:done', null);
|
|
792
|
-
} catch {}
|
|
1318
|
+
_resetBackfillForModelChange();
|
|
793
1319
|
|
|
794
1320
|
console.log(`[embeddings] Switched to ${config.label} (${modelName}, ${config.dimensions}d)`);
|
|
795
|
-
|
|
1321
|
+
const result = { ok: true, message: `Switched to ${config.label}` };
|
|
1322
|
+
_trackEmbeddingProviderSwitch(previousModel, modelName, result);
|
|
1323
|
+
return result;
|
|
796
1324
|
}
|
|
797
1325
|
|
|
798
1326
|
// --- Initialization ---
|
|
799
1327
|
|
|
800
1328
|
function init(adapter) {
|
|
1329
|
+
// Sub-phase timing so the boot profile pinpoints what's slow inside embeddings.init
|
|
1330
|
+
// (on the live brain this whole call was ~15s; isolate which step). No-op if profiler absent.
|
|
1331
|
+
let _boot; try { _boot = require('./lib/boot-profile'); } catch { _boot = { measure: (_l, f) => f() }; }
|
|
801
1332
|
if (adapter) _adapter = adapter;
|
|
802
|
-
if (!ensureVecLoaded()) {
|
|
1333
|
+
if (!_boot.measure('embeddings.ensureVecLoaded', () => ensureVecLoaded())) {
|
|
803
1334
|
console.log('[embeddings] sqlite-vec not available — vector search disabled');
|
|
804
1335
|
return false;
|
|
805
1336
|
}
|
|
1337
|
+
_boot.measure('embeddings.autoSelectProvider', () => autoSelectProvider('boot'));
|
|
806
1338
|
if (!getEmbeddingModel()) {
|
|
807
|
-
console.log('[embeddings] No embedding
|
|
1339
|
+
console.log('[embeddings] No embedding provider active — vector search disabled until local provider is available or cloud embeddings are allowed');
|
|
808
1340
|
return false;
|
|
809
1341
|
}
|
|
810
|
-
const ok = createEmbeddingTables();
|
|
1342
|
+
const ok = _boot.measure('embeddings.createEmbeddingTables', () => createEmbeddingTables());
|
|
811
1343
|
if (ok) {
|
|
812
|
-
const stats = getEmbeddingStats();
|
|
1344
|
+
const stats = _boot.measure('embeddings.getEmbeddingStats', () => getEmbeddingStats());
|
|
813
1345
|
const config = getModelConfig();
|
|
814
1346
|
console.log(`[embeddings] Initialized (${stats.total} embeddings, model: ${config.name}, dims: ${config.dimensions}, provider: ${config.label})`);
|
|
815
1347
|
}
|
|
@@ -822,14 +1354,20 @@ function _resetForTesting() {
|
|
|
822
1354
|
_adapter = null;
|
|
823
1355
|
_defaultAdapter = null;
|
|
824
1356
|
_ollamaAvailableCache = { value: false, ts: 0 };
|
|
1357
|
+
_lastSetupStatusTelemetry = { key: '', ts: 0 };
|
|
1358
|
+
_lastUsageTelemetryByKey = new Map();
|
|
825
1359
|
}
|
|
826
1360
|
|
|
827
1361
|
module.exports = {
|
|
828
1362
|
init, ensureVecLoaded, isAvailable, createEmbeddingTables,
|
|
829
|
-
getEmbeddingModel,
|
|
1363
|
+
getEmbeddingModel, getActiveEmbeddingModelInfo, getEmbeddingPolicy, setEmbeddingPolicy,
|
|
1364
|
+
getEmbeddingSetupState, autoSelectProvider,
|
|
1365
|
+
getModelConfig, getDimensions, EMBEDDING_MODELS, EMBEDDING_POLICY,
|
|
1366
|
+
trackEmbeddingStatus, trackEmbeddingSetupView,
|
|
830
1367
|
computeEmbedding, batchComputeEmbeddings,
|
|
831
1368
|
storeEmbedding, hasEmbedding, getEmbeddingStats, getAllEmbeddingStats,
|
|
832
1369
|
getProviderStatus, switchProvider, deleteEmbeddingsForModel,
|
|
1370
|
+
deleteEmbeddingsForEntity, pruneOrphanEmbeddings,
|
|
833
1371
|
searchSimilarMemories, searchSimilarKnowledge, searchSimilarEntities, reciprocalRankFusion,
|
|
834
1372
|
_resetForTesting,
|
|
835
1373
|
};
|