agenthub-multiagent-mcp 1.52.0 → 1.53.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/captureHookInstall.d.ts +127 -0
- package/dist/captureHookInstall.d.ts.map +1 -0
- package/dist/captureHookInstall.js +256 -0
- package/dist/captureHookInstall.js.map +1 -0
- package/dist/captureHookInstall.test.d.ts +10 -0
- package/dist/captureHookInstall.test.d.ts.map +1 -0
- package/dist/captureHookInstall.test.js +325 -0
- package/dist/captureHookInstall.test.js.map +1 -0
- package/dist/channel.js +96 -0
- package/dist/channel.js.map +1 -1
- package/dist/client.d.ts +50 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +90 -0
- package/dist/client.js.map +1 -1
- package/dist/commands/setup-shell.js +11 -11
- package/dist/daemon.js +142 -3
- package/dist/daemon.js.map +1 -1
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -1
- package/dist/session-capture.sh +410 -0
- package/dist/sessionCapture.d.ts +163 -0
- package/dist/sessionCapture.d.ts.map +1 -0
- package/dist/sessionCapture.js +279 -0
- package/dist/sessionCapture.js.map +1 -0
- package/dist/sessionCapture.test.d.ts +9 -0
- package/dist/sessionCapture.test.d.ts.map +1 -0
- package/dist/sessionCapture.test.js +366 -0
- package/dist/sessionCapture.test.js.map +1 -0
- package/dist/sessionCaptureScript.test.d.ts +16 -0
- package/dist/sessionCaptureScript.test.d.ts.map +1 -0
- package/dist/sessionCaptureScript.test.js +92 -0
- package/dist/sessionCaptureScript.test.js.map +1 -0
- package/dist/steering.test.d.ts +2 -0
- package/dist/steering.test.d.ts.map +1 -0
- package/dist/steering.test.js +223 -0
- package/dist/steering.test.js.map +1 -0
- package/dist/streamingRuntimeLib.d.ts +260 -1
- package/dist/streamingRuntimeLib.d.ts.map +1 -1
- package/dist/streamingRuntimeLib.js +474 -1
- package/dist/streamingRuntimeLib.js.map +1 -1
- package/dist/streamingRuntimeLib.test.js +87 -1
- package/dist/streamingRuntimeLib.test.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +8 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/sessionControl.d.ts +53 -0
- package/dist/tools/sessionControl.d.ts.map +1 -0
- package/dist/tools/sessionControl.js +265 -0
- package/dist/tools/sessionControl.js.map +1 -0
- package/dist/tools/sessionControl.test.d.ts +14 -0
- package/dist/tools/sessionControl.test.d.ts.map +1 -0
- package/dist/tools/sessionControl.test.js +298 -0
- package/dist/tools/sessionControl.test.js.map +1 -0
- package/dist/tools/tools.test.js +4 -2
- package/dist/tools/tools.test.js.map +1 -1
- package/native/agenthub-memvid/agenthub-memvid.linux-x64-gnu.node +0 -0
- package/package.json +2 -2
- package/skills/commands/start-session.md +77 -77
- package/skills/skills/deploy-staging/SKILL.md +164 -164
- package/skills/skills/deploy-vps-openclaw/SKILL.md +97 -97
- package/skills/skills/karpathy-guidelines/SKILL.md +67 -67
|
@@ -8,6 +8,21 @@
|
|
|
8
8
|
* Verified premise (VPS spike 2026-06-26, org brain #2209): the Claude Agent SDK
|
|
9
9
|
* streaming mode runs on the subscription (CLAUDE_CODE_OAUTH_TOKEN, no
|
|
10
10
|
* ANTHROPIC_API_KEY) and processes a mid-run yielded message in the same session.
|
|
11
|
+
*
|
|
12
|
+
* ---------------------------------------------------------------------------
|
|
13
|
+
* CANONICAL COPY — `employee-runtime/src/streamingRuntimeLib.ts` is vendored
|
|
14
|
+
* from this file and must stay **functionally** in sync (add-multiplayer-sessions
|
|
15
|
+
* §2.4). Change one, change the other in the same commit.
|
|
16
|
+
*
|
|
17
|
+
* Why two copies rather than an import: `employee-runtime` is a separate package
|
|
18
|
+
* precisely because the agent-sdk's peer deps conflict with this package's, so it
|
|
19
|
+
* cannot depend on `agenthub-multiagent-mcp`. The daemon side imports this copy
|
|
20
|
+
* (for the `streamingEnabled()` gate); the runtime imports the vendored one.
|
|
21
|
+
*
|
|
22
|
+
* The two headers differ, and the vendored copy carries fewer prose comments;
|
|
23
|
+
* everything below the header is expected to be identical. Keep it import-free —
|
|
24
|
+
* pure logic only — so neither copy drags a package-specific dependency across.
|
|
25
|
+
* ---------------------------------------------------------------------------
|
|
11
26
|
*/
|
|
12
27
|
// =============================================================================
|
|
13
28
|
// Kill switch + auth (§2.1 / §D3)
|
|
@@ -16,6 +31,22 @@
|
|
|
16
31
|
export function streamingEnabled(env = process.env) {
|
|
17
32
|
return /^(1|true|yes|on)$/i.test((env.AGENTHUB_EMPLOYEE_STREAMING_ENABLED || "").trim());
|
|
18
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Human steering kill switch (add-multiplayer-sessions §4.8). Default **off** —
|
|
36
|
+
* unset, empty, or unrecognised means disabled, and while it is off the producer
|
|
37
|
+
* side does no I/O at all: no transport is declared, nothing is polled, and the
|
|
38
|
+
* runtime loop is what it was before this feature existed.
|
|
39
|
+
*
|
|
40
|
+
* It lives here rather than in a runtime-only module because both producers
|
|
41
|
+
* consult it: the streaming runtime (which declares `turn_queue` and pulls
|
|
42
|
+
* steers) and the one-shot daemon (which declares `one_shot` so its shifts are
|
|
43
|
+
* refused explicitly rather than by accident of having declared nothing).
|
|
44
|
+
* Mirrors the server's `AGENTHUB_SESSION_STEER_ENABLED`; a producer that polled
|
|
45
|
+
* while the server had it off would earn a 503 on every tick.
|
|
46
|
+
*/
|
|
47
|
+
export function sessionSteerEnabled(env = process.env) {
|
|
48
|
+
return /^(1|true|yes|on)$/i.test((env.AGENTHUB_SESSION_STEER_ENABLED || "").trim());
|
|
49
|
+
}
|
|
19
50
|
/**
|
|
20
51
|
* §D3 — assert subscription auth and fail CLOSED against metered billing. The
|
|
21
52
|
* SDK subprocess inherits the env; if ANTHROPIC_API_KEY were set it would take
|
|
@@ -55,8 +86,26 @@ export function decideInbound(envelope, agentId, handled) {
|
|
|
55
86
|
return { yield: false, reason: "duplicate", message: m };
|
|
56
87
|
return { yield: true, message: m };
|
|
57
88
|
}
|
|
58
|
-
/**
|
|
89
|
+
/**
|
|
90
|
+
* The message type a human steer arrives as. Mirrors `sessions.SteerMessageType`
|
|
91
|
+
* on the server, which validates it on the way in — an unrecognised type would be
|
|
92
|
+
* persisted and then framed by whatever branch this function fell through to,
|
|
93
|
+
* which is the same silent-wrong-behaviour class as a dropped steer.
|
|
94
|
+
*/
|
|
95
|
+
export const STEER_MESSAGE_TYPE = "steer";
|
|
96
|
+
/**
|
|
97
|
+
* Render an AgentHub message as the text content of a session turn.
|
|
98
|
+
*
|
|
99
|
+
* Two framings, and the distinction is the whole point of §4.3. An inbox message
|
|
100
|
+
* is agent-to-agent: somebody is waiting on an answer, so the turn tells the
|
|
101
|
+
* model to answer with `mcp__agenthub__reply`. A steer is a human leaning over
|
|
102
|
+
* the session and redirecting the work: nobody is waiting on a message, and
|
|
103
|
+
* telling the model to reply would turn "stop rewriting that file" into a note
|
|
104
|
+
* written back to the person who typed it while the work carried on unchanged.
|
|
105
|
+
*/
|
|
59
106
|
export function buildTurnContent(m) {
|
|
107
|
+
if (m.type === STEER_MESSAGE_TYPE)
|
|
108
|
+
return buildSteerTurnContent(m);
|
|
60
109
|
const priority = m.priority && m.priority !== "normal" ? ` | Priority: ${m.priority}` : "";
|
|
61
110
|
return (`You received a message on AgentHub.\n` +
|
|
62
111
|
`From: ${m.from_agent}\n` +
|
|
@@ -66,6 +115,63 @@ export function buildTurnContent(m) {
|
|
|
66
115
|
`Reply with mcp__agenthub__reply(message_id="${m.id}", body="…"). ` +
|
|
67
116
|
`If this is outside your charter, escalate via send_message instead of guessing.`);
|
|
68
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Frame a steer as what it is: a direct instruction from a human operator
|
|
120
|
+
* watching this session (§4.3).
|
|
121
|
+
*
|
|
122
|
+
* Deliberately parallel to `buildInstructionTurnContent` in `workstation.ts`,
|
|
123
|
+
* which solves the same problem for a driven personal workspace. Both say the
|
|
124
|
+
* same three things, because all three are load-bearing: who this came from, that
|
|
125
|
+
* it is an instruction rather than correspondence, and that no reply is wanted.
|
|
126
|
+
* The refusal clause is here for the same reason it is there — a human can steer
|
|
127
|
+
* a session into something the agent should not do, and "say so and stop" is a
|
|
128
|
+
* better outcome than either silent compliance or silent refusal.
|
|
129
|
+
*/
|
|
130
|
+
export function buildSteerTurnContent(m) {
|
|
131
|
+
const from = m.from_agent && m.from_agent !== "" ? m.from_agent : "a human operator";
|
|
132
|
+
return (`A person is steering this session from the AgentHub dashboard.\n` +
|
|
133
|
+
`From: ${from}\n\n` +
|
|
134
|
+
`${m.body}\n\n` +
|
|
135
|
+
`This is a direct instruction from a human operator, not an agent-to-agent ` +
|
|
136
|
+
`message: change what you are doing to follow it and carry on with the work in ` +
|
|
137
|
+
`this session. Do NOT call mcp__agenthub__reply — nobody is waiting on a ` +
|
|
138
|
+
`message, and a reply would leave the work itself unchanged. If the instruction ` +
|
|
139
|
+
`is unsafe, or conflicts with your charter, say so in your next message and stop.`);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Parse a `session_steer` WebSocket frame (the channel transport's delivery
|
|
143
|
+
* path). Returns `null` for anything that is not a well-formed steer.
|
|
144
|
+
*
|
|
145
|
+
* Lives here rather than in the channel plugin because the plugin is an
|
|
146
|
+
* executable script — importing it to test it would start an MCP server — and
|
|
147
|
+
* because the frame is a wire contract shared with the server, which is the kind
|
|
148
|
+
* of thing that should have one parser and one test.
|
|
149
|
+
*
|
|
150
|
+
* A frame missing an id or text is `null` rather than a partially-populated
|
|
151
|
+
* steer: an instruction with no text delivers nothing, and one with no id cannot
|
|
152
|
+
* be acked, so "deliver it anyway" would produce exactly the accepted-and-dropped
|
|
153
|
+
* outcome the whole transport contract exists to rule out.
|
|
154
|
+
*/
|
|
155
|
+
export function parseSteerFrame(envelope) {
|
|
156
|
+
if (envelope === null || typeof envelope !== "object")
|
|
157
|
+
return null;
|
|
158
|
+
const e = envelope;
|
|
159
|
+
if (e.type !== "session_steer")
|
|
160
|
+
return null;
|
|
161
|
+
const data = e.data !== null && typeof e.data === "object" ? e.data : {};
|
|
162
|
+
const sessionId = typeof data.session_id === "string" ? data.session_id.trim() : "";
|
|
163
|
+
const steerId = typeof data.steer_id === "string" ? data.steer_id.trim() : "";
|
|
164
|
+
const text = typeof data.text === "string" ? data.text : "";
|
|
165
|
+
if (sessionId === "" || steerId === "" || text.trim() === "")
|
|
166
|
+
return null;
|
|
167
|
+
return {
|
|
168
|
+
sessionId,
|
|
169
|
+
steerId,
|
|
170
|
+
text,
|
|
171
|
+
createdBy: typeof data.created_by === "string" ? data.created_by : "",
|
|
172
|
+
messageType: typeof data.message_type === "string" && data.message_type !== "" ? data.message_type : STEER_MESSAGE_TYPE,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
69
175
|
export function toUserTurn(content, sessionId) {
|
|
70
176
|
return { type: "user", message: { role: "user", content }, parent_tool_use_id: null, session_id: sessionId };
|
|
71
177
|
}
|
|
@@ -152,4 +258,371 @@ export function assertWorkerIdentityReady(stateExists, agentId) {
|
|
|
152
258
|
`off; a missing token would hang the in-session MCP. Exiting for restart + re-heal.`);
|
|
153
259
|
}
|
|
154
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Client-side payload bounds. The server is authoritative for truncation and
|
|
263
|
+
* redaction (add-multiplayer-sessions §1.8/§1.9) and this code deliberately does
|
|
264
|
+
* **not** duplicate either. These bounds exist only so a single event is never
|
|
265
|
+
* megabytes on the wire; they mirror `session-capture.sh`'s TRUNC / TRUNC_SHORT
|
|
266
|
+
* so both producers put comparable volumes in front of the same server pipeline.
|
|
267
|
+
*/
|
|
268
|
+
export const PAYLOAD_MAX_CHARS = 8000;
|
|
269
|
+
export const PAYLOAD_SUMMARY_MAX_CHARS = 1000;
|
|
270
|
+
function asRecord(value) {
|
|
271
|
+
return value !== null && typeof value === "object" ? value : {};
|
|
272
|
+
}
|
|
273
|
+
/** JSON that never throws — a cyclic or exotic value degrades to its String form. */
|
|
274
|
+
function safeStringify(value) {
|
|
275
|
+
try {
|
|
276
|
+
return JSON.stringify(value) ?? String(value);
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
return String(value);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function truncate(value, limit) {
|
|
283
|
+
if (value === undefined || value === null)
|
|
284
|
+
return "";
|
|
285
|
+
const s = typeof value === "string" ? value : safeStringify(value);
|
|
286
|
+
return s.length > limit ? s.slice(0, limit) + "..." : s;
|
|
287
|
+
}
|
|
288
|
+
/** `tool_result.content` is a string, a block list, or (rarely) something else. */
|
|
289
|
+
function flattenToolResult(content) {
|
|
290
|
+
if (typeof content === "string")
|
|
291
|
+
return content;
|
|
292
|
+
if (Array.isArray(content)) {
|
|
293
|
+
return content
|
|
294
|
+
.map((block) => {
|
|
295
|
+
const b = asRecord(block);
|
|
296
|
+
return b.type === "text" ? String(b.text ?? "") : "";
|
|
297
|
+
})
|
|
298
|
+
.filter((s) => s !== "")
|
|
299
|
+
.join("\n");
|
|
300
|
+
}
|
|
301
|
+
return truncate(content, PAYLOAD_MAX_CHARS);
|
|
302
|
+
}
|
|
303
|
+
function sessionEvent(eventType, payload) {
|
|
304
|
+
return { event_type: eventType, payload: safeStringify(payload), token_count: 0 };
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Turn SDK stream messages into session events (§2.2).
|
|
308
|
+
*
|
|
309
|
+
* The event vocabulary is exactly the one `session-capture.sh` already produces
|
|
310
|
+
* (`assistant_message`, `command_exec`, `file_read`, `file_edit`, `file_write`,
|
|
311
|
+
* `tool_call`) so the dashboard renders hook-captured and runtime-captured
|
|
312
|
+
* sessions identically. No new event types are introduced here.
|
|
313
|
+
*
|
|
314
|
+
* One deliberate difference from the hook: the hook runs PostToolUse and so sees
|
|
315
|
+
* a tool's input and response together, emitting a single merged event. The
|
|
316
|
+
* stream delivers them as two separate messages (`tool_use` on the assistant
|
|
317
|
+
* message, `tool_result` on the following user message), so this emits two
|
|
318
|
+
* events. The class is stateful only to carry `tool_use_id → tool name` across
|
|
319
|
+
* that gap, so the result event can name the tool it belongs to. The map is
|
|
320
|
+
* bounded: a long session must not accumulate ids forever.
|
|
321
|
+
*/
|
|
322
|
+
export class SessionEventExtractor {
|
|
323
|
+
toolNames = new Map();
|
|
324
|
+
maxCorrelations;
|
|
325
|
+
constructor(maxCorrelations = 256) {
|
|
326
|
+
this.maxCorrelations = Math.max(1, maxCorrelations);
|
|
327
|
+
}
|
|
328
|
+
extract(message) {
|
|
329
|
+
const m = asRecord(message);
|
|
330
|
+
if (m.type === "assistant")
|
|
331
|
+
return this.fromAssistant(m);
|
|
332
|
+
if (m.type === "user")
|
|
333
|
+
return this.fromToolResults(m);
|
|
334
|
+
// `result` (usage/recycle), `system`, and every other message type produce no
|
|
335
|
+
// events. `result` in particular is handled by the runtime's existing usage
|
|
336
|
+
// branch and must stay that way — context recycling depends on it.
|
|
337
|
+
return [];
|
|
338
|
+
}
|
|
339
|
+
blocks(m) {
|
|
340
|
+
const content = asRecord(m.message).content;
|
|
341
|
+
return Array.isArray(content) ? content : [];
|
|
342
|
+
}
|
|
343
|
+
fromAssistant(m) {
|
|
344
|
+
const events = [];
|
|
345
|
+
for (const block of this.blocks(m)) {
|
|
346
|
+
const b = asRecord(block);
|
|
347
|
+
if (b.type === "text") {
|
|
348
|
+
const text = String(b.text ?? "");
|
|
349
|
+
if (text.trim() === "")
|
|
350
|
+
continue; // whitespace-only frames are noise
|
|
351
|
+
events.push(sessionEvent("assistant_message", { content: truncate(text, PAYLOAD_MAX_CHARS) }));
|
|
352
|
+
}
|
|
353
|
+
else if (b.type === "tool_use") {
|
|
354
|
+
const name = String(b.name ?? "");
|
|
355
|
+
const id = typeof b.id === "string" ? b.id : "";
|
|
356
|
+
if (id !== "")
|
|
357
|
+
this.rememberTool(id, name);
|
|
358
|
+
events.push(toolUseEvent(name, id, b.input));
|
|
359
|
+
}
|
|
360
|
+
// `thinking` / `redacted_thinking` are intentionally not captured: they are
|
|
361
|
+
// model-internal and watchers have no consent story for them.
|
|
362
|
+
}
|
|
363
|
+
return events;
|
|
364
|
+
}
|
|
365
|
+
fromToolResults(m) {
|
|
366
|
+
const events = [];
|
|
367
|
+
for (const block of this.blocks(m)) {
|
|
368
|
+
const b = asRecord(block);
|
|
369
|
+
if (b.type !== "tool_result")
|
|
370
|
+
continue;
|
|
371
|
+
const id = typeof b.tool_use_id === "string" ? b.tool_use_id : "";
|
|
372
|
+
const payload = {
|
|
373
|
+
tool_name: this.toolNames.get(id) ?? "",
|
|
374
|
+
tool_use_id: id,
|
|
375
|
+
result_summary: truncate(flattenToolResult(b.content), PAYLOAD_SUMMARY_MAX_CHARS),
|
|
376
|
+
};
|
|
377
|
+
if (b.is_error === true)
|
|
378
|
+
payload.is_error = true;
|
|
379
|
+
if (id !== "")
|
|
380
|
+
this.toolNames.delete(id);
|
|
381
|
+
events.push(sessionEvent("tool_call", payload));
|
|
382
|
+
}
|
|
383
|
+
return events;
|
|
384
|
+
}
|
|
385
|
+
rememberTool(id, name) {
|
|
386
|
+
if (this.toolNames.size >= this.maxCorrelations) {
|
|
387
|
+
// Map iteration is insertion-ordered, so this evicts the oldest pending id.
|
|
388
|
+
const oldest = this.toolNames.keys().next();
|
|
389
|
+
if (!oldest.done)
|
|
390
|
+
this.toolNames.delete(oldest.value);
|
|
391
|
+
}
|
|
392
|
+
this.toolNames.set(id, name);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Map one `tool_use` block onto the hook's per-tool event vocabulary. Field names
|
|
397
|
+
* match `session-capture.sh` exactly, because the dashboard reads those names
|
|
398
|
+
* (`command`, `file_path`, `old_string`/`new_string`, `content_preview`,
|
|
399
|
+
* `input_summary`) and renders nothing useful without them.
|
|
400
|
+
*/
|
|
401
|
+
function toolUseEvent(name, toolUseId, rawInput) {
|
|
402
|
+
const input = asRecord(rawInput);
|
|
403
|
+
const base = { tool_name: name, tool_use_id: toolUseId };
|
|
404
|
+
switch (name.toLowerCase()) {
|
|
405
|
+
case "bash":
|
|
406
|
+
return sessionEvent("command_exec", {
|
|
407
|
+
...base,
|
|
408
|
+
command: truncate(input.command, PAYLOAD_SUMMARY_MAX_CHARS),
|
|
409
|
+
});
|
|
410
|
+
case "read":
|
|
411
|
+
return sessionEvent("file_read", {
|
|
412
|
+
...base,
|
|
413
|
+
file_path: String(input.file_path ?? ""),
|
|
414
|
+
lines_read: typeof input.limit === "number" ? input.limit : 0,
|
|
415
|
+
});
|
|
416
|
+
case "edit":
|
|
417
|
+
return sessionEvent("file_edit", {
|
|
418
|
+
...base,
|
|
419
|
+
file_path: String(input.file_path ?? ""),
|
|
420
|
+
old_string: truncate(input.old_string, PAYLOAD_SUMMARY_MAX_CHARS),
|
|
421
|
+
new_string: truncate(input.new_string, PAYLOAD_SUMMARY_MAX_CHARS),
|
|
422
|
+
});
|
|
423
|
+
case "write":
|
|
424
|
+
return sessionEvent("file_write", {
|
|
425
|
+
...base,
|
|
426
|
+
file_path: String(input.file_path ?? ""),
|
|
427
|
+
content_preview: truncate(input.content, PAYLOAD_SUMMARY_MAX_CHARS),
|
|
428
|
+
});
|
|
429
|
+
case "glob":
|
|
430
|
+
case "grep":
|
|
431
|
+
return sessionEvent("tool_call", {
|
|
432
|
+
...base,
|
|
433
|
+
pattern: String(input.pattern ?? ""),
|
|
434
|
+
path: String(input.path ?? ""),
|
|
435
|
+
});
|
|
436
|
+
default:
|
|
437
|
+
return sessionEvent("tool_call", {
|
|
438
|
+
...base,
|
|
439
|
+
input_summary: truncate(input, PAYLOAD_SUMMARY_MAX_CHARS),
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Batches session events and owns the captured session's lifecycle (§2.3).
|
|
445
|
+
*
|
|
446
|
+
* Why batching: a chatty turn emits an event per text block and per tool call.
|
|
447
|
+
* One HTTP request each would put the runtime's throughput at the mercy of
|
|
448
|
+
* network latency and would trip the server's per-session rate cap on volume
|
|
449
|
+
* that is really one turn. Events are flushed when `maxBatch` accumulates, when
|
|
450
|
+
* `flushIntervalMs` elapses, and unconditionally at turn end and session end, so
|
|
451
|
+
* the tail of a conversation is never left sitting in the buffer.
|
|
452
|
+
*
|
|
453
|
+
* Why lazy open: the session is created on the first event rather than at
|
|
454
|
+
* construction, so an idle employee (the common case — the runtime blocks on the
|
|
455
|
+
* turn queue) creates nothing, and a runtime that crash-loops before producing
|
|
456
|
+
* output does not create a session per retry.
|
|
457
|
+
*
|
|
458
|
+
* Everything here is fail-soft, matching `sessionCapture.ts`: a failed create
|
|
459
|
+
* degrades the whole session to uncaptured (`failed` state, no retry storm), a
|
|
460
|
+
* failed post drops that batch, and no method ever throws. Capture is
|
|
461
|
+
* observability; it must never take down real work.
|
|
462
|
+
*/
|
|
463
|
+
export class SessionEventEmitter {
|
|
464
|
+
transport;
|
|
465
|
+
enabled;
|
|
466
|
+
maxBatch;
|
|
467
|
+
flushIntervalMs;
|
|
468
|
+
maxPending;
|
|
469
|
+
onOpen;
|
|
470
|
+
log;
|
|
471
|
+
pending = [];
|
|
472
|
+
state = "idle";
|
|
473
|
+
openSessionId = null;
|
|
474
|
+
timer = null;
|
|
475
|
+
chain = Promise.resolve();
|
|
476
|
+
dropped = 0;
|
|
477
|
+
dropWarned = false;
|
|
478
|
+
constructor(opts) {
|
|
479
|
+
this.transport = opts.transport;
|
|
480
|
+
this.enabled = opts.enabled;
|
|
481
|
+
this.maxBatch = Math.max(1, opts.maxBatch ?? 25);
|
|
482
|
+
this.flushIntervalMs = Math.max(1, opts.flushIntervalMs ?? 1000);
|
|
483
|
+
this.maxPending = Math.max(1, opts.maxPending ?? 1000);
|
|
484
|
+
this.onOpen = opts.onOpen;
|
|
485
|
+
this.log = opts.log ?? (() => { });
|
|
486
|
+
}
|
|
487
|
+
/** The session events are being recorded under, or `null` before/after capture. */
|
|
488
|
+
get currentSessionId() {
|
|
489
|
+
return this.openSessionId;
|
|
490
|
+
}
|
|
491
|
+
get pendingCount() {
|
|
492
|
+
return this.pending.length;
|
|
493
|
+
}
|
|
494
|
+
get droppedCount() {
|
|
495
|
+
return this.dropped;
|
|
496
|
+
}
|
|
497
|
+
/** Queue events. Synchronous and non-throwing — safe to call from the stream loop. */
|
|
498
|
+
emit(events) {
|
|
499
|
+
if (!this.enabled || events.length === 0)
|
|
500
|
+
return;
|
|
501
|
+
if (this.state === "failed" || this.state === "closed")
|
|
502
|
+
return;
|
|
503
|
+
for (const ev of events) {
|
|
504
|
+
if (this.pending.length >= this.maxPending) {
|
|
505
|
+
this.dropped++;
|
|
506
|
+
if (!this.dropWarned) {
|
|
507
|
+
this.dropWarned = true;
|
|
508
|
+
this.log(`session capture: event backlog exceeded ${this.maxPending} — dropping events ` +
|
|
509
|
+
`(the shift continues; capture is lossy under backpressure by design)`);
|
|
510
|
+
}
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
this.pending.push(ev);
|
|
514
|
+
}
|
|
515
|
+
if (this.pending.length >= this.maxBatch) {
|
|
516
|
+
void this.drain();
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
this.schedule();
|
|
520
|
+
}
|
|
521
|
+
/** Send everything pending now. Awaits the in-flight post; never throws. */
|
|
522
|
+
async flush() {
|
|
523
|
+
this.clearTimer();
|
|
524
|
+
await this.drain();
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Flush the tail and close the captured session. After this the emitter is
|
|
528
|
+
* inert — a late `emit` from a straggling stream message is discarded rather
|
|
529
|
+
* than reopening a session nobody is watching.
|
|
530
|
+
*/
|
|
531
|
+
async end(reason) {
|
|
532
|
+
if (!this.enabled || this.state === "closed")
|
|
533
|
+
return;
|
|
534
|
+
await this.flush();
|
|
535
|
+
const sessionId = this.openSessionId;
|
|
536
|
+
this.state = "closed";
|
|
537
|
+
this.openSessionId = null;
|
|
538
|
+
this.clearTimer();
|
|
539
|
+
if (sessionId !== null) {
|
|
540
|
+
try {
|
|
541
|
+
await this.transport.close(sessionId, reason);
|
|
542
|
+
}
|
|
543
|
+
catch (e) {
|
|
544
|
+
this.log(`session capture: terminate failed (${errText(e)}) — session left open server-side`);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (this.dropped > 0) {
|
|
548
|
+
this.log(`session capture: ${this.dropped} event(s) dropped under backpressure this session`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
schedule() {
|
|
552
|
+
if (this.timer !== null)
|
|
553
|
+
return;
|
|
554
|
+
this.timer = setTimeout(() => {
|
|
555
|
+
this.timer = null;
|
|
556
|
+
void this.drain();
|
|
557
|
+
}, this.flushIntervalMs);
|
|
558
|
+
// Metrics/observability must never keep the process alive.
|
|
559
|
+
this.timer.unref?.();
|
|
560
|
+
}
|
|
561
|
+
clearTimer() {
|
|
562
|
+
if (this.timer !== null) {
|
|
563
|
+
clearTimeout(this.timer);
|
|
564
|
+
this.timer = null;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
/** Serialized: posts must not race, or the server assigns sequence numbers out of order. */
|
|
568
|
+
drain() {
|
|
569
|
+
// The .catch is load-bearing: without it one rejection poisons `chain`
|
|
570
|
+
// forever, and every later `void this.drain()` becomes an unhandled
|
|
571
|
+
// rejection in a process that runs for weeks. drainOnce is written not to
|
|
572
|
+
// throw; this is the belt to that braces.
|
|
573
|
+
this.chain = this.chain
|
|
574
|
+
.then(() => this.drainOnce())
|
|
575
|
+
.catch((e) => this.log(`session capture: drain failed (${errText(e)}) — capture continues`));
|
|
576
|
+
return this.chain;
|
|
577
|
+
}
|
|
578
|
+
async drainOnce() {
|
|
579
|
+
if (this.pending.length === 0)
|
|
580
|
+
return;
|
|
581
|
+
if (!this.enabled || this.state === "failed" || this.state === "closed") {
|
|
582
|
+
this.pending = [];
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (this.state === "idle") {
|
|
586
|
+
let sessionId = null;
|
|
587
|
+
try {
|
|
588
|
+
sessionId = await this.transport.open();
|
|
589
|
+
}
|
|
590
|
+
catch (e) {
|
|
591
|
+
this.log(`session capture: create threw (${errText(e)}) — shift runs without capture`);
|
|
592
|
+
}
|
|
593
|
+
if (sessionId === null || sessionId === "") {
|
|
594
|
+
// One attempt only. Retrying per batch would hammer a server that is
|
|
595
|
+
// already failing, for an observability feature.
|
|
596
|
+
this.state = "failed";
|
|
597
|
+
this.pending = [];
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
this.openSessionId = sessionId;
|
|
601
|
+
this.state = "open";
|
|
602
|
+
// Fires exactly once per session, before the first batch is posted. The
|
|
603
|
+
// try/catch is not defensive habit: the listener is steering, and a broken
|
|
604
|
+
// steering path must cost the shift its steerability, never its capture.
|
|
605
|
+
try {
|
|
606
|
+
this.onOpen?.(sessionId);
|
|
607
|
+
}
|
|
608
|
+
catch (e) {
|
|
609
|
+
this.log(`session capture: onOpen listener threw (${errText(e)}) — capture continues`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
const batch = this.pending;
|
|
613
|
+
this.pending = [];
|
|
614
|
+
try {
|
|
615
|
+
await this.transport.post(this.openSessionId, batch);
|
|
616
|
+
}
|
|
617
|
+
catch (e) {
|
|
618
|
+
// Dropped, not requeued: a retry queue behind a down server grows without
|
|
619
|
+
// bound in a process that runs for weeks.
|
|
620
|
+
this.log(`session capture: posting ${batch.length} event(s) failed (${errText(e)}) — batch dropped`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
/** Same shape as `sessionCapture.ts`'s internal helper — errors are logged, never thrown. */
|
|
625
|
+
function errText(e) {
|
|
626
|
+
return e instanceof Error ? e.message : String(e);
|
|
627
|
+
}
|
|
155
628
|
//# sourceMappingURL=streamingRuntimeLib.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamingRuntimeLib.js","sourceRoot":"","sources":["../src/streamingRuntimeLib.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"streamingRuntimeLib.js","sourceRoot":"","sources":["../src/streamingRuntimeLib.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { streamingEnabled, assertSubscriptionAuth, decideInbound, buildTurnContent, toUserTurn, shouldRecycle, defaultRecycleOptions, newRuntimeStats, recordResultUsage, formatUsageLine, inSessionMcpEnv, assertWorkerIdentityReady, } from "./streamingRuntimeLib.js";
|
|
2
|
+
import { streamingEnabled, assertSubscriptionAuth, decideInbound, buildTurnContent, toUserTurn, shouldRecycle, defaultRecycleOptions, newRuntimeStats, recordResultUsage, formatUsageLine, inSessionMcpEnv, assertWorkerIdentityReady, SessionEventExtractor, SessionEventEmitter, } from "./streamingRuntimeLib.js";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
3
6
|
// add-employee-streaming-runtime §2 — pure-helper tests.
|
|
4
7
|
describe("streamingEnabled (kill switch §2.1)", () => {
|
|
5
8
|
it("off by default / falsey", () => {
|
|
@@ -144,4 +147,87 @@ describe("assertWorkerIdentityReady (§3 fail-fast identity guard)", () => {
|
|
|
144
147
|
expect(() => assertWorkerIdentityReady(true, "eng-ic-test")).not.toThrow();
|
|
145
148
|
});
|
|
146
149
|
});
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// add-multiplayer-sessions §2 — session events.
|
|
152
|
+
//
|
|
153
|
+
// The behavioural suite for the extractor and emitter lives beside the runtime
|
|
154
|
+
// that uses them (employee-runtime/src/sessionEvents.test.ts). What matters here
|
|
155
|
+
// is that the canonical copy exposes the same API and the same behaviour, and
|
|
156
|
+
// that the two copies have not drifted (§2.4).
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
describe("SessionEventExtractor (canonical copy parity)", () => {
|
|
159
|
+
it("maps assistant text, tool_use, and tool_result onto the hook's vocabulary", () => {
|
|
160
|
+
const x = new SessionEventExtractor();
|
|
161
|
+
expect(x.extract({ type: "assistant", message: { content: [{ type: "text", text: "hi" }] } })[0].event_type).toBe("assistant_message");
|
|
162
|
+
expect(x.extract({
|
|
163
|
+
type: "assistant",
|
|
164
|
+
message: { content: [{ type: "tool_use", id: "t1", name: "Bash", input: { command: "ls" } }] },
|
|
165
|
+
})[0].event_type).toBe("command_exec");
|
|
166
|
+
const result = x.extract({
|
|
167
|
+
type: "user",
|
|
168
|
+
message: { content: [{ type: "tool_result", tool_use_id: "t1", content: "a.ts" }] },
|
|
169
|
+
})[0];
|
|
170
|
+
expect(result.event_type).toBe("tool_call");
|
|
171
|
+
expect(JSON.parse(result.payload)).toMatchObject({ tool_name: "Bash", result_summary: "a.ts" });
|
|
172
|
+
});
|
|
173
|
+
it("emits nothing for a result message — usage/recycle handling owns it", () => {
|
|
174
|
+
expect(new SessionEventExtractor().extract({ type: "result", usage: { input_tokens: 9 } })).toEqual([]);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe("SessionEventEmitter (canonical copy parity)", () => {
|
|
178
|
+
const event = { event_type: "assistant_message", payload: "{}", token_count: 0 };
|
|
179
|
+
it("batches, then flushes the tail and closes on end()", async () => {
|
|
180
|
+
const posts = [];
|
|
181
|
+
const closes = [];
|
|
182
|
+
const emitter = new SessionEventEmitter({
|
|
183
|
+
enabled: true,
|
|
184
|
+
maxBatch: 100,
|
|
185
|
+
transport: {
|
|
186
|
+
open: async () => "sess-1",
|
|
187
|
+
post: async (_id, events) => void posts.push(events.length),
|
|
188
|
+
close: async (_id, reason) => void closes.push(reason),
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
for (let i = 0; i < 5; i++)
|
|
192
|
+
emitter.emit([event]);
|
|
193
|
+
expect(posts).toEqual([]);
|
|
194
|
+
await emitter.end("completed");
|
|
195
|
+
expect(posts).toEqual([5]);
|
|
196
|
+
expect(closes).toEqual(["completed"]);
|
|
197
|
+
});
|
|
198
|
+
it("is a hard no-op when capture is disabled", async () => {
|
|
199
|
+
let opened = 0;
|
|
200
|
+
const emitter = new SessionEventEmitter({
|
|
201
|
+
enabled: false,
|
|
202
|
+
transport: {
|
|
203
|
+
open: async () => {
|
|
204
|
+
opened++;
|
|
205
|
+
return "sess-1";
|
|
206
|
+
},
|
|
207
|
+
post: async () => { },
|
|
208
|
+
close: async () => { },
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
emitter.emit([event]);
|
|
212
|
+
await emitter.end("completed");
|
|
213
|
+
expect(opened).toBe(0);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
describe("vendored copy stays in sync (§2.4)", () => {
|
|
217
|
+
// Comments and the file header are allowed to differ between the two copies;
|
|
218
|
+
// code is not. Strip comments and whitespace, then require an exact match.
|
|
219
|
+
const strip = (src) => src
|
|
220
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
221
|
+
.replace(/(^|\s)\/\/[^\n]*/g, "$1")
|
|
222
|
+
.split("\n")
|
|
223
|
+
.map((l) => l.trim())
|
|
224
|
+
.filter((l) => l !== "")
|
|
225
|
+
.join("\n");
|
|
226
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
227
|
+
const canonical = path.join(here, "streamingRuntimeLib.ts");
|
|
228
|
+
const vendored = path.join(here, "..", "..", "employee-runtime", "src", "streamingRuntimeLib.ts");
|
|
229
|
+
it.skipIf(!fs.existsSync(vendored))("employee-runtime/src/streamingRuntimeLib.ts is code-identical to this one", () => {
|
|
230
|
+
expect(strip(fs.readFileSync(vendored, "utf8"))).toBe(strip(fs.readFileSync(canonical, "utf8")));
|
|
231
|
+
});
|
|
232
|
+
});
|
|
147
233
|
//# sourceMappingURL=streamingRuntimeLib.test.js.map
|