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

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
@@ -20054,10 +20054,20 @@ function isEssentialTrafficOnly() {
20054
20054
  return getPrivacyLevel() === "essential-traffic";
20055
20055
  }
20056
20056
  function getEssentialTrafficOnlyReason() {
20057
- if (resolveEnvVar("DISABLE_NONESSENTIAL_TRAFFIC")) {
20058
- return "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC";
20057
+ if (!resolveEnvVar("DISABLE_NONESSENTIAL_TRAFFIC")) {
20058
+ return null;
20059
20059
  }
20060
- return null;
20060
+ const primaryName = "OPENCOW_DISABLE_NONESSENTIAL_TRAFFIC";
20061
+ const legacyName = "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC";
20062
+ const primaryValue = getQueryEnvVar(primaryName);
20063
+ const legacyValue = getQueryEnvVar(legacyName);
20064
+ if (primaryValue && legacyValue) {
20065
+ return `${primaryName} and ${legacyName}`;
20066
+ }
20067
+ if (legacyValue) {
20068
+ return legacyName;
20069
+ }
20070
+ return primaryName;
20061
20071
  }
20062
20072
  var init_privacyLevel = __esm(() => {
20063
20073
  init_state();
@@ -86008,6 +86018,7 @@ var init_managedEnvConstants = __esm(() => {
86008
86018
  "MAX_THINKING_TOKENS",
86009
86019
  "MCP_TIMEOUT",
86010
86020
  "MCP_TOOL_TIMEOUT",
86021
+ "OPENCOW_DISABLE_NONESSENTIAL_TRAFFIC",
86011
86022
  "OTEL_EXPORTER_OTLP_HEADERS",
86012
86023
  "OTEL_EXPORTER_OTLP_LOGS_HEADERS",
86013
86024
  "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL",
@@ -86726,7 +86737,7 @@ function printStartupScreen() {
86726
86737
  const sLen = ` ● ${sL} Ready — type /help to begin`.length;
86727
86738
  out.push(boxRow(sRow, W, sLen));
86728
86739
  out.push(`${rgb(...BORDER)}╚${"═".repeat(W - 2)}╝${RESET}`);
86729
- out.push(` ${DIM}${rgb(...DIMCOL)}opencow ${RESET}${rgb(...ACCENT)}v${"0.4.27"}${RESET}`);
86740
+ out.push(` ${DIM}${rgb(...DIMCOL)}opencow ${RESET}${rgb(...ACCENT)}v${"0.4.29"}${RESET}`);
86730
86741
  out.push("");
86731
86742
  process.stdout.write(out.join(`
86732
86743
  `) + `
@@ -105105,6 +105116,10 @@ function tokenCountWithEstimation(messages) {
105105
105116
  }
105106
105117
  return getTokenCountFromUsage(usage) + roughTokenCountEstimationForMessages(messages.slice(i + 1));
105107
105118
  }
105119
+ const baselineTokens = message?.contextBaselineTokens;
105120
+ if (typeof baselineTokens === "number" && Number.isFinite(baselineTokens) && baselineTokens >= 0) {
105121
+ return baselineTokens + roughTokenCountEstimationForMessages(messages.slice(i + 1));
105122
+ }
105108
105123
  i--;
105109
105124
  }
105110
105125
  return roughTokenCountEstimationForMessages(messages);
@@ -138830,6 +138845,9 @@ function wrapBodyTransportError(error) {
138830
138845
  return new APIConnectionError({ message: error.message, cause: error });
138831
138846
  }
138832
138847
  function toReplayableStreamError(error) {
138848
+ if (error instanceof EmptyStreamError) {
138849
+ return new APIConnectionError({ message: error.message, cause: error });
138850
+ }
138833
138851
  const wrapped = error instanceof Error && error.message.startsWith(STREAM_WATCHDOG_ABORT_PREFIX) ? new APIConnectionError({ message: error.message, cause: error }) : wrapBodyTransportError(error);
138834
138852
  if (!(wrapped instanceof APIConnectionError) || wrapped instanceof APIConnectionTimeoutError || extractConnectionErrorDetails(wrapped)?.isSSLError) {
138835
138853
  return null;
@@ -139288,7 +139306,7 @@ function getRateLimitResetDelayMs(error) {
139288
139306
  }
139289
139307
  return null;
139290
139308
  }
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;
139309
+ 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
139310
  var init_retry = __esm(() => {
139293
139311
  init_sdk();
139294
139312
  init_aws();
@@ -139325,6 +139343,12 @@ var init_retry = __esm(() => {
139325
139343
  ...[]
139326
139344
  ]);
139327
139345
  BODY_TRANSPORT_FAILURE_RE = /socket connection was closed|^terminated$|other side closed|fetch failed/i;
139346
+ EmptyStreamError = class EmptyStreamError extends Error {
139347
+ constructor(message = "Stream ended before completing the response") {
139348
+ super(message);
139349
+ this.name = "EmptyStreamError";
139350
+ }
139351
+ };
139328
139352
  PERSISTENT_MAX_BACKOFF_MS = 5 * 60 * 1000;
139329
139353
  PERSISTENT_RESET_CAP_MS = 6 * 60 * 60 * 1000;
139330
139354
  DEFAULT_CONNECTION_WAIT_BACKOFF = {
@@ -235159,7 +235183,7 @@ function getAnthropicEnvMetadata() {
235159
235183
  function getBuildAgeMinutes() {
235160
235184
  if (false)
235161
235185
  ;
235162
- const buildTime = new Date("2026-10-20T14:00:47.620Z").getTime();
235186
+ const buildTime = new Date("2026-11-03T08:26:22.842Z").getTime();
235163
235187
  if (isNaN(buildTime))
235164
235188
  return;
235165
235189
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -242982,6 +243006,11 @@ var init_coreSchemas = __esm(() => {
242982
243006
  trigger: _enum(["manual", "auto"]),
242983
243007
  pre_tokens: number2(),
242984
243008
  post_tokens: number2().optional().describe("Estimated resulting context size after compaction."),
243009
+ context_snapshot: object({
243010
+ schemaVersion: literal(1),
243011
+ messages: array(unknown()),
243012
+ baselineTokens: number2()
243013
+ }).optional().describe("Opaque canonical post-compact context. Hosts persist and return it unchanged via Options.initialContextSnapshot."),
242985
243014
  preserved_segment: object({
242986
243015
  head_uuid: UUIDPlaceholder(),
242987
243016
  anchor_uuid: UUIDPlaceholder(),
@@ -291920,6 +291949,41 @@ var init_contextAnalysis = __esm(() => {
291920
291949
  init_slowOperations();
291921
291950
  });
291922
291951
 
291952
+ // src/session/contextSnapshot.ts
291953
+ function cloneSnapshotMessage(message) {
291954
+ if (message.type !== "system" || message.subtype !== "compact_boundary") {
291955
+ return message;
291956
+ }
291957
+ const { contextSnapshot: _ignored, ...compactMetadata } = message.compactMetadata;
291958
+ return {
291959
+ ...message,
291960
+ compactMetadata
291961
+ };
291962
+ }
291963
+ function isSnapshotMessage(message) {
291964
+ return CONTEXT_SNAPSHOT_MESSAGE_TYPES.has(message.type);
291965
+ }
291966
+ function buildContextSnapshot(messages) {
291967
+ const snapshotMessages = messages.filter(isSnapshotMessage).map(cloneSnapshotMessage);
291968
+ if (snapshotMessages.length === 0)
291969
+ return;
291970
+ return {
291971
+ schemaVersion: 1,
291972
+ messages: snapshotMessages,
291973
+ baselineTokens: tokenCountWithEstimation(snapshotMessages)
291974
+ };
291975
+ }
291976
+ var CONTEXT_SNAPSHOT_MESSAGE_TYPES;
291977
+ var init_contextSnapshot = __esm(() => {
291978
+ init_tokens();
291979
+ CONTEXT_SNAPSHOT_MESSAGE_TYPES = new Set([
291980
+ "assistant",
291981
+ "user",
291982
+ "attachment",
291983
+ "system"
291984
+ ]);
291985
+ });
291986
+
291923
291987
  // src/session/memory/memdir/types.ts
291924
291988
  var MEMORY_TYPE_VALUES;
291925
291989
  var init_types10 = __esm(() => {
@@ -292501,6 +292565,15 @@ function buildPostCompactMessages(result) {
292501
292565
  ...result.hookResults
292502
292566
  ];
292503
292567
  }
292568
+ function attachContextSnapshot(result) {
292569
+ if (!result.boundaryMarker?.compactMetadata)
292570
+ return result;
292571
+ const snapshot = buildContextSnapshot(buildPostCompactMessages(result));
292572
+ if (snapshot) {
292573
+ result.boundaryMarker.compactMetadata.contextSnapshot = snapshot;
292574
+ }
292575
+ return result;
292576
+ }
292504
292577
  function annotateBoundaryWithPreservedSegment(boundary, anchorUuid, messagesToKeep) {
292505
292578
  const keep = messagesToKeep ?? [];
292506
292579
  if (keep.length === 0)
@@ -293274,6 +293347,7 @@ var init_compact = __esm(() => {
293274
293347
  init_debug();
293275
293348
  init_errors2();
293276
293349
  init_fileStateCache();
293350
+ init_contextSnapshot();
293277
293351
  init_forkedAgent();
293278
293352
  init_hooks2();
293279
293353
  init_log3();
@@ -294054,7 +294128,7 @@ async function autoCompactIfNeeded(messages, toolUseContext, cacheSafeParams, qu
294054
294128
  markPostCompaction2();
294055
294129
  return {
294056
294130
  wasCompacted: true,
294057
- compactionResult: sessionMemoryResult
294131
+ compactionResult: attachContextSnapshot(sessionMemoryResult)
294058
294132
  };
294059
294133
  }
294060
294134
  try {
@@ -294063,7 +294137,7 @@ async function autoCompactIfNeeded(messages, toolUseContext, cacheSafeParams, qu
294063
294137
  runPostCompactCleanup(querySource);
294064
294138
  return {
294065
294139
  wasCompacted: true,
294066
- compactionResult,
294140
+ compactionResult: attachContextSnapshot(compactionResult),
294067
294141
  consecutiveFailures: 0
294068
294142
  };
294069
294143
  } catch (error) {
@@ -294088,13 +294162,19 @@ async function manualCompact(messages, toolUseContext, cacheSafeParams, instruct
294088
294162
  setLastSummarizedMessageId(undefined);
294089
294163
  runPostCompactCleanup();
294090
294164
  markPostCompaction2();
294091
- return { wasCompacted: true, compactionResult: sessionMemoryResult };
294165
+ return {
294166
+ wasCompacted: true,
294167
+ compactionResult: attachContextSnapshot(sessionMemoryResult)
294168
+ };
294092
294169
  }
294093
294170
  }
294094
294171
  const compactionResult = await deps.compactConversation(messages, toolUseContext, cacheSafeParams, false, instructions, false);
294095
294172
  setLastSummarizedMessageId(undefined);
294096
294173
  runPostCompactCleanup();
294097
- return { wasCompacted: true, compactionResult };
294174
+ return {
294175
+ wasCompacted: true,
294176
+ compactionResult: attachContextSnapshot(compactionResult)
294177
+ };
294098
294178
  }
294099
294179
  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
294180
  var init_autoCompact = __esm(() => {
@@ -297985,12 +298065,15 @@ ${deferredToolList}
297985
298065
  throw new Error("Stream idle timeout - no chunks received");
297986
298066
  }
297987
298067
  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" });
298068
+ 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
298069
  logEvent("tengu_stream_no_events", {
297990
298070
  model: options.model,
297991
298071
  request_id: streamRequestId ?? "unknown"
297992
298072
  });
297993
- throw new Error("Stream ended without receiving any events");
298073
+ if (!partialMessage) {
298074
+ throw new Error("Stream ended without receiving any events");
298075
+ }
298076
+ throw new EmptyStreamError;
297994
298077
  }
297995
298078
  if (stallCount > 0) {
297996
298079
  logForDebugging2(`Streaming completed with ${stallCount} stall(s), total stall time: ${(totalStallTime / 1000).toFixed(1)}s`, { level: "warn" });
@@ -298035,6 +298118,9 @@ ${deferredToolList}
298035
298118
  throw new APIConnectionTimeoutError({ message: "Request timed out" });
298036
298119
  }
298037
298120
  }
298121
+ if (options.streamReplayEscape && newMessages.length === 0 && !signal.aborted && toReplayableStreamError(streamingError)) {
298122
+ throw streamingError;
298123
+ }
298038
298124
  const disableFallback = isEnvTruthy(resolveEnvVar("DISABLE_NONSTREAMING_FALLBACK")) || getFeatureValue_CACHED_MAY_BE_STALE("tengu_disable_streaming_to_non_streaming_fallback", false);
298039
298125
  if (disableFallback) {
298040
298126
  logForDebugging2(`Error streaming (non-streaming fallback disabled): ${errorMessage(streamingError)}`, { level: "error" });
@@ -435130,10 +435216,10 @@ var init_fuse = __esm(() => {
435130
435216
  });
435131
435217
 
435132
435218
  // missing-module-stub:/home/runner/work/opencow-agent-sdk/opencow-agent-sdk/src/cli/suggestions/skillUsageTracking.js
435133
- var noop17 = () => null, recordSkillUsage2, getSkillUsageScore;
435219
+ var noop17 = () => null, getSkillUsageScore, recordSkillUsage2;
435134
435220
  var init_skillUsageTracking2 = __esm(() => {
435135
- recordSkillUsage2 = noop17;
435136
435221
  getSkillUsageScore = noop17;
435222
+ recordSkillUsage2 = noop17;
435137
435223
  });
435138
435224
 
435139
435225
  // src/cli/suggestions/commandSuggestions.ts
@@ -445826,6 +445912,9 @@ function toSDKCompactMetadata(meta) {
445826
445912
  trigger: meta.trigger,
445827
445913
  pre_tokens: meta.preTokens,
445828
445914
  ...meta.postTokens !== undefined && { post_tokens: meta.postTokens },
445915
+ ...meta.contextSnapshot !== undefined && {
445916
+ context_snapshot: meta.contextSnapshot
445917
+ },
445829
445918
  ...seg && {
445830
445919
  preserved_segment: {
445831
445920
  head_uuid: seg.headUuid,
@@ -445841,6 +445930,9 @@ function fromSDKCompactMetadata(meta) {
445841
445930
  trigger: meta.trigger,
445842
445931
  preTokens: meta.pre_tokens,
445843
445932
  ...meta.post_tokens !== undefined && { postTokens: meta.post_tokens },
445933
+ ...meta.context_snapshot !== undefined && {
445934
+ contextSnapshot: meta.context_snapshot
445935
+ },
445844
445936
  ...seg && {
445845
445937
  preservedSegment: {
445846
445938
  headUuid: seg.head_uuid,
@@ -480124,7 +480216,7 @@ function buildPrimarySection() {
480124
480216
  }, undefined, false, undefined, this);
480125
480217
  return [{
480126
480218
  label: "Version",
480127
- value: "0.4.27"
480219
+ value: "0.4.29"
480128
480220
  }, {
480129
480221
  label: "Session name",
480130
480222
  value: nameValue
@@ -528204,7 +528296,7 @@ var init_bridge_kick = __esm(() => {
528204
528296
  var call58 = async () => {
528205
528297
  return {
528206
528298
  type: "text",
528207
- value: `${"99.0.0"} (built ${"2026-10-20T14:00:47.620Z"})`
528299
+ value: `${"99.0.0"} (built ${"2026-11-03T08:26:22.842Z"})`
528208
528300
  };
528209
528301
  }, version2, version_default;
528210
528302
  var init_version = __esm(() => {
@@ -545883,7 +545975,7 @@ function WelcomeV2() {
545883
545975
  dimColor: true,
545884
545976
  children: [
545885
545977
  "v",
545886
- "0.4.27",
545978
+ "0.4.29",
545887
545979
  " "
545888
545980
  ]
545889
545981
  }, undefined, true, undefined, this)
@@ -546083,7 +546175,7 @@ function WelcomeV2() {
546083
546175
  dimColor: true,
546084
546176
  children: [
546085
546177
  "v",
546086
- "0.4.27",
546178
+ "0.4.29",
546087
546179
  " "
546088
546180
  ]
546089
546181
  }, undefined, true, undefined, this)
@@ -546309,7 +546401,7 @@ function AppleTerminalWelcomeV2(t0) {
546309
546401
  dimColor: true,
546310
546402
  children: [
546311
546403
  "v",
546312
- "0.4.27",
546404
+ "0.4.29",
546313
546405
  " "
546314
546406
  ]
546315
546407
  }, undefined, true, undefined, this);
@@ -546563,7 +546655,7 @@ function AppleTerminalWelcomeV2(t0) {
546563
546655
  dimColor: true,
546564
546656
  children: [
546565
546657
  "v",
546566
- "0.4.27",
546658
+ "0.4.29",
546567
546659
  " "
546568
546660
  ]
546569
546661
  }, undefined, true, undefined, this);
@@ -567320,7 +567412,7 @@ Usage: claude --remote "your task description"`, () => gracefulShutdown(1));
567320
567412
  pendingHookMessages
567321
567413
  }, renderAndRun);
567322
567414
  }
567323
- }).version("0.4.27 (OpenCow)", "-v, --version", "Output the version number");
567415
+ }).version("0.4.29 (OpenCow)", "-v, --version", "Output the version number");
567324
567416
  program.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)");
