@workerdeck/core 2.13.0 → 2.14.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/build/index.d.mts +128 -6
- package/build/index.mjs +550 -79
- package/build/index.mjs.map +1 -1
- package/package.json +3 -3
package/build/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { createHash, randomUUID } from "node:crypto";
|
|
3
3
|
import { createSdkMcpServer, getSessionInfo, getSessionMessages, listSessions, query, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
4
|
-
import { CHECKLIST_TEXT_MAX, ENGINE_CAPABILITIES, PEER_MENTION_MAX, PROTOCOL_VERSION, SUBAGENT_HISTORY, TOOL_RESULT_HEAD_CHARS, addTokenUsage, byModelTotalTokens, canonicalModel, contextReading, costOfByModel, emptyTokenUsage, imagePartRef, mergeByModel, parseTodoWriteInput, peerDeliveredPrefix, replayCoalesceKey, replayRetains, sanitizeToolTitle, snapshotRetains, tokenUsageFromWire, totalTokens, transcriptActivity, transcriptContent, transcriptProse } from "@workerdeck/protocol";
|
|
4
|
+
import { CHECKLIST_TEXT_MAX, ENGINE_CAPABILITIES, PEER_MENTION_MAX, PROTOCOL_VERSION, SHELL_CONTEXT_FLUSH_MAX_CHARS, SHELL_CONTEXT_HEAD_CHARS, SHELL_CONTEXT_HEAD_LINES, SHELL_CONTEXT_TAIL_CHARS, SHELL_CONTEXT_TAIL_LINES, SHELL_INLINE_CHARS, SHELL_INLINE_LINES, SHELL_INLINE_LINE_CHARS, SHELL_READ_DEFAULT_LINES, SHELL_READ_MAX_LINES, SUBAGENT_HISTORY, TOOL_RESULT_HEAD_CHARS, addTokenUsage, byModelTotalTokens, canonicalModel, contextReading, costOfByModel, emptyTokenUsage, imagePartRef, logCoalesceKey, mergeByModel, parseTodoWriteInput, peerDeliveredPrefix, replayCoalesceKey, replayRetains, sanitizeToolTitle, snapshotRetains, tokenUsageFromWire, totalTokens, transcriptActivity, transcriptContent, transcriptProse } from "@workerdeck/protocol";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { ToolLoopAgent, generateText, isStepCount, tool as tool$1 } from "ai";
|
|
7
7
|
import { execFile, spawn } from "node:child_process";
|
|
@@ -275,7 +275,110 @@ var InputQueue = class {
|
|
|
275
275
|
}
|
|
276
276
|
};
|
|
277
277
|
//#endregion
|
|
278
|
+
//#region src/lib/tty-text.ts
|
|
279
|
+
const OSC_SEQUENCE = /(?:\x1b\]|\x9d)[\s\S]*?(?:\x07|\x1b\\|\x9c)/g;
|
|
280
|
+
const STRING_SEQUENCE = /(?:\x1b[PX^_]|[\x90\x98\x9e\x9f])[\s\S]*?(?:\x1b\\|\x9c)/g;
|
|
281
|
+
const CSI_SEQUENCE = /(?:\x1b\[|\x9b)[0-?]*[ -/]*[@-~]/g;
|
|
282
|
+
const ESCAPE_SEQUENCE = /\x1b[ -/]*[0-~]/g;
|
|
283
|
+
const CONTROL_CHARS = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/g;
|
|
284
|
+
function ttyText(raw) {
|
|
285
|
+
return raw.replace(OSC_SEQUENCE, "").replace(STRING_SEQUENCE, "").replace(CSI_SEQUENCE, "").replace(ESCAPE_SEQUENCE, "").replace(CONTROL_CHARS, "").replace(/\r\n/g, "\n").split("\n").map((line) => overwritten(line).trimEnd()).join("\n");
|
|
286
|
+
}
|
|
287
|
+
function splitLines(text) {
|
|
288
|
+
if (text === "") return [];
|
|
289
|
+
return (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n");
|
|
290
|
+
}
|
|
291
|
+
function countLines(text) {
|
|
292
|
+
if (text === "") return 0;
|
|
293
|
+
let count = 1;
|
|
294
|
+
for (let at = text.indexOf("\n"); at !== -1; at = text.indexOf("\n", at + 1)) count++;
|
|
295
|
+
return text.endsWith("\n") ? count - 1 : count;
|
|
296
|
+
}
|
|
297
|
+
function headTail(text, opts) {
|
|
298
|
+
const lines = splitLines(text);
|
|
299
|
+
const totalLines = lines.length;
|
|
300
|
+
const totalBytes = Buffer.byteLength(text, "utf8");
|
|
301
|
+
const body = lines.join("\n");
|
|
302
|
+
if (totalLines <= opts.headLines + opts.tailLines && body.length <= opts.headChars + opts.tailChars) return {
|
|
303
|
+
head: body,
|
|
304
|
+
tail: "",
|
|
305
|
+
omittedLines: 0,
|
|
306
|
+
totalLines,
|
|
307
|
+
totalBytes
|
|
308
|
+
};
|
|
309
|
+
const tail = takeTail(lines, Math.min(opts.tailLines, totalLines), opts.tailChars);
|
|
310
|
+
const head = takeHead(lines, Math.min(opts.headLines, totalLines - tail.lines), opts.headChars);
|
|
311
|
+
return {
|
|
312
|
+
head: head.text,
|
|
313
|
+
tail: tail.text,
|
|
314
|
+
omittedLines: totalLines - head.lines - tail.lines,
|
|
315
|
+
totalLines,
|
|
316
|
+
totalBytes
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function overwritten(line) {
|
|
320
|
+
if (!line.includes("\r")) return line;
|
|
321
|
+
const segments = line.split("\r");
|
|
322
|
+
for (let i = segments.length - 1; i >= 0; i--) if (segments[i] !== "") return segments[i];
|
|
323
|
+
return "";
|
|
324
|
+
}
|
|
325
|
+
function takeHead(lines, maxLines, maxChars) {
|
|
326
|
+
let taken = 0;
|
|
327
|
+
let length = 0;
|
|
328
|
+
while (taken < maxLines) {
|
|
329
|
+
const next = length + lines[taken].length + (taken > 0 ? 1 : 0);
|
|
330
|
+
if (next > maxChars) break;
|
|
331
|
+
length = next;
|
|
332
|
+
taken++;
|
|
333
|
+
}
|
|
334
|
+
if (taken === 0) return {
|
|
335
|
+
text: maxLines > 0 ? lines[0].slice(0, maxChars) : "",
|
|
336
|
+
lines: 0
|
|
337
|
+
};
|
|
338
|
+
return {
|
|
339
|
+
text: lines.slice(0, taken).join("\n"),
|
|
340
|
+
lines: taken
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function takeTail(lines, maxLines, maxChars) {
|
|
344
|
+
let taken = 0;
|
|
345
|
+
let length = 0;
|
|
346
|
+
while (taken < maxLines) {
|
|
347
|
+
const next = length + lines[lines.length - 1 - taken].length + (taken > 0 ? 1 : 0);
|
|
348
|
+
if (next > maxChars) break;
|
|
349
|
+
length = next;
|
|
350
|
+
taken++;
|
|
351
|
+
}
|
|
352
|
+
if (taken === 0) {
|
|
353
|
+
const last = lines[lines.length - 1];
|
|
354
|
+
return {
|
|
355
|
+
text: maxLines > 0 && maxChars > 0 ? last.slice(-maxChars) : "",
|
|
356
|
+
lines: 0
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
text: lines.slice(lines.length - taken).join("\n"),
|
|
361
|
+
lines: taken
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
//#endregion
|
|
278
365
|
//#region src/lib/local-command.ts
|
|
366
|
+
const OUTPUT_POINTER = "the full output is in the transcript";
|
|
367
|
+
const INLINE_MORE = "[... more output ...]";
|
|
368
|
+
const INLINE_CLIPPED = " [...]";
|
|
369
|
+
const INLINE_SCAN_CHARS = SHELL_INLINE_LINES * (SHELL_INLINE_LINE_CHARS + 1);
|
|
370
|
+
const TAIL_ONLY = {
|
|
371
|
+
headLines: 0,
|
|
372
|
+
headChars: 0,
|
|
373
|
+
tailLines: SHELL_CONTEXT_TAIL_LINES,
|
|
374
|
+
tailChars: SHELL_CONTEXT_TAIL_CHARS
|
|
375
|
+
};
|
|
376
|
+
const HEAD_AND_TAIL = {
|
|
377
|
+
headLines: SHELL_CONTEXT_HEAD_LINES,
|
|
378
|
+
headChars: SHELL_CONTEXT_HEAD_CHARS,
|
|
379
|
+
tailLines: SHELL_CONTEXT_TAIL_LINES,
|
|
380
|
+
tailChars: SHELL_CONTEXT_TAIL_CHARS
|
|
381
|
+
};
|
|
279
382
|
const LOCAL_COMMAND_CAVEAT = "<local-command-caveat>Caveat: the messages below were generated by the user while running local commands. DO NOT respond to these messages or otherwise consider them in your response unless the user explicitly asks you to.</local-command-caveat>";
|
|
280
383
|
function localCommandTranscript(result) {
|
|
281
384
|
const tag = result.exitCode === 0 ? "local-command-stdout" : "local-command-stderr";
|
|
@@ -294,6 +397,222 @@ function localCommandContext(pending) {
|
|
|
294
397
|
function isSlashCommand(text) {
|
|
295
398
|
return /^\s*\/[A-Za-z]/.test(text);
|
|
296
399
|
}
|
|
400
|
+
function isLocalShellSource(input) {
|
|
401
|
+
return typeof input.subscribe === "function";
|
|
402
|
+
}
|
|
403
|
+
function localCommandEvent(text, uuid, shell) {
|
|
404
|
+
return {
|
|
405
|
+
type: "user_message",
|
|
406
|
+
message: {
|
|
407
|
+
role: "user",
|
|
408
|
+
content: text
|
|
409
|
+
},
|
|
410
|
+
parentToolUseId: null,
|
|
411
|
+
synthetic: true,
|
|
412
|
+
uuid,
|
|
413
|
+
...shell ? { shell } : {}
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
function shellInlineText(shell, text) {
|
|
417
|
+
const head = inlineHead(text);
|
|
418
|
+
const body = [`$ ${shell.command}`, ...head.lines];
|
|
419
|
+
if (head.more) body.push(INLINE_MORE);
|
|
420
|
+
const end = shellEndLine(shell);
|
|
421
|
+
if (end) body.push(end);
|
|
422
|
+
return frame(shellTag(shell), body);
|
|
423
|
+
}
|
|
424
|
+
function shellContextText(shell, text, options = {}) {
|
|
425
|
+
const body = [`$ ${shell.command}`];
|
|
426
|
+
if (shell.status === "running") {
|
|
427
|
+
body.push(runningStatusLine(shell, text, options));
|
|
428
|
+
if (!options.collapsed && !options.previous) {
|
|
429
|
+
const part = headTail(text, TAIL_ONLY);
|
|
430
|
+
const shown = part.omittedLines > 0 ? part.tail : part.head;
|
|
431
|
+
if (part.omittedLines > 0) body.push(`[last ${count(part.totalLines - part.omittedLines)} of ${count(part.totalLines)} lines]`);
|
|
432
|
+
if (shown) body.push(shown);
|
|
433
|
+
}
|
|
434
|
+
} else if (options.collapsed) {
|
|
435
|
+
const part = headTail(text, {
|
|
436
|
+
headLines: 0,
|
|
437
|
+
headChars: 0,
|
|
438
|
+
tailLines: 0,
|
|
439
|
+
tailChars: 0
|
|
440
|
+
});
|
|
441
|
+
if (part.totalLines > 0) body.push(omissionLine(shell, part.totalLines, part.totalLines, part.totalBytes));
|
|
442
|
+
} else {
|
|
443
|
+
const part = headTail(text, HEAD_AND_TAIL);
|
|
444
|
+
if (part.head) body.push(part.head);
|
|
445
|
+
if (part.omittedLines > 0) body.push(omissionLine(shell, part.omittedLines, part.totalLines, part.totalBytes));
|
|
446
|
+
if (part.tail) body.push(part.tail);
|
|
447
|
+
}
|
|
448
|
+
const end = shellEndLine(shell);
|
|
449
|
+
if (end) body.push(end);
|
|
450
|
+
return frame(shellTag(shell), body);
|
|
451
|
+
}
|
|
452
|
+
var LocalCommandQueue = class {
|
|
453
|
+
#entries = [];
|
|
454
|
+
#emit;
|
|
455
|
+
constructor(emit) {
|
|
456
|
+
this.#emit = emit;
|
|
457
|
+
}
|
|
458
|
+
push(input) {
|
|
459
|
+
if (!isLocalShellSource(input)) {
|
|
460
|
+
const text = localCommandTranscript(input);
|
|
461
|
+
this.#entries.push({
|
|
462
|
+
kind: "text",
|
|
463
|
+
text
|
|
464
|
+
});
|
|
465
|
+
this.#emit(text, randomUUID(), void 0);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const uuid = randomUUID();
|
|
469
|
+
const row = () => {
|
|
470
|
+
const info = { ...input.info() };
|
|
471
|
+
this.#emit(shellInlineText(info, input.text()), uuid, info);
|
|
472
|
+
};
|
|
473
|
+
row();
|
|
474
|
+
const unsubscribe = input.subscribe(row);
|
|
475
|
+
this.#entries.push({
|
|
476
|
+
kind: "shell",
|
|
477
|
+
source: input,
|
|
478
|
+
unsubscribe
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
take() {
|
|
482
|
+
if (this.#entries.length === 0) return;
|
|
483
|
+
const rendered = this.#entries.map((entry) => render(entry));
|
|
484
|
+
const texts = capped(rendered);
|
|
485
|
+
const remaining = [];
|
|
486
|
+
this.#entries.forEach((entry, i) => {
|
|
487
|
+
if (entry.kind === "text") return;
|
|
488
|
+
if (rendered[i].settled) {
|
|
489
|
+
entry.unsubscribe();
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
entry.flushedLines = rendered[i].lines;
|
|
493
|
+
remaining.push(entry);
|
|
494
|
+
});
|
|
495
|
+
this.#entries = remaining;
|
|
496
|
+
return localCommandContext(texts);
|
|
497
|
+
}
|
|
498
|
+
materialize() {
|
|
499
|
+
return capped(this.#entries.map((entry) => render(entry)));
|
|
500
|
+
}
|
|
501
|
+
restore(texts) {
|
|
502
|
+
for (const text of texts) this.#entries.push({
|
|
503
|
+
kind: "text",
|
|
504
|
+
text
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
clear() {
|
|
508
|
+
for (const entry of this.#entries) if (entry.kind === "shell") entry.unsubscribe();
|
|
509
|
+
this.#entries = [];
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
function render(entry) {
|
|
513
|
+
if (entry.kind === "text") return {
|
|
514
|
+
full: entry.text,
|
|
515
|
+
settled: true,
|
|
516
|
+
lines: 0
|
|
517
|
+
};
|
|
518
|
+
const info = entry.source.info();
|
|
519
|
+
const text = entry.source.text();
|
|
520
|
+
const previous = entry.flushedLines === void 0 ? void 0 : { lines: entry.flushedLines };
|
|
521
|
+
return {
|
|
522
|
+
full: shellContextText(info, text, { previous }),
|
|
523
|
+
collapsed: () => shellContextText(info, text, {
|
|
524
|
+
previous,
|
|
525
|
+
collapsed: true
|
|
526
|
+
}),
|
|
527
|
+
settled: info.status !== "running",
|
|
528
|
+
lines: countLines(text)
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
function capped(rendered) {
|
|
532
|
+
const texts = Array.from({ length: rendered.length });
|
|
533
|
+
let budget = SHELL_CONTEXT_FLUSH_MAX_CHARS;
|
|
534
|
+
for (let i = rendered.length - 1; i >= 0; i--) {
|
|
535
|
+
const entry = rendered[i];
|
|
536
|
+
const text = entry.collapsed === void 0 || entry.full.length <= budget ? entry.full : entry.collapsed();
|
|
537
|
+
texts[i] = text;
|
|
538
|
+
budget -= text.length;
|
|
539
|
+
}
|
|
540
|
+
return texts;
|
|
541
|
+
}
|
|
542
|
+
function frame(tag, body) {
|
|
543
|
+
return `<${tag}>${body.join("\n")}</${tag}>`;
|
|
544
|
+
}
|
|
545
|
+
function inlineHead(text) {
|
|
546
|
+
const total = text.length;
|
|
547
|
+
const window = total > INLINE_SCAN_CHARS ? text.slice(0, INLINE_SCAN_CHARS) : text;
|
|
548
|
+
const lines = [];
|
|
549
|
+
let at = 0;
|
|
550
|
+
let chars = 0;
|
|
551
|
+
while (lines.length < SHELL_INLINE_LINES && at < total) {
|
|
552
|
+
const segment = window.slice(at, at + SHELL_INLINE_LINE_CHARS + 1);
|
|
553
|
+
const newline = segment.indexOf("\n");
|
|
554
|
+
const clipped = newline === -1 && segment.length > SHELL_INLINE_LINE_CHARS;
|
|
555
|
+
const line = clipped ? segment.slice(0, SHELL_INLINE_LINE_CHARS) + INLINE_CLIPPED : newline === -1 ? segment : segment.slice(0, newline);
|
|
556
|
+
const next = chars + line.length + (lines.length > 0 ? 1 : 0);
|
|
557
|
+
if (lines.length > 0 && next > SHELL_INLINE_CHARS) return {
|
|
558
|
+
lines,
|
|
559
|
+
more: true
|
|
560
|
+
};
|
|
561
|
+
lines.push(line);
|
|
562
|
+
chars = next;
|
|
563
|
+
if (clipped) return {
|
|
564
|
+
lines,
|
|
565
|
+
more: true
|
|
566
|
+
};
|
|
567
|
+
at = newline === -1 ? total : at + newline + 1;
|
|
568
|
+
}
|
|
569
|
+
return {
|
|
570
|
+
lines,
|
|
571
|
+
more: at < total
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
function shellTag(shell) {
|
|
575
|
+
return shell.status === "running" || shell.exitCode === 0 ? "local-command-stdout" : "local-command-stderr";
|
|
576
|
+
}
|
|
577
|
+
function shellName(shell) {
|
|
578
|
+
return `shell #${shell.ordinal} (${shell.id})`;
|
|
579
|
+
}
|
|
580
|
+
function shellEndLine(shell) {
|
|
581
|
+
if (shell.status === "running" || shell.exitCode === 0) return;
|
|
582
|
+
if (typeof shell.exitCode === "number") return `[exit ${shell.exitCode}]`;
|
|
583
|
+
switch (shell.endReason) {
|
|
584
|
+
case "killed": return "[killed]";
|
|
585
|
+
case "timeout": return "[timed out]";
|
|
586
|
+
case "server_stopped": return "[killed: the gateway stopped]";
|
|
587
|
+
case "server_restarted": return "[ended: the gateway restarted; the process may still be running]";
|
|
588
|
+
case "spawn_failed": return "[failed to start]";
|
|
589
|
+
default: return "[ended]";
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function omissionLine(shell, omitted, total, bytes) {
|
|
593
|
+
const span = omitted === total ? `all ${count(total)} lines omitted (${size(bytes)})` : `${count(omitted)} of ${count(total)} lines omitted (${size(bytes)} in all)`;
|
|
594
|
+
return `[${shellName(shell)}: ${span}; ${OUTPUT_POINTER}]`;
|
|
595
|
+
}
|
|
596
|
+
function runningStatusLine(shell, text, options) {
|
|
597
|
+
const lines = countLines(text);
|
|
598
|
+
const progress = options.previous ? lines > options.previous.lines ? `${count(lines - options.previous.lines)} new lines since the last message` : "no new lines since the last message" : `${count(lines)} lines so far`;
|
|
599
|
+
return `[${shellName(shell)} is still running as of the time this was written (started ${elapsed(Date.now() - shell.startedAt)} ago, ${progress}). Nothing from it streams into this conversation; ${OUTPUT_POINTER}.]`;
|
|
600
|
+
}
|
|
601
|
+
function count(n) {
|
|
602
|
+
return n.toLocaleString("en-US");
|
|
603
|
+
}
|
|
604
|
+
function size(bytes) {
|
|
605
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
606
|
+
if (bytes < 1048576) return `${Math.round(bytes / 1024)} KiB`;
|
|
607
|
+
return `${(bytes / 1048576).toFixed(1)} MiB`;
|
|
608
|
+
}
|
|
609
|
+
function elapsed(ms) {
|
|
610
|
+
const seconds = Math.max(0, Math.floor(ms / 1e3));
|
|
611
|
+
if (seconds < 60) return `${seconds}s`;
|
|
612
|
+
const minutes = Math.floor(seconds / 60);
|
|
613
|
+
if (minutes < 60) return `${minutes}m ${seconds % 60}s`;
|
|
614
|
+
return `${Math.floor(minutes / 60)}h ${minutes % 60}m`;
|
|
615
|
+
}
|
|
297
616
|
//#endregion
|
|
298
617
|
//#region src/lib/patch.ts
|
|
299
618
|
const MAX_PATCH_LINES = 400;
|
|
@@ -774,23 +1093,36 @@ var EventLog = class {
|
|
|
774
1093
|
};
|
|
775
1094
|
this.#lastActivityAt = event.ts;
|
|
776
1095
|
this.#fold(event);
|
|
777
|
-
this.#
|
|
1096
|
+
this.#retain(event);
|
|
778
1097
|
return event;
|
|
779
1098
|
}
|
|
780
1099
|
at(seq) {
|
|
781
1100
|
return this.#events.find((event) => event.seq === seq);
|
|
782
1101
|
}
|
|
783
1102
|
restore(events, seq, lastActivityAt) {
|
|
784
|
-
this.#events = [
|
|
1103
|
+
this.#events = [];
|
|
785
1104
|
this.#seq = seq;
|
|
786
1105
|
this.#activityCount = 0;
|
|
787
1106
|
this.#proseCount = 0;
|
|
788
1107
|
this.#contextUsage = void 0;
|
|
789
1108
|
this.#checklist = void 0;
|
|
790
1109
|
this.#resetSeq = 0;
|
|
791
|
-
for (const event of
|
|
1110
|
+
for (const event of events) {
|
|
1111
|
+
this.#fold(event);
|
|
1112
|
+
this.#retain(event);
|
|
1113
|
+
}
|
|
792
1114
|
this.#lastActivityAt = lastActivityAt;
|
|
793
1115
|
}
|
|
1116
|
+
#retain(event) {
|
|
1117
|
+
const key = logCoalesceKey(event);
|
|
1118
|
+
if (key !== void 0) {
|
|
1119
|
+
for (let index = this.#events.length - 1; index >= 0; index--) if (logCoalesceKey(this.#events[index]) === key) {
|
|
1120
|
+
this.#events.splice(index, 1);
|
|
1121
|
+
break;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
this.#events.push(event);
|
|
1125
|
+
}
|
|
794
1126
|
#fold(event) {
|
|
795
1127
|
this.#activityCount += transcriptActivity(event);
|
|
796
1128
|
this.#proseCount += transcriptProse(event);
|
|
@@ -1041,7 +1373,7 @@ async function runPeerTool(peers, from, name, args) {
|
|
|
1041
1373
|
}
|
|
1042
1374
|
case "peers_peek": {
|
|
1043
1375
|
const input = z.object(PEER_TOOL_SHAPES.peers_peek.shape).safeParse(args ?? {});
|
|
1044
|
-
if (!input.success) return invalidArguments(name, input.error);
|
|
1376
|
+
if (!input.success) return invalidArguments$1(name, input.error);
|
|
1045
1377
|
const peek = await peers.peek(from, input.data.sessionId, { recent: input.data.recent });
|
|
1046
1378
|
if (!peek) return {
|
|
1047
1379
|
text: `no such session: ${input.data.sessionId}`,
|
|
@@ -1054,7 +1386,7 @@ async function runPeerTool(peers, from, name, args) {
|
|
|
1054
1386
|
}
|
|
1055
1387
|
case "peers_send": {
|
|
1056
1388
|
const input = z.object(PEER_TOOL_SHAPES.peers_send.shape).safeParse(args ?? {});
|
|
1057
|
-
if (!input.success) return invalidArguments(name, input.error);
|
|
1389
|
+
if (!input.success) return invalidArguments$1(name, input.error);
|
|
1058
1390
|
const result = await peers.send(from, input.data.sessionId, input.data.text);
|
|
1059
1391
|
if (!result.delivered) return {
|
|
1060
1392
|
text: `not delivered: ${result.reason}`,
|
|
@@ -1074,7 +1406,7 @@ async function runPeerTool(peers, from, name, args) {
|
|
|
1074
1406
|
};
|
|
1075
1407
|
}
|
|
1076
1408
|
}
|
|
1077
|
-
function invalidArguments(name, error) {
|
|
1409
|
+
function invalidArguments$1(name, error) {
|
|
1078
1410
|
return {
|
|
1079
1411
|
text: `invalid arguments for ${name}: ${error.issues.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`).join("; ")}`,
|
|
1080
1412
|
isError: true
|
|
@@ -1165,6 +1497,138 @@ function peerDirectoryHandle() {
|
|
|
1165
1497
|
};
|
|
1166
1498
|
}
|
|
1167
1499
|
//#endregion
|
|
1500
|
+
//#region src/lib/shells.ts
|
|
1501
|
+
const SHELL_REFUSAL = "shell commands are not available on this session";
|
|
1502
|
+
const SHELL_DIRECTORY_SLOT = Symbol.for("workerdeck.shells.directory");
|
|
1503
|
+
const SHELL_TOOL_SHAPES = {
|
|
1504
|
+
shell_list: {
|
|
1505
|
+
description: "List the shell commands your user has run in this session with `$`: id, command, whether it is still running, its exit code and how much output it produced. Call this before shell_read to find the shell id.",
|
|
1506
|
+
shape: {}
|
|
1507
|
+
},
|
|
1508
|
+
shell_read: {
|
|
1509
|
+
description: "Read the end of one shell command's output, as a terminal would show it. Use it to answer \"what is the dev server printing now\" or to see more of a command whose output was summarised in the conversation. Reading never writes to the shell and never interrupts it.",
|
|
1510
|
+
shape: {
|
|
1511
|
+
shellId: z.string().describe("The shell id from shell_list"),
|
|
1512
|
+
tail: z.number().int().min(1).optional().describe(`How many trailing lines to return (default ${SHELL_READ_DEFAULT_LINES}, capped at ${SHELL_READ_MAX_LINES})`)
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
};
|
|
1516
|
+
const SHELL_TOOL_NAMES = Object.keys(SHELL_TOOL_SHAPES);
|
|
1517
|
+
function shellToolSpecs() {
|
|
1518
|
+
return SHELL_TOOL_NAMES.map((name) => {
|
|
1519
|
+
const { description, shape } = SHELL_TOOL_SHAPES[name];
|
|
1520
|
+
return {
|
|
1521
|
+
name,
|
|
1522
|
+
description,
|
|
1523
|
+
inputSchema: z.toJSONSchema(z.object(shape))
|
|
1524
|
+
};
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
function isShellToolName(name) {
|
|
1528
|
+
return Object.hasOwn(SHELL_TOOL_SHAPES, name);
|
|
1529
|
+
}
|
|
1530
|
+
function clampShellTail(tail) {
|
|
1531
|
+
if (tail === void 0 || !Number.isFinite(tail)) return SHELL_READ_DEFAULT_LINES;
|
|
1532
|
+
return Math.max(1, Math.min(Math.floor(tail), SHELL_READ_MAX_LINES));
|
|
1533
|
+
}
|
|
1534
|
+
function shellSummary(info) {
|
|
1535
|
+
return {
|
|
1536
|
+
id: info.id,
|
|
1537
|
+
ordinal: info.ordinal,
|
|
1538
|
+
command: info.command,
|
|
1539
|
+
label: info.label,
|
|
1540
|
+
cwd: info.cwd,
|
|
1541
|
+
owner: info.owner,
|
|
1542
|
+
status: info.status,
|
|
1543
|
+
startedAt: info.startedAt,
|
|
1544
|
+
endedAt: info.endedAt,
|
|
1545
|
+
exitCode: info.exitCode,
|
|
1546
|
+
endReason: info.endReason,
|
|
1547
|
+
bytes: info.bytes,
|
|
1548
|
+
capped: info.capped
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
function shellTail(text, tail) {
|
|
1552
|
+
const body = text.endsWith("\n") ? text.slice(0, -1) : text;
|
|
1553
|
+
const all = body.length === 0 ? [] : body.split("\n");
|
|
1554
|
+
const kept = all.slice(Math.max(0, all.length - tail));
|
|
1555
|
+
return {
|
|
1556
|
+
text: kept.join("\n"),
|
|
1557
|
+
lines: kept.length,
|
|
1558
|
+
totalLines: all.length,
|
|
1559
|
+
truncated: kept.length < all.length
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
async function runShellTool(shells, from, name, args) {
|
|
1563
|
+
if (!isShellToolName(name)) return {
|
|
1564
|
+
text: `unknown shell tool: ${name}`,
|
|
1565
|
+
isError: true
|
|
1566
|
+
};
|
|
1567
|
+
try {
|
|
1568
|
+
switch (name) {
|
|
1569
|
+
case "shell_list": {
|
|
1570
|
+
const rows = await shells.list(from);
|
|
1571
|
+
return {
|
|
1572
|
+
text: rows.length ? JSON.stringify(rows, null, 2) : "No shell commands have run in this session.",
|
|
1573
|
+
isError: false
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
case "shell_read": {
|
|
1577
|
+
const input = z.object(SHELL_TOOL_SHAPES.shell_read.shape).safeParse(args ?? {});
|
|
1578
|
+
if (!input.success) return invalidArguments(name, input.error);
|
|
1579
|
+
const result = await shells.read(from, input.data.shellId, { tail: clampShellTail(input.data.tail) });
|
|
1580
|
+
if (!result) return {
|
|
1581
|
+
text: `no such shell: ${input.data.shellId}`,
|
|
1582
|
+
isError: true
|
|
1583
|
+
};
|
|
1584
|
+
return {
|
|
1585
|
+
text: shellReadText(result),
|
|
1586
|
+
isError: false
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
} catch (error) {
|
|
1591
|
+
return {
|
|
1592
|
+
text: error instanceof Error ? error.message : String(error),
|
|
1593
|
+
isError: true
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
function invalidArguments(name, error) {
|
|
1598
|
+
return {
|
|
1599
|
+
text: `invalid arguments for ${name}: ${error.issues.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`).join("; ")}`,
|
|
1600
|
+
isError: true
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
function shellReadText(result) {
|
|
1604
|
+
const { shell } = result;
|
|
1605
|
+
const scope = result.truncated ? `showing the last ${result.lines} of ${result.totalLines} lines` : `showing all ${result.lines} lines`;
|
|
1606
|
+
const head = `[shell #${shell.ordinal} ${shell.id}: $ ${shell.command}, ${shellState(shell)}, ${scope}]`;
|
|
1607
|
+
return result.text ? `${head}\n${result.text}` : `${head}\n(no output)`;
|
|
1608
|
+
}
|
|
1609
|
+
function shellState(shell) {
|
|
1610
|
+
if (shell.status === "running") return "still running";
|
|
1611
|
+
if (shell.exitCode !== void 0) return `exited ${shell.exitCode}`;
|
|
1612
|
+
return `ended (${shell.endReason ?? "exit"})`;
|
|
1613
|
+
}
|
|
1614
|
+
function installShellDirectory(directory) {
|
|
1615
|
+
globalThis[SHELL_DIRECTORY_SLOT] = directory;
|
|
1616
|
+
}
|
|
1617
|
+
function installedShellDirectory() {
|
|
1618
|
+
return globalThis[SHELL_DIRECTORY_SLOT];
|
|
1619
|
+
}
|
|
1620
|
+
function shellDirectoryHandle() {
|
|
1621
|
+
const resolve = () => {
|
|
1622
|
+
const directory = installedShellDirectory();
|
|
1623
|
+
if (!directory) throw new Error(SHELL_REFUSAL);
|
|
1624
|
+
return directory;
|
|
1625
|
+
};
|
|
1626
|
+
return {
|
|
1627
|
+
list: async (from) => resolve().list(from),
|
|
1628
|
+
read: async (from, shellId, options) => resolve().read(from, shellId, options)
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
//#endregion
|
|
1168
1632
|
//#region src/engines/claude/subagents.ts
|
|
1169
1633
|
const SPAWNER_NAMES = /* @__PURE__ */ new Set(["Task", "Agent"]);
|
|
1170
1634
|
var SubagentTracker = class {
|
|
@@ -1372,7 +1836,7 @@ var SessionRunner = class {
|
|
|
1372
1836
|
#cost = new CostLedger();
|
|
1373
1837
|
#numTurns;
|
|
1374
1838
|
#input = new InputQueue();
|
|
1375
|
-
#
|
|
1839
|
+
#localCommands = new LocalCommandQueue((text, uuid, shell) => this.#emit(localCommandEvent(text, uuid, shell)));
|
|
1376
1840
|
#compactionId;
|
|
1377
1841
|
#compactionTurns = 0;
|
|
1378
1842
|
#idleWhileCompacting = false;
|
|
@@ -1497,25 +1961,12 @@ var SessionRunner = class {
|
|
|
1497
1961
|
...options?.origin ? { origin: options.origin } : {}
|
|
1498
1962
|
});
|
|
1499
1963
|
}
|
|
1500
|
-
queueLocalCommand(
|
|
1964
|
+
queueLocalCommand(input) {
|
|
1501
1965
|
if (this.#closed) throw new Error("session is closed");
|
|
1502
|
-
|
|
1503
|
-
this.#pendingLocalCommands.push(text);
|
|
1504
|
-
this.#emit({
|
|
1505
|
-
type: "user_message",
|
|
1506
|
-
message: {
|
|
1507
|
-
role: "user",
|
|
1508
|
-
content: text
|
|
1509
|
-
},
|
|
1510
|
-
parentToolUseId: null,
|
|
1511
|
-
synthetic: true,
|
|
1512
|
-
uuid: randomUUID()
|
|
1513
|
-
});
|
|
1966
|
+
this.#localCommands.push(input);
|
|
1514
1967
|
}
|
|
1515
1968
|
#takeLocalCommands() {
|
|
1516
|
-
|
|
1517
|
-
this.#pendingLocalCommands = [];
|
|
1518
|
-
return context;
|
|
1969
|
+
return this.#localCommands.take();
|
|
1519
1970
|
}
|
|
1520
1971
|
async mcpServers() {
|
|
1521
1972
|
const query = this.#query;
|
|
@@ -1543,7 +1994,7 @@ var SessionRunner = class {
|
|
|
1543
1994
|
}
|
|
1544
1995
|
async clearContext() {
|
|
1545
1996
|
if (this.#status === "closed" || this.#status === "failed") throw new Error("session is closed");
|
|
1546
|
-
this.#
|
|
1997
|
+
this.#localCommands.clear();
|
|
1547
1998
|
this.sendMessage("/clear");
|
|
1548
1999
|
}
|
|
1549
2000
|
async setPermissionMode(mode) {
|
|
@@ -1574,7 +2025,7 @@ var SessionRunner = class {
|
|
|
1574
2025
|
close(reason = "client") {
|
|
1575
2026
|
if (this.#closed) return;
|
|
1576
2027
|
this.#closed = true;
|
|
1577
|
-
this.#
|
|
2028
|
+
this.#localCommands.clear();
|
|
1578
2029
|
for (const [id, pending] of this.#pending) this.#settleApproval(id, pending, {
|
|
1579
2030
|
behavior: "deny",
|
|
1580
2031
|
message: "Session closed"
|
|
@@ -1692,8 +2143,9 @@ var SessionRunner = class {
|
|
|
1692
2143
|
#mcpServersOption() {
|
|
1693
2144
|
const declared = this.#config.mcpServers;
|
|
1694
2145
|
const peers = this.#config.peers;
|
|
1695
|
-
|
|
1696
|
-
|
|
2146
|
+
const shells = this.#config.shells;
|
|
2147
|
+
if (!peers && !shells) return declared;
|
|
2148
|
+
const tools = [...peers ? PEER_TOOL_NAMES.map((name) => tool(name, PEER_TOOL_SHAPES[name].description, PEER_TOOL_SHAPES[name].shape, async (args) => {
|
|
1697
2149
|
const output = await runPeerTool(peers, this.id, name, args);
|
|
1698
2150
|
return {
|
|
1699
2151
|
content: [{
|
|
@@ -1702,7 +2154,16 @@ var SessionRunner = class {
|
|
|
1702
2154
|
}],
|
|
1703
2155
|
isError: output.isError
|
|
1704
2156
|
};
|
|
1705
|
-
}))
|
|
2157
|
+
})) : [], ...shells ? SHELL_TOOL_NAMES.map((name) => tool(name, SHELL_TOOL_SHAPES[name].description, SHELL_TOOL_SHAPES[name].shape, async (args) => {
|
|
2158
|
+
const output = await runShellTool(shells, this.id, name, args);
|
|
2159
|
+
return {
|
|
2160
|
+
content: [{
|
|
2161
|
+
type: "text",
|
|
2162
|
+
text: output.text
|
|
2163
|
+
}],
|
|
2164
|
+
isError: output.isError
|
|
2165
|
+
};
|
|
2166
|
+
})) : []];
|
|
1706
2167
|
return {
|
|
1707
2168
|
...declared,
|
|
1708
2169
|
[PEER_MCP_SERVER]: createSdkMcpServer({
|
|
@@ -1769,7 +2230,7 @@ var SessionRunner = class {
|
|
|
1769
2230
|
this.#emit(body);
|
|
1770
2231
|
if (body.type === "conversation_reset") {
|
|
1771
2232
|
this.#cost.rollover();
|
|
1772
|
-
this.#
|
|
2233
|
+
this.#localCommands.clear();
|
|
1773
2234
|
if (body.sdkSessionId) this.#sdkSessionId = body.sdkSessionId;
|
|
1774
2235
|
this.#fetchContextUsage();
|
|
1775
2236
|
}
|
|
@@ -2079,7 +2540,7 @@ var AiSdkRunner = class {
|
|
|
2079
2540
|
#statusDetail;
|
|
2080
2541
|
#permissionMode;
|
|
2081
2542
|
#messages = [];
|
|
2082
|
-
#
|
|
2543
|
+
#localCommands = new LocalCommandQueue((text, uuid, shell) => this.#emit(localCommandEvent(text, uuid, shell)));
|
|
2083
2544
|
#pendingToolCalls = /* @__PURE__ */ new Map();
|
|
2084
2545
|
#dispatched = /* @__PURE__ */ new Set();
|
|
2085
2546
|
#turnChain = Promise.resolve();
|
|
@@ -2114,7 +2575,7 @@ var AiSdkRunner = class {
|
|
|
2114
2575
|
if (!state || !Array.isArray(state.messages)) throw new Error("session snapshot is missing its provider-engine state");
|
|
2115
2576
|
this.#log.restore(snapshot.events, snapshot.seq, state.lastActivityAt);
|
|
2116
2577
|
this.#messages = [...state.messages];
|
|
2117
|
-
this.#
|
|
2578
|
+
this.#localCommands.restore(state.pendingLocalCommands ?? []);
|
|
2118
2579
|
for (const call of state.pendingToolCalls) {
|
|
2119
2580
|
const legacy = call.deferred;
|
|
2120
2581
|
this.#pendingToolCalls.set(call.toolCallId, legacy === void 0 ? call : {
|
|
@@ -2206,6 +2667,7 @@ var AiSdkRunner = class {
|
|
|
2206
2667
|
const snapshot = this.#buildSnapshot();
|
|
2207
2668
|
this.#parked = true;
|
|
2208
2669
|
this.#subscribers.clear();
|
|
2670
|
+
this.#localCommands.clear();
|
|
2209
2671
|
try {
|
|
2210
2672
|
Promise.resolve(this.#config.onClose?.()).catch(() => {});
|
|
2211
2673
|
} catch {}
|
|
@@ -2222,6 +2684,7 @@ var AiSdkRunner = class {
|
|
|
2222
2684
|
toolName: call.toolName,
|
|
2223
2685
|
expiresAt: call.expiresAt
|
|
2224
2686
|
}));
|
|
2687
|
+
const pendingLocalCommands = this.#localCommands.materialize();
|
|
2225
2688
|
const state = {
|
|
2226
2689
|
messages: this.#messages,
|
|
2227
2690
|
pendingToolCalls: [...this.#pendingToolCalls.values()],
|
|
@@ -2232,7 +2695,7 @@ var AiSdkRunner = class {
|
|
|
2232
2695
|
model: this.#modelAlias,
|
|
2233
2696
|
lastActivityAt: this.#log.lastActivityAt,
|
|
2234
2697
|
parkedAt: Date.now(),
|
|
2235
|
-
...
|
|
2698
|
+
...pendingLocalCommands.length ? { pendingLocalCommands } : {}
|
|
2236
2699
|
};
|
|
2237
2700
|
return {
|
|
2238
2701
|
engine: "provider",
|
|
@@ -2255,8 +2718,7 @@ var AiSdkRunner = class {
|
|
|
2255
2718
|
mediaType: normalizeMediaType(attachment.mediaType),
|
|
2256
2719
|
filename: attachment.name
|
|
2257
2720
|
}));
|
|
2258
|
-
const context =
|
|
2259
|
-
this.#pendingLocalCommands = [];
|
|
2721
|
+
const context = this.#localCommands.take();
|
|
2260
2722
|
const content = files.length || context ? [
|
|
2261
2723
|
...context ? [{
|
|
2262
2724
|
type: "text",
|
|
@@ -2285,21 +2747,10 @@ var AiSdkRunner = class {
|
|
|
2285
2747
|
});
|
|
2286
2748
|
this.#scheduleTurn();
|
|
2287
2749
|
}
|
|
2288
|
-
queueLocalCommand(
|
|
2750
|
+
queueLocalCommand(input) {
|
|
2289
2751
|
if (this.#parked) throw new Error("session is parked");
|
|
2290
2752
|
if (this.#closed) throw new Error("session is closed");
|
|
2291
|
-
|
|
2292
|
-
this.#pendingLocalCommands.push(text);
|
|
2293
|
-
this.#emit({
|
|
2294
|
-
type: "user_message",
|
|
2295
|
-
message: {
|
|
2296
|
-
role: "user",
|
|
2297
|
-
content: text
|
|
2298
|
-
},
|
|
2299
|
-
parentToolUseId: null,
|
|
2300
|
-
synthetic: true,
|
|
2301
|
-
uuid: randomUUID()
|
|
2302
|
-
});
|
|
2753
|
+
this.#localCommands.push(input);
|
|
2303
2754
|
}
|
|
2304
2755
|
resolveToolCall(toolCallId, output, options) {
|
|
2305
2756
|
if (!this.#settlePendingCall(toolCallId, output, options?.isError === true)) return false;
|
|
@@ -2401,7 +2852,7 @@ var AiSdkRunner = class {
|
|
|
2401
2852
|
if (this.#closed) throw new Error("session is closed");
|
|
2402
2853
|
if (this.#pendingToolCalls.size > 0) throw new Error("cannot clear context while tool calls are outstanding");
|
|
2403
2854
|
this.#messages = [];
|
|
2404
|
-
this.#
|
|
2855
|
+
this.#localCommands.clear();
|
|
2405
2856
|
this.#emit({ type: "conversation_reset" });
|
|
2406
2857
|
});
|
|
2407
2858
|
this.#turnChain = run.then(() => void 0, () => void 0);
|
|
@@ -2483,7 +2934,7 @@ var AiSdkRunner = class {
|
|
|
2483
2934
|
if (this.#closed || this.#parked) return;
|
|
2484
2935
|
this.#closed = true;
|
|
2485
2936
|
this.#abort?.abort();
|
|
2486
|
-
this.#
|
|
2937
|
+
this.#localCommands.clear();
|
|
2487
2938
|
this.#pendingToolCalls.clear();
|
|
2488
2939
|
this.#dispatched.clear();
|
|
2489
2940
|
for (const { timer } of this.#pendingApprovals.values()) clearTimeout(timer);
|
|
@@ -3378,6 +3829,22 @@ function createToolContext(options) {
|
|
|
3378
3829
|
})
|
|
3379
3830
|
});
|
|
3380
3831
|
}
|
|
3832
|
+
if (options.shells) {
|
|
3833
|
+
const shells = options.shells;
|
|
3834
|
+
const selfId = options.selfId ?? (() => options.sessionId);
|
|
3835
|
+
for (const name of SHELL_TOOL_NAMES) definitions.push({
|
|
3836
|
+
name,
|
|
3837
|
+
trust: "authoritative",
|
|
3838
|
+
tool: tool$1({
|
|
3839
|
+
description: SHELL_TOOL_SHAPES[name].description,
|
|
3840
|
+
inputSchema: z.object(SHELL_TOOL_SHAPES[name].shape),
|
|
3841
|
+
execute: async (args) => {
|
|
3842
|
+
const output = await runShellTool(shells, selfId(), name, args);
|
|
3843
|
+
return output.isError ? { error: output.text } : { result: output.text };
|
|
3844
|
+
}
|
|
3845
|
+
})
|
|
3846
|
+
});
|
|
3847
|
+
}
|
|
3381
3848
|
if (options.search) {
|
|
3382
3849
|
const search = options.search;
|
|
3383
3850
|
definitions.push({
|
|
@@ -3738,6 +4205,7 @@ function createEngineSession(options) {
|
|
|
3738
4205
|
webFetch,
|
|
3739
4206
|
onFileDelivered: options.capabilities?.deliverFiles === false || !isGranted("deliverFiles") ? void 0 : (file) => runner?.emitFileDelivered(file),
|
|
3740
4207
|
peers: options.config.peers,
|
|
4208
|
+
shells: options.config.shells,
|
|
3741
4209
|
selfId: () => runner?.id ?? "pending"
|
|
3742
4210
|
});
|
|
3743
4211
|
const declaredServers = options.profile?.session?.mcpServers;
|
|
@@ -4818,7 +5286,7 @@ var CodexRunner = class {
|
|
|
4818
5286
|
#planType;
|
|
4819
5287
|
#resolvedEffort;
|
|
4820
5288
|
#queue = [];
|
|
4821
|
-
#
|
|
5289
|
+
#localCommands = new LocalCommandQueue((text, uuid, shell) => this.#emit(localCommandEvent(text, uuid, shell)));
|
|
4822
5290
|
#turnChain = Promise.resolve();
|
|
4823
5291
|
#activeTurn;
|
|
4824
5292
|
#connection;
|
|
@@ -5077,28 +5545,16 @@ var CodexRunner = class {
|
|
|
5077
5545
|
type: "text",
|
|
5078
5546
|
text
|
|
5079
5547
|
});
|
|
5080
|
-
const context =
|
|
5081
|
-
this.#pendingLocalCommands = [];
|
|
5548
|
+
const context = this.#localCommands.take();
|
|
5082
5549
|
if (context) parts.unshift({
|
|
5083
5550
|
type: "text",
|
|
5084
5551
|
text: context
|
|
5085
5552
|
});
|
|
5086
5553
|
return withSkillItems(parts, this.#skillPaths);
|
|
5087
5554
|
}
|
|
5088
|
-
queueLocalCommand(
|
|
5555
|
+
queueLocalCommand(input) {
|
|
5089
5556
|
if (this.#closed) throw new Error("session is closed");
|
|
5090
|
-
|
|
5091
|
-
this.#pendingLocalCommands.push(text);
|
|
5092
|
-
this.#emit({
|
|
5093
|
-
type: "user_message",
|
|
5094
|
-
message: {
|
|
5095
|
-
role: "user",
|
|
5096
|
-
content: text
|
|
5097
|
-
},
|
|
5098
|
-
parentToolUseId: null,
|
|
5099
|
-
synthetic: true,
|
|
5100
|
-
uuid: randomUUID()
|
|
5101
|
-
});
|
|
5557
|
+
this.#localCommands.push(input);
|
|
5102
5558
|
}
|
|
5103
5559
|
resolvePermission(requestId, decision) {
|
|
5104
5560
|
const pending = this.#approvals.get(requestId);
|
|
@@ -5146,7 +5602,7 @@ var CodexRunner = class {
|
|
|
5146
5602
|
message: "the conversation was cleared"
|
|
5147
5603
|
}, "policy");
|
|
5148
5604
|
this.#resumedHistory = void 0;
|
|
5149
|
-
this.#
|
|
5605
|
+
this.#localCommands.clear();
|
|
5150
5606
|
this.#emit({
|
|
5151
5607
|
type: "conversation_reset",
|
|
5152
5608
|
sdkSessionId: this.#resumableThreadId()
|
|
@@ -5200,7 +5656,7 @@ var CodexRunner = class {
|
|
|
5200
5656
|
if (this.#closed) return;
|
|
5201
5657
|
this.#closed = true;
|
|
5202
5658
|
this.#queue.length = 0;
|
|
5203
|
-
this.#
|
|
5659
|
+
this.#localCommands.clear();
|
|
5204
5660
|
for (const [id, pending] of this.#approvals) this.#settleApproval(id, pending, {
|
|
5205
5661
|
behavior: "deny",
|
|
5206
5662
|
message: "Session closed"
|
|
@@ -5293,7 +5749,8 @@ var CodexRunner = class {
|
|
|
5293
5749
|
};
|
|
5294
5750
|
if (this.#model) options.model = this.#model;
|
|
5295
5751
|
if (this.#instructions !== void 0) options.developerInstructions = this.#instructions;
|
|
5296
|
-
|
|
5752
|
+
const dynamic = [...this.#config.peers ? peerToolSpecs() : [], ...this.#config.shells ? shellToolSpecs() : []];
|
|
5753
|
+
if (dynamic.length) options.dynamicTools = dynamic.map((spec) => ({
|
|
5297
5754
|
type: "function",
|
|
5298
5755
|
...spec
|
|
5299
5756
|
}));
|
|
@@ -5715,16 +6172,30 @@ var CodexRunner = class {
|
|
|
5715
6172
|
async #answerServerRequest(method, params, wireId) {
|
|
5716
6173
|
const channel = APPROVAL_CHANNELS[method];
|
|
5717
6174
|
if (channel) return this.#requestApproval(channel, method, params, wireId);
|
|
5718
|
-
if (method === "item/tool/call"
|
|
6175
|
+
if (method === "item/tool/call") {
|
|
5719
6176
|
const call = params;
|
|
5720
|
-
const
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
6177
|
+
const peers = this.#config.peers;
|
|
6178
|
+
const shells = this.#config.shells;
|
|
6179
|
+
if (peers && isPeerToolName(call.tool)) {
|
|
6180
|
+
const output = await runPeerTool(peers, this.id, call.tool, call.arguments);
|
|
6181
|
+
return {
|
|
6182
|
+
success: !output.isError,
|
|
6183
|
+
contentItems: [{
|
|
6184
|
+
type: "inputText",
|
|
6185
|
+
text: output.text
|
|
6186
|
+
}]
|
|
6187
|
+
};
|
|
6188
|
+
}
|
|
6189
|
+
if (shells && isShellToolName(call.tool)) {
|
|
6190
|
+
const output = await runShellTool(shells, this.id, call.tool, call.arguments);
|
|
6191
|
+
return {
|
|
6192
|
+
success: !output.isError,
|
|
6193
|
+
contentItems: [{
|
|
6194
|
+
type: "inputText",
|
|
6195
|
+
text: output.text
|
|
6196
|
+
}]
|
|
6197
|
+
};
|
|
6198
|
+
}
|
|
5728
6199
|
}
|
|
5729
6200
|
throw new JsonRpcError(-32601, `workerdeck does not handle server request '${method}'`);
|
|
5730
6201
|
}
|
|
@@ -6485,6 +6956,6 @@ function getEngineAdapter(engine) {
|
|
|
6485
6956
|
return ADAPTERS[engine ?? "claude"];
|
|
6486
6957
|
}
|
|
6487
6958
|
//#endregion
|
|
6488
|
-
export { AiSdkRunner, BrowserBridgeExecutor, CLAUDE_CATALOG, CODEX_CATALOG, CodexRunner, CostLedger, DeferredExecutor, InputQueue, JsonRpcError, JsonRpcStdioConnection, PEER_MCP_SERVER, PEER_MESSAGE_MAX_CHARS, PEER_RECENT_DEFAULT, PEER_RECENT_MAX, PEER_TOOL_NAMES, PendingRequestRegistry, QuickJsExecutor, SUPPORTED_ATTACHMENT_TYPES, SessionRunner, attachmentContentBlocks, attachmentKind, attachmentRef, checkClaudeAuth, claudeAdapter, codexAdapter, composeInstructions, connectAppServer, connectMcpTools, createEngineSession, createToolContext, createWebFetch, getEngineAdapter, htmlToMarkdown, installPeerDirectory, installedPeerDirectory, isHostAllowed, isPrivateAddress, isSlashCommand, listCodexSessions, localCommandContext, localCommandTranscript, mcpStatusInfo, modelOptionsFromSdk, normalizeMediaType, normalizeSdkMessage, peerDirectoryHandle, peerMentionsEnvelope, peerMessageEnvelope, peerSummary, peerToolSpecs, providerAdapter, recentLines, replaySlice, resolveBundledClaudeExecutable, resolveBundledCodexExecutable, resolveInstructions, runPeerTool, toApiMessage, toExecutionResult, truncateResultBlocks, withHostTools, withMcpTools, withPeerContext };
|
|
6959
|
+
export { AiSdkRunner, BrowserBridgeExecutor, CLAUDE_CATALOG, CODEX_CATALOG, CodexRunner, CostLedger, DeferredExecutor, InputQueue, JsonRpcError, JsonRpcStdioConnection, LocalCommandQueue, PEER_MCP_SERVER, PEER_MESSAGE_MAX_CHARS, PEER_RECENT_DEFAULT, PEER_RECENT_MAX, PEER_TOOL_NAMES, PendingRequestRegistry, QuickJsExecutor, SHELL_REFUSAL, SHELL_TOOL_NAMES, SHELL_TOOL_SHAPES, SUPPORTED_ATTACHMENT_TYPES, SessionRunner, attachmentContentBlocks, attachmentKind, attachmentRef, checkClaudeAuth, clampShellTail, claudeAdapter, codexAdapter, composeInstructions, connectAppServer, connectMcpTools, countLines, createEngineSession, createToolContext, createWebFetch, getEngineAdapter, headTail, htmlToMarkdown, installPeerDirectory, installShellDirectory, installedPeerDirectory, installedShellDirectory, isHostAllowed, isLocalShellSource, isPrivateAddress, isShellToolName, isSlashCommand, listCodexSessions, localCommandContext, localCommandEvent, localCommandTranscript, mcpStatusInfo, modelOptionsFromSdk, normalizeMediaType, normalizeSdkMessage, peerDirectoryHandle, peerMentionsEnvelope, peerMessageEnvelope, peerSummary, peerToolSpecs, providerAdapter, recentLines, replaySlice, resolveBundledClaudeExecutable, resolveBundledCodexExecutable, resolveInstructions, runPeerTool, runShellTool, shellContextText, shellDirectoryHandle, shellInlineText, shellReadText, shellSummary, shellTail, shellToolSpecs, splitLines, toApiMessage, toExecutionResult, truncateResultBlocks, ttyText, withHostTools, withMcpTools, withPeerContext };
|
|
6489
6960
|
|
|
6490
6961
|
//# sourceMappingURL=index.mjs.map
|