mixdog 0.9.95 → 0.9.97

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 (282) hide show
  1. package/LICENSES/Apache-2.0.txt +201 -0
  2. package/LICENSES/MIT.txt +56 -0
  3. package/LICENSES/codex-NOTICE.txt +6 -0
  4. package/NOTICE.md +46 -9
  5. package/README.md +0 -17
  6. package/package.json +16 -12
  7. package/scripts/.tmp-coverage-report.mjs +96 -0
  8. package/scripts/build-tui.mjs +17 -1
  9. package/scripts/{channel-daemon-stub.mjs → daemon-stub.mjs} +18 -12
  10. package/scripts/fixtures/patch-replay-corpus.json +98 -0
  11. package/src/defaults/mixdog-config.template.json +5 -8
  12. package/src/defaults/skills/setup/SKILL.md +1 -1
  13. package/src/lib/keychain-cjs.cjs +1 -1
  14. package/src/lib/mixdog-debug.cjs +2 -3
  15. package/src/rules/agent/30-explorer.md +7 -4
  16. package/src/rules/lead/01-general.md +1 -2
  17. package/src/rules/lead/lead-tool.md +0 -3
  18. package/src/rules/shared/01-tool.md +21 -29
  19. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +18 -2
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +31 -1
  21. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +6 -17
  22. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +12 -1
  23. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +4 -13
  24. package/src/runtime/agent/orchestrator/config.mjs +246 -67
  25. package/src/runtime/agent/orchestrator/mcp/client.mjs +104 -41
  26. package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +23 -0
  27. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +113 -14
  28. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +5 -1
  29. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +32 -11
  30. package/src/runtime/agent/orchestrator/providers/gemini.mjs +89 -2
  31. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +12 -1
  32. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +19 -1
  33. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +144 -39
  34. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +22 -8
  35. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +15 -1
  36. package/src/runtime/agent/orchestrator/providers/openai-transport-policy.mjs +1 -1
  37. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +3 -4
  38. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +3 -6
  39. package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -2
  40. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +67 -7
  41. package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +291 -0
  42. package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +21 -0
  43. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +36 -10
  44. package/src/runtime/agent/orchestrator/session/compact.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +22 -33
  46. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +13 -0
  47. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +2 -2
  48. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +0 -24
  49. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +21 -29
  50. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +7 -10
  51. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +45 -25
  52. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -3
  53. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +142 -84
  54. package/src/runtime/agent/orchestrator/session/manager/prefetch-bridge.mjs +12 -6
  55. package/src/runtime/agent/orchestrator/session/manager/runtime-loaders.mjs +4 -0
  56. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +31 -1
  57. package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +38 -2
  58. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +29 -9
  59. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -0
  60. package/src/runtime/agent/orchestrator/session/result-classification.mjs +65 -0
  61. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +86 -8
  62. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +10 -3
  63. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +4 -0
  64. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +71 -42
  65. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +4 -33
  66. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -5
  67. package/src/runtime/agent/orchestrator/stall-policy.mjs +1 -1
  68. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
  69. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +35 -1
  70. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +116 -26
  71. package/src/runtime/agent/orchestrator/tools/builtin/binary-file.mjs +92 -19
  72. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +8 -9
  73. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +2 -2
  74. package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +2 -2
  75. package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +2 -2
  76. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +6 -15
  77. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +12 -26
  78. package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +5 -5
  79. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -33
  80. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +42 -45
  81. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +27 -9
  82. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +7 -0
  83. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +17 -39
  84. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +73 -0
  85. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +1 -1
  86. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +0 -2
  87. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +3 -8
  88. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +30 -0
  89. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +5 -5
  90. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +142 -26
  91. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +12 -1
  92. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +47 -19
  93. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -2
  94. package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +1 -2
  95. package/src/runtime/agent/orchestrator/tools/lib/pwsh-standby-pool.mjs +83 -13
  96. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +39 -1
  97. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +94 -20
  99. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +16 -0
  100. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +108 -12
  101. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +12 -11
  102. package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
  103. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +63 -9
  104. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +2 -2
  105. package/src/runtime/channels/lib/config.mjs +34 -34
  106. package/src/runtime/channels/lib/inbound-handler.mjs +8 -8
  107. package/src/runtime/channels/lib/interaction-handlers.mjs +6 -6
  108. package/src/runtime/channels/lib/memory-client.mjs +31 -157
  109. package/src/runtime/channels/lib/output-forwarder.mjs +16 -16
  110. package/src/runtime/channels/lib/owned-runtime.mjs +60 -60
  111. package/src/runtime/channels/lib/owner-heartbeat.mjs +1 -1
  112. package/src/runtime/channels/lib/{backend-dispatch.mjs → provider-dispatch.mjs} +6 -6
  113. package/src/runtime/channels/lib/runtime-paths.mjs +4 -30
  114. package/src/runtime/channels/lib/scheduler.mjs +2 -2
  115. package/src/runtime/channels/lib/status-snapshot.mjs +3 -3
  116. package/src/runtime/channels/lib/tool-dispatch.mjs +4 -4
  117. package/src/runtime/channels/lib/worker-bootstrap.mjs +1 -1
  118. package/src/runtime/channels/lib/worker-ipc.mjs +7 -7
  119. package/src/runtime/channels/lib/worker-main.mjs +38 -38
  120. package/src/runtime/channels/{backends → providers}/discord-access.mjs +1 -1
  121. package/src/runtime/channels/{backends → providers}/discord-gateway.mjs +1 -1
  122. package/src/runtime/channels/{backends → providers}/discord.mjs +3 -3
  123. package/src/runtime/channels/{backends → providers}/telegram.mjs +12 -12
  124. package/src/runtime/channels/tool-defs.mjs +1 -1
  125. package/src/runtime/memory/index.mjs +76 -248
  126. package/src/runtime/memory/lib/agent-ipc.mjs +146 -81
  127. package/src/runtime/memory/lib/compact-vector-cache.mjs +88 -0
  128. package/src/runtime/memory/lib/core-memory-store.mjs +18 -1
  129. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +13 -34
  130. package/src/runtime/memory/lib/embedding-provider.mjs +47 -26
  131. package/src/runtime/memory/lib/embedding-worker.mjs +30 -34
  132. package/src/runtime/memory/lib/http-router.mjs +9 -6
  133. package/src/runtime/memory/lib/ko-morph.mjs +3 -20
  134. package/src/runtime/memory/lib/memory-action-handlers.mjs +2 -1
  135. package/src/runtime/memory/lib/memory-config-flags.mjs +1 -1
  136. package/src/runtime/memory/lib/memory-cycle1.mjs +1 -0
  137. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +4 -2
  138. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -0
  139. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +4 -1
  140. package/src/runtime/memory/lib/memory-cycle3.mjs +3 -1
  141. package/src/runtime/memory/lib/memory-daemon-lifecycle.mjs +5 -0
  142. package/src/runtime/memory/lib/memory-log.mjs +1 -1
  143. package/src/runtime/memory/lib/memory-process-lock.mjs +5 -60
  144. package/src/runtime/memory/lib/memory.mjs +1 -1
  145. package/src/runtime/memory/lib/pg/process.mjs +32 -9
  146. package/src/runtime/memory/lib/session-ingest.mjs +1 -1
  147. package/src/runtime/memory/tool-defs.mjs +1 -5
  148. package/src/runtime/search/index.mjs +2 -2
  149. package/src/runtime/search/tool-defs.mjs +2 -2
  150. package/src/runtime/shared/abort-race.mjs +70 -0
  151. package/src/runtime/shared/agent-route-config.mjs +119 -0
  152. package/src/runtime/shared/atomic-file.mjs +80 -4
  153. package/src/runtime/shared/channel-notification-routing.mjs +2 -2
  154. package/src/runtime/shared/child-guardian.mjs +204 -108
  155. package/src/runtime/shared/child-spawn-gate.mjs +139 -72
  156. package/src/runtime/shared/config.mjs +120 -47
  157. package/src/runtime/shared/llm/index.mjs +1 -1
  158. package/src/runtime/shared/owner-fair-gate.mjs +134 -0
  159. package/src/runtime/shared/resource-admission.mjs +150 -100
  160. package/src/runtime/shared/stream-progress.mjs +6 -0
  161. package/src/runtime/shared/tool-execution-owner.mjs +12 -0
  162. package/src/runtime/shared/tool-result-summary.mjs +1 -1
  163. package/src/runtime/shared/tool-surface.mjs +91 -27
  164. package/src/runtime/shared/tool-workload-gates.mjs +29 -0
  165. package/src/runtime/shared/turn-snapshot.mjs +47 -6
  166. package/src/runtime/shared/turn-worktree-snapshot.mjs +14 -2
  167. package/src/session-runtime/channel-config-api.mjs +5 -5
  168. package/src/session-runtime/config-helpers.mjs +39 -37
  169. package/src/session-runtime/config-lifecycle.mjs +27 -27
  170. package/src/session-runtime/lifecycle-api.mjs +22 -3
  171. package/src/session-runtime/memory-runtime-prewarm.mjs +15 -0
  172. package/src/session-runtime/model-route-api.mjs +7 -5
  173. package/src/session-runtime/prewarm.mjs +1 -1
  174. package/src/session-runtime/provider-auth-api.mjs +0 -7
  175. package/src/session-runtime/provider-models.mjs +1 -1
  176. package/src/session-runtime/remote-control.mjs +6 -7
  177. package/src/session-runtime/runtime-core.mjs +63 -39
  178. package/src/session-runtime/runtime-tunables.mjs +4 -4
  179. package/src/session-runtime/self-update.mjs +1 -1
  180. package/src/session-runtime/session-lifecycle.mjs +45 -17
  181. package/src/session-runtime/session-title.mjs +1 -1
  182. package/src/session-runtime/session-turn-api.mjs +96 -24
  183. package/src/session-runtime/settings-api.mjs +12 -24
  184. package/src/session-runtime/tool-defs.mjs +3 -3
  185. package/src/session-runtime/workflow-agents-api.mjs +55 -81
  186. package/src/session-runtime/workflow.mjs +14 -24
  187. package/src/standalone/agent-dispatch-broker.mjs +198 -0
  188. package/src/standalone/agent-tool/helpers.mjs +2 -2
  189. package/src/standalone/agent-tool/spawn-flow.mjs +2 -0
  190. package/src/standalone/agent-tool/spawn-preset.mjs +11 -21
  191. package/src/standalone/agent-tool/tool-def.mjs +0 -14
  192. package/src/standalone/channel-admin.mjs +53 -64
  193. package/src/standalone/{channel-daemon-client.mjs → channel-client.mjs} +33 -19
  194. package/src/standalone/{channel-daemon-transport.mjs → channel-transport.mjs} +127 -46
  195. package/src/standalone/channel-worker.mjs +124 -647
  196. package/src/standalone/{backend-daemon.mjs → daemon.mjs} +270 -154
  197. package/src/standalone/explore-tool.mjs +187 -70
  198. package/src/standalone/fair-call-scheduler.mjs +264 -0
  199. package/src/standalone/memory-runtime-proxy.mjs +58 -1
  200. package/src/standalone/session-client.mjs +1134 -0
  201. package/src/standalone/session-protocol.mjs +192 -0
  202. package/src/standalone/session-runtime-pool.mjs +393 -0
  203. package/src/standalone/session-runtime-record.mjs +20 -0
  204. package/src/standalone/session-runtime-worker.mjs +203 -0
  205. package/src/standalone/{engine-daemon-service.mjs → session-service.mjs} +481 -224
  206. package/src/standalone/{engine-daemon-transport.mjs → session-transport.mjs} +390 -121
  207. package/src/standalone/session-wire.mjs +55 -0
  208. package/src/tui/App.jsx +34 -21
  209. package/src/tui/app/app-format.mjs +1 -1
  210. package/src/tui/app/app-view.jsx +5 -2
  211. package/src/tui/app/channel-pickers.mjs +41 -41
  212. package/src/tui/app/core-memory-picker.mjs +19 -19
  213. package/src/tui/app/doctor.mjs +5 -11
  214. package/src/tui/app/input-parsers.mjs +2 -2
  215. package/src/tui/app/message-selector.mjs +103 -0
  216. package/src/tui/app/onboarding-steps.mjs +3 -4
  217. package/src/tui/app/project-picker.mjs +80 -56
  218. package/src/tui/app/prompt-submit.mjs +48 -43
  219. package/src/tui/app/resume-picker.mjs +2 -2
  220. package/src/tui/app/route-pickers.mjs +3 -1
  221. package/src/tui/app/settings-picker.mjs +31 -33
  222. package/src/tui/app/transcript-window.mjs +38 -0
  223. package/src/tui/app/use-prompt-draft-flow.mjs +5 -7
  224. package/src/tui/app/use-prompt-handlers.mjs +86 -21
  225. package/src/tui/app/use-prompt-hint.mjs +3 -2
  226. package/src/tui/app/use-prompt-queue-history.mjs +10 -3
  227. package/src/tui/app/use-transcript-scroll.mjs +10 -3
  228. package/src/tui/app/use-transcript-window.mjs +50 -17
  229. package/src/tui/components/PromptInput.jsx +71 -38
  230. package/src/tui/components/Spinner.jsx +1 -1
  231. package/src/tui/components/ToolExecution.jsx +1 -1
  232. package/src/tui/components/prompt-input/edit-helpers.mjs +9 -11
  233. package/src/tui/components/prompt-input/escape-policy.mjs +42 -0
  234. package/src/tui/components/prompt-input/immediate-render.mjs +0 -10
  235. package/src/tui/components/prompt-input/interrupt-policy.mjs +10 -0
  236. package/src/tui/components/prompt-input/restore-policy.mjs +10 -0
  237. package/src/tui/components/tool-execution/surface-detail.mjs +1 -1
  238. package/src/tui/dist/index.mjs +1031 -1676
  239. package/src/tui/hooks/useSession.mjs +16 -0
  240. package/src/tui/index.jsx +12 -13
  241. package/src/tui/{engine → session}/agent-envelope.mjs +3 -3
  242. package/src/tui/{engine → session}/agent-job-feed.mjs +4 -4
  243. package/src/tui/{engine → session}/boot-profile.mjs +2 -2
  244. package/src/tui/{engine → session}/context-state.mjs +3 -3
  245. package/src/tui/{engine → session}/labels.mjs +2 -2
  246. package/src/tui/{engine → session}/live-share.mjs +38 -4
  247. package/src/tui/{engine → session}/notice-text.mjs +2 -2
  248. package/src/tui/{engine → session}/notification-plan.mjs +3 -3
  249. package/src/tui/session/oauth-flows.mjs +140 -0
  250. package/src/tui/{engine → session}/prompt-history.mjs +3 -3
  251. package/src/tui/{engine → session}/queue-helpers.mjs +2 -2
  252. package/src/tui/{engine → session}/render-timing.mjs +2 -2
  253. package/src/tui/{engine → session}/session-api-ext.mjs +30 -20
  254. package/src/tui/{engine → session}/session-api.mjs +110 -93
  255. package/src/tui/{engine → session}/session-flow.mjs +8 -2
  256. package/src/tui/{engine → session}/session-stats.mjs +3 -3
  257. package/src/tui/{engine → session}/tool-approval.mjs +3 -3
  258. package/src/tui/{engine → session}/tool-call-fields.mjs +2 -2
  259. package/src/tui/{engine → session}/tool-card-results.mjs +8 -6
  260. package/src/tui/{engine → session}/tool-result-status.mjs +5 -5
  261. package/src/tui/{engine → session}/tool-result-text.mjs +13 -3
  262. package/src/tui/{engine → session}/transcript-spill.mjs +34 -34
  263. package/src/tui/{engine → session}/turn.mjs +38 -176
  264. package/src/tui/{engine-local-session.mjs → session-local.mjs} +103 -103
  265. package/src/tui/session.mjs +22 -0
  266. package/src/tui/spinner-meta.mjs +4 -4
  267. package/src/tui/spinner-verbs.mjs +51 -3
  268. package/src/ui/statusline-segments.mjs +1 -1
  269. package/src/ui/statusline.mjs +1 -1
  270. package/vendor/ink/build/components/App.js +10 -1
  271. package/src/session-runtime/memory-daemon-probe.mjs +0 -61
  272. package/src/standalone/engine-daemon-client.mjs +0 -894
  273. package/src/standalone/engine-daemon-local-bridge.mjs +0 -20
  274. package/src/standalone/engine-daemon-protocol.mjs +0 -33
  275. package/src/tui/engine/turn-watchdog.mjs +0 -92
  276. package/src/tui/engine.mjs +0 -22
  277. package/src/tui/hooks/useEngine.mjs +0 -16
  278. /package/src/runtime/agent/orchestrator/session/compact/{engine.mjs → runner.mjs} +0 -0
  279. /package/src/runtime/channels/{backends → providers}/discord-attachments.mjs +0 -0
  280. /package/src/tui/{engine → session}/agent-response-tail.mjs +0 -0
  281. /package/src/tui/{engine → session}/frame-batched-store.mjs +0 -0
  282. /package/src/tui/{engine → session}/tui-steering-persist.mjs +0 -0