567325
567417
  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
567418
  if (canUserConfigureAdvisor()) {
@@ -567915,7 +568007,7 @@ if (false) {}
567915
568007
  async function main2() {
567916
568008
  const args = process.argv.slice(2);
567917
568009
  if (args.length === 1 && (args[0] === "--version" || args[0] === "-v" || args[0] === "-V")) {
567918
- console.log(`${"0.4.27"} (OpenCow)`);
568010
+ console.log(`${"0.4.29"} (OpenCow)`);
567919
568011
  return;
567920
568012
  }
567921
568013
  if (args.includes("--provider")) {
@@ -568015,4 +568107,4 @@ async function main2() {
568015
568107
  }
568016
568108
  main2();
568017
568109
 
568018
- //# debugId=25745C572ABF064464756E2164756E21
568110
+ //# debugId=3D4AFE0926C15EE564756E2164756E21
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-11-03T08:26:22.842Z").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=0DC7328726EDA85F64756E2164756E21
@@ -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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Privacy level controls how much nonessential network traffic and telemetry
3
- * Claude Code generates.
3
+ * OpenCow generates.
4
4
  *
5
5
  * Levels are ordered by restrictiveness:
6
6
  * default < no-telemetry < essential-traffic
@@ -11,8 +11,8 @@
11
11
  * (telemetry + auto-updates, grove, release notes, model capabilities, etc.).
12
12
  *
13
13
  * The resolved level is the most restrictive signal from:
14
- * CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC → essential-traffic
15
- * DISABLE_TELEMETRY → no-telemetry
14
+ * OPENCOW_DISABLE_NONESSENTIAL_TRAFFIC → essential-traffic
15
+ * DISABLE_TELEMETRY → no-telemetry
16
16
  */
17
17
  type PrivacyLevel = 'default' | 'no-telemetry' | 'essential-traffic';
18
18
  export declare function getPrivacyLevel(): PrivacyLevel;
@@ -27,7 +27,7 @@ export declare function isEssentialTrafficOnly(): boolean;
27
27
  */
28
28
  export declare function isTelemetryDisabled(): boolean;
29
29
  /**
30
- * Returns the env var name responsible for the current essential-traffic restriction,
30
+ * Returns the env var name(s) responsible for the current essential-traffic restriction,
31
31
  * or null if unrestricted. Used for user-facing "unset X to re-enable" messages.
32
32
  */
33
33
  export declare function getEssentialTrafficOnlyReason(): string | null;
@@ -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-11-03T08:26:22.842Z").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=461FB770EF3F268264756E2164756E21
@@ -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.29",
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",