@workerdeck/core 2.12.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 +143 -8
- package/build/index.mjs +597 -84
- 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);
|
|
@@ -971,6 +1303,19 @@ function withTitle(config, title) {
|
|
|
971
1303
|
};
|
|
972
1304
|
}
|
|
973
1305
|
//#endregion
|
|
1306
|
+
//#region src/lib/instructions.ts
|
|
1307
|
+
function resolveInstructions(instructions, context) {
|
|
1308
|
+
if (instructions === void 0) return;
|
|
1309
|
+
const text = (typeof instructions === "function" ? instructions(context) : instructions).trim();
|
|
1310
|
+
return text === "" ? void 0 : text;
|
|
1311
|
+
}
|
|
1312
|
+
function composeInstructions(...parts) {
|
|
1313
|
+
const present = parts.filter((part) => part !== void 0 && part !== "");
|
|
1314
|
+
if (present.length <= 1) return present[0];
|
|
1315
|
+
if (present.every((part) => typeof part === "string")) return present.join("\n\n");
|
|
1316
|
+
return (context) => present.map((part) => resolveInstructions(part, context)).filter((text) => text !== void 0).join("\n\n");
|
|
1317
|
+
}
|
|
1318
|
+
//#endregion
|
|
974
1319
|
//#region src/lib/peers.ts
|
|
975
1320
|
const PEER_MCP_SERVER = "workerdeck";
|
|
976
1321
|
const PEER_MESSAGE_MAX_CHARS = 16e3;
|
|
@@ -1028,7 +1373,7 @@ async function runPeerTool(peers, from, name, args) {
|
|
|
1028
1373
|
}
|
|
1029
1374
|
case "peers_peek": {
|
|
1030
1375
|
const input = z.object(PEER_TOOL_SHAPES.peers_peek.shape).safeParse(args ?? {});
|
|
1031
|
-
if (!input.success) return invalidArguments(name, input.error);
|
|
1376
|
+
if (!input.success) return invalidArguments$1(name, input.error);
|
|
1032
1377
|
const peek = await peers.peek(from, input.data.sessionId, { recent: input.data.recent });
|
|
1033
1378
|
if (!peek) return {
|
|
1034
1379
|
text: `no such session: ${input.data.sessionId}`,
|
|
@@ -1041,7 +1386,7 @@ async function runPeerTool(peers, from, name, args) {
|
|
|
1041
1386
|
}
|
|
1042
1387
|
case "peers_send": {
|
|
1043
1388
|
const input = z.object(PEER_TOOL_SHAPES.peers_send.shape).safeParse(args ?? {});
|
|
1044
|
-
if (!input.success) return invalidArguments(name, input.error);
|
|
1389
|
+
if (!input.success) return invalidArguments$1(name, input.error);
|
|
1045
1390
|
const result = await peers.send(from, input.data.sessionId, input.data.text);
|
|
1046
1391
|
if (!result.delivered) return {
|
|
1047
1392
|
text: `not delivered: ${result.reason}`,
|
|
@@ -1061,7 +1406,7 @@ async function runPeerTool(peers, from, name, args) {
|
|
|
1061
1406
|
};
|
|
1062
1407
|
}
|
|
1063
1408
|
}
|
|
1064
|
-
function invalidArguments(name, error) {
|
|
1409
|
+
function invalidArguments$1(name, error) {
|
|
1065
1410
|
return {
|
|
1066
1411
|
text: `invalid arguments for ${name}: ${error.issues.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`).join("; ")}`,
|
|
1067
1412
|
isError: true
|
|
@@ -1152,6 +1497,138 @@ function peerDirectoryHandle() {
|
|
|
1152
1497
|
};
|
|
1153
1498
|
}
|
|
1154
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
|
|
1155
1632
|
//#region src/engines/claude/subagents.ts
|
|
1156
1633
|
const SPAWNER_NAMES = /* @__PURE__ */ new Set(["Task", "Agent"]);
|
|
1157
1634
|
var SubagentTracker = class {
|
|
@@ -1343,6 +1820,7 @@ var SessionRunner = class {
|
|
|
1343
1820
|
createdAt;
|
|
1344
1821
|
#config;
|
|
1345
1822
|
#cwd;
|
|
1823
|
+
#instructions;
|
|
1346
1824
|
#log = new EventLog();
|
|
1347
1825
|
#subscribers = new SubscriberSet();
|
|
1348
1826
|
#tasks = new TaskChecklist();
|
|
@@ -1358,7 +1836,7 @@ var SessionRunner = class {
|
|
|
1358
1836
|
#cost = new CostLedger();
|
|
1359
1837
|
#numTurns;
|
|
1360
1838
|
#input = new InputQueue();
|
|
1361
|
-
#
|
|
1839
|
+
#localCommands = new LocalCommandQueue((text, uuid, shell) => this.#emit(localCommandEvent(text, uuid, shell)));
|
|
1362
1840
|
#compactionId;
|
|
1363
1841
|
#compactionTurns = 0;
|
|
1364
1842
|
#idleWhileCompacting = false;
|
|
@@ -1379,6 +1857,12 @@ var SessionRunner = class {
|
|
|
1379
1857
|
this.#permissionMode = config.permissionMode;
|
|
1380
1858
|
this.id = id;
|
|
1381
1859
|
this.createdAt = Date.now();
|
|
1860
|
+
this.#instructions = resolveInstructions(config.instructions, {
|
|
1861
|
+
sessionId: id,
|
|
1862
|
+
cwd: config.cwd,
|
|
1863
|
+
profile: config.profile
|
|
1864
|
+
});
|
|
1865
|
+
if (this.#instructions !== void 0 && config.extraOptions?.systemPrompt !== void 0) throw new Error("instructions and extraOptions.systemPrompt both set - the host must pick one");
|
|
1382
1866
|
}
|
|
1383
1867
|
get status() {
|
|
1384
1868
|
return this.#status;
|
|
@@ -1477,25 +1961,12 @@ var SessionRunner = class {
|
|
|
1477
1961
|
...options?.origin ? { origin: options.origin } : {}
|
|
1478
1962
|
});
|
|
1479
1963
|
}
|
|
1480
|
-
queueLocalCommand(
|
|
1964
|
+
queueLocalCommand(input) {
|
|
1481
1965
|
if (this.#closed) throw new Error("session is closed");
|
|
1482
|
-
|
|
1483
|
-
this.#pendingLocalCommands.push(text);
|
|
1484
|
-
this.#emit({
|
|
1485
|
-
type: "user_message",
|
|
1486
|
-
message: {
|
|
1487
|
-
role: "user",
|
|
1488
|
-
content: text
|
|
1489
|
-
},
|
|
1490
|
-
parentToolUseId: null,
|
|
1491
|
-
synthetic: true,
|
|
1492
|
-
uuid: randomUUID()
|
|
1493
|
-
});
|
|
1966
|
+
this.#localCommands.push(input);
|
|
1494
1967
|
}
|
|
1495
1968
|
#takeLocalCommands() {
|
|
1496
|
-
|
|
1497
|
-
this.#pendingLocalCommands = [];
|
|
1498
|
-
return context;
|
|
1969
|
+
return this.#localCommands.take();
|
|
1499
1970
|
}
|
|
1500
1971
|
async mcpServers() {
|
|
1501
1972
|
const query = this.#query;
|
|
@@ -1523,7 +1994,7 @@ var SessionRunner = class {
|
|
|
1523
1994
|
}
|
|
1524
1995
|
async clearContext() {
|
|
1525
1996
|
if (this.#status === "closed" || this.#status === "failed") throw new Error("session is closed");
|
|
1526
|
-
this.#
|
|
1997
|
+
this.#localCommands.clear();
|
|
1527
1998
|
this.sendMessage("/clear");
|
|
1528
1999
|
}
|
|
1529
2000
|
async setPermissionMode(mode) {
|
|
@@ -1554,7 +2025,7 @@ var SessionRunner = class {
|
|
|
1554
2025
|
close(reason = "client") {
|
|
1555
2026
|
if (this.#closed) return;
|
|
1556
2027
|
this.#closed = true;
|
|
1557
|
-
this.#
|
|
2028
|
+
this.#localCommands.clear();
|
|
1558
2029
|
for (const [id, pending] of this.#pending) this.#settleApproval(id, pending, {
|
|
1559
2030
|
behavior: "deny",
|
|
1560
2031
|
message: "Session closed"
|
|
@@ -1661,14 +2132,20 @@ var SessionRunner = class {
|
|
|
1661
2132
|
env: c.env,
|
|
1662
2133
|
pathToClaudeCodeExecutable: c.pathToClaudeCodeExecutable,
|
|
1663
2134
|
...c.permissionMode === "bypassPermissions" || c.allowDangerouslySkipPermissions ? { allowDangerouslySkipPermissions: true } : {},
|
|
1664
|
-
...c.extraOptions
|
|
2135
|
+
...c.extraOptions,
|
|
2136
|
+
...this.#instructions === void 0 ? {} : { systemPrompt: {
|
|
2137
|
+
type: "preset",
|
|
2138
|
+
preset: "claude_code",
|
|
2139
|
+
append: this.#instructions
|
|
2140
|
+
} }
|
|
1665
2141
|
};
|
|
1666
2142
|
}
|
|
1667
2143
|
#mcpServersOption() {
|
|
1668
2144
|
const declared = this.#config.mcpServers;
|
|
1669
2145
|
const peers = this.#config.peers;
|
|
1670
|
-
|
|
1671
|
-
|
|
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) => {
|
|
1672
2149
|
const output = await runPeerTool(peers, this.id, name, args);
|
|
1673
2150
|
return {
|
|
1674
2151
|
content: [{
|
|
@@ -1677,7 +2154,16 @@ var SessionRunner = class {
|
|
|
1677
2154
|
}],
|
|
1678
2155
|
isError: output.isError
|
|
1679
2156
|
};
|
|
1680
|
-
}))
|
|
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
|
+
})) : []];
|
|
1681
2167
|
return {
|
|
1682
2168
|
...declared,
|
|
1683
2169
|
[PEER_MCP_SERVER]: createSdkMcpServer({
|
|
@@ -1744,7 +2230,7 @@ var SessionRunner = class {
|
|
|
1744
2230
|
this.#emit(body);
|
|
1745
2231
|
if (body.type === "conversation_reset") {
|
|
1746
2232
|
this.#cost.rollover();
|
|
1747
|
-
this.#
|
|
2233
|
+
this.#localCommands.clear();
|
|
1748
2234
|
if (body.sdkSessionId) this.#sdkSessionId = body.sdkSessionId;
|
|
1749
2235
|
this.#fetchContextUsage();
|
|
1750
2236
|
}
|
|
@@ -2046,6 +2532,7 @@ var AiSdkRunner = class {
|
|
|
2046
2532
|
id;
|
|
2047
2533
|
createdAt;
|
|
2048
2534
|
#config;
|
|
2535
|
+
#instructions;
|
|
2049
2536
|
#model;
|
|
2050
2537
|
#log = new EventLog();
|
|
2051
2538
|
#subscribers = new SubscriberSet();
|
|
@@ -2053,7 +2540,7 @@ var AiSdkRunner = class {
|
|
|
2053
2540
|
#statusDetail;
|
|
2054
2541
|
#permissionMode;
|
|
2055
2542
|
#messages = [];
|
|
2056
|
-
#
|
|
2543
|
+
#localCommands = new LocalCommandQueue((text, uuid, shell) => this.#emit(localCommandEvent(text, uuid, shell)));
|
|
2057
2544
|
#pendingToolCalls = /* @__PURE__ */ new Map();
|
|
2058
2545
|
#dispatched = /* @__PURE__ */ new Set();
|
|
2059
2546
|
#turnChain = Promise.resolve();
|
|
@@ -2075,6 +2562,11 @@ var AiSdkRunner = class {
|
|
|
2075
2562
|
this.#modelAlias = config.model;
|
|
2076
2563
|
this.id = config.restore?.id ?? id;
|
|
2077
2564
|
this.createdAt = config.restore?.createdAt ?? Date.now();
|
|
2565
|
+
this.#instructions = resolveInstructions(config.instructions, {
|
|
2566
|
+
sessionId: this.id,
|
|
2567
|
+
cwd: config.cwd,
|
|
2568
|
+
profile: config.profile
|
|
2569
|
+
});
|
|
2078
2570
|
if (config.restore) this.#restore(config.restore);
|
|
2079
2571
|
}
|
|
2080
2572
|
#restore(snapshot) {
|
|
@@ -2083,7 +2575,7 @@ var AiSdkRunner = class {
|
|
|
2083
2575
|
if (!state || !Array.isArray(state.messages)) throw new Error("session snapshot is missing its provider-engine state");
|
|
2084
2576
|
this.#log.restore(snapshot.events, snapshot.seq, state.lastActivityAt);
|
|
2085
2577
|
this.#messages = [...state.messages];
|
|
2086
|
-
this.#
|
|
2578
|
+
this.#localCommands.restore(state.pendingLocalCommands ?? []);
|
|
2087
2579
|
for (const call of state.pendingToolCalls) {
|
|
2088
2580
|
const legacy = call.deferred;
|
|
2089
2581
|
this.#pendingToolCalls.set(call.toolCallId, legacy === void 0 ? call : {
|
|
@@ -2175,6 +2667,7 @@ var AiSdkRunner = class {
|
|
|
2175
2667
|
const snapshot = this.#buildSnapshot();
|
|
2176
2668
|
this.#parked = true;
|
|
2177
2669
|
this.#subscribers.clear();
|
|
2670
|
+
this.#localCommands.clear();
|
|
2178
2671
|
try {
|
|
2179
2672
|
Promise.resolve(this.#config.onClose?.()).catch(() => {});
|
|
2180
2673
|
} catch {}
|
|
@@ -2191,6 +2684,7 @@ var AiSdkRunner = class {
|
|
|
2191
2684
|
toolName: call.toolName,
|
|
2192
2685
|
expiresAt: call.expiresAt
|
|
2193
2686
|
}));
|
|
2687
|
+
const pendingLocalCommands = this.#localCommands.materialize();
|
|
2194
2688
|
const state = {
|
|
2195
2689
|
messages: this.#messages,
|
|
2196
2690
|
pendingToolCalls: [...this.#pendingToolCalls.values()],
|
|
@@ -2201,7 +2695,7 @@ var AiSdkRunner = class {
|
|
|
2201
2695
|
model: this.#modelAlias,
|
|
2202
2696
|
lastActivityAt: this.#log.lastActivityAt,
|
|
2203
2697
|
parkedAt: Date.now(),
|
|
2204
|
-
...
|
|
2698
|
+
...pendingLocalCommands.length ? { pendingLocalCommands } : {}
|
|
2205
2699
|
};
|
|
2206
2700
|
return {
|
|
2207
2701
|
engine: "provider",
|
|
@@ -2224,8 +2718,7 @@ var AiSdkRunner = class {
|
|
|
2224
2718
|
mediaType: normalizeMediaType(attachment.mediaType),
|
|
2225
2719
|
filename: attachment.name
|
|
2226
2720
|
}));
|
|
2227
|
-
const context =
|
|
2228
|
-
this.#pendingLocalCommands = [];
|
|
2721
|
+
const context = this.#localCommands.take();
|
|
2229
2722
|
const content = files.length || context ? [
|
|
2230
2723
|
...context ? [{
|
|
2231
2724
|
type: "text",
|
|
@@ -2254,21 +2747,10 @@ var AiSdkRunner = class {
|
|
|
2254
2747
|
});
|
|
2255
2748
|
this.#scheduleTurn();
|
|
2256
2749
|
}
|
|
2257
|
-
queueLocalCommand(
|
|
2750
|
+
queueLocalCommand(input) {
|
|
2258
2751
|
if (this.#parked) throw new Error("session is parked");
|
|
2259
2752
|
if (this.#closed) throw new Error("session is closed");
|
|
2260
|
-
|
|
2261
|
-
this.#pendingLocalCommands.push(text);
|
|
2262
|
-
this.#emit({
|
|
2263
|
-
type: "user_message",
|
|
2264
|
-
message: {
|
|
2265
|
-
role: "user",
|
|
2266
|
-
content: text
|
|
2267
|
-
},
|
|
2268
|
-
parentToolUseId: null,
|
|
2269
|
-
synthetic: true,
|
|
2270
|
-
uuid: randomUUID()
|
|
2271
|
-
});
|
|
2753
|
+
this.#localCommands.push(input);
|
|
2272
2754
|
}
|
|
2273
2755
|
resolveToolCall(toolCallId, output, options) {
|
|
2274
2756
|
if (!this.#settlePendingCall(toolCallId, output, options?.isError === true)) return false;
|
|
@@ -2370,7 +2852,7 @@ var AiSdkRunner = class {
|
|
|
2370
2852
|
if (this.#closed) throw new Error("session is closed");
|
|
2371
2853
|
if (this.#pendingToolCalls.size > 0) throw new Error("cannot clear context while tool calls are outstanding");
|
|
2372
2854
|
this.#messages = [];
|
|
2373
|
-
this.#
|
|
2855
|
+
this.#localCommands.clear();
|
|
2374
2856
|
this.#emit({ type: "conversation_reset" });
|
|
2375
2857
|
});
|
|
2376
2858
|
this.#turnChain = run.then(() => void 0, () => void 0);
|
|
@@ -2452,7 +2934,7 @@ var AiSdkRunner = class {
|
|
|
2452
2934
|
if (this.#closed || this.#parked) return;
|
|
2453
2935
|
this.#closed = true;
|
|
2454
2936
|
this.#abort?.abort();
|
|
2455
|
-
this.#
|
|
2937
|
+
this.#localCommands.clear();
|
|
2456
2938
|
this.#pendingToolCalls.clear();
|
|
2457
2939
|
this.#dispatched.clear();
|
|
2458
2940
|
for (const { timer } of this.#pendingApprovals.values()) clearTimeout(timer);
|
|
@@ -2646,7 +3128,7 @@ var AiSdkRunner = class {
|
|
|
2646
3128
|
const agent = new ToolLoopAgent({
|
|
2647
3129
|
model: this.#model,
|
|
2648
3130
|
tools: this.#config.tools ?? {},
|
|
2649
|
-
instructions: this.#
|
|
3131
|
+
instructions: this.#instructions,
|
|
2650
3132
|
stopWhen: isStepCount(this.#config.maxSteps ?? 20)
|
|
2651
3133
|
});
|
|
2652
3134
|
const abort = new AbortController();
|
|
@@ -3347,6 +3829,22 @@ function createToolContext(options) {
|
|
|
3347
3829
|
})
|
|
3348
3830
|
});
|
|
3349
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
|
+
}
|
|
3350
3848
|
if (options.search) {
|
|
3351
3849
|
const search = options.search;
|
|
3352
3850
|
definitions.push({
|
|
@@ -3707,6 +4205,7 @@ function createEngineSession(options) {
|
|
|
3707
4205
|
webFetch,
|
|
3708
4206
|
onFileDelivered: options.capabilities?.deliverFiles === false || !isGranted("deliverFiles") ? void 0 : (file) => runner?.emitFileDelivered(file),
|
|
3709
4207
|
peers: options.config.peers,
|
|
4208
|
+
shells: options.config.shells,
|
|
3710
4209
|
selfId: () => runner?.id ?? "pending"
|
|
3711
4210
|
});
|
|
3712
4211
|
const declaredServers = options.profile?.session?.mcpServers;
|
|
@@ -3718,7 +4217,7 @@ function createEngineSession(options) {
|
|
|
3718
4217
|
runner = new AiSdkRunner({
|
|
3719
4218
|
...options.config,
|
|
3720
4219
|
languageModel: options.resolveModel(options.profile, options.config),
|
|
3721
|
-
instructions: options.profile?.session?.instructions
|
|
4220
|
+
instructions: composeInstructions(options.profile?.session?.instructions, options.instructions, options.config.instructions),
|
|
3722
4221
|
tools: context.tools,
|
|
3723
4222
|
vfs,
|
|
3724
4223
|
executor,
|
|
@@ -3954,9 +4453,12 @@ const claudeAdapter = {
|
|
|
3954
4453
|
};
|
|
3955
4454
|
return { available: "unknown" };
|
|
3956
4455
|
},
|
|
3957
|
-
createRunner({ config, restore, id }) {
|
|
4456
|
+
createRunner({ config, profile, restore, id }) {
|
|
3958
4457
|
if (restore) throw new Error("the Claude engine cannot rebuild a parked session");
|
|
3959
|
-
return new SessionRunner(
|
|
4458
|
+
return new SessionRunner({
|
|
4459
|
+
...config,
|
|
4460
|
+
instructions: composeInstructions(profile?.session?.instructions, config.instructions)
|
|
4461
|
+
}, id);
|
|
3960
4462
|
},
|
|
3961
4463
|
async listSessions({ dir, limit, offset }) {
|
|
3962
4464
|
return (await listSessions({
|
|
@@ -4771,6 +5273,7 @@ var CodexRunner = class {
|
|
|
4771
5273
|
createdAt;
|
|
4772
5274
|
#config;
|
|
4773
5275
|
#cwd;
|
|
5276
|
+
#instructions;
|
|
4774
5277
|
#log = new EventLog();
|
|
4775
5278
|
#subscribers = new SubscriberSet();
|
|
4776
5279
|
#status = "starting";
|
|
@@ -4783,7 +5286,7 @@ var CodexRunner = class {
|
|
|
4783
5286
|
#planType;
|
|
4784
5287
|
#resolvedEffort;
|
|
4785
5288
|
#queue = [];
|
|
4786
|
-
#
|
|
5289
|
+
#localCommands = new LocalCommandQueue((text, uuid, shell) => this.#emit(localCommandEvent(text, uuid, shell)));
|
|
4787
5290
|
#turnChain = Promise.resolve();
|
|
4788
5291
|
#activeTurn;
|
|
4789
5292
|
#connection;
|
|
@@ -4827,6 +5330,11 @@ var CodexRunner = class {
|
|
|
4827
5330
|
this.#threadMaterialized = config.resume !== void 0;
|
|
4828
5331
|
this.id = id;
|
|
4829
5332
|
this.createdAt = Date.now();
|
|
5333
|
+
this.#instructions = resolveInstructions(config.instructions, {
|
|
5334
|
+
sessionId: id,
|
|
5335
|
+
cwd: config.cwd,
|
|
5336
|
+
profile: config.profile
|
|
5337
|
+
});
|
|
4830
5338
|
}
|
|
4831
5339
|
#childEnv() {
|
|
4832
5340
|
return codexChildEnv(this.#config.env ?? process.env, this.#config.codexHome);
|
|
@@ -5037,28 +5545,16 @@ var CodexRunner = class {
|
|
|
5037
5545
|
type: "text",
|
|
5038
5546
|
text
|
|
5039
5547
|
});
|
|
5040
|
-
const context =
|
|
5041
|
-
this.#pendingLocalCommands = [];
|
|
5548
|
+
const context = this.#localCommands.take();
|
|
5042
5549
|
if (context) parts.unshift({
|
|
5043
5550
|
type: "text",
|
|
5044
5551
|
text: context
|
|
5045
5552
|
});
|
|
5046
5553
|
return withSkillItems(parts, this.#skillPaths);
|
|
5047
5554
|
}
|
|
5048
|
-
queueLocalCommand(
|
|
5555
|
+
queueLocalCommand(input) {
|
|
5049
5556
|
if (this.#closed) throw new Error("session is closed");
|
|
5050
|
-
|
|
5051
|
-
this.#pendingLocalCommands.push(text);
|
|
5052
|
-
this.#emit({
|
|
5053
|
-
type: "user_message",
|
|
5054
|
-
message: {
|
|
5055
|
-
role: "user",
|
|
5056
|
-
content: text
|
|
5057
|
-
},
|
|
5058
|
-
parentToolUseId: null,
|
|
5059
|
-
synthetic: true,
|
|
5060
|
-
uuid: randomUUID()
|
|
5061
|
-
});
|
|
5557
|
+
this.#localCommands.push(input);
|
|
5062
5558
|
}
|
|
5063
5559
|
resolvePermission(requestId, decision) {
|
|
5064
5560
|
const pending = this.#approvals.get(requestId);
|
|
@@ -5106,7 +5602,7 @@ var CodexRunner = class {
|
|
|
5106
5602
|
message: "the conversation was cleared"
|
|
5107
5603
|
}, "policy");
|
|
5108
5604
|
this.#resumedHistory = void 0;
|
|
5109
|
-
this.#
|
|
5605
|
+
this.#localCommands.clear();
|
|
5110
5606
|
this.#emit({
|
|
5111
5607
|
type: "conversation_reset",
|
|
5112
5608
|
sdkSessionId: this.#resumableThreadId()
|
|
@@ -5160,7 +5656,7 @@ var CodexRunner = class {
|
|
|
5160
5656
|
if (this.#closed) return;
|
|
5161
5657
|
this.#closed = true;
|
|
5162
5658
|
this.#queue.length = 0;
|
|
5163
|
-
this.#
|
|
5659
|
+
this.#localCommands.clear();
|
|
5164
5660
|
for (const [id, pending] of this.#approvals) this.#settleApproval(id, pending, {
|
|
5165
5661
|
behavior: "deny",
|
|
5166
5662
|
message: "Session closed"
|
|
@@ -5252,7 +5748,9 @@ var CodexRunner = class {
|
|
|
5252
5748
|
approvalsReviewer: APPROVALS_REVIEWER_BY_MODE[this.#permissionMode]
|
|
5253
5749
|
};
|
|
5254
5750
|
if (this.#model) options.model = this.#model;
|
|
5255
|
-
if (this.#
|
|
5751
|
+
if (this.#instructions !== void 0) options.developerInstructions = this.#instructions;
|
|
5752
|
+
const dynamic = [...this.#config.peers ? peerToolSpecs() : [], ...this.#config.shells ? shellToolSpecs() : []];
|
|
5753
|
+
if (dynamic.length) options.dynamicTools = dynamic.map((spec) => ({
|
|
5256
5754
|
type: "function",
|
|
5257
5755
|
...spec
|
|
5258
5756
|
}));
|
|
@@ -5674,16 +6172,30 @@ var CodexRunner = class {
|
|
|
5674
6172
|
async #answerServerRequest(method, params, wireId) {
|
|
5675
6173
|
const channel = APPROVAL_CHANNELS[method];
|
|
5676
6174
|
if (channel) return this.#requestApproval(channel, method, params, wireId);
|
|
5677
|
-
if (method === "item/tool/call"
|
|
6175
|
+
if (method === "item/tool/call") {
|
|
5678
6176
|
const call = params;
|
|
5679
|
-
const
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
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
|
+
}
|
|
5687
6199
|
}
|
|
5688
6200
|
throw new JsonRpcError(-32601, `workerdeck does not handle server request '${method}'`);
|
|
5689
6201
|
}
|
|
@@ -6391,6 +6903,7 @@ const codexAdapter = {
|
|
|
6391
6903
|
return new CodexRunner({
|
|
6392
6904
|
...config,
|
|
6393
6905
|
codexHome: profile?.codexHome,
|
|
6906
|
+
instructions: composeInstructions(profile?.session?.instructions, config.instructions),
|
|
6394
6907
|
connectFn: (options) => connectAppServer({
|
|
6395
6908
|
executable,
|
|
6396
6909
|
...options
|
|
@@ -6443,6 +6956,6 @@ function getEngineAdapter(engine) {
|
|
|
6443
6956
|
return ADAPTERS[engine ?? "claude"];
|
|
6444
6957
|
}
|
|
6445
6958
|
//#endregion
|
|
6446
|
-
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, 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, 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 };
|
|
6447
6960
|
|
|
6448
6961
|
//# sourceMappingURL=index.mjs.map
|