@@ -0,0 +1,264 @@
1
+ function positiveInt(value, fallback) {
2
+ const parsed = Math.floor(Number(value));
3
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
4
+ }
5
+
6
+ function activeLimit(value, fallback) {
7
+ if (value === null || value === Infinity) return Infinity;
8
+ return positiveInt(value, fallback);
9
+ }
10
+
11
+ function schedulerError(message, statusCode = 503) {
12
+ const error = new Error(message);
13
+ error.statusCode = statusCode;
14
+ return error;
15
+ }
16
+
17
+ function abortError(signal, fallback = 'scheduled call canceled') {
18
+ return signal?.reason instanceof Error
19
+ ? signal.reason
20
+ : schedulerError(String(signal?.reason || fallback), 499);
21
+ }
22
+
23
+ /**
24
+ * Owner-fair async dispatcher with an optional active limit.
25
+ *
26
+ * One owner may borrow the entire queue while it is alone. When another owner
27
+ * appears, new admissions are divided by weight and a completely full queue
28
+ * evicts one tail item from the largest borrower so the newcomer always gets a
29
+ * seat. Running work is never revoked.
30
+ */
31
+ export function createFairCallScheduler({
32
+ name = 'daemon call',
33
+ activeMax = 32,
34
+ queueMax = 256,
35
+ minOwnerQueue = 8,
36
+ dispatchBurst = 8,
37
+ yieldUnbounded = false,
38
+ schedule = setImmediate,
39
+ now = Date.now,
40
+ } = {}) {
41
+ const maxActive = activeLimit(activeMax, 32);
42
+ const maxQueued = positiveInt(queueMax, 256);
43
+ const ownerFloor = Math.max(1, Math.min(maxQueued, positiveInt(minOwnerQueue, 8)));
44
+ const maxBurst = positiveInt(dispatchBurst, 8);
45
+ const groups = new Map();
46
+ let active = 0;
47
+ let queued = 0;
48
+ let scheduled = false;
49
+ let closed = false;
50
+
51
+ function ownerId(value) {
52
+ const clean = String(value || '').trim();
53
+ return clean ? clean.slice(0, 240) : 'anonymous';
54
+ }
55
+
56
+ function groupFor(owner, weight = 1) {
57
+ const id = ownerId(owner);
58
+ let group = groups.get(id);
59
+ if (!group) {
60
+ group = {
61
+ owner: id,
62
+ weight: positiveInt(weight, 1),
63
+ current: 0,
64
+ active: 0,
65
+ queue: [],
66
+ };
67
+ groups.set(id, group);
68
+ } else if (weight !== undefined) {
69
+ group.weight = positiveInt(weight, group.weight);
70
+ }
71
+ return group;
72
+ }
73
+
74
+ function maybeDeleteGroup(group) {
75
+ if (group && group.active === 0 && group.queue.length === 0) groups.delete(group.owner);
76
+ }
77
+
78
+ function detach(item) {
79
+ if (!item?.onAbort || !item.signal) return;
80
+ try { item.signal.removeEventListener('abort', item.onAbort); } catch {}
81
+ item.onAbort = null;
82
+ }
83
+
84
+ function queuedGroups(extra = null) {
85
+ const out = [];
86
+ for (const group of groups.values()) {
87
+ if (group.queue.length > 0 || group === extra) out.push(group);
88
+ }
89
+ if (extra && !out.includes(extra)) out.push(extra);
90
+ return out;
91
+ }
92
+
93
+ function rejectBorrowedTail(incoming) {
94
+ let borrower = null;
95
+ for (const group of groups.values()) {
96
+ if (group === incoming || group.queue.length <= ownerFloor) continue;
97
+ if (!borrower || group.queue.length > borrower.queue.length) borrower = group;
98
+ }
99
+ if (!borrower) return false;
100
+ const displaced = borrower.queue.pop();
101
+ queued = Math.max(0, queued - 1);
102
+ detach(displaced);
103
+ displaced.reject(schedulerError(
104
+ `${name} queue rebalanced for another client; retry after running work completes`,
105
+ 503,
106
+ ));
107
+ maybeDeleteGroup(borrower);
108
+ return true;
109
+ }
110
+
111
+ function fairQueueLimit(group) {
112
+ const contenders = queuedGroups(group);
113
+ if (contenders.length <= 1) return maxQueued;
114
+ const totalWeight = contenders.reduce((sum, candidate) => sum + candidate.weight, 0);
115
+ return Math.max(
116
+ ownerFloor,
117
+ Math.floor(maxQueued * group.weight / Math.max(1, totalWeight)),
118
+ );
119
+ }
120
+
121
+ function pickGroup() {
122
+ const ready = [...groups.values()].filter((group) => group.queue.length > 0);
123
+ if (ready.length === 0) return null;
124
+ const totalWeight = ready.reduce((sum, group) => sum + group.weight, 0);
125
+ let chosen = null;
126
+ for (const group of ready) {
127
+ group.current += group.weight;
128
+ if (!chosen || group.current > chosen.current) chosen = group;
129
+ }
130
+ chosen.current -= totalWeight;
131
+ return chosen;
132
+ }
133
+
134
+ function scheduleDispatch() {
135
+ if (closed || scheduled || active >= maxActive || queued === 0) return;
136
+ scheduled = true;
137
+ schedule(() => {
138
+ scheduled = false;
139
+ let started = 0;
140
+ while (!closed && active < maxActive && queued > 0 && started < maxBurst) {
141
+ const group = pickGroup();
142
+ const item = group?.queue.shift();
143
+ if (!group || !item) break;
144
+ queued = Math.max(0, queued - 1);
145
+ detach(item);
146
+ active += 1;
147
+ group.active += 1;
148
+ started += 1;
149
+ Promise.resolve()
150
+ .then(item.run)
151
+ .then(item.resolve, item.reject)
152
+ .finally(() => {
153
+ active = Math.max(0, active - 1);
154
+ group.active = Math.max(0, group.active - 1);
155
+ maybeDeleteGroup(group);
156
+ scheduleDispatch();
157
+ });
158
+ }
159
+ // A bounded burst amortizes loopback dispatch without letting synchronous
160
+ // setup monopolize the supervisor's socket/control-plane turn.
161
+ scheduleDispatch();
162
+ });
163
+ }
164
+
165
+ function enqueue(owner, run, { weight = 1, signal = null } = {}) {
166
+ if (closed) return Promise.reject(schedulerError(`${name} scheduler is closed`));
167
+ if (typeof run !== 'function') return Promise.reject(new TypeError('scheduled call must be a function'));
168
+ if (signal?.aborted) return Promise.reject(abortError(signal));
169
+ const group = groupFor(owner, weight);
170
+ // Default daemon lanes are unbounded. Do not enqueue them only to bounce
171
+ // through setImmediate: independent loopback calls start in this microtask,
172
+ // while finite operator-configured lanes retain the fair queue below.
173
+ if (maxActive === Infinity && !yieldUnbounded && queued === 0) {
174
+ active += 1;
175
+ group.active += 1;
176
+ return Promise.resolve()
177
+ .then(run)
178
+ .finally(() => {
179
+ active = Math.max(0, active - 1);
180
+ group.active = Math.max(0, group.active - 1);
181
+ maybeDeleteGroup(group);
182
+ scheduleDispatch();
183
+ });
184
+ }
185
+ const hasCompetitor = queuedGroups(group).some((candidate) =>
186
+ candidate !== group && candidate.queue.length > 0);
187
+ if (hasCompetitor && group.queue.length >= fairQueueLimit(group)) {
188
+ return Promise.reject(schedulerError(
189
+ `${name} client queue is full; retry after this client's running work completes`,
190
+ 429,
191
+ ));
192
+ }
193
+ if (queued >= maxQueued && !rejectBorrowedTail(group)) {
194
+ return Promise.reject(schedulerError(`${name} queue is full`, 503));
195
+ }
196
+ let item;
197
+ const promise = new Promise((resolve, reject) => {
198
+ item = {
199
+ run,
200
+ resolve,
201
+ reject,
202
+ signal,
203
+ onAbort: null,
204
+ queuedAt: now(),
205
+ };
206
+ if (signal) {
207
+ item.onAbort = () => {
208
+ const index = group.queue.indexOf(item);
209
+ if (index < 0) return;
210
+ group.queue.splice(index, 1);
211
+ queued = Math.max(0, queued - 1);
212
+ detach(item);
213
+ maybeDeleteGroup(group);
214
+ reject(abortError(signal));
215
+ };
216
+ signal.addEventListener('abort', item.onAbort, { once: true });
217
+ }
218
+ group.queue.push(item);
219
+ queued += 1;
220
+ });
221
+ scheduleDispatch();
222
+ return promise;
223
+ }
224
+
225
+ function close(reason = `${name} scheduler is closed`) {
226
+ if (closed) return;
227
+ closed = true;
228
+ const error = schedulerError(reason);
229
+ for (const group of groups.values()) {
230
+ for (const item of group.queue.splice(0)) {
231
+ detach(item);
232
+ item.reject(error);
233
+ }
234
+ }
235
+ queued = 0;
236
+ for (const group of [...groups.values()]) maybeDeleteGroup(group);
237
+ }
238
+
239
+ function snapshot() {
240
+ let oldestQueuedAt = Infinity;
241
+ for (const group of groups.values()) {
242
+ for (const item of group.queue) {
243
+ oldestQueuedAt = Math.min(oldestQueuedAt, Number(item.queuedAt) || Infinity);
244
+ }
245
+ }
246
+ return {
247
+ active,
248
+ queued,
249
+ owners: [...groups.values()].filter((group) => group.active || group.queue.length).length,
250
+ activeMax: Number.isFinite(maxActive) ? maxActive : null,
251
+ dispatchBurst: maxBurst,
252
+ queueMax: maxQueued,
253
+ oldestWaitMs: Number.isFinite(oldestQueuedAt) ? Math.max(0, now() - oldestQueuedAt) : 0,
254
+ };
255
+ }
256
+
257
+ return {
258
+ enqueue,
259
+ close,
260
+ snapshot,
261
+ get active() { return active; },
262
+ get queued() { return queued; },
263
+ };
264
+ }
@@ -540,6 +540,45 @@ export function createStandaloneMemoryRuntime({
540
540
  return { running: true, port, mode: 'http-proxy' };
541
541
  }
542
542
 
543
+ async function retain() {
544
+ const started = await start();
545
+ const port = await ensureClientRegistered(started.port);
546
+ return { ...started, port };
547
+ }
548
+
549
+ async function requestMemoryPath(path, body, {
550
+ timeoutMs = 30_000,
551
+ readOnlyRpc = false,
552
+ } = {}) {
553
+ return await withTransientMemoryRpcRetry(async () => {
554
+ const started = await retain();
555
+ return await requestJson({
556
+ port: started.port,
557
+ method: 'POST',
558
+ path,
559
+ body,
560
+ timeoutMs,
561
+ });
562
+ }, { readOnlyRpc });
563
+ }
564
+
565
+ async function appendEntry(data = {}) {
566
+ return await requestMemoryPath('/entry', data, { timeoutMs: 3_000 });
567
+ }
568
+
569
+ async function ingestTranscript(filePath, { cwd: transcriptCwd } = {}) {
570
+ return await requestMemoryPath('/ingest-transcript', {
571
+ filePath,
572
+ ...(transcriptCwd ? { cwd: transcriptCwd } : {}),
573
+ });
574
+ }
575
+
576
+ async function recordTraceEvents(events = []) {
577
+ return await requestMemoryPath('/admin/trace-record', { events }, {
578
+ timeoutMs: 5_000,
579
+ });
580
+ }
581
+
543
582
  async function handleToolCall(name, args = {}, signalOrOptions = null) {
544
583
  const signal = signalOrOptions?.signal || signalOrOptions || null;
545
584
  const readOnlyRpc = isMemoryReadOnlyToolCall(name, args);
@@ -631,12 +670,30 @@ export function createStandaloneMemoryRuntime({
631
670
  }
632
671
 
633
672
  return {
634
- init: start,
673
+ init: retain,
674
+ retain,
635
675
  start,
636
676
  stop,
637
677
  status,
638
678
  handleToolCall,
639
679
  buildSessionCoreMemoryPayload,
680
+ appendEntry,
681
+ ingestTranscript,
682
+ recordTraceEvents,
640
683
  moduleUrl: pathToFileURL(entry).href,
641
684
  };
642
685
  }
686
+
687
+ const sharedMemoryRuntimes = new Map();
688
+
689
+ export function getStandaloneMemoryRuntime(options = {}) {
690
+ const entry = options.entry ? resolve(options.entry) : '';
691
+ const dataDir = options.dataDir ? resolve(options.dataDir) : '';
692
+ const key = `${entry}\0${dataDir}`;
693
+ let runtime = sharedMemoryRuntimes.get(key);
694
+ if (!runtime) {
695
+ runtime = createStandaloneMemoryRuntime(options);
696
+ sharedMemoryRuntimes.set(key, runtime);
697
+ }
698
+ return runtime;
699
+ }