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.
Files changed (404) hide show
  1. package/.claude-plugin/marketplace.json +31 -0
  2. package/.claude-plugin/plugin.json +20 -0
  3. package/.gitattributes +34 -0
  4. package/.mcp.json +14 -0
  5. package/ARCHITECTURE.md +77 -0
  6. package/CHANGELOG.md +7 -0
  7. package/CONTRIBUTING.md +45 -0
  8. package/DATA-FLOW.md +79 -0
  9. package/LICENSE +21 -0
  10. package/README.md +389 -0
  11. package/SECURITY.md +138 -0
  12. package/UNINSTALL.md +112 -0
  13. package/agents/maintenance.md +5 -0
  14. package/agents/memory-classification.md +30 -0
  15. package/agents/scheduler-task.md +18 -0
  16. package/agents/webhook-handler.md +27 -0
  17. package/agents/worker.md +24 -0
  18. package/bin/bridge +133 -0
  19. package/bin/statusline-launcher.mjs +78 -0
  20. package/bin/statusline-lib.mjs +550 -0
  21. package/bin/statusline.mjs +607 -0
  22. package/bun.lock +802 -0
  23. package/commands/config.md +16 -0
  24. package/commands/doctor.md +13 -0
  25. package/commands/setup.md +17 -0
  26. package/defaults/cycle3-review-prompt.md +90 -0
  27. package/defaults/hidden-roles.json +65 -0
  28. package/defaults/memory-chunk-prompt.md +63 -0
  29. package/defaults/memory-promote-prompt.md +135 -0
  30. package/defaults/mixdog-config.template.json +27 -0
  31. package/defaults/user-workflow.json +8 -0
  32. package/defaults/user-workflow.md +12 -0
  33. package/hooks/hooks.json +73 -0
  34. package/hooks/lib/active-instance.cjs +77 -0
  35. package/hooks/lib/permission-evaluator.cjs +411 -0
  36. package/hooks/lib/permission-route.cjs +63 -0
  37. package/hooks/lib/permission-rules.cjs +170 -0
  38. package/hooks/lib/settings-loader.cjs +116 -0
  39. package/hooks/post-tool-use.cjs +84 -0
  40. package/hooks/pre-mcp-sandbox.cjs +158 -0
  41. package/hooks/pre-tool-subagent.cjs +253 -0
  42. package/hooks/session-start.cjs +1372 -0
  43. package/hooks/turn-timer.cjs +82 -0
  44. package/lib/claude-md-writer.cjs +386 -0
  45. package/lib/config-cjs.cjs +61 -0
  46. package/lib/hook-pipe-path.cjs +10 -0
  47. package/lib/keychain-cjs.cjs +263 -0
  48. package/lib/plugin-paths.cjs +61 -0
  49. package/lib/rules-builder.cjs +241 -0
  50. package/lib/text-utils.cjs +61 -0
  51. package/native/README.md +117 -0
  52. package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
  53. package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
  54. package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
  55. package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
  56. package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
  57. package/package.json +107 -0
  58. package/prompts/code-review.txt +16 -0
  59. package/prompts/security-audit.txt +17 -0
  60. package/rules/bridge/00-common.md +39 -0
  61. package/rules/bridge/20-skip-protocol.md +18 -0
  62. package/rules/bridge/30-explorer.md +33 -0
  63. package/rules/bridge/40-cycle1-agent.md +52 -0
  64. package/rules/bridge/41-cycle2-agent.md +62 -0
  65. package/rules/bridge/42-cycle3-agent.md +44 -0
  66. package/rules/lead/00-tool-lead.md +61 -0
  67. package/rules/lead/01-general.md +23 -0
  68. package/rules/lead/02-channels.md +49 -0
  69. package/rules/lead/03-team.md +27 -0
  70. package/rules/lead/04-workflow.md +20 -0
  71. package/rules/shared/00-language.md +14 -0
  72. package/rules/shared/01-tool.md +138 -0
  73. package/scripts/bootstrap.mjs +184 -0
  74. package/scripts/bridge-unify-smoke.mjs +308 -0
  75. package/scripts/build-runtime-linux.sh +348 -0
  76. package/scripts/build-runtime-macos.sh +217 -0
  77. package/scripts/build-runtime-windows.ps1 +242 -0
  78. package/scripts/builtin-utils-smoke.mjs +392 -0
  79. package/scripts/check-json.mjs +45 -0
  80. package/scripts/check-syntax-changed.mjs +102 -0
  81. package/scripts/check-syntax.mjs +58 -0
  82. package/scripts/code-graph-batch.test.mjs +33 -0
  83. package/scripts/config-preserve-smoke.mjs +180 -0
  84. package/scripts/doctor.mjs +484 -0
  85. package/scripts/edit-normalize-fuzz.mjs +130 -0
  86. package/scripts/edit-normalize-smoke.mjs +401 -0
  87. package/scripts/edit-operation-smoke.mjs +369 -0
  88. package/scripts/edit2-smoke.mjs +63 -0
  89. package/scripts/fuzzy-e2e.mjs +28 -0
  90. package/scripts/fuzzy-smoke.mjs +26 -0
  91. package/scripts/generate-runtime-manifest.mjs +166 -0
  92. package/scripts/guard-smoke.mjs +66 -0
  93. package/scripts/hidden-role-schema-smoke.mjs +162 -0
  94. package/scripts/hook-routing-smoke.mjs +29 -0
  95. package/scripts/inject-input.ps1 +204 -0
  96. package/scripts/io-complex-smoke.mjs +667 -0
  97. package/scripts/io-explore-bench.mjs +424 -0
  98. package/scripts/io-guardrails-smoke.mjs +205 -0
  99. package/scripts/io-mini-bench-baseline.json +11 -0
  100. package/scripts/io-mini-bench.mjs +216 -0
  101. package/scripts/io-route-harness.mjs +933 -0
  102. package/scripts/io-telemetry-report.mjs +691 -0
  103. package/scripts/mutation-bench.mjs +564 -0
  104. package/scripts/mutation-io-smoke.mjs +1081 -0
  105. package/scripts/native-patch-bridge-smoke.mjs +288 -0
  106. package/scripts/native-patch-smoke.mjs +304 -0
  107. package/scripts/patch-interior-context-smoke.mjs +49 -0
  108. package/scripts/patch-newline-utf8-smoke.mjs +157 -0
  109. package/scripts/perf-hook-smoke.mjs +71 -0
  110. package/scripts/permission-eval-smoke.mjs +426 -0
  111. package/scripts/prep-patch.mjs +53 -0
  112. package/scripts/prep-shim.mjs +96 -0
  113. package/scripts/provider-cache-smoke.mjs +687 -0
  114. package/scripts/report-runtime-health.mjs +132 -0
  115. package/scripts/run-mcp.mjs +1547 -0
  116. package/scripts/salvage-v4a-shatter.test.mjs +58 -0
  117. package/scripts/scoped-cache-io-smoke.mjs +103 -0
  118. package/scripts/shell-policy-round3-smoke.mjs +46 -0
  119. package/scripts/smoke-runtime-negative.ps1 +100 -0
  120. package/scripts/smoke-runtime-negative.sh +95 -0
  121. package/scripts/stall-policy-smoke.mjs +50 -0
  122. package/scripts/start-memory-worker.mjs +23 -0
  123. package/scripts/statusline-launcher-smoke.mjs +82 -0
  124. package/scripts/stress-atomic-write.mjs +1028 -0
  125. package/scripts/test-config-rmw-restore.mjs +122 -0
  126. package/scripts/test-fault-inject.mjs +164 -0
  127. package/scripts/test-large-file.mjs +174 -0
  128. package/scripts/tool-edge-smoke.mjs +209 -0
  129. package/scripts/uninstall.mjs +201 -0
  130. package/scripts/webhook-selfheal-smoke.mjs +29 -0
  131. package/scripts/write-overwrite-guard-smoke.mjs +56 -0
  132. package/server-main.mjs +3055 -0
  133. package/server.mjs +468 -0
  134. package/setup/config-merge.mjs +254 -0
  135. package/setup/install.mjs +120 -0
  136. package/setup/launch-core.mjs +507 -0
  137. package/setup/launch.mjs +101 -0
  138. package/setup/setup-server.mjs +3206 -0
  139. package/setup/setup.html +3693 -0
  140. package/skills/retro-skill-proposer/SKILL.md +92 -0
  141. package/skills/schedule-add/SKILL.md +77 -0
  142. package/skills/setup/SKILL.md +346 -0
  143. package/skills/webhook-add/SKILL.md +81 -0
  144. package/src/agent/bridge-stall-watchdog.mjs +337 -0
  145. package/src/agent/index.mjs +2138 -0
  146. package/src/agent/orchestrator/activity-bus.mjs +38 -0
  147. package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +1010 -0
  148. package/src/agent/orchestrator/bridge-retry.mjs +220 -0
  149. package/src/agent/orchestrator/bridge-trace.mjs +583 -0
  150. package/src/agent/orchestrator/cache-mtime.mjs +58 -0
  151. package/src/agent/orchestrator/config.mjs +358 -0
  152. package/src/agent/orchestrator/context/collect.mjs +651 -0
  153. package/src/agent/orchestrator/dispatch-persist.mjs +549 -0
  154. package/src/agent/orchestrator/drain-registry.mjs +50 -0
  155. package/src/agent/orchestrator/explore-validator.mjs +8 -0
  156. package/src/agent/orchestrator/internal-roles.mjs +118 -0
  157. package/src/agent/orchestrator/internal-tools.mjs +88 -0
  158. package/src/agent/orchestrator/jobs.mjs +116 -0
  159. package/src/agent/orchestrator/mcp/client.mjs +364 -0
  160. package/src/agent/orchestrator/providers/anthropic-betas.mjs +21 -0
  161. package/src/agent/orchestrator/providers/anthropic-oauth.mjs +1745 -0
  162. package/src/agent/orchestrator/providers/anthropic.mjs +437 -0
  163. package/src/agent/orchestrator/providers/gemini.mjs +1175 -0
  164. package/src/agent/orchestrator/providers/grok-oauth.mjs +782 -0
  165. package/src/agent/orchestrator/providers/model-catalog.mjs +241 -0
  166. package/src/agent/orchestrator/providers/openai-compat.mjs +1467 -0
  167. package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +1890 -0
  168. package/src/agent/orchestrator/providers/openai-oauth.mjs +1307 -0
  169. package/src/agent/orchestrator/providers/openai-ws.mjs +104 -0
  170. package/src/agent/orchestrator/providers/registry.mjs +192 -0
  171. package/src/agent/orchestrator/providers/retry-classifier.mjs +325 -0
  172. package/src/agent/orchestrator/session/abort-lookup.mjs +13 -0
  173. package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
  174. package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
  175. package/src/agent/orchestrator/session/cache/read-cache.mjs +319 -0
  176. package/src/agent/orchestrator/session/cache/scoped-cache-outcome.mjs +11 -0
  177. package/src/agent/orchestrator/session/cache/scoped-cache.mjs +361 -0
  178. package/src/agent/orchestrator/session/cache/util.mjs +49 -0
  179. package/src/agent/orchestrator/session/loop.mjs +1478 -0
  180. package/src/agent/orchestrator/session/manager.mjs +1975 -0
  181. package/src/agent/orchestrator/session/read-dedup.mjs +6 -0
  182. package/src/agent/orchestrator/session/result-classification.mjs +65 -0
  183. package/src/agent/orchestrator/session/save-session-worker.mjs +18 -0
  184. package/src/agent/orchestrator/session/store.mjs +624 -0
  185. package/src/agent/orchestrator/session/stream-watchdog.mjs +130 -0
  186. package/src/agent/orchestrator/session/tool-result-offload.mjs +166 -0
  187. package/src/agent/orchestrator/session/trim.mjs +491 -0
  188. package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +115 -0
  189. package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +327 -0
  190. package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +150 -0
  191. package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +228 -0
  192. package/src/agent/orchestrator/smart-bridge/index.mjs +215 -0
  193. package/src/agent/orchestrator/smart-bridge/profiles.mjs +37 -0
  194. package/src/agent/orchestrator/smart-bridge/registry.mjs +348 -0
  195. package/src/agent/orchestrator/smart-bridge/session-builder.mjs +116 -0
  196. package/src/agent/orchestrator/stall-policy.mjs +195 -0
  197. package/src/agent/orchestrator/tool-loop-guard.mjs +75 -0
  198. package/src/agent/orchestrator/tools/bash-policy-scan.mjs +77 -0
  199. package/src/agent/orchestrator/tools/bash-session.mjs +721 -0
  200. package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
  201. package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +455 -0
  202. package/src/agent/orchestrator/tools/builtin/atomic-write.mjs +236 -0
  203. package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +480 -0
  204. package/src/agent/orchestrator/tools/builtin/binary-file.mjs +76 -0
  205. package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +256 -0
  206. package/src/agent/orchestrator/tools/builtin/cache-layers.mjs +386 -0
  207. package/src/agent/orchestrator/tools/builtin/cwd-utils.mjs +37 -0
  208. package/src/agent/orchestrator/tools/builtin/device-paths.mjs +154 -0
  209. package/src/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +292 -0
  210. package/src/agent/orchestrator/tools/builtin/diff-utils.mjs +109 -0
  211. package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +58 -0
  212. package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +240 -0
  213. package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +113 -0
  214. package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +74 -0
  215. package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +242 -0
  216. package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +211 -0
  217. package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +1364 -0
  218. package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +126 -0
  219. package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +141 -0
  220. package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +194 -0
  221. package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +60 -0
  222. package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +168 -0
  223. package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +173 -0
  224. package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +48 -0
  225. package/src/agent/orchestrator/tools/builtin/fs-reachability.mjs +48 -0
  226. package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +99 -0
  227. package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +170 -0
  228. package/src/agent/orchestrator/tools/builtin/grep-formatting.mjs +113 -0
  229. package/src/agent/orchestrator/tools/builtin/hash-utils.mjs +6 -0
  230. package/src/agent/orchestrator/tools/builtin/list-formatting.mjs +7 -0
  231. package/src/agent/orchestrator/tools/builtin/list-tool.mjs +593 -0
  232. package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +89 -0
  233. package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +300 -0
  234. package/src/agent/orchestrator/tools/builtin/open-config-tool.mjs +26 -0
  235. package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +152 -0
  236. package/src/agent/orchestrator/tools/builtin/path-locks.mjs +35 -0
  237. package/src/agent/orchestrator/tools/builtin/path-utils.mjs +201 -0
  238. package/src/agent/orchestrator/tools/builtin/read-args.mjs +103 -0
  239. package/src/agent/orchestrator/tools/builtin/read-batch.mjs +172 -0
  240. package/src/agent/orchestrator/tools/builtin/read-constants.mjs +40 -0
  241. package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
  242. package/src/agent/orchestrator/tools/builtin/read-image-resize.mjs +189 -0
  243. package/src/agent/orchestrator/tools/builtin/read-image.mjs +88 -0
  244. package/src/agent/orchestrator/tools/builtin/read-lines.mjs +12 -0
  245. package/src/agent/orchestrator/tools/builtin/read-mode-tool.mjs +455 -0
  246. package/src/agent/orchestrator/tools/builtin/read-open.mjs +190 -0
  247. package/src/agent/orchestrator/tools/builtin/read-range-index.mjs +271 -0
  248. package/src/agent/orchestrator/tools/builtin/read-ranges.mjs +26 -0
  249. package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +728 -0
  250. package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +173 -0
  251. package/src/agent/orchestrator/tools/builtin/read-special-files.mjs +268 -0
  252. package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
  253. package/src/agent/orchestrator/tools/builtin/read-tool.mjs +530 -0
  254. package/src/agent/orchestrator/tools/builtin/read-windows.mjs +107 -0
  255. package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +196 -0
  256. package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +422 -0
  257. package/src/agent/orchestrator/tools/builtin/search-builders.mjs +158 -0
  258. package/src/agent/orchestrator/tools/builtin/search-tool.mjs +869 -0
  259. package/src/agent/orchestrator/tools/builtin/shell-analysis.mjs +653 -0
  260. package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +936 -0
  261. package/src/agent/orchestrator/tools/builtin/shell-output.mjs +36 -0
  262. package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +214 -0
  263. package/src/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +143 -0
  264. package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +206 -0
  265. package/src/agent/orchestrator/tools/builtin/snapshot-validation.mjs +98 -0
  266. package/src/agent/orchestrator/tools/builtin/text-stats.mjs +69 -0
  267. package/src/agent/orchestrator/tools/builtin/windows-roots.mjs +23 -0
  268. package/src/agent/orchestrator/tools/builtin/write-tool.mjs +401 -0
  269. package/src/agent/orchestrator/tools/builtin.mjs +500 -0
  270. package/src/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +39 -0
  271. package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
  272. package/src/agent/orchestrator/tools/code-graph.mjs +4095 -0
  273. package/src/agent/orchestrator/tools/cwd-tool.mjs +298 -0
  274. package/src/agent/orchestrator/tools/destructive-warning.mjs +323 -0
  275. package/src/agent/orchestrator/tools/edit-normalize.mjs +603 -0
  276. package/src/agent/orchestrator/tools/env-scrub.mjs +100 -0
  277. package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +144 -0
  278. package/src/agent/orchestrator/tools/graph-manifest.json +26 -0
  279. package/src/agent/orchestrator/tools/host-input.mjs +204 -0
  280. package/src/agent/orchestrator/tools/mutation-content-cache.mjs +67 -0
  281. package/src/agent/orchestrator/tools/mutation-planner.mjs +75 -0
  282. package/src/agent/orchestrator/tools/next-call-utils.mjs +48 -0
  283. package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +133 -0
  284. package/src/agent/orchestrator/tools/patch-manifest.json +26 -0
  285. package/src/agent/orchestrator/tools/patch-tool-defs.mjs +20 -0
  286. package/src/agent/orchestrator/tools/patch.mjs +2754 -0
  287. package/src/agent/orchestrator/tools/progress-message.mjs +118 -0
  288. package/src/agent/orchestrator/tools/result-compression.mjs +279 -0
  289. package/src/agent/orchestrator/tools/shell-command.mjs +865 -0
  290. package/src/agent/orchestrator/tools/shell-exec-policy.mjs +89 -0
  291. package/src/agent/orchestrator/tools/shell-policy-danger-target.mjs +27 -0
  292. package/src/agent/orchestrator/tools/shell-policy-imports.mjs +7 -0
  293. package/src/agent/orchestrator/tools/shell-policy.mjs +345 -0
  294. package/src/agent/orchestrator/tools/shell-snapshot.mjs +313 -0
  295. package/src/agent/orchestrator/workflow-store.mjs +93 -0
  296. package/src/agent/tool-defs.mjs +103 -0
  297. package/src/channels/backends/discord.mjs +784 -0
  298. package/src/channels/data/voice-runtime-manifest.json +138 -0
  299. package/src/channels/index.mjs +3229 -0
  300. package/src/channels/lib/cli-worker-host.mjs +12 -0
  301. package/src/channels/lib/config-lock.mjs +13 -0
  302. package/src/channels/lib/config.mjs +292 -0
  303. package/src/channels/lib/drop-trace.mjs +71 -0
  304. package/src/channels/lib/event-pipeline.mjs +81 -0
  305. package/src/channels/lib/event-queue.mjs +345 -0
  306. package/src/channels/lib/executor.mjs +168 -0
  307. package/src/channels/lib/format.mjs +188 -0
  308. package/src/channels/lib/holidays.mjs +138 -0
  309. package/src/channels/lib/hook-pipe-server.mjs +802 -0
  310. package/src/channels/lib/interaction-workflows.mjs +184 -0
  311. package/src/channels/lib/memory-client.mjs +149 -0
  312. package/src/channels/lib/output-forwarder.mjs +765 -0
  313. package/src/channels/lib/runtime-paths.mjs +479 -0
  314. package/src/channels/lib/scheduler.mjs +723 -0
  315. package/src/channels/lib/session-control.mjs +36 -0
  316. package/src/channels/lib/session-discovery.mjs +103 -0
  317. package/src/channels/lib/settings.mjs +11 -0
  318. package/src/channels/lib/state-file.mjs +68 -0
  319. package/src/channels/lib/status-snapshot.mjs +219 -0
  320. package/src/channels/lib/tool-format.mjs +140 -0
  321. package/src/channels/lib/transcript-discovery.mjs +195 -0
  322. package/src/channels/lib/voice-runtime-fetcher.mjs +734 -0
  323. package/src/channels/lib/webhook.mjs +1179 -0
  324. package/src/channels/lib/whisper-server.mjs +477 -0
  325. package/src/channels/tool-defs.mjs +170 -0
  326. package/src/daemon/host.mjs +118 -0
  327. package/src/daemon/mcp-transport.mjs +47 -0
  328. package/src/daemon/session.mjs +100 -0
  329. package/src/daemon/thin-client.mjs +71 -0
  330. package/src/daemon/transport.mjs +163 -0
  331. package/src/memory/data/runtime-manifest.json +40 -0
  332. package/src/memory/index.mjs +3305 -0
  333. package/src/memory/lib/agent-ipc.mjs +93 -0
  334. package/src/memory/lib/bridge-trace-queries.mjs +120 -0
  335. package/src/memory/lib/core-memory-store.mjs +330 -0
  336. package/src/memory/lib/embedding-provider.mjs +269 -0
  337. package/src/memory/lib/embedding-worker.mjs +323 -0
  338. package/src/memory/lib/llm-worker-host.mjs +17 -0
  339. package/src/memory/lib/memory-cycle.mjs +11 -0
  340. package/src/memory/lib/memory-cycle1.mjs +641 -0
  341. package/src/memory/lib/memory-cycle2.mjs +1284 -0
  342. package/src/memory/lib/memory-cycle3.mjs +540 -0
  343. package/src/memory/lib/memory-embed.mjs +299 -0
  344. package/src/memory/lib/memory-extraction.mjs +5 -0
  345. package/src/memory/lib/memory-maintenance-store.mjs +32 -0
  346. package/src/memory/lib/memory-ops-policy.mjs +190 -0
  347. package/src/memory/lib/memory-recall-id-patch.mjs +15 -0
  348. package/src/memory/lib/memory-recall-read-query.mjs +7 -0
  349. package/src/memory/lib/memory-recall-scope-filter.mjs +63 -0
  350. package/src/memory/lib/memory-recall-store.mjs +621 -0
  351. package/src/memory/lib/memory-retrievers.mjs +112 -0
  352. package/src/memory/lib/memory-score.mjs +71 -0
  353. package/src/memory/lib/memory-text-utils.mjs +58 -0
  354. package/src/memory/lib/memory.mjs +412 -0
  355. package/src/memory/lib/model-profile.mjs +85 -0
  356. package/src/memory/lib/pg/adapter.mjs +308 -0
  357. package/src/memory/lib/pg/process.mjs +360 -0
  358. package/src/memory/lib/pg/supervisor.mjs +396 -0
  359. package/src/memory/lib/project-id-resolver.mjs +86 -0
  360. package/src/memory/lib/runtime-fetcher.mjs +442 -0
  361. package/src/memory/lib/trace-store.mjs +728 -0
  362. package/src/memory/tool-defs.mjs +79 -0
  363. package/src/search/index.mjs +1173 -0
  364. package/src/search/lib/backends/anthropic-oauth.mjs +98 -0
  365. package/src/search/lib/backends/exa.mjs +50 -0
  366. package/src/search/lib/backends/firecrawl.mjs +61 -0
  367. package/src/search/lib/backends/gemini-api.mjs +83 -0
  368. package/src/search/lib/backends/grok-oauth.mjs +86 -0
  369. package/src/search/lib/backends/index.mjs +150 -0
  370. package/src/search/lib/backends/openai-api.mjs +144 -0
  371. package/src/search/lib/backends/openai-oauth.mjs +98 -0
  372. package/src/search/lib/backends/openai-web-search.mjs +76 -0
  373. package/src/search/lib/backends/tavily.mjs +55 -0
  374. package/src/search/lib/backends/xai-api.mjs +113 -0
  375. package/src/search/lib/cache.mjs +131 -0
  376. package/src/search/lib/config.mjs +192 -0
  377. package/src/search/lib/formatter.mjs +115 -0
  378. package/src/search/lib/provider-usage.mjs +67 -0
  379. package/src/search/lib/providers.mjs +47 -0
  380. package/src/search/lib/search-intent.mjs +109 -0
  381. package/src/search/lib/setup-handler.mjs +261 -0
  382. package/src/search/lib/state.mjs +201 -0
  383. package/src/search/lib/web-tools.mjs +1207 -0
  384. package/src/search/tool-defs.mjs +83 -0
  385. package/src/setup/defender-exclusion.mjs +183 -0
  386. package/src/shared/abort-controller.mjs +15 -0
  387. package/src/shared/atomic-file.mjs +420 -0
  388. package/src/shared/config.mjs +350 -0
  389. package/src/shared/daemon-recycle.mjs +108 -0
  390. package/src/shared/disable-claude-builtins.mjs +88 -0
  391. package/src/shared/err-text.mjs +12 -0
  392. package/src/shared/llm/cost.mjs +66 -0
  393. package/src/shared/llm/http-agent.mjs +123 -0
  394. package/src/shared/llm/index.mjs +41 -0
  395. package/src/shared/llm/pid-cleanup.mjs +27 -0
  396. package/src/shared/llm/usage-log.mjs +47 -0
  397. package/src/shared/plugin-paths.mjs +58 -0
  398. package/src/shared/schedules-store.mjs +70 -0
  399. package/src/shared/seed.mjs +119 -0
  400. package/src/shared/user-cwd.mjs +213 -0
  401. package/src/shared/user-data-guard.mjs +238 -0
  402. package/src/status/aggregator.mjs +584 -0
  403. package/src/status/server.mjs +413 -0
  404. 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'