@demicodes/agent 0.10.1 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,15 +1,14 @@
1
- import { a as cloneBlocks, i as applyTranscriptPatches, n as createWebSocketServerTransport, o as ProviderStreamError, r as AgentClient, s as isContextLengthExceeded, t as createWebSocketClientTransport } from "./websocket-transport-os0mfcLi.mjs";
2
- import { AbortError, abortable, asError, asRecord, asString, bytesToBase64, createId, delay, isAbortError, isRecord, noop, parseJsonOrString, safeJsonStringify, throwIfAborted, truncate } from "@demicodes/utils";
1
+ import { a as cloneBlocks, i as applyTranscriptPatches, n as createWebSocketServerTransport, r as AgentClient, t as createWebSocketClientTransport } from "./websocket-transport-Bod0puog.mjs";
2
+ import { AbortError, abortable, asError, asRecord, asString, createId, delay, isAbortError, isRecord, noop, parseJsonOrString, safeJsonStringify, throwIfAborted, truncate } from "@demicodes/utils";
3
3
  import { BashEnvironment, CommandRegistry, MAX_TIMEOUT_MS, heredocDelimiter, shellQuote } from "@demicodes/shell";
4
4
  import { providerRuntime } from "@demicodes/provider";
5
- import { modelAcceptsMediaType, sniffModelMediaType } from "@demicodes/core";
6
5
  //#region src/transcript.ts
7
6
  const DEFAULT_MODEL_TEXT_HEAD_CHARS = 8e3;
8
7
  const DEFAULT_MODEL_TEXT_TAIL_CHARS = 8e3;
9
8
  const IMAGE_BASE_TOKENS = 1600;
10
9
  const IMAGE_BYTES_PER_TOKEN = 1e3;
11
10
  const DOCUMENT_BYTES_PER_TOKEN = 4;
