@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.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.#events.push(event);
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 = [...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 this.#events) this.#fold(event);
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
- #pendingLocalCommands = [];
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(result) {
1964
+ queueLocalCommand(input) {
1481
1965
  if (this.#closed) throw new Error("session is closed");
1482
- const text = localCommandTranscript(result);
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
- const context = localCommandContext(this.#pendingLocalCommands);
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.#pendingLocalCommands = [];
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.#pendingLocalCommands = [];
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
- if (!peers) return declared;
1671
- const tools = PEER_TOOL_NAMES.map((name) => tool(name, PEER_TOOL_SHAPES[name].description, PEER_TOOL_SHAPES[name].shape, async (args) => {
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.#pendingLocalCommands = [];
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
- #pendingLocalCommands = [];
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.#pendingLocalCommands = [...state.pendingLocalCommands ?? []];
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
- ...this.#pendingLocalCommands.length ? { pendingLocalCommands: [...this.#pendingLocalCommands] } : {}
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 = localCommandContext(this.#pendingLocalCommands);
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(result) {
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
- const text = localCommandTranscript(result);
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.#pendingLocalCommands = [];
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.#pendingLocalCommands = [];
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.#config.instructions,
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 ?? options.instructions ?? options.config.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(config, id);
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
- #pendingLocalCommands = [];
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 = localCommandContext(this.#pendingLocalCommands);
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(result) {
5555
+ queueLocalCommand(input) {
5049
5556
  if (this.#closed) throw new Error("session is closed");
5050
- const text = localCommandTranscript(result);
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.#pendingLocalCommands = [];
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.#pendingLocalCommands = [];
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.#config.peers) options.dynamicTools = peerToolSpecs().map((spec) => ({
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" && this.#config.peers) {
6175
+ if (method === "item/tool/call") {
5678
6176
  const call = params;
5679
- const output = await runPeerTool(this.#config.peers, this.id, call.tool, call.arguments);
5680
- return {
5681
- success: !output.isError,
5682
- contentItems: [{
5683
- type: "inputText",
5684
- text: output.text
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