@opencow-ai/opencow-agent-sdk 0.4.27 → 0.4.28

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/cli.mjs CHANGED
@@ -86726,7 +86726,7 @@ function printStartupScreen() {
86726
86726
  const sLen = ` ● ${sL} Ready — type /help to begin`.length;
86727
86727
  out.push(boxRow(sRow, W, sLen));
86728
86728
  out.push(`${rgb(...BORDER)}╚${"═".repeat(W - 2)}╝${RESET}`);
86729
- out.push(` ${DIM}${rgb(...DIMCOL)}opencow ${RESET}${rgb(...ACCENT)}v${"0.4.27"}${RESET}`);
86729
+ out.push(` ${DIM}${rgb(...DIMCOL)}opencow ${RESET}${rgb(...ACCENT)}v${"0.4.28"}${RESET}`);
86730
86730
  out.push("");
86731
86731
  process.stdout.write(out.join(`
86732
86732
  `) + `
@@ -105105,6 +105105,10 @@ function tokenCountWithEstimation(messages) {
105105
105105
  }
105106
105106
  return getTokenCountFromUsage(usage) + roughTokenCountEstimationForMessages(messages.slice(i + 1));
105107
105107
  }
105108
+ const baselineTokens = message?.contextBaselineTokens;
105109
+ if (typeof baselineTokens === "number" && Number.isFinite(baselineTokens) && baselineTokens >= 0) {
105110
+ return baselineTokens + roughTokenCountEstimationForMessages(messages.slice(i + 1));
105111
+ }
105108
105112
  i--;
105109
105113
  }
105110
105114
  return roughTokenCountEstimationForMessages(messages);
@@ -138830,6 +138834,9 @@ function wrapBodyTransportError(error) {
138830
138834
  return new APIConnectionError({ message: error.message, cause: error });
138831
138835
  }
138832
138836
  function toReplayableStreamError(error) {
138837
+ if (error instanceof EmptyStreamError) {
138838
+ return new APIConnectionError({ message: error.message, cause: error });
138839
+ }
138833
138840
  const wrapped = error instanceof Error && error.message.startsWith(STREAM_WATCHDOG_ABORT_PREFIX) ? new APIConnectionError({ message: error.message, cause: error }) : wrapBodyTransportError(error);
138834
138841
  if (!(wrapped instanceof APIConnectionError) || wrapped instanceof APIConnectionTimeoutError || extractConnectionErrorDetails(wrapped)?.isSSLError) {
138835
138842
  return null;
@@ -139288,7 +139295,7 @@ function getRateLimitResetDelayMs(error) {
139288
139295
  }
139289
139296
  return null;
139290
139297
  }
139291
- var abortError = () => new APIUserAbortError, DEFAULT_MAX_RETRIES = 10, FLOOR_OUTPUT_TOKENS = 3000, MAX_529_RETRIES = 3, BASE_DELAY_MS = 500, FOREGROUND_529_RETRY_SOURCES, CONNECTION_WAIT_INITIAL_DELAY_MS = 5000, CONNECTION_WAIT_MAX_DELAY_MS = 60000, BODY_TRANSPORT_FAILURE_RE, STREAM_WATCHDOG_ABORT_PREFIX = "Stream idle timeout", PERSISTENT_MAX_BACKOFF_MS, PERSISTENT_RESET_CAP_MS, HEARTBEAT_INTERVAL_MS = 30000, DEFAULT_CONNECTION_WAIT_BACKOFF, CannotRetryError, FallbackTriggeredError, DEFAULT_FAST_MODE_FALLBACK_HOLD_MS, SHORT_RETRY_THRESHOLD_MS, MIN_COOLDOWN_MS;
139298
+ var abortError = () => new APIUserAbortError, DEFAULT_MAX_RETRIES = 10, FLOOR_OUTPUT_TOKENS = 3000, MAX_529_RETRIES = 3, BASE_DELAY_MS = 500, FOREGROUND_529_RETRY_SOURCES, CONNECTION_WAIT_INITIAL_DELAY_MS = 5000, CONNECTION_WAIT_MAX_DELAY_MS = 60000, BODY_TRANSPORT_FAILURE_RE, STREAM_WATCHDOG_ABORT_PREFIX = "Stream idle timeout", EmptyStreamError, PERSISTENT_MAX_BACKOFF_MS, PERSISTENT_RESET_CAP_MS, HEARTBEAT_INTERVAL_MS = 30000, DEFAULT_CONNECTION_WAIT_BACKOFF, CannotRetryError, FallbackTriggeredError, DEFAULT_FAST_MODE_FALLBACK_HOLD_MS, SHORT_RETRY_THRESHOLD_MS, MIN_COOLDOWN_MS;
139292
139299
  var init_retry = __esm(() => {
139293
139300
  init_sdk();
139294
139301
  init_aws();
@@ -139325,6 +139332,12 @@ var init_retry = __esm(() => {
139325
139332
  ...[]
139326
139333
  ]);
139327
139334
  BODY_TRANSPORT_FAILURE_RE = /socket connection was closed|^terminated$|other side closed|fetch failed/i;
139335
+ EmptyStreamError = class EmptyStreamError extends Error {
139336
+ constructor(message = "Stream ended before completing the response") {
139337
+ super(message);
139338
+ this.name = "EmptyStreamError";
139339
+ }
139340
+ };
139328
139341
  PERSISTENT_MAX_BACKOFF_MS = 5 * 60 * 1000;
139329
139342
  PERSISTENT_RESET_CAP_MS = 6 * 60 * 60 * 1000;
139330
139343
  DEFAULT_CONNECTION_WAIT_BACKOFF = {
@@ -235159,7 +235172,7 @@ function getAnthropicEnvMetadata() {
235159
235172
  function getBuildAgeMinutes() {
235160
235173
  if (false)
235161
235174
  ;
235162
- const buildTime = new Date("2026-10-20T14:00:47.620Z").getTime();
235175
+ const buildTime = new Date("2026-10-24T12:55:52.344Z").getTime();
235163
235176
  if (isNaN(buildTime))
235164
235177
  return;
235165
235178
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -242982,6 +242995,11 @@ var init_coreSchemas = __esm(() => {
242982
242995
  trigger: _enum(["manual", "auto"]),
242983
242996
  pre_tokens: number2(),
242984
242997
  post_tokens: number2().optional().describe("Estimated resulting context size after compaction."),
242998
+ context_snapshot: object({
242999
+ schemaVersion: literal(1),
243000
+ messages: array(unknown()),
243001
+ baselineTokens: number2()
243002
+ }).optional().describe("Opaque canonical post-compact context. Hosts persist and return it unchanged via Options.initialContextSnapshot."),
242985
243003
  preserved_segment: object({
242986
243004
  head_uuid: UUIDPlaceholder(),
242987
243005
  anchor_uuid: UUIDPlaceholder(),
@@ -291920,6 +291938,41 @@ var init_contextAnalysis = __esm(() => {
291920
291938
  init_slowOperations();
291921
291939
  });
291922
291940
 
291941
+ // src/session/contextSnapshot.ts
291942
+ function cloneSnapshotMessage(message) {
291943
+ if (message.type !== "system" || message.subtype !== "compact_boundary") {
291944
+ return message;
291945
+ }
291946
+ const { contextSnapshot: _ignored, ...compactMetadata } = message.compactMetadata;
291947
+ return {
291948
+ ...message,
291949
+ compactMetadata
291950
+ };
291951
+ }
291952
+ function isSnapshotMessage(message) {
291953
+ return CONTEXT_SNAPSHOT_MESSAGE_TYPES.has(message.type);
291954
+ }
291955
+ function buildContextSnapshot(messages) {
291956
+ const snapshotMessages = messages.filter(isSnapshotMessage).map(cloneSnapshotMessage);
291957
+ if (snapshotMessages.length === 0)
291958
+ return;
291959
+ return {
291960
+ schemaVersion: 1,
291961
+ messages: snapshotMessages,
291962
+ baselineTokens: tokenCountWithEstimation(snapshotMessages)
291963
+ };
291964
+ }
291965
+ var CONTEXT_SNAPSHOT_MESSAGE_TYPES;
291966
+ var init_contextSnapshot = __esm(() => {
291967
+ init_tokens();
291968
+ CONTEXT_SNAPSHOT_MESSAGE_TYPES = new Set([
291969
+ "assistant",
291970
+ "user",
291971
+ "attachment",
291972
+ "system"
291973
+ ]);
291974
+ });
291975
+
291923
291976
  // src/session/memory/memdir/types.ts
291924
291977
  var MEMORY_TYPE_VALUES;
291925
291978
  var init_types10 = __esm(() => {
@@ -292501,6 +292554,15 @@ function buildPostCompactMessages(result) {
292501
292554
  ...result.hookResults
292502
292555
  ];
292503
292556
  }
292557
+ function attachContextSnapshot(result) {
292558
+ if (!result.boundaryMarker?.compactMetadata)
292559
+ return result;
292560
+ const snapshot = buildContextSnapshot(buildPostCompactMessages(result));
292561
+ if (snapshot) {
292562
+ result.boundaryMarker.compactMetadata.contextSnapshot = snapshot;
292563
+ }
292564
+ return result;
292565
+ }
292504
292566
  function annotateBoundaryWithPreservedSegment(boundary, anchorUuid, messagesToKeep) {
292505
292567
  const keep = messagesToKeep ?? [];
292506
292568
  if (keep.length === 0)
@@ -293274,6 +293336,7 @@ var init_compact = __esm(() => {
293274
293336
  init_debug();
293275
293337
  init_errors2();
293276
293338
  init_fileStateCache();
293339
+ init_contextSnapshot();
293277
293340
  init_forkedAgent();
293278
293341
  init_hooks2();
293279
293342
  init_log3();
@@ -294054,7 +294117,7 @@ async function autoCompactIfNeeded(messages, toolUseContext, cacheSafeParams, qu
294054
294117
  markPostCompaction2();
294055
294118
  return {
294056
294119
  wasCompacted: true,
294057
- compactionResult: sessionMemoryResult
294120
+ compactionResult: attachContextSnapshot(sessionMemoryResult)
294058
294121
  };
294059
294122
  }
294060
294123
  try {
@@ -294063,7 +294126,7 @@ async function autoCompactIfNeeded(messages, toolUseContext, cacheSafeParams, qu
294063
294126
  runPostCompactCleanup(querySource);
294064
294127
  return {
294065
294128
  wasCompacted: true,
294066
- compactionResult,
294129
+ compactionResult: attachContextSnapshot(compactionResult),
294067
294130
  consecutiveFailures: 0
294068
294131
  };
294069
294132
  } catch (error) {
@@ -294088,13 +294151,19 @@ async function manualCompact(messages, toolUseContext, cacheSafeParams, instruct
294088
294151
  setLastSummarizedMessageId(undefined);
294089
294152
  runPostCompactCleanup();
294090
294153
  markPostCompaction2();
294091
- return { wasCompacted: true, compactionResult: sessionMemoryResult };
294154
+ return {
294155
+ wasCompacted: true,
294156
+ compactionResult: attachContextSnapshot(sessionMemoryResult)
294157
+ };
294092
294158
  }
294093
294159
  }
294094
294160
  const compactionResult = await deps.compactConversation(messages, toolUseContext, cacheSafeParams, false, instructions, false);
294095
294161
  setLastSummarizedMessageId(undefined);
294096
294162
  runPostCompactCleanup();
294097
- return { wasCompacted: true, compactionResult };
294163
+ return {
294164
+ wasCompacted: true,
294165
+ compactionResult: attachContextSnapshot(compactionResult)
294166
+ };
294098
294167
  }
294099
294168
  var MAX_OUTPUT_TOKENS_FOR_SUMMARY = 20000, AUTOCOMPACT_BUFFER_TOKENS = 13000, WARNING_THRESHOLD_BUFFER_TOKENS = 20000, ERROR_THRESHOLD_BUFFER_TOKENS = 20000, MANUAL_COMPACT_BUFFER_TOKENS = 3000, MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3;
294100
294169
  var init_autoCompact = __esm(() => {
@@ -297985,12 +298054,15 @@ ${deferredToolList}
297985
298054
  throw new Error("Stream idle timeout - no chunks received");
297986
298055
  }
297987
298056
  if (!partialMessage || newMessages.length === 0 && !stopReason) {
297988
- logForDebugging2(!partialMessage ? "Stream completed without receiving message_start event - triggering non-streaming fallback" : "Stream completed with message_start but no content blocks completed - triggering non-streaming fallback", { level: "error" });
298057
+ logForDebugging2(!partialMessage ? "Stream completed without receiving message_start event - triggering non-streaming fallback" : "Stream completed with message_start but no content blocks completed - incomplete stream", { level: "error" });
297989
298058
  logEvent("tengu_stream_no_events", {
297990
298059
  model: options.model,
297991
298060
  request_id: streamRequestId ?? "unknown"
297992
298061
  });
297993
- throw new Error("Stream ended without receiving any events");
298062
+ if (!partialMessage) {
298063
+ throw new Error("Stream ended without receiving any events");
298064
+ }
298065
+ throw new EmptyStreamError;
297994
298066
  }
297995
298067
  if (stallCount > 0) {
297996
298068
  logForDebugging2(`Streaming completed with ${stallCount} stall(s), total stall time: ${(totalStallTime / 1000).toFixed(1)}s`, { level: "warn" });
@@ -298035,6 +298107,9 @@ ${deferredToolList}
298035
298107
  throw new APIConnectionTimeoutError({ message: "Request timed out" });
298036
298108
  }
298037
298109
  }
298110
+ if (options.streamReplayEscape && newMessages.length === 0 && !signal.aborted && toReplayableStreamError(streamingError)) {
298111
+ throw streamingError;
298112
+ }
298038
298113
  const disableFallback = isEnvTruthy(resolveEnvVar("DISABLE_NONSTREAMING_FALLBACK")) || getFeatureValue_CACHED_MAY_BE_STALE("tengu_disable_streaming_to_non_streaming_fallback", false);
298039
298114
  if (disableFallback) {
298040
298115
  logForDebugging2(`Error streaming (non-streaming fallback disabled): ${errorMessage(streamingError)}`, { level: "error" });
@@ -435130,10 +435205,10 @@ var init_fuse = __esm(() => {
435130
435205
  });
435131
435206
 
435132
435207
  // missing-module-stub:/home/runner/work/opencow-agent-sdk/opencow-agent-sdk/src/cli/suggestions/skillUsageTracking.js
435133
- var noop17 = () => null, recordSkillUsage2, getSkillUsageScore;
435208
+ var noop17 = () => null, getSkillUsageScore, recordSkillUsage2;
435134
435209
  var init_skillUsageTracking2 = __esm(() => {
435135
- recordSkillUsage2 = noop17;
435136
435210
  getSkillUsageScore = noop17;
435211
+ recordSkillUsage2 = noop17;
435137
435212
  });
435138
435213
 
435139
435214
  // src/cli/suggestions/commandSuggestions.ts
@@ -445826,6 +445901,9 @@ function toSDKCompactMetadata(meta) {
445826
445901
  trigger: meta.trigger,
445827
445902
  pre_tokens: meta.preTokens,
445828
445903
  ...meta.postTokens !== undefined && { post_tokens: meta.postTokens },
445904
+ ...meta.contextSnapshot !== undefined && {
445905
+ context_snapshot: meta.contextSnapshot
445906
+ },
445829
445907
  ...seg && {
445830
445908
  preserved_segment: {
445831
445909
  head_uuid: seg.headUuid,
@@ -445841,6 +445919,9 @@ function fromSDKCompactMetadata(meta) {
445841
445919
  trigger: meta.trigger,
445842
445920
  preTokens: meta.pre_tokens,
445843
445921
  ...meta.post_tokens !== undefined && { postTokens: meta.post_tokens },
445922
+ ...meta.context_snapshot !== undefined && {
445923
+ contextSnapshot: meta.context_snapshot
445924
+ },
445844
445925
  ...seg && {
445845
445926
  preservedSegment: {
445846
445927
  headUuid: seg.head_uuid,
@@ -480124,7 +480205,7 @@ function buildPrimarySection() {
480124
480205
  }, undefined, false, undefined, this);
480125
480206
  return [{
480126
480207
  label: "Version",
480127
- value: "0.4.27"
480208
+ value: "0.4.28"
480128
480209
  }, {
480129
480210
  label: "Session name",
480130
480211
  value: nameValue
@@ -528204,7 +528285,7 @@ var init_bridge_kick = __esm(() => {
528204
528285
  var call58 = async () => {
528205
528286
  return {
528206
528287
  type: "text",
528207
- value: `${"99.0.0"} (built ${"2026-10-20T14:00:47.620Z"})`
528288
+ value: `${"99.0.0"} (built ${"2026-10-24T12:55:52.344Z"})`
528208
528289
  };
528209
528290
  }, version2, version_default;
528210
528291
  var init_version = __esm(() => {
@@ -545883,7 +545964,7 @@ function WelcomeV2() {
545883
545964
  dimColor: true,
545884
545965
  children: [
545885
545966
  "v",
545886
- "0.4.27",
545967
+ "0.4.28",
545887
545968
  " "
545888
545969
  ]
545889
545970
  }, undefined, true, undefined, this)
@@ -546083,7 +546164,7 @@ function WelcomeV2() {
546083
546164
  dimColor: true,
546084
546165
  children: [
546085
546166
  "v",
546086
- "0.4.27",
546167
+ "0.4.28",
546087
546168
  " "
546088
546169
  ]
546089
546170
  }, undefined, true, undefined, this)
@@ -546309,7 +546390,7 @@ function AppleTerminalWelcomeV2(t0) {
546309
546390
  dimColor: true,
546310
546391
  children: [
546311
546392
  "v",
546312
- "0.4.27",
546393
+ "0.4.28",
546313
546394
  " "
546314
546395
  ]
546315
546396
  }, undefined, true, undefined, this);
@@ -546563,7 +546644,7 @@ function AppleTerminalWelcomeV2(t0) {
546563
546644
  dimColor: true,
546564
546645
  children: [
546565
546646
  "v",
546566
- "0.4.27",
546647
+ "0.4.28",
546567
546648
  " "
546568
546649
  ]
546569
546650
  }, undefined, true, undefined, this);
@@ -567320,7 +567401,7 @@ Usage: claude --remote "your task description"`, () => gracefulShutdown(1));
567320
567401
  pendingHookMessages
567321
567402
  }, renderAndRun);
