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.
- package/LICENSES/Apache-2.0.txt +201 -0
- package/LICENSES/MIT.txt +56 -0
- package/LICENSES/codex-NOTICE.txt +6 -0
- package/NOTICE.md +46 -9
- package/README.md +0 -17
- package/package.json +16 -12
- package/scripts/.tmp-coverage-report.mjs +96 -0
- package/scripts/build-tui.mjs +17 -1
- package/scripts/{channel-daemon-stub.mjs → daemon-stub.mjs} +18 -12
- package/scripts/fixtures/patch-replay-corpus.json +98 -0
- package/src/defaults/mixdog-config.template.json +5 -8
- package/src/defaults/skills/setup/SKILL.md +1 -1
- package/src/lib/keychain-cjs.cjs +1 -1
- package/src/lib/mixdog-debug.cjs +2 -3
- package/src/rules/agent/30-explorer.md +7 -4
- package/src/rules/lead/01-general.md +1 -2
- package/src/rules/lead/lead-tool.md +0 -3
- package/src/rules/shared/01-tool.md +21 -29
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +18 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +31 -1
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +6 -17
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +12 -1
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +4 -13
- package/src/runtime/agent/orchestrator/config.mjs +246 -67
- package/src/runtime/agent/orchestrator/mcp/client.mjs +104 -41
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +23 -0
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +113 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +32 -11
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +89 -2
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +12 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +19 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +144 -39
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +22 -8
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +15 -1
- package/src/runtime/agent/orchestrator/providers/openai-transport-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +3 -4
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +3 -6
- package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +67 -7
- package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +291 -0
- package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +36 -10
- package/src/runtime/agent/orchestrator/session/compact.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +22 -33
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +0 -24
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +21 -29
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +7 -10
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +45 -25
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -3
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +142 -84
- package/src/runtime/agent/orchestrator/session/manager/prefetch-bridge.mjs +12 -6
- package/src/runtime/agent/orchestrator/session/manager/runtime-loaders.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +31 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +38 -2
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +29 -9
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/result-classification.mjs +65 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +86 -8
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +71 -42
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +4 -33
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -5
- package/src/runtime/agent/orchestrator/stall-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +35 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +116 -26
- package/src/runtime/agent/orchestrator/tools/builtin/binary-file.mjs +92 -19
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +8 -9
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +6 -15
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +12 -26
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -33
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +42 -45
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +27 -9
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +7 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +17 -39
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +73 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +0 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +3 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +30 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +142 -26
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +47 -19
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/lib/pwsh-standby-pool.mjs +83 -13
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +39 -1
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +94 -20
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +16 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +108 -12
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +12 -11
- package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +63 -9
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +2 -2
- package/src/runtime/channels/lib/config.mjs +34 -34
- package/src/runtime/channels/lib/inbound-handler.mjs +8 -8
- package/src/runtime/channels/lib/interaction-handlers.mjs +6 -6
- package/src/runtime/channels/lib/memory-client.mjs +31 -157
- package/src/runtime/channels/lib/output-forwarder.mjs +16 -16
- package/src/runtime/channels/lib/owned-runtime.mjs +60 -60
- package/src/runtime/channels/lib/owner-heartbeat.mjs +1 -1
- package/src/runtime/channels/lib/{backend-dispatch.mjs → provider-dispatch.mjs} +6 -6
- package/src/runtime/channels/lib/runtime-paths.mjs +4 -30
- package/src/runtime/channels/lib/scheduler.mjs +2 -2
- package/src/runtime/channels/lib/status-snapshot.mjs +3 -3
- package/src/runtime/channels/lib/tool-dispatch.mjs +4 -4
- package/src/runtime/channels/lib/worker-bootstrap.mjs +1 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +7 -7
- package/src/runtime/channels/lib/worker-main.mjs +38 -38
- package/src/runtime/channels/{backends → providers}/discord-access.mjs +1 -1
- package/src/runtime/channels/{backends → providers}/discord-gateway.mjs +1 -1
- package/src/runtime/channels/{backends → providers}/discord.mjs +3 -3
- package/src/runtime/channels/{backends → providers}/telegram.mjs +12 -12
- package/src/runtime/channels/tool-defs.mjs +1 -1
- package/src/runtime/memory/index.mjs +76 -248
- package/src/runtime/memory/lib/agent-ipc.mjs +146 -81
- package/src/runtime/memory/lib/compact-vector-cache.mjs +88 -0
- package/src/runtime/memory/lib/core-memory-store.mjs +18 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +13 -34
- package/src/runtime/memory/lib/embedding-provider.mjs +47 -26
- package/src/runtime/memory/lib/embedding-worker.mjs +30 -34
- package/src/runtime/memory/lib/http-router.mjs +9 -6
- package/src/runtime/memory/lib/ko-morph.mjs +3 -20
- package/src/runtime/memory/lib/memory-action-handlers.mjs +2 -1
- package/src/runtime/memory/lib/memory-config-flags.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -0
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +4 -2
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +4 -1
- package/src/runtime/memory/lib/memory-cycle3.mjs +3 -1
- package/src/runtime/memory/lib/memory-daemon-lifecycle.mjs +5 -0
- package/src/runtime/memory/lib/memory-log.mjs +1 -1
- package/src/runtime/memory/lib/memory-process-lock.mjs +5 -60
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/pg/process.mjs +32 -9
- package/src/runtime/memory/lib/session-ingest.mjs +1 -1
- package/src/runtime/memory/tool-defs.mjs +1 -5
- package/src/runtime/search/index.mjs +2 -2
- package/src/runtime/search/tool-defs.mjs +2 -2
- package/src/runtime/shared/abort-race.mjs +70 -0
- package/src/runtime/shared/agent-route-config.mjs +119 -0
- package/src/runtime/shared/atomic-file.mjs +80 -4
- package/src/runtime/shared/channel-notification-routing.mjs +2 -2
- package/src/runtime/shared/child-guardian.mjs +204 -108
- package/src/runtime/shared/child-spawn-gate.mjs +139 -72
- package/src/runtime/shared/config.mjs +120 -47
- package/src/runtime/shared/llm/index.mjs +1 -1
- package/src/runtime/shared/owner-fair-gate.mjs +134 -0
- package/src/runtime/shared/resource-admission.mjs +150 -100
- package/src/runtime/shared/stream-progress.mjs +6 -0
- package/src/runtime/shared/tool-execution-owner.mjs +12 -0
- package/src/runtime/shared/tool-result-summary.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +91 -27
- package/src/runtime/shared/tool-workload-gates.mjs +29 -0
- package/src/runtime/shared/turn-snapshot.mjs +47 -6
- package/src/runtime/shared/turn-worktree-snapshot.mjs +14 -2
- package/src/session-runtime/channel-config-api.mjs +5 -5
- package/src/session-runtime/config-helpers.mjs +39 -37
- package/src/session-runtime/config-lifecycle.mjs +27 -27
- package/src/session-runtime/lifecycle-api.mjs +22 -3
- package/src/session-runtime/memory-runtime-prewarm.mjs +15 -0
- package/src/session-runtime/model-route-api.mjs +7 -5
- package/src/session-runtime/prewarm.mjs +1 -1
- package/src/session-runtime/provider-auth-api.mjs +0 -7
- package/src/session-runtime/provider-models.mjs +1 -1
- package/src/session-runtime/remote-control.mjs +6 -7
- package/src/session-runtime/runtime-core.mjs +63 -39
- package/src/session-runtime/runtime-tunables.mjs +4 -4
- package/src/session-runtime/self-update.mjs +1 -1
- package/src/session-runtime/session-lifecycle.mjs +45 -17
- package/src/session-runtime/session-title.mjs +1 -1
- package/src/session-runtime/session-turn-api.mjs +96 -24
- package/src/session-runtime/settings-api.mjs +12 -24
- package/src/session-runtime/tool-defs.mjs +3 -3
- package/src/session-runtime/workflow-agents-api.mjs +55 -81
- package/src/session-runtime/workflow.mjs +14 -24
- package/src/standalone/agent-dispatch-broker.mjs +198 -0
- package/src/standalone/agent-tool/helpers.mjs +2 -2
- package/src/standalone/agent-tool/spawn-flow.mjs +2 -0
- package/src/standalone/agent-tool/spawn-preset.mjs +11 -21
- package/src/standalone/agent-tool/tool-def.mjs +0 -14
- package/src/standalone/channel-admin.mjs +53 -64
- package/src/standalone/{channel-daemon-client.mjs → channel-client.mjs} +33 -19
- package/src/standalone/{channel-daemon-transport.mjs → channel-transport.mjs} +127 -46
- package/src/standalone/channel-worker.mjs +124 -647
- package/src/standalone/{backend-daemon.mjs → daemon.mjs} +270 -154
- package/src/standalone/explore-tool.mjs +187 -70
- package/src/standalone/fair-call-scheduler.mjs +264 -0
- package/src/standalone/memory-runtime-proxy.mjs +58 -1
- package/src/standalone/session-client.mjs +1134 -0
- package/src/standalone/session-protocol.mjs +192 -0
- package/src/standalone/session-runtime-pool.mjs +393 -0
- package/src/standalone/session-runtime-record.mjs +20 -0
- package/src/standalone/session-runtime-worker.mjs +203 -0
- package/src/standalone/{engine-daemon-service.mjs → session-service.mjs} +481 -224
- package/src/standalone/{engine-daemon-transport.mjs → session-transport.mjs} +390 -121
- package/src/standalone/session-wire.mjs +55 -0
- package/src/tui/App.jsx +34 -21
- package/src/tui/app/app-format.mjs +1 -1
- package/src/tui/app/app-view.jsx +5 -2
- package/src/tui/app/channel-pickers.mjs +41 -41
- package/src/tui/app/core-memory-picker.mjs +19 -19
- package/src/tui/app/doctor.mjs +5 -11
- package/src/tui/app/input-parsers.mjs +2 -2
- package/src/tui/app/message-selector.mjs +103 -0
- package/src/tui/app/onboarding-steps.mjs +3 -4
- package/src/tui/app/project-picker.mjs +80 -56
- package/src/tui/app/prompt-submit.mjs +48 -43
- package/src/tui/app/resume-picker.mjs +2 -2
- package/src/tui/app/route-pickers.mjs +3 -1
- package/src/tui/app/settings-picker.mjs +31 -33
- package/src/tui/app/transcript-window.mjs +38 -0
- package/src/tui/app/use-prompt-draft-flow.mjs +5 -7
- package/src/tui/app/use-prompt-handlers.mjs +86 -21
- package/src/tui/app/use-prompt-hint.mjs +3 -2
- package/src/tui/app/use-prompt-queue-history.mjs +10 -3
- package/src/tui/app/use-transcript-scroll.mjs +10 -3
- package/src/tui/app/use-transcript-window.mjs +50 -17
- package/src/tui/components/PromptInput.jsx +71 -38
- package/src/tui/components/Spinner.jsx +1 -1
- package/src/tui/components/ToolExecution.jsx +1 -1
- package/src/tui/components/prompt-input/edit-helpers.mjs +9 -11
- package/src/tui/components/prompt-input/escape-policy.mjs +42 -0
- package/src/tui/components/prompt-input/immediate-render.mjs +0 -10
- package/src/tui/components/prompt-input/interrupt-policy.mjs +10 -0
- package/src/tui/components/prompt-input/restore-policy.mjs +10 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +1 -1
- package/src/tui/dist/index.mjs +1031 -1676
- package/src/tui/hooks/useSession.mjs +16 -0
- package/src/tui/index.jsx +12 -13
- package/src/tui/{engine → session}/agent-envelope.mjs +3 -3
- package/src/tui/{engine → session}/agent-job-feed.mjs +4 -4
- package/src/tui/{engine → session}/boot-profile.mjs +2 -2
- package/src/tui/{engine → session}/context-state.mjs +3 -3
- package/src/tui/{engine → session}/labels.mjs +2 -2
- package/src/tui/{engine → session}/live-share.mjs +38 -4
- package/src/tui/{engine → session}/notice-text.mjs +2 -2
- package/src/tui/{engine → session}/notification-plan.mjs +3 -3
- package/src/tui/session/oauth-flows.mjs +140 -0
- package/src/tui/{engine → session}/prompt-history.mjs +3 -3
- package/src/tui/{engine → session}/queue-helpers.mjs +2 -2
- package/src/tui/{engine → session}/render-timing.mjs +2 -2
- package/src/tui/{engine → session}/session-api-ext.mjs +30 -20
- package/src/tui/{engine → session}/session-api.mjs +110 -93
- package/src/tui/{engine → session}/session-flow.mjs +8 -2
- package/src/tui/{engine → session}/session-stats.mjs +3 -3
- package/src/tui/{engine → session}/tool-approval.mjs +3 -3
- package/src/tui/{engine → session}/tool-call-fields.mjs +2 -2
- package/src/tui/{engine → session}/tool-card-results.mjs +8 -6
- package/src/tui/{engine → session}/tool-result-status.mjs +5 -5
- package/src/tui/{engine → session}/tool-result-text.mjs +13 -3
- package/src/tui/{engine → session}/transcript-spill.mjs +34 -34
- package/src/tui/{engine → session}/turn.mjs +38 -176
- package/src/tui/{engine-local-session.mjs → session-local.mjs} +103 -103
- package/src/tui/session.mjs +22 -0
- package/src/tui/spinner-meta.mjs +4 -4
- package/src/tui/spinner-verbs.mjs +51 -3
- package/src/ui/statusline-segments.mjs +1 -1
- package/src/ui/statusline.mjs +1 -1
- package/vendor/ink/build/components/App.js +10 -1
- package/src/session-runtime/memory-daemon-probe.mjs +0 -61
- package/src/standalone/engine-daemon-client.mjs +0 -894
- package/src/standalone/engine-daemon-local-bridge.mjs +0 -20
- package/src/standalone/engine-daemon-protocol.mjs +0 -33
- package/src/tui/engine/turn-watchdog.mjs +0 -92
- package/src/tui/engine.mjs +0 -22
- package/src/tui/hooks/useEngine.mjs +0 -16
- /package/src/runtime/agent/orchestrator/session/compact/{engine.mjs → runner.mjs} +0 -0
- /package/src/runtime/channels/{backends → providers}/discord-attachments.mjs +0 -0
- /package/src/tui/{engine → session}/agent-response-tail.mjs +0 -0
- /package/src/tui/{engine → session}/frame-batched-store.mjs +0 -0
- /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:
|
|
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
|
+
}
|