12
- var TranscriptLog = class {
11
+ var Transcript = class {
13
12
  blocks;
14
13
  idFactory;
15
14
  now;
@@ -24,7 +23,7 @@ var TranscriptLog = class {
24
23
  this.replayHeadChars = options.replayTextBounds?.headChars ?? DEFAULT_MODEL_TEXT_HEAD_CHARS;
25
24
  this.replayTailChars = options.replayTextBounds?.tailChars ?? DEFAULT_MODEL_TEXT_TAIL_CHARS;
26
25
  }
27
- toJSON() {
26
+ snapshot() {
28
27
  return { blocks: structuredClone(this.blocks) };
29
28
  }
30
29
  /** Monotonic revision, advanced once per drained patch batch. */
@@ -149,22 +148,6 @@ var TranscriptLog = class {
149
148
  }
150
149
  return null;
151
150
  }
152
- /**
153
- * Drops the blocks from `index` onward, returning true when anything went.
154
- * Used to unwind a turn to its resume point before re-inferring.
155
- */
156
- truncateFrom(index) {
157
- let changed = false;
158
- for (let i = this.blocks.length - 1; i >= index; i -= 1) {
159
- this.blocks.splice(i, 1);
160
- this.record({
161
- op: "remove",
162
- path: ["blocks", i]
163
- });
164
- changed = true;
165
- }
166
- return changed;
167
- }
168
151
  applyProviderEvent(model, event) {
169
152
  switch (event.type) {
170
153
  case "thinking_start": return this.appendThinking(model, "");
@@ -189,7 +172,7 @@ var TranscriptLog = class {
189
172
  status: "executing",
190
173
  streamingOutput: [],
191
174
  output: [],
192
- view: null
175
+ metadata: null
193
176
  });
194
177
  case "response": return this.appendBlock({
195
178
  type: "response",
@@ -204,20 +187,19 @@ var TranscriptLog = class {
204
187
  createdAt: this.now(),
205
188
  model,
206
189
  message: event.message,
207
- code: event.code,
208
- ...event.diagnostics ? { diagnostics: event.diagnostics } : {}
190
+ code: event.code
209
191
  });
210
192
  case "abort": return this.pushAbort(model);
211
193
  }
212
194
  }
213
- completeToolCall(toolUseId, output, isError = false, view = null) {
195
+ completeToolCall(toolUseId, output, isError = false, metadata = null) {
214
196
  const index = findPendingToolCallIndex(this.blocks, toolUseId);
215
197
  if (index === null) return null;
216
198
  const block = this.blocks[index];
217
199
  block.status = isError ? "error" : "completed";
218
200
  block.output = output;
219
201
  block.streamingOutput = [];
220
- block.view = view;
202
+ block.metadata = metadata;
221
203
  this.recordBlockReplace(index);
222
204
  return block;
223
205
  }
@@ -396,15 +378,9 @@ var TranscriptLog = class {
396
378
  * history), it anchors the estimate and only blocks streamed after it are
397
379
  * char-estimated; otherwise the whole replay window is estimated at ~4
398
380
  * chars/token with fixed weights for images and documents.
399
- *
400
- * When `contextWindow` is given, an anchor larger than the window is
401
- * discarded: a single request's usage physically cannot exceed the window,
402
- * so such a value is a provider violation of the response-usage contract
403
- * (e.g. a turn-cumulative total) and would poison the estimate.
404
381
  */
405
- estimateContextTokens(contextWindow) {
406
- let anchor = this.usageAnchor();
407
- if (anchor !== null && contextWindow !== void 0 && contextWindow > 0 && anchor.tokens > contextWindow) anchor = null;
382
+ estimateContextTokens() {
383
+ const anchor = this.usageAnchor();
408
384
  if (anchor === null) return this.replayableBlocks().reduce((total, block) => total + estimateBlockTokens(block), 0);
409
385
  let total = anchor.tokens;
410
386
  for (let i = anchor.blockIndex + 1; i < this.blocks.length; i += 1) total += estimateBlockTokens(this.blocks[i]);
@@ -555,7 +531,6 @@ function stringifyUserContent(content) {
555
531
  switch (content.type) {
556
532
  case "text": return content.text;
557
533
  case "image": return content.source.type === "url" ? content.source.url : content.source.mediaType;
558
- case "video": return content.source.type === "url" ? content.source.url : content.source.mediaType;
559
534
  case "document": return `${content.source.fileName} ${content.source.mediaType}`;
560
535
  case "reference": return content.reference;
561
536
  }
@@ -564,7 +539,6 @@ function stringifyToolResult(content) {
564
539
  switch (content.type) {
565
540
  case "text": return content.text;
566
541
  case "image": return content.source.mediaType;
567
- case "video": return content.source.mediaType;
568
542
  }
569
543
  }
570
544
  function boundUserContent(content, headChars, tailChars) {
@@ -620,57 +594,6 @@ function retryDelayMs(policy, attempt, retryAfterMs) {
620
594
  return Math.floor(Math.random() * ceiling);
621
595
  }
622
596
  //#endregion
623
- //#region src/recovery.ts
624
- /**
625
- * Whether a block is a leftover of the failed attempt that nobody can have acted on.
626
- *
627
- * Everything else has to be assumed acted on. Transcript blocks stream outward as
628
- * they are produced and products turn them into effects that cannot be recalled —
629
- * rendering them, posting them to a chat, executing the tool they describe. A tool
630
- * call counts whatever its status: one still marked executing outlived the process
631
- * that was running it, so whether its effect landed is unknown, and unknown has to
632
- * be treated as landed. An abort block is history the user created, not a leftover.
633
- *
634
- * Thinking is the interesting case: products display it, but nothing keys off it and
635
- * a rerun simply reasons again, so it is discardable. A `response` is not — it
636
- * records a provider request that did complete, and its usage anchors the context
637
- * estimate for everything after it.
638
- */
639
- function isDiscardableLeftover(block) {
640
- switch (block.type) {
641
- case "thinking":
642
- case "redacted_thinking":
643
- case "error": return true;
644
- case "text": return block.text.trim().length === 0;
645
- default: return false;
646
- }
647
- }
648
- /**
649
- * Finds how far back an unfinished turn can be unwound before re-inferring.
650
- *
651
- * This is the single decision behind both recovery paths. A transient provider
652
- * failure and a human asking to continue a dead round ask the same question — how
653
- * do we finish this turn — and the answer depends only on what has already left
654
- * the process, never on which of the two asked or on how the turn died.
655
- */
656
- function findResumePoint(blocks) {
657
- for (let i = blocks.length - 1; i >= 0; i -= 1) {
658
- const block = blocks[i];
659
- if (block.type === "user") return {
660
- cut: i + 1,
661
- isFullRerun: true
662
- };
663
- if (!isDiscardableLeftover(block)) return {
664
- cut: i + 1,
665
- isFullRerun: false
666
- };
667
- }
668
- return {
669
- cut: blocks.length,
670
- isFullRerun: false
671
- };
672
- }
673
- //#endregion
674
597
  //#region src/yield-scheduler.ts
675
598
  /**
676
599
  * Tracks scheduled `yield` wakeups and their timers. This class owns only the
@@ -689,15 +612,14 @@ var YieldScheduler = class {
689
612
  return this.pending.length > 0;
690
613
  }
691
614
  /** Registers a new (unarmed) wakeup and returns its id. */
692
- schedule(durationMs, metadata) {
615
+ schedule(durationMs) {
693
616
  const id = this.idFactory();
694
617
  this.pending.push({
695
618
  id,
696
619
  durationMs,
697
620
  timer: null,
698
621
  dueAt: null,
699
- armed: false,
700
- metadata
622
+ armed: false
701
623
  });
702
624
  return id;
703
625
  }
@@ -708,7 +630,7 @@ var YieldScheduler = class {
708
630
  if (wakeup.armed) continue;
709
631
  wakeup.armed = true;
710
632
  wakeup.dueAt = now + wakeup.durationMs;
711
- wakeup.timer = setTimeout(() => this.onFire(wakeup.id, wakeup.metadata), wakeup.durationMs);
633
+ wakeup.timer = setTimeout(() => this.onFire(wakeup.id), wakeup.durationMs);
712
634
  }
713
635
  }
714
636
  /** Removes the wakeup with `wakeupId` (clearing its timer); returns whether it existed. */
@@ -867,6 +789,21 @@ function buildCompactionSummaryRequest(rendered, context) {
867
789
  };
868
790
  }
869
791
  //#endregion
792
+ //#region src/provider-stream-error.ts
793
+ /** Error carrying a provider stream's error code (e.g. `context_length_exceeded`). */
794
+ var ProviderStreamError = class extends Error {
795
+ code;
796
+ constructor(message, code) {
797
+ super(message);
798
+ this.name = "ProviderStreamError";
799
+ this.code = code;
800
+ }
801
+ };
802
+ /** Whether an error is a provider stream error reporting the context window was exceeded. */
803
+ function isContextLengthExceeded(error) {
804
+ return error instanceof ProviderStreamError && error.code === "context_length_exceeded";
805
+ }
806
+ //#endregion
870
807
  //#region src/compaction-controller.ts
871
808
  /**
872
809
  * Owns the compaction algorithm: pick a window of old transcript blocks, summarize
@@ -883,9 +820,9 @@ var CompactionController = class {
883
820
  const contextWindow = targetModel.model.contextWindow;
884
821
  if (contextWindow <= 0) return;
885
822
  const threshold = Math.floor(contextWindow * this.host.thresholdRatio);
886
- if (this.host.transcript.estimateContextTokens(contextWindow) < threshold) return;
823
+ if (this.host.transcript.estimateContextTokens() < threshold) return;
887
824
  await this.host.runWithCompactingPhase(async () => {
888
- for (let attempt = 0; attempt < 8 && this.host.transcript.estimateContextTokens(contextWindow) >= threshold; attempt += 1) if (!await this.run()) break;
825
+ for (let attempt = 0; attempt < 8 && this.host.transcript.estimateContextTokens() >= threshold; attempt += 1) if (!await this.run()) break;
889
826
  });
890
827
  }
891
828
  /** Runs one compaction pass before a turn when the current model is over threshold. */
@@ -893,7 +830,7 @@ var CompactionController = class {
893
830
  const contextWindow = this.host.model.model.contextWindow;
894
831
  if (contextWindow <= 0) return;
895
832
  const threshold = Math.floor(contextWindow * this.host.thresholdRatio);
896
- if (this.host.transcript.estimateContextTokens(contextWindow) < threshold) return;
833
+ if (this.host.transcript.estimateContextTokens() < threshold) return;
897
834
  await this.host.runWithCompactingPhase(() => this.run());
898
835
  }
899
836
  /** Runs one compaction pass; returns whether it compacted anything. */
@@ -923,7 +860,7 @@ var CompactionController = class {
923
860
  return false;
924
861
  }
925
862
  async generateSummary(blocks) {
926
- const rendered = renderItemsForSummary(new TranscriptLog(blocks).collectInferenceItems());
863
+ const rendered = renderItemsForSummary(new Transcript(blocks).collectInferenceItems());
927
864
  const policy = this.host.retryPolicy;
928
865
  for (let attempt = 1;; attempt += 1) {
929
866
  const request = buildCompactionSummaryRequest(rendered, {
@@ -942,16 +879,10 @@ var CompactionController = class {
942
879
  if (event.type === "text_delta") summary += event.text;
943
880
  if (event.type === "abort") throw new AbortError();
944
881
  if (event.type === "error") {
945
- const diagnostics = {
946
- source: event.diagnostics?.source ?? "unknown",
947
- ...event.diagnostics,
948
- clientRequestId: request.requestId
949
- };
950
- if (!(summary.length === 0 && attempt < policy.maxAttempts && isRetryableCode(policy, event.code))) throw new ProviderStreamError(event.message, event.code, diagnostics);
882
+ if (!(summary.length === 0 && attempt < policy.maxAttempts && isRetryableCode(policy, event.code))) throw new ProviderStreamError(event.message, event.code);
951
883
  transient = {
952
884
  code: event.code,
953
- retryAfterMs: event.retryAfterMs ?? null,
954
- diagnostics
885
+ retryAfterMs: event.retryAfterMs ?? null
955
886
  };
956
887
  break;
957
888
  }
@@ -962,8 +893,7 @@ var CompactionController = class {
962
893
  type: "retry_scheduled",
963
894
  attempt,
964
895
  delayMs,
965
- code: transient.code,
966
- diagnostics: transient.diagnostics
896
+ code: transient.code
967
897
  });
968
898
  await abortable(delay(delayMs), request.cancel);
969
899
  }
@@ -986,7 +916,6 @@ var ProviderTurnLoop = class {
986
916
  let autoCompactions = 0;
987
917
  while (true) {
988
918
  throwIfAborted(this.host.currentSignal());
989
- await this.host.materializePendingSteers();
990
919
  const steerContinuationBeforeStream = this.host.steerContinuationCount;
991
920
  const shouldAutoRecover = await this.streamProviderOnce();
992
921
  throwIfAborted(this.host.currentSignal());
@@ -1011,9 +940,8 @@ var ProviderTurnLoop = class {
1011
940
  }
1012
941
  /**
1013
942
  * Streams one provider response, silently retrying transient failures
1014
- * (rate_limit/overloaded) with backoff. A retry is taken only when everything
1015
- * the failed attempt put in the transcript can be unwound — the same question
1016
- * `resume` asks, so an automatic recovery and a human-driven one never disagree.
943
+ * (rate_limit/overloaded) with backoff but only while the attempt has
944
+ * produced no transcript content, so a retry can never duplicate output.
1017
945
  * The turn stays in provider_streaming across backoff waits so steers keep
1018
946
  * queueing instead of being rejected.
1019
947
  */
@@ -1028,8 +956,7 @@ var ProviderTurnLoop = class {
1028
956
  type: "retry_scheduled",
1029
957
  attempt: outcome.attempt,
1030
958
  delayMs: outcome.delayMs,
1031
- code: outcome.code,
1032
- diagnostics: outcome.diagnostics
959
+ code: outcome.code
1033
960
  });
1034
961
  await abortable(delay(outcome.delayMs), this.host.currentSignal());
1035
962
  }
@@ -1039,47 +966,26 @@ var ProviderTurnLoop = class {
1039
966
  }
1040
967
  async streamAttempt(attempt, policy) {
1041
968
  const request = this.buildInferenceRequest();
1042
- const attemptStart = this.host.transcript.blocks.length;
1043
969
  const run = this.host.provider.run(request);
1044
970
  let shouldAutoRecover = false;
1045
- let hasPendingThinkingStart = false;
971
+ let produced = false;
1046
972
  this.host.setActiveProviderRun(run);
1047
973
  try {
1048
974
  for await (const event of this.host.streamProvider(request, run)) {
1049
975
  throwIfAborted(request.cancel);
1050
976
  if (event.type === "abort") throw new AbortError();
1051
- if (event.type === "thinking_start") {
1052
- hasPendingThinkingStart = true;
1053
- continue;
1054
- }
1055
977
  if (event.type === "error") {
1056
- const diagnostics = {
1057
- source: event.diagnostics?.source ?? "unknown",
1058
- ...event.diagnostics,
1059
- clientRequestId: request.requestId
1060
- };
1061
- const errorEvent = {
1062
- ...event,
1063
- diagnostics
978
+ if (!produced && attempt < policy.maxAttempts && isRetryableCode(policy, event.code)) return {
979
+ type: "retry",
980
+ attempt,
981
+ delayMs: retryDelayMs(policy, attempt, event.retryAfterMs ?? null),
982
+ code: event.code
1064
983
  };
1065
- if (findResumePoint(this.host.transcript.blocks).cut <= attemptStart && attempt < policy.maxAttempts && isRetryableCode(policy, errorEvent.code)) {
1066
- if (this.host.transcript.truncateFrom(attemptStart)) await this.host.commitTranscript();
1067
- return {
1068
- type: "retry",
1069
- attempt,
1070
- delayMs: retryDelayMs(policy, attempt, errorEvent.retryAfterMs ?? null),
1071
- code: errorEvent.code,
1072
- diagnostics: errorEvent.diagnostics
1073
- };
1074
- }
1075
- await this.applyProviderEvent(errorEvent);
1076
- throw new ProviderStreamError(errorEvent.message, errorEvent.code, errorEvent.diagnostics);
1077
- }
1078
- if (hasPendingThinkingStart) {
1079
- await this.applyProviderEvent({ type: "thinking_start" });
1080
- hasPendingThinkingStart = false;
984
+ await this.applyProviderEvent(event);
985
+ throw new ProviderStreamError(event.message, event.code);
1081
986
  }
1082
987
  await this.applyProviderEvent(event);
988
+ produced = true;
1083
989
  if (event.type === "response" && this.isUsageNearLimit(event.usage)) shouldAutoRecover = true;
1084
990
  }
1085
991
  } finally {
@@ -1117,7 +1023,7 @@ var ProviderTurnLoop = class {
1117
1023
  }
1118
1024
  const input = parseJsonOrString(toolCall.input);
1119
1025
  const result = await this.invokeToolAsResult(tool, toolCall.toolUseId, input);
1120
- this.host.transcript.completeToolCall(toolCall.toolUseId, result.output, result.isError ?? false, result.view ?? null);
1026
+ this.host.transcript.completeToolCall(toolCall.toolUseId, result.output, result.isError ?? false, result.metadata ?? result.continuation ?? null);
1121
1027
  await this.host.runtime.lifecycle?.({
1122
1028
  type: "after_tool_call",
1123
1029
  agentSessionId: this.host.agentSessionId,
@@ -1125,8 +1031,7 @@ var ProviderTurnLoop = class {
1125
1031
  transcript: this.host.transcript,
1126
1032
  toolCallId: toolCall.toolUseId,
1127
1033
  toolName: toolCall.toolName,
1128
- result,
1129
- metadata: this.host.metadata
1034
+ result
1130
1035
  });
1131
1036
  await this.host.commitTranscript();
1132
1037
  if (!options.deferSteerMaterialization) await this.host.materializeSteersArrivedSince(steerContinuationBeforeTool);
@@ -1149,7 +1054,6 @@ var ProviderTurnLoop = class {
1149
1054
  model: this.host.model,
1150
1055
  toolCallId,
1151
1056
  signal,
1152
- metadata: this.host.metadata,
1153
1057
  emitProgress: (progress) => {
1154
1058
  this.host.emit({
1155
1059
  type: "tool_progress",
@@ -1172,10 +1076,7 @@ var ProviderTurnLoop = class {
1172
1076
  text: `Tool failed: ${normalized.message}`
1173
1077
  }],
1174
1078
  isError: true,
1175
- view: {
1176
- kind: "tool_error",
1177
- error: normalized.message
1178
- }
1079
+ metadata: { error: normalized.message }
1179
1080
  };
1180
1081
  }
1181
1082
  }
@@ -1183,8 +1084,7 @@ var ProviderTurnLoop = class {
1183
1084
  return this.host.runtime.tools({
1184
1085
  agentSessionId: this.host.agentSessionId,
1185
1086
  state: this.host.agentState,
1186
- cwd: this.host.cwd,
1187
- metadata: this.host.metadata
1087
+ cwd: this.host.cwd
1188
1088
  });
1189
1089
  }
1190
1090
  buildInferenceRequest() {
@@ -1238,9 +1138,6 @@ var AgentSession = class AgentSession {
1238
1138
  listeners = /* @__PURE__ */ new Set();
1239
1139
  pendingActions = [];
1240
1140
  queued = [];
1241
- get modelSelection() {
1242
- return this.model;
1243
- }
1244
1141
  transcriptLog;
1245
1142
  agentState;
1246
1143
  currentPhase = "idle";
@@ -1250,7 +1147,6 @@ var AgentSession = class AgentSession {
1250
1147
  activeTurnId = null;
1251
1148
  activeTurnPhase = null;
1252
1149
  activeProviderRun = null;
1253
- activeMetadata = null;
1254
1150
  steerQueue = new PendingSteerQueue();
1255
1151
  yields;
1256
1152
  compaction;
@@ -1262,27 +1158,22 @@ var AgentSession = class AgentSession {
1262
1158
  persistTimer = null;
1263
1159
  persistDirty = false;
1264
1160
  /**
1265
- * Restores a session from a checkpoint. Ownership of the checkpoint (including
1161
+ * Restores a session from a snapshot. Ownership of the snapshot (including
1266
1162
  * `state`) transfers to the session: the caller must not mutate it afterwards.
1267
1163
  * Passing state by reference — not a clone — lets the caller share the same
1268
1164
  * object with harness closures (host/commands), keeping one live state.
1269
1165
  */
1270
- static fromCheckpoint(params, options = {}) {
1271
- if (params.checkpoint.harnessName !== params.runtime.harnessName) throw new Error(`AgentSession: checkpoint harness "${params.checkpoint.harnessName}" does not match "${params.runtime.harnessName}"`);
1272
- const checkpoint = params.checkpoint;
1273
- const session = new AgentSession({
1166
+ static fromSnapshot(params, options = {}) {
1167
+ if (params.snapshot.harnessName !== params.runtime.harnessName) throw new Error(`AgentSession: snapshot harness "${params.snapshot.harnessName}" does not match "${params.runtime.harnessName}"`);
1168
+ const snapshot = params.snapshot;
1169
+ return new AgentSession({
1274
1170
  provider: params.provider,
1275
- model: checkpoint.model,
1276
- cwd: checkpoint.cwd,
1171
+ model: snapshot.model,
1172
+ cwd: snapshot.cwd,
1277
1173
  runtime: params.runtime,
1278
- transcript: checkpoint.transcript,
1279
- state: checkpoint.state
1174
+ transcript: snapshot.transcript,
1175
+ state: snapshot.state
1280
1176
  }, options);
1281
- for (const toolCall of session.transcriptLog.pendingToolCalls()) session.transcriptLog.completeToolCall(toolCall.toolUseId, [{
1282
- type: "text",
1283
- text: `Tool call interrupted: ${toolCall.toolName} (the process died before a result was recorded)`
1284
- }], true);
1285
- return session;
1286
1177
  }
1287
1178
  constructor(params, options = {}) {
1288
1179
  this.provider = params.provider;
@@ -1292,8 +1183,8 @@ var AgentSession = class AgentSession {
1292
1183
  this.agentState = params.state === void 0 ? params.runtime.initialState() : params.state;
1293
1184
  this.agentSessionId = options.agentSessionId ?? createId();
1294
1185
  this.idFactory = options.idFactory ?? createId;
1295
- this.yields = new YieldScheduler(this.idFactory, (wakeupId, metadata) => {
1296
- this.deliverYieldWakeup(wakeupId, metadata);
1186
+ this.yields = new YieldScheduler(this.idFactory, (wakeupId) => {
1187
+ this.deliverYieldWakeup(wakeupId);
1297
1188
  });
1298
1189
  this.store = options.store;
1299
1190
  this.persistIntervalMs = options.persistIntervalMs ?? DEFAULT_PERSIST_INTERVAL_MS;
@@ -1304,7 +1195,7 @@ var AgentSession = class AgentSession {
1304
1195
  idFactory: this.idFactory,
1305
1196
  now: options.now
1306
1197
  };
1307
- this.transcriptLog = params.transcript instanceof TranscriptLog ? params.transcript : new TranscriptLog(params.transcript?.blocks ?? [], transcriptOptions);
1198
+ this.transcriptLog = params.transcript instanceof Transcript ? params.transcript : new Transcript(params.transcript?.blocks ?? [], transcriptOptions);
1308
1199
  const self = this;
1309
1200
  const compactionHost = {
1310
1201
  get transcript() {
@@ -1371,9 +1262,6 @@ var AgentSession = class AgentSession {
1371
1262
  get retryPolicy() {
1372
1263
  return self.retryPolicy;
1373
1264
  },
1374
- get metadata() {
1375
- return self.activeMetadata;
1376
- },
1377
1265
  currentSignal: () => self.currentSignal(),
1378
1266
  currentTurnId: () => self.currentTurnId(),
1379
1267
  nextRequestId: () => self.idFactory(),
@@ -1391,8 +1279,7 @@ var AgentSession = class AgentSession {
1391
1279
  runWithCompactingPhase: (fn) => self.runWithCompactingPhase(fn),
1392
1280
  commitTranscript: () => self.commitTranscript(),
1393
1281
  emit: (event) => self.emit(event),
1394
- materializeSteersArrivedSince: (count) => self.materializePendingSteersArrivedSince(count),
1395
- materializePendingSteers: () => self.materializePendingSteersForCurrentTurn()
1282
+ materializeSteersArrivedSince: (count) => self.materializePendingSteersArrivedSince(count)
1396
1283
  };
1397
1284
  this.turnLoop = new ProviderTurnLoop(turnLoopHost);
1398
1285
  }
@@ -1402,46 +1289,27 @@ var AgentSession = class AgentSession {
1402
1289
  type: "send",
1403
1290
  id,
1404
1291
  content,
1405
- metadata: cloneMetadata(options.metadata),
1406
1292
  resolve: noop,
1407
1293
  reject: noop
1408
1294
  });
1409
1295
  }
1410
- /**
1411
- * Discards the whole latest turn and runs it again from the user's input.
1412
- *
1413
- * This is "regenerate": it is only meaningful when the caller wants a different
1414
- * answer to the same question and knows the turn's effects can be repeated. It is
1415
- * NOT the way to recover a failed turn — use `resume`, which unwinds only as far
1416
- * as is safe.
1417
- */
1418
- retry(options = {}) {
1296
+ retry() {
1419
1297
  return this.enqueue({
1420
1298
  type: "retry",
1421
- metadata: cloneMetadata(options.metadata),
1422
1299
  resolve: noop,
1423
1300
  reject: noop
1424
1301
  });
1425
1302
  }
1426
- /**
1427
- * Finishes a turn that did not finish, after an abort or a terminal provider
1428
- * error. Unwinds to the turn's resume point — dropping the failed attempt's
1429
- * leftovers, keeping everything that already left the process — and re-infers
1430
- * from there. Callers do not choose the granularity and do not need to know how
1431
- * the turn died.
1432
- */
1433
- resume(options = {}) {
1303
+ resume() {
1434
1304
  return this.enqueue({
1435
1305
  type: "resume",
1436
- metadata: cloneMetadata(options.metadata),
1437
1306
  resolve: noop,
1438
1307
  reject: noop
1439
1308
  });
1440
1309
  }
1441
- compact(options = {}) {
1310
+ compact() {
1442
1311
  return this.enqueue({
1443
1312
  type: "compact",
1444
- metadata: cloneMetadata(options.metadata),
1445
1313
  resolve: noop,
1446
1314
  reject: noop
1447
1315
  });
@@ -1544,8 +1412,8 @@ var AgentSession = class AgentSession {
1544
1412
  canAbortAgain: false
1545
1413
  };
1546
1414
  }
1547
- scheduleYieldWakeup(durationMs, metadata = this.activeMetadata) {
1548
- const wakeupId = this.yields.schedule(durationMs, metadata);
1415
+ scheduleYieldWakeup(durationMs) {
1416
+ const wakeupId = this.yields.schedule(durationMs);
1549
1417
  return {
1550
1418
  output: [{
1551
1419
  type: "text",
@@ -1555,7 +1423,7 @@ var AgentSession = class AgentSession {
1555
1423
  `durationMs: ${durationMs}`
1556
1424
  ].join("\n")
1557
1425
  }],
1558
- view: {
1426
+ metadata: {
1559
1427
  kind: "yield_wakeup",
1560
1428
  wakeupId,
1561
1429
  durationMs
@@ -1704,13 +1572,13 @@ var AgentSession = class AgentSession {
1704
1572
  action.resolve();
1705
1573
  }
1706
1574
  }
1707
- async deliverYieldWakeup(wakeupId, metadata) {
1575
+ async deliverYieldWakeup(wakeupId) {
1708
1576
  if (!this.yields.take(wakeupId)) return;
1709
1577
  const content = [{
1710
1578
  type: "text",
1711
1579
  text: "Scheduled yield wakeup fired. Continue the previous work and inspect any running command with shell_status when needed."
1712
1580
  }];
1713
- if (metadata === this.activeMetadata && this.canAcceptInternalSteer()) try {
1581
+ if (this.canAcceptInternalSteer()) try {
1714
1582
  await this.steerInternal(content, wakeupId, true);
1715
1583
  return;
1716
1584
  } catch (error) {
@@ -1719,7 +1587,7 @@ var AgentSession = class AgentSession {
1719
1587
  error: asError(error)
1720
1588
  });
1721
1589
  }
1722
- this.enqueueHiddenSend(content, metadata);
1590
+ this.enqueueHiddenSend(content);
1723
1591
  }
1724
1592
  canAcceptInternalSteer() {
1725
1593
  try {
@@ -1751,12 +1619,11 @@ var AgentSession = class AgentSession {
1751
1619
  hidden
1752
1620
  });
1753
1621
  }
1754
- enqueueHiddenSend(content, metadata) {
1622
+ enqueueHiddenSend(content) {
1755
1623
  this.enqueue({
1756
1624
  type: "send",
1757
1625
  id: this.idFactory(),
1758
1626
  content,
1759
- metadata,
1760
1627
  hidden: true,
1761
1628
  resolve: noop,
1762
1629
  reject: noop
@@ -1765,13 +1632,13 @@ var AgentSession = class AgentSession {
1765
1632
  steerDelivery() {
1766
1633
  if (!this.activeTurnId || !this.currentAbortController || !this.activeTurnPhase) throw new Error("AgentSession: no active turn to steer");
1767
1634
  if (this.currentAbortController.signal.aborted) throw new Error("AgentSession: active turn is aborted");
1768
- if (this.activeTurnPhase === "finalizing") throw new Error("AgentSession: active turn cannot accept steering while finalizing");
1635
+ if (this.activeTurnPhase === "compacting" || this.activeTurnPhase === "finalizing") throw new Error(`AgentSession: active turn cannot accept steering while ${this.activeTurnPhase}`);
1769
1636
  const run = this.activeProviderRun;
1770
1637
  if (run?.steer) return {
1771
1638
  type: "provider",
1772
1639
  run
1773
1640
  };
1774
- if (this.activeTurnPhase === "tool_executing" || this.activeTurnPhase === "provider_streaming" || this.activeTurnPhase === "compacting") return { type: "next_provider_continuation" };
1641
+ if (this.activeTurnPhase === "tool_executing" || this.activeTurnPhase === "provider_streaming") return { type: "next_provider_continuation" };
1775
1642
  throw new Error("AgentSession: active turn cannot accept steering now");
1776
1643
  }
1777
1644
  enqueue(action) {
@@ -1804,7 +1671,6 @@ var AgentSession = class AgentSession {
1804
1671
  if (action.type === "send") this.removeQueuedMessage(action.id);
1805
1672
  this.currentAbortController = new AbortController();
1806
1673
  this.activeTurnId = action.type === "send" ? action.id : this.idFactory();
1807
- this.activeMetadata = action.metadata;
1808
1674
  this.abortRecorded = false;
1809
1675
  try {
1810
1676
  await this.executeAction(action);
@@ -1846,7 +1712,6 @@ var AgentSession = class AgentSession {
1846
1712
  this.discardPendingSteersForCurrentTurn();
1847
1713
  this.activeTurnPhase = "finalizing";
1848
1714
  this.activeProviderRun = null;
1849
- this.activeMetadata = null;
1850
1715
  this.currentAbortController = null;
1851
1716
  this.activeTurnId = null;
1852
1717
  this.activeTurnPhase = null;
@@ -1880,7 +1745,6 @@ var AgentSession = class AgentSession {
1880
1745
  this.setPhase("compacting");
1881
1746
  this.activeTurnPhase = "compacting";
1882
1747
  await this.compaction.run();
1883
- await this.materializePendingSteersForCurrentTurn();
1884
1748
  return;
1885
1749
  }
1886
1750
  }
@@ -1921,8 +1785,7 @@ var AgentSession = class AgentSession {
1921
1785
  agentSessionId: this.agentSessionId,
1922
1786
  state: this.agentState,
1923
1787
  transcript: this.transcriptLog,
1924
- content,
1925
- metadata: this.activeMetadata
1788
+ content
1926
1789
  });
1927
1790
  const resolvedContent = await this.resolveReferences(content);
1928
1791
  await this.applyPendingModelSwitch();
@@ -1941,8 +1804,7 @@ var AgentSession = class AgentSession {
1941
1804
  agentSessionId: this.agentSessionId,
1942
1805
  cwd: this.cwd,
1943
1806
  transcript: this.transcriptLog,
1944
- signal,
1945
- metadata: this.activeMetadata
1807
+ signal
1946
1808
  }, content)), signal);
1947
1809
  }
1948
1810
  async executeRetry() {
@@ -1954,29 +1816,15 @@ var AgentSession = class AgentSession {
1954
1816
  agentSessionId: this.agentSessionId,
1955
1817
  state: this.agentState,
1956
1818
  transcript: this.transcriptLog,
1957
- reason: "retry",
1958
- metadata: this.activeMetadata
1819
+ reason: "retry"
1959
1820
  });
1960
1821
  await this.commitTranscript();
1961
1822
  await this.applyPendingModelSwitch();
1962
1823
  await this.compaction.preflight();
1963
1824
  await this.turnLoop.run();
1964
1825
  }
1965
- /**
1966
- * Finishes an unfinished turn. Unwinds to its resume point — dropping the failed
1967
- * attempt's leftovers, keeping everything that already left the process — and
1968
- * re-infers from there. When the whole turn turns out to be discardable this is
1969
- * a plain rerun, which spares the model a continuation boundary attached to a
1970
- * stub of its own aborted output.
1971
- */
1972
1826
  async executeResume() {
1973
- const { cut, isFullRerun } = findResumePoint(this.transcriptLog.blocks);
1974
- if (isFullRerun) {
1975
- await this.executeRetry();
1976
- return;
1977
- }
1978
1827
  await this.applyPendingModelSwitch();
1979
- this.transcriptLog.truncateFrom(cut);
1980
1828
  this.transcriptLog.markLatestAbortResumed();
1981
1829
  this.transcriptLog.pushResumeTurn(this.currentTurnId(), this.model);
1982
1830
  await this.commitTranscript();
@@ -2004,8 +1852,7 @@ var AgentSession = class AgentSession {
2004
1852
  agentSessionId: this.agentSessionId,
2005
1853
  state: this.agentState,
2006
1854
  cwd: this.cwd,
2007
- transcript: this.transcriptLog,
2008
- metadata: this.activeMetadata
1855
+ transcript: this.transcriptLog
2009
1856
  };
2010
1857
  }
2011
1858
  currentSignal() {
@@ -2095,7 +1942,7 @@ var AgentSession = class AgentSession {
2095
1942
  /**
2096
1943
  * Publishes transcript changes: drains the mutation journal into a patch
2097
1944
  * event (O(changed content), not O(transcript)) and schedules a throttled
2098
- * checkpoint write. Boundaries (action end, abort, dispose) flush the write.
1945
+ * snapshot write. Boundaries (action end, abort, dispose) flush the write.
2099
1946
  */
2100
1947
  async commitTranscript() {
2101
1948
  const drained = this.transcriptLog.takePatches();
@@ -2112,7 +1959,7 @@ var AgentSession = class AgentSession {
2112
1959
  if (this.persistTimer) return;
2113
1960
  this.persistTimer = setTimeout(() => {
2114
1961
  this.persistTimer = null;
2115
- this.persistCheckpoint().catch((error) => {
1962
+ this.persistSnapshot().catch((error) => {
2116
1963
  this.emit({
2117
1964
  type: "error",
2118
1965
  error: asError(error)
@@ -2120,11 +1967,11 @@ var AgentSession = class AgentSession {
2120
1967
  });
2121
1968
  }, this.persistIntervalMs);
2122
1969
  }
2123
- async persistCheckpoint() {
1970
+ async persistSnapshot() {
2124
1971
  if (!this.store || !this.persistDirty) return;
2125
1972
  this.persistDirty = false;
2126
- await this.store.saveCheckpoint({
2127
- transcript: this.transcriptLog.toJSON(),
1973
+ await this.store.saveSnapshot({
1974
+ transcript: this.transcriptLog.snapshot(),
2128
1975
  state: structuredClone(this.agentState),
2129
1976
  phase: this.currentPhase,
2130
1977
  queue: structuredClone(this.queuedMessages()),
@@ -2138,7 +1985,7 @@ var AgentSession = class AgentSession {
2138
1985
  clearTimeout(this.persistTimer);
2139
1986
  this.persistTimer = null;
2140
1987
  }
2141
- await this.persistCheckpoint();
1988
+ await this.persistSnapshot();
2142
1989
  }
2143
1990
  emit(event) {
2144
1991
  for (const listener of this.listeners) listener(event);
@@ -2152,9 +1999,6 @@ var AgentSession = class AgentSession {
2152
1999
  function textContentSummary(content) {
2153
2000
  return truncate(content.map((block) => block.type === "text" ? block.text : `[${block.type}]`).join("\n").trim(), 120, "...");
2154
2001
  }
2155
- function cloneMetadata(metadata) {
2156
- return metadata === void 0 ? null : structuredClone(metadata);
2157
- }
2158
2002
  async function readProviderIterator(iterator, signal) {
2159
2003
  try {
2160
2004
  return await abortable(iterator.next(), signal);
@@ -2230,6 +2074,7 @@ const APPROX_CHARS_PER_TOKEN = 4;
2230
2074
  const TOOL_DESCRIPTION_FIELD = "Concise title for the concrete user-visible state or result to make visible or confirm. Do not describe waiting, pausing, tool mechanics, generic actions, object labels, steps, tool names, ids, internals, or reasons.";
2231
2075
  const execRepeatStates = /* @__PURE__ */ new WeakMap();
2232
2076
  function createStandardAgentTools(options) {
2077
+ const { environment } = options;
2233
2078
  return [
2234
2079
  {
2235
2080
  name: "shell_exec",
@@ -2254,7 +2099,6 @@ function createStandardAgentTools(options) {
2254
2099
  },
2255
2100
  invoke: async (ctx, input) => {
2256
2101
  const parsed = parseShellExecInput(input);
2257
- const environment = await resolveEnvironment(options.environment, ctx, { shellId: parsed.shellId });
2258
2102
  const repeatGuard = repeatedShellExecResult(environment, ctx.agentSessionId, parsed.script);
2259
2103
  if (repeatGuard) return repeatGuard;
2260
2104
  const result = await environment.exec({
@@ -2282,9 +2126,7 @@ function createStandardAgentTools(options) {
2282
2126
  }
2283
2127
  },
2284
2128
  invoke: async (ctx, input) => {
2285
- const parsed = parseShellStatusInput(input);
2286
- const environment = await resolveEnvironment(options.environment, ctx, { commandId: parsed.commandId });
2287
- const result = await environment.status(parsed);
2129
+ const result = await environment.status(parseShellStatusInput(input));
2288
2130
  ctx.emitProgress(result);
2289
2131
  return finishShellToolResult(environment, result, ctx);
2290
2132
  }
@@ -2306,10 +2148,8 @@ function createStandardAgentTools(options) {
2306
2148
  }
2307
2149
  },
2308
2150
  invoke: async (ctx, input) => {
2309
- const parsed = parseShellWriteInput(input);
2310
- const environment = await resolveEnvironment(options.environment, ctx, { commandId: parsed.commandId });
2311
2151
  const result = await environment.write({
2312
- ...parsed,
2152
+ ...parseShellWriteInput(input),
2313
2153
  signal: ctx.signal
2314
2154
  });
2315
2155
  ctx.emitProgress(result);
@@ -2332,9 +2172,7 @@ function createStandardAgentTools(options) {
2332
2172
  }
2333
2173
  },
2334
2174
  invoke: async (ctx, input) => {
2335
- const parsed = parseShellAbortInput(input);
2336
- const environment = await resolveEnvironment(options.environment, ctx, { commandId: parsed.commandId });
2337
- const result = await environment.abort(parsed);
2175
+ const result = await environment.abort(parseShellAbortInput(input));
2338
2176
  ctx.emitProgress(result);
2339
2177
  return {
2340
2178
  ...await finishShellToolResult(environment, result, ctx),
@@ -2365,136 +2203,31 @@ function createStandardAgentTools(options) {
2365
2203
  }
2366
2204
  ];
2367
2205
  }
2368
- function resolveEnvironment(source, ctx, handle) {
2369
- return typeof source === "function" ? source(ctx, handle) : source;
2370
- }
2371
- /**
2372
- * How many bytes of each modality a tool may hand to the model.
2373
- *
2374
- * One number cannot serve both, because bytes buy wildly different amounts of
2375
- * context per modality — measured against a frontier model, a KiB of video
2376
- * costs ~2 tokens where a KiB of image costs ~50. A cap generous enough to show
2377
- * a five-minute clip would let a single still eat a six-figure token budget.
2378
- *
2379
- * image (4 MiB): well past any sane still — a 4000x3000 PNG lands under it — so
2380
- * crossing this line is a mistake, not a use case.
2381
- * video (16 MiB): roughly ten minutes at a viewing-grade encoding, and
2382
- * deliberately under the ~20 MB inline-payload ceiling the major APIs enforce.
2383
- * A larger cap buys no reach, only a rejection further downstream where the
2384
- * reason is harder to read.
2385
- *
2386
- * Bytes are a proxy, not a budget: for video they track cost reasonably at a
2387
- * fixed encoding, but an image's real driver is its pixel dimensions.
2388
- */
2389
- const DEFAULT_MAX_MEDIA_BYTES = {
2390
- image: 4 * 1024 * 1024,
2391
- video: 16 * 1024 * 1024
2392
- };
2393
2206
  function shellPreviewBudgetTokens(contextWindow) {
2394
2207
  return contextWindow >= LARGE_CONTEXT_THRESHOLD_TOKENS ? LARGE_CONTEXT_PREVIEW_TOKENS : SMALL_CONTEXT_PREVIEW_TOKENS;
2395
2208
  }
2396
- function toShellToolResult(result, options = {}) {
2209
+ function toShellToolResult(result, toolCallId = "", options = {}) {
2397
2210
  const output = [{
2398
2211
  type: "text",
2399
2212
  text: formatShellToolResult(result, options)
2400
2213
  }];
2401
- if (result.status === "exited" && result.binaryStdout) {
2402
- const verdict = binaryStreamVerdict(result.binaryStdout, result.commandId, options.model, {
2403
- ...DEFAULT_MAX_MEDIA_BYTES,
2404
- ...options.maxMediaBytes
2405
- });
2406
- if (verdict.block) output.push(verdict.block);
2407
- if (verdict.note) output.push({
2408
- type: "text",
2409
- text: verdict.note
2410
- });
2411
- }
2214
+ if (result.status === "exited" && result.assets) for (const asset of result.assets) output.push({
2215
+ type: "image",
2216
+ source: {
2217
+ mediaType: asset.mediaType,
2218
+ data: asset.data
2219
+ }
2220
+ });
2412
2221
  return {
2413
2222
  output,
2414
2223
  isError: false,
2415
- view: shellToolView(result)
2416
- };
2417
- }
2418
- /** Character budget for a shell view's render window (tail-biased). */
2419
- const SHELL_VIEW_MAX_CHARS = 32768;
2420
- function shellToolView(result) {
2421
- const window = tailChunkWindow(result.output.chunks, SHELL_VIEW_MAX_CHARS);
2422
- const view = {
2423
- kind: "shell",
2424
- status: result.status,
2425
- shellId: result.shellId,
2426
- commandId: result.commandId,
2427
- runningMs: result.runningMs,
2428
- idleMs: result.idleMs,
2429
- chunks: window.chunks,
2430
- viewTruncated: window.truncated || result.output.truncated
2431
- };
2432
- if (result.status === "exited") {
2433
- view.exitCode = result.exitCode;
2434
- if (result.audit.length > 0) view.audit = result.audit;
2435
- if (result.commandMetadata && result.commandMetadata.length > 0) view.commandMeta = result.commandMetadata;
2436
- }
2437
- return view;
2438
- }
2439
- function tailChunkWindow(chunks, maxChars) {
2440
- const kept = [];
2441
- let total = 0;
2442
- for (let i = chunks.length - 1; i >= 0; i -= 1) {
2443
- const chunk = chunks[i];
2444
- if (chunk.text.length === 0) continue;
2445
- const remaining = maxChars - total;
2446
- if (remaining <= 0) return {
2447
- chunks: kept,
2448
- truncated: true
2449
- };
2450
- if (chunk.text.length <= remaining) {
2451
- kept.unshift({
2452
- stream: chunk.stream,
2453
- text: chunk.text
2454
- });
2455
- total += chunk.text.length;
2456
- } else {
2457
- kept.unshift({
2458
- stream: chunk.stream,
2459
- text: chunk.text.slice(chunk.text.length - remaining)
2460
- });
2461
- return {
2462
- chunks: kept,
2463
- truncated: true
2464
- };
2465
- }
2466
- }
2467
- return {
2468
- chunks: kept,
2469
- truncated: false
2470
- };
2471
- }
2472
- /**
2473
- * Boundary decision for a binary final stream: attach as native media when the
2474
- * magic matches the closed model-media set, the model accepts the type, and
2475
- * the stream was not truncated; otherwise explain why nothing was attached.
2476
- */
2477
- function binaryStreamVerdict(binary, commandId, model, maxMediaBytes) {
2478
- const media = sniffModelMediaType(binary.data);
2479
- const binPath = `/@/commands/${commandId}/stdout.bin`;
2480
- if (binary.truncated) return { note: `Binary stdout (${binary.totalBytes} bytes${media ? `, ${media.mediaType}` : ""}) exceeded the shell's ${binary.limitBytes}-byte binary limit (maxBinaryBytes) and was not attached; the raw bytes remain readable at ${binPath}. Produce a smaller version and re-run.` };
2481
- if (!media) return { note: `Binary stdout does not match any model-viewable media type; the raw bytes remain readable at ${binPath}.` };
2482
- if (!model || !modelAcceptsMediaType(model, media.mediaType)) return { note: `Binary stdout is ${media.mediaType}, which this model does not accept natively; the raw bytes remain readable at ${binPath}.` };
2483
- const cap = maxMediaBytes[media.kind];
2484
- if (binary.totalBytes > cap) return { note: `Binary stdout is ${media.mediaType} (${binary.totalBytes} bytes), over the ${cap}-byte ${media.kind} cap; it was not attached and the raw bytes remain readable at ${binPath}. Produce a smaller version — for video, fewer frames or a lower resolution — and re-run.` };
2485
- const source = {
2486
- mediaType: media.mediaType,
2487
- data: bytesToBase64(binary.data)
2488
- };
2489
- return {
2490
- block: media.kind === "video" ? {
2491
- type: "video",
2492
- source
2493
- } : {
2494
- type: "image",
2495
- source
2496
- },
2497
- note: `Attached stdout as ${media.mediaType} (${binary.totalBytes} bytes).`
2224
+ metadata: result,
2225
+ continuation: result.status === "running" ? {
2226
+ toolCallId,
2227
+ shellId: result.shellId,
2228
+ commandId: result.commandId,
2229
+ status: "running"
2230
+ } : void 0
2498
2231
  };
2499
2232
  }
2500
2233
  function parseShellExecInput(input) {
@@ -2556,8 +2289,8 @@ function repeatedShellExecResult(environment, agentSessionId, script) {
2556
2289
  ].join("\n")
2557
2290
  }],
2558
2291
  isError: true,
2559
- view: {
2560
- kind: "repeated_shell_exec",
2292
+ metadata: {
2293
+ kind: "repeated_identical_shell_exec",
2561
2294
  script,
2562
2295
  count
2563
2296
  }
@@ -2615,11 +2348,10 @@ function boundedPreview(text, budgetTokens) {
2615
2348
  async function finishShellToolResult(environment, result, ctx) {
2616
2349
  const previewBudgetTokens = shellPreviewBudgetTokens(ctx.model.model.contextWindow);
2617
2350
  const exposeCommandHandle = shellCommandHandleRequired(result, previewBudgetTokens);
2618
- const toolResult = toShellToolResult(result, {
2351
+ const toolResult = toShellToolResult(result, ctx.toolCallId, {
2619
2352
  includePreview: true,
2620
2353
  previewBudgetTokens,
2621
- exposeCommandHandle,
2622
- model: ctx.model.model
2354
+ exposeCommandHandle
2623
2355
  });
2624
2356
  if (!exposeCommandHandle) await environment.releaseCommand(result.commandId);
2625
2357
  return toolResult;
@@ -2632,20 +2364,12 @@ function shellCommandHandleRequired(result, budgetTokens) {
2632
2364
  }
2633
2365
  //#endregion
2634
2366
  //#region src/server.ts
2635
- var RunCommandLineShellNotFoundError = class extends Error {
2636
- shellId;
2637
- constructor(shellId) {
2638
- super(`runCommandLine: shell "${shellId}" is not open in this process`);
2639
- this.shellId = shellId;
2640
- this.name = "RunCommandLineShellNotFoundError";
2641
- }
2642
- };
2643
- var RunCommandLineCommandNotRegisteredError = class extends Error {
2644
- commandName;
2645
- constructor(commandName) {
2646
- super(`runCommandLine: command "${commandName}" is not registered for this session`);
2647
- this.commandName = commandName;
2648
- this.name = "RunCommandLineCommandNotRegisteredError";
2367
+ var RunCommandLineSessionNotFoundError = class extends Error {
2368
+ agentSessionId;
2369
+ constructor(agentSessionId) {
2370
+ super(`runCommandLine: no session "${agentSessionId}" is open in this process`);
2371
+ this.agentSessionId = agentSessionId;
2372
+ this.name = "RunCommandLineSessionNotFoundError";
2649
2373
  }
2650
2374
  };
2651
2375
  var RunCommandLineTimeoutError = class extends Error {
@@ -2665,7 +2389,7 @@ var AgentServer = class {
2665
2389
  providers;
2666
2390
  shellOptions;
2667
2391
  sessionOptions;
2668
- prepareShell;
2392
+ prepareSessionShell;
2669
2393
  bindings = /* @__PURE__ */ new Set();
2670
2394
  sessionOwnership = new SessionOwnershipRegistry();
2671
2395
  constructor(options) {
@@ -2673,7 +2397,7 @@ var AgentServer = class {
2673
2397
  this.providers = createProviderMap(options.providers);
2674
2398
  this.shellOptions = options.shell ?? {};
2675
2399
  this.sessionOptions = options.session ?? {};
2676
- this.prepareShell = options.prepareShell ?? null;
2400
+ this.prepareSessionShell = options.prepareSessionShell ?? null;
2677
2401
  }
2678
2402
  client() {
2679
2403
  const transports = createInProcessTransportPair();
@@ -2687,7 +2411,7 @@ var AgentServer = class {
2687
2411
  providers: this.providers,
2688
2412
  shell: this.shellOptions,
2689
2413
  session: this.sessionOptions,
2690
- prepareShell: this.prepareShell,
2414
+ prepareSessionShell: this.prepareSessionShell,
2691
2415
  sessions: this.sessionOwnership
2692
2416
  });
2693
2417
  this.bindings.add(binding);
@@ -2703,18 +2427,17 @@ var AgentServer = class {
2703
2427
  * Transport-agnostic: callers (e.g. LocalHost command bridge) supply their
2704
2428
  * own IPC; AgentServer only knows how to exec against the open session shell.
2705
2429
  */
2706
- async runCommandLine(shellId, name, args, opts) {
2707
- const owners = [...this.bindings].filter((binding) => binding.hasShell(shellId));
2708
- if (owners.length === 0) throw new RunCommandLineShellNotFoundError(shellId);
2709
- if (owners.length > 1) throw new Error(`runCommandLine: shell id "${shellId}" is not unique`);
2710
- return owners[0].runCommandLine(shellId, name, args, opts);
2430
+ async runCommandLine(agentSessionId, name, args, opts) {
2431
+ const binding = this.sessionOwnership.get(agentSessionId);
2432
+ if (!binding) throw new RunCommandLineSessionNotFoundError(agentSessionId);
2433
+ return binding.runCommandLine(name, args, opts);
2711
2434
  }
2712
2435
  };
2713
2436
  /**
2714
2437
  * Tracks which transport binding currently owns each client-provided session
2715
2438
  * id. Opening a session id that is already owned takes it over: the previous
2716
- * binding's session is closed (flushing its checkpoint) before the new open
2717
- * proceeds, so two connections never write the same checkpoint key concurrently.
2439
+ * binding's session is closed (flushing its snapshot) before the new open
2440
+ * proceeds, so two connections never write the same snapshot key concurrently.
2718
2441
  */
2719
2442
  var SessionOwnershipRegistry = class {
2720
2443
  holders = /* @__PURE__ */ new Map();
@@ -2736,17 +2459,14 @@ var AgentTransportBindingImpl = class {
2736
2459
  providers;
2737
2460
  shellOptions;
2738
2461
  sessionOptions;
2739
- prepareShell;
2462
+ prepareSessionShell;
2740
2463
  sessions;
2741
2464
  session = null;
2742
2465
  currentAgent = null;
2743
- environmentsByHost = /* @__PURE__ */ new Map();
2744
- pendingEnvironmentsByHost = /* @__PURE__ */ new Map();
2745
- currentCommandRegistry = null;
2466
+ currentEnvironment = null;
2746
2467
  currentCwd = null;
2747
2468
  currentProviderId = null;
2748
2469
  currentSessionId = null;
2749
- currentCommandNames = /* @__PURE__ */ new Set();
2750
2470
  unsubscribeSession = null;
2751
2471
  unsubscribeTransport = null;
2752
2472
  closed = false;
@@ -2756,7 +2476,7 @@ var AgentTransportBindingImpl = class {
2756
2476
  this.providers = options.providers;
2757
2477
  this.shellOptions = options.shell ?? {};
2758
2478
  this.sessionOptions = options.session ?? {};
2759
- this.prepareShell = options.prepareShell;
2479
+ this.prepareSessionShell = options.prepareSessionShell;
2760
2480
  this.sessions = options.sessions;
2761
2481
  this.unsubscribeTransport = this.transport.onFrame((frame) => {
2762
2482
  this.handleFrame(frame);
@@ -2793,10 +2513,7 @@ var AgentTransportBindingImpl = class {
2793
2513
  case "send": {
2794
2514
  const session = this.sessionFor("send");
2795
2515
  if (!session) return;
2796
- this.observeSessionAction(session.send(frame.content, {
2797
- id: frame.messageId,
2798
- metadata: frame.metadata
2799
- }));
2516
+ this.observeSessionAction(session.send(frame.content, { id: frame.messageId }));
2800
2517
  return;
2801
2518
  }
2802
2519
  case "dequeue_message": {
@@ -2889,19 +2606,19 @@ var AgentTransportBindingImpl = class {
2889
2606
  case "retry": {
2890
2607
  const session = this.sessionFor("retry");
2891
2608
  if (!session || this.rejectIfBusy(session, "retry")) return;
2892
- this.observeSessionAction(session.retry({ metadata: frame.metadata }));
2609
+ this.observeSessionAction(session.retry());
2893
2610
  return;
2894
2611
  }
2895
2612
  case "resume": {
2896
2613
  const session = this.sessionFor("resume");
2897
2614
  if (!session || this.rejectIfBusy(session, "resume")) return;
2898
- this.observeSessionAction(session.resume({ metadata: frame.metadata }));
2615
+ this.observeSessionAction(session.resume());
2899
2616
  return;
2900
2617
  }
2901
2618
  case "compact": {
2902
2619
  const session = this.sessionFor("compact");
2903
2620
  if (!session || this.rejectIfBusy(session, "compact")) return;
2904
- this.observeSessionAction(session.compact({ metadata: frame.metadata }));
2621
+ this.observeSessionAction(session.compact());
2905
2622
  return;
2906
2623
  }
2907
2624
  case "abort": {
@@ -2923,7 +2640,7 @@ var AgentTransportBindingImpl = class {
2923
2640
  case "sync_transcript": {
2924
2641
  const session = this.sessionFor("sync_transcript");
2925
2642
  if (!session) return;
2926
- this.sendTranscriptReset(session);
2643
+ this.sendTranscriptSnapshot(session);
2927
2644
  return;
2928
2645
  }
2929
2646
  case "close":
@@ -2950,30 +2667,41 @@ var AgentTransportBindingImpl = class {
2950
2667
  await this.sessions.claim(agentSessionId, this);
2951
2668
  this.currentSessionId = agentSessionId;
2952
2669
  const initialState = agent.initialState();
2953
- const store = new HostAgentSessionStore((await agent.host({
2670
+ const provisionalHost = agent.host({
2954
2671
  state: initialState,
2955
2672
  cwd: frame.cwd
2956
- })).store, agentSessionId);
2957
- const checkpoint = await store.loadCheckpoint();
2958
- const restoring = checkpoint !== null && checkpoint.harnessName === agent.name;
2959
- const state = restoring ? structuredClone(checkpoint.state) : initialState;
2673
+ });
2674
+ const store = new HostAgentSessionStore(provisionalHost.store, agentSessionId);
2675
+ const snapshot = await store.loadSnapshot();
2676
+ const restoring = snapshot !== null && snapshot.harnessName === agent.name;
2677
+ const state = restoring ? structuredClone(snapshot.state) : initialState;
2960
2678
  const harnessContext = {
2961
2679
  state,
2962
2680
  cwd: frame.cwd
2963
2681
  };
2682
+ const host = restoring ? agent.host(harnessContext) : provisionalHost;
2964
2683
  const commands = agent.commands?.(harnessContext) ?? [];
2965
2684
  const commandRegistry = new CommandRegistry();
2966
2685
  for (const command of commands) commandRegistry.register(command);
2967
- const commandNames = commandRegistry.list().map((command) => command.name);
2686
+ const environment = new BashEnvironment({
2687
+ ...this.prepareSessionShell ? await this.prepareSessionShell({
2688
+ agentSessionId,
2689
+ host,
2690
+ commandNames: commandRegistry.list().map((command) => command.name),
2691
+ shell: this.shellOptions
2692
+ }) : this.shellOptions,
2693
+ host,
2694
+ commands: commandRegistry
2695
+ });
2968
2696
  let sessionRef = null;
2969
2697
  const tools = createStandardAgentTools({
2970
- environment: (ctx, handle) => this.resolveEnvironment(ctx, handle),
2971
- scheduleYield: (ctx, durationMs) => {
2698
+ environment,
2699
+ scheduleYield: (_ctx, durationMs) => {
2972
2700
  if (!sessionRef) throw new Error("AgentServer: session is not ready for yield scheduling");
2973
- return sessionRef.scheduleYieldWakeup(durationMs, ctx.metadata);
2701
+ return sessionRef.scheduleYieldWakeup(durationMs);
2974
2702
  }
2975
2703
  });
2976
- const commandsPrompt = commandRegistry.renderHelp();
2704
+ const commandsPrompt = commandRegistry.renderPrompt();
2977
2705
  const runtime = {
2978
2706
  harnessName: agent.name,
2979
2707
  initialState: () => agent.initialState(),
@@ -2986,11 +2714,11 @@ var AgentTransportBindingImpl = class {
2986
2714
  lifecycle: (event) => agent.lifecycle?.(event),
2987
2715
  tools: () => tools
2988
2716
  };
2989
- const session = restoring ? AgentSession.fromCheckpoint({
2717
+ const session = restoring ? AgentSession.fromSnapshot({
2990
2718
  provider,
2991
2719
  runtime,
2992
- checkpoint: {
2993
- ...checkpoint,
2720
+ snapshot: {
2721
+ ...snapshot,
2994
2722
  state
2995
2723
  }
2996
2724
  }, {
@@ -3011,14 +2739,13 @@ var AgentTransportBindingImpl = class {
3011
2739
  sessionRef = session;
3012
2740
  this.session = session;
3013
2741
  this.currentAgent = agent;
3014
- this.currentCommandRegistry = commandRegistry;
2742
+ this.currentEnvironment = environment;
3015
2743
  this.currentCwd = frame.cwd;
3016
2744
  this.currentProviderId = frame.provider.providerId;
3017
- this.currentCommandNames = new Set(commandNames);
3018
2745
  if (restoring) session.updateModel(null, frame.provider.model);
3019
2746
  this.unsubscribeSession = this.session.subscribe((event) => this.handleSessionEvent(event));
3020
2747
  this.send({ type: "opened" });
3021
- this.sendTranscriptReset(session);
2748
+ this.sendTranscriptSnapshot(session);
3022
2749
  this.send({
3023
2750
  type: "phase",
3024
2751
  phase: this.session.phase()
@@ -3028,10 +2755,10 @@ var AgentTransportBindingImpl = class {
3028
2755
  queue: this.session.queuedMessages()
3029
2756
  });
3030
2757
  }
3031
- sendTranscriptReset(session) {
2758
+ sendTranscriptSnapshot(session) {
3032
2759
  const transcript = session.transcript();
3033
2760
  this.send({
3034
- type: "transcript_reset",
2761
+ type: "transcript_snapshot",
3035
2762
  blocks: cloneBlocks(transcript.blocks),
3036
2763
  revision: transcript.revision
3037
2764
  });
@@ -3065,8 +2792,7 @@ var AgentTransportBindingImpl = class {
3065
2792
  type: "retry_scheduled",
3066
2793
  attempt: event.attempt,
3067
2794
  delayMs: event.delayMs,
3068
- code: event.code,
3069
- diagnostics: event.diagnostics
2795
+ code: event.code
3070
2796
  });
3071
2797
  return;
3072
2798
  case "error":
@@ -3101,13 +2827,11 @@ var AgentTransportBindingImpl = class {
3101
2827
  async closeSession() {
3102
2828
  const session = this.session;
3103
2829
  const agent = this.currentAgent;
2830
+ const environment = this.currentEnvironment;
3104
2831
  const cwd = this.currentCwd;
3105
2832
  try {
3106
2833
  if (session) await session.dispose();
3107
- const pendingEnvironments = await Promise.allSettled(this.pendingEnvironmentsByHost.values());
3108
- const environments = new Set(this.environmentsByHost.values());
3109
- for (const result of pendingEnvironments) if (result.status === "fulfilled") environments.add(result.value);
3110
- await Promise.all([...environments].map((environment) => environment.disposeAllShells()));
2834
+ if (environment) await environment.disposeAllShells();
3111
2835
  if (session && agent && cwd) await agent.dispose?.({
3112
2836
  agentSessionId: session.id(),
3113
2837
  state: session.state(),
@@ -3119,12 +2843,9 @@ var AgentTransportBindingImpl = class {
3119
2843
  this.unsubscribeSession = null;
3120
2844
  this.session = null;
3121
2845
  this.currentAgent = null;
3122
- this.environmentsByHost.clear();
3123
- this.pendingEnvironmentsByHost.clear();
3124
- this.currentCommandRegistry = null;
2846
+ this.currentEnvironment = null;
3125
2847
  this.currentCwd = null;
3126
2848
  this.currentProviderId = null;
3127
- this.currentCommandNames = /* @__PURE__ */ new Set();
3128
2849
  if (this.currentSessionId) {
3129
2850
  this.sessions.release(this.currentSessionId, this);
3130
2851
  this.currentSessionId = null;
@@ -3132,17 +2853,17 @@ var AgentTransportBindingImpl = class {
3132
2853
  }
3133
2854
  }
3134
2855
  async listConversations(cwd) {
3135
- const host = await this.agent.host({
2856
+ const host = this.agent.host({
3136
2857
  state: this.agent.initialState(),
3137
2858
  cwd
3138
2859
  });
3139
2860
  const keys = await host.store.list("agent-sessions/");
3140
2861
  const conversations = [];
3141
2862
  for (const key of keys) {
3142
- if (!key.endsWith("/checkpoint.json")) continue;
3143
- const checkpoint = await host.store.readJson(key);
3144
- if (!checkpoint || checkpoint.cwd !== cwd) continue;
3145
- conversations.push(summarizeConversation(key.slice(15, -16), checkpoint));
2863
+ if (!key.endsWith("/snapshot.json")) continue;
2864
+ const snapshot = await host.store.readJson(key);
2865
+ if (!snapshot || snapshot.cwd !== cwd) continue;
2866
+ conversations.push(summarizeConversation(key.slice(15, -14), snapshot));
3146
2867
  }
3147
2868
  conversations.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
3148
2869
  this.send({
@@ -3151,122 +2872,39 @@ var AgentTransportBindingImpl = class {
3151
2872
  });
3152
2873
  }
3153
2874
  async handleShellWrite(frame) {
3154
- const session = this.sessionFor("shell_write");
3155
- if (!session || !this.currentCwd) return;
3156
- const result = await (await this.resolveEnvironment({
3157
- agentSessionId: session.id(),
3158
- state: session.state(),
3159
- cwd: this.currentCwd,
3160
- metadata: frame.metadata ?? null
3161
- }, { commandId: frame.commandId })).write({
2875
+ if (!this.sessionFor("shell_write") || !this.currentEnvironment || !this.currentCwd) return;
2876
+ const result = await this.currentEnvironment.write({
3162
2877
  commandId: frame.commandId,
3163
2878
  stdin: frame.stdin
3164
2879
  });
3165
2880
  this.sendShellWriteResult(frame.commandId, result);
3166
2881
  }
3167
2882
  /** Backs `AgentServer.runCommandLine` — see its doc comment for the contract. */
3168
- async runCommandLine(shellId, name, args, opts) {
3169
- const environment = this.environmentForShell(shellId);
2883
+ async runCommandLine(name, args, opts) {
2884
+ const environment = this.currentEnvironment;
3170
2885
  const agentSessionId = this.currentSessionId;
3171
2886
  if (!environment || !agentSessionId) throw new Error("runCommandLine: session has no active shell environment");
3172
- if (!this.currentCommandNames.has(name)) throw new RunCommandLineCommandNotRegisteredError(name);
3173
2887
  const words = [name, ...args].map(shellQuote).join(" ");
3174
2888
  let script = words;
3175
2889
  if (opts.stdin.length > 0) {
3176
2890
  const delimiter = heredocDelimiter(opts.stdin);
3177
- script = `${words} <<'${delimiter}'\n${opts.stdin.endsWith("\n") ? opts.stdin : `${opts.stdin}\n`}${delimiter}`;
2891
+ script = `${words} <<'${delimiter}'\n${opts.stdin}\n${delimiter}`;
3178
2892
  }
2893
+ const cdScript = `cd ${shellQuote(opts.cwd)} && ${script}`;
3179
2894
  const result = await environment.exec({
3180
2895
  agentSessionId,
3181
- script,
2896
+ script: cdScript,
3182
2897
  timeoutMs: MAX_TIMEOUT_MS,
3183
- signal: opts.signal,
3184
- ephemeral: true,
3185
- cwd: opts.cwd
2898
+ signal: opts.signal
3186
2899
  });
3187
- try {
3188
- if (result.status === "exited") {
3189
- if (result.binaryStdout) {
3190
- const truncationNote = result.binaryStdout.truncated ? `command bridge: binary stdout truncated at the output limit (${result.binaryStdout.data.length} of ${result.binaryStdout.totalBytes} bytes)\n` : "";
3191
- return {
3192
- exitCode: result.exitCode,
3193
- stdout: bytesToBase64(result.binaryStdout.data),
3194
- stdoutEncoding: "base64",
3195
- stderr: `${result.stderr.delta}${truncationNote}`
3196
- };
3197
- }
3198
- return {
3199
- exitCode: result.exitCode,
3200
- stdout: result.stdout.delta,
3201
- stderr: result.stderr.delta
3202
- };
3203
- }
3204
- if (result.status === "aborted") throw new Error(`runCommandLine: call for "${name}" was cancelled before it completed`);
3205
- const aborted = await environment.abort({ commandId: result.commandId });
3206
- throw new RunCommandLineTimeoutError(result.commandId, aborted.status === "aborted" ? aborted.stdout.delta : "", aborted.status === "aborted" ? aborted.stderr.delta : "");
3207
- } finally {
3208
- await environment.disposeShell(result.shellId).catch(() => {});
3209
- }
3210
- }
3211
- hasShell(shellId) {
3212
- return this.environmentForShell(shellId) !== null;
3213
- }
3214
- async resolveEnvironment(ctx, handle) {
3215
- const agent = this.currentAgent;
3216
- const commandRegistry = this.currentCommandRegistry;
3217
- if (!agent || !commandRegistry) throw new Error("Shell environment is not available before session open");
3218
- const host = await agent.host({
3219
- agentSessionId: ctx.agentSessionId,
3220
- state: ctx.state,
3221
- cwd: ctx.cwd,
3222
- metadata: ctx.metadata
3223
- });
3224
- const environment = await this.environmentForHost(host, commandRegistry);
3225
- const owner = handle.shellId ? this.environmentForShell(handle.shellId) : handle.commandId ? this.environmentForCommand(handle.commandId) : null;
3226
- if (owner && owner !== environment) {
3227
- const id = handle.shellId ?? handle.commandId;
3228
- throw new Error(`Shell handle "${id}" belongs to a different Host`);
3229
- }
3230
- return environment;
3231
- }
3232
- async environmentForHost(host, commands) {
3233
- const existing = this.environmentsByHost.get(host);
3234
- if (existing) return existing;
3235
- const pending = this.pendingEnvironmentsByHost.get(host);
3236
- if (pending) return pending;
3237
- const creation = this.createEnvironment(host, commands);
3238
- this.pendingEnvironmentsByHost.set(host, creation);
3239
- try {
3240
- const environment = await creation;
3241
- this.environmentsByHost.set(host, environment);
3242
- return environment;
3243
- } finally {
3244
- this.pendingEnvironmentsByHost.delete(host);
3245
- }
3246
- }
3247
- async createEnvironment(host, commands) {
3248
- const agentSessionId = this.currentSessionId;
3249
- if (!agentSessionId) throw new Error("Shell environment is not available before session open");
3250
- return new BashEnvironment({
3251
- ...this.prepareShell ? await this.prepareShell({
3252
- agentSessionId,
3253
- host,
3254
- commandNames: commands.list().map((command) => command.name),
3255
- shell: this.shellOptions
3256
- }) : this.shellOptions,
3257
- host,
3258
- commands
3259
- });
3260
- }
3261
- environmentForShell(shellId) {
3262
- const matches = [...this.environmentsByHost.values()].filter((environment) => environment.getShell(shellId));
3263
- if (matches.length > 1) throw new Error(`Shell id "${shellId}" is not unique in this session`);
3264
- return matches[0] ?? null;
3265
- }
3266
- environmentForCommand(commandId) {
3267
- const matches = [...this.environmentsByHost.values()].filter((environment) => environment.hasCommand(commandId));
3268
- if (matches.length > 1) throw new Error(`Command id "${commandId}" is not unique in this session`);
3269
- return matches[0] ?? null;
2900
+ if (result.status === "exited") return {
2901
+ exitCode: result.exitCode,
2902
+ stdout: result.stdout.delta,
2903
+ stderr: result.stderr.delta
2904
+ };
2905
+ if (result.status === "aborted") throw new Error(`runCommandLine: call for "${name}" was cancelled before it completed`);
2906
+ const aborted = await environment.abort({ commandId: result.commandId });
2907
+ throw new RunCommandLineTimeoutError(result.commandId, aborted.status === "aborted" ? aborted.stdout.delta : "", aborted.status === "aborted" ? aborted.stderr.delta : "");
3270
2908
  }
3271
2909
  sessionFor(command) {
3272
2910
  if (!this.session) {
@@ -3301,7 +2939,7 @@ var AgentTransportBindingImpl = class {
3301
2939
  type: "shell_output",
3302
2940
  shellId: shell.shellId,
3303
2941
  commandId: shell.commandId,
3304
- status: shell.status
2942
+ snapshot: shell.snapshot
3305
2943
  });
3306
2944
  const audit = progressToAudit(progress);
3307
2945
  if (audit.length > 0) this.send({
@@ -3315,7 +2953,7 @@ var AgentTransportBindingImpl = class {
3315
2953
  type: "shell_output",
3316
2954
  shellId: shell.shellId,
3317
2955
  commandId: shell.commandId,
3318
- status: shell.status
2956
+ snapshot: shell.snapshot
3319
2957
  });
3320
2958
  const audit = progressToAudit(progress);
3321
2959
  if (audit.length > 0) this.send({
@@ -3337,12 +2975,13 @@ var AgentTransportBindingImpl = class {
3337
2975
  sendError(error) {
3338
2976
  const normalized = error instanceof Error ? error : new Error(String(error));
3339
2977
  const code = errorCode(error);
3340
- const diagnostics = errorDiagnostics(error);
3341
- this.send({
2978
+ this.send(code ? {
3342
2979
  type: "error",
3343
2980
  message: normalized.message,
3344
- ...code ? { code } : {},
3345
- ...diagnostics ? { diagnostics } : {}
2981
+ code
2982
+ } : {
2983
+ type: "error",
2984
+ message: normalized.message
3346
2985
  });
3347
2986
  }
3348
2987
  };
@@ -3353,15 +2992,15 @@ var HostAgentSessionStore = class {
3353
2992
  this.store = store;
3354
2993
  this.agentSessionId = agentSessionId;
3355
2994
  }
3356
- saveCheckpoint(checkpoint) {
3357
- return this.store.writeJson(`agent-sessions/${this.agentSessionId}/checkpoint.json`, checkpoint);
2995
+ saveSnapshot(snapshot) {
2996
+ return this.store.writeJson(`agent-sessions/${this.agentSessionId}/snapshot.json`, snapshot);
3358
2997
  }
3359
- loadCheckpoint() {
3360
- return this.store.readJson(`agent-sessions/${this.agentSessionId}/checkpoint.json`);
2998
+ loadSnapshot() {
2999
+ return this.store.readJson(`agent-sessions/${this.agentSessionId}/snapshot.json`);
3361
3000
  }
3362
3001
  };
3363
- function summarizeConversation(id, checkpoint) {
3364
- const blocks = checkpoint.transcript.blocks;
3002
+ function summarizeConversation(id, snapshot) {
3003
+ const blocks = snapshot.transcript.blocks;
3365
3004
  const first = blocks[0];
3366
3005
  const last = blocks[blocks.length - 1];
3367
3006
  return {
@@ -3395,14 +3034,14 @@ function progressToShellOutput(progress) {
3395
3034
  if (!isRecord(progress.stdout) || !isRecord(progress.stderr)) return null;
3396
3035
  const stdout = progress.stdout;
3397
3036
  const stderr = progress.stderr;
3398
- if (!isShellStreamView(stdout) || !isShellStreamView(stderr) || typeof progress.runningMs !== "number" || typeof progress.idleMs !== "number") return null;
3037
+ if (!isStreamArtifact(stdout) || !isStreamArtifact(stderr) || typeof progress.runningMs !== "number" || typeof progress.idleMs !== "number") return null;
3399
3038
  return {
3400
3039
  shellId: progress.shellId,
3401
3040
  commandId: progress.commandId,
3402
- status: progress
3041
+ snapshot: progress
3403
3042
  };
3404
3043
  }
3405
- function isShellStreamView(value) {
3044
+ function isStreamArtifact(value) {
3406
3045
  return typeof value.path === "string" && typeof value.offset === "number" && typeof value.delta === "string" && typeof value.tail === "string" && typeof value.bytes === "number" && typeof value.truncated === "boolean";
3407
3046
  }
3408
3047
  function progressToAudit(progress) {
@@ -3423,10 +3062,6 @@ function errorCode(error) {
3423
3062
  if (!isRecord(error) || typeof error.code !== "string") return void 0;
3424
3063
  return error.code;
3425
3064
  }
3426
- function errorDiagnostics(error) {
3427
- if (!(error instanceof ProviderStreamError)) return void 0;
3428
- return error.diagnostics;
3429
- }
3430
3065
  function createProviderMap(providers) {
3431
3066
  const map = /* @__PURE__ */ new Map();
3432
3067
  for (const provider of providers) {
@@ -3436,4 +3071,4 @@ function createProviderMap(providers) {
3436
3071
  return map;
3437
3072
  }
3438
3073
  //#endregion
3439
- export { AgentClient, AgentServer, AgentSession, DEFAULT_MAX_MEDIA_BYTES, DEFAULT_TURN_RETRY_POLICY, ProviderStreamError, RunCommandLineCommandNotRegisteredError, RunCommandLineShellNotFoundError, RunCommandLineTimeoutError, SHELL_VIEW_MAX_CHARS, TranscriptLog, applyTranscriptPatches, cloneBlocks, createInProcessTransportPair, createStandardAgentTools, createWebSocketClientTransport, createWebSocketServerTransport, estimateTranscriptBlockTokens, findResumePoint, finishShellToolResult, isContextLengthExceeded, isRetryableCode, resolveRetryPolicy, retryDelayMs, shellCommandHandleRequired, shellPreviewBudgetTokens, toShellToolResult };
3074
+ export { AgentClient, AgentServer, AgentSession, DEFAULT_TURN_RETRY_POLICY, RunCommandLineSessionNotFoundError, RunCommandLineTimeoutError, Transcript, applyTranscriptPatches, cloneBlocks, createInProcessTransportPair, createStandardAgentTools, createWebSocketClientTransport, createWebSocketServerTransport, estimateTranscriptBlockTokens, finishShellToolResult, isRetryableCode, resolveRetryPolicy, retryDelayMs, shellCommandHandleRequired, shellPreviewBudgetTokens, toShellToolResult };