mixdog 0.9.151 → 0.9.152
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -68
- package/package.json +3 -2
- package/scripts/tool-stress.mjs +0 -2
- package/src/defaults/skills/setup/SKILL.md +18 -27
- package/src/headless-exec.mjs +3 -10
- package/src/headless-exec.test.mjs +2 -7
- package/src/runtime/agent/orchestrator/config.mjs +6 -66
- package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +153 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +49 -45
- package/src/runtime/agent/orchestrator/mcp/client.mjs +242 -15
- package/src/runtime/agent/orchestrator/mcp/features.test.mjs +66 -0
- package/src/runtime/agent/orchestrator/mcp/security.test.mjs +36 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +54 -33
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +3 -8
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +10 -2
- package/src/runtime/agent/orchestrator/providers/lib/provider-replay.mjs +50 -0
- package/src/runtime/agent/orchestrator/providers/lib/sse-framing.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +3 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +35 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +22 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +0 -2
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +4 -7
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +235 -0
- package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +719 -0
- package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +399 -39
- package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +26 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +24 -1
- package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +161 -0
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +31 -18
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +556 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +457 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +105 -186
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +115 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +23 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +14 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -10
- package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +30 -14
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +15 -4
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +73 -15
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +76 -2
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +24 -683
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +64 -72
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +2 -5
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.mjs +75 -0
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +24 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +24 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +12 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +35 -21
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +28 -13
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +54 -0
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
- package/src/runtime/browser-bridge/client.mjs +103 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +42 -0
- package/src/runtime/computer-bridge/client.mjs +101 -0
- package/src/runtime/computer-bridge/tool-defs.mjs +53 -0
- package/src/runtime/media/store.mjs +18 -8
- package/src/runtime/media/store.test.mjs +14 -0
- package/src/runtime/memory/index.mjs +1 -1
- package/src/runtime/memory/lib/transcript-ingest.mjs +8 -6
- package/src/runtime/memory/lib/transcript-ingest.test.mjs +52 -0
- package/src/runtime/shared/child-spawn-gate.mjs +17 -0
- package/src/runtime/shared/child-spawn-remote.mjs +69 -7
- package/src/runtime/shared/child-spawn-remote.test.mjs +124 -0
- package/src/runtime/shared/pristine-execution-contract.json +2 -1
- package/src/runtime/shared/provider-api-key.mjs +1 -0
- package/src/runtime/shared/session-runtime-health.mjs +91 -0
- package/src/runtime/shared/session-runtime-health.test.mjs +53 -0
- package/src/runtime/shared/skill-document.mjs +86 -0
- package/src/runtime/shared/skill-document.test.mjs +81 -0
- package/src/runtime/shared/tool-surface.mjs +18 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +1 -0
- package/src/runtime/shared/turn-snapshot.mjs +32 -9
- package/src/runtime/shared/user-cwd.mjs +33 -14
- package/src/runtime/shared/user-cwd.test.mjs +43 -0
- package/src/session-runtime/cwd-plugins.mjs +26 -26
- package/src/session-runtime/cwd-plugins.test.mjs +75 -0
- package/src/session-runtime/cwd-tool-routing.test.mjs +5 -0
- package/src/session-runtime/global-extensions.mjs +26 -0
- package/src/session-runtime/global-extensions.test.mjs +28 -0
- package/src/session-runtime/goal-runtime.mjs +721 -0
- package/src/session-runtime/goal-runtime.test.mjs +108 -0
- package/src/session-runtime/lifecycle-api.mjs +18 -4
- package/src/session-runtime/lifecycle-api.test.mjs +21 -0
- package/src/session-runtime/mcp-glue.mjs +49 -47
- package/src/session-runtime/plugin-mcp-standard.test.mjs +177 -0
- package/src/session-runtime/plugin-mcp.mjs +96 -22
- package/src/session-runtime/prewarm.mjs +1 -10
- package/src/session-runtime/resource-api-global.test.mjs +69 -0
- package/src/session-runtime/resource-api.mjs +173 -58
- package/src/session-runtime/runtime-core.mjs +148 -20
- package/src/session-runtime/runtime-tunables.mjs +6 -8
- package/src/session-runtime/session-turn-api.mjs +22 -11
- package/src/session-runtime/skills-api.mjs +79 -25
- package/src/session-runtime/skills-api.test.mjs +99 -0
- package/src/session-runtime/tool-catalog-schema.mjs +0 -5
- package/src/session-runtime/tool-catalog.mjs +4 -13
- package/src/session-runtime/tool-policy-surface.test.mjs +33 -11
- package/src/session-runtime/tool-surface.mjs +12 -0
- package/src/standalone/agent-dispatch-broker.mjs +5 -9
- package/src/standalone/agent-tool/notify.mjs +6 -8
- package/src/standalone/agent-tool/notify.test.mjs +37 -0
- package/src/standalone/daemon.mjs +31 -15
- package/src/standalone/hook-bus/config.mjs +1 -0
- package/src/standalone/plugin-admin.mjs +17 -0
- package/src/standalone/plugin-admin.test.mjs +7 -0
- package/src/standalone/provider-admin.mjs +1 -0
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-runtime-agent-control-client.mjs +133 -0
- package/src/standalone/session-runtime-agent-control-client.test.mjs +71 -0
- package/src/standalone/session-runtime-dispatch-cancel.test.mjs +139 -0
- package/src/standalone/session-runtime-host-factory.mjs +17 -0
- package/src/standalone/session-runtime-host-factory.test.mjs +31 -0
- package/src/standalone/session-runtime-host-health.test.mjs +5 -0
- package/src/standalone/session-runtime-host.mjs +696 -58
- package/src/standalone/session-runtime-inline-host.mjs +351 -0
- package/src/standalone/session-runtime-inline-host.test.mjs +83 -0
- package/src/standalone/session-runtime-provider-cooldown.mjs +78 -0
- package/src/standalone/session-runtime-provider-cooldown.test.mjs +80 -0
- package/src/standalone/session-runtime-shard-host.test.mjs +569 -0
- package/src/standalone/session-runtime-shard-router.mjs +149 -0
- package/src/standalone/session-runtime-shard-router.test.mjs +101 -0
- package/src/standalone/session-runtime-worker.mjs +476 -18
- package/src/standalone/session-service.mjs +81 -6
- package/src/tui/app/app-view.jsx +1 -0
- package/src/tui/app/core-memory-picker.mjs +2 -0
- package/src/tui/app/extension-pickers.mjs +1 -1
- package/src/tui/app/maintenance-pickers.mjs +3 -3
- package/src/tui/app/model-options.mjs +2 -0
- package/src/tui/app/model-picker.mjs +14 -4
- package/src/tui/app/panel-handoff.test.mjs +311 -0
- package/src/tui/app/project-picker.mjs +1 -0
- package/src/tui/app/route-pickers.mjs +21 -6
- package/src/tui/app/settings-picker.mjs +26 -3
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch.mjs +73 -22
- package/src/tui/app/theme-effort-pickers.mjs +7 -2
- package/src/tui/components/Picker.jsx +10 -1
- package/src/tui/dist/index.mjs +163 -44
- package/src/tui/session/context-state.mjs +1 -0
- package/src/tui/session/goal-continuation.mjs +110 -0
- package/src/tui/session/goal-continuation.test.mjs +84 -0
- package/src/tui/session/queue-helpers.mjs +5 -2
- package/src/tui/session/session-action-surface.test.mjs +108 -0
- package/src/tui/session/session-api-ext.mjs +39 -8
- package/src/tui/session/session-api.mjs +58 -0
- package/src/tui/session/session-flow.mjs +6 -0
- package/src/tui/session/turn.mjs +1 -0
- package/src/tui/session-local.mjs +15 -1
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +0 -301
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
// Supervised child
|
|
1
|
+
// Supervised child process for ONE session runtime shard.
|
|
2
2
|
//
|
|
3
3
|
// Provider parsing, transcript projection, tool execution, and runtime timers
|
|
4
4
|
// stay on this event loop. The machine daemon receives only identity-preserving
|
|
5
5
|
// state deltas and remains free to serve health, input, and abort dispatch.
|
|
6
|
+
//
|
|
7
|
+
// This process is one of a BOUNDED set of identical shards (see
|
|
8
|
+
// session-runtime-host.mjs): the host routes each session/dispatch to a shard
|
|
9
|
+
// deterministically, so a saturated or crashed shard degrades only the work it
|
|
10
|
+
// owns instead of every runtime on the machine. Shard-crossing facts —
|
|
11
|
+
// machine-wide spawn leases and account-wide provider cooldowns — are
|
|
12
|
+
// coordinated through the host, never assumed process-local.
|
|
6
13
|
process.env.MIXDOG_SESSION_RUNTIME_WORKER = '1';
|
|
7
14
|
// Pid-scoped marker: child tools inherit the plain flag, so callers that need
|
|
8
15
|
// the actual runtime worker must also verify this exact pid.
|
|
@@ -14,16 +21,33 @@ import { safeIpcSend } from '../runtime/shared/safe-ipc-send.mjs';
|
|
|
14
21
|
import { sanitizeForWire } from './session-service.mjs';
|
|
15
22
|
import { disposeSessionRuntimeRecord } from './session-runtime-record.mjs';
|
|
16
23
|
import { diffSessionState } from './session-state-patch.mjs';
|
|
24
|
+
import {
|
|
25
|
+
applyProviderCooldown,
|
|
26
|
+
mergeKnownProviderCooldown,
|
|
27
|
+
providerCooldownAdvanced,
|
|
28
|
+
readProviderCooldown,
|
|
29
|
+
} from './session-runtime-provider-cooldown.mjs';
|
|
17
30
|
import {
|
|
18
31
|
reportRuntimeAbortListenerPressure,
|
|
19
32
|
SESSION_RUNTIME_WORKER_UNHEALTHY_EVENT,
|
|
33
|
+
startRuntimeEventLoopLagMonitor,
|
|
20
34
|
} from '../runtime/shared/session-runtime-health.mjs';
|
|
21
35
|
|
|
36
|
+
// Shard identity. The host runs a BOUNDED set of identical runtime children so
|
|
37
|
+
// that provider parsing, tool results and transcript projection for unrelated
|
|
38
|
+
// sessions no longer share one event loop; this process owns exactly the
|
|
39
|
+
// sessions/dispatches the host routed to this shard index.
|
|
40
|
+
const SHARD_INDEX = Math.max(0, Math.floor(Number(process.env.MIXDOG_SESSION_RUNTIME_SHARD) || 0));
|
|
41
|
+
|
|
22
42
|
const records = new Map();
|
|
23
43
|
let sessionModulePromise = null;
|
|
24
44
|
let stopping = false;
|
|
25
45
|
let unhealthyDetail = null;
|
|
26
46
|
let unhealthyReported = false;
|
|
47
|
+
let unsubscribeAgentSessionPublisher = null;
|
|
48
|
+
let agentSessionPublisherPromise = null;
|
|
49
|
+
const pendingAgentSessionProjections = new Map();
|
|
50
|
+
const activeAgentSessionProjections = new Set();
|
|
27
51
|
const pendingAbortPressureChecks = new WeakSet();
|
|
28
52
|
const ABORT_PRESSURE_RETENTION_CHECK_MS = 30_000;
|
|
29
53
|
|
|
@@ -38,7 +62,9 @@ function reportUnhealthy() {
|
|
|
38
62
|
process.on(SESSION_RUNTIME_WORKER_UNHEALTHY_EVENT, (detail) => {
|
|
39
63
|
if (stopping || unhealthyDetail) return;
|
|
40
64
|
unhealthyDetail = sanitizeForWire(detail) || { reason: 'session runtime worker unhealthy' };
|
|
41
|
-
process.stderr.write(
|
|
65
|
+
process.stderr.write(
|
|
66
|
+
`[session-runtime-health] shard ${SHARD_INDEX}: ${unhealthyDetail.reason || 'unhealthy'}\n`,
|
|
67
|
+
);
|
|
42
68
|
reportUnhealthy();
|
|
43
69
|
});
|
|
44
70
|
|
|
@@ -67,6 +93,140 @@ function send(message) {
|
|
|
67
93
|
return safeIpcSend(process, message);
|
|
68
94
|
}
|
|
69
95
|
|
|
96
|
+
// ── Per-shard event-loop lag ───────────────────────────────────────────────
|
|
97
|
+
// The daemon can only measure its OWN loop; a shard saturated by provider
|
|
98
|
+
// parsing or a runaway tool is invisible there. Sampling here and reporting to
|
|
99
|
+
// the host gives per-shard saturation telemetry and lets the host stop placing
|
|
100
|
+
// NEW work on this shard. It never triggers a kill: this process still owns
|
|
101
|
+
// accepted input and in-flight turns.
|
|
102
|
+
let lastLagSample = null;
|
|
103
|
+
|
|
104
|
+
const stopEventLoopLagMonitor = startRuntimeEventLoopLagMonitor({
|
|
105
|
+
onSample: (sample) => {
|
|
106
|
+
lastLagSample = sample;
|
|
107
|
+
if (stopping) return;
|
|
108
|
+
send({ type: 'event-loop-lag', shard: SHARD_INDEX, sample });
|
|
109
|
+
void publishProviderCooldown();
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// ── Cross-shard provider cooldown ──────────────────────────────────────────
|
|
114
|
+
// Fast-mode capacity cooldown is an ACCOUNT-wide fact held in process-local
|
|
115
|
+
// state. With several shards, one shard discovering the drained pool must not
|
|
116
|
+
// leave siblings hammering it, so cooldowns are published to the host and
|
|
117
|
+
// replayed into every other shard. Only the module's public API is used: a
|
|
118
|
+
// replay is expressed as the same capacity rejection the discovering shard saw.
|
|
119
|
+
let fastModePromise = null;
|
|
120
|
+
let fastModePolicy = null;
|
|
121
|
+
let lastCooldownSent = { untilMs: 0, disabledReason: null };
|
|
122
|
+
let admissionModulePromise = null;
|
|
123
|
+
let cooldownBridgeUnsubscribe = null;
|
|
124
|
+
let cooldownBridgePromise = null;
|
|
125
|
+
|
|
126
|
+
function fastModeModule() {
|
|
127
|
+
fastModePromise ??= import('../runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs')
|
|
128
|
+
.then((policy) => { fastModePolicy = policy; return policy; })
|
|
129
|
+
.catch(() => null);
|
|
130
|
+
return fastModePromise;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function admissionSchedulerModule() {
|
|
134
|
+
admissionModulePromise ??= import('../runtime/agent/orchestrator/providers/admission-scheduler.mjs')
|
|
135
|
+
.catch(() => null);
|
|
136
|
+
return admissionModulePromise;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Subscribe to the provider admission scheduler's cooldown events so a 429
|
|
141
|
+
* observed here reaches sibling shards ON THE EVENT — waiting for the next
|
|
142
|
+
* telemetry tick would leave every other shard probing a pool this process
|
|
143
|
+
* already knows is drained. Idempotent and safe to call from any provider
|
|
144
|
+
* entry point.
|
|
145
|
+
*/
|
|
146
|
+
function ensureProviderCooldownBridge() {
|
|
147
|
+
if (cooldownBridgeUnsubscribe || cooldownBridgePromise || stopping) return;
|
|
148
|
+
cooldownBridgePromise = admissionSchedulerModule().then((module) => {
|
|
149
|
+
if (!module || stopping || cooldownBridgeUnsubscribe) return;
|
|
150
|
+
cooldownBridgeUnsubscribe = module.providerAdmissionScheduler.onCooldownEvent((event) => {
|
|
151
|
+
publishProviderCooldownNow(event);
|
|
152
|
+
});
|
|
153
|
+
}).catch(() => { /* provider graph is absent in this shard */ })
|
|
154
|
+
.finally(() => { cooldownBridgePromise = null; });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function normalizeAdmissionCooldownEvent(event) {
|
|
158
|
+
if (!event || typeof event !== 'object') return null;
|
|
159
|
+
const type = String(event.type || '');
|
|
160
|
+
if (type === 'cooldown') {
|
|
161
|
+
const key = String(event.key || '');
|
|
162
|
+
const cooldownUntil = Number(event.cooldownUntil) || 0;
|
|
163
|
+
if (!key || cooldownUntil <= Date.now()) return null;
|
|
164
|
+
return { type, key, cooldownUntil };
|
|
165
|
+
}
|
|
166
|
+
if (type === 'reset') {
|
|
167
|
+
return { type, provider: event.provider ? String(event.provider) : null };
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Synchronous publish path. Costs one comparison when nothing changed, so it
|
|
174
|
+
* can be called from provider/runtime boundaries and from the cooldown event
|
|
175
|
+
* itself without waiting on any import.
|
|
176
|
+
*/
|
|
177
|
+
function publishProviderCooldownNow(admissionEvent = null) {
|
|
178
|
+
if (stopping) return false;
|
|
179
|
+
const admission = normalizeAdmissionCooldownEvent(admissionEvent);
|
|
180
|
+
const observed = fastModePolicy ? readProviderCooldown(fastModePolicy) : null;
|
|
181
|
+
const advanced = Boolean(observed) && providerCooldownAdvanced(lastCooldownSent, observed);
|
|
182
|
+
if (!admission && !advanced) return false;
|
|
183
|
+
if (advanced) lastCooldownSent = mergeKnownProviderCooldown(lastCooldownSent, observed);
|
|
184
|
+
send({
|
|
185
|
+
type: 'provider-cooldown',
|
|
186
|
+
shard: SHARD_INDEX,
|
|
187
|
+
cooldown: observed || { untilMs: 0, disabledReason: null, observedAt: Date.now() },
|
|
188
|
+
...(admission ? { admission } : {}),
|
|
189
|
+
});
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Backstop sweep (telemetry tick): loads the policy module if provider work
|
|
194
|
+
* has happened, then reuses the synchronous path. */
|
|
195
|
+
async function publishProviderCooldown() {
|
|
196
|
+
// Only shards that actually run provider work carry cooldown state.
|
|
197
|
+
if (stopping || (!agentGraphPromise && records.size === 0)) return;
|
|
198
|
+
const policy = await fastModeModule();
|
|
199
|
+
if (!policy || stopping) return;
|
|
200
|
+
publishProviderCooldownNow();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function applyReplayedProviderCooldown(frame) {
|
|
204
|
+
const cooldown = frame?.cooldown ?? frame ?? null;
|
|
205
|
+
const admission = frame?.admission && typeof frame.admission === 'object' ? frame.admission : null;
|
|
206
|
+
if (admission) {
|
|
207
|
+
const module = await admissionSchedulerModule();
|
|
208
|
+
if (module) {
|
|
209
|
+
// applyExternalCooldown/resetCooldowns are the documented cross-process
|
|
210
|
+
// seams: neither re-emits for a broadcast that changed nothing, so the
|
|
211
|
+
// replay cannot loop between shards.
|
|
212
|
+
if (admission.type === 'cooldown') {
|
|
213
|
+
module.providerAdmissionScheduler.applyExternalCooldown(
|
|
214
|
+
String(admission.key || ''),
|
|
215
|
+
Number(admission.cooldownUntil) || 0,
|
|
216
|
+
);
|
|
217
|
+
} else if (admission.type === 'reset') {
|
|
218
|
+
module.resetProviderAdmissionCooldowns(admission.provider || null);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const policy = await fastModeModule();
|
|
223
|
+
if (!policy) return Boolean(admission);
|
|
224
|
+
const applied = applyProviderCooldown(policy, cooldown);
|
|
225
|
+
// A replayed cooldown is not a local discovery: suppress the echo back.
|
|
226
|
+
lastCooldownSent = mergeKnownProviderCooldown(lastCooldownSent, cooldown);
|
|
227
|
+
return applied || Boolean(admission);
|
|
228
|
+
}
|
|
229
|
+
|
|
70
230
|
process.on('mixdog:turn-timing', (row = {}) => {
|
|
71
231
|
send({
|
|
72
232
|
type: 'turn-timing',
|
|
@@ -101,6 +261,94 @@ function publish(record, forceFull = false) {
|
|
|
101
261
|
});
|
|
102
262
|
}
|
|
103
263
|
|
|
264
|
+
function agentOwnedSession(session) {
|
|
265
|
+
const owner = String(session?.owner || '').trim().toLowerCase();
|
|
266
|
+
const agent = String(session?.agent || '').trim().toLowerCase();
|
|
267
|
+
return Boolean(session?.id)
|
|
268
|
+
&& (owner === 'agent' || (agent && agent !== 'lead'));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function projectAgentSession(session) {
|
|
272
|
+
const [
|
|
273
|
+
{ restoreTranscriptItems },
|
|
274
|
+
{ getSessionProgressSnapshot },
|
|
275
|
+
] = await Promise.all([
|
|
276
|
+
import('../tui/session/session-api-ext.mjs'),
|
|
277
|
+
import('../runtime/agent/orchestrator/session/manager.mjs'),
|
|
278
|
+
]);
|
|
279
|
+
const sessionId = String(session.id || '');
|
|
280
|
+
const status = String(session.status || '').trim().toLowerCase();
|
|
281
|
+
const progress = getSessionProgressSnapshot?.(sessionId) || null;
|
|
282
|
+
const stage = String(progress?.stage || '').trim().toLowerCase();
|
|
283
|
+
const busy = status === 'running' || status === 'queued'
|
|
284
|
+
|| (stage && stage !== 'idle' && stage !== 'done' && stage !== 'closed');
|
|
285
|
+
return sanitizeForWire({
|
|
286
|
+
sessionId,
|
|
287
|
+
items: restoreTranscriptItems(
|
|
288
|
+
Array.isArray(session.messages) ? session.messages : [],
|
|
289
|
+
{ sessionId, itemLimit: 512 },
|
|
290
|
+
),
|
|
291
|
+
queued: [],
|
|
292
|
+
busy,
|
|
293
|
+
commandBusy: false,
|
|
294
|
+
provider: session.provider || '',
|
|
295
|
+
model: session.model || '',
|
|
296
|
+
effort: session.effort || '',
|
|
297
|
+
fast: session.fast === true,
|
|
298
|
+
modelParameters: session.modelParameters || {},
|
|
299
|
+
cwd: session.cwd || '',
|
|
300
|
+
desktopSession: session.desktopSession || null,
|
|
301
|
+
workflow: session.workflow || null,
|
|
302
|
+
ownerClientHostPid: Number(session.clientHostPid) || process.pid,
|
|
303
|
+
}) || null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function scheduleAgentSessionProjection(session) {
|
|
307
|
+
if (!agentOwnedSession(session) || stopping) return;
|
|
308
|
+
const sessionId = String(session.id);
|
|
309
|
+
pendingAgentSessionProjections.set(sessionId, session);
|
|
310
|
+
if (activeAgentSessionProjections.has(sessionId)) return;
|
|
311
|
+
activeAgentSessionProjections.add(sessionId);
|
|
312
|
+
queueMicrotask(() => {
|
|
313
|
+
void (async () => {
|
|
314
|
+
while (!stopping) {
|
|
315
|
+
const latest = pendingAgentSessionProjections.get(sessionId);
|
|
316
|
+
if (!latest) break;
|
|
317
|
+
pendingAgentSessionProjections.delete(sessionId);
|
|
318
|
+
const snapshot = await projectAgentSession(latest);
|
|
319
|
+
if (!snapshot || stopping) continue;
|
|
320
|
+
send({ type: 'agent-session-state', sessionId, snapshot });
|
|
321
|
+
}
|
|
322
|
+
})().catch((error) => {
|
|
323
|
+
try {
|
|
324
|
+
process.stderr.write(
|
|
325
|
+
`[session-runtime-worker] agent session projection failed session=${sessionId}: `
|
|
326
|
+
+ `${error?.message || error}\n`,
|
|
327
|
+
);
|
|
328
|
+
} catch {}
|
|
329
|
+
}).finally(() => {
|
|
330
|
+
activeAgentSessionProjections.delete(sessionId);
|
|
331
|
+
if (pendingAgentSessionProjections.has(sessionId) && !stopping) {
|
|
332
|
+
scheduleAgentSessionProjection(pendingAgentSessionProjections.get(sessionId));
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async function ensureAgentSessionPublisher() {
|
|
339
|
+
if (unsubscribeAgentSessionPublisher) return;
|
|
340
|
+
agentSessionPublisherPromise ??= import(
|
|
341
|
+
'../runtime/agent/orchestrator/session/store.mjs'
|
|
342
|
+
).then((store) => {
|
|
343
|
+
unsubscribeAgentSessionPublisher = store.subscribeLiveSessions(
|
|
344
|
+
scheduleAgentSessionProjection,
|
|
345
|
+
);
|
|
346
|
+
}).finally(() => {
|
|
347
|
+
agentSessionPublisherPromise = null;
|
|
348
|
+
});
|
|
349
|
+
await agentSessionPublisherPromise;
|
|
350
|
+
}
|
|
351
|
+
|
|
104
352
|
async function createRuntime(message) {
|
|
105
353
|
const module = await sessionModule();
|
|
106
354
|
const runtime = await module.createLocalSessionRuntime(message.options || {});
|
|
@@ -115,6 +363,9 @@ async function createRuntime(message) {
|
|
|
115
363
|
records.set(record.id, record);
|
|
116
364
|
record.unsubscribe = runtime.subscribe?.(() => publish(record)) ?? null;
|
|
117
365
|
publish(record, true);
|
|
366
|
+
// Session turns run providers in this shard too: their cooldowns must reach
|
|
367
|
+
// sibling shards on the event, not on the next telemetry tick.
|
|
368
|
+
ensureProviderCooldownBridge();
|
|
118
369
|
return { created: true };
|
|
119
370
|
}
|
|
120
371
|
|
|
@@ -130,6 +381,9 @@ async function callRuntime(message) {
|
|
|
130
381
|
}
|
|
131
382
|
const value = await target.apply(record.runtime, Array.isArray(message.args) ? message.args : []);
|
|
132
383
|
publish(record);
|
|
384
|
+
// Cheap synchronous check: a fast-mode cooldown that the admission scheduler
|
|
385
|
+
// never saw (long provider window) still leaves at the first call boundary.
|
|
386
|
+
publishProviderCooldownNow();
|
|
133
387
|
return sanitizeForWire(value);
|
|
134
388
|
}
|
|
135
389
|
|
|
@@ -157,6 +411,162 @@ function agentGraph() {
|
|
|
157
411
|
}
|
|
158
412
|
const agentDispatchers = new Map();
|
|
159
413
|
const agentDispatchRuns = new Map(); // dispatchId -> AbortController
|
|
414
|
+
let distributedAgentToolPromise = null;
|
|
415
|
+
const distributedAgentControlRuns = new Map(); // controlId -> AbortController
|
|
416
|
+
|
|
417
|
+
function distributedAgentTool() {
|
|
418
|
+
distributedAgentToolPromise ??= Promise.all([
|
|
419
|
+
import('../standalone/agent-tool.mjs'),
|
|
420
|
+
import('../runtime/agent/orchestrator/config.mjs'),
|
|
421
|
+
import('../runtime/agent/orchestrator/providers/registry.mjs'),
|
|
422
|
+
import('../runtime/agent/orchestrator/session/manager.mjs'),
|
|
423
|
+
]).then(async ([agentModule, cfgMod, reg, mgr]) => {
|
|
424
|
+
await ensureAgentSessionPublisher();
|
|
425
|
+
return agentModule.createStandaloneAgent({
|
|
426
|
+
cfgMod,
|
|
427
|
+
reg,
|
|
428
|
+
mgr,
|
|
429
|
+
dataDir: cfgMod.getPluginData(),
|
|
430
|
+
cwd: process.cwd(),
|
|
431
|
+
awaitKeychainPrewarm: async () => {},
|
|
432
|
+
isKeychainPrewarmReady: () => true,
|
|
433
|
+
notifySessionCompletion(ownerSessionId, text, meta = {}) {
|
|
434
|
+
return send({
|
|
435
|
+
type: 'agent-control-notification',
|
|
436
|
+
ownerSessionId: String(ownerSessionId || ''),
|
|
437
|
+
text: String(text || ''),
|
|
438
|
+
meta: sanitizeForWire(meta) || {},
|
|
439
|
+
});
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
}).catch((error) => {
|
|
443
|
+
distributedAgentToolPromise = null;
|
|
444
|
+
throw error;
|
|
445
|
+
});
|
|
446
|
+
return distributedAgentToolPromise;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
async function runDistributedAgentControl(message) {
|
|
450
|
+
const controlId = String(message.controlId || '');
|
|
451
|
+
if (!controlId) throw new Error('agent control id is required');
|
|
452
|
+
if (distributedAgentControlRuns.has(controlId)) {
|
|
453
|
+
throw new Error(`agent control ${controlId} is already running`);
|
|
454
|
+
}
|
|
455
|
+
const controller = new AbortController();
|
|
456
|
+
distributedAgentControlRuns.set(controlId, controller);
|
|
457
|
+
try {
|
|
458
|
+
const tool = await distributedAgentTool();
|
|
459
|
+
if (controller.signal.aborted) throw controller.signal.reason;
|
|
460
|
+
const context = message.context && typeof message.context === 'object'
|
|
461
|
+
? message.context
|
|
462
|
+
: {};
|
|
463
|
+
if (String(message.args?.type || '') === '__close_all') {
|
|
464
|
+
tool.closeAll?.(
|
|
465
|
+
String(message.args?.reason || 'agent owner closed'),
|
|
466
|
+
{ callerSessionId: context.callerSessionId || null },
|
|
467
|
+
);
|
|
468
|
+
return 'agent close all: ok';
|
|
469
|
+
}
|
|
470
|
+
return await tool.execute(message.args || {}, {
|
|
471
|
+
callerCwd: context.callerCwd || process.cwd(),
|
|
472
|
+
invocationSource: context.invocationSource || 'model-tool',
|
|
473
|
+
callerSessionId: context.callerSessionId || null,
|
|
474
|
+
clientHostPid: Number(context.clientHostPid) || process.pid,
|
|
475
|
+
signal: controller.signal,
|
|
476
|
+
});
|
|
477
|
+
} finally {
|
|
478
|
+
distributedAgentControlRuns.delete(controlId);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async function distributedAgentStatus(message) {
|
|
483
|
+
const tool = await distributedAgentTool();
|
|
484
|
+
return tool.getStatus?.(message.context || {}) || { workers: [], jobs: [], scope: null };
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function cancelDistributedAgentControl(message) {
|
|
488
|
+
const controlId = String(message.controlId || '');
|
|
489
|
+
const controller = distributedAgentControlRuns.get(controlId);
|
|
490
|
+
if (!controller) return { cancelled: false };
|
|
491
|
+
abortDispatchController(
|
|
492
|
+
controller,
|
|
493
|
+
new Error(String(message.reason || 'agent control canceled')),
|
|
494
|
+
);
|
|
495
|
+
return { cancelled: true };
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
async function deliverDistributedAgentNotification(message) {
|
|
499
|
+
const ownerSessionId = String(message.ownerSessionId || '');
|
|
500
|
+
if (!ownerSessionId || !message.text) return false;
|
|
501
|
+
for (const record of records.values()) {
|
|
502
|
+
if (record.disposed) continue;
|
|
503
|
+
try {
|
|
504
|
+
if (record.runtime?.deliverToolCompletion?.(
|
|
505
|
+
ownerSessionId,
|
|
506
|
+
String(message.text),
|
|
507
|
+
message.meta || {},
|
|
508
|
+
)) return true;
|
|
509
|
+
} catch {}
|
|
510
|
+
}
|
|
511
|
+
// The owning runtime may be between recycle and recreate. Persist the
|
|
512
|
+
// completion in its ordinary pending queue so the resumed session still
|
|
513
|
+
// consumes it exactly once.
|
|
514
|
+
try {
|
|
515
|
+
const [mgr, contract] = await Promise.all([
|
|
516
|
+
import('../runtime/agent/orchestrator/session/manager.mjs'),
|
|
517
|
+
import('../runtime/shared/tool-execution-contract.mjs'),
|
|
518
|
+
]);
|
|
519
|
+
const visible = contract.modelVisibleToolCompletionMessage(
|
|
520
|
+
String(message.text),
|
|
521
|
+
message.meta || {},
|
|
522
|
+
);
|
|
523
|
+
return Boolean(visible && mgr.enqueuePendingMessage(
|
|
524
|
+
ownerSessionId,
|
|
525
|
+
mgr.markCompletionEntry(visible, {
|
|
526
|
+
executionId: message.meta?.execution_id,
|
|
527
|
+
}),
|
|
528
|
+
) > 0);
|
|
529
|
+
} catch {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
// A cancel can arrive while a dispatch is still in its COLD-START window
|
|
534
|
+
// (orchestrator import + provider init) or even before its own request frame is
|
|
535
|
+
// handled. Answering "not running" there would drop the abort and let the
|
|
536
|
+
// dispatch run anyway, so the cancellation is retained and adopted the moment
|
|
537
|
+
// the dispatch registers.
|
|
538
|
+
const retainedDispatchCancels = new Map(); // dispatchId -> { reason, at }
|
|
539
|
+
const RETAINED_CANCEL_MAX = 256;
|
|
540
|
+
const RETAINED_CANCEL_TTL_MS = 5 * 60_000;
|
|
541
|
+
|
|
542
|
+
function retainDispatchCancel(dispatchId, reason) {
|
|
543
|
+
retainedDispatchCancels.delete(dispatchId);
|
|
544
|
+
retainedDispatchCancels.set(dispatchId, { reason, at: Date.now() });
|
|
545
|
+
while (retainedDispatchCancels.size > RETAINED_CANCEL_MAX) {
|
|
546
|
+
const oldest = retainedDispatchCancels.keys().next().value;
|
|
547
|
+
if (oldest === undefined) break;
|
|
548
|
+
retainedDispatchCancels.delete(oldest);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function takeRetainedDispatchCancel(dispatchId) {
|
|
553
|
+
const entry = retainedDispatchCancels.get(dispatchId);
|
|
554
|
+
if (!entry) return null;
|
|
555
|
+
retainedDispatchCancels.delete(dispatchId);
|
|
556
|
+
// A stale tombstone must never abort an unrelated later dispatch.
|
|
557
|
+
if (Date.now() - entry.at > RETAINED_CANCEL_TTL_MS) return null;
|
|
558
|
+
return entry.reason;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function abortDispatchController(controller, reason) {
|
|
562
|
+
try { controller.abort(reason); } catch { try { controller.abort(); } catch { /* settled */ } }
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function throwIfDispatchAborted(controller) {
|
|
566
|
+
if (!controller.signal.aborted) return;
|
|
567
|
+
const reason = controller.signal.reason;
|
|
568
|
+
throw reason instanceof Error ? reason : new Error(String(reason || 'agent dispatch canceled'));
|
|
569
|
+
}
|
|
160
570
|
let preparedProviderSignature = null;
|
|
161
571
|
let providerPreparePromise = null;
|
|
162
572
|
|
|
@@ -187,20 +597,31 @@ async function runAgentDispatch(message) {
|
|
|
187
597
|
if (agentDispatchRuns.has(dispatchId)) {
|
|
188
598
|
throw new Error(`agent dispatch ${dispatchId} is already running`);
|
|
189
599
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
let dispatcher = agentDispatchers.get(agent);
|
|
194
|
-
if (!dispatcher) {
|
|
195
|
-
dispatcher = dispatch.makeAgentDispatch({
|
|
196
|
-
agent,
|
|
197
|
-
...(message.options && typeof message.options === 'object' ? message.options : {}),
|
|
198
|
-
});
|
|
199
|
-
agentDispatchers.set(agent, dispatcher);
|
|
200
|
-
}
|
|
600
|
+
// Registration happens BEFORE the first await. The cold-start window (module
|
|
601
|
+
// graph import + provider init) is exactly when a user abort arrives, and an
|
|
602
|
+
// unregistered dispatch would answer cancelled:false and then run anyway.
|
|
201
603
|
const controller = new AbortController();
|
|
202
604
|
agentDispatchRuns.set(dispatchId, controller);
|
|
605
|
+
const retained = takeRetainedDispatchCancel(dispatchId);
|
|
606
|
+
if (retained) abortDispatchController(controller, retained);
|
|
203
607
|
try {
|
|
608
|
+
throwIfDispatchAborted(controller);
|
|
609
|
+
const agent = String(message.agent || '');
|
|
610
|
+
const { dispatch } = await agentGraph();
|
|
611
|
+
throwIfDispatchAborted(controller);
|
|
612
|
+
await ensureAgentSessionPublisher();
|
|
613
|
+
throwIfDispatchAborted(controller);
|
|
614
|
+
await prepareAgentProviders();
|
|
615
|
+
throwIfDispatchAborted(controller);
|
|
616
|
+
ensureProviderCooldownBridge();
|
|
617
|
+
let dispatcher = agentDispatchers.get(agent);
|
|
618
|
+
if (!dispatcher) {
|
|
619
|
+
dispatcher = dispatch.makeAgentDispatch({
|
|
620
|
+
agent,
|
|
621
|
+
...(message.options && typeof message.options === 'object' ? message.options : {}),
|
|
622
|
+
});
|
|
623
|
+
agentDispatchers.set(agent, dispatcher);
|
|
624
|
+
}
|
|
204
625
|
const params = message.params && typeof message.params === 'object' ? message.params : {};
|
|
205
626
|
const value = await dispatcher({
|
|
206
627
|
prompt: String(params.prompt ?? ''),
|
|
@@ -214,15 +635,24 @@ async function runAgentDispatch(message) {
|
|
|
214
635
|
return { value: sanitizeForWire(value) ?? null };
|
|
215
636
|
} finally {
|
|
216
637
|
agentDispatchRuns.delete(dispatchId);
|
|
638
|
+
publishProviderCooldownNow();
|
|
217
639
|
}
|
|
218
640
|
}
|
|
219
641
|
|
|
220
642
|
function cancelAgentDispatch(message) {
|
|
221
|
-
const
|
|
222
|
-
if (!controller) return { cancelled: false };
|
|
643
|
+
const dispatchId = String(message.dispatchId || '');
|
|
223
644
|
const reason = new Error(String(message.reason || 'agent dispatch canceled'));
|
|
224
|
-
|
|
225
|
-
|
|
645
|
+
const controller = agentDispatchRuns.get(dispatchId);
|
|
646
|
+
if (controller) {
|
|
647
|
+
abortDispatchController(controller, reason);
|
|
648
|
+
return { cancelled: true, retained: false };
|
|
649
|
+
}
|
|
650
|
+
if (!dispatchId) return { cancelled: false, retained: false };
|
|
651
|
+
// Not registered yet: retain the cancellation instead of losing it. The
|
|
652
|
+
// dispatch adopts it at registration, so a cold-start abort can never be
|
|
653
|
+
// answered "not running" and then keep running.
|
|
654
|
+
retainDispatchCancel(dispatchId, reason);
|
|
655
|
+
return { cancelled: true, retained: true };
|
|
226
656
|
}
|
|
227
657
|
|
|
228
658
|
async function prewarm() {
|
|
@@ -247,6 +677,8 @@ async function workloadSnapshot() {
|
|
|
247
677
|
]);
|
|
248
678
|
const memory = process.memoryUsage();
|
|
249
679
|
return {
|
|
680
|
+
shard: SHARD_INDEX,
|
|
681
|
+
eventLoopLag: lastLagSample,
|
|
250
682
|
runtimes: records.size,
|
|
251
683
|
memory: {
|
|
252
684
|
rssBytes: memory.rss,
|
|
@@ -284,9 +716,21 @@ async function exitAfterPendingWrites(code = 0) {
|
|
|
284
716
|
async function stopAll(reason = 'session runtime worker shutdown') {
|
|
285
717
|
if (stopping) return;
|
|
286
718
|
stopping = true;
|
|
719
|
+
try { stopEventLoopLagMonitor(); } catch {}
|
|
720
|
+
try { cooldownBridgeUnsubscribe?.(); } catch {}
|
|
721
|
+
cooldownBridgeUnsubscribe = null;
|
|
722
|
+
retainedDispatchCancels.clear();
|
|
723
|
+
for (const controller of distributedAgentControlRuns.values()) {
|
|
724
|
+
abortDispatchController(controller, new Error(reason));
|
|
725
|
+
}
|
|
726
|
+
distributedAgentControlRuns.clear();
|
|
287
727
|
for (const controller of agentDispatchRuns.values()) {
|
|
288
728
|
try { controller.abort(new Error(reason)); } catch { /* settled */ }
|
|
289
729
|
}
|
|
730
|
+
try { unsubscribeAgentSessionPublisher?.(); } catch {}
|
|
731
|
+
unsubscribeAgentSessionPublisher = null;
|
|
732
|
+
pendingAgentSessionProjections.clear();
|
|
733
|
+
try { (await distributedAgentToolPromise)?.closeAll?.(); } catch {}
|
|
290
734
|
for (const record of [...records.values()]) {
|
|
291
735
|
try {
|
|
292
736
|
await disposeSessionRuntimeRecord(
|
|
@@ -302,7 +746,16 @@ async function stopAll(reason = 'session runtime worker shutdown') {
|
|
|
302
746
|
process.on('message', (message) => {
|
|
303
747
|
if (!message || typeof message !== 'object') return;
|
|
304
748
|
// token-native.mjs owns daemon-relayed token count responses.
|
|
305
|
-
if (message.type === 'token-native-result') return;
|
|
749
|
+
if (message.type === 'token-native-result' || message.type === 'agent-control-result') return;
|
|
750
|
+
// Fire-and-forget shard coordination: no response frame, no request id.
|
|
751
|
+
if (message.type === 'provider-cooldown-sync') {
|
|
752
|
+
void applyReplayedProviderCooldown(message);
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
if (message.type === 'agent-control-notification') {
|
|
756
|
+
void deliverDistributedAgentNotification(message);
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
306
759
|
const requestId = String(message.requestId || '');
|
|
307
760
|
void (async () => {
|
|
308
761
|
if (message.type === 'create') return createRuntime(message);
|
|
@@ -317,6 +770,11 @@ process.on('message', (message) => {
|
|
|
317
770
|
if (message.type === 'workload') return workloadSnapshot();
|
|
318
771
|
if (message.type === 'agent-dispatch') return runAgentDispatch(message);
|
|
319
772
|
if (message.type === 'agent-dispatch-cancel') return cancelAgentDispatch(message);
|
|
773
|
+
if (message.type === 'agent-control-local') return runDistributedAgentControl(message);
|
|
774
|
+
if (message.type === 'agent-control-status') return distributedAgentStatus(message);
|
|
775
|
+
if (message.type === 'agent-control-local-cancel') {
|
|
776
|
+
return cancelDistributedAgentControl(message);
|
|
777
|
+
}
|
|
320
778
|
if (message.type === 'shutdown') {
|
|
321
779
|
await stopAll(message.reason);
|
|
322
780
|
return { stopped: true };
|