zelari-code 2.15.0 → 2.16.0
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/dist/cli/companion/cors.js +52 -0
- package/dist/cli/companion/cors.js.map +1 -0
- package/dist/cli/companion/runManager.js +233 -1
- package/dist/cli/companion/runManager.js.map +1 -1
- package/dist/cli/companion/serve.js +21 -4
- package/dist/cli/companion/serve.js.map +1 -1
- package/dist/cli/extensions/extensionToolWiring.js +57 -0
- package/dist/cli/extensions/extensionToolWiring.js.map +1 -0
- package/dist/cli/extensions/loader.js +213 -0
- package/dist/cli/extensions/loader.js.map +1 -0
- package/dist/cli/extensions/sandboxedFs.js +69 -0
- package/dist/cli/extensions/sandboxedFs.js.map +1 -0
- package/dist/cli/headless/runOneTurn.js +783 -0
- package/dist/cli/headless/runOneTurn.js.map +1 -0
- package/dist/cli/kraken/completionGate.js +22 -5
- package/dist/cli/kraken/completionGate.js.map +1 -1
- package/dist/cli/lsp/manager.js +16 -0
- package/dist/cli/lsp/manager.js.map +1 -1
- package/dist/cli/main.bundled.js +17714 -15752
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +20 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/runHeadless.js +5 -695
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/safety/jails/darwin.js +87 -0
- package/dist/cli/safety/jails/darwin.js.map +1 -0
- package/dist/cli/safety/jails/linux.js +80 -0
- package/dist/cli/safety/jails/linux.js.map +1 -0
- package/dist/cli/safety/jails/win32.js +19 -0
- package/dist/cli/safety/jails/win32.js.map +1 -0
- package/dist/cli/safety/lifecycleHooks.js +33 -3
- package/dist/cli/safety/lifecycleHooks.js.map +1 -1
- package/dist/cli/safety/osJail.js +289 -0
- package/dist/cli/safety/osJail.js.map +1 -0
- package/dist/cli/safety/policyLoadMode.js.map +1 -1
- package/dist/cli/serve/harnessClient.js +150 -0
- package/dist/cli/serve/harnessClient.js.map +1 -0
- package/dist/cli/serve/harnessServer.js +333 -0
- package/dist/cli/serve/harnessServer.js.map +1 -0
- package/dist/cli/serve/sessionControl.js +67 -0
- package/dist/cli/serve/sessionControl.js.map +1 -0
- package/dist/cli/toolRegistry.js +338 -33
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/tools/execProcess.js +36 -15
- package/dist/cli/tools/execProcess.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* harnessServer — `--serve-harness` (t29, Pilastro B).
|
|
3
|
+
*
|
|
4
|
+
* A long-lived host for the HarnessAppServer kernel
|
|
5
|
+
* (@zelari/core/harness) speaking line-delimited JSON (NDJSON) over
|
|
6
|
+
* stdio, on top of the EXISTING headless protocol v2: the boot line is
|
|
7
|
+
* the same `protocol_info` event `--headless` emits (version 2), and
|
|
8
|
+
* while a turn runs the same BrainEvent stream is forwarded verbatim.
|
|
9
|
+
*
|
|
10
|
+
* Wire format (client → server requests carry `id`; server → client
|
|
11
|
+
* unsolicited events carry `type` — clients demultiplex on that):
|
|
12
|
+
*
|
|
13
|
+
* → {"id":1,"method":"session.create","params":{"workspaceRoot":"…"}}
|
|
14
|
+
* ← {"id":1,"ok":true,"result":{"sessionId":"…"}}
|
|
15
|
+
* → {"id":2,"method":"run.turn","params":{"sessionId":"…", …turn input
|
|
16
|
+
* same shape as a headless single turn (HeadlessOptions)}}
|
|
17
|
+
* ← {"id":2,"ok":true,"result":{"exitCode":0}}
|
|
18
|
+
* → {"id":3,"method":"session.dispose","params":{"sessionId":"…"}}
|
|
19
|
+
* ← {"id":3,"ok":true,"result":{"disposed":true}}
|
|
20
|
+
* → {"id":4,"method":"session.steer","params":{"sessionId":"…","text":"…",
|
|
21
|
+
* "controlId":"s1"}} (t32, protocol v2 unchanged)
|
|
22
|
+
* ← {"id":4,"ok":true,"result":{"accepted":true,"controlId":"s1",…}}
|
|
23
|
+
* ← {"type":"control_accepted","controlId":"s1","controlType":"steer",…}
|
|
24
|
+
* (unsolicited §24 acks ride the same stdout; `control_applied`
|
|
25
|
+
* arrives later, when the turn's SteeringObserver drains the queue)
|
|
26
|
+
* → {"id":5,"method":"session.cancel","params":{"sessionId":"…","reason":"…"}}
|
|
27
|
+
* ← {"id":5,"ok":true,"result":{"accepted":true,"delivered":true,…}}
|
|
28
|
+
* steer/cancel on a session with no running turn → ok:true with
|
|
29
|
+
* {"accepted":false,"outcome":"already_finished"} (explicit noop, no crash)
|
|
30
|
+
* unknown method → {"id":n,"ok":false,"error":{"code":"unknown_method",…}}
|
|
31
|
+
* unknown session → {"id":n,"ok":false,"error":{"code":"unknown_session",…}}
|
|
32
|
+
* malformed line → {"id":null,"ok":false,"error":{"code":"bad_json",…}}
|
|
33
|
+
* (the server process NEVER exits on bad input)
|
|
34
|
+
*
|
|
35
|
+
* Error codes: bad_json · bad_request · unknown_method · unknown_session ·
|
|
36
|
+
* method_failed. Malformed JSON and handler failures are contained — the
|
|
37
|
+
* process keeps serving the next line.
|
|
38
|
+
*
|
|
39
|
+
* Ownership (the whole point of Pilastro B): the SERVER owns per-workspace
|
|
40
|
+
* services (one LspManager + policy cache per resolved workspace root —
|
|
41
|
+
* a second session on the same workspace does NOT respawn them) and the
|
|
42
|
+
* completion-proof persistence (a client disconnect never cancels an
|
|
43
|
+
* in-flight/queued proof write). The in-process stdin control plane is
|
|
44
|
+
* suppressed in this mode (ZELARI_SERVE_HARNESS=1) because the transport
|
|
45
|
+
* owns stdin. `--headless` remains the in-process CI client of the same
|
|
46
|
+
* kernel via runOneTurn — no fork, same code path.
|
|
47
|
+
*/
|
|
48
|
+
import { createInterface } from 'node:readline';
|
|
49
|
+
import { randomUUID } from 'node:crypto';
|
|
50
|
+
import { HarnessAppServer } from '@zelari/core/harness';
|
|
51
|
+
import { controlAcceptedEvent, controlAppliedEvent, protocolInfoEvent } from '../headless/protocol.js';
|
|
52
|
+
import { runOneTurn } from '../headless/runOneTurn.js';
|
|
53
|
+
import { clearSessionTurnControl, getLiveTurnControl, runWithSession, } from './sessionControl.js';
|
|
54
|
+
import { resolveHeadlessKey, resolveHeadlessProvider } from '../headless.js';
|
|
55
|
+
import { LspManager } from '../lsp/manager.js';
|
|
56
|
+
import { writeCompletionProofDetailed } from '../kraken/completionProof.js';
|
|
57
|
+
import { setActiveProofPersistenceSurface } from '../kraken/completionProofPersist.js';
|
|
58
|
+
import { checkStrictPolicyLoad } from '../headless/policyGate.js';
|
|
59
|
+
import { activePolicyLoadMode, setActivePolicyLoadSurface } from '../safety/policyLoadMode.js';
|
|
60
|
+
/**
|
|
61
|
+
* Real per-workspace services: one LspManager anchored to the workspace,
|
|
62
|
+
* the strict policy-load snapshot (cached per root by the kernel), and the
|
|
63
|
+
* durable completion-proof writer (atomic tmp→fsync→rename, CLI-owned).
|
|
64
|
+
*/
|
|
65
|
+
export function createCliWorkspaceServices(workspaceRoot) {
|
|
66
|
+
const mode = activePolicyLoadMode();
|
|
67
|
+
const policyLoad = checkStrictPolicyLoad(workspaceRoot, { mode });
|
|
68
|
+
// Extra host fields ride along; the kernel sees the PolicyCacheLike shape.
|
|
69
|
+
const policyCache = {
|
|
70
|
+
workspaceRoot,
|
|
71
|
+
loadedAt: Date.now(),
|
|
72
|
+
mode,
|
|
73
|
+
blocked: policyLoad.blocked,
|
|
74
|
+
warnings: policyLoad.warnings,
|
|
75
|
+
};
|
|
76
|
+
return {
|
|
77
|
+
lspManager: new LspManager({ cwd: workspaceRoot }),
|
|
78
|
+
policyCache,
|
|
79
|
+
completionProofWriter: async (request) => {
|
|
80
|
+
await writeCompletionProofDetailed(request.payload, {
|
|
81
|
+
baseDir: request.baseDir,
|
|
82
|
+
meta: { surface: request.surface, sessionId: request.sessionId },
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Real turn implementation: provider/key/stream resolution happens ONCE
|
|
89
|
+
* per server (mirroring runHeadless: one process, one key), then each
|
|
90
|
+
* run.turn is a runOneTurn call — the exact `--headless` code path.
|
|
91
|
+
*/
|
|
92
|
+
export function createCliRunTurn() {
|
|
93
|
+
let streamPromise = null;
|
|
94
|
+
const ensureStream = () => {
|
|
95
|
+
if (!streamPromise) {
|
|
96
|
+
streamPromise = (async () => {
|
|
97
|
+
const { provider, model } = resolveHeadlessProvider({});
|
|
98
|
+
const key = await resolveHeadlessKey(provider);
|
|
99
|
+
if ('error' in key)
|
|
100
|
+
throw new Error(key.error);
|
|
101
|
+
const { buildProviderStream } = await import('../provider/resolveStream.js');
|
|
102
|
+
const stream = buildProviderStream({
|
|
103
|
+
providerId: provider,
|
|
104
|
+
apiKey: key.apiKey,
|
|
105
|
+
baseUrl: key.baseUrl,
|
|
106
|
+
model,
|
|
107
|
+
});
|
|
108
|
+
return { provider, model, stream };
|
|
109
|
+
})();
|
|
110
|
+
streamPromise.catch(() => {
|
|
111
|
+
streamPromise = null; // allow a retry after the key materializes
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return streamPromise;
|
|
115
|
+
};
|
|
116
|
+
return async (input, deps) => {
|
|
117
|
+
const { provider, model, stream } = await ensureStream();
|
|
118
|
+
// Transport-boundary validation: a headless single turn needs a task.
|
|
119
|
+
// Everything else arrives as untrusted JSON, so the payload is cast
|
|
120
|
+
// once (unknown → Omit) and the required/forced fields are set here.
|
|
121
|
+
const turnInput = input;
|
|
122
|
+
if (typeof turnInput.task !== 'string' || turnInput.task.length === 0) {
|
|
123
|
+
throw new Error('run.turn requires a non-empty string `task`');
|
|
124
|
+
}
|
|
125
|
+
const opts = {
|
|
126
|
+
...input,
|
|
127
|
+
task: turnInput.task,
|
|
128
|
+
mode: (typeof turnInput.mode === 'string'
|
|
129
|
+
? turnInput.mode
|
|
130
|
+
: 'kraken'),
|
|
131
|
+
output: 'json', // NDJSON transport: BrainEvents ride the same stdout
|
|
132
|
+
};
|
|
133
|
+
const exitCode = await runOneTurn(opts, provider, model, stream);
|
|
134
|
+
void deps.services.policyCache.workspaceRoot;
|
|
135
|
+
return { exitCode };
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/** Typed unknown_session envelope (shared by the session-scoped methods). */
|
|
139
|
+
function unknownSessionEnvelope(id, rawSessionId) {
|
|
140
|
+
return {
|
|
141
|
+
id,
|
|
142
|
+
ok: false,
|
|
143
|
+
error: { code: 'unknown_session', message: `no session '${String(rawSessionId ?? '')}'` },
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Boot the kernel + NDJSON transport. Injectable I/O keeps this testable
|
|
148
|
+
* without spawning a process; `runHarnessServer()` below is the stdio entry.
|
|
149
|
+
*/
|
|
150
|
+
export function startHarnessServer(options = {}) {
|
|
151
|
+
const io = options.io ?? { input: process.stdin, output: process.stdout };
|
|
152
|
+
const server = new HarnessAppServer({
|
|
153
|
+
createWorkspaceServices: options.createWorkspaceServices ?? createCliWorkspaceServices,
|
|
154
|
+
});
|
|
155
|
+
const write = (line) => {
|
|
156
|
+
try {
|
|
157
|
+
io.output.write(line + '\n');
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
/* transport is gone (EPIPE) — the kernel outlives the client */
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const respond = (envelope) => {
|
|
164
|
+
write(JSON.stringify(envelope));
|
|
165
|
+
};
|
|
166
|
+
const dispatch = async (req) => {
|
|
167
|
+
if (typeof req.method !== 'string') {
|
|
168
|
+
return { id: req.id ?? null, ok: false, error: { code: 'bad_request', message: 'missing method' } };
|
|
169
|
+
}
|
|
170
|
+
const params = req.params ?? {};
|
|
171
|
+
switch (req.method) {
|
|
172
|
+
case 'session.create': {
|
|
173
|
+
const root = typeof params.workspaceRoot === 'string' ? params.workspaceRoot : process.cwd();
|
|
174
|
+
const session = server.createSession({ workspaceRoot: root, runTurn: options.runTurn ?? createCliRunTurn() });
|
|
175
|
+
return { id: req.id ?? null, ok: true, result: { sessionId: session.id, workspaceRoot: session.workspaceRoot } };
|
|
176
|
+
}
|
|
177
|
+
case 'run.turn': {
|
|
178
|
+
const session = server.getSession(String(params.sessionId ?? ''));
|
|
179
|
+
if (!session) {
|
|
180
|
+
return { id: req.id ?? null, ok: false, error: { code: 'unknown_session', message: `no session '${String(params.sessionId ?? '')}'` } };
|
|
181
|
+
}
|
|
182
|
+
const { sessionId: _ignored, ...turnInput } = params;
|
|
183
|
+
// t32: carry the session id into runOneTurn's control-plane
|
|
184
|
+
// registration via async context (same call-chain, no new
|
|
185
|
+
// HeadlessOptions field). Settlement cleanup drops the registration
|
|
186
|
+
// even on a crash path, so later steers get the honest
|
|
187
|
+
// already_finished noop instead of targeting a dead queue.
|
|
188
|
+
const result = await runWithSession(session.id, async () => {
|
|
189
|
+
try {
|
|
190
|
+
return await session.runTurn(turnInput);
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
clearSessionTurnControl(session.id);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
return { id: req.id ?? null, ok: true, result };
|
|
197
|
+
}
|
|
198
|
+
case 'session.dispose': {
|
|
199
|
+
const session = server.getSession(String(params.sessionId ?? ''));
|
|
200
|
+
if (!session) {
|
|
201
|
+
return { id: req.id ?? null, ok: false, error: { code: 'unknown_session', message: `no session '${String(params.sessionId ?? '')}'` } };
|
|
202
|
+
}
|
|
203
|
+
await session.dispose();
|
|
204
|
+
return { id: req.id ?? null, ok: true, result: { disposed: true } };
|
|
205
|
+
}
|
|
206
|
+
// t32: session-scoped steer/cancel over the SAME protocol v2 — no new
|
|
207
|
+
// protocol version, same §24 ack events the stdin bridge emits.
|
|
208
|
+
case 'session.steer':
|
|
209
|
+
case 'session.cancel': {
|
|
210
|
+
const isCancel = req.method === 'session.cancel';
|
|
211
|
+
const session = server.getSession(String(params.sessionId ?? ''));
|
|
212
|
+
if (!session) {
|
|
213
|
+
return unknownSessionEnvelope(req.id ?? null, params.sessionId);
|
|
214
|
+
}
|
|
215
|
+
const controlId = typeof params.controlId === 'string' && params.controlId.length > 0
|
|
216
|
+
? params.controlId
|
|
217
|
+
: `ctrl-${randomUUID()}`;
|
|
218
|
+
const controlType = isCancel ? 'cancel' : 'steer';
|
|
219
|
+
// Shape validation BEFORE state checks: a malformed control is a
|
|
220
|
+
// bad_request regardless of session/turn state (same shape rule as
|
|
221
|
+
// the stdin control reader, controlReader.ts).
|
|
222
|
+
const text = params.text;
|
|
223
|
+
if (!isCancel && (typeof text !== 'string' || text.trim().length === 0)) {
|
|
224
|
+
return {
|
|
225
|
+
id: req.id ?? null,
|
|
226
|
+
ok: false,
|
|
227
|
+
error: { code: 'bad_request', message: 'session.steer requires a non-empty string `text`' },
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
const live = getLiveTurnControl(session.id);
|
|
231
|
+
if (!live) {
|
|
232
|
+
// Known session, but no turn is running (never started, already
|
|
233
|
+
// completed or cancelled): EXPLICIT noop result — never a crash,
|
|
234
|
+
// never a fake acceptance (§24: never pretend a control applied).
|
|
235
|
+
return {
|
|
236
|
+
id: req.id ?? null,
|
|
237
|
+
ok: true,
|
|
238
|
+
result: { accepted: false, outcome: 'already_finished', controlId, controlType },
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (isCancel) {
|
|
242
|
+
// Nothing drains cancel events mid-run, so application goes
|
|
243
|
+
// through the cooperative harness cancel hook directly; the queue
|
|
244
|
+
// never holds an undrainable cancel.
|
|
245
|
+
const delivered = live.cancel();
|
|
246
|
+
if (delivered) {
|
|
247
|
+
write(JSON.stringify(controlAppliedEvent(controlId, 'cancel', 'cancel')));
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
id: req.id ?? null,
|
|
251
|
+
ok: true,
|
|
252
|
+
result: { accepted: true, delivered, controlId, controlType },
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
// §24: enqueue = accepted; the control_applied ack (boundary
|
|
256
|
+
// turn-end) is emitted by the turn when SteeringObserver drains.
|
|
257
|
+
live.queue.enqueue({ type: 'steer', id: controlId, text: text, ts: Date.now() });
|
|
258
|
+
write(JSON.stringify(controlAcceptedEvent(controlId, 'steer')));
|
|
259
|
+
return {
|
|
260
|
+
id: req.id ?? null,
|
|
261
|
+
ok: true,
|
|
262
|
+
result: { accepted: true, controlId, controlType, boundary: 'turn-end' },
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
default:
|
|
266
|
+
return {
|
|
267
|
+
id: req.id ?? null,
|
|
268
|
+
ok: false,
|
|
269
|
+
error: { code: 'unknown_method', message: `unsupported method '${req.method}'` },
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
const rl = createInterface({ input: io.input });
|
|
274
|
+
// Boot handshake — same protocol_info envelope/version as `--headless`.
|
|
275
|
+
write(JSON.stringify(protocolInfoEvent()));
|
|
276
|
+
rl.on('line', (line) => {
|
|
277
|
+
const trimmed = line.trim();
|
|
278
|
+
if (trimmed === '')
|
|
279
|
+
return;
|
|
280
|
+
let req;
|
|
281
|
+
try {
|
|
282
|
+
req = JSON.parse(trimmed);
|
|
283
|
+
if (req === null || typeof req !== 'object')
|
|
284
|
+
throw new Error('not an object');
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
respond({
|
|
288
|
+
id: null,
|
|
289
|
+
ok: false,
|
|
290
|
+
error: {
|
|
291
|
+
code: 'bad_json',
|
|
292
|
+
message: `malformed JSON line skipped: ${err instanceof Error ? err.message : String(err)}`,
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
return; // server keeps running (DoD e)
|
|
296
|
+
}
|
|
297
|
+
void dispatch(req).then((envelope) => respond(envelope), (err) => respond({
|
|
298
|
+
id: (req.id ?? null),
|
|
299
|
+
ok: false,
|
|
300
|
+
error: {
|
|
301
|
+
code: 'method_failed',
|
|
302
|
+
message: err instanceof Error ? err.message : String(err),
|
|
303
|
+
},
|
|
304
|
+
}));
|
|
305
|
+
});
|
|
306
|
+
return {
|
|
307
|
+
server,
|
|
308
|
+
close() {
|
|
309
|
+
// Kernel teardown FIRST-class: pending completion-proof writes are
|
|
310
|
+
// awaited (never cancelled) before services die.
|
|
311
|
+
rl.close();
|
|
312
|
+
io.input.pause();
|
|
313
|
+
return server.dispose();
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/** Process entry for `--serve-harness`: stdio transport + host pre-flight. */
|
|
318
|
+
export async function runHarnessServer() {
|
|
319
|
+
// Same host registration runHeadless performs before any registry exists.
|
|
320
|
+
setActivePolicyLoadSurface('headless');
|
|
321
|
+
setActiveProofPersistenceSurface('headless');
|
|
322
|
+
// runOneTurn checks this to keep its paws off stdin (transport-owned).
|
|
323
|
+
process.env.ZELARI_SERVE_HARNESS = '1';
|
|
324
|
+
const started = startHarnessServer();
|
|
325
|
+
await new Promise((resolvePromise) => {
|
|
326
|
+
const finish = () => void started.close().then(() => resolvePromise());
|
|
327
|
+
process.stdin.once('end', finish);
|
|
328
|
+
process.stdin.once('close', finish);
|
|
329
|
+
process.once('SIGINT', finish);
|
|
330
|
+
process.once('SIGTERM', finish);
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=harnessServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harnessServer.js","sourceRoot":"","sources":["../../../src/cli/serve/harnessServer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAMxD,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACvG,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAwB,MAAM,gBAAgB,CAAC;AACnG,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,gCAAgC,EAAE,MAAM,qCAAqC,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AA6B/F;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,aAAqB;IAC9D,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;IACpC,MAAM,UAAU,GAAG,qBAAqB,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,2EAA2E;IAC3E,MAAM,WAAW,GAAG;QAClB,aAAa;QACb,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE;QACpB,IAAI;QACJ,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,QAAQ,EAAE,UAAU,CAAC,QAAQ;KAC9B,CAAC;IACF,OAAO;QACL,UAAU,EAAE,IAAI,UAAU,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC;QAClD,WAAW;QACX,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACvC,MAAM,4BAA4B,CAChC,OAAO,CAAC,OAAwE,EAChF;gBACE,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE;aACjE,CACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,IAAI,aAAa,GAAyE,IAAI,CAAC;IAC/F,MAAM,YAAY,GAAG,GAAG,EAAE;QACxB,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,aAAa,GAAG,CAAC,KAAK,IAAI,EAAE;gBAC1B,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,uBAAuB,CAAC,EAAqB,CAAC,CAAC;gBAC3E,MAAM,GAAG,GAAG,MAAM,kBAAkB,CAAC,QAAQ,CAAC,CAAC;gBAC/C,IAAI,OAAO,IAAI,GAAG;oBAAE,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBAC/C,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;gBAC7E,MAAM,MAAM,GAAG,mBAAmB,CAAC;oBACjC,UAAU,EAAE,QAAiD;oBAC7D,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,OAAO,EAAE,GAAG,CAAC,OAAO;oBACpB,KAAK;iBACN,CAAC,CAAC;gBACH,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;YACrC,CAAC,CAAC,EAAE,CAAC;YACL,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE;gBACvB,aAAa,GAAG,IAAI,CAAC,CAAC,2CAA2C;YACnE,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,aAAa,CAAC;IACvB,CAAC,CAAC;IACF,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,YAAY,EAAE,CAAC;QACzD,sEAAsE;QACtE,oEAAoE;QACpE,qEAAqE;QACrE,MAAM,SAAS,GAAG,KAAiC,CAAC;QACpD,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,IAAI,GAAoB;YAC5B,GAAI,KAAsE;YAC1E,IAAI,EAAE,SAAS,CAAC,IAAI;YACpB,IAAI,EAAE,CAAC,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ;gBACvC,CAAC,CAAC,SAAS,CAAC,IAAI;gBAChB,CAAC,CAAC,QAAQ,CAAyC;YACrD,MAAM,EAAE,MAAM,EAAE,qDAAqD;SACtE,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,UAAU,CAC/B,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,MAA0C,CAC3C,CAAC;QACF,KAAK,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,aAAa,CAAC;QAC7C,OAAO,EAAE,QAAQ,EAAE,CAAC;IACtB,CAAC,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,SAAS,sBAAsB,CAC7B,EAA0B,EAC1B,YAAqB;IAErB,OAAO;QACL,EAAE;QACF,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC,GAAG,EAAE;KAC1F,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAqC,EAAE;IAIxE,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IAC1E,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC;QAClC,uBAAuB,EAAE,OAAO,CAAC,uBAAuB,IAAI,0BAA0B;KACvF,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,CAAC,IAAY,EAAQ,EAAE;QACnC,IAAI,CAAC;YACH,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,gEAAgE;QAClE,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,CAAC,QAA0B,EAAQ,EAAE;QACnD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,KAAK,EAAE,GAAoB,EAA6B,EAAE;QACzE,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACnC,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,gBAAgB,EAAE,EAAE,CAAC;QACtG,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;QAChC,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,gBAAgB,CAAC,CAAC,CAAC;gBACtB,MAAM,IAAI,GAAG,OAAO,MAAM,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;gBAC7F,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,gBAAgB,EAAE,EAAE,CAAC,CAAC;gBAC9G,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;YACnH,CAAC;YACD,KAAK,UAAU,CAAC,CAAC,CAAC;gBAChB,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC;gBAClE,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe,MAAM,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;gBAC1I,CAAC;gBACD,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,SAAS,EAAE,GAAG,MAAM,CAAC;gBACrD,4DAA4D;gBAC5D,0DAA0D;gBAC1D,oEAAoE;gBACpE,uDAAuD;gBACvD,2DAA2D;gBAC3D,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI,EAAE;oBACzD,IAAI,CAAC;wBACH,OAAO,MAAM,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;oBAC1C,CAAC;4BAAS,CAAC;wBACT,uBAAuB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBACtC,CAAC;gBACH,CAAC,CAAC,CAAC;gBACH,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YAClD,CAAC;YACD,KAAK,iBAAiB,CAAC,CAAC,CAAC;gBACvB,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC;gBAClE,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe,MAAM,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;gBAC1I,CAAC;gBACD,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;gBACxB,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;YACtE,CAAC;YACD,sEAAsE;YACtE,gEAAgE;YAChE,KAAK,eAAe,CAAC;YACrB,KAAK,gBAAgB,CAAC,CAAC,CAAC;gBACtB,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,KAAK,gBAAgB,CAAC;gBACjD,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC;gBAClE,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,sBAAsB,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;gBAClE,CAAC;gBACD,MAAM,SAAS,GACb,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;oBACjE,CAAC,CAAC,MAAM,CAAC,SAAS;oBAClB,CAAC,CAAC,QAAQ,UAAU,EAAE,EAAE,CAAC;gBAC7B,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;gBAClD,iEAAiE;gBACjE,mEAAmE;gBACnE,+CAA+C;gBAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;gBACzB,IAAI,CAAC,QAAQ,IAAI,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;oBACxE,OAAO;wBACL,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI;wBAClB,EAAE,EAAE,KAAK;wBACT,KAAK,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,kDAAkD,EAAE;qBAC5F,CAAC;gBACJ,CAAC;gBACD,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAC5C,IAAI,CAAC,IAAI,EAAE,CAAC;oBACV,gEAAgE;oBAChE,iEAAiE;oBACjE,kEAAkE;oBAClE,OAAO;wBACL,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI;wBAClB,EAAE,EAAE,IAAI;wBACR,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,WAAW,EAAE;qBACjF,CAAC;gBACJ,CAAC;gBACD,IAAI,QAAQ,EAAE,CAAC;oBACb,4DAA4D;oBAC5D,kEAAkE;oBAClE,qCAAqC;oBACrC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;oBAChC,IAAI,SAAS,EAAE,CAAC;wBACd,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;oBAC5E,CAAC;oBACD,OAAO;wBACL,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI;wBAClB,EAAE,EAAE,IAAI;wBACR,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE;qBAC9D,CAAC;gBACJ,CAAC;gBACD,6DAA6D;gBAC7D,iEAAiE;gBACjE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAc,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBAC3F,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;gBAChE,OAAO;oBACL,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI;oBAClB,EAAE,EAAE,IAAI;oBACR,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE;iBACzE,CAAC;YACJ,CAAC;YACD;gBACE,OAAO;oBACL,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI;oBAClB,EAAE,EAAE,KAAK;oBACT,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,uBAAuB,GAAG,CAAC,MAAM,GAAG,EAAE;iBACjF,CAAC;QACN,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;IAChD,wEAAwE;IACxE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC;IAC3C,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACrB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,KAAK,EAAE;YAAE,OAAO;QAC3B,IAAI,GAAoB,CAAC;QACzB,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAoB,CAAC;YAC7C,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QAChF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC;gBACN,EAAE,EAAE,IAAI;gBACR,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE;oBACL,IAAI,EAAE,UAAU;oBAChB,OAAO,EAAE,gCAAgC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;iBAC5F;aACF,CAAC,CAAC;YACH,OAAO,CAAC,+BAA+B;QACzC,CAAC;QACD,KAAK,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CACrB,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,EAC/B,CAAC,GAAG,EAAE,EAAE,CACN,OAAO,CAAC;YACN,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,CAA2B;YAC9C,EAAE,EAAE,KAAK;YACT,KAAK,EAAE;gBACL,IAAI,EAAE,eAAe;gBACrB,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aAC1D;SACF,CAAC,CACL,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,MAAM;QACN,KAAK;YACH,mEAAmE;YACnE,iDAAiD;YACjD,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC;KACF,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,0EAA0E;IAC1E,0BAA0B,CAAC,UAAU,CAAC,CAAC;IACvC,gCAAgC,CAAC,UAAU,CAAC,CAAC;IAC7C,uEAAuE;IACvE,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,GAAG,CAAC;IACvC,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;IACrC,MAAM,IAAI,OAAO,CAAO,CAAC,cAAc,EAAE,EAAE;QACzC,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,CAAC;QACvE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACpC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/B,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-scoped control plane for the harness App Server (t32, Pilastro B
|
|
3
|
+
* residuo).
|
|
4
|
+
*
|
|
5
|
+
* In `--serve-harness` mode the NDJSON transport owns stdin, so the headless
|
|
6
|
+
* stdin control plane (controlBridge.ts) cannot serve per-session
|
|
7
|
+
* steer/cancel. Instead:
|
|
8
|
+
* - the server's `run.turn` dispatch carries the harness sessionId down the
|
|
9
|
+
* turn's async call-chain via AsyncLocalStorage (no new HeadlessOptions
|
|
10
|
+
* field, CLI/protocol surface unchanged, concurrent sessions isolated);
|
|
11
|
+
* - runOneTurn (serve mode ONLY) registers its per-turn RuntimeControlQueue
|
|
12
|
+
* + cooperative cancel hook under that session id;
|
|
13
|
+
* - the server answers `session.steer` / `session.cancel` NDJSON methods by
|
|
14
|
+
* targeting the live registration, with protocol-v2 acks (§24:
|
|
15
|
+
* accepted ≠ applied — `control_applied` still fires from the queue drain
|
|
16
|
+
* / the cancel hook, never fabricated by this module).
|
|
17
|
+
*
|
|
18
|
+
* Plain `--headless` never dispatches through runWithSession, so
|
|
19
|
+
* registerLiveTurnControl returns undefined there and behavior is unchanged.
|
|
20
|
+
*/
|
|
21
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
22
|
+
const dispatchContext = new AsyncLocalStorage();
|
|
23
|
+
/** Live registry: harness sessionId → its running turn control (if any). */
|
|
24
|
+
const liveTurns = new Map();
|
|
25
|
+
/** Run `fn` inside a session dispatch context (server → runOneTurn). */
|
|
26
|
+
export function runWithSession(sessionId, fn) {
|
|
27
|
+
return dispatchContext.run({ sessionId, token: {} }, fn);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* runOneTurn (serve mode) registers the per-turn control surface. Returns
|
|
31
|
+
* the unregister fn (identity-guarded, safe across back-to-back turns on
|
|
32
|
+
* the same session), or undefined when NOT dispatched by the harness server
|
|
33
|
+
* — plain `--headless` keeps its stdin control plane, zero behavior change.
|
|
34
|
+
*/
|
|
35
|
+
export function registerLiveTurnControl(control) {
|
|
36
|
+
const store = dispatchContext.getStore();
|
|
37
|
+
if (!store)
|
|
38
|
+
return undefined;
|
|
39
|
+
const registered = { ...control, token: store.token };
|
|
40
|
+
liveTurns.set(store.sessionId, registered);
|
|
41
|
+
return () => {
|
|
42
|
+
if (liveTurns.get(store.sessionId) === registered) {
|
|
43
|
+
liveTurns.delete(store.sessionId);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** The live turn control for a session, when one is running. */
|
|
48
|
+
export function getLiveTurnControl(sessionId) {
|
|
49
|
+
return liveTurns.get(sessionId);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Server-side settlement cleanup (called inside runWithSession, after the
|
|
53
|
+
* turn promise settles). Drops the registration of THIS dispatch only —
|
|
54
|
+
* token-matched, so a concurrent newer turn on the same session survives.
|
|
55
|
+
* Covers the crash path where the turn impl never unregistered, keeping the
|
|
56
|
+
* `already_finished` noop honest instead of steering a dead queue.
|
|
57
|
+
*/
|
|
58
|
+
export function clearSessionTurnControl(sessionId) {
|
|
59
|
+
const store = dispatchContext.getStore();
|
|
60
|
+
if (!store || store.sessionId !== sessionId)
|
|
61
|
+
return;
|
|
62
|
+
const registered = liveTurns.get(sessionId);
|
|
63
|
+
if (registered && registered.token === store.token) {
|
|
64
|
+
liveTurns.delete(sessionId);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=sessionControl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionControl.js","sourceRoot":"","sources":["../../../src/cli/serve/sessionControl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAoBrD,MAAM,eAAe,GAAG,IAAI,iBAAiB,EAAwC,CAAC;AAEtF,4EAA4E;AAC5E,MAAM,SAAS,GAAG,IAAI,GAAG,EAA0B,CAAC;AAEpD,wEAAwE;AACxE,MAAM,UAAU,cAAc,CAAI,SAAiB,EAAE,EAAW;IAC9D,OAAO,eAAe,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,EAAY,EAAE,EAAE,EAAE,CAAC,CAAC;AACrE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAAwB;IAExB,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC;IACzC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,UAAU,GAAmB,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IACtE,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC3C,OAAO,GAAG,EAAE;QACV,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,UAAU,EAAE,CAAC;YAClD,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,kBAAkB,CAChC,SAAiB;IAEjB,OAAO,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAiB;IACvD,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC;IACzC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;QAAE,OAAO;IACpD,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC5C,IAAI,UAAU,IAAI,UAAU,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC;QACnD,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC9B,CAAC;AACH,CAAC"}
|