567322
567403
  }
567323
- }).version("0.4.27 (OpenCow)", "-v, --version", "Output the version number");
567404
+ }).version("0.4.28 (OpenCow)", "-v, --version", "Output the version number");
567324
567405
  program.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)");
567325
567406
  program.option("--tmux", "Create a tmux session for the worktree (requires --worktree). Uses iTerm2 native panes when available; use --tmux=classic for traditional tmux.");
567326
567407
  if (canUserConfigureAdvisor()) {
@@ -567915,7 +567996,7 @@ if (false) {}
567915
567996
  async function main2() {
567916
567997
  const args = process.argv.slice(2);
567917
567998
  if (args.length === 1 && (args[0] === "--version" || args[0] === "-v" || args[0] === "-V")) {
567918
- console.log(`${"0.4.27"} (OpenCow)`);
567999
+ console.log(`${"0.4.28"} (OpenCow)`);
567919
568000
  return;
567920
568001
  }
567921
568002
  if (args.includes("--provider")) {
@@ -568015,4 +568096,4 @@ async function main2() {
568015
568096
  }
568016
568097
  main2();
568017
568098
 
568018
- //# debugId=25745C572ABF064464756E2164756E21
568099
+ //# debugId=9998D5AD757C65B264756E2164756E21
package/dist/client.js CHANGED
@@ -119774,6 +119774,10 @@ function tokenCountWithEstimation(messages) {
119774
119774
  }
119775
119775
  return getTokenCountFromUsage(usage) + roughTokenCountEstimationForMessages(messages.slice(i + 1));
119776
119776
  }
119777
+ const baselineTokens = message?.contextBaselineTokens;
119778
+ if (typeof baselineTokens === "number" && Number.isFinite(baselineTokens) && baselineTokens >= 0) {
119779
+ return baselineTokens + roughTokenCountEstimationForMessages(messages.slice(i + 1));
119780
+ }
119777
119781
  i--;
119778
119782
  }
119779
119783
  return roughTokenCountEstimationForMessages(messages);
@@ -120441,6 +120445,75 @@ var init_contextAnalysis = __esm(() => {
120441
120445
  init_slowOperations();
120442
120446
  });
120443
120447
 
120448
+ // src/session/contextSnapshot.ts
120449
+ function cloneSnapshotMessage(message) {
120450
+ if (message.type !== "system" || message.subtype !== "compact_boundary") {
120451
+ return message;
120452
+ }
120453
+ const { contextSnapshot: _ignored, ...compactMetadata } = message.compactMetadata;
120454
+ return {
120455
+ ...message,
120456
+ compactMetadata
120457
+ };
120458
+ }
120459
+ function isSnapshotMessage(message) {
120460
+ return CONTEXT_SNAPSHOT_MESSAGE_TYPES.has(message.type);
120461
+ }
120462
+ function buildContextSnapshot(messages) {
120463
+ const snapshotMessages = messages.filter(isSnapshotMessage).map(cloneSnapshotMessage);
120464
+ if (snapshotMessages.length === 0)
120465
+ return;
120466
+ return {
120467
+ schemaVersion: 1,
120468
+ messages: snapshotMessages,
120469
+ baselineTokens: tokenCountWithEstimation(snapshotMessages)
120470
+ };
120471
+ }
120472
+ function isRecord2(value) {
120473
+ return value !== null && typeof value === "object" && !Array.isArray(value);
120474
+ }
120475
+ function isValidSnapshotMessage(value) {
120476
+ if (!isRecord2(value) || typeof value.type !== "string")
120477
+ return false;
120478
+ return CONTEXT_SNAPSHOT_MESSAGE_TYPES.has(value.type);
120479
+ }
120480
+ function normalizeContextSnapshot(raw) {
120481
+ if (!isRecord2(raw)) {
120482
+ throw new Error("Invalid context snapshot: expected an object");
120483
+ }
120484
+ if (raw.schemaVersion !== 1) {
120485
+ throw new Error(`Invalid context snapshot: unsupported schemaVersion ${String(raw.schemaVersion)}`);
120486
+ }
120487
+ if (!Array.isArray(raw.messages) || raw.messages.length === 0) {
120488
+ throw new Error("Invalid context snapshot: messages must be a non-empty array");
120489
+ }
120490
+ if (typeof raw.baselineTokens !== "number" || !Number.isFinite(raw.baselineTokens) || raw.baselineTokens < 0) {
120491
+ throw new Error("Invalid context snapshot: baselineTokens must be a non-negative number");
120492
+ }
120493
+ const messages = raw.messages.map((message, index) => {
120494
+ if (!isValidSnapshotMessage(message)) {
120495
+ throw new Error(`Invalid context snapshot: unsupported message at index ${index}`);
120496
+ }
120497
+ return message;
120498
+ });
120499
+ const baselineTokens = raw.baselineTokens;
120500
+ if (baselineTokens > 0) {
120501
+ const last = messages.at(-1);
120502
+ last.contextBaselineTokens = baselineTokens;
120503
+ }
120504
+ return { messages, baselineTokens };
120505
+ }
120506
+ var CONTEXT_SNAPSHOT_MESSAGE_TYPES;
120507
+ var init_contextSnapshot = __esm(() => {
120508
+ init_tokens();
120509
+ CONTEXT_SNAPSHOT_MESSAGE_TYPES = new Set([
120510
+ "assistant",
120511
+ "user",
120512
+ "attachment",
120513
+ "system"
120514
+ ]);
120515
+ });
120516
+
120444
120517
  // src/providers/anthropic/rateLimits/mocking.ts
120445
120518
  function processRateLimitHeaders(headers) {
120446
120519
  if (shouldProcessMockLimits()) {
@@ -122554,6 +122627,9 @@ function wrapBodyTransportError(error) {
122554
122627
  return new APIConnectionError({ message: error.message, cause: error });
122555
122628
  }
122556
122629
  function toReplayableStreamError(error) {
122630
+ if (error instanceof EmptyStreamError) {
122631
+ return new APIConnectionError({ message: error.message, cause: error });
122632
+ }
122557
122633
  const wrapped = error instanceof Error && error.message.startsWith(STREAM_WATCHDOG_ABORT_PREFIX) ? new APIConnectionError({ message: error.message, cause: error }) : wrapBodyTransportError(error);
122558
122634
  if (!(wrapped instanceof APIConnectionError) || wrapped instanceof APIConnectionTimeoutError || extractConnectionErrorDetails(wrapped)?.isSSLError) {
122559
122635
  return null;
@@ -123012,7 +123088,7 @@ function getRateLimitResetDelayMs(error) {
123012
123088
  }
123013
123089
  return null;
123014
123090
  }
123015
- var abortError = () => new APIUserAbortError, DEFAULT_MAX_RETRIES = 10, FLOOR_OUTPUT_TOKENS = 3000, MAX_529_RETRIES = 3, BASE_DELAY_MS = 500, FOREGROUND_529_RETRY_SOURCES, CONNECTION_WAIT_INITIAL_DELAY_MS = 5000, CONNECTION_WAIT_MAX_DELAY_MS = 60000, BODY_TRANSPORT_FAILURE_RE, STREAM_WATCHDOG_ABORT_PREFIX = "Stream idle timeout", PERSISTENT_MAX_BACKOFF_MS, PERSISTENT_RESET_CAP_MS, HEARTBEAT_INTERVAL_MS = 30000, DEFAULT_CONNECTION_WAIT_BACKOFF, CannotRetryError, FallbackTriggeredError, DEFAULT_FAST_MODE_FALLBACK_HOLD_MS, SHORT_RETRY_THRESHOLD_MS, MIN_COOLDOWN_MS;
123091
+ var abortError = () => new APIUserAbortError, DEFAULT_MAX_RETRIES = 10, FLOOR_OUTPUT_TOKENS = 3000, MAX_529_RETRIES = 3, BASE_DELAY_MS = 500, FOREGROUND_529_RETRY_SOURCES, CONNECTION_WAIT_INITIAL_DELAY_MS = 5000, CONNECTION_WAIT_MAX_DELAY_MS = 60000, BODY_TRANSPORT_FAILURE_RE, STREAM_WATCHDOG_ABORT_PREFIX = "Stream idle timeout", EmptyStreamError, PERSISTENT_MAX_BACKOFF_MS, PERSISTENT_RESET_CAP_MS, HEARTBEAT_INTERVAL_MS = 30000, DEFAULT_CONNECTION_WAIT_BACKOFF, CannotRetryError, FallbackTriggeredError, DEFAULT_FAST_MODE_FALLBACK_HOLD_MS, SHORT_RETRY_THRESHOLD_MS, MIN_COOLDOWN_MS;
123016
123092
  var init_retry = __esm(() => {
123017
123093
  init_sdk();
123018
123094
  init_aws();
@@ -123049,6 +123125,12 @@ var init_retry = __esm(() => {
123049
123125
  ...[]
123050
123126
  ]);
123051
123127
  BODY_TRANSPORT_FAILURE_RE = /socket connection was closed|^terminated$|other side closed|fetch failed/i;
123128
+ EmptyStreamError = class EmptyStreamError extends Error {
123129
+ constructor(message = "Stream ended before completing the response") {
123130
+ super(message);
123131
+ this.name = "EmptyStreamError";
123132
+ }
123133
+ };
123052
123134
  PERSISTENT_MAX_BACKOFF_MS = 5 * 60 * 1000;
123053
123135
  PERSISTENT_RESET_CAP_MS = 6 * 60 * 60 * 1000;
123054
123136
  DEFAULT_CONNECTION_WAIT_BACKOFF = {
@@ -179946,6 +180028,11 @@ var init_coreSchemas = __esm(() => {
179946
180028
  trigger: _enum(["manual", "auto"]),
179947
180029
  pre_tokens: number2(),
179948
180030
  post_tokens: number2().optional().describe("Estimated resulting context size after compaction."),
180031
+ context_snapshot: object2({
180032
+ schemaVersion: literal(1),
180033
+ messages: array(unknown()),
180034
+ baselineTokens: number2()
180035
+ }).optional().describe("Opaque canonical post-compact context. Hosts persist and return it unchanged via Options.initialContextSnapshot."),
179949
180036
  preserved_segment: object2({
179950
180037
  head_uuid: UUIDPlaceholder(),
179951
180038
  anchor_uuid: UUIDPlaceholder(),
@@ -214416,7 +214503,7 @@ async function loadPluginSettings(pluginPath, manifest) {
214416
214503
  try {
214417
214504
  const content = await getFsImplementation().readFile(settingsJsonPath, { encoding: "utf-8" });
214418
214505
  const parsed = jsonParse(content);
214419
- if (isRecord2(parsed)) {
214506
+ if (isRecord3(parsed)) {
214420
214507
  const filtered = parsePluginSettings(parsed);
214421
214508
  if (filtered) {
214422
214509
  logForDebugging(`Loaded settings from settings.json for plugin ${manifest.name}`);
@@ -215095,7 +215182,7 @@ function cachePluginSettings(plugins) {
215095
215182
  logForDebugging(`Cached plugin settings with keys: ${Object.keys(settings).join(", ")}`);
215096
215183
  }
215097
215184
  }
215098
- function isRecord2(value) {
215185
+ function isRecord3(value) {
215099
215186
  return typeof value === "object" && value !== null && !Array.isArray(value);
215100
215187
  }
215101
215188
  var PluginSettingsSchema, loadAllPlugins, loadAllPluginsCacheOnly;
@@ -227296,7 +227383,7 @@ function getAnthropicEnvMetadata() {
227296
227383
  function getBuildAgeMinutes() {
227297
227384
  if (false)
227298
227385
  ;
227299
- const buildTime = new Date("2026-10-20T14:00:47.620Z").getTime();
227386
+ const buildTime = new Date("2026-10-24T12:55:52.344Z").getTime();
227300
227387
  if (isNaN(buildTime))
227301
227388
  return;
227302
227389
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -228494,6 +228581,15 @@ function buildPostCompactMessages(result) {
228494
228581
  ...result.hookResults
228495
228582
  ];
228496
228583
  }
228584
+ function attachContextSnapshot(result) {
228585
+ if (!result.boundaryMarker?.compactMetadata)
228586
+ return result;
228587
+ const snapshot = buildContextSnapshot(buildPostCompactMessages(result));
228588
+ if (snapshot) {
228589
+ result.boundaryMarker.compactMetadata.contextSnapshot = snapshot;
228590
+ }
228591
+ return result;
228592
+ }
228497
228593
  function annotateBoundaryWithPreservedSegment(boundary, anchorUuid, messagesToKeep) {
228498
228594
  const keep = messagesToKeep ?? [];
228499
228595
  if (keep.length === 0)
@@ -229064,6 +229160,7 @@ var init_compact = __esm(() => {
229064
229160
  init_debug();
229065
229161
  init_errors5();
229066
229162
  init_fileStateCache();
229163
+ init_contextSnapshot();
229067
229164
  init_forkedAgent();
229068
229165
  init_hooks2();
229069
229166
  init_log2();
@@ -229686,7 +229783,7 @@ async function autoCompactIfNeeded(messages, toolUseContext, cacheSafeParams, qu
229686
229783
  markPostCompaction();
229687
229784
  return {
229688
229785
  wasCompacted: true,
229689
- compactionResult: sessionMemoryResult
229786
+ compactionResult: attachContextSnapshot(sessionMemoryResult)
229690
229787
  };
229691
229788
  }
229692
229789
  try {
@@ -229695,7 +229792,7 @@ async function autoCompactIfNeeded(messages, toolUseContext, cacheSafeParams, qu
229695
229792
  runPostCompactCleanup(querySource);
229696
229793
  return {
229697
229794
  wasCompacted: true,
229698
- compactionResult,
229795
+ compactionResult: attachContextSnapshot(compactionResult),
229699
229796
  consecutiveFailures: 0
229700
229797
  };
229701
229798
  } catch (error) {
@@ -229720,13 +229817,19 @@ async function manualCompact(messages, toolUseContext, cacheSafeParams, instruct
229720
229817
  setLastSummarizedMessageId(undefined);
229721
229818
  runPostCompactCleanup();
229722
229819
  markPostCompaction();
229723
- return { wasCompacted: true, compactionResult: sessionMemoryResult };
229820
+ return {
229821
+ wasCompacted: true,
229822
+ compactionResult: attachContextSnapshot(sessionMemoryResult)
229823
+ };
229724
229824
  }
229725
229825
  }
229726
229826
  const compactionResult = await deps.compactConversation(messages, toolUseContext, cacheSafeParams, false, instructions, false);
229727
229827
  setLastSummarizedMessageId(undefined);
229728
229828
  runPostCompactCleanup();
229729
- return { wasCompacted: true, compactionResult };
229829
+ return {
229830
+ wasCompacted: true,
229831
+ compactionResult: attachContextSnapshot(compactionResult)
229832
+ };
229730
229833
  }
229731
229834
  var MAX_OUTPUT_TOKENS_FOR_SUMMARY = 20000, AUTOCOMPACT_BUFFER_TOKENS = 13000, WARNING_THRESHOLD_BUFFER_TOKENS = 20000, ERROR_THRESHOLD_BUFFER_TOKENS = 20000, MANUAL_COMPACT_BUFFER_TOKENS = 3000, MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3;
229732
229835
  var init_autoCompact = __esm(() => {
@@ -238778,12 +238881,15 @@ ${deferredToolList}
238778
238881
  throw new Error("Stream idle timeout - no chunks received");
238779
238882
  }
238780
238883
  if (!partialMessage || newMessages.length === 0 && !stopReason) {
238781
- logForDebugging(!partialMessage ? "Stream completed without receiving message_start event - triggering non-streaming fallback" : "Stream completed with message_start but no content blocks completed - triggering non-streaming fallback", { level: "error" });
238884
+ logForDebugging(!partialMessage ? "Stream completed without receiving message_start event - triggering non-streaming fallback" : "Stream completed with message_start but no content blocks completed - incomplete stream", { level: "error" });
238782
238885
  logEvent("tengu_stream_no_events", {
238783
238886
  model: options.model,
238784
238887
  request_id: streamRequestId ?? "unknown"
238785
238888
  });
238786
- throw new Error("Stream ended without receiving any events");
238889
+ if (!partialMessage) {
238890
+ throw new Error("Stream ended without receiving any events");
238891
+ }
238892
+ throw new EmptyStreamError;
238787
238893
  }
238788
238894
  if (stallCount > 0) {
238789
238895
  logForDebugging(`Streaming completed with ${stallCount} stall(s), total stall time: ${(totalStallTime / 1000).toFixed(1)}s`, { level: "warn" });
@@ -238828,6 +238934,9 @@ ${deferredToolList}
238828
238934
  throw new APIConnectionTimeoutError({ message: "Request timed out" });
238829
238935
  }
238830
238936
  }
238937
+ if (options.streamReplayEscape && newMessages.length === 0 && !signal.aborted && toReplayableStreamError(streamingError)) {
238938
+ throw streamingError;
238939
+ }
238831
238940
  const disableFallback = isEnvTruthy(resolveEnvVar("DISABLE_NONSTREAMING_FALLBACK")) || getFeatureValue_CACHED_MAY_BE_STALE("tengu_disable_streaming_to_non_streaming_fallback", false);
238832
238941
  if (disableFallback) {
238833
238942
  logForDebugging(`Error streaming (non-streaming fallback disabled): ${errorMessage(streamingError)}`, { level: "error" });
@@ -270690,6 +270799,9 @@ function toSDKCompactMetadata(meta) {
270690
270799
  trigger: meta.trigger,
270691
270800
  pre_tokens: meta.preTokens,
270692
270801
  ...meta.postTokens !== undefined && { post_tokens: meta.postTokens },
270802
+ ...meta.contextSnapshot !== undefined && {
270803
+ context_snapshot: meta.contextSnapshot
270804
+ },
270693
270805
  ...seg && {
270694
270806
  preserved_segment: {
270695
270807
  head_uuid: seg.headUuid,
@@ -272942,6 +273054,7 @@ function registerSdkInlineSkillHandler() {
272942
273054
  init_backgroundAbortRegistry();
272943
273055
  init_backgroundTaskScope();
272944
273056
  init_sessionStorage();
273057
+ init_contextSnapshot();
272945
273058
  init_branding();
272946
273059
  init_hooks2();
272947
273060
  init_debug();
@@ -278977,7 +279090,9 @@ function runSdkQueryRuntime(params) {
278977
279090
  const appStateStore = buildAppStateStore(applyFileCheckpointingInitialState(applyDisallowedTools(applyPermissionMode(getDefaultAppState(), options.permissionMode), options.disallowedTools), options.fileCheckpointing?.initialState), fileCheckpointingOnChange);
278978
279091
  const releaseBackgroundTaskScope = bindBackgroundTaskScope(appStateStore.setAppState, ownership.backgroundTaskScope);
278979
279092
  const initialMessagesForReplay = options.resume ? loadAndRestampHistoricalMessages(options.resume, queryContext.identity.sessionId, options.initialMessages) : options.initialMessages;
278980
- let mutableMessages = normalizeInitialMessages(initialMessagesForReplay);
279093
+ const contextSnapshot = options.initialContextSnapshot === undefined ? undefined : normalizeContextSnapshot(options.initialContextSnapshot);
279094
+ const initialMessages = normalizeInitialMessages(initialMessagesForReplay);
279095
+ let mutableMessages = contextSnapshot ? [...contextSnapshot.messages, ...initialMessages] : initialMessages;
278981
279096
  let readFileCache = createFileStateCacheWithSizeLimit(DEFAULT_READ_FILE_CACHE_SIZE);
278982
279097
  const dynamicClients = [];
278983
279098
  const sdkClients = [];
@@ -280500,4 +280615,4 @@ export {
280500
280615
  watchScheduledTasks
280501
280616
  };
280502
280617
 
280503
- //# debugId=7F28B624A121A3CF64756E2164756E21
280618
+ //# debugId=E18A4788D5E5D36064756E2164756E21
@@ -3,7 +3,7 @@ import type { QuerySource } from '../../constants/querySource.js';
3
3
  import type { CanUseToolFn } from '../../types/canUseTool.js';
4
4
  import type { ToolUseContext } from '../../Tool.js';
5
5
  import type { AgentId } from '../../types/ids.js';
6
- import type { AssistantMessage, AttachmentMessage, HookResultMessage, Message, PartialCompactDirection, SystemCompactBoundaryMessage, SystemMessage, UserMessage } from '../../types/message.js';
6
+ import type { AssistantMessage, AttachmentMessage, HookResultMessage, Message, PartialCompactDirection, SystemCompactBoundaryMessage, UserMessage } from '../../types/message.js';
7
7
  import { type CacheSafeParams } from '../../session/forkedAgent.js';
8
8
  import { getTokenUsage } from '../../session/tokens.js';
9
9
  export declare const POST_COMPACT_MAX_FILES_TO_RESTORE = 5;
@@ -53,7 +53,7 @@ export declare const ERROR_MESSAGE_PROMPT_TOO_LONG = "Conversation too long. Pre
53
53
  export declare const ERROR_MESSAGE_USER_ABORT = "API Error: Request was aborted.";
54
54
  export declare const ERROR_MESSAGE_INCOMPLETE_RESPONSE = "Compaction interrupted \u00B7 This may be due to network issues \u2014 please try again.";
55
55
  export interface CompactionResult {
56
- boundaryMarker: SystemMessage;
56
+ boundaryMarker: SystemCompactBoundaryMessage;
57
57
  summaryMessages: UserMessage[];
58
58
  attachments: AttachmentMessage[];
59
59
  hookResults: HookResultMessage[];
@@ -82,6 +82,12 @@ export type RecompactionInfo = {
82
82
  * Order: boundaryMarker, summaryMessages, messagesToKeep, attachments, hookResults
83
83
  */
84
84
  export declare function buildPostCompactMessages(result: CompactionResult): Message[];
85
+ /**
86
+ * Attach the complete post-compact context to the public boundary metadata.
87
+ * The snapshot is built before the boundary is annotated so its embedded copy
88
+ * cannot reference itself.
89
+ */
90
+ export declare function attachContextSnapshot(result: CompactionResult): CompactionResult;
85
91
  /**
86
92
  * Annotate a compact boundary with relink metadata for messagesToKeep.
87
93
  * Preserved messages keep their original parentUuids on disk (dedup-skipped);
@@ -28,7 +28,8 @@ export declare function adjustParamsForNonStreaming<T extends {
28
28
  export type Options = {
29
29
  /**
30
30
  * 内部:由 replayStreamOnTransportError 置位。为 true 时,读流阶段的可重放传输错误
31
- * (且尚无完整消息送出)作为异常抛给调用方重放,而不是转成 assistant 错误消息。
31
+ * (且尚无完整消息送出)优先作为异常抛给调用方重放,而不是先走 non-streaming
32
+ * fallback 或转成 assistant 错误消息;预算用尽后该标记为 false,恢复原路径。
32
33
  */
33
34
  streamReplayEscape?: boolean;
34
35
  getToolPermissionContext: () => Promise<ToolPermissionContext>;
@@ -4389,6 +4389,11 @@ export declare const StdoutMessageSchema: () => z.ZodUnion<readonly [z.ZodUnion<
4389
4389
  }>;
4390
4390
  pre_tokens: z.ZodNumber;
4391
4391
  post_tokens: z.ZodOptional<z.ZodNumber>;
4392
+ context_snapshot: z.ZodOptional<z.ZodObject<{
4393
+ schemaVersion: z.ZodLiteral<1>;
4394
+ messages: z.ZodArray<z.ZodUnknown>;
4395
+ baselineTokens: z.ZodNumber;
4396
+ }, z.core.$strip>>;
4392
4397
  preserved_segment: z.ZodOptional<z.ZodObject<{
4393
4398
  head_uuid: z.ZodString;
4394
4399
  anchor_uuid: z.ZodString;
@@ -2509,6 +2509,11 @@ export declare const SDKCompactBoundaryMessageSchema: () => z.ZodObject<{
2509
2509
  }>;
2510
2510
  pre_tokens: z.ZodNumber;
2511
2511
  post_tokens: z.ZodOptional<z.ZodNumber>;
2512
+ context_snapshot: z.ZodOptional<z.ZodObject<{
2513
+ schemaVersion: z.ZodLiteral<1>;
2514
+ messages: z.ZodArray<z.ZodUnknown>;
2515
+ baselineTokens: z.ZodNumber;
2516
+ }, z.core.$strip>>;
2512
2517
  preserved_segment: z.ZodOptional<z.ZodObject<{
2513
2518
  head_uuid: z.ZodString;
2514
2519
  anchor_uuid: z.ZodString;
@@ -2918,6 +2923,11 @@ export declare const SDKMessageSchema: () => z.ZodUnion<readonly [z.ZodObject<{
2918
2923
  }>;
2919
2924
  pre_tokens: z.ZodNumber;
2920
2925
  post_tokens: z.ZodOptional<z.ZodNumber>;
2926
+ context_snapshot: z.ZodOptional<z.ZodObject<{
2927
+ schemaVersion: z.ZodLiteral<1>;
2928
+ messages: z.ZodArray<z.ZodUnknown>;
2929
+ baselineTokens: z.ZodNumber;
2930
+ }, z.core.$strip>>;
2921
2931
  preserved_segment: z.ZodOptional<z.ZodObject<{
2922
2932
  head_uuid: z.ZodString;
2923
2933
  anchor_uuid: z.ZodString;
@@ -1,6 +1,8 @@
1
1
  import type { ChildProcessWithoutNullStreams } from 'node:child_process';
2
2
  import type { ModelProviders } from '../../providers/shared/routing.js';
3
3
  import type { UploadMediaFn } from '../../types/toolRuntime.js';
4
+ import type { SDKContextSnapshot } from '../../types/contextSnapshot.js';
5
+ export type { SDKContextSnapshot } from '../../types/contextSnapshot.js';
4
6
  export type { ModelProviders, ModelProviderConfig, } from '../../providers/shared/routing.js';
5
7
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
6
8
  import type { CallToolResult, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
@@ -527,6 +529,17 @@ export type Options = {
527
529
  */
528
530
  spawnClaudeCodeProcess?: (opts: SpawnOptions) => SpawnedProcess;
529
531
  initialMessages?: unknown[];
532
+ /**
533
+ * Opaque canonical post-compact context returned by a previous
534
+ * `compact_metadata.context_snapshot`. Hosts persist it unchanged and pass
535
+ * it back after restart. When both this and `initialMessages` are provided,
536
+ * the snapshot is restored first and `initialMessages` are appended as the
537
+ * tail (normally events recorded after the snapshot).
538
+ *
539
+ * Invalid snapshots throw instead of silently falling back to a smaller
540
+ * context.
541
+ */
542
+ initialContextSnapshot?: SDKContextSnapshot;
530
543
  /**
531
544
  * Controls whether the SDK performs ANY filesystem reads of its own
532
545
  * during session execution.
@@ -10,7 +10,19 @@ export declare const BASE_DELAY_MS = 500;
10
10
  * 只认传输特征(cause 带系统错误码或固定文案),普通 TypeError 仍按程序错误处理。
11
11
  */
12
12
  export declare function wrapBodyTransportError(error: unknown): unknown;
13
- /** 读流阶段的传输中断(对端关闭、socket 断开、空闲看门狗中止)可以整条请求重放;其余不行。 */
13
+ /**
14
+ * 已收到 message_start、但流在任何完整 content block / stop_reason 之前结束。
15
+ * 这类空流可能由代理瞬断造成,且尚未提交任何模型输出,可以安全地整条请求重放。
16
+ * 没有 message_start 的 200 空响应不归入此类:它更像网关不兼容 SSE,保留
17
+ * non-streaming fallback(见 controller/loop.ts)。
18
+ *
19
+ * 重放前可能已经向消费方 yield 过半截 stream_event。消费方必须在新的
20
+ * message_start 到达时重置未完成的流式内容;SDK 不保证撤回已经展示的部分。
21
+ */
22
+ export declare class EmptyStreamError extends Error {
23
+ constructor(message?: string);
24
+ }
25
+ /** 读流阶段的传输中断(对端关闭、socket 断开、空闲看门狗中止、空流)可以整条请求重放;其余不行。 */
14
26
  export declare function toReplayableStreamError(error: unknown): APIConnectionError | null;
15
27
  /**
16
28
  * 流式响应读到一半断开、且还没有任何完整消息送出时,整个流式请求重放。
package/dist/sdk.js CHANGED
@@ -119774,6 +119774,10 @@ function tokenCountWithEstimation(messages) {
119774
119774
  }
119775
119775
  return getTokenCountFromUsage(usage) + roughTokenCountEstimationForMessages(messages.slice(i + 1));
119776
119776
  }
119777
+ const baselineTokens = message?.contextBaselineTokens;
119778
+ if (typeof baselineTokens === "number" && Number.isFinite(baselineTokens) && baselineTokens >= 0) {
119779
+ return baselineTokens + roughTokenCountEstimationForMessages(messages.slice(i + 1));
119780
+ }
119777
119781
  i--;
119778
119782
  }
119779
119783
  return roughTokenCountEstimationForMessages(messages);
@@ -120441,6 +120445,75 @@ var init_contextAnalysis = __esm(() => {
120441
120445
  init_slowOperations();
120442
120446
  });
120443
120447
 
120448
+ // src/session/contextSnapshot.ts
120449
+ function cloneSnapshotMessage(message) {
120450
+ if (message.type !== "system" || message.subtype !== "compact_boundary") {
120451
+ return message;
120452
+ }
120453
+ const { contextSnapshot: _ignored, ...compactMetadata } = message.compactMetadata;
120454
+ return {
120455
+ ...message,
120456
+ compactMetadata
120457
+ };
120458
+ }
120459
+ function isSnapshotMessage(message) {
120460
+ return CONTEXT_SNAPSHOT_MESSAGE_TYPES.has(message.type);
120461
+ }
120462
+ function buildContextSnapshot(messages) {
120463
+ const snapshotMessages = messages.filter(isSnapshotMessage).map(cloneSnapshotMessage);
120464
+ if (snapshotMessages.length === 0)
120465
+ return;
120466
+ return {
120467
+ schemaVersion: 1,
120468
+ messages: snapshotMessages,
120469
+ baselineTokens: tokenCountWithEstimation(snapshotMessages)
120470
+ };
120471
+ }
120472
+ function isRecord2(value) {
120473
+ return value !== null && typeof value === "object" && !Array.isArray(value);
120474
+ }
120475
+ function isValidSnapshotMessage(value) {
120476
+ if (!isRecord2(value) || typeof value.type !== "string")
120477
+ return false;
120478
+ return CONTEXT_SNAPSHOT_MESSAGE_TYPES.has(value.type);
120479
+ }
120480
+ function normalizeContextSnapshot(raw) {
120481
+ if (!isRecord2(raw)) {
120482
+ throw new Error("Invalid context snapshot: expected an object");
120483
+ }
120484
+ if (raw.schemaVersion !== 1) {
120485
+ throw new Error(`Invalid context snapshot: unsupported schemaVersion ${String(raw.schemaVersion)}`);
120486
+ }
120487
+ if (!Array.isArray(raw.messages) || raw.messages.length === 0) {
120488
+ throw new Error("Invalid context snapshot: messages must be a non-empty array");
120489
+ }
120490
+ if (typeof raw.baselineTokens !== "number" || !Number.isFinite(raw.baselineTokens) || raw.baselineTokens < 0) {
120491
+ throw new Error("Invalid context snapshot: baselineTokens must be a non-negative number");
120492
+ }
120493
+ const messages = raw.messages.map((message, index) => {
120494
+ if (!isValidSnapshotMessage(message)) {
120495
+ throw new Error(`Invalid context snapshot: unsupported message at index ${index}`);
120496
+ }
120497
+ return message;
120498
+ });
120499
+ const baselineTokens = raw.baselineTokens;
120500
+ if (baselineTokens > 0) {
120501
+ const last = messages.at(-1);
120502
+ last.contextBaselineTokens = baselineTokens;
120503
+ }
120504
+ return { messages, baselineTokens };
120505
+ }
120506
+ var CONTEXT_SNAPSHOT_MESSAGE_TYPES;
120507
+ var init_contextSnapshot = __esm(() => {
120508
+ init_tokens();
120509
+ CONTEXT_SNAPSHOT_MESSAGE_TYPES = new Set([
120510
+ "assistant",
120511
+ "user",
120512
+ "attachment",
120513
+ "system"
120514
+ ]);
120515
+ });
120516
+
120444
120517
  // src/providers/anthropic/rateLimits/mocking.ts
120445
120518
  function processRateLimitHeaders(headers) {
120446
120519
  if (shouldProcessMockLimits()) {
@@ -122554,6 +122627,9 @@ function wrapBodyTransportError(error) {
122554
122627
  return new APIConnectionError({ message: error.message, cause: error });
122555
122628
  }
122556
122629
  function toReplayableStreamError(error) {
122630
+ if (error instanceof EmptyStreamError) {
122631
+ return new APIConnectionError({ message: error.message, cause: error });
122632
+ }
122557
122633
  const wrapped = error instanceof Error && error.message.startsWith(STREAM_WATCHDOG_ABORT_PREFIX) ? new APIConnectionError({ message: error.message, cause: error }) : wrapBodyTransportError(error);
122558
122634
  if (!(wrapped instanceof APIConnectionError) || wrapped instanceof APIConnectionTimeoutError || extractConnectionErrorDetails(wrapped)?.isSSLError) {
122559
122635
  return null;
@@ -123012,7 +123088,7 @@ function getRateLimitResetDelayMs(error) {
123012
123088
  }
123013
123089
  return null;
123014
123090
  }
123015
- var abortError = () => new APIUserAbortError, DEFAULT_MAX_RETRIES = 10, FLOOR_OUTPUT_TOKENS = 3000, MAX_529_RETRIES = 3, BASE_DELAY_MS = 500, FOREGROUND_529_RETRY_SOURCES, CONNECTION_WAIT_INITIAL_DELAY_MS = 5000, CONNECTION_WAIT_MAX_DELAY_MS = 60000, BODY_TRANSPORT_FAILURE_RE, STREAM_WATCHDOG_ABORT_PREFIX = "Stream idle timeout", PERSISTENT_MAX_BACKOFF_MS, PERSISTENT_RESET_CAP_MS, HEARTBEAT_INTERVAL_MS = 30000, DEFAULT_CONNECTION_WAIT_BACKOFF, CannotRetryError, FallbackTriggeredError, DEFAULT_FAST_MODE_FALLBACK_HOLD_MS, SHORT_RETRY_THRESHOLD_MS, MIN_COOLDOWN_MS;
123091
+ var abortError = () => new APIUserAbortError, DEFAULT_MAX_RETRIES = 10, FLOOR_OUTPUT_TOKENS = 3000, MAX_529_RETRIES = 3, BASE_DELAY_MS = 500, FOREGROUND_529_RETRY_SOURCES, CONNECTION_WAIT_INITIAL_DELAY_MS = 5000, CONNECTION_WAIT_MAX_DELAY_MS = 60000, BODY_TRANSPORT_FAILURE_RE, STREAM_WATCHDOG_ABORT_PREFIX = "Stream idle timeout", EmptyStreamError, PERSISTENT_MAX_BACKOFF_MS, PERSISTENT_RESET_CAP_MS, HEARTBEAT_INTERVAL_MS = 30000, DEFAULT_CONNECTION_WAIT_BACKOFF, CannotRetryError, FallbackTriggeredError, DEFAULT_FAST_MODE_FALLBACK_HOLD_MS, SHORT_RETRY_THRESHOLD_MS, MIN_COOLDOWN_MS;
123016
123092
  var init_retry = __esm(() => {
123017
123093
  init_sdk();
123018
123094
  init_aws();
@@ -123049,6 +123125,12 @@ var init_retry = __esm(() => {
123049
123125
  ...[]
123050
123126
  ]);
123051
123127
  BODY_TRANSPORT_FAILURE_RE = /socket connection was closed|^terminated$|other side closed|fetch failed/i;
123128
+ EmptyStreamError = class EmptyStreamError extends Error {
123129
+ constructor(message = "Stream ended before completing the response") {
123130
+ super(message);
123131
+ this.name = "EmptyStreamError";
123132
+ }
123133
+ };
123052
123134
  PERSISTENT_MAX_BACKOFF_MS = 5 * 60 * 1000;
123053
123135
  PERSISTENT_RESET_CAP_MS = 6 * 60 * 60 * 1000;
123054
123136
  DEFAULT_CONNECTION_WAIT_BACKOFF = {
@@ -179946,6 +180028,11 @@ var init_coreSchemas = __esm(() => {
179946
180028
  trigger: _enum(["manual", "auto"]),
179947
180029
  pre_tokens: number2(),
179948
180030
  post_tokens: number2().optional().describe("Estimated resulting context size after compaction."),
180031
+ context_snapshot: object2({
180032
+ schemaVersion: literal(1),
180033
+ messages: array(unknown()),
180034
+ baselineTokens: number2()
180035
+ }).optional().describe("Opaque canonical post-compact context. Hosts persist and return it unchanged via Options.initialContextSnapshot."),
179949
180036
  preserved_segment: object2({
179950
180037
  head_uuid: UUIDPlaceholder(),
179951
180038
  anchor_uuid: UUIDPlaceholder(),
@@ -214416,7 +214503,7 @@ async function loadPluginSettings(pluginPath, manifest) {
214416
214503
  try {
214417
214504
  const content = await getFsImplementation().readFile(settingsJsonPath, { encoding: "utf-8" });
214418
214505
  const parsed = jsonParse(content);
214419
- if (isRecord2(parsed)) {
214506
+ if (isRecord3(parsed)) {
214420
214507
  const filtered = parsePluginSettings(parsed);
214421
214508
  if (filtered) {
214422
214509
  logForDebugging(`Loaded settings from settings.json for plugin ${manifest.name}`);
@@ -215095,7 +215182,7 @@ function cachePluginSettings(plugins) {
215095
215182
  logForDebugging(`Cached plugin settings with keys: ${Object.keys(settings).join(", ")}`);
215096
215183
  }
215097
215184
  }
215098
- function isRecord2(value) {
215185
+ function isRecord3(value) {
215099
215186
  return typeof value === "object" && value !== null && !Array.isArray(value);
215100
215187
  }
215101
215188
  var PluginSettingsSchema, loadAllPlugins, loadAllPluginsCacheOnly;
@@ -227296,7 +227383,7 @@ function getAnthropicEnvMetadata() {
227296
227383
  function getBuildAgeMinutes() {
227297
227384
  if (false)
227298
227385
  ;
227299
- const buildTime = new Date("2026-10-20T14:00:47.620Z").getTime();
227386
+ const buildTime = new Date("2026-10-24T12:55:52.344Z").getTime();
227300
227387
  if (isNaN(buildTime))
227301
227388
  return;
227302
227389
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -228494,6 +228581,15 @@ function buildPostCompactMessages(result) {
228494
228581
  ...result.hookResults
228495
228582
  ];
228496
228583
  }
228584
+ function attachContextSnapshot(result) {
228585
+ if (!result.boundaryMarker?.compactMetadata)
228586
+ return result;
228587
+ const snapshot = buildContextSnapshot(buildPostCompactMessages(result));
228588
+ if (snapshot) {
228589
+ result.boundaryMarker.compactMetadata.contextSnapshot = snapshot;
228590
+ }
228591
+ return result;
228592
+ }
228497
228593
  function annotateBoundaryWithPreservedSegment(boundary, anchorUuid, messagesToKeep) {
228498
228594
  const keep = messagesToKeep ?? [];
228499
228595
  if (keep.length === 0)
@@ -229064,6 +229160,7 @@ var init_compact = __esm(() => {
229064
229160
  init_debug();
229065
229161
  init_errors5();
229066
229162
  init_fileStateCache();
229163
+ init_contextSnapshot();
229067
229164
  init_forkedAgent();
229068
229165
  init_hooks2();
229069
229166
  init_log2();
@@ -229686,7 +229783,7 @@ async function autoCompactIfNeeded(messages, toolUseContext, cacheSafeParams, qu
229686
229783
  markPostCompaction();
229687
229784
  return {
229688
229785
  wasCompacted: true,
229689
- compactionResult: sessionMemoryResult
229786
+ compactionResult: attachContextSnapshot(sessionMemoryResult)
229690
229787
  };
229691
229788
  }
229692
229789
  try {
@@ -229695,7 +229792,7 @@ async function autoCompactIfNeeded(messages, toolUseContext, cacheSafeParams, qu
229695
229792
  runPostCompactCleanup(querySource);
229696
229793
  return {
229697
229794
  wasCompacted: true,
229698
- compactionResult,
229795
+ compactionResult: attachContextSnapshot(compactionResult),
229699
229796
  consecutiveFailures: 0
229700
229797
  };
229701
229798
  } catch (error) {
@@ -229720,13 +229817,19 @@ async function manualCompact(messages, toolUseContext, cacheSafeParams, instruct
229720
229817
  setLastSummarizedMessageId(undefined);
229721
229818
  runPostCompactCleanup();
229722
229819
  markPostCompaction();
229723
- return { wasCompacted: true, compactionResult: sessionMemoryResult };
229820
+ return {
229821
+ wasCompacted: true,
229822
+ compactionResult: attachContextSnapshot(sessionMemoryResult)
229823
+ };
229724
229824
  }
229725
229825
  }
229726
229826
  const compactionResult = await deps.compactConversation(messages, toolUseContext, cacheSafeParams, false, instructions, false);
229727
229827
  setLastSummarizedMessageId(undefined);
229728
229828
  runPostCompactCleanup();
229729
- return { wasCompacted: true, compactionResult };
229829
+ return {
229830
+ wasCompacted: true,
229831
+ compactionResult: attachContextSnapshot(compactionResult)
229832
+ };
229730
229833
  }
229731
229834
  var MAX_OUTPUT_TOKENS_FOR_SUMMARY = 20000, AUTOCOMPACT_BUFFER_TOKENS = 13000, WARNING_THRESHOLD_BUFFER_TOKENS = 20000, ERROR_THRESHOLD_BUFFER_TOKENS = 20000, MANUAL_COMPACT_BUFFER_TOKENS = 3000, MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3;
229732
229835
  var init_autoCompact = __esm(() => {
@@ -238778,12 +238881,15 @@ ${deferredToolList}
238778
238881
  throw new Error("Stream idle timeout - no chunks received");
238779
238882
  }
238780
238883
  if (!partialMessage || newMessages.length === 0 && !stopReason) {
238781
- logForDebugging(!partialMessage ? "Stream completed without receiving message_start event - triggering non-streaming fallback" : "Stream completed with message_start but no content blocks completed - triggering non-streaming fallback", { level: "error" });
238884
+ logForDebugging(!partialMessage ? "Stream completed without receiving message_start event - triggering non-streaming fallback" : "Stream completed with message_start but no content blocks completed - incomplete stream", { level: "error" });
238782
238885
  logEvent("tengu_stream_no_events", {
238783
238886
  model: options.model,
238784
238887
  request_id: streamRequestId ?? "unknown"
238785
238888
  });
238786
- throw new Error("Stream ended without receiving any events");
238889
+ if (!partialMessage) {
238890
+ throw new Error("Stream ended without receiving any events");
238891
+ }
238892
+ throw new EmptyStreamError;
238787
238893
  }
238788
238894
  if (stallCount > 0) {
238789
238895
  logForDebugging(`Streaming completed with ${stallCount} stall(s), total stall time: ${(totalStallTime / 1000).toFixed(1)}s`, { level: "warn" });
@@ -238828,6 +238934,9 @@ ${deferredToolList}
238828
238934
  throw new APIConnectionTimeoutError({ message: "Request timed out" });
238829
238935
  }
238830
238936
  }
238937
+ if (options.streamReplayEscape && newMessages.length === 0 && !signal.aborted && toReplayableStreamError(streamingError)) {
238938
+ throw streamingError;
238939
+ }
238831
238940
  const disableFallback = isEnvTruthy(resolveEnvVar("DISABLE_NONSTREAMING_FALLBACK")) || getFeatureValue_CACHED_MAY_BE_STALE("tengu_disable_streaming_to_non_streaming_fallback", false);
238832
238941
  if (disableFallback) {
238833
238942
  logForDebugging(`Error streaming (non-streaming fallback disabled): ${errorMessage(streamingError)}`, { level: "error" });
@@ -270690,6 +270799,9 @@ function toSDKCompactMetadata(meta) {
270690
270799
  trigger: meta.trigger,
270691
270800
  pre_tokens: meta.preTokens,
270692
270801
  ...meta.postTokens !== undefined && { post_tokens: meta.postTokens },
270802
+ ...meta.contextSnapshot !== undefined && {
270803
+ context_snapshot: meta.contextSnapshot
270804
+ },
270693
270805
  ...seg && {
270694
270806
  preserved_segment: {
270695
270807
  head_uuid: seg.headUuid,
@@ -272942,6 +273054,7 @@ function registerSdkInlineSkillHandler() {
272942
273054
  init_backgroundAbortRegistry();
272943
273055
  init_backgroundTaskScope();
272944
273056
  init_sessionStorage();
273057
+ init_contextSnapshot();
272945
273058
  init_branding();
272946
273059
  init_hooks2();
272947
273060
  init_debug();
@@ -278977,7 +279090,9 @@ function runSdkQueryRuntime(params) {
278977
279090
  const appStateStore = buildAppStateStore(applyFileCheckpointingInitialState(applyDisallowedTools(applyPermissionMode(getDefaultAppState(), options.permissionMode), options.disallowedTools), options.fileCheckpointing?.initialState), fileCheckpointingOnChange);
278978
279091
  const releaseBackgroundTaskScope = bindBackgroundTaskScope(appStateStore.setAppState, ownership.backgroundTaskScope);
278979
279092
  const initialMessagesForReplay = options.resume ? loadAndRestampHistoricalMessages(options.resume, queryContext.identity.sessionId, options.initialMessages) : options.initialMessages;
278980
- let mutableMessages = normalizeInitialMessages(initialMessagesForReplay);
279093
+ const contextSnapshot = options.initialContextSnapshot === undefined ? undefined : normalizeContextSnapshot(options.initialContextSnapshot);
279094
+ const initialMessages = normalizeInitialMessages(initialMessagesForReplay);
279095
+ let mutableMessages = contextSnapshot ? [...contextSnapshot.messages, ...initialMessages] : initialMessages;
278981
279096
  let readFileCache = createFileStateCacheWithSizeLimit(DEFAULT_READ_FILE_CACHE_SIZE);
278982
279097
  const dynamicClients = [];
278983
279098
  const sdkClients = [];
@@ -280500,4 +280615,4 @@ export {
280500
280615
  watchScheduledTasks
280501
280616
  };
280502
280617
 
280503
- //# debugId=170548B7935A529064756E2164756E21
280618
+ //# debugId=FB4FE49FF3A1776C64756E2164756E21
@@ -0,0 +1,14 @@
1
+ import type { Message } from '../types/message.js';
2
+ import type { SDKContextSnapshot } from '../types/contextSnapshot.js';
3
+ export declare function buildContextSnapshot(messages: readonly Message[]): SDKContextSnapshot | undefined;
4
+ /**
5
+ * Validates a persisted snapshot and restores its token baseline marker.
6
+ *
7
+ * Invalid snapshots throw instead of silently falling back to a smaller
8
+ * context: a corrupt persisted snapshot is a host data problem that must be
9
+ * visible, not converted into a different model context.
10
+ */
11
+ export declare function normalizeContextSnapshot(raw: unknown): {
12
+ messages: Message[];
13
+ baselineTokens: number;
14
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Versioned, host-persistable snapshot of the post-compact model context.
3
+ *
4
+ * `messages` intentionally stays opaque at the public boundary: the SDK owns
5
+ * the internal canonical message shapes, while hosts only persist and return
6
+ * the snapshot unchanged.
7
+ */
8
+ export type SDKContextSnapshot = {
9
+ schemaVersion: 1;
10
+ messages: readonly unknown[];
11
+ /**
12
+ * Estimated or API-reported size of `messages` at snapshot creation time.
13
+ * Used as the context baseline until a later assistant response carries real
14
+ * usage.
15
+ */
16
+ baselineTokens: number;
17
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencow-ai/opencow-agent-sdk",
3
- "version": "0.4.27",
3
+ "version": "0.4.28",
4
4
  "description": "Claude Code opened to any LLM — OpenAI, Gemini, DeepSeek, Ollama, and 200+ models",
5
5
  "type": "module",
6
6
  "main": "./dist/sdk.js",