mixdog 0.7.1
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/.claude-plugin/marketplace.json +31 -0
- package/.claude-plugin/plugin.json +20 -0
- package/.gitattributes +34 -0
- package/.mcp.json +14 -0
- package/ARCHITECTURE.md +77 -0
- package/CHANGELOG.md +7 -0
- package/CONTRIBUTING.md +45 -0
- package/DATA-FLOW.md +79 -0
- package/LICENSE +21 -0
- package/README.md +389 -0
- package/SECURITY.md +138 -0
- package/UNINSTALL.md +112 -0
- package/agents/maintenance.md +5 -0
- package/agents/memory-classification.md +30 -0
- package/agents/scheduler-task.md +18 -0
- package/agents/webhook-handler.md +27 -0
- package/agents/worker.md +24 -0
- package/bin/bridge +133 -0
- package/bin/statusline-launcher.mjs +78 -0
- package/bin/statusline-lib.mjs +550 -0
- package/bin/statusline.mjs +607 -0
- package/bun.lock +802 -0
- package/commands/config.md +16 -0
- package/commands/doctor.md +13 -0
- package/commands/setup.md +17 -0
- package/defaults/cycle3-review-prompt.md +90 -0
- package/defaults/hidden-roles.json +65 -0
- package/defaults/memory-chunk-prompt.md +63 -0
- package/defaults/memory-promote-prompt.md +135 -0
- package/defaults/mixdog-config.template.json +27 -0
- package/defaults/user-workflow.json +8 -0
- package/defaults/user-workflow.md +12 -0
- package/hooks/hooks.json +73 -0
- package/hooks/lib/active-instance.cjs +77 -0
- package/hooks/lib/permission-evaluator.cjs +411 -0
- package/hooks/lib/permission-route.cjs +63 -0
- package/hooks/lib/permission-rules.cjs +170 -0
- package/hooks/lib/settings-loader.cjs +116 -0
- package/hooks/post-tool-use.cjs +84 -0
- package/hooks/pre-mcp-sandbox.cjs +158 -0
- package/hooks/pre-tool-subagent.cjs +253 -0
- package/hooks/session-start.cjs +1372 -0
- package/hooks/turn-timer.cjs +82 -0
- package/lib/claude-md-writer.cjs +386 -0
- package/lib/config-cjs.cjs +61 -0
- package/lib/hook-pipe-path.cjs +10 -0
- package/lib/keychain-cjs.cjs +263 -0
- package/lib/plugin-paths.cjs +61 -0
- package/lib/rules-builder.cjs +241 -0
- package/lib/text-utils.cjs +61 -0
- package/native/README.md +117 -0
- package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
- package/package.json +107 -0
- package/prompts/code-review.txt +16 -0
- package/prompts/security-audit.txt +17 -0
- package/rules/bridge/00-common.md +39 -0
- package/rules/bridge/20-skip-protocol.md +18 -0
- package/rules/bridge/30-explorer.md +33 -0
- package/rules/bridge/40-cycle1-agent.md +52 -0
- package/rules/bridge/41-cycle2-agent.md +62 -0
- package/rules/bridge/42-cycle3-agent.md +44 -0
- package/rules/lead/00-tool-lead.md +61 -0
- package/rules/lead/01-general.md +23 -0
- package/rules/lead/02-channels.md +49 -0
- package/rules/lead/03-team.md +27 -0
- package/rules/lead/04-workflow.md +20 -0
- package/rules/shared/00-language.md +14 -0
- package/rules/shared/01-tool.md +138 -0
- package/scripts/bootstrap.mjs +184 -0
- package/scripts/bridge-unify-smoke.mjs +308 -0
- package/scripts/build-runtime-linux.sh +348 -0
- package/scripts/build-runtime-macos.sh +217 -0
- package/scripts/build-runtime-windows.ps1 +242 -0
- package/scripts/builtin-utils-smoke.mjs +392 -0
- package/scripts/check-json.mjs +45 -0
- package/scripts/check-syntax-changed.mjs +102 -0
- package/scripts/check-syntax.mjs +58 -0
- package/scripts/code-graph-batch.test.mjs +33 -0
- package/scripts/config-preserve-smoke.mjs +180 -0
- package/scripts/doctor.mjs +484 -0
- package/scripts/edit-normalize-fuzz.mjs +130 -0
- package/scripts/edit-normalize-smoke.mjs +401 -0
- package/scripts/edit-operation-smoke.mjs +369 -0
- package/scripts/edit2-smoke.mjs +63 -0
- package/scripts/fuzzy-e2e.mjs +28 -0
- package/scripts/fuzzy-smoke.mjs +26 -0
- package/scripts/generate-runtime-manifest.mjs +166 -0
- package/scripts/guard-smoke.mjs +66 -0
- package/scripts/hidden-role-schema-smoke.mjs +162 -0
- package/scripts/hook-routing-smoke.mjs +29 -0
- package/scripts/inject-input.ps1 +204 -0
- package/scripts/io-complex-smoke.mjs +667 -0
- package/scripts/io-explore-bench.mjs +424 -0
- package/scripts/io-guardrails-smoke.mjs +205 -0
- package/scripts/io-mini-bench-baseline.json +11 -0
- package/scripts/io-mini-bench.mjs +216 -0
- package/scripts/io-route-harness.mjs +933 -0
- package/scripts/io-telemetry-report.mjs +691 -0
- package/scripts/mutation-bench.mjs +564 -0
- package/scripts/mutation-io-smoke.mjs +1081 -0
- package/scripts/native-patch-bridge-smoke.mjs +288 -0
- package/scripts/native-patch-smoke.mjs +304 -0
- package/scripts/patch-interior-context-smoke.mjs +49 -0
- package/scripts/patch-newline-utf8-smoke.mjs +157 -0
- package/scripts/perf-hook-smoke.mjs +71 -0
- package/scripts/permission-eval-smoke.mjs +426 -0
- package/scripts/prep-patch.mjs +53 -0
- package/scripts/prep-shim.mjs +96 -0
- package/scripts/provider-cache-smoke.mjs +687 -0
- package/scripts/report-runtime-health.mjs +132 -0
- package/scripts/run-mcp.mjs +1547 -0
- package/scripts/salvage-v4a-shatter.test.mjs +58 -0
- package/scripts/scoped-cache-io-smoke.mjs +103 -0
- package/scripts/shell-policy-round3-smoke.mjs +46 -0
- package/scripts/smoke-runtime-negative.ps1 +100 -0
- package/scripts/smoke-runtime-negative.sh +95 -0
- package/scripts/stall-policy-smoke.mjs +50 -0
- package/scripts/start-memory-worker.mjs +23 -0
- package/scripts/statusline-launcher-smoke.mjs +82 -0
- package/scripts/stress-atomic-write.mjs +1028 -0
- package/scripts/test-config-rmw-restore.mjs +122 -0
- package/scripts/test-fault-inject.mjs +164 -0
- package/scripts/test-large-file.mjs +174 -0
- package/scripts/tool-edge-smoke.mjs +209 -0
- package/scripts/uninstall.mjs +201 -0
- package/scripts/webhook-selfheal-smoke.mjs +29 -0
- package/scripts/write-overwrite-guard-smoke.mjs +56 -0
- package/server-main.mjs +3055 -0
- package/server.mjs +468 -0
- package/setup/config-merge.mjs +254 -0
- package/setup/install.mjs +120 -0
- package/setup/launch-core.mjs +507 -0
- package/setup/launch.mjs +101 -0
- package/setup/setup-server.mjs +3206 -0
- package/setup/setup.html +3693 -0
- package/skills/retro-skill-proposer/SKILL.md +92 -0
- package/skills/schedule-add/SKILL.md +77 -0
- package/skills/setup/SKILL.md +346 -0
- package/skills/webhook-add/SKILL.md +81 -0
- package/src/agent/bridge-stall-watchdog.mjs +337 -0
- package/src/agent/index.mjs +2138 -0
- package/src/agent/orchestrator/activity-bus.mjs +38 -0
- package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +1010 -0
- package/src/agent/orchestrator/bridge-retry.mjs +220 -0
- package/src/agent/orchestrator/bridge-trace.mjs +583 -0
- package/src/agent/orchestrator/cache-mtime.mjs +58 -0
- package/src/agent/orchestrator/config.mjs +358 -0
- package/src/agent/orchestrator/context/collect.mjs +651 -0
- package/src/agent/orchestrator/dispatch-persist.mjs +549 -0
- package/src/agent/orchestrator/drain-registry.mjs +50 -0
- package/src/agent/orchestrator/explore-validator.mjs +8 -0
- package/src/agent/orchestrator/internal-roles.mjs +118 -0
- package/src/agent/orchestrator/internal-tools.mjs +88 -0
- package/src/agent/orchestrator/jobs.mjs +116 -0
- package/src/agent/orchestrator/mcp/client.mjs +364 -0
- package/src/agent/orchestrator/providers/anthropic-betas.mjs +21 -0
- package/src/agent/orchestrator/providers/anthropic-oauth.mjs +1745 -0
- package/src/agent/orchestrator/providers/anthropic.mjs +437 -0
- package/src/agent/orchestrator/providers/gemini.mjs +1175 -0
- package/src/agent/orchestrator/providers/grok-oauth.mjs +782 -0
- package/src/agent/orchestrator/providers/model-catalog.mjs +241 -0
- package/src/agent/orchestrator/providers/openai-compat.mjs +1467 -0
- package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +1890 -0
- package/src/agent/orchestrator/providers/openai-oauth.mjs +1307 -0
- package/src/agent/orchestrator/providers/openai-ws.mjs +104 -0
- package/src/agent/orchestrator/providers/registry.mjs +192 -0
- package/src/agent/orchestrator/providers/retry-classifier.mjs +325 -0
- package/src/agent/orchestrator/session/abort-lookup.mjs +13 -0
- package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
- package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
- package/src/agent/orchestrator/session/cache/read-cache.mjs +319 -0
- package/src/agent/orchestrator/session/cache/scoped-cache-outcome.mjs +11 -0
- package/src/agent/orchestrator/session/cache/scoped-cache.mjs +361 -0
- package/src/agent/orchestrator/session/cache/util.mjs +49 -0
- package/src/agent/orchestrator/session/loop.mjs +1478 -0
- package/src/agent/orchestrator/session/manager.mjs +1975 -0
- package/src/agent/orchestrator/session/read-dedup.mjs +6 -0
- package/src/agent/orchestrator/session/result-classification.mjs +65 -0
- package/src/agent/orchestrator/session/save-session-worker.mjs +18 -0
- package/src/agent/orchestrator/session/store.mjs +624 -0
- package/src/agent/orchestrator/session/stream-watchdog.mjs +130 -0
- package/src/agent/orchestrator/session/tool-result-offload.mjs +166 -0
- package/src/agent/orchestrator/session/trim.mjs +491 -0
- package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +115 -0
- package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +327 -0
- package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +150 -0
- package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +228 -0
- package/src/agent/orchestrator/smart-bridge/index.mjs +215 -0
- package/src/agent/orchestrator/smart-bridge/profiles.mjs +37 -0
- package/src/agent/orchestrator/smart-bridge/registry.mjs +348 -0
- package/src/agent/orchestrator/smart-bridge/session-builder.mjs +116 -0
- package/src/agent/orchestrator/stall-policy.mjs +195 -0
- package/src/agent/orchestrator/tool-loop-guard.mjs +75 -0
- package/src/agent/orchestrator/tools/bash-policy-scan.mjs +77 -0
- package/src/agent/orchestrator/tools/bash-session.mjs +721 -0
- package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
- package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +455 -0
- package/src/agent/orchestrator/tools/builtin/atomic-write.mjs +236 -0
- package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +480 -0
- package/src/agent/orchestrator/tools/builtin/binary-file.mjs +76 -0
- package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +256 -0
- package/src/agent/orchestrator/tools/builtin/cache-layers.mjs +386 -0
- package/src/agent/orchestrator/tools/builtin/cwd-utils.mjs +37 -0
- package/src/agent/orchestrator/tools/builtin/device-paths.mjs +154 -0
- package/src/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +292 -0
- package/src/agent/orchestrator/tools/builtin/diff-utils.mjs +109 -0
- package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +58 -0
- package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +240 -0
- package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +113 -0
- package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +74 -0
- package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +242 -0
- package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +211 -0
- package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +1364 -0
- package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +126 -0
- package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +141 -0
- package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +194 -0
- package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +60 -0
- package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +168 -0
- package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +173 -0
- package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +48 -0
- package/src/agent/orchestrator/tools/builtin/fs-reachability.mjs +48 -0
- package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +99 -0
- package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +170 -0
- package/src/agent/orchestrator/tools/builtin/grep-formatting.mjs +113 -0
- package/src/agent/orchestrator/tools/builtin/hash-utils.mjs +6 -0
- package/src/agent/orchestrator/tools/builtin/list-formatting.mjs +7 -0
- package/src/agent/orchestrator/tools/builtin/list-tool.mjs +593 -0
- package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +89 -0
- package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +300 -0
- package/src/agent/orchestrator/tools/builtin/open-config-tool.mjs +26 -0
- package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +152 -0
- package/src/agent/orchestrator/tools/builtin/path-locks.mjs +35 -0
- package/src/agent/orchestrator/tools/builtin/path-utils.mjs +201 -0
- package/src/agent/orchestrator/tools/builtin/read-args.mjs +103 -0
- package/src/agent/orchestrator/tools/builtin/read-batch.mjs +172 -0
- package/src/agent/orchestrator/tools/builtin/read-constants.mjs +40 -0
- package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
- package/src/agent/orchestrator/tools/builtin/read-image-resize.mjs +189 -0
- package/src/agent/orchestrator/tools/builtin/read-image.mjs +88 -0
- package/src/agent/orchestrator/tools/builtin/read-lines.mjs +12 -0
- package/src/agent/orchestrator/tools/builtin/read-mode-tool.mjs +455 -0
- package/src/agent/orchestrator/tools/builtin/read-open.mjs +190 -0
- package/src/agent/orchestrator/tools/builtin/read-range-index.mjs +271 -0
- package/src/agent/orchestrator/tools/builtin/read-ranges.mjs +26 -0
- package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +728 -0
- package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +173 -0
- package/src/agent/orchestrator/tools/builtin/read-special-files.mjs +268 -0
- package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
- package/src/agent/orchestrator/tools/builtin/read-tool.mjs +530 -0
- package/src/agent/orchestrator/tools/builtin/read-windows.mjs +107 -0
- package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +196 -0
- package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +422 -0
- package/src/agent/orchestrator/tools/builtin/search-builders.mjs +158 -0
- package/src/agent/orchestrator/tools/builtin/search-tool.mjs +869 -0
- package/src/agent/orchestrator/tools/builtin/shell-analysis.mjs +653 -0
- package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +936 -0
- package/src/agent/orchestrator/tools/builtin/shell-output.mjs +36 -0
- package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +214 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +143 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +206 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-validation.mjs +98 -0
- package/src/agent/orchestrator/tools/builtin/text-stats.mjs +69 -0
- package/src/agent/orchestrator/tools/builtin/windows-roots.mjs +23 -0
- package/src/agent/orchestrator/tools/builtin/write-tool.mjs +401 -0
- package/src/agent/orchestrator/tools/builtin.mjs +500 -0
- package/src/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +39 -0
- package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
- package/src/agent/orchestrator/tools/code-graph.mjs +4095 -0
- package/src/agent/orchestrator/tools/cwd-tool.mjs +298 -0
- package/src/agent/orchestrator/tools/destructive-warning.mjs +323 -0
- package/src/agent/orchestrator/tools/edit-normalize.mjs +603 -0
- package/src/agent/orchestrator/tools/env-scrub.mjs +100 -0
- package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +144 -0
- package/src/agent/orchestrator/tools/graph-manifest.json +26 -0
- package/src/agent/orchestrator/tools/host-input.mjs +204 -0
- package/src/agent/orchestrator/tools/mutation-content-cache.mjs +67 -0
- package/src/agent/orchestrator/tools/mutation-planner.mjs +75 -0
- package/src/agent/orchestrator/tools/next-call-utils.mjs +48 -0
- package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +133 -0
- package/src/agent/orchestrator/tools/patch-manifest.json +26 -0
- package/src/agent/orchestrator/tools/patch-tool-defs.mjs +20 -0
- package/src/agent/orchestrator/tools/patch.mjs +2754 -0
- package/src/agent/orchestrator/tools/progress-message.mjs +118 -0
- package/src/agent/orchestrator/tools/result-compression.mjs +279 -0
- package/src/agent/orchestrator/tools/shell-command.mjs +865 -0
- package/src/agent/orchestrator/tools/shell-exec-policy.mjs +89 -0
- package/src/agent/orchestrator/tools/shell-policy-danger-target.mjs +27 -0
- package/src/agent/orchestrator/tools/shell-policy-imports.mjs +7 -0
- package/src/agent/orchestrator/tools/shell-policy.mjs +345 -0
- package/src/agent/orchestrator/tools/shell-snapshot.mjs +313 -0
- package/src/agent/orchestrator/workflow-store.mjs +93 -0
- package/src/agent/tool-defs.mjs +103 -0
- package/src/channels/backends/discord.mjs +784 -0
- package/src/channels/data/voice-runtime-manifest.json +138 -0
- package/src/channels/index.mjs +3229 -0
- package/src/channels/lib/cli-worker-host.mjs +12 -0
- package/src/channels/lib/config-lock.mjs +13 -0
- package/src/channels/lib/config.mjs +292 -0
- package/src/channels/lib/drop-trace.mjs +71 -0
- package/src/channels/lib/event-pipeline.mjs +81 -0
- package/src/channels/lib/event-queue.mjs +345 -0
- package/src/channels/lib/executor.mjs +168 -0
- package/src/channels/lib/format.mjs +188 -0
- package/src/channels/lib/holidays.mjs +138 -0
- package/src/channels/lib/hook-pipe-server.mjs +802 -0
- package/src/channels/lib/interaction-workflows.mjs +184 -0
- package/src/channels/lib/memory-client.mjs +149 -0
- package/src/channels/lib/output-forwarder.mjs +765 -0
- package/src/channels/lib/runtime-paths.mjs +479 -0
- package/src/channels/lib/scheduler.mjs +723 -0
- package/src/channels/lib/session-control.mjs +36 -0
- package/src/channels/lib/session-discovery.mjs +103 -0
- package/src/channels/lib/settings.mjs +11 -0
- package/src/channels/lib/state-file.mjs +68 -0
- package/src/channels/lib/status-snapshot.mjs +219 -0
- package/src/channels/lib/tool-format.mjs +140 -0
- package/src/channels/lib/transcript-discovery.mjs +195 -0
- package/src/channels/lib/voice-runtime-fetcher.mjs +734 -0
- package/src/channels/lib/webhook.mjs +1179 -0
- package/src/channels/lib/whisper-server.mjs +477 -0
- package/src/channels/tool-defs.mjs +170 -0
- package/src/daemon/host.mjs +118 -0
- package/src/daemon/mcp-transport.mjs +47 -0
- package/src/daemon/session.mjs +100 -0
- package/src/daemon/thin-client.mjs +71 -0
- package/src/daemon/transport.mjs +163 -0
- package/src/memory/data/runtime-manifest.json +40 -0
- package/src/memory/index.mjs +3305 -0
- package/src/memory/lib/agent-ipc.mjs +93 -0
- package/src/memory/lib/bridge-trace-queries.mjs +120 -0
- package/src/memory/lib/core-memory-store.mjs +330 -0
- package/src/memory/lib/embedding-provider.mjs +269 -0
- package/src/memory/lib/embedding-worker.mjs +323 -0
- package/src/memory/lib/llm-worker-host.mjs +17 -0
- package/src/memory/lib/memory-cycle.mjs +11 -0
- package/src/memory/lib/memory-cycle1.mjs +641 -0
- package/src/memory/lib/memory-cycle2.mjs +1284 -0
- package/src/memory/lib/memory-cycle3.mjs +540 -0
- package/src/memory/lib/memory-embed.mjs +299 -0
- package/src/memory/lib/memory-extraction.mjs +5 -0
- package/src/memory/lib/memory-maintenance-store.mjs +32 -0
- package/src/memory/lib/memory-ops-policy.mjs +190 -0
- package/src/memory/lib/memory-recall-id-patch.mjs +15 -0
- package/src/memory/lib/memory-recall-read-query.mjs +7 -0
- package/src/memory/lib/memory-recall-scope-filter.mjs +63 -0
- package/src/memory/lib/memory-recall-store.mjs +621 -0
- package/src/memory/lib/memory-retrievers.mjs +112 -0
- package/src/memory/lib/memory-score.mjs +71 -0
- package/src/memory/lib/memory-text-utils.mjs +58 -0
- package/src/memory/lib/memory.mjs +412 -0
- package/src/memory/lib/model-profile.mjs +85 -0
- package/src/memory/lib/pg/adapter.mjs +308 -0
- package/src/memory/lib/pg/process.mjs +360 -0
- package/src/memory/lib/pg/supervisor.mjs +396 -0
- package/src/memory/lib/project-id-resolver.mjs +86 -0
- package/src/memory/lib/runtime-fetcher.mjs +442 -0
- package/src/memory/lib/trace-store.mjs +728 -0
- package/src/memory/tool-defs.mjs +79 -0
- package/src/search/index.mjs +1173 -0
- package/src/search/lib/backends/anthropic-oauth.mjs +98 -0
- package/src/search/lib/backends/exa.mjs +50 -0
- package/src/search/lib/backends/firecrawl.mjs +61 -0
- package/src/search/lib/backends/gemini-api.mjs +83 -0
- package/src/search/lib/backends/grok-oauth.mjs +86 -0
- package/src/search/lib/backends/index.mjs +150 -0
- package/src/search/lib/backends/openai-api.mjs +144 -0
- package/src/search/lib/backends/openai-oauth.mjs +98 -0
- package/src/search/lib/backends/openai-web-search.mjs +76 -0
- package/src/search/lib/backends/tavily.mjs +55 -0
- package/src/search/lib/backends/xai-api.mjs +113 -0
- package/src/search/lib/cache.mjs +131 -0
- package/src/search/lib/config.mjs +192 -0
- package/src/search/lib/formatter.mjs +115 -0
- package/src/search/lib/provider-usage.mjs +67 -0
- package/src/search/lib/providers.mjs +47 -0
- package/src/search/lib/search-intent.mjs +109 -0
- package/src/search/lib/setup-handler.mjs +261 -0
- package/src/search/lib/state.mjs +201 -0
- package/src/search/lib/web-tools.mjs +1207 -0
- package/src/search/tool-defs.mjs +83 -0
- package/src/setup/defender-exclusion.mjs +183 -0
- package/src/shared/abort-controller.mjs +15 -0
- package/src/shared/atomic-file.mjs +420 -0
- package/src/shared/config.mjs +350 -0
- package/src/shared/daemon-recycle.mjs +108 -0
- package/src/shared/disable-claude-builtins.mjs +88 -0
- package/src/shared/err-text.mjs +12 -0
- package/src/shared/llm/cost.mjs +66 -0
- package/src/shared/llm/http-agent.mjs +123 -0
- package/src/shared/llm/index.mjs +41 -0
- package/src/shared/llm/pid-cleanup.mjs +27 -0
- package/src/shared/llm/usage-log.mjs +47 -0
- package/src/shared/plugin-paths.mjs +58 -0
- package/src/shared/schedules-store.mjs +70 -0
- package/src/shared/seed.mjs +119 -0
- package/src/shared/user-cwd.mjs +213 -0
- package/src/shared/user-data-guard.mjs +238 -0
- package/src/status/aggregator.mjs +584 -0
- package/src/status/server.mjs +413 -0
- package/tools.json +1653 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* embedding-provider.mjs — Embedding provider with worker_threads isolation.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Worker } from 'worker_threads'
|
|
6
|
+
import { join } from 'path'
|
|
7
|
+
import { fileURLToPath } from 'url'
|
|
8
|
+
import { writeProfilePoint } from './model-profile.mjs'
|
|
9
|
+
|
|
10
|
+
const MODEL_ID = 'Xenova/bge-m3'
|
|
11
|
+
|
|
12
|
+
// Static dims registry — bypasses first-boot measurement for registered models.
|
|
13
|
+
// Validated against measured dims inside warmupEmbeddingProvider; mismatch throws.
|
|
14
|
+
const KNOWN_MODEL_DIMS = { 'Xenova/bge-m3': 1024 }
|
|
15
|
+
|
|
16
|
+
let worker = null
|
|
17
|
+
let _restartCount = 0
|
|
18
|
+
let _lastRestartMs = 0
|
|
19
|
+
const MAX_RESTART_BACKOFF_MS = 30_000
|
|
20
|
+
let cachedDims = null
|
|
21
|
+
let _modelReady = false
|
|
22
|
+
let _device = 'cpu'
|
|
23
|
+
let _embedCallCount = 0
|
|
24
|
+
let _msgId = 0
|
|
25
|
+
const _pending = new Map()
|
|
26
|
+
const EMBED_STEADY_SAMPLE_EVERY = 20
|
|
27
|
+
const queryEmbeddingCache = new Map()
|
|
28
|
+
const QUERY_EMBEDDING_CACHE_LIMIT = 1000
|
|
29
|
+
|
|
30
|
+
const WORKER_PATH = join(fileURLToPath(import.meta.url), '..', 'embedding-worker.mjs')
|
|
31
|
+
|
|
32
|
+
function cacheEmbedding(key, vector) {
|
|
33
|
+
if (queryEmbeddingCache.has(key)) queryEmbeddingCache.delete(key)
|
|
34
|
+
queryEmbeddingCache.set(key, vector)
|
|
35
|
+
if (queryEmbeddingCache.size > QUERY_EMBEDDING_CACHE_LIMIT) {
|
|
36
|
+
const oldestKey = queryEmbeddingCache.keys().next().value
|
|
37
|
+
if (oldestKey) queryEmbeddingCache.delete(oldestKey)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getCachedEmbedding(key) {
|
|
42
|
+
if (!queryEmbeddingCache.has(key)) return null
|
|
43
|
+
const value = queryEmbeddingCache.get(key)
|
|
44
|
+
queryEmbeddingCache.delete(key)
|
|
45
|
+
queryEmbeddingCache.set(key, value)
|
|
46
|
+
return value
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function ensureWorker() {
|
|
50
|
+
if (worker) return worker
|
|
51
|
+
const now = Date.now()
|
|
52
|
+
if (_restartCount > 0) {
|
|
53
|
+
const backoffMs = Math.min(1000 * Math.pow(2, _restartCount - 1), MAX_RESTART_BACKOFF_MS)
|
|
54
|
+
const elapsed = now - _lastRestartMs
|
|
55
|
+
if (elapsed < backoffMs) {
|
|
56
|
+
throw new Error(`embed worker in restart backoff (${Math.ceil((backoffMs - elapsed) / 1000)}s remaining)`)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
_lastRestartMs = now
|
|
60
|
+
const execArgv = process.execArgv.filter((arg) => !String(arg).startsWith('--input-type'))
|
|
61
|
+
worker = new Worker(WORKER_PATH, { env: { ...process.env }, execArgv })
|
|
62
|
+
worker.on('message', (msg) => {
|
|
63
|
+
if (msg.type === 'profile') {
|
|
64
|
+
writeProfilePoint(msg.record)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
if (msg.type === 'idle-dispose') {
|
|
68
|
+
cachedDims = null
|
|
69
|
+
_modelReady = false
|
|
70
|
+
_device = 'cpu'
|
|
71
|
+
process.stderr.write('[embed] idle timeout — model disposed\n')
|
|
72
|
+
writeProfilePoint({ phase: 'post-idle', model: MODEL_ID, device: msg.device, dtype: msg.dtype, note: 'idle dispose' })
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
const pending = _pending.get(msg.id)
|
|
76
|
+
if (!pending) return
|
|
77
|
+
_pending.delete(msg.id)
|
|
78
|
+
if (msg.type === 'error') {
|
|
79
|
+
pending.reject(new Error(msg.message))
|
|
80
|
+
} else {
|
|
81
|
+
pending.resolve(msg)
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
worker.on('error', (err) => {
|
|
85
|
+
process.stderr.write(`[embed] worker error: ${err?.message || err}\n`)
|
|
86
|
+
for (const [, p] of _pending) p.reject(err)
|
|
87
|
+
_pending.clear()
|
|
88
|
+
worker = null
|
|
89
|
+
_modelReady = false
|
|
90
|
+
_restartCount++
|
|
91
|
+
})
|
|
92
|
+
worker.on('exit', (code) => {
|
|
93
|
+
if (code !== 0) {
|
|
94
|
+
process.stderr.write(`[embed] worker exited with code ${code}\n`)
|
|
95
|
+
for (const [, p] of _pending) p.reject(new Error(`Worker exited with code ${code}`))
|
|
96
|
+
_pending.clear()
|
|
97
|
+
_restartCount++
|
|
98
|
+
} else {
|
|
99
|
+
_restartCount = 0
|
|
100
|
+
}
|
|
101
|
+
worker = null
|
|
102
|
+
_modelReady = false
|
|
103
|
+
})
|
|
104
|
+
return worker
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const EMBED_WORKER_TIMEOUT_MS = 60_000
|
|
108
|
+
|
|
109
|
+
function sendToWorker(action, extra = {}, timeoutMs = EMBED_WORKER_TIMEOUT_MS) {
|
|
110
|
+
const w = ensureWorker()
|
|
111
|
+
const id = ++_msgId
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
const timer = setTimeout(() => {
|
|
114
|
+
_pending.delete(id)
|
|
115
|
+
process.stderr.write(`[embed] worker ${action} timed out — terminating worker\n`)
|
|
116
|
+
if (worker) {
|
|
117
|
+
const stuck = worker
|
|
118
|
+
worker = null
|
|
119
|
+
_restartCount++
|
|
120
|
+
stuck.terminate().catch(() => {})
|
|
121
|
+
}
|
|
122
|
+
reject(new Error(`embed worker ${action} timed out after ${timeoutMs}ms`))
|
|
123
|
+
}, timeoutMs)
|
|
124
|
+
_pending.set(id, {
|
|
125
|
+
resolve: (v) => { clearTimeout(timer); resolve(v) },
|
|
126
|
+
reject: (e) => { clearTimeout(timer); reject(e) },
|
|
127
|
+
})
|
|
128
|
+
try {
|
|
129
|
+
w.postMessage({ id, action, ...extra })
|
|
130
|
+
} catch (postErr) {
|
|
131
|
+
clearTimeout(timer)
|
|
132
|
+
_pending.delete(id)
|
|
133
|
+
reject(postErr)
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function configureEmbedding(config = {}) {
|
|
139
|
+
cachedDims = null
|
|
140
|
+
_modelReady = false
|
|
141
|
+
_device = 'cpu'
|
|
142
|
+
queryEmbeddingCache.clear()
|
|
143
|
+
if (worker) {
|
|
144
|
+
sendToWorker('configure', { dtype: config.dtype }).catch((err) => {
|
|
145
|
+
// Silent .catch hid worker reconfigure failures (dtype mismatch,
|
|
146
|
+
// worker crash, IPC closed). At least one log line so cycle1 /
|
|
147
|
+
// cycle2 root-cause investigation can see the upstream failure
|
|
148
|
+
// instead of just the downstream `db write failed`.
|
|
149
|
+
process.stderr.write(`[embed] worker configure failed: ${err?.message || err}\n`)
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function primeEmbeddingDims(dims) {
|
|
155
|
+
const n = Number(dims)
|
|
156
|
+
if (Number.isFinite(n) && n > 0) cachedDims = n
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function getEmbeddingModelId() {
|
|
160
|
+
return MODEL_ID
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function getKnownDimsForCurrentModel() {
|
|
164
|
+
return KNOWN_MODEL_DIMS[MODEL_ID] ?? null
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function getEmbeddingDims() {
|
|
168
|
+
if (!cachedDims) throw new Error('embedding dims not yet measured — warmup required')
|
|
169
|
+
return cachedDims
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export async function warmupEmbeddingProvider() {
|
|
173
|
+
if (_modelReady && cachedDims) return true
|
|
174
|
+
const result = await sendToWorker('warmup')
|
|
175
|
+
if (!result.dims) throw new Error('warmup returned no dims — model output missing')
|
|
176
|
+
const known = KNOWN_MODEL_DIMS[MODEL_ID]
|
|
177
|
+
if (known != null && known !== result.dims) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`embedding dims invariant violation: model=${MODEL_ID} expected=${known} measured=${result.dims}`,
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
cachedDims = result.dims
|
|
183
|
+
_modelReady = true
|
|
184
|
+
_device = result.device || 'cpu'
|
|
185
|
+
return true
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export async function embedText(text) {
|
|
189
|
+
const clean = String(text ?? '').trim()
|
|
190
|
+
if (!clean) return []
|
|
191
|
+
const cacheKey = `${MODEL_ID}\n${clean}`
|
|
192
|
+
const cached = getCachedEmbedding(cacheKey)
|
|
193
|
+
if (cached) return [...cached]
|
|
194
|
+
|
|
195
|
+
const result = await sendToWorker('embed', { text: clean })
|
|
196
|
+
if (!result.dims) throw new Error(`embed result missing dims (model=${MODEL_ID})`)
|
|
197
|
+
const resultDims = result.dims
|
|
198
|
+
if (cachedDims && resultDims !== cachedDims) {
|
|
199
|
+
throw new Error(`embed vector dims mismatch: expected ${cachedDims}, got ${resultDims}`)
|
|
200
|
+
}
|
|
201
|
+
cachedDims = resultDims
|
|
202
|
+
_modelReady = true
|
|
203
|
+
_device = result.device || 'cpu'
|
|
204
|
+
const vector = result.vector
|
|
205
|
+
if (!Array.isArray(vector) || vector.length !== cachedDims) {
|
|
206
|
+
throw new Error(`embed vector length mismatch: expected ${cachedDims}, got ${vector?.length}`)
|
|
207
|
+
}
|
|
208
|
+
cacheEmbedding(cacheKey, vector)
|
|
209
|
+
_embedCallCount++
|
|
210
|
+
if (_embedCallCount % EMBED_STEADY_SAMPLE_EVERY === 0) {
|
|
211
|
+
writeProfilePoint({
|
|
212
|
+
phase: 'steady',
|
|
213
|
+
model: MODEL_ID,
|
|
214
|
+
device: _device,
|
|
215
|
+
dtype: result.dtype,
|
|
216
|
+
wallMs: result.wallMs,
|
|
217
|
+
note: `sample@${_embedCallCount}`,
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
return vector
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Batch variant of embedText. Pre-warms the per-query cache so subsequent
|
|
225
|
+
* single-text calls hit instantly. Invariant-preserving: each cached entry
|
|
226
|
+
* follows the same MODEL_ID-keyed format as embedText, so cache hits look
|
|
227
|
+
* identical regardless of whether the vector was originally produced via
|
|
228
|
+
* single or batch path. Worker still serializes the single ONNX run, but
|
|
229
|
+
* one batched run replaces N sequential runs.
|
|
230
|
+
*/
|
|
231
|
+
export async function embedTexts(texts) {
|
|
232
|
+
if (!Array.isArray(texts)) throw new Error('embedTexts requires an array')
|
|
233
|
+
const cleaned = texts.map(t => String(t ?? '').trim())
|
|
234
|
+
const missing = []
|
|
235
|
+
for (const t of cleaned) {
|
|
236
|
+
if (!t) continue
|
|
237
|
+
const key = `${MODEL_ID}\n${t}`
|
|
238
|
+
if (!queryEmbeddingCache.has(key)) missing.push(t)
|
|
239
|
+
}
|
|
240
|
+
if (missing.length === 0) return cleaned.map(t => {
|
|
241
|
+
if (!t) return []
|
|
242
|
+
return [...queryEmbeddingCache.get(`${MODEL_ID}\n${t}`)]
|
|
243
|
+
})
|
|
244
|
+
const result = await sendToWorker('embed-batch', { texts: missing })
|
|
245
|
+
if (!result.dims) throw new Error(`embed-batch result missing dims (model=${MODEL_ID})`)
|
|
246
|
+
const resultDims = result.dims
|
|
247
|
+
if (cachedDims && resultDims !== cachedDims) {
|
|
248
|
+
throw new Error(`embed-batch vector dims mismatch: expected ${cachedDims}, got ${resultDims}`)
|
|
249
|
+
}
|
|
250
|
+
cachedDims = resultDims
|
|
251
|
+
_modelReady = true
|
|
252
|
+
_device = result.device || _device
|
|
253
|
+
if (!Array.isArray(result.vectors) || result.vectors.length !== missing.length) {
|
|
254
|
+
throw new Error(`embed-batch vectors count mismatch: expected ${missing.length}, got ${result.vectors?.length}`)
|
|
255
|
+
}
|
|
256
|
+
for (let i = 0; i < missing.length; i++) {
|
|
257
|
+
const vec = result.vectors[i]
|
|
258
|
+
if (!Array.isArray(vec) || vec.length !== cachedDims) {
|
|
259
|
+
throw new Error(`embed-batch vector length mismatch at idx ${i}: expected ${cachedDims}, got ${vec?.length}`)
|
|
260
|
+
}
|
|
261
|
+
cacheEmbedding(`${MODEL_ID}\n${missing[i]}`, vec)
|
|
262
|
+
}
|
|
263
|
+
_embedCallCount += missing.length
|
|
264
|
+
return cleaned.map(t => {
|
|
265
|
+
if (!t) return []
|
|
266
|
+
const cached = queryEmbeddingCache.get(`${MODEL_ID}\n${t}`)
|
|
267
|
+
return cached ? [...cached] : []
|
|
268
|
+
})
|
|
269
|
+
}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { parentPort } from 'worker_threads'
|
|
2
|
+
import { createRequire } from 'module'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import { pathToFileURL } from 'url'
|
|
5
|
+
import { mkdirSync } from 'fs'
|
|
6
|
+
import os from 'os'
|
|
7
|
+
import { execFile } from 'child_process'
|
|
8
|
+
import { promisify } from 'util'
|
|
9
|
+
|
|
10
|
+
const MODEL_ID = 'Xenova/bge-m3'
|
|
11
|
+
const DEFAULT_DTYPE = 'q4'
|
|
12
|
+
const INTRA_OP_THREADS = 1
|
|
13
|
+
const INTER_OP_THREADS = 1
|
|
14
|
+
// Session-create graph optimization. ORT defaults to 'all' (full node fusion),
|
|
15
|
+
// which is the bulk of the cold-load CPU spike. 'basic' trims that fusion work
|
|
16
|
+
// — the load gets noticeably cheaper on CPU at a negligible inference cost for
|
|
17
|
+
// short-text embeddings.
|
|
18
|
+
const GRAPH_OPT_LEVEL = 'basic'
|
|
19
|
+
const execFileAsync = promisify(execFile)
|
|
20
|
+
// Cores the worker is pinned to *during* the cold model load, to cap the
|
|
21
|
+
// CPU/heat (fan) of DirectML graph compilation + weight dequant — work the ORT
|
|
22
|
+
// thread settings cannot bound (the GPU driver compiles on its own threads).
|
|
23
|
+
// Full affinity is restored the instant the load resolves, so steady-state
|
|
24
|
+
// inference is unaffected. Lower = quieter fan, slower load.
|
|
25
|
+
// MIXDOG_EMBED_LOAD_CORES overrides (default 1 = single core).
|
|
26
|
+
const _envLoadCores = Number(process.env.MIXDOG_EMBED_LOAD_CORES)
|
|
27
|
+
const LOAD_AFFINITY_CORES = Number.isInteger(_envLoadCores) && _envLoadCores >= 1 ? _envLoadCores : 1
|
|
28
|
+
const MODEL_CACHE_DIR = join(process.env.HOME || process.env.USERPROFILE, '.cache', 'mixdog-memory', 'models')
|
|
29
|
+
// Idle dispose was previously 15 min. Production profiling showed the model
|
|
30
|
+
// re-load cost (~3 s DirectML cold start) repeating ~880×/4 h because cycle1
|
|
31
|
+
// gaps exceed 15 min in normal use. Default to keep-alive (0) so the model
|
|
32
|
+
// stays resident for the lifetime of the worker. Set MIXDOG_EMBED_IDLE_TIMEOUT_MS
|
|
33
|
+
// to a positive value (in ms) to restore the prior dispose behaviour when GPU
|
|
34
|
+
// VRAM pressure is a concern.
|
|
35
|
+
const _envIdleMs = Number(process.env.MIXDOG_EMBED_IDLE_TIMEOUT_MS)
|
|
36
|
+
const IDLE_TIMEOUT_MS = Number.isFinite(_envIdleMs) && _envIdleMs >= 0 ? _envIdleMs : 0
|
|
37
|
+
|
|
38
|
+
let extractorPromise = null
|
|
39
|
+
let configuredDtype = DEFAULT_DTYPE
|
|
40
|
+
let _device = 'cpu'
|
|
41
|
+
let _idleTimer = null
|
|
42
|
+
let _embedInFlight = false
|
|
43
|
+
const _msgQueue = []
|
|
44
|
+
let ortPatched = false
|
|
45
|
+
// Actions that must hold the in-flight guard for their entire async duration.
|
|
46
|
+
// Inference actions hold it so concurrent embeds serialize; configure/dispose
|
|
47
|
+
// hold it so a new embed arriving mid-await cannot race extractorPromise
|
|
48
|
+
// reset / ext.dispose() while the prior extractor is still being torn down.
|
|
49
|
+
const GUARDED_ACTIONS = new Set(['embed', 'embed-batch', 'warmup', 'configure', 'dispose'])
|
|
50
|
+
|
|
51
|
+
function resetIdleTimer() {
|
|
52
|
+
if (_idleTimer) clearTimeout(_idleTimer)
|
|
53
|
+
if (IDLE_TIMEOUT_MS <= 0) return
|
|
54
|
+
_idleTimer = setTimeout(() => {
|
|
55
|
+
if (extractorPromise) {
|
|
56
|
+
extractorPromise.then(ext => { try { ext.dispose() } catch {} }).catch(() => {})
|
|
57
|
+
extractorPromise = null
|
|
58
|
+
const prevDevice = _device
|
|
59
|
+
_device = 'cpu'
|
|
60
|
+
process.stderr.write('[embed-worker] idle timeout — model disposed\n')
|
|
61
|
+
parentPort.postMessage({ type: 'idle-dispose', device: prevDevice, dtype: configuredDtype })
|
|
62
|
+
}
|
|
63
|
+
_idleTimer = null
|
|
64
|
+
}, IDLE_TIMEOUT_MS)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Set this process's CPU affinity to `mask` (bitmask of allowed logical
|
|
68
|
+
// processors). Windows-only and best-effort: there is no Node API, so go
|
|
69
|
+
// through PowerShell; a process may set its own affinity without elevation.
|
|
70
|
+
// Returns true on success; non-win32 or failure returns false (caller then
|
|
71
|
+
// skips the matching restore).
|
|
72
|
+
async function setSelfAffinity(mask) {
|
|
73
|
+
if (process.platform !== 'win32') return false
|
|
74
|
+
try {
|
|
75
|
+
await execFileAsync('powershell', ['-NoProfile', '-Command',
|
|
76
|
+
`(Get-Process -Id ${process.pid}).ProcessorAffinity = ${mask}`], { timeout: 8000, windowsHide: true })
|
|
77
|
+
return true
|
|
78
|
+
} catch {
|
|
79
|
+
return false
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function patchOrtThreads() {
|
|
84
|
+
if (ortPatched) return
|
|
85
|
+
try {
|
|
86
|
+
const require = createRequire(import.meta.url)
|
|
87
|
+
let ort = null
|
|
88
|
+
try {
|
|
89
|
+
const transformersPkg = require.resolve('@huggingface/transformers/package.json')
|
|
90
|
+
const transformersRequire = createRequire(pathToFileURL(transformersPkg))
|
|
91
|
+
ort = transformersRequire('onnxruntime-node')
|
|
92
|
+
} catch {
|
|
93
|
+
ort = require('onnxruntime-node')
|
|
94
|
+
}
|
|
95
|
+
if (!ort?.InferenceSession?.create) {
|
|
96
|
+
process.stderr.write('[embed-worker] ORT patch skipped: InferenceSession.create not found\n')
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
const origCreate = ort.InferenceSession.create.bind(ort.InferenceSession)
|
|
100
|
+
ort.InferenceSession.create = async function (pathOrBuffer, options = {}) {
|
|
101
|
+
if (!options.intraOpNumThreads) options.intraOpNumThreads = INTRA_OP_THREADS
|
|
102
|
+
if (!options.interOpNumThreads) options.interOpNumThreads = INTER_OP_THREADS
|
|
103
|
+
if (!options.graphOptimizationLevel) options.graphOptimizationLevel = GRAPH_OPT_LEVEL
|
|
104
|
+
return origCreate(pathOrBuffer, options)
|
|
105
|
+
}
|
|
106
|
+
ortPatched = true
|
|
107
|
+
process.stderr.write(`[embed-worker] ORT patched OK: intra=${INTRA_OP_THREADS} inter=${INTER_OP_THREADS} graphOpt=${GRAPH_OPT_LEVEL}\n`)
|
|
108
|
+
} catch (err) {
|
|
109
|
+
process.stderr.write(`[embed-worker] ORT patch failed: ${err?.message || err}\n`)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function loadExtractor() {
|
|
114
|
+
if (!extractorPromise) {
|
|
115
|
+
extractorPromise = (async () => {
|
|
116
|
+
parentPort.postMessage({ type: 'profile', record: { phase: 'baseline', model: MODEL_ID, device: _device, dtype: configuredDtype, note: 'pre-load' } })
|
|
117
|
+
patchOrtThreads()
|
|
118
|
+
const { pipeline, env } = await import('@huggingface/transformers')
|
|
119
|
+
env.allowLocalModels = false
|
|
120
|
+
try { mkdirSync(MODEL_CACHE_DIR, { recursive: true }) } catch {}
|
|
121
|
+
env.cacheDir = MODEL_CACHE_DIR
|
|
122
|
+
try { env.backends.onnx.wasm.numThreads = INTRA_OP_THREADS } catch {}
|
|
123
|
+
const opts = {}
|
|
124
|
+
if (configuredDtype && configuredDtype !== 'fp32') {
|
|
125
|
+
opts.dtype = configuredDtype
|
|
126
|
+
}
|
|
127
|
+
const startMs = Date.now()
|
|
128
|
+
let extractor
|
|
129
|
+
const requestedDevice = String(process.env.MIXDOG_MEMORY_EMBED_DEVICE || 'auto').trim().toLowerCase()
|
|
130
|
+
const preferGpu = requestedDevice === 'dml'
|
|
131
|
+
|| requestedDevice === 'gpu'
|
|
132
|
+
|| (requestedDevice === 'auto' && process.platform === 'win32')
|
|
133
|
+
// Cap CPU affinity for the heavy session-create so DirectML graph
|
|
134
|
+
// compilation cannot saturate every core (the fan lever). The process
|
|
135
|
+
// starts at full affinity, so the full mask is the correct restore
|
|
136
|
+
// baseline; restored in the finally below.
|
|
137
|
+
const _totalCores = os.cpus().length
|
|
138
|
+
const _fullAffinity = (2 ** _totalCores) - 1
|
|
139
|
+
const _loadAffinity = (2 ** Math.min(LOAD_AFFINITY_CORES, _totalCores)) - 1
|
|
140
|
+
const affinityCapped = await setSelfAffinity(_loadAffinity)
|
|
141
|
+
// Yield the cold-load CPU spike to foreground work: drop process priority
|
|
142
|
+
// for the heavy session-create, then restore it the instant the load
|
|
143
|
+
// resolves. setPriority is advisory (may EPERM on locked-down hosts), so
|
|
144
|
+
// guard it; the finally restore is the invariant — priority never stays
|
|
145
|
+
// depressed past the load.
|
|
146
|
+
let priorityLowered = false
|
|
147
|
+
try { os.setPriority(0, os.constants.priority.PRIORITY_BELOW_NORMAL); priorityLowered = true } catch {}
|
|
148
|
+
try {
|
|
149
|
+
if (preferGpu) {
|
|
150
|
+
extractor = await pipeline('feature-extraction', MODEL_ID, { ...opts, device: 'dml' })
|
|
151
|
+
_device = 'dml'
|
|
152
|
+
} else {
|
|
153
|
+
extractor = await pipeline('feature-extraction', MODEL_ID, { ...opts, device: 'cpu' })
|
|
154
|
+
_device = 'cpu'
|
|
155
|
+
}
|
|
156
|
+
} finally {
|
|
157
|
+
if (priorityLowered) {
|
|
158
|
+
// Restore is the invariant: never leave the worker pinned at
|
|
159
|
+
// BELOW_NORMAL. Retry once, then surface loudly if it still fails
|
|
160
|
+
// rather than silently swallowing a stuck-low state.
|
|
161
|
+
try {
|
|
162
|
+
os.setPriority(0, os.constants.priority.PRIORITY_NORMAL)
|
|
163
|
+
} catch {
|
|
164
|
+
try {
|
|
165
|
+
os.setPriority(0, os.constants.priority.PRIORITY_NORMAL)
|
|
166
|
+
} catch (e) {
|
|
167
|
+
process.stderr.write(`[embed-worker] WARN: process priority stuck below normal (restore failed: ${e?.message || e})\n`)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (affinityCapped) {
|
|
172
|
+
// Restore is the invariant: never leave the worker pinned to a core
|
|
173
|
+
// subset. setSelfAffinity never throws; retry once, then warn loudly.
|
|
174
|
+
let restored = await setSelfAffinity(_fullAffinity)
|
|
175
|
+
if (!restored) restored = await setSelfAffinity(_fullAffinity)
|
|
176
|
+
if (!restored) process.stderr.write(`[embed-worker] WARN: CPU affinity stuck on ${LOAD_AFFINITY_CORES} core(s); restore to all cores failed\n`)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const loadMs = Date.now() - startMs
|
|
180
|
+
process.stderr.write(`[embed-worker] loaded ${MODEL_ID} dtype=${configuredDtype} device=${_device} threads=${INTRA_OP_THREADS} in ${loadMs}ms\n`)
|
|
181
|
+
parentPort.postMessage({ type: 'profile', record: { phase: 'load', model: MODEL_ID, device: _device, dtype: configuredDtype, wallMs: loadMs } })
|
|
182
|
+
return extractor
|
|
183
|
+
})()
|
|
184
|
+
}
|
|
185
|
+
return extractorPromise
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function processMessage(msg) {
|
|
189
|
+
const { id, action } = msg
|
|
190
|
+
try {
|
|
191
|
+
switch (action) {
|
|
192
|
+
case 'embed-batch': {
|
|
193
|
+
if (_embedInFlight) {
|
|
194
|
+
_msgQueue.push(msg)
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
_embedInFlight = true
|
|
198
|
+
resetIdleTimer()
|
|
199
|
+
const extractor = await loadExtractor()
|
|
200
|
+
const texts = Array.isArray(msg.texts) ? msg.texts : []
|
|
201
|
+
if (texts.length === 0) {
|
|
202
|
+
parentPort.postMessage({ id, type: 'result', vectors: [], dims: 0, wallMs: 0, device: _device, dtype: configuredDtype })
|
|
203
|
+
break
|
|
204
|
+
}
|
|
205
|
+
const t0 = Date.now()
|
|
206
|
+
const output = await extractor(texts, { pooling: 'mean', normalize: true })
|
|
207
|
+
const wallMs = Date.now() - t0
|
|
208
|
+
if (!output.data?.length) throw new Error(`embed-batch output missing data (model=${MODEL_ID})`)
|
|
209
|
+
const total = output.data.length
|
|
210
|
+
if (total % texts.length !== 0) throw new Error(`embed-batch data length ${total} not divisible by texts ${texts.length}`)
|
|
211
|
+
const dims = total / texts.length
|
|
212
|
+
const vectors = new Array(texts.length)
|
|
213
|
+
for (let i = 0; i < texts.length; i++) vectors[i] = Array.from(output.data.subarray(i * dims, (i + 1) * dims))
|
|
214
|
+
parentPort.postMessage({ id, type: 'result', vectors, dims, wallMs, device: _device, dtype: configuredDtype })
|
|
215
|
+
break
|
|
216
|
+
// _embedInFlight cleared in drainQueue / catch
|
|
217
|
+
}
|
|
218
|
+
case 'embed': {
|
|
219
|
+
if (_embedInFlight) {
|
|
220
|
+
// Re-queue behind current — serialize all embed calls
|
|
221
|
+
_msgQueue.push(msg)
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
_embedInFlight = true
|
|
225
|
+
resetIdleTimer()
|
|
226
|
+
const extractor = await loadExtractor()
|
|
227
|
+
const t0 = Date.now()
|
|
228
|
+
const output = await extractor(msg.text, { pooling: 'mean', normalize: true })
|
|
229
|
+
const wallMs = Date.now() - t0
|
|
230
|
+
if (!output.data?.length) throw new Error(`embed output missing data (model=${MODEL_ID})`)
|
|
231
|
+
const dims = output.data.length
|
|
232
|
+
const vector = Array.from(output.data)
|
|
233
|
+
parentPort.postMessage({ id, type: 'result', vector, dims, wallMs, device: _device, dtype: configuredDtype })
|
|
234
|
+
break
|
|
235
|
+
// _embedInFlight cleared in finally below
|
|
236
|
+
}
|
|
237
|
+
case 'warmup': {
|
|
238
|
+
if (_embedInFlight) {
|
|
239
|
+
_msgQueue.push(msg)
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
_embedInFlight = true
|
|
243
|
+
resetIdleTimer()
|
|
244
|
+
const extractor = await loadExtractor()
|
|
245
|
+
const t0 = Date.now()
|
|
246
|
+
const warmupOutput = await extractor('warmup', { pooling: 'mean', normalize: true })
|
|
247
|
+
const wallMs = Date.now() - t0
|
|
248
|
+
if (!warmupOutput.data?.length) throw new Error(`warmup output missing data (model=${MODEL_ID})`)
|
|
249
|
+
const measuredDims = warmupOutput.data.length
|
|
250
|
+
parentPort.postMessage({ id, type: 'result', dims: measuredDims, wallMs, device: _device, dtype: configuredDtype })
|
|
251
|
+
parentPort.postMessage({ type: 'profile', record: { phase: 'warmup', model: MODEL_ID, device: _device, dtype: configuredDtype, wallMs } })
|
|
252
|
+
resetIdleTimer()
|
|
253
|
+
break
|
|
254
|
+
}
|
|
255
|
+
case 'configure': {
|
|
256
|
+
if (_embedInFlight) {
|
|
257
|
+
_msgQueue.push(msg)
|
|
258
|
+
return
|
|
259
|
+
}
|
|
260
|
+
_embedInFlight = true
|
|
261
|
+
if (_idleTimer) { clearTimeout(_idleTimer); _idleTimer = null }
|
|
262
|
+
if (msg.dtype != null) {
|
|
263
|
+
const dt = String(msg.dtype).trim().toLowerCase()
|
|
264
|
+
configuredDtype = ['fp32', 'fp16', 'q8', 'q4'].includes(dt) ? dt : DEFAULT_DTYPE
|
|
265
|
+
}
|
|
266
|
+
if (extractorPromise) {
|
|
267
|
+
try {
|
|
268
|
+
const ext = await extractorPromise
|
|
269
|
+
try { ext.dispose() } catch {}
|
|
270
|
+
} catch {}
|
|
271
|
+
extractorPromise = null
|
|
272
|
+
_device = 'cpu'
|
|
273
|
+
}
|
|
274
|
+
parentPort.postMessage({ id, type: 'result' })
|
|
275
|
+
break
|
|
276
|
+
}
|
|
277
|
+
case 'dispose': {
|
|
278
|
+
if (_embedInFlight) {
|
|
279
|
+
_msgQueue.push(msg)
|
|
280
|
+
return
|
|
281
|
+
}
|
|
282
|
+
_embedInFlight = true
|
|
283
|
+
if (_idleTimer) { clearTimeout(_idleTimer); _idleTimer = null }
|
|
284
|
+
const prevDevice = _device
|
|
285
|
+
if (extractorPromise) {
|
|
286
|
+
try {
|
|
287
|
+
const ext = await extractorPromise
|
|
288
|
+
try { ext.dispose() } catch {}
|
|
289
|
+
} catch {}
|
|
290
|
+
extractorPromise = null
|
|
291
|
+
_device = 'cpu'
|
|
292
|
+
}
|
|
293
|
+
parentPort.postMessage({ id, type: 'result', prevDevice, dtype: configuredDtype })
|
|
294
|
+
break
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
} catch (err) {
|
|
298
|
+
parentPort.postMessage({ id, type: 'error', message: err?.message || String(err) })
|
|
299
|
+
if (GUARDED_ACTIONS.has(action)) _embedInFlight = false
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function drainQueue() {
|
|
304
|
+
while (_msgQueue.length > 0) {
|
|
305
|
+
const next = _msgQueue.shift()
|
|
306
|
+
_embedInFlight = false
|
|
307
|
+
await processMessage(next)
|
|
308
|
+
}
|
|
309
|
+
_embedInFlight = false
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
parentPort.on('message', async (msg) => {
|
|
313
|
+
// All guarded actions (embed/embed-batch/warmup/configure/dispose) wait
|
|
314
|
+
// behind any in-flight guarded action. Without queueing configure/dispose
|
|
315
|
+
// here, a new embed arriving mid-dispose would bypass the queue and race
|
|
316
|
+
// extractorPromise reset / ext.dispose() against the prior tear-down.
|
|
317
|
+
if (_embedInFlight) {
|
|
318
|
+
_msgQueue.push(msg)
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
await processMessage(msg)
|
|
322
|
+
if (GUARDED_ACTIONS.has(msg.action)) await drainQueue()
|
|
323
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* llm-worker-host.mjs — LLM worker host using direct spawn (no fork).
|
|
3
|
+
*
|
|
4
|
+
* Replaces fork-based approach that broke in bundled environments
|
|
5
|
+
* where the separate worker .mjs file cannot be resolved.
|
|
6
|
+
* Each task spawns a child process directly and communicates via stdio.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
let active = false
|
|
10
|
+
|
|
11
|
+
export function startLlmWorker() {
|
|
12
|
+
active = true
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function stopLlmWorker() {
|
|
16
|
+
active = false
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Barrel — original memory-cycle module split into memory-embed / memory-cycle1 / memory-cycle2.
|
|
2
|
+
// External callers import from this path; the split is transparent.
|
|
3
|
+
export {
|
|
4
|
+
syncRootEmbedding, deleteRootEmbedding, flushEmbeddingDirty, inferChunkProjectId,
|
|
5
|
+
} from './memory-embed.mjs'
|
|
6
|
+
export { runCycle1 } from './memory-cycle1.mjs'
|
|
7
|
+
export {
|
|
8
|
+
runCycle2, runUnifiedGate, applySimpleStatus, applyUpdate, applyMerge, CYCLE2_ACTIVE_TARGET_CAP,
|
|
9
|
+
parseInterval,
|
|
10
|
+
} from './memory-cycle2.mjs'
|
|
11
|
+
export { runCycle3 } from './memory-cycle3.mjs'
|