scream-code 0.19.6 → 0.19.7

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.
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
6
6
  import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } from "./chunk-D90kvbyJ.mjs";
7
7
  import { A as sharedMemoStore, B as buildProjectTagCloud, C as applyConsolidation, D as parseMemoryMemos, E as buildExitExtractionPrompt, F as probeLocalEmbeddingSupport, G as join$1, H as basename$1, I as createMemoryMemo, J as relative$1, K as normalize, L as toSummary, M as clearEmbeddingModelCache, N as createEmbeddingEngine, O as stripMemoryTags, P as hasEmbeddingModelCache$1, R as generateTags, S as DreamTracker, T as EXIT_EXTRACTION_SYSTEM_PROMPT, U as dirname$2, V as rankMemos, W as isAbsolute$1, Y as resolve$2, a as isSupportedFile, b as sharedKnowledgeStoreReady, i as ingestFile, j as classifyEmbeddingFailure, k as MemoryMemoStore, q as parse$7, r as ingestDirectory, t as multiSearch, w as buildConsolidationPlan, y as sharedKnowledgeStore, z as normalizeTags } from "./src-Cu3sUIJK.mjs";
8
8
  import { n as require_base64_js, t as XMLParser } from "./XMLParser-DFAnGqkc.mjs";
9
- import { a as assertScreamHostIdentity, i as t, n as getLocale, o as createScreamDefaultHeaders, r as setLocale, t as TextInputDialogComponent } from "./text-input-dialog-BqMy3ifH.mjs";
9
+ import { a as assertScreamHostIdentity, i as t, n as getLocale, o as createScreamDefaultHeaders, r as setLocale, t as TextInputDialogComponent } from "./text-input-dialog-Ce2AEFU9.mjs";
10
10
  import { createRequire } from "node:module";
11
11
  import { createCipheriv, createDecipheriv, createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
12
12
  import * as fs$1 from "node:fs/promises";
@@ -8711,6 +8711,8 @@ function normalizeAnthropicStopReason(raw) {
8711
8711
  }
8712
8712
  }
8713
8713
  const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14";
8714
+ /** Required by Anthropic for `cache_control.ttl: '1h'` breakpoints. */
8715
+ const EXTENDED_CACHE_TTL_BETA = "extended-cache-ttl-2025-04-11";
8714
8716
  const FAMILY_VERSION_RE = /(?:opus|sonnet|haiku)[.-](\d+)[.-](\d{1,2})(?!\d)/;
8715
8717
  const OPUS_VERSION_RE = /opus[.-](\d+)[.-](\d{1,2})(?!\d)/;
8716
8718
  const ADAPTIVE_MIN_VERSION = {
@@ -8869,7 +8871,11 @@ const ANTHROPIC_TOOL_CALL_ID_POLICY = {
8869
8871
  normalize: (id) => sanitizeToolCallId(id, 64),
8870
8872
  maxLength: 64
8871
8873
  };
8872
- const CACHE_CONTROL = { type: "ephemeral" };
8874
+ const CACHE_CONTROL_SHORT = { type: "ephemeral" };
8875
+ const CACHE_CONTROL_LONG = {
8876
+ type: "ephemeral",
8877
+ ttl: "1h"
8878
+ };
8873
8879
  /**
8874
8880
  * Content block types that support cache_control injection.
8875
8881
  */
@@ -8883,22 +8889,22 @@ const CACHEABLE_TYPES = new Set([
8883
8889
  "server_tool_use",
8884
8890
  "web_search_tool_result"
8885
8891
  ]);
8886
- function injectCacheControlOnLastBlock(messages, messageIndex) {
8892
+ function injectCacheControlOnLastBlock(messages, cacheControl, messageIndex) {
8887
8893
  const message = messages[messageIndex ?? messages.length - 1];
8888
8894
  if (message === void 0) return;
8889
8895
  const content = message.content;
8890
8896
  if (!Array.isArray(content) || content.length === 0) return;
8891
8897
  const lastBlock = content.at(-1);
8892
8898
  if (lastBlock === void 0) return;
8893
- if (CACHEABLE_TYPES.has(lastBlock.type)) lastBlock.cache_control = CACHE_CONTROL;
8899
+ if (CACHEABLE_TYPES.has(lastBlock.type)) lastBlock.cache_control = cacheControl;
8894
8900
  }
8895
8901
  /**
8896
8902
  * Inject cache_control on the last block of the second-to-last message,
8897
8903
  * when there is enough budget for an extra breakpoint.
8898
8904
  */
8899
- function injectCacheControlOnPenultimateMessage(messages) {
8905
+ function injectCacheControlOnPenultimateMessage(messages, cacheControl) {
8900
8906
  if (messages.length < 2) return;
8901
- injectCacheControlOnLastBlock(messages, messages.length - 2);
8907
+ injectCacheControlOnLastBlock(messages, cacheControl, messages.length - 2);
8902
8908
  }
8903
8909
  /**
8904
8910
  * Check whether a MessageParam is a user message whose content consists
@@ -9221,6 +9227,7 @@ var AnthropicStreamedMessage = class {
9221
9227
  }
9222
9228
  };
9223
9229
  var AnthropicChatProvider = class {
9230
+ _cacheControl;
9224
9231
  name = "anthropic";
9225
9232
  _model;
9226
9233
  _stream;
@@ -9247,6 +9254,8 @@ var AnthropicChatProvider = class {
9247
9254
  max_tokens: resolveDefaultMaxTokens(options.model, options.defaultMaxTokens),
9248
9255
  betaFeatures: options.betaFeatures ?? [INTERLEAVED_THINKING_BETA]
9249
9256
  };
9257
+ this._cacheControl = options.cacheRetention === "long" ? CACHE_CONTROL_LONG : CACHE_CONTROL_SHORT;
9258
+ if (options.cacheRetention === "long" && !this._generationKwargs.betaFeatures.includes(EXTENDED_CACHE_TTL_BETA)) this._generationKwargs.betaFeatures = [...this._generationKwargs.betaFeatures, EXTENDED_CACHE_TTL_BETA];
9250
9259
  }
9251
9260
  get modelName() {
9252
9261
  return this._model;
@@ -9285,7 +9294,7 @@ var AnthropicChatProvider = class {
9285
9294
  const system = systemBlocks.length === 0 ? void 0 : systemBlocks.map((text, index) => ({
9286
9295
  type: "text",
9287
9296
  text,
9288
- ...index === 0 ? { cache_control: CACHE_CONTROL } : {}
9297
+ ...index === 0 ? { cache_control: this._cacheControl } : {}
9289
9298
  }));
9290
9299
  const normalizedHistory = normalizeToolCallIdsForProvider(history, ANTHROPIC_TOOL_CALL_ID_POLICY);
9291
9300
  const messages = [];
@@ -9295,8 +9304,8 @@ var AnthropicChatProvider = class {
9295
9304
  if (last !== void 0 && isToolResultOnly(last) && isToolResultOnly(converted)) last.content = [...last.content, ...converted.content];
9296
9305
  else messages.push(converted);
9297
9306
  }
9298
- injectCacheControlOnPenultimateMessage(messages);
9299
- injectCacheControlOnLastBlock(messages);
9307
+ injectCacheControlOnPenultimateMessage(messages, this._cacheControl);
9308
+ injectCacheControlOnLastBlock(messages, this._cacheControl);
9300
9309
  const kwargs = {};
9301
9310
  if (this._generationKwargs.max_tokens !== void 0) kwargs["max_tokens"] = this._generationKwargs.max_tokens;
9302
9311
  if (this._generationKwargs.temperature !== void 0) kwargs["temperature"] = this._generationKwargs.temperature;
@@ -9310,7 +9319,7 @@ var AnthropicChatProvider = class {
9310
9319
  const anthropicTools = tools.map((t) => convertTool$2(t));
9311
9320
  if (anthropicTools.length > 0) {
9312
9321
  const lastTool = anthropicTools.at(-1);
9313
- if (lastTool !== void 0) lastTool.cache_control = CACHE_CONTROL;
9322
+ if (lastTool !== void 0) lastTool.cache_control = this._cacheControl;
9314
9323
  }
9315
9324
  const createParams = {
9316
9325
  model: this._model,
@@ -49805,6 +49814,7 @@ function catalogModelToCapability(model) {
49805
49814
  name: typeof model.name === "string" && model.name.length > 0 ? model.name : void 0,
49806
49815
  maxOutputSize: typeof output === "number" && output > 0 ? output : void 0,
49807
49816
  reasoningKey: catalogReasoningKey(model.interleaved),
49817
+ ...priceFromEntry(model) === void 0 ? {} : { price: priceFromEntry(model) },
49808
49818
  capability: {
49809
49819
  image_in: inputs.includes("image"),
49810
49820
  video_in: inputs.includes("video"),
@@ -49815,6 +49825,17 @@ function catalogModelToCapability(model) {
49815
49825
  }
49816
49826
  };
49817
49827
  }
49828
+ /** USD per 1M tokens when the entry carries a usable price. */
49829
+ function priceFromEntry(model) {
49830
+ const cost = model.cost;
49831
+ if (typeof cost?.input !== "number" || typeof cost?.output !== "number") return void 0;
49832
+ return {
49833
+ input: cost.input,
49834
+ output: cost.output,
49835
+ ...typeof cost.cache_read === "number" ? { cacheRead: cost.cache_read } : {},
49836
+ ...typeof cost.cache_write === "number" ? { cacheWrite: cost.cache_write } : {}
49837
+ };
49838
+ }
49818
49839
  function catalogReasoningKey(interleaved) {
49819
49840
  if (interleaved === true) return "reasoning_content";
49820
49841
  if (typeof interleaved !== "object" || interleaved === null) return void 0;
@@ -49827,6 +49848,25 @@ function catalogProviderModels(entry) {
49827
49848
  return Object.values(models).map((model) => catalogModelToCapability(model)).filter((model) => model !== void 0);
49828
49849
  }
49829
49850
  //#endregion
49851
+ //#region ../../packages/ltod/src/cost.ts
49852
+ /**
49853
+ * Cost of one usage record in USD, or `undefined` when the model has no price.
49854
+ *
49855
+ * Absence is meaningful: showing `$0.00` for an unpriced model would read as
49856
+ * "free", so callers must render a placeholder instead of a number.
49857
+ */
49858
+ function estimateCost(usage, price) {
49859
+ if (price === void 0) return void 0;
49860
+ const cacheRead = price.cacheRead ?? price.input;
49861
+ const cacheWrite = price.cacheWrite ?? price.input;
49862
+ return (usage.inputOther * price.input + usage.inputCacheRead * cacheRead + usage.inputCacheCreation * cacheWrite + usage.output * price.output) / 1e6;
49863
+ }
49864
+ /** `$1.2345` with four decimals — sub-cent precision matters for single calls. */
49865
+ function formatUsd(cost) {
49866
+ if (cost === void 0) return "—";
49867
+ return `$${cost.toFixed(4)}`;
49868
+ }
49869
+ //#endregion
49830
49870
  //#region ../../packages/ltod/src/idle-iterator.ts
49831
49871
  /**
49832
49872
  * Idle-stream watchdog.
@@ -51574,7 +51614,7 @@ function renderAgentFiles(files) {
51574
51614
  const frames = files.map((file, index) => ({
51575
51615
  annotation: annotationFor(file.path),
51576
51616
  separator: index < files.length - 1 ? "\n\n" : "",
51577
- size: byteLength$1(file.content)
51617
+ size: byteLength$2(file.content)
51578
51618
  }));
51579
51619
  const framed = files.map(() => false);
51580
51620
  const allocation = files.map(() => 0);
@@ -51583,7 +51623,7 @@ function renderAgentFiles(files) {
51583
51623
  const take = (index, want) => {
51584
51624
  if (want <= 0) return 0;
51585
51625
  const frame = frames[index];
51586
- const frameCost = framed[index] === true ? 0 : byteLength$1(frame.annotation) + byteLength$1(frame.separator);
51626
+ const frameCost = framed[index] === true ? 0 : byteLength$2(frame.annotation) + byteLength$2(frame.separator);
51587
51627
  const available = remaining - frameCost;
51588
51628
  if (available <= 0) return 0;
51589
51629
  const give = Math.min(want, available);
@@ -51597,15 +51637,15 @@ function renderAgentFiles(files) {
51597
51637
  const given = allocation[index];
51598
51638
  if (given === 0) {
51599
51639
  const marker = omissionMarker(file.path);
51600
- const cost = byteLength$1(frames[index].annotation) + byteLength$1(marker) + byteLength$1(frames[index].separator);
51640
+ const cost = byteLength$2(frames[index].annotation) + byteLength$2(marker) + byteLength$2(frames[index].separator);
51601
51641
  if (remaining < cost) return "";
51602
- take(index, byteLength$1(marker));
51642
+ take(index, byteLength$2(marker));
51603
51643
  return `${frames[index].annotation}${marker.slice(2)}`;
51604
51644
  }
51605
51645
  if (given >= frames[index].size) return `${frames[index].annotation}${file.content}`;
51606
51646
  const estimated = truncationMarker(frames[index].size - given, file.path);
51607
- const kept = truncateUtf8$1(file.content, Math.max(0, given - byteLength$1(estimated))).trim();
51608
- const marker = truncationMarker(frames[index].size - byteLength$1(kept), file.path);
51647
+ const kept = truncateUtf8$1(file.content, Math.max(0, given - byteLength$2(estimated))).trim();
51648
+ const marker = truncationMarker(frames[index].size - byteLength$2(kept), file.path);
51609
51649
  return `${frames[index].annotation}${kept}${marker}`;
51610
51650
  }).filter((block) => block.length > 0).join("\n\n");
51611
51651
  }
@@ -51619,13 +51659,13 @@ function renderAgentFiles(files) {
51619
51659
  * loop for ~5s before this.
51620
51660
  */
51621
51661
  function truncateUtf8$1(text, maxBytes) {
51622
- if (byteLength$1(text) <= maxBytes) return text;
51662
+ if (byteLength$2(text) <= maxBytes) return text;
51623
51663
  const bytes = Buffer.from(text, "utf8");
51624
51664
  let end = Math.min(maxBytes, bytes.length);
51625
51665
  while (end > 0 && (bytes[end] & 192) === 128) end -= 1;
51626
51666
  return bytes.toString("utf8", 0, end);
51627
51667
  }
51628
- function byteLength$1(text) {
51668
+ function byteLength$2(text) {
51629
51669
  return Buffer.byteLength(text, "utf8");
51630
51670
  }
51631
51671
  function annotationFor(path) {
@@ -59281,7 +59321,7 @@ function isBackgroundTaskTerminal(status) {
59281
59321
  * Callers that need the full output (e.g. `TaskOutput`) must read the
59282
59322
  * disk log via `getOutputSizeBytes` / `readOutputBytesFromDisk`.
59283
59323
  */
59284
- const MAX_OUTPUT_BYTES$1 = 1024 * 1024;
59324
+ const MAX_OUTPUT_BYTES$2 = 1024 * 1024;
59285
59325
  /**
59286
59326
  * How many terminal tasks stay addressable after eviction from `processes`.
59287
59327
  * The full output is on disk (`output.log`); the ring only keeps the metadata
@@ -60091,15 +60131,15 @@ var BackgroundProcessManager = class {
60091
60131
  * multi-byte output cannot exceed the cap the way a code-unit count would.
60092
60132
  */
60093
60133
  trimLiveOutput(entry) {
60094
- while (entry.outputRingBytes > MAX_OUTPUT_BYTES$1 && entry.outputChunks.length > 1) {
60134
+ while (entry.outputRingBytes > MAX_OUTPUT_BYTES$2 && entry.outputChunks.length > 1) {
60095
60135
  const removed = entry.outputChunks.shift();
60096
60136
  if (removed === void 0) break;
60097
60137
  entry.outputRingBytes -= Buffer.byteLength(removed, "utf-8");
60098
60138
  }
60099
- if (entry.outputRingBytes > MAX_OUTPUT_BYTES$1) {
60139
+ if (entry.outputRingBytes > MAX_OUTPUT_BYTES$2) {
60100
60140
  const only = entry.outputChunks[0];
60101
60141
  if (only === void 0) return;
60102
- const tail = utf8ByteTail(only, MAX_OUTPUT_BYTES$1);
60142
+ const tail = utf8ByteTail(only, MAX_OUTPUT_BYTES$2);
60103
60143
  entry.outputChunks[0] = tail;
60104
60144
  entry.outputRingBytes = Buffer.byteLength(tail, "utf-8");
60105
60145
  }
@@ -60124,15 +60164,15 @@ var BackgroundProcessManager = class {
60124
60164
  * budget either.
60125
60165
  */
60126
60166
  trimRetiredOutput(retired) {
60127
- while (retired.outputTextBytes > MAX_OUTPUT_BYTES$1 && retired.outputChunks.length > 1) {
60167
+ while (retired.outputTextBytes > MAX_OUTPUT_BYTES$2 && retired.outputChunks.length > 1) {
60128
60168
  const removed = retired.outputChunks.shift();
60129
60169
  if (removed === void 0) break;
60130
60170
  retired.outputTextBytes -= Buffer.byteLength(removed, "utf-8");
60131
60171
  }
60132
- if (retired.outputTextBytes > MAX_OUTPUT_BYTES$1) {
60172
+ if (retired.outputTextBytes > MAX_OUTPUT_BYTES$2) {
60133
60173
  const only = retired.outputChunks[0];
60134
60174
  if (only === void 0) return;
60135
- const tail = utf8ByteTail(only, MAX_OUTPUT_BYTES$1);
60175
+ const tail = utf8ByteTail(only, MAX_OUTPUT_BYTES$2);
60136
60176
  retired.outputChunks[0] = tail;
60137
60177
  retired.outputTextBytes = Buffer.byteLength(tail, "utf-8");
60138
60178
  }
@@ -63798,6 +63838,233 @@ function normalizePath$1(path) {
63798
63838
  return folded;
63799
63839
  }
63800
63840
  //#endregion
63841
+ //#region ../../packages/agent-core/src/tools/support/result-builder.ts
63842
+ const DEFAULT_MAX_BYTES$1 = 50 * 1024;
63843
+ const DEFAULT_TAIL_BYTES = 20 * 1024;
63844
+ const DEFAULT_MAX_LINE_BYTES = 2048;
63845
+ const DEFAULT_MAX_LINES = 2e3;
63846
+ const DEFAULT_MAX_FULL_OUTPUT_BYTES = 1e6;
63847
+ const TRUNCATION_MARKER$1 = "[...truncated]";
63848
+ const TRUNCATION_MESSAGE = "Output is truncated to fit in the message.";
63849
+ var ToolResultBuilder = class {
63850
+ maxBytes;
63851
+ maxTailBytes;
63852
+ maxLineBytes;
63853
+ maxLines;
63854
+ artifactSink;
63855
+ maxFullOutputBytes;
63856
+ buffer = [];
63857
+ nBytesValue = 0;
63858
+ nCharsValue = 0;
63859
+ linesInHead = 0;
63860
+ truncationHappened = false;
63861
+ headTruncated = false;
63862
+ tailBuf = [];
63863
+ tailBytesValue = 0;
63864
+ tailCharsValue = 0;
63865
+ fullOutput;
63866
+ fullOutputBytes = 0;
63867
+ totalLinesWritten = 0;
63868
+ constructor(options = {}) {
63869
+ this.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES$1;
63870
+ this.maxTailBytes = options.maxTailBytes ?? DEFAULT_TAIL_BYTES;
63871
+ this.maxLineBytes = options.maxLineBytes === void 0 ? DEFAULT_MAX_LINE_BYTES : options.maxLineBytes;
63872
+ this.maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
63873
+ if (this.maxLineBytes !== null && this.maxLineBytes <= byteLength$1(TRUNCATION_MARKER$1)) throw new Error("maxLineBytes must be greater than the truncation marker length.");
63874
+ this.artifactSink = options.artifactSink;
63875
+ this.maxFullOutputBytes = options.maxFullOutputBytes ?? DEFAULT_MAX_FULL_OUTPUT_BYTES;
63876
+ if (this.artifactSink !== void 0) this.fullOutput = [];
63877
+ }
63878
+ /** Characters retained so far (UI/logging); the budget itself is bytes. */
63879
+ get nChars() {
63880
+ return this.nCharsValue + this.tailCharsValue;
63881
+ }
63882
+ /** Bytes retained so far. */
63883
+ get nBytes() {
63884
+ return this.nBytesValue + this.tailBytesValue;
63885
+ }
63886
+ toString() {
63887
+ const buffered = this.buffer.join("");
63888
+ if (!this.headTruncated || this.tailBytesValue === 0) return buffered;
63889
+ this.trimTail();
63890
+ const tail = this.tailBuf.join("");
63891
+ const headBudget = this.maxBytes - 128 - this.tailBytesValue;
63892
+ const head = headBudget >= 0 && byteLength$1(buffered) > headBudget ? truncateToBytes(buffered, headBudget) : buffered;
63893
+ const elided = this.computeElidedLines(head, tail);
63894
+ const marker = elided > 0 ? `[…${String(elided)} lines elided…]\n${TRUNCATION_MARKER$1}` : TRUNCATION_MARKER$1;
63895
+ return `${head}${head.endsWith("\n") ? `${marker}\n` : marker}${tail}`;
63896
+ }
63897
+ write(text) {
63898
+ if (text.length === 0) return 0;
63899
+ const lines = text.match(/[^\r\n]*(?:\r\n|[\n\r])|[^\r\n]+/g) ?? [];
63900
+ if (lines.length === 0) return 0;
63901
+ this.totalLinesWritten += lines.length;
63902
+ if (this.fullOutput !== void 0 && this.fullOutputBytes < this.maxFullOutputBytes) {
63903
+ this.fullOutput.push(text);
63904
+ this.fullOutputBytes += byteLength$1(text);
63905
+ }
63906
+ let charsWritten = 0;
63907
+ for (const originalLine of lines) {
63908
+ let line = originalLine;
63909
+ if (this.maxLineBytes !== null && byteLength$1(line) > this.maxLineBytes) {
63910
+ line = truncateLineToBytes(line, this.maxLineBytes);
63911
+ this.truncationHappened = true;
63912
+ }
63913
+ const lineBytes = byteLength$1(line);
63914
+ const remainingHeadBytes = this.maxBytes - this.nBytesValue;
63915
+ if (!this.headTruncated && this.linesInHead < this.maxLines && lineBytes <= remainingHeadBytes) {
63916
+ this.pushHead(line);
63917
+ charsWritten += line.length;
63918
+ continue;
63919
+ }
63920
+ if (!this.headTruncated && this.linesInHead === 0 && lineBytes > remainingHeadBytes && remainingHeadBytes > 0) {
63921
+ const prefix = truncateToBytes(line, remainingHeadBytes);
63922
+ this.pushHead(prefix);
63923
+ this.headTruncated = true;
63924
+ this.truncationHappened = true;
63925
+ const rest = line.slice(prefix.length);
63926
+ if (rest.length > 0) this.appendTail(rest);
63927
+ charsWritten += line.length;
63928
+ continue;
63929
+ }
63930
+ this.headTruncated = true;
63931
+ this.truncationHappened = true;
63932
+ this.appendTail(line);
63933
+ charsWritten += line.length;
63934
+ }
63935
+ return charsWritten;
63936
+ }
63937
+ pushHead(line) {
63938
+ this.buffer.push(line);
63939
+ this.nBytesValue += byteLength$1(line);
63940
+ this.nCharsValue += line.length;
63941
+ this.linesInHead += 1;
63942
+ }
63943
+ appendTail(text) {
63944
+ if (text.length === 0) return;
63945
+ if (this.maxTailBytes === 0) return;
63946
+ const bytes = byteLength$1(text);
63947
+ if (bytes >= this.maxTailBytes) {
63948
+ const trimmed = tailAtCodePoint(text, this.maxTailBytes);
63949
+ this.tailBuf.length = 0;
63950
+ this.tailBuf.push(trimmed);
63951
+ this.tailBytesValue = byteLength$1(trimmed);
63952
+ this.tailCharsValue = trimmed.length;
63953
+ return;
63954
+ }
63955
+ this.tailBuf.push(text);
63956
+ this.tailBytesValue += bytes;
63957
+ this.tailCharsValue += text.length;
63958
+ if (this.tailBytesValue > this.maxTailBytes * 2) this.trimTail();
63959
+ }
63960
+ trimTail() {
63961
+ if (this.tailBytesValue <= this.maxTailBytes) return;
63962
+ const trimmed = tailAtCodePoint(this.tailBuf.join(""), this.maxTailBytes);
63963
+ this.tailBuf.length = 0;
63964
+ this.tailBuf.push(trimmed);
63965
+ this.tailBytesValue = byteLength$1(trimmed);
63966
+ this.tailCharsValue = trimmed.length;
63967
+ }
63968
+ computeElidedLines(head, tail) {
63969
+ if (this.totalLinesWritten === 0) return 0;
63970
+ if (this.fullOutput !== void 0) {
63971
+ const total = countLines$1(this.fullOutput.join(""));
63972
+ return Math.max(0, total - countLines$1(head) - countLines$1(tail));
63973
+ }
63974
+ return Math.max(0, this.totalLinesWritten - countLines$1(head) - countLines$1(tail));
63975
+ }
63976
+ async maybeWriteArtifact() {
63977
+ if (this.artifactSink === void 0 || this.fullOutput === void 0) return void 0;
63978
+ if (!this.truncationHappened) return void 0;
63979
+ const full = this.fullOutput.join("");
63980
+ if (full.length === 0) return void 0;
63981
+ try {
63982
+ return await this.artifactSink(full);
63983
+ } catch {
63984
+ return;
63985
+ }
63986
+ }
63987
+ appendArtifactRef(output, ref) {
63988
+ const line = `[full output saved: ${ref}]`;
63989
+ return output.length === 0 ? line : output.endsWith("\n") ? `${output}${line}` : `${output}\n${line}`;
63990
+ }
63991
+ async ok(message = "", options = {}) {
63992
+ let finalMessage = message;
63993
+ if (finalMessage.length > 0 && !finalMessage.endsWith(".")) finalMessage += ".";
63994
+ if (this.truncationHappened) finalMessage = finalMessage.length === 0 ? TRUNCATION_MESSAGE : `${finalMessage} ${TRUNCATION_MESSAGE}`;
63995
+ const baseOutput = this.toString();
63996
+ const artifactRef = await this.maybeWriteArtifact();
63997
+ const output = artifactRef === void 0 ? baseOutput : this.appendArtifactRef(baseOutput, artifactRef);
63998
+ const shouldAppendMessage = finalMessage.length > 0 && (this.truncationHappened || output.length === 0);
63999
+ return {
64000
+ isError: false,
64001
+ output: shouldAppendMessage ? output.length === 0 ? finalMessage : output.endsWith("\n") ? `${output}${finalMessage}` : `${output}\n${finalMessage}` : output,
64002
+ message: shouldAppendMessage ? void 0 : finalMessage,
64003
+ truncated: this.truncationHappened,
64004
+ brief: options.brief
64005
+ };
64006
+ }
64007
+ async error(message, options = {}) {
64008
+ const finalMessage = this.truncationHappened ? message.length === 0 ? TRUNCATION_MESSAGE : `${message} ${TRUNCATION_MESSAGE}` : message;
64009
+ const baseOutput = this.toString();
64010
+ const artifactRef = await this.maybeWriteArtifact();
64011
+ const output = artifactRef === void 0 ? baseOutput : this.appendArtifactRef(baseOutput, artifactRef);
64012
+ return {
64013
+ isError: true,
64014
+ output: finalMessage.length === 0 ? output : output.length === 0 ? finalMessage : output.endsWith("\n") ? `${output}${finalMessage}` : `${output}\n${finalMessage}`,
64015
+ truncated: this.truncationHappened,
64016
+ brief: options.brief
64017
+ };
64018
+ }
64019
+ };
64020
+ /** UTF-8 byte length; the budget unit for every tool result. */
64021
+ function byteLength$1(text) {
64022
+ return Buffer.byteLength(text, "utf8");
64023
+ }
64024
+ /**
64025
+ * Cut `text` to a byte budget without splitting a character.
64026
+ *
64027
+ * The cut backs up to the lead byte of the sequence it would have split, so the
64028
+ * result decodes to complete code points — no lone surrogate, no U+FFFD.
64029
+ */
64030
+ function truncateToBytes(text, maxBytes) {
64031
+ if (maxBytes <= 0) return "";
64032
+ if (text.length === 0) return text;
64033
+ if (byteLength$1(text) <= maxBytes) return text;
64034
+ const bytes = Buffer.from(text, "utf8");
64035
+ let end = Math.min(maxBytes, bytes.length);
64036
+ while (end > 0 && (bytes[end] & 192) === 128) end -= 1;
64037
+ return bytes.toString("utf8", 0, end);
64038
+ }
64039
+ /** Last `maxBytes` bytes of `text`, starting on a character boundary. */
64040
+ function tailToBytes(text, maxBytes) {
64041
+ if (maxBytes <= 0) return "";
64042
+ if (text.length === 0) return text;
64043
+ if (byteLength$1(text) <= maxBytes) return text;
64044
+ const bytes = Buffer.from(text, "utf8");
64045
+ let start = bytes.length - maxBytes;
64046
+ while (start < bytes.length && (bytes[start] & 192) === 128) start += 1;
64047
+ return bytes.toString("utf8", start);
64048
+ }
64049
+ /**
64050
+ * A single over-long line keeps its head and the truncation marker, and the cut
64051
+ * lands on a character boundary. The marker (and any trailing line break) is
64052
+ * reserved out of the same budget.
64053
+ */
64054
+ function truncateLineToBytes(line, maxLineBytes) {
64055
+ const lineBreak = /[\r\n]+$/.exec(line)?.[0] ?? "";
64056
+ const suffix = TRUNCATION_MARKER$1 + lineBreak;
64057
+ const keep = Math.max(0, maxLineBytes - byteLength$1(suffix));
64058
+ return `${truncateToBytes(line.slice(0, line.length - lineBreak.length), keep)}${suffix}`;
64059
+ }
64060
+ function tailAtCodePoint(text, maxBytes) {
64061
+ return tailToBytes(text, maxBytes);
64062
+ }
64063
+ function countLines$1(text) {
64064
+ if (text.length === 0) return 0;
64065
+ return text.split("\n").length - (text.endsWith("\n") ? 1 : 0);
64066
+ }
64067
+ //#endregion
63801
64068
  //#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-background-disabled.md
63802
64069
  var agent_background_disabled_default = "Background agent execution is disabled for this agent. Do not set `run_in_background=true`.";
63803
64070
  //#endregion
@@ -64201,13 +64468,15 @@ var AgentTool = class {
64201
64468
  }
64202
64469
  /** Render the completed-subagent result text (summary + optional structured block). */
64203
64470
  formatCompletion(handle, result, outputSchema) {
64471
+ const MAX_SUMMARY_BYTES = 50 * 1024;
64472
+ const summary = byteLength$1(result.result) <= MAX_SUMMARY_BYTES ? result.result : `${truncateToBytes(result.result, MAX_SUMMARY_BYTES - 160)}\n[...truncated; Agent(resume="${handle.agentId}") returns the rest]`;
64204
64473
  const lines = [
64205
64474
  `agent_id: ${handle.agentId}`,
64206
64475
  `actual_subagent_type: ${handle.profileName}`,
64207
64476
  "status: completed",
64208
64477
  "",
64209
64478
  "[summary]",
64210
- result.result
64479
+ summary
64211
64480
  ];
64212
64481
  if (outputSchema !== void 0) {
64213
64482
  const structured = parseJsonObject(result.result);
@@ -67848,6 +68117,216 @@ var DiscoverMcpToolsTool = class {
67848
68117
  }
67849
68118
  };
67850
68119
  //#endregion
68120
+ //#region ../../packages/agent-core/src/tools/builtin/mcp/resources.ts
68121
+ /**
68122
+ * MCP resource tools — the same three names and semantics the other agents in
68123
+ * this space converged on (`list_mcp_resources`, `list_mcp_resource_templates`,
68124
+ * `read_mcp_resource`), so a model that knows one of them knows all three:
68125
+ *
68126
+ * - `server` is optional; omitting it covers every connected server
68127
+ * - pagination is not exposed: our client lists a server's resources in one call
68128
+ * - output is JSON so the model can reason about URIs without re-parsing prose
68129
+ *
68130
+ * They exist as host tools rather than synthetic MCP tools because our MCP
68131
+ * registration path carries descriptors only (execution is always `callTool`),
68132
+ * and a resource read is not a tool call.
68133
+ */
68134
+ const ServerArg = z.string().min(1).optional().describe("Server config name. Omit to cover every connected server.");
68135
+ const ListMcpResourcesInputSchema = z.object({ server: ServerArg }).strict();
68136
+ const ListMcpResourceTemplatesInputSchema = z.object({ server: ServerArg }).strict();
68137
+ const ReadMcpResourceInputSchema = z.object({
68138
+ server: z.string().min(1).describe("Server config name."),
68139
+ uri: z.string().min(1).describe("Resource URI, as listed by list_mcp_resources.")
68140
+ }).strict();
68141
+ /** Connected servers with a client, optionally narrowed to one name. */
68142
+ function connectedServers(mcp, server) {
68143
+ const names = server === void 0 ? mcp.list().map((entry) => entry.name) : [server];
68144
+ const servers = [];
68145
+ for (const name of names) {
68146
+ const resolved = mcp.resolved(name);
68147
+ if (resolved !== void 0) servers.push({
68148
+ name,
68149
+ client: resolved.client
68150
+ });
68151
+ }
68152
+ return servers;
68153
+ }
68154
+ function unknownServer(server, mcp) {
68155
+ const entries = mcp.list();
68156
+ if (entries.length === 0) return "No MCP servers are configured.";
68157
+ const configured = entries.find((entry) => entry.name === server);
68158
+ if (configured !== void 0) return `MCP server "${server}" is configured but not connected (status: ${configured.status}).`;
68159
+ return `No MCP server named "${server}". Configured: ${entries.map((entry) => entry.name).join(", ")}.`;
68160
+ }
68161
+ var ListMcpResourcesTool = class {
68162
+ tools;
68163
+ name = "ListMcpResources";
68164
+ description = "List MCP resources. Omit `server` to cover every connected server. Returns JSON with `resources` (each carrying its `server`) and an `instructions` map for servers that sent any during the handshake.";
68165
+ parameters = toInputJsonSchema(ListMcpResourcesInputSchema);
68166
+ annotations = {
68167
+ readOnlyHint: true,
68168
+ openWorldHint: true
68169
+ };
68170
+ constructor(tools) {
68171
+ this.tools = tools;
68172
+ }
68173
+ resolveExecution(args) {
68174
+ return {
68175
+ description: "Listing MCP resources",
68176
+ approvalRule: this.name,
68177
+ execute: async () => {
68178
+ const mcp = this.tools.mcp;
68179
+ if (mcp === void 0) return {
68180
+ output: "MCP is not configured.",
68181
+ isError: true
68182
+ };
68183
+ const servers = connectedServers(mcp, args.server);
68184
+ if (servers.length === 0) return {
68185
+ output: args.server === void 0 ? "No MCP servers are connected." : unknownServer(args.server, mcp),
68186
+ isError: args.server !== void 0
68187
+ };
68188
+ const resources = [];
68189
+ const instructions = {};
68190
+ const failures = [];
68191
+ for (const { name, client } of servers) {
68192
+ if (client.listResources === void 0) continue;
68193
+ const text = client.getInstructions?.();
68194
+ if (text !== void 0) instructions[name] = text;
68195
+ try {
68196
+ for (const resource of await client.listResources()) resources.push({
68197
+ server: name,
68198
+ ...resource
68199
+ });
68200
+ } catch (error) {
68201
+ failures.push({
68202
+ server: name,
68203
+ error: error instanceof Error ? error.message : String(error)
68204
+ });
68205
+ }
68206
+ }
68207
+ if (resources.length === 0 && Object.keys(instructions).length === 0) return {
68208
+ output: JSON.stringify({
68209
+ resources: [],
68210
+ note: "No server advertises resources."
68211
+ }),
68212
+ isError: false
68213
+ };
68214
+ return {
68215
+ output: JSON.stringify({
68216
+ resources,
68217
+ ...Object.keys(instructions).length === 0 ? {} : { instructions },
68218
+ ...failures.length === 0 ? {} : { failures }
68219
+ }),
68220
+ isError: false
68221
+ };
68222
+ }
68223
+ };
68224
+ }
68225
+ };
68226
+ var ListMcpResourceTemplatesTool = class {
68227
+ tools;
68228
+ name = "ListMcpResourceTemplates";
68229
+ description = "List MCP resource templates (URI patterns a server can expand). Omit `server` for all connected servers.";
68230
+ parameters = toInputJsonSchema(ListMcpResourceTemplatesInputSchema);
68231
+ annotations = {
68232
+ readOnlyHint: true,
68233
+ openWorldHint: true
68234
+ };
68235
+ constructor(tools) {
68236
+ this.tools = tools;
68237
+ }
68238
+ resolveExecution(args) {
68239
+ return {
68240
+ description: "Listing MCP resource templates",
68241
+ approvalRule: this.name,
68242
+ execute: async () => {
68243
+ const mcp = this.tools.mcp;
68244
+ if (mcp === void 0) return {
68245
+ output: "MCP is not configured.",
68246
+ isError: true
68247
+ };
68248
+ const servers = connectedServers(mcp, args.server);
68249
+ if (servers.length === 0) return {
68250
+ output: args.server === void 0 ? "No MCP servers are connected." : unknownServer(args.server, mcp),
68251
+ isError: args.server !== void 0
68252
+ };
68253
+ const templates = [];
68254
+ const failures = [];
68255
+ for (const { name, client } of servers) {
68256
+ if (client.listResourceTemplates === void 0) continue;
68257
+ try {
68258
+ for (const template of await client.listResourceTemplates()) templates.push({
68259
+ server: name,
68260
+ ...template
68261
+ });
68262
+ } catch (error) {
68263
+ failures.push({
68264
+ server: name,
68265
+ error: error instanceof Error ? error.message : String(error)
68266
+ });
68267
+ }
68268
+ }
68269
+ return {
68270
+ output: JSON.stringify({
68271
+ resourceTemplates: templates,
68272
+ ...failures.length === 0 ? {} : { failures }
68273
+ }),
68274
+ isError: false
68275
+ };
68276
+ }
68277
+ };
68278
+ }
68279
+ };
68280
+ var ReadMcpResourceTool = class {
68281
+ tools;
68282
+ name = "ReadMcpResource";
68283
+ description = "Read one MCP resource by `server` + `uri`. Text contents are returned as-is; a resource without text content reports its MIME type so the caller knows it is binary.";
68284
+ parameters = toInputJsonSchema(ReadMcpResourceInputSchema);
68285
+ annotations = {
68286
+ readOnlyHint: true,
68287
+ openWorldHint: true
68288
+ };
68289
+ constructor(tools) {
68290
+ this.tools = tools;
68291
+ }
68292
+ resolveExecution(args) {
68293
+ return {
68294
+ description: `Reading MCP resource ${args.uri}`,
68295
+ approvalRule: this.name,
68296
+ execute: async () => {
68297
+ const mcp = this.tools.mcp;
68298
+ if (mcp === void 0) return {
68299
+ output: "MCP is not configured.",
68300
+ isError: true
68301
+ };
68302
+ const resolved = mcp.resolved(args.server);
68303
+ if (resolved === void 0) return {
68304
+ output: unknownServer(args.server, mcp),
68305
+ isError: true
68306
+ };
68307
+ const { client } = resolved;
68308
+ if (client.readResource === void 0) return {
68309
+ output: `Server "${args.server}" cannot read resources.`,
68310
+ isError: true
68311
+ };
68312
+ const content = await client.readResource(args.uri);
68313
+ if (content.text !== void 0) return {
68314
+ output: content.text,
68315
+ isError: false
68316
+ };
68317
+ return {
68318
+ output: JSON.stringify({
68319
+ uri: content.uri,
68320
+ mimeType: content.mimeType ?? null,
68321
+ note: "Resource has no text content (binary or unsupported)."
68322
+ }),
68323
+ isError: false
68324
+ };
68325
+ }
68326
+ };
68327
+ }
68328
+ };
68329
+ //#endregion
67851
68330
  //#region ../../node_modules/.pnpm/object-keys@1.1.1/node_modules/object-keys/isArguments.js
67852
68331
  var require_isArguments = /* @__PURE__ */ __commonJSMin(((exports, module) => {
67853
68332
  var toStr = Object.prototype.toString;
@@ -71773,6 +72252,7 @@ const ModelAliasSchema = z.object({
71773
72252
  displayName: z.string().optional(),
71774
72253
  reasoningKey: z.string().optional(),
71775
72254
  adaptiveThinking: z.boolean().optional(),
72255
+ cacheRetention: z.enum(["short", "long"]).optional(),
71776
72256
  forceThinking: z.boolean().optional(),
71777
72257
  thinkingLevels: z.array(ThinkingEffortSchema).optional()
71778
72258
  });
@@ -74241,7 +74721,7 @@ const diffLinesWithBudget = diffLines;
74241
74721
  * counts them: a trailing newline terminates the preceding line instead of
74242
74722
  * opening an empty one, and the empty string has no lines.
74243
74723
  */
74244
- function countLines$1(value) {
74724
+ function countLines(value) {
74245
74725
  if (value.length === 0) return 0;
74246
74726
  const lineBreaks = value.split("\n").length - 1;
74247
74727
  return value.endsWith("\n") ? lineBreaks : lineBreaks + 1;
@@ -74263,7 +74743,7 @@ function fileDiffSummary(before, after) {
74263
74743
  let added = 0;
74264
74744
  let removed = 0;
74265
74745
  for (const part of parts) {
74266
- const lines = countLines$1(part.value);
74746
+ const lines = countLines(part.value);
74267
74747
  if (part.added) added += lines;
74268
74748
  else if (part.removed) removed += lines;
74269
74749
  }
@@ -74879,6 +75359,8 @@ const GlobInputSchema = z.object({
74879
75359
  include_dirs: z.boolean().default(true).optional().describe("Whether to include directories in results. Defaults to true. Set false to return only files.")
74880
75360
  });
74881
75361
  const MAX_MATCHES = 1e3;
75362
+ /** Byte ceiling for the rendered path list (the match count is not a size cap). */
75363
+ const MAX_OUTPUT_BYTES$1 = 50 * 1024;
74882
75364
  /**
74883
75365
  * Directory basenames the Glob tool prunes by default — see
74884
75366
  * `globExcludesForPattern`. Kept as one exported list so the default and
@@ -75057,17 +75539,27 @@ var GlobTool = class {
75057
75539
  const relBase = searchRoots[0] ?? this.workspace.workspaceDir;
75058
75540
  const displayLines = paths.map((p) => relativizeIfUnder$1(p, relBase, pathClass));
75059
75541
  let output;
75542
+ let headerLines = 0;
75060
75543
  if (entries.length === 0 && !truncated) output = "No matches found";
75061
75544
  else {
75062
75545
  const lines = [];
75063
75546
  if (truncated) {
75064
75547
  lines.push(`[Truncated at ${String(MAX_MATCHES)} matches — use a more specific pattern]`);
75065
75548
  lines.push(`Only the first ${String(MAX_MATCHES)} matches are returned.`);
75549
+ headerLines = 2;
75066
75550
  }
75067
75551
  lines.push(...displayLines);
75068
75552
  if (!truncated && entries.length === 1e3) lines.push(`Found ${String(entries.length)} matches`);
75069
75553
  output = lines.join("\n");
75070
75554
  }
75555
+ if (byteLength$1(output) > 51200) {
75556
+ const raw = truncateToBytes(output, MAX_OUTPUT_BYTES$1 - 96);
75557
+ const lastBreak = raw.lastIndexOf("\n");
75558
+ const kept = lastBreak > 0 ? raw.slice(0, lastBreak) : raw;
75559
+ const keptPaths = Math.max(0, countKeptPaths(kept) - headerLines);
75560
+ const omitted = Math.max(1, displayLines.length - keptPaths);
75561
+ output = `${kept}\n[...${String(omitted)} paths omitted: output exceeds ${String(MAX_OUTPUT_BYTES$1 / 1024)} KB — narrow the pattern]`;
75562
+ }
75071
75563
  scanCache.set(searchRoots[0], args.pattern, includeDirs, output);
75072
75564
  return { output };
75073
75565
  } catch (error) {
@@ -75148,6 +75640,16 @@ function containsBraceExpansion(pattern) {
75148
75640
  }
75149
75641
  return false;
75150
75642
  }
75643
+ /**
75644
+ * Paths that survived the byte cut.
75645
+ *
75646
+ * The cut backs up to the last newline, so every retained line is a complete
75647
+ * path: `newlines + 1`, not `newlines`.
75648
+ */
75649
+ function countKeptPaths(text) {
75650
+ if (text.length === 0) return 0;
75651
+ return text.split("\n").length;
75652
+ }
75151
75653
  //#endregion
75152
75654
  //#region ../../node_modules/.pnpm/tar@7.5.15/node_modules/tar/dist/esm/index.min.js
75153
75655
  var vr = Object.defineProperty;
@@ -78536,161 +79038,6 @@ function isRegexSyntaxError(stderr) {
78536
79038
  return REGEX_ERROR_MARKERS.some((marker) => lower.includes(marker));
78537
79039
  }
78538
79040
  //#endregion
78539
- //#region ../../packages/agent-core/src/tools/support/result-builder.ts
78540
- const DEFAULT_MAX_CHARS = 5e4;
78541
- const DEFAULT_TAIL_CHARS = 2e4;
78542
- const DEFAULT_MAX_LINE_LENGTH = 2e3;
78543
- const TRUNCATION_MARKER$1 = "[...truncated]";
78544
- const TRUNCATION_MESSAGE = "Output is truncated to fit in the message.";
78545
- var ToolResultBuilder = class {
78546
- maxChars;
78547
- maxTailChars;
78548
- maxLineLength;
78549
- artifactSink;
78550
- maxFullOutputChars;
78551
- buffer = [];
78552
- nCharsValue = 0;
78553
- truncationHappened = false;
78554
- headTruncated = false;
78555
- tailBuf = [];
78556
- tailCharsValue = 0;
78557
- fullOutput;
78558
- fullOutputChars = 0;
78559
- totalLinesWritten = 0;
78560
- constructor(options = {}) {
78561
- this.maxChars = options.maxChars ?? DEFAULT_MAX_CHARS;
78562
- this.maxTailChars = options.maxTailChars ?? DEFAULT_TAIL_CHARS;
78563
- this.maxLineLength = options.maxLineLength === void 0 ? DEFAULT_MAX_LINE_LENGTH : options.maxLineLength;
78564
- if (this.maxLineLength !== null && this.maxLineLength <= 14) throw new Error("maxLineLength must be greater than the truncation marker length.");
78565
- this.artifactSink = options.artifactSink;
78566
- this.maxFullOutputChars = options.maxFullOutputChars ?? 1e6;
78567
- if (this.artifactSink !== void 0) this.fullOutput = [];
78568
- }
78569
- get nChars() {
78570
- return this.nCharsValue + this.tailCharsValue;
78571
- }
78572
- toString() {
78573
- const head = this.buffer.join("");
78574
- if (!this.headTruncated || this.tailCharsValue === 0) return head;
78575
- this.trimTail();
78576
- const tail = this.tailBuf.join("");
78577
- const separator = head.endsWith("\n") ? "" : "\n";
78578
- const elided = this.computeElidedLines(head, tail);
78579
- return `${head}${separator}${elided > 0 ? `[…${String(elided)} lines elided…]\n${TRUNCATION_MARKER$1}` : TRUNCATION_MARKER$1}\n${tail}`;
78580
- }
78581
- write(text) {
78582
- if (text.length === 0) return 0;
78583
- const lines = text.match(/[^\r\n]*(?:\r\n|[\n\r])|[^\r\n]+/g) ?? [];
78584
- if (lines.length === 0) return 0;
78585
- this.totalLinesWritten += lines.length;
78586
- if (this.fullOutput !== void 0 && this.fullOutputChars < this.maxFullOutputChars) {
78587
- this.fullOutput.push(text);
78588
- this.fullOutputChars += text.length;
78589
- }
78590
- let charsWritten = 0;
78591
- for (const originalLine of lines) if (this.nCharsValue < this.maxChars) {
78592
- const remainingChars = this.maxChars - this.nCharsValue;
78593
- const limit = this.maxLineLength === null ? remainingChars : Math.min(remainingChars, this.maxLineLength);
78594
- let line = originalLine;
78595
- if (line.length > limit) {
78596
- const suffix = TRUNCATION_MARKER$1 + (/[\r\n]+$/.exec(line)?.[0] ?? "");
78597
- const effectiveMaxLength = Math.max(limit, suffix.length);
78598
- line = line.slice(0, effectiveMaxLength - suffix.length) + suffix;
78599
- this.truncationHappened = true;
78600
- }
78601
- this.buffer.push(line);
78602
- charsWritten += line.length;
78603
- this.nCharsValue += line.length;
78604
- if (this.nCharsValue >= this.maxChars) {
78605
- this.headTruncated = true;
78606
- this.truncationHappened = true;
78607
- }
78608
- } else {
78609
- this.appendTail(originalLine);
78610
- charsWritten += originalLine.length;
78611
- this.headTruncated = true;
78612
- this.truncationHappened = true;
78613
- }
78614
- return charsWritten;
78615
- }
78616
- appendTail(text) {
78617
- if (text.length === 0) return;
78618
- if (this.maxTailChars === 0) return;
78619
- if (text.length >= this.maxTailChars) {
78620
- const trimmed = text.slice(-this.maxTailChars);
78621
- this.tailBuf.length = 0;
78622
- this.tailBuf.push(trimmed);
78623
- this.tailCharsValue = trimmed.length;
78624
- return;
78625
- }
78626
- this.tailBuf.push(text);
78627
- this.tailCharsValue += text.length;
78628
- if (this.tailCharsValue > this.maxTailChars * 2) this.trimTail();
78629
- }
78630
- trimTail() {
78631
- if (this.tailCharsValue <= this.maxTailChars) return;
78632
- const trimmed = this.tailBuf.join("").slice(-this.maxTailChars);
78633
- this.tailBuf.length = 0;
78634
- this.tailBuf.push(trimmed);
78635
- this.tailCharsValue = trimmed.length;
78636
- }
78637
- computeElidedLines(head, tail) {
78638
- if (this.totalLinesWritten === 0) return 0;
78639
- if (this.fullOutput !== void 0) {
78640
- const total = countLines(this.fullOutput.join(""));
78641
- return Math.max(0, total - countLines(head) - countLines(tail));
78642
- }
78643
- return Math.max(0, this.totalLinesWritten - countLines(head) - countLines(tail));
78644
- }
78645
- async maybeWriteArtifact() {
78646
- if (this.artifactSink === void 0 || this.fullOutput === void 0) return void 0;
78647
- if (!this.truncationHappened) return void 0;
78648
- const full = this.fullOutput.join("");
78649
- if (full.length === 0) return void 0;
78650
- try {
78651
- return await this.artifactSink(full);
78652
- } catch {
78653
- return;
78654
- }
78655
- }
78656
- appendArtifactRef(output, ref) {
78657
- const line = `[full output saved: ${ref}]`;
78658
- return output.length === 0 ? line : output.endsWith("\n") ? `${output}${line}` : `${output}\n${line}`;
78659
- }
78660
- async ok(message = "", options = {}) {
78661
- let finalMessage = message;
78662
- if (finalMessage.length > 0 && !finalMessage.endsWith(".")) finalMessage += ".";
78663
- if (this.truncationHappened) finalMessage = finalMessage.length === 0 ? TRUNCATION_MESSAGE : `${finalMessage} ${TRUNCATION_MESSAGE}`;
78664
- const baseOutput = this.toString();
78665
- const artifactRef = await this.maybeWriteArtifact();
78666
- const output = artifactRef === void 0 ? baseOutput : this.appendArtifactRef(baseOutput, artifactRef);
78667
- const shouldAppendMessage = finalMessage.length > 0 && (this.truncationHappened || output.length === 0);
78668
- return {
78669
- isError: false,
78670
- output: shouldAppendMessage ? output.length === 0 ? finalMessage : output.endsWith("\n") ? `${output}${finalMessage}` : `${output}\n${finalMessage}` : output,
78671
- message: shouldAppendMessage ? void 0 : finalMessage,
78672
- truncated: this.truncationHappened,
78673
- brief: options.brief
78674
- };
78675
- }
78676
- async error(message, options = {}) {
78677
- const finalMessage = this.truncationHappened ? message.length === 0 ? TRUNCATION_MESSAGE : `${message} ${TRUNCATION_MESSAGE}` : message;
78678
- const baseOutput = this.toString();
78679
- const artifactRef = await this.maybeWriteArtifact();
78680
- const output = artifactRef === void 0 ? baseOutput : this.appendArtifactRef(baseOutput, artifactRef);
78681
- return {
78682
- isError: true,
78683
- output: finalMessage.length === 0 ? output : output.length === 0 ? finalMessage : output.endsWith("\n") ? `${output}${finalMessage}` : `${output}\n${finalMessage}`,
78684
- truncated: this.truncationHappened,
78685
- brief: options.brief
78686
- };
78687
- }
78688
- };
78689
- function countLines(text) {
78690
- if (text.length === 0) return 0;
78691
- return text.split("\n").length - (text.endsWith("\n") ? 1 : 0);
78692
- }
78693
- //#endregion
78694
79041
  //#region ../../packages/agent-core/src/tools/builtin/file/grep.md
78695
79042
  var grep_default = "Search file contents using regular expressions (powered by ripgrep).\n\nUse Grep when the task is to find unknown content or unknown file locations. Do not use shell `grep` or `rg` directly; this tool applies workspace path policy, output limits, and sensitive-file filtering.\nALWAYS use Grep tool instead of running `grep` or `rg` from a shell - direct shell calls bypass workspace policy, output limits, and sensitive-file filtering.\n\nWrite patterns in ripgrep regex syntax, which differs from POSIX `grep` syntax. For example, braces are special, so escape them as `\\{` to match a literal `{`.\n\nHidden files (dotfiles such as `.gitlab-ci.yml` or `.eslintrc.json`) are searched by default.\n";
78696
79043
  //#endregion
@@ -79839,11 +80186,11 @@ async function partitionExistingPaths(paths, jian, workspace) {
79839
80186
  }
79840
80187
  //#endregion
79841
80188
  //#region ../../packages/agent-core/src/tools/builtin/file/read.md
79842
- var read_default = "Read a text file from the local filesystem.\n\nIf the user provides a concrete file path to a text file, call Read directly. Do not `Glob`, `ls`, or otherwise pre-check known text file paths; missing or invalid file paths return errors you can handle. Do not use Read for directories; use `ls` via Bash for a known directory, or Glob when you need files/directories matching a pattern. Use `Grep` only when the task is to search for unknown content or locations.\n\nWhen you need several files, prefer to read them in parallel: emit multiple `Read` calls in a single response instead of reading one file per turn.\n\n- Returns up to {{ MAX_BYTES_KB }} KB per call; lines longer than {{ MAX_LINE_LENGTH }} chars are truncated mid-line.\n- Sensitive files (`.env` files, credential stores, SSH keys, and similar secrets) are refused to protect secrets; do not attempt to read them.\n- Only UTF-8 text files can be read. Non-UTF-8 encodings, binary files, and files containing NUL bytes are refused; use `ReadMediaFile` for images or video, and Bash or an MCP tool for other binary formats.\n- Output format: `<line-number>\\t<content>` per line.\n- A `<system>...</system>` status block is appended after the file content; it summarizes how much was read (line and byte counts, truncation, line-ending notes) and is not part of the file itself. The status block includes an `Anchor: <hash>` value that can be passed to `Edit.anchor` to verify the file has not changed before editing.\n- Pure CRLF files are displayed with LF line endings; `Edit` matches this output and preserves CRLF when writing back.\n- Mixed or lone carriage-return line endings are shown as `\\r` and require exact `Edit.old_string` escapes.\n- After a successful `Edit`/`Write`, do not re-read solely to prove the write landed. When the task depends on an exact file, API, or output shape, inspect the final external contract before finishing.\n";
80189
+ var read_default = "Read a text file from the local filesystem.\n\nIf the user provides a concrete file path to a text file, call Read directly. Do not `Glob`, `ls`, or otherwise pre-check known text file paths; missing or invalid file paths return errors you can handle. Do not use Read for directories; use `ls` via Bash for a known directory, or Glob when you need files/directories matching a pattern. Use `Grep` only when the task is to search for unknown content or locations.\n\nWhen you need several files, prefer to read them in parallel: emit multiple `Read` calls in a single response instead of reading one file per turn.\n\n- Returns up to {{ MAX_BYTES_KB }} KB per call; lines longer than {{ MAX_LINE_BYTES }} bytes are truncated mid-line, and the status block names the exact bash command that prints such a line in full.\n- Sensitive files (`.env` files, credential stores, SSH keys, and similar secrets) are refused to protect secrets; do not attempt to read them.\n- Only UTF-8 text files can be read. Non-UTF-8 encodings, binary files, and files containing NUL bytes are refused; use `ReadMediaFile` for images or video, and Bash or an MCP tool for other binary formats.\n- Output format: `<line-number>\\t<content>` per line.\n- A `<system>...</system>` status block is appended after the file content; it summarizes how much was read (line and byte counts, truncation, line-ending notes) and is not part of the file itself. The status block includes an `Anchor: <hash>` value that can be passed to `Edit.anchor` to verify the file has not changed before editing.\n- Pure CRLF files are displayed with LF line endings; `Edit` matches this output and preserves CRLF when writing back.\n- Mixed or lone carriage-return line endings are shown as `\\r` and require exact `Edit.old_string` escapes.\n- After a successful `Edit`/`Write`, do not re-read solely to prove the write landed. When the task depends on an exact file, API, or output shape, inspect the final external contract before finishing.\n";
79843
80190
  //#endregion
79844
80191
  //#region ../../packages/agent-core/src/tools/builtin/file/read.ts
79845
80192
  const MAX_LINES = 1e3;
79846
- const MAX_LINE_LENGTH = 2e3;
80193
+ const MAX_LINE_BYTES = 2048;
79847
80194
  const MAX_BYTES = 100 * 1024;
79848
80195
  const S_IFMT$2 = 61440;
79849
80196
  const S_IFREG$1 = 32768;
@@ -79858,11 +80205,9 @@ z.object({
79858
80205
  content: z.string(),
79859
80206
  lineCount: z.number().int().nonnegative()
79860
80207
  });
79861
- function truncateLine(line, maxLength) {
79862
- if (line.length <= maxLength) return line;
79863
- const marker = "...";
79864
- const target = Math.max(maxLength, 3);
79865
- return line.slice(0, target - 3) + marker;
80208
+ function truncateLine(line, maxBytes) {
80209
+ if (byteLength$1(line) <= maxBytes) return line;
80210
+ return truncateToBytes(line, Math.max(maxBytes, 3) - 3) + "...";
79866
80211
  }
79867
80212
  function stripTrailingLf(line) {
79868
80213
  return line.endsWith("\n") ? line.slice(0, -1) : line;
@@ -79884,7 +80229,7 @@ function lineEndingStyleFromFlags(flags) {
79884
80229
  }
79885
80230
  function renderLine(entry, lineEndingStyle) {
79886
80231
  const modelContent = lineEndingStyle === "crlf" && entry.rawContent.endsWith("\r") ? entry.rawContent.slice(0, -1) : entry.rawContent;
79887
- const truncated = truncateLine(modelContent, MAX_LINE_LENGTH);
80232
+ const truncated = truncateLine(modelContent, MAX_LINE_BYTES);
79888
80233
  const renderedContent = lineEndingStyle === "mixed" ? makeCarriageReturnsVisible(truncated) : truncated;
79889
80234
  return {
79890
80235
  line: `${String(entry.lineNo)}\t${renderedContent}`,
@@ -79933,7 +80278,7 @@ function notReadableFileOutput(path) {
79933
80278
  const READ_DESCRIPTION = renderPrompt(read_default, {
79934
80279
  MAX_LINES,
79935
80280
  MAX_BYTES_KB: MAX_BYTES / 1024,
79936
- MAX_LINE_LENGTH
80281
+ MAX_LINE_BYTES
79937
80282
  });
79938
80283
  var ReadTool = class {
79939
80284
  jian;
@@ -80205,7 +80550,7 @@ var ReadTool = class {
80205
80550
  if (input.maxLinesReached) parts.push(`Max ${String(MAX_LINES)} lines reached.`);
80206
80551
  else if (input.maxBytesReached) parts.push(`Max ${String(MAX_BYTES)} bytes reached.`);
80207
80552
  else if (lineCount < input.requestedLines) parts.push("End of file reached.");
80208
- if (input.truncatedLineNumbers.length > 0) parts.push(`Lines [${input.truncatedLineNumbers.join(", ")}] exceed the ${String(MAX_LINE_LENGTH)}-char limit and were truncated. Re-read with line_offset and n_lines=1 to view individual long lines in full.`);
80553
+ if (input.truncatedLineNumbers.length > 0) parts.push(`Line(s) [${input.truncatedLineNumbers.join(", ")}] exceed the ${String(Math.floor(MAX_LINE_BYTES / 1024))} KB line limit. Read one with: bash sed -n '<line>p' <file> | head -c 65536 (prints up to 64 KB of that line)`);
80209
80554
  if (input.lineEndingStyle === "mixed") parts.push("Mixed or lone carriage-return line endings are shown as \\r. Use exact \\r\\n or \\r escapes in Edit.old_string for those lines.");
80210
80555
  parts.push(`Anchor: ${input.anchor}`);
80211
80556
  return parts.join(" ");
@@ -80741,7 +81086,7 @@ var WriteTool = class {
80741
81086
  overwriteFileDiffDisplay(content, existing) {
80742
81087
  if (existing.kind === "missing") return {
80743
81088
  kind: "file_diff",
80744
- added: countLines$1(content),
81089
+ added: countLines(content),
80745
81090
  removed: 0
80746
81091
  };
80747
81092
  if (existing.kind === "unavailable") return void 0;
@@ -81759,7 +82104,7 @@ var ManagePluginTool = class {
81759
82104
  if (record === void 0) return result(notFound(id, manager), true);
81760
82105
  const runtime = this.runtime;
81761
82106
  if (runtime === void 0) return result(fail$1("extension_runtime_unavailable", "This runtime has no extension manager, so plugin code cannot be activated.", "The plugin stays installed; use the /plugin command or report this so the host can expose an ExtensionRuntime."), true);
81762
- if (runtime.isActive(record.id)) return result({
82107
+ if (runtime.isActive(record.id, this.agent.sessionId)) return result({
81763
82108
  action: "activate",
81764
82109
  id: record.id,
81765
82110
  active: true,
@@ -81797,7 +82142,7 @@ var ManagePluginTool = class {
81797
82142
  async reloadCode(manager, id) {
81798
82143
  const record = manager.get(id);
81799
82144
  if (record === void 0) return result(notFound(id, manager), true);
81800
- if (this.runtime?.isActive(record.id) === true) {
82145
+ if (this.runtime?.isActive(record.id, this.agent?.sessionId) === true) {
81801
82146
  const stopped = await this.deactivate(manager, record.id);
81802
82147
  if (stopped.isError === true) return stopped;
81803
82148
  }
@@ -81811,12 +82156,15 @@ var ManagePluginTool = class {
81811
82156
  });
81812
82157
  }
81813
82158
  async deactivate(manager, id) {
82159
+ return this.deactivateSession(manager, id, this.agent?.sessionId);
82160
+ }
82161
+ async deactivateSession(manager, id, sessionId) {
81814
82162
  const record = manager.get(id);
81815
82163
  if (record === void 0) return result(notFound(id, manager), true);
81816
82164
  const runtime = this.runtime;
81817
82165
  if (runtime === void 0) return result(fail$1("extension_runtime_unavailable", "This runtime has no extension manager to deactivate from.", "Use the /plugin command or report this so the host can expose an ExtensionRuntime."), true);
81818
82166
  const wasActive = runtime.isActive(record.id);
81819
- await runtime.deactivate(record.id);
82167
+ await runtime.deactivate(record.id, sessionId);
81820
82168
  const toolsUnregistered = this.agent.services?.tools?.unregisterToolsByOwner?.(record.id) ?? 0;
81821
82169
  return result({
81822
82170
  action: "deactivate",
@@ -83598,7 +83946,11 @@ var BashTool = class {
83598
83946
  try {
83599
83947
  const builder = new ToolResultBuilder({ artifactSink: async (fullOutput) => {
83600
83948
  const artifactPath = join(tmpdir(), `scream-bash-output-${randomUUID()}.log`);
83601
- await writeFile(artifactPath, fullOutput, "utf8");
83949
+ await writeFile(artifactPath, fullOutput, {
83950
+ encoding: "utf8",
83951
+ mode: 384,
83952
+ flag: "wx"
83953
+ });
83602
83954
  return artifactPath;
83603
83955
  } });
83604
83956
  const stdoutDone = readStreamIntoBuilder(proc.stdout, builder, (text) => forwardChunk("stdout", text));
@@ -84499,7 +84851,7 @@ var FetchURLTool = class {
84499
84851
  output: "The response body is empty.",
84500
84852
  isError: false
84501
84853
  };
84502
- const builder = new ToolResultBuilder({ maxLineLength: null });
84854
+ const builder = new ToolResultBuilder({ maxLineBytes: null });
84503
84855
  builder.write(content);
84504
84856
  const message = kind === "passthrough" ? "The returned content is the full response body, returned verbatim." : "The returned content is the main text extracted from the page.";
84505
84857
  return builder.ok(message);
@@ -84584,7 +84936,7 @@ var PaperSearchTool = class {
84584
84936
  if (args.year_from !== void 0) options.yearFrom = args.year_from;
84585
84937
  if (args.sources !== void 0) options.sources = args.sources;
84586
84938
  const { results, failures } = await this.provider.search(args.query, options);
84587
- const builder = new ToolResultBuilder({ maxLineLength: null });
84939
+ const builder = new ToolResultBuilder({ maxLineBytes: null });
84588
84940
  if (results.length === 0) builder.write("No papers found.");
84589
84941
  else {
84590
84942
  let first = true;
@@ -84690,7 +85042,7 @@ var WebSearchTool = class {
84690
85042
  if (args.limit !== void 0) opts.limit = args.limit;
84691
85043
  if (args.include_content !== void 0) opts.includeContent = args.include_content;
84692
85044
  const results = await this.provider.search(args.query, opts);
84693
- const builder = new ToolResultBuilder({ maxLineLength: null });
85045
+ const builder = new ToolResultBuilder({ maxLineBytes: null });
84694
85046
  if (results.length === 0) {
84695
85047
  builder.write("No search results found.");
84696
85048
  return builder.ok();
@@ -90250,13 +90602,13 @@ const KILL_GRACE_MS = 100;
90250
90602
  * HookResult. Outputs below the cap round-trip byte-for-byte, so structured
90251
90603
  * JSON parsing is unaffected.
90252
90604
  */
90253
- const HOOK_STREAM_MAX_CHARS = 24 * 1024;
90254
- const HOOK_STREAM_TAIL_CHARS = 8 * 1024;
90605
+ const HOOK_STREAM_MAX_BYTES = 24 * 1024;
90606
+ const HOOK_STREAM_TAIL_BYTES = 8 * 1024;
90255
90607
  function createHookStreamBuffer() {
90256
90608
  return new ToolResultBuilder({
90257
- maxChars: HOOK_STREAM_MAX_CHARS,
90258
- maxTailChars: HOOK_STREAM_TAIL_CHARS,
90259
- maxLineLength: null
90609
+ maxBytes: HOOK_STREAM_MAX_BYTES,
90610
+ maxTailBytes: HOOK_STREAM_TAIL_BYTES,
90611
+ maxLineBytes: null
90260
90612
  });
90261
90613
  }
90262
90614
  const OptionalStringSchema = z.preprocess((value) => {
@@ -109584,6 +109936,9 @@ var ToolManager = class {
109584
109936
  this.agent.cron && new CronListTool(this.agent.cron),
109585
109937
  this.agent.cron && new CronDeleteTool(this.agent.cron),
109586
109938
  this.agent.mcp && new DiscoverMcpToolsTool(this),
109939
+ this.agent.mcp && new ListMcpResourcesTool(this.agent),
109940
+ this.agent.mcp && new ListMcpResourceTemplatesTool(this.agent),
109941
+ this.agent.mcp && new ReadMcpResourceTool(this.agent),
109587
109942
  this.agent.type === "main" && new CreateGoalTool(this.agent),
109588
109943
  this.agent.type === "main" && new UpdateGoalTool(this.agent, createGoalGrader(this.agent)),
109589
109944
  this.agent.type === "main" && new GetGoalTool(this.agent),
@@ -115069,6 +115424,47 @@ var HttpMcpClient = class {
115069
115424
  async listTools() {
115070
115425
  return (await this.client.listTools()).tools.map(toMcpToolDefinition);
115071
115426
  }
115427
+ getInstructions() {
115428
+ const instructions = this.client.getInstructions?.();
115429
+ return typeof instructions === "string" && instructions.length > 0 ? instructions : void 0;
115430
+ }
115431
+ async listResources() {
115432
+ if (this.client.getServerCapabilities()?.resources === void 0) return [];
115433
+ return (await this.client.listResources()).resources.map((resource) => ({
115434
+ uri: resource.uri,
115435
+ ...resource.name === void 0 ? {} : { name: resource.name },
115436
+ ...resource.description === void 0 ? {} : { description: resource.description },
115437
+ ...resource.mimeType === void 0 ? {} : { mimeType: resource.mimeType }
115438
+ }));
115439
+ }
115440
+ async readResource(uri) {
115441
+ const parts = (await this.client.readResource({ uri })).contents;
115442
+ const first = parts[0];
115443
+ if (first === void 0) return { uri };
115444
+ const texts = parts.map((part) => part.text).filter((value) => typeof value === "string");
115445
+ return {
115446
+ uri: first.uri ?? uri,
115447
+ ...first.mimeType === void 0 ? {} : { mimeType: first.mimeType },
115448
+ ...texts.length === 0 ? {} : { text: texts.join("\n\n") }
115449
+ };
115450
+ }
115451
+ async listResourceTemplates() {
115452
+ if (this.client.getServerCapabilities()?.resources === void 0) return [];
115453
+ return (await this.client.listResourceTemplates()).resourceTemplates.map((template) => ({
115454
+ uriTemplate: template.uriTemplate,
115455
+ ...template.name === void 0 ? {} : { name: template.name },
115456
+ ...template.description === void 0 ? {} : { description: template.description },
115457
+ ...template.mimeType === void 0 ? {} : { mimeType: template.mimeType }
115458
+ }));
115459
+ }
115460
+ async listPrompts() {
115461
+ if (this.client.getServerCapabilities()?.prompts === void 0) return [];
115462
+ return (await this.client.listPrompts()).prompts.map((prompt) => ({
115463
+ name: prompt.name,
115464
+ ...prompt.description === void 0 ? {} : { description: prompt.description },
115465
+ ...prompt.arguments === void 0 ? {} : { arguments: prompt.arguments.map((argument) => argument.name) }
115466
+ }));
115467
+ }
115072
115468
  async callTool(name, args, signal) {
115073
115469
  const requestOptions = buildRequestOptions(this.toolCallTimeoutMs, signal);
115074
115470
  return toMcpToolResult(await this.client.callTool({
@@ -115819,6 +116215,47 @@ var StdioMcpClient = class {
115819
116215
  async listTools() {
115820
116216
  return (await this.client.listTools()).tools.map(toMcpToolDefinition);
115821
116217
  }
116218
+ getInstructions() {
116219
+ const instructions = this.client.getInstructions?.();
116220
+ return typeof instructions === "string" && instructions.length > 0 ? instructions : void 0;
116221
+ }
116222
+ async listResources() {
116223
+ if (this.client.getServerCapabilities()?.resources === void 0) return [];
116224
+ return (await this.client.listResources()).resources.map((resource) => ({
116225
+ uri: resource.uri,
116226
+ ...resource.name === void 0 ? {} : { name: resource.name },
116227
+ ...resource.description === void 0 ? {} : { description: resource.description },
116228
+ ...resource.mimeType === void 0 ? {} : { mimeType: resource.mimeType }
116229
+ }));
116230
+ }
116231
+ async readResource(uri) {
116232
+ const parts = (await this.client.readResource({ uri })).contents;
116233
+ const first = parts[0];
116234
+ if (first === void 0) return { uri };
116235
+ const texts = parts.map((part) => part.text).filter((value) => typeof value === "string");
116236
+ return {
116237
+ uri: first.uri ?? uri,
116238
+ ...first.mimeType === void 0 ? {} : { mimeType: first.mimeType },
116239
+ ...texts.length === 0 ? {} : { text: texts.join("\n\n") }
116240
+ };
116241
+ }
116242
+ async listResourceTemplates() {
116243
+ if (this.client.getServerCapabilities()?.resources === void 0) return [];
116244
+ return (await this.client.listResourceTemplates()).resourceTemplates.map((template) => ({
116245
+ uriTemplate: template.uriTemplate,
116246
+ ...template.name === void 0 ? {} : { name: template.name },
116247
+ ...template.description === void 0 ? {} : { description: template.description },
116248
+ ...template.mimeType === void 0 ? {} : { mimeType: template.mimeType }
116249
+ }));
116250
+ }
116251
+ async listPrompts() {
116252
+ if (this.client.getServerCapabilities()?.prompts === void 0) return [];
116253
+ return (await this.client.listPrompts()).prompts.map((prompt) => ({
116254
+ name: prompt.name,
116255
+ ...prompt.description === void 0 ? {} : { description: prompt.description },
116256
+ ...prompt.arguments === void 0 ? {} : { arguments: prompt.arguments.map((argument) => argument.name) }
116257
+ }));
116258
+ }
115822
116259
  async callTool(name, args, signal) {
115823
116260
  const requestOptions = buildRequestOptions(this.toolCallTimeoutMs, signal);
115824
116261
  return toMcpToolResult(await this.client.callTool({
@@ -116323,6 +116760,29 @@ async function resolveSessionMcpConfig(input) {
116323
116760
  return { servers };
116324
116761
  }
116325
116762
  //#endregion
116763
+ //#region ../../packages/agent-core/src/mcp/instructions.ts
116764
+ /**
116765
+ * Server instructions from every connected MCP server, as a bullet list.
116766
+ *
116767
+ * The MCP handshake lets a server describe how its tools should be used
116768
+ * (`initialize.instructions`). Those notes are per-connection, so they cannot
116769
+ * live in the static system prompt: MCP servers load asynchronously after the
116770
+ * prompt is rendered, and a prompt rebuilt later would invalidate the prefix
116771
+ * cache. The session injects this text as a reminder once the connections
116772
+ * settle, and again when the set of connected servers changes.
116773
+ */
116774
+ function collectMcpInstructions(mcp) {
116775
+ if (mcp === void 0) return "";
116776
+ const blocks = [];
116777
+ for (const entry of mcp.list()) {
116778
+ if (entry.status !== "connected") continue;
116779
+ const text = mcp.resolved(entry.name)?.client.getInstructions?.();
116780
+ if (text === void 0) continue;
116781
+ blocks.push(`- **${entry.name}**: ${text.replaceAll("\n", "\n ")}`);
116782
+ }
116783
+ return blocks.join("\n");
116784
+ }
116785
+ //#endregion
116326
116786
  //#region ../../packages/agent-core/src/session/git-context.ts
116327
116787
  const GIT_TIMEOUT_MS = 5e3;
116328
116788
  const MAX_DIRTY_FILES = 20;
@@ -117433,6 +117893,9 @@ var Session$1 = class {
117433
117893
  backgroundManagers = /* @__PURE__ */ new Set();
117434
117894
  skillsReady;
117435
117895
  mcpReady;
117896
+ /** Coalesces a burst of MCP connect events into one instructions reminder. */
117897
+ mcpInstructionsTimer;
117898
+ lastMcpInstructions = "";
117436
117899
  metadata = {
117437
117900
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
117438
117901
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -117522,6 +117985,9 @@ var Session$1 = class {
117522
117985
  const { agent } = await this.createAgent({ type: "main" }, profile ?? DEFAULT_AGENT_PROFILES["agent"]);
117523
117986
  await agent.memoStoreReady;
117524
117987
  await agent.knowledgeStoreReady;
117988
+ this.mcpReady.then(() => {
117989
+ this.injectMcpInstructions();
117990
+ });
117525
117991
  await this.triggerSessionStart("startup");
117526
117992
  return agent;
117527
117993
  }
@@ -117556,10 +118022,17 @@ var Session$1 = class {
117556
118022
  kind: "injection",
117557
118023
  variant: "recap"
117558
118024
  });
118025
+ if (main !== void 0) this.mcpReady.then(() => {
118026
+ this.injectMcpInstructions();
118027
+ });
117559
118028
  await this.triggerSessionStart("resume");
117560
118029
  return { warning: resumeWarning };
117561
118030
  }
117562
118031
  async close() {
118032
+ if (this.mcpInstructionsTimer !== void 0) {
118033
+ clearTimeout(this.mcpInstructionsTimer);
118034
+ this.mcpInstructionsTimer = void 0;
118035
+ }
117563
118036
  for (const manager of this.liveBackgroundManagers()) manager.markSessionClosed();
117564
118037
  const main = this.agents.get("main");
117565
118038
  if (main !== void 0) {
@@ -117872,6 +118345,7 @@ var Session$1 = class {
117872
118345
  }).catch(() => {});
117873
118346
  }
117874
118347
  onMcpServerStatusChange(entry) {
118348
+ this.scheduleMcpInstructionsInjection();
117875
118349
  this.rpc.emitEvent({
117876
118350
  type: "mcp.server.status",
117877
118351
  agentId: "main",
@@ -117889,6 +118363,33 @@ var Session$1 = class {
117889
118363
  const timeoutS = this.options.background?.agentTaskTimeoutS;
117890
118364
  return timeoutS === void 0 ? void 0 : timeoutS * 1e3;
117891
118365
  }
118366
+ /**
118367
+ * Inject MCP server instructions once the connections settle.
118368
+ *
118369
+ * Servers connect asynchronously and often in a burst, so a short coalescing
118370
+ * window turns "three servers connected" into one reminder instead of three.
118371
+ * The text is compared against the last injection: reconnects that carry the
118372
+ * same instructions stay silent.
118373
+ */
118374
+ scheduleMcpInstructionsInjection() {
118375
+ if (this.mcpInstructionsTimer !== void 0) clearTimeout(this.mcpInstructionsTimer);
118376
+ this.mcpInstructionsTimer = setTimeout(() => {
118377
+ this.mcpInstructionsTimer = void 0;
118378
+ this.injectMcpInstructions();
118379
+ }, 250);
118380
+ this.mcpInstructionsTimer.unref?.();
118381
+ }
118382
+ injectMcpInstructions() {
118383
+ const text = collectMcpInstructions(this.mcp);
118384
+ if (text.length === 0 || text === this.lastMcpInstructions) return;
118385
+ const main = this.agents.get("main");
118386
+ if (main === void 0) return;
118387
+ this.lastMcpInstructions = text;
118388
+ main.context.appendSystemReminder(`<scream-mcp-instructions>\n${text}\n</scream-mcp-instructions>`, {
118389
+ kind: "injection",
118390
+ variant: "mcp-instructions"
118391
+ });
118392
+ }
117892
118393
  refreshAgentBuiltinTools() {
117893
118394
  for (const agent of this.agents.values()) {
117894
118395
  if (!agent.config.hasProvider) continue;
@@ -118104,24 +118605,78 @@ function createRPC() {
118104
118605
  return [leftClient, rightClient];
118105
118606
  }
118106
118607
  //#endregion
118107
- //#region ../../packages/agent-core/src/plugin/runtime/extension.ts
118608
+ //#region ../../packages/agent-core/src/plugin/runtime/ui.ts
118108
118609
  /**
118109
- * Loads and activates code-entry plugins inside the agent process.
118610
+ * `QuestionResult` may be the answer map itself or a wrapper carrying
118611
+ * `answers`; both shapes reach a plugin depending on the host.
118612
+ */
118613
+ function readAnswer(result, question) {
118614
+ if (result === null) return void 0;
118615
+ if ("answers" in result) return result.answers?.[question];
118616
+ return result[question];
118617
+ }
118618
+ /** Progress text is a status line; a burst of updates must not flood it. */
118619
+ const PROGRESS_MIN_INTERVAL_MS = 200;
118620
+ /**
118621
+ * Build the `ui` surface for one plugin activation.
118110
118622
  *
118111
- * Responsibilities:
118112
- * - `discover()` — pick plugins whose manifest declares an `entryPoint`
118113
- * - `load()` — dynamic-import the entry point (cached per path)
118114
- * - `activate()` — inject declared manifest hooks into the agent's HookEngine,
118115
- * then call the plugin's `activate(context)`; tools are registered by the
118116
- * plugin itself via `context.services.tools.registerUserTool`. Hooks and
118117
- * event subscriptions made during a failed activation are rolled back
118118
- * - `deactivate()` — symmetric removal (hooks + event subscriptions +
118119
- * deactivate hook + the cached module, so an edited entry point is re-imported
118120
- * on the next activate), isolated so a failing plugin never breaks the agent
118121
- *
118122
- * Activation is deliberately lazy (a `/plugin activate` command or explicit
118123
- * config opt-in), so merely installing a plugin never executes its code.
118623
+ * `notify` rides the existing `warning` event (the host already renders it as a
118624
+ * status message) with `code: 'plugin:<level>'` so the UI could style levels
118625
+ * later without a new event type. `confirm` rides `requestQuestion`, the same
118626
+ * channel `AskUserQuestion` uses, so the TUI needs no new dialog code.
118124
118627
  */
118628
+ function createPluginUi(agent, pluginId) {
118629
+ const notify = (level, message) => {
118630
+ agent.eventBus.dispatch({
118631
+ type: "warning",
118632
+ message: `[${pluginId}] ${message}`,
118633
+ code: `plugin:${level}`
118634
+ });
118635
+ };
118636
+ return {
118637
+ notify,
118638
+ async confirm(request) {
118639
+ const ask = agent.rpc?.requestQuestion;
118640
+ const confirmLabel = request.confirmLabel ?? "Yes";
118641
+ if (ask === void 0) {
118642
+ notify("warn", `"${request.title}" needs a confirmation, but this host cannot ask; denied.`);
118643
+ return false;
118644
+ }
118645
+ try {
118646
+ return readAnswer(await ask.call(agent.rpc, { questions: [{
118647
+ question: request.title,
118648
+ ...request.body === void 0 ? {} : { body: request.body },
118649
+ options: [{ label: confirmLabel }, { label: request.cancelLabel ?? "No" }]
118650
+ }] }), request.title) === confirmLabel;
118651
+ } catch {
118652
+ return false;
118653
+ }
118654
+ },
118655
+ progress(title) {
118656
+ let lastEmit = 0;
118657
+ const emit = (level, text) => {
118658
+ const now = Date.now();
118659
+ if (level === "info" && now - lastEmit < PROGRESS_MIN_INTERVAL_MS) return;
118660
+ lastEmit = now;
118661
+ notify(level, text);
118662
+ };
118663
+ emit("info", `${title}…`);
118664
+ return {
118665
+ update: (label) => {
118666
+ emit("info", `${title}: ${label}`);
118667
+ },
118668
+ done: (message) => {
118669
+ emit("info", message ?? `${title}: done`);
118670
+ },
118671
+ fail: (message) => {
118672
+ emit("error", message ?? `${title}: failed`);
118673
+ }
118674
+ };
118675
+ }
118676
+ };
118677
+ }
118678
+ //#endregion
118679
+ //#region ../../packages/agent-core/src/plugin/runtime/extension.ts
118125
118680
  var ExtensionRuntime = class {
118126
118681
  loaded = /* @__PURE__ */ new Map();
118127
118682
  /**
@@ -118133,6 +118688,11 @@ var ExtensionRuntime = class {
118133
118688
  * an activation failed, which made reload re-run the broken code.
118134
118689
  */
118135
118690
  generations = /* @__PURE__ */ new Map();
118691
+ /**
118692
+ * Live activations, keyed by session then plugin: two sessions can run the
118693
+ * same plugin independently (each with its own hooks, subscriptions and
118694
+ * user tools), and stopping it in one must not affect the other.
118695
+ */
118136
118696
  activations = /* @__PURE__ */ new Map();
118137
118697
  /** Plugins that declare a code entry point, in installation order. */
118138
118698
  discover(plugins) {
@@ -118183,7 +118743,9 @@ var ExtensionRuntime = class {
118183
118743
  }
118184
118744
  /** Activate a discovered plugin against a live agent. */
118185
118745
  async activate(agent, extension) {
118186
- if (this.activations.has(extension.pluginId)) throw new Error(`Plugin "${extension.pluginId}" is already active`);
118746
+ const sessionKey = agent.sessionId ?? "";
118747
+ const sessionActivations = this.sessionActivations(sessionKey);
118748
+ if (sessionActivations.has(extension.pluginId)) throw new Error(`Plugin "${extension.pluginId}" is already active`);
118187
118749
  await this.assertConfined(extension);
118188
118750
  const module = await this.load(extension.entryPoint);
118189
118751
  let hooksUnregister;
@@ -118192,10 +118754,11 @@ var ExtensionRuntime = class {
118192
118754
  let handlerFaults = 0;
118193
118755
  const onHandlerFault = () => {
118194
118756
  handlerFaults += 1;
118195
- if (handlerFaults >= 3 && this.isActive(extension.pluginId)) this.deactivate(extension.pluginId);
118757
+ if (handlerFaults >= 3 && this.isActive(extension.pluginId, sessionKey)) this.deactivate(extension.pluginId, sessionKey);
118196
118758
  };
118197
118759
  const context = {
118198
118760
  services: withPluginToolOwnership(agent.services, extension.pluginId),
118761
+ ui: createPluginUi(agent, extension.pluginId),
118199
118762
  events: createTrackedEventBus(agent.eventBus, subscriptions, onHandlerFault),
118200
118763
  config: extension.manifest.config,
118201
118764
  pluginId: extension.pluginId
@@ -118208,7 +118771,7 @@ var ExtensionRuntime = class {
118208
118771
  this.invalidate(extension.entryPoint);
118209
118772
  throw error;
118210
118773
  }
118211
- this.activations.set(extension.pluginId, {
118774
+ sessionActivations.set(extension.pluginId, {
118212
118775
  module,
118213
118776
  entryPoint: extension.entryPoint,
118214
118777
  hooksUnregister,
@@ -118217,13 +118780,26 @@ var ExtensionRuntime = class {
118217
118780
  agent
118218
118781
  });
118219
118782
  }
118220
- /** Deactivate a plugin: drop its hooks/subscriptions, then call its deactivate. */
118221
- async deactivate(pluginId) {
118222
- const activation = this.activations.get(pluginId);
118223
- if (activation === void 0) return;
118783
+ /**
118784
+ * Deactivate a plugin in one session, or everywhere when no session is given.
118785
+ *
118786
+ * Hosts that do not know the owning session (a global disable/remove, a
118787
+ * capability sync unwind) pass `undefined` and hit every copy; an explicit
118788
+ * session id only unwinds that one.
118789
+ */
118790
+ async deactivate(pluginId, sessionId) {
118791
+ const sessionKeys = sessionId === void 0 ? [...this.activations.keys()] : sessionId !== "" && this.activations.has(sessionId) ? [sessionId] : sessionId === "" ? [...this.activations.keys()].filter((key) => key === "") : [];
118792
+ for (const sessionKey of sessionKeys) await this.deactivateInSession(sessionKey, pluginId);
118793
+ }
118794
+ /** Tear down one activation record; the plugin's own deactivate is isolated. */
118795
+ async deactivateInSession(sessionKey, pluginId) {
118796
+ const sessionActivations = this.activations.get(sessionKey);
118797
+ const activation = sessionActivations?.get(pluginId);
118798
+ if (sessionActivations === void 0 || activation === void 0) return;
118224
118799
  activation.hooksUnregister?.();
118225
118800
  releaseSubscriptions(activation.subscriptions);
118226
- this.activations.delete(pluginId);
118801
+ sessionActivations.delete(pluginId);
118802
+ if (sessionActivations.size === 0) this.activations.delete(sessionKey);
118227
118803
  this.invalidate(activation.entryPoint);
118228
118804
  try {
118229
118805
  activation.agent.services.tools?.unregisterToolsByOwner?.(pluginId);
@@ -118233,6 +118809,7 @@ var ExtensionRuntime = class {
118233
118809
  } catch (error) {
118234
118810
  activation.log?.warn("plugin deactivate failed", {
118235
118811
  pluginId,
118812
+ sessionId: sessionKey === "" ? void 0 : sessionKey,
118236
118813
  error: error instanceof Error ? error.message : String(error)
118237
118814
  });
118238
118815
  }
@@ -118241,14 +118818,40 @@ var ExtensionRuntime = class {
118241
118818
  * Deactivate every live plugin. Provided so a host can tear down all code
118242
118819
  * plugins in one call; each plugin's failure is isolated from the others.
118243
118820
  */
118821
+ /**
118822
+ * Tear down every plugin activated in one session.
118823
+ *
118824
+ * Called when a session closes: its bucket would otherwise outlive the
118825
+ * agent, keeping `isActive(pluginId)` true and making the plugin centre show
118826
+ * ● for a session nobody runs.
118827
+ */
118828
+ async deactivateSession(sessionId) {
118829
+ const sessionActivations = this.activations.get(sessionId);
118830
+ if (sessionActivations === void 0) return;
118831
+ for (const pluginId of Array.from(sessionActivations.keys())) await this.deactivateInSession(sessionId, pluginId);
118832
+ }
118244
118833
  async deactivateAll() {
118245
- for (const pluginId of Array.from(this.activations.keys())) await this.deactivate(pluginId);
118834
+ for (const [sessionKey, plugins] of Array.from(this.activations.entries())) for (const pluginId of Array.from(plugins.keys())) await this.deactivateInSession(sessionKey, pluginId);
118246
118835
  }
118247
- isActive(pluginId) {
118248
- return this.activations.has(pluginId);
118836
+ /** Whether the plugin is live in the given session (or anywhere, when omitted). */
118837
+ isActive(pluginId, sessionId) {
118838
+ if (sessionId !== void 0) return this.activations.get(sessionId)?.has(pluginId) === true;
118839
+ for (const plugins of this.activations.values()) if (plugins.has(pluginId)) return true;
118840
+ return false;
118841
+ }
118842
+ /** Live plugin ids for a session (or across all sessions, when omitted). */
118843
+ activePluginIds(sessionId) {
118844
+ if (sessionId !== void 0) return [...this.activations.get(sessionId)?.keys() ?? []];
118845
+ return [...new Set([...this.activations.values()].flatMap((plugins) => [...plugins.keys()]))];
118249
118846
  }
118250
- activePluginIds() {
118251
- return [...this.activations.keys()];
118847
+ /** Get-or-create the per-session bucket. */
118848
+ sessionActivations(sessionKey) {
118849
+ let bucket = this.activations.get(sessionKey);
118850
+ if (bucket === void 0) {
118851
+ bucket = /* @__PURE__ */ new Map();
118852
+ this.activations.set(sessionKey, bucket);
118853
+ }
118854
+ return bucket;
118252
118855
  }
118253
118856
  };
118254
118857
  function normalizeExtensionModule(mod) {
@@ -133577,7 +134180,7 @@ var ProviderManager = class {
133577
134180
  const providerConfig = this.config.providers[providerName];
133578
134181
  if (providerConfig === void 0) throw new ScreamError(ErrorCodes.CONFIG_INVALID, `Provider "${providerName}" for model "${model}" is not configured.`);
133579
134182
  if (!Number.isInteger(alias.maxContextSize) || alias.maxContextSize <= 0) throw new ScreamError(ErrorCodes.CONFIG_INVALID, `Model "${model}" must define a positive max_context_size in config.toml.`);
133580
- const provider = toLtodProviderConfig(providerConfig, alias.model, this.options.screamRequestHeaders, alias.maxOutputSize, alias.reasoningKey, this.options.promptCacheKey, alias.adaptiveThinking, alias.forceThinking);
134183
+ const provider = toLtodProviderConfig(providerConfig, alias.model, this.options.screamRequestHeaders, alias.maxOutputSize, alias.reasoningKey, this.options.promptCacheKey, alias.adaptiveThinking, alias.forceThinking, alias.cacheRetention);
133581
134184
  return {
133582
134185
  providerName,
133583
134186
  provider,
@@ -133666,7 +134269,7 @@ function resolvedHeaderFields(provider, sessionId, screamRequestHeaders) {
133666
134269
  };
133667
134270
  return Object.keys(headers).length > 0 ? headers : void 0;
133668
134271
  }
133669
- function toLtodProviderConfig(provider, model, screamRequestHeaders, maxOutputSize, reasoningKey, promptCacheKey, adaptiveThinking, forceThinking) {
134272
+ function toLtodProviderConfig(provider, model, screamRequestHeaders, maxOutputSize, reasoningKey, promptCacheKey, adaptiveThinking, forceThinking, cacheRetention) {
133670
134273
  switch (provider.type) {
133671
134274
  case "anthropic": return {
133672
134275
  type: "anthropic",
@@ -133675,6 +134278,7 @@ function toLtodProviderConfig(provider, model, screamRequestHeaders, maxOutputSi
133675
134278
  apiKey: providerApiKey(provider),
133676
134279
  ...maxOutputSize !== void 0 ? { defaultMaxTokens: maxOutputSize } : {},
133677
134280
  ...adaptiveThinking !== void 0 ? { adaptiveThinking } : {},
134281
+ ...cacheRetention !== void 0 ? { cacheRetention } : {},
133678
134282
  ...defaultHeadersField(resolvedHeaderFields(provider, promptCacheKey, screamRequestHeaders))
133679
134283
  };
133680
134284
  case "openai": return {
@@ -134741,6 +135345,7 @@ var ScreamCore = class {
134741
135345
  if (session) try {
134742
135346
  await session.close();
134743
135347
  } finally {
135348
+ await this.extensionRuntime.deactivateSession(sessionId).catch(() => void 0);
134744
135349
  this.sessions.delete(sessionId);
134745
135350
  }
134746
135351
  }
@@ -134749,6 +135354,7 @@ var ScreamCore = class {
134749
135354
  if (active) try {
134750
135355
  await active.close();
134751
135356
  } finally {
135357
+ await this.extensionRuntime.deactivateSession(sessionId).catch(() => void 0);
134752
135358
  this.sessions.delete(sessionId);
134753
135359
  }
134754
135360
  await this.sessionStore.delete(sessionId);
@@ -134927,6 +135533,7 @@ var ScreamCore = class {
134927
135533
  if (agent === void 0) throw new ScreamError(ErrorCodes.AGENT_NOT_FOUND, `Session "${sessionId}" has no main agent`, { details: { sessionId } });
134928
135534
  const plugin = this.plugins.list().find((record) => record.id === pluginId);
134929
135535
  if (plugin === void 0) throw new ScreamError(ErrorCodes.PLUGIN_NOT_FOUND, `Plugin "${pluginId}" was not found`, { details: { pluginId } });
135536
+ if (!plugin.enabled) throw new ScreamError(ErrorCodes.PLUGIN_NOT_FOUND, `Plugin "${pluginId}" is disabled; enable it before activating`, { details: { pluginId } });
134930
135537
  const [extension] = this.extensionRuntime.discover([plugin]);
134931
135538
  if (extension === void 0) throw new ScreamError(ErrorCodes.PLUGIN_NOT_FOUND, `Plugin "${pluginId}" has no code entry point`, { details: { pluginId } });
134932
135539
  try {
@@ -134954,20 +135561,23 @@ var ScreamCore = class {
134954
135561
  }
134955
135562
  }
134956
135563
  /** Deactivate a code plugin (removes its hooks and runs its deactivate). */
134957
- async deactivatePlugin({ pluginId }) {
135564
+ async deactivatePlugin({ pluginId, sessionId }) {
134958
135565
  await this.pluginsReady;
134959
- await this.extensionRuntime.deactivate(pluginId);
134960
- for (const session of this.sessions.values()) for (const agent of session.agents.values()) try {
134961
- agent.tools.unregisterToolsByOwner(pluginId);
134962
- } catch {}
135566
+ await this.extensionRuntime.deactivate(pluginId, sessionId);
135567
+ for (const [id, session] of this.sessions) {
135568
+ if (sessionId !== void 0 && id !== sessionId) continue;
135569
+ for (const agent of session.agents.values()) try {
135570
+ agent.tools.unregisterToolsByOwner(pluginId);
135571
+ } catch {}
135572
+ }
134963
135573
  }
134964
135574
  /** Code plugins the runtime can load, with their activation state. */
134965
- async pluginExtensionStatus() {
135575
+ async pluginExtensionStatus({ sessionId } = {}) {
134966
135576
  await this.pluginsReady;
134967
135577
  return this.extensionRuntime.discover(this.plugins.list()).map((extension) => ({
134968
135578
  pluginId: extension.pluginId,
134969
135579
  entryPoint: extension.entryPoint,
134970
- active: this.extensionRuntime.isActive(extension.pluginId)
135580
+ active: this.extensionRuntime.isActive(extension.pluginId, sessionId)
134971
135581
  }));
134972
135582
  }
134973
135583
  unregisterTool({ sessionId, ...payload }) {
@@ -136187,7 +136797,10 @@ var SDKRpcClient = class {
136187
136797
  });
136188
136798
  }
136189
136799
  async deactivatePlugin(input) {
136190
- return (await this.getRpc()).deactivatePlugin({ pluginId: input.pluginId });
136800
+ return (await this.getRpc()).deactivatePlugin({
136801
+ pluginId: input.pluginId,
136802
+ ...input.sessionId === void 0 ? {} : { sessionId: input.sessionId }
136803
+ });
136191
136804
  }
136192
136805
  async pluginExtensionStatus(input) {
136193
136806
  return (await this.getRpc()).pluginExtensionStatus({ sessionId: input.sessionId });
@@ -137140,25 +137753,58 @@ function catalogCachePath(screamHome) {
137140
137753
  * the next time the network is unreachable. Best-effort — write failures
137141
137754
  * are silently ignored so they never block the happy path.
137142
137755
  */
137143
- function saveCatalogCache(catalog, screamHome) {
137756
+ function saveCatalogCache(catalog, screamHome, meta = {}) {
137144
137757
  try {
137145
137758
  const path = catalogCachePath(screamHome);
137146
137759
  mkdirSync(dirname$1(path), { recursive: true });
137147
- writeFileSync(path, JSON.stringify(catalog), "utf-8");
137760
+ const payload = meta.etag === void 0 && meta.fetchedAt === void 0 ? catalog : {
137761
+ catalog,
137762
+ ...meta
137763
+ };
137764
+ writeFileSync(path, JSON.stringify(payload), "utf-8");
137148
137765
  } catch {}
137149
137766
  }
137150
137767
  /**
137151
- * Load the most recently cached catalog snapshot. Returns `undefined` when
137152
- * no cache file exists or the file is corrupt.
137768
+ * Update only the freshness metadata of an existing snapshot (used after a
137769
+ * `304 Not Modified`, where there is no new catalog to store).
137153
137770
  */
137154
- function loadCatalogCache(screamHome) {
137771
+ function saveCatalogCacheMeta(screamHome, meta) {
137772
+ try {
137773
+ const path = catalogCachePath(screamHome);
137774
+ const current = loadCatalogCacheFile(screamHome);
137775
+ if (current === void 0) return;
137776
+ writeFileSync(path, JSON.stringify({
137777
+ ...current,
137778
+ ...meta
137779
+ }), "utf-8");
137780
+ } catch {}
137781
+ }
137782
+ /** Reads the cache file in either shape: a bare catalog (legacy) or `{catalog, etag, fetchedAt}`. */
137783
+ function loadCatalogCacheFile(screamHome) {
137155
137784
  try {
137156
- const raw = readFileSync(catalogCachePath(screamHome), "utf-8");
137157
- return JSON.parse(raw);
137785
+ const raw = JSON.parse(readFileSync(catalogCachePath(screamHome), "utf-8"));
137786
+ if (raw !== null && typeof raw === "object" && "catalog" in raw) return raw;
137787
+ return { catalog: raw };
137158
137788
  } catch {
137159
137789
  return;
137160
137790
  }
137161
137791
  }
137792
+ /** Freshness metadata of the cached snapshot, when it has any. */
137793
+ function loadCatalogCacheMeta(screamHome) {
137794
+ const file = loadCatalogCacheFile(screamHome);
137795
+ if (file === void 0) return {};
137796
+ return {
137797
+ ...file.fetchedAt === void 0 ? {} : { fetchedAt: file.fetchedAt },
137798
+ ...file.etag === void 0 ? {} : { etag: file.etag }
137799
+ };
137800
+ }
137801
+ /**
137802
+ * Load the most recently cached catalog snapshot. Returns `undefined` when
137803
+ * no cache file exists or the file is corrupt.
137804
+ */
137805
+ function loadCatalogCache(screamHome) {
137806
+ return loadCatalogCacheFile(screamHome)?.catalog;
137807
+ }
137162
137808
  const DEFAULT_CATALOG_URL = "https://models.dev/api.json";
137163
137809
  var CatalogFetchError = class extends Error {
137164
137810
  status;
@@ -137178,6 +137824,28 @@ async function fetchCatalog(url, signal, fetchImpl = fetch) {
137178
137824
  if (typeof payload !== "object" || payload === null || Array.isArray(payload)) throw new Error(`Unexpected catalog response from ${url}.`);
137179
137825
  return payload;
137180
137826
  }
137827
+ /**
137828
+ * Conditional catalog fetch: sends `If-None-Match` when an ETag is known and
137829
+ * reports `304` as `notModified` instead of throwing.
137830
+ */
137831
+ async function fetchCatalogWithMeta(url, options = {}, fetchImpl = fetch) {
137832
+ const headers = { Accept: "application/json" };
137833
+ if (options.etag !== void 0) headers["If-None-Match"] = options.etag;
137834
+ const res = await fetchImpl(url, {
137835
+ headers,
137836
+ ...options.signal === void 0 ? {} : { signal: options.signal }
137837
+ });
137838
+ if (res.status === 304) return { notModified: true };
137839
+ if (!res.ok) throw new CatalogFetchError(`Failed to fetch catalog (HTTP ${res.status}).`, res.status);
137840
+ const payload = await res.json();
137841
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) throw new Error(`Unexpected catalog response from ${url}.`);
137842
+ const etag = res.headers.get("etag");
137843
+ return {
137844
+ catalog: payload,
137845
+ notModified: false,
137846
+ ...etag === null ? {} : { etag }
137847
+ };
137848
+ }
137181
137849
  function capabilityToStrings(capability) {
137182
137850
  const caps = [];
137183
137851
  if (capability.image_in) caps.push("image_in");
@@ -143487,7 +144155,7 @@ function sumTokenRows(rows) {
143487
144155
  output
143488
144156
  };
143489
144157
  }
143490
- function buildSessionUsageSection(usage, error, table, value, muted, errorStyle, subagentUsage) {
144158
+ function buildSessionUsageSection(usage, error, table, value, muted, errorStyle, subagentUsage, priceFor) {
143491
144159
  if (error !== void 0) return [errorStyle(` ${error}`)];
143492
144160
  const byModel = usage?.byModel;
143493
144161
  const entries = Object.entries(byModel ?? {});
@@ -143496,6 +144164,12 @@ function buildSessionUsageSection(usage, error, table, value, muted, errorStyle,
143496
144164
  const sessionTotal = sumTokenRows(entries.map(([, row]) => row));
143497
144165
  const subagentRows = Object.values(subagentUsage ?? {});
143498
144166
  const subagentTotal = sumTokenRows(subagentRows);
144167
+ const priceOf = (model) => priceFor?.(model);
144168
+ const modelCosts = entries.map(([model, row]) => estimateCost(row, priceOf(model)));
144169
+ const sessionCost = modelCosts.reduce((acc, cost) => {
144170
+ if (cost === void 0) return acc;
144171
+ return acc === void 0 ? cost : acc + cost;
144172
+ }, void 0);
143499
144173
  const lines = [];
143500
144174
  lines.push(padName(t("usage.session_total")) + num(sessionTotal.input) + num(sessionTotal.output) + num(sessionTotal.input + sessionTotal.output));
143501
144175
  let cacheReadTotal = 0;
@@ -143505,6 +144179,7 @@ function buildSessionUsageSection(usage, error, table, value, muted, errorStyle,
143505
144179
  cacheCreationTotal += usageNumber(row.inputCacheCreation);
143506
144180
  }
143507
144181
  if (cacheReadTotal > 0 || cacheCreationTotal > 0) lines.push(padName(` ↳ ${t("usage.cache_rw")}`) + num(cacheReadTotal) + num(cacheCreationTotal));
144182
+ if (sessionCost !== void 0) lines.push(padName(` ↳ ${t("usage.cost")}`) + value(formatUsd(sessionCost)));
143508
144183
  if (subagentRows.length > 0) {
143509
144184
  const mainInput = Math.max(0, sessionTotal.input - subagentTotal.input);
143510
144185
  const mainOutput = Math.max(0, sessionTotal.output - subagentTotal.output);
@@ -143512,10 +144187,11 @@ function buildSessionUsageSection(usage, error, table, value, muted, errorStyle,
143512
144187
  lines.push(padName(` └ ${t("usage.sub_agent")}`) + num(subagentTotal.input) + num(subagentTotal.output) + num(subagentTotal.input + subagentTotal.output));
143513
144188
  }
143514
144189
  lines.push(usageTableHeader(table, t("usage.model")));
143515
- for (const [model, row] of entries) {
144190
+ for (const [entryIndex, [model, row]] of entries.entries()) {
143516
144191
  const input = usageInputTotal$1(row);
143517
144192
  const output = usageNumber(row.output);
143518
- lines.push(padNameColored(model, muted) + num(input) + num(output) + num(input + output));
144193
+ const costSuffix = modelCosts[entryIndex] === void 0 ? "" : ` ${muted(formatUsd(modelCosts[entryIndex]))}`;
144194
+ lines.push(padNameColored(model, muted) + num(input) + num(output) + num(input + output) + costSuffix);
143519
144195
  }
143520
144196
  return lines;
143521
144197
  }
@@ -143584,7 +144260,7 @@ function buildUsageReportLines(options) {
143584
144260
  ...subagentNames,
143585
144261
  t("usage.managed_title")
143586
144262
  ], options.terminalWidth);
143587
- const lines = buildSessionUsageSection(options.sessionUsage, options.sessionUsageError, table, value, muted, errorStyle, options.subagentUsage);
144263
+ const lines = buildSessionUsageSection(options.sessionUsage, options.sessionUsageError, table, value, muted, errorStyle, options.subagentUsage, options.priceFor);
143588
144264
  if (options.maxContextTokens > 0) {
143589
144265
  const ratio = safeUsageRatio(options.contextUsage);
143590
144266
  const bar = renderProgressBar(ratio, 20);
@@ -143750,7 +144426,7 @@ function dismissInfoPanel(state) {
143750
144426
  activeInfoTimer = void 0;
143751
144427
  }
143752
144428
  if (activeInfoPanel !== void 0) {
143753
- state.transcriptContainer.removeChild(activeInfoPanel);
144429
+ state.transcriptContainer.removeChild.bind(state.transcriptContainer)(activeInfoPanel);
143754
144430
  activeInfoPanel = void 0;
143755
144431
  state.ui.requestRender();
143756
144432
  }
@@ -143761,6 +144437,21 @@ function clearInfoPanelState(state) {
143761
144437
  async function showUsage(host) {
143762
144438
  const sessionUsage = await loadSessionUsageReport(host);
143763
144439
  const managedUsage = await loadManagedUsageReport(host);
144440
+ const catalog = loadCatalogCache(resolveScreamHome());
144441
+ const prices = /* @__PURE__ */ new Map();
144442
+ if (catalog !== void 0) {
144443
+ for (const [providerId, provider] of Object.entries(catalog)) for (const [modelId, entry] of Object.entries(provider.models ?? {})) if (entry.cost?.input !== void 0 && entry.cost.output !== void 0) {
144444
+ const price = {
144445
+ input: entry.cost.input,
144446
+ output: entry.cost.output,
144447
+ ...entry.cost.cache_read === void 0 ? {} : { cacheRead: entry.cost.cache_read },
144448
+ ...entry.cost.cache_write === void 0 ? {} : { cacheWrite: entry.cost.cache_write }
144449
+ };
144450
+ prices.set(modelId, price);
144451
+ prices.set(`${providerId}/${modelId}`, price);
144452
+ }
144453
+ }
144454
+ const priceFor = (modelId) => prices.get(modelId);
143764
144455
  const lines = buildUsageReportLines({
143765
144456
  colors: host.state.theme.colors,
143766
144457
  sessionUsage: sessionUsage.usage,
@@ -143771,7 +144462,8 @@ async function showUsage(host) {
143771
144462
  managedUsage: managedUsage?.usage,
143772
144463
  managedUsageError: managedUsage?.error,
143773
144464
  subagentUsage: host.state.appState.subagentUsage,
143774
- terminalWidth: host.state.terminal.columns
144465
+ terminalWidth: host.state.terminal.columns,
144466
+ priceFor
143775
144467
  });
143776
144468
  dismissInfoPanel(host.state);
143777
144469
  const panel = new UsagePanelComponent(lines, host.state.theme.colors.primary);
@@ -145484,7 +146176,7 @@ async function guidedGoalSetup(host) {
145484
146176
  host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
145485
146177
  return;
145486
146178
  }
145487
- const { TextInputDialogComponent } = await import("./text-input-dialog-CbYhsAbz.mjs");
146179
+ const { TextInputDialogComponent } = await import("./text-input-dialog-BLUlZjUL.mjs");
145488
146180
  const initialDesc = await promptText(host, TextInputDialogComponent, {
145489
146181
  title: t("goal.setup_title_initial"),
145490
146182
  subtitle: t("goal.setup_desc_hint"),
@@ -145505,7 +146197,7 @@ async function guidedGoalSetup(host) {
145505
146197
  await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
145506
146198
  }
145507
146199
  async function showGoalConfigWizard(host, session, objective, replace) {
145508
- const { TextInputDialogComponent } = await import("./text-input-dialog-CbYhsAbz.mjs");
146200
+ const { TextInputDialogComponent } = await import("./text-input-dialog-BLUlZjUL.mjs");
145509
146201
  const turnInput = await promptNumber(host, TextInputDialogComponent, {
145510
146202
  title: t("goal.wizard_title", { objective }),
145511
146203
  subtitle: t("goal.budget_turns_hint"),
@@ -157509,4 +158201,4 @@ async function handleBuiltInSlashCommand(host, name, args) {
157509
158201
  }
157510
158202
  }
157511
158203
  //#endregion
157512
- export { handleExportMdCommand as $, validateFeishuCredentials as $n, handleLogoutCommand as $t, langFromPath as A, hasEmbeddingModelCache as An, flushDiagnosticLogs as Ar, createThemeStyles as At, getSharedSpeedTracker as B, isSendThrottled as Bn, OSC11_RESPONSE as Bt, handleLineCommand as C, BUILTIN_SLASH_COMMANDS as Cn, fetchCatalog as Cr, showUsage as Ct, renderDiffLines as D, embeddingProgressLabel as Dn, MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE as Dr, isEmptySessionHintDismissed as Dt, ToolCallComponent as E, setExperimentalFlags as En, normalizeWorkDir as Er, createMarkdownTheme as Et, SkillActivationComponent as F, startLineDaemonDetached as Fn, SCREAM_ERROR_INFO as Fr, DISABLE_TERMINAL_FOCUS_REPORTING as Ft, AgentGroupComponent as G, parseAesKey as Gn, TERMINAL_FOCUS_OUT as Gt, lerpGradient as H, DEFAULT_CDN_BASE_URL as Hn, OSC11_RESPONSE_PREFIX_NO_ESC as Ht, AssistantMessageComponent as I, stopLineDaemon as In, DISABLE_TERMINAL_THEME_REPORTING as It, getBreathingFrame as J, TuiConfigParseError as Jn, safeUsageRatio as Jt, WelcomeComponent as K, replaceTabs as Kn, TERMINAL_THEME_DARK as Kt, ActivityGroupComponent as L, UPLOAD_MEDIA_TYPE as Ln, ENABLE_TERMINAL_FOCUS_REPORTING as Lt, ThinkingComponent as M, PIXEL_PULSE_FRAMES as Mn, resolveGlobalLogPath as Mr, getColorPalette as Mt, NoticeMessageComponent as N, PULSE_WAVE_FRAMES as Nn, isScreamError as Nr, detectTerminalTheme as Nt, renderDiffLinesClustered as O, embeddingFailureDetail as On, isRealUserPrompt as Or, isTurnElapsedEnabled as Ot, StatusMessageComponent as P, readLineDaemonStatus as Pn, isOrphanedToolCallError as Pr, parseOsc11BackgroundTheme as Pt, handleExportDebugZipCommand as Q, feishuFileType as Qn, handleConnectCommand as Qt, charsForTokenBudget as R, WeixinApiError as Rn, ENABLE_TERMINAL_THEME_REPORTING as Rt, readJsonlFile as S, buildSkillSlashCommands as Sn, DEFAULT_CATALOG_URL as Sr, showStatusReport as St, UserMessageComponent as T, isExperimentalFlagEnabled as Tn, ScreamHarness as Tr, createEditorTheme as Tt, lerpHex as U, buildCdnDownloadUrl as Un, QUERY_TERMINAL_THEME as Ut, GRADIENT_CYCLE_MS as V, validateWeixinToken as Vn, OSC11_RESPONSE_PREFIX as Vt, BackgroundAgentStatusComponent as W, decryptAesEcb as Wn, TERMINAL_FOCUS_IN as Wt, CompactionComponent as X, FeishuClient as Xn, handleTraceCommand as Xt, resetBreathingClock as Y, loadTuiConfig as Yn, SubagentSlots as Yt, clearGoalState as Z, FeishuCredentialError as Zn, handleSearchCommand as Zt, handleUpdateCommand as _, TIP_ROTATION_INTERVAL_MS as _n, detectInstallSource as _r, showPermissionPicker as _t, writeTextClipboard as a, appendStreamingArgsPreview as an, getWeixinContextTokensFile as ar, getModelCycleLevel as at, readUpdateCache as b, getLlmNotSetMessage as bn, CLI_USER_AGENT_PRODUCT as br, supportsBalance as bt, handleSkillCommand as c, isTodoItemShape as cn, loadLineConfig as cr, handleCompactCommand as ct, isExpandable as d, stringValue as dn, resolveTurnTimeoutMs as dr, handleModelCommand as dt, isBusy as en, LineConfigError as er, handleForkCommand as et, isPlanExpandable as f, truncateErrorMessage as fn, resolveWeixinOptions as fr, handlePlanCommand as ft, handleMcpCommand as g, SESSION_TIPS as gn, getLogDir as gr, showModelPicker as gt, handleMemoryCommand as h, MAIN_AGENT_ID$1 as hn, getInputHistoryFile as hr, handleYoloCommand as ht, openUrl as i, appendSessionHeaderHint as in, getLinePidFile as ir, changeThinkingLevel as it, CachedContainer as j, startManualEmbeddingDownload as jn, log as jr, contrastTextHex as jt, highlightLines as k, getKnowledgeStore as kn, resolveScreamHome as kr, toggleEmptySessionHint as kt, disposeChildren as l, parseStreamingArgs as ln, resolveCustomCommands as lr, handleEditorCommand as lt, formatMemoryMemoForInjection as m, EXIT_CONFIRM_WINDOW_MS as mn, getDataDir as mr, handleWolfpackCommand as mt, clearEvalPanelState as n, printableChar as nn, assertSinglePlatformConfig as nr, handleTitleCommand as nt, syncDiagramCapabilityPrompt as o, argsRecord as on, getWeixinCursorFile as or, handleAutoCommand as ot, MoonLoader as p, EMPTY_SESSION_HINT_URL as pn, detectShellEnvironment as pr, handleThemeCommand as pt, BREATHE_CYCLE_MS as q, sanitizeShellOutput as qn, TERMINAL_THEME_LIGHT as qt, handleGrantsCommand as r, STATUS_BULLET as rn, getLineConfigPath as rr, toTerminalHyperlink as rt, handleExtensionCommand as s, formatErrorMessage as sn, getWeixinStateDir as sr, handleBotCommand as st, dispatchInput as t, isStreaming as tn, MINUTE_MS as tr, handleInitCommand as tt, hasDispose as u, serializeToolResultOutput as un, resolveFeishuOptions as ur, handleFusionPlanCommand as ut, selectUpdateTarget as v, getCtrlCHint as vn, CLI_COMMAND_NAME as vr, showSettingsSelector as vt, handleRevokeCommand as w, sortSlashCommands as wn, saveCatalogCache as wr, resolveThemeSync as wt, appendJsonlLine as x, getNoActiveSessionMessage as xn, PRODUCT_NAME as xr, clearInfoPanelState as xt, refreshUpdateCache as y, getCtrlDHint as yn, CLI_UI_MODE as yr, refreshProviderBalance as yt, estimateTokens as z, WeixinClient as zn, OSC11_QUERY as zt };
158204
+ export { handleExportMdCommand as $, validateFeishuCredentials as $n, handleLogoutCommand as $t, langFromPath as A, hasEmbeddingModelCache as An, MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE as Ar, createThemeStyles as At, getSharedSpeedTracker as B, isSendThrottled as Bn, OSC11_RESPONSE as Bt, handleLineCommand as C, BUILTIN_SLASH_COMMANDS as Cn, fetchCatalogWithMeta as Cr, showUsage as Ct, renderDiffLines as D, embeddingProgressLabel as Dn, saveCatalogCacheMeta as Dr, isEmptySessionHintDismissed as Dt, ToolCallComponent as E, setExperimentalFlags as En, saveCatalogCache as Er, createMarkdownTheme as Et, SkillActivationComponent as F, startLineDaemonDetached as Fn, resolveGlobalLogPath as Fr, DISABLE_TERMINAL_FOCUS_REPORTING as Ft, AgentGroupComponent as G, parseAesKey as Gn, TERMINAL_FOCUS_OUT as Gt, lerpGradient as H, DEFAULT_CDN_BASE_URL as Hn, OSC11_RESPONSE_PREFIX_NO_ESC as Ht, AssistantMessageComponent as I, stopLineDaemon as In, isScreamError as Ir, DISABLE_TERMINAL_THEME_REPORTING as It, getBreathingFrame as J, TuiConfigParseError as Jn, safeUsageRatio as Jt, WelcomeComponent as K, replaceTabs as Kn, TERMINAL_THEME_DARK as Kt, ActivityGroupComponent as L, UPLOAD_MEDIA_TYPE as Ln, isOrphanedToolCallError as Lr, ENABLE_TERMINAL_FOCUS_REPORTING as Lt, ThinkingComponent as M, PIXEL_PULSE_FRAMES as Mn, resolveScreamHome as Mr, getColorPalette as Mt, NoticeMessageComponent as N, PULSE_WAVE_FRAMES as Nn, flushDiagnosticLogs as Nr, detectTerminalTheme as Nt, renderDiffLinesClustered as O, embeddingFailureDetail as On, ScreamHarness as Or, isTurnElapsedEnabled as Ot, StatusMessageComponent as P, readLineDaemonStatus as Pn, log as Pr, parseOsc11BackgroundTheme as Pt, handleExportDebugZipCommand as Q, feishuFileType as Qn, handleConnectCommand as Qt, charsForTokenBudget as R, WeixinApiError as Rn, SCREAM_ERROR_INFO as Rr, ENABLE_TERMINAL_THEME_REPORTING as Rt, readJsonlFile as S, buildSkillSlashCommands as Sn, DEFAULT_CATALOG_URL as Sr, showStatusReport as St, UserMessageComponent as T, isExperimentalFlagEnabled as Tn, loadCatalogCacheMeta as Tr, createEditorTheme as Tt, lerpHex as U, buildCdnDownloadUrl as Un, QUERY_TERMINAL_THEME as Ut, GRADIENT_CYCLE_MS as V, validateWeixinToken as Vn, OSC11_RESPONSE_PREFIX as Vt, BackgroundAgentStatusComponent as W, decryptAesEcb as Wn, TERMINAL_FOCUS_IN as Wt, CompactionComponent as X, FeishuClient as Xn, handleTraceCommand as Xt, resetBreathingClock as Y, loadTuiConfig as Yn, SubagentSlots as Yt, clearGoalState as Z, FeishuCredentialError as Zn, handleSearchCommand as Zt, handleUpdateCommand as _, TIP_ROTATION_INTERVAL_MS as _n, detectInstallSource as _r, showPermissionPicker as _t, writeTextClipboard as a, appendStreamingArgsPreview as an, getWeixinContextTokensFile as ar, getModelCycleLevel as at, readUpdateCache as b, getLlmNotSetMessage as bn, CLI_USER_AGENT_PRODUCT as br, supportsBalance as bt, handleSkillCommand as c, isTodoItemShape as cn, loadLineConfig as cr, handleCompactCommand as ct, isExpandable as d, stringValue as dn, resolveTurnTimeoutMs as dr, handleModelCommand as dt, isBusy as en, LineConfigError as er, handleForkCommand as et, isPlanExpandable as f, truncateErrorMessage as fn, resolveWeixinOptions as fr, handlePlanCommand as ft, handleMcpCommand as g, SESSION_TIPS as gn, getLogDir as gr, showModelPicker as gt, handleMemoryCommand as h, MAIN_AGENT_ID$1 as hn, getInputHistoryFile as hr, handleYoloCommand as ht, openUrl as i, appendSessionHeaderHint as in, getLinePidFile as ir, changeThinkingLevel as it, CachedContainer as j, startManualEmbeddingDownload as jn, isRealUserPrompt as jr, contrastTextHex as jt, highlightLines as k, getKnowledgeStore as kn, normalizeWorkDir as kr, toggleEmptySessionHint as kt, disposeChildren as l, parseStreamingArgs as ln, resolveCustomCommands as lr, handleEditorCommand as lt, formatMemoryMemoForInjection as m, EXIT_CONFIRM_WINDOW_MS as mn, getDataDir as mr, handleWolfpackCommand as mt, clearEvalPanelState as n, printableChar as nn, assertSinglePlatformConfig as nr, handleTitleCommand as nt, syncDiagramCapabilityPrompt as o, argsRecord as on, getWeixinCursorFile as or, handleAutoCommand as ot, MoonLoader as p, EMPTY_SESSION_HINT_URL as pn, detectShellEnvironment as pr, handleThemeCommand as pt, BREATHE_CYCLE_MS as q, sanitizeShellOutput as qn, TERMINAL_THEME_LIGHT as qt, handleGrantsCommand as r, STATUS_BULLET as rn, getLineConfigPath as rr, toTerminalHyperlink as rt, handleExtensionCommand as s, formatErrorMessage as sn, getWeixinStateDir as sr, handleBotCommand as st, dispatchInput as t, isStreaming as tn, MINUTE_MS as tr, handleInitCommand as tt, hasDispose as u, serializeToolResultOutput as un, resolveFeishuOptions as ur, handleFusionPlanCommand as ut, selectUpdateTarget as v, getCtrlCHint as vn, CLI_COMMAND_NAME as vr, showSettingsSelector as vt, handleRevokeCommand as w, sortSlashCommands as wn, loadCatalogCache as wr, resolveThemeSync as wt, appendJsonlLine as x, getNoActiveSessionMessage as xn, PRODUCT_NAME as xr, clearInfoPanelState as xt, refreshUpdateCache as y, getCtrlDHint as yn, CLI_UI_MODE as yr, refreshProviderBalance as yt, estimateTokens as z, WeixinClient as zn, OSC11_QUERY as zt };