scream-code 0.10.1 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,25 +3,27 @@ import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
3
3
  import { dirname as __cjsShimDirname } from 'node:path';
4
4
  const __filename = __cjsShimFileURLToPath(import.meta.url);
5
5
  const __dirname = __cjsShimDirname(__filename);
6
- import { a as __toESM, i as __require, r as __exportAll, t as __commonJSMin } from "./chunk-apG1qJts.mjs";
6
+ import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } from "./chunk-D90kvbyJ.mjs";
7
7
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
8
8
  import { C as join$1, D as resolve$1, E as relative$1, S as isAbsolute$1, T as parse$7, a as isSupportedFile, b as basename$1, i as ingestFile, r as ingestDirectory, t as multiSearch, w as normalize, x as dirname$2, y as KnowledgeStore } from "./src-oeUnRY3N.mjs";
9
- import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-D_xK0DJH.mjs";
9
+ import { t as require_base64_js } from "./base64-js-DzVmk6Nb.mjs";
10
+ import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-Cj7OClhs.mjs";
10
11
  import { createRequire } from "node:module";
11
12
  import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
13
+ import * as fs$1 from "node:fs/promises";
12
14
  import Jn, { access, appendFile, chmod, copyFile, cp, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
13
15
  import I, { createWriteStream } from "fs";
14
16
  import Vr, { EventEmitter } from "events";
15
17
  import * as path$1$1 from "path";
16
18
  import Xs, { dirname, parse } from "path";
17
19
  import { Buffer as Buffer$1 } from "buffer";
18
- import * as fs$10 from "fs/promises";
20
+ import * as fs$11 from "fs/promises";
19
21
  import { writeFile as writeFile$1 } from "fs/promises";
20
22
  import Ds, { PassThrough, Readable } from "node:stream";
21
23
  import { finished, pipeline as pipeline$1 } from "node:stream/promises";
22
24
  import * as vs from "zlib";
23
25
  import Qr from "zlib";
24
- import * as fs$9 from "node:fs";
26
+ import * as fs$10 from "node:fs";
25
27
  import Vt, { appendFileSync, chmodSync, closeSync, constants, createReadStream, createWriteStream as createWriteStream$1, existsSync, fsyncSync, mkdirSync, openSync, promises, readFileSync, readSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
26
28
  import * as path$8 from "node:path";
27
29
  import path, { basename, dirname as dirname$1, extname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
@@ -684,6 +686,38 @@ var APIProviderRateLimitError = class extends APIStatusError {
684
686
  }
685
687
  };
686
688
  /**
689
+ * HTTP status error meaning the conversation history sent to the provider
690
+ * has orphaned tool calls (an assistant `tool_calls` block whose matching
691
+ * tool results are missing). Recoverable only by rebuilding the history —
692
+ * retrying the same request always fails. Carried as a distinct class so
693
+ * callers can reset the session on THIS kind alone instead of sniffing
694
+ * error message text.
695
+ */
696
+ var APIOrphanedToolCallError = class extends APIStatusError {
697
+ constructor(statusCode, message, requestId) {
698
+ super(statusCode, message, requestId);
699
+ this.name = "APIOrphanedToolCallError";
700
+ }
701
+ };
702
+ /**
703
+ * Message-text parity check shared by normalizeAPIStatusError and
704
+ * isOrphanedToolCallError. Mirrors the historical two-includes semantics:
705
+ * the fragments may appear in any order.
706
+ */
707
+ function isOrphanedToolCallMessage(lowerMessage) {
708
+ return lowerMessage.includes("insufficient tool messages") || lowerMessage.includes("tool_calls") && lowerMessage.includes("followed by tool messages");
709
+ }
710
+ /**
711
+ * True when an error signals orphaned tool calls in the request history.
712
+ * instanceof is checked first (provider threw the typed error); the message
713
+ * patterns are the fallback for errors that crossed wrapping boundaries
714
+ * (session/RPC layers re-wrapping the provider error).
715
+ */
716
+ function isOrphanedToolCallError(error) {
717
+ if (error instanceof APIOrphanedToolCallError) return true;
718
+ return isOrphanedToolCallMessage(errorMessage$5(error).toLowerCase());
719
+ }
720
+ /**
687
721
  * The API returned an empty response (no content, no tool calls).
688
722
  */
689
723
  var APIEmptyResponseError = class extends ChatProviderError {
@@ -720,6 +754,7 @@ function isContextOverflowErrorCode(code) {
720
754
  function normalizeAPIStatusError(statusCode, message, requestId) {
721
755
  if (statusCode === 429) return new APIProviderRateLimitError(message, requestId, parseRateLimitReason(message));
722
756
  if (isContextOverflowStatusError(statusCode, message)) return new APIContextOverflowError(statusCode, message, requestId);
757
+ if (statusCode === 400 && isOrphanedToolCallMessage(message.toLowerCase())) return new APIOrphanedToolCallError(statusCode, message, requestId);
723
758
  return new APIStatusError(statusCode, message, requestId);
724
759
  }
725
760
  function isContextOverflowStatusError(statusCode, message) {
@@ -727,6 +762,9 @@ function isContextOverflowStatusError(statusCode, message) {
727
762
  const lowerMessage = message.toLowerCase();
728
763
  return CONTEXT_OVERFLOW_MESSAGE_PATTERNS.some((pattern) => pattern.test(lowerMessage));
729
764
  }
765
+ function errorMessage$5(error) {
766
+ return error instanceof Error ? error.message : String(error);
767
+ }
730
768
  //#endregion
731
769
  //#region ../../packages/ltod/src/providers/tool-call-id.ts
732
770
  const EMPTY_TOOL_CALL_ID = "tool_call";
@@ -10293,12 +10331,12 @@ var require_gaxios = /* @__PURE__ */ __commonJSMin(((exports) => {
10293
10331
  * @returns A proxy agent
10294
10332
  */
10295
10333
  static async #getProxyAgent() {
10296
- this.#proxyAgent ||= (await import("./dist-0bMQWc-B.mjs").then((m) => /* @__PURE__ */ __toESM(m.default))).HttpsProxyAgent;
10334
+ this.#proxyAgent ||= (await import("./dist-AcjRoBBn.mjs").then((m) => /* @__PURE__ */ __toESM(m.default))).HttpsProxyAgent;
10297
10335
  return this.#proxyAgent;
10298
10336
  }
10299
10337
  static async #getFetch() {
10300
10338
  const hasWindow = typeof window !== "undefined" && !!window;
10301
- this.#fetch ||= hasWindow ? window.fetch : (await import("./src-Dae4j3bv.mjs")).default;
10339
+ this.#fetch ||= hasWindow ? window.fetch : (await import("./src-BMbOMRuY.mjs")).default;
10302
10340
  return this.#fetch;
10303
10341
  }
10304
10342
  /**
@@ -12752,94 +12790,6 @@ var require_src$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
12752
12790
  __exportStar(require_gcp_residency(), exports);
12753
12791
  }));
12754
12792
  //#endregion
12755
- //#region ../../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
12756
- var require_base64_js = /* @__PURE__ */ __commonJSMin(((exports) => {
12757
- exports.byteLength = byteLength;
12758
- exports.toByteArray = toByteArray;
12759
- exports.fromByteArray = fromByteArray;
12760
- var lookup = [];
12761
- var revLookup = [];
12762
- var Arr = typeof Uint8Array !== "undefined" ? Uint8Array : Array;
12763
- var code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
12764
- for (var i = 0, len = code.length; i < len; ++i) {
12765
- lookup[i] = code[i];
12766
- revLookup[code.charCodeAt(i)] = i;
12767
- }
12768
- revLookup["-".charCodeAt(0)] = 62;
12769
- revLookup["_".charCodeAt(0)] = 63;
12770
- function getLens(b64) {
12771
- var len = b64.length;
12772
- if (len % 4 > 0) throw new Error("Invalid string. Length must be a multiple of 4");
12773
- var validLen = b64.indexOf("=");
12774
- if (validLen === -1) validLen = len;
12775
- var placeHoldersLen = validLen === len ? 0 : 4 - validLen % 4;
12776
- return [validLen, placeHoldersLen];
12777
- }
12778
- function byteLength(b64) {
12779
- var lens = getLens(b64);
12780
- var validLen = lens[0];
12781
- var placeHoldersLen = lens[1];
12782
- return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen;
12783
- }
12784
- function _byteLength(b64, validLen, placeHoldersLen) {
12785
- return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen;
12786
- }
12787
- function toByteArray(b64) {
12788
- var tmp;
12789
- var lens = getLens(b64);
12790
- var validLen = lens[0];
12791
- var placeHoldersLen = lens[1];
12792
- var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen));
12793
- var curByte = 0;
12794
- var len = placeHoldersLen > 0 ? validLen - 4 : validLen;
12795
- var i;
12796
- for (i = 0; i < len; i += 4) {
12797
- tmp = revLookup[b64.charCodeAt(i)] << 18 | revLookup[b64.charCodeAt(i + 1)] << 12 | revLookup[b64.charCodeAt(i + 2)] << 6 | revLookup[b64.charCodeAt(i + 3)];
12798
- arr[curByte++] = tmp >> 16 & 255;
12799
- arr[curByte++] = tmp >> 8 & 255;
12800
- arr[curByte++] = tmp & 255;
12801
- }
12802
- if (placeHoldersLen === 2) {
12803
- tmp = revLookup[b64.charCodeAt(i)] << 2 | revLookup[b64.charCodeAt(i + 1)] >> 4;
12804
- arr[curByte++] = tmp & 255;
12805
- }
12806
- if (placeHoldersLen === 1) {
12807
- tmp = revLookup[b64.charCodeAt(i)] << 10 | revLookup[b64.charCodeAt(i + 1)] << 4 | revLookup[b64.charCodeAt(i + 2)] >> 2;
12808
- arr[curByte++] = tmp >> 8 & 255;
12809
- arr[curByte++] = tmp & 255;
12810
- }
12811
- return arr;
12812
- }
12813
- function tripletToBase64(num) {
12814
- return lookup[num >> 18 & 63] + lookup[num >> 12 & 63] + lookup[num >> 6 & 63] + lookup[num & 63];
12815
- }
12816
- function encodeChunk(uint8, start, end) {
12817
- var tmp;
12818
- var output = [];
12819
- for (var i = start; i < end; i += 3) {
12820
- tmp = (uint8[i] << 16 & 16711680) + (uint8[i + 1] << 8 & 65280) + (uint8[i + 2] & 255);
12821
- output.push(tripletToBase64(tmp));
12822
- }
12823
- return output.join("");
12824
- }
12825
- function fromByteArray(uint8) {
12826
- var tmp;
12827
- var len = uint8.length;
12828
- var extraBytes = len % 3;
12829
- var parts = [];
12830
- var maxChunkLength = 16383;
12831
- for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) parts.push(encodeChunk(uint8, i, i + maxChunkLength > len2 ? len2 : i + maxChunkLength));
12832
- if (extraBytes === 1) {
12833
- tmp = uint8[len - 1];
12834
- parts.push(lookup[tmp >> 2] + lookup[tmp << 4 & 63] + "==");
12835
- } else if (extraBytes === 2) {
12836
- tmp = (uint8[len - 2] << 8) + uint8[len - 1];
12837
- parts.push(lookup[tmp >> 10] + lookup[tmp >> 4 & 63] + lookup[tmp << 2 & 63] + "=");
12838
- }
12839
- return parts.join("");
12840
- }
12841
- }));
12842
- //#endregion
12843
12793
  //#region ../../node_modules/.pnpm/google-auth-library@10.6.2/node_modules/google-auth-library/build/src/crypto/shared.js
12844
12794
  var require_shared$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
12845
12795
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -13207,7 +13157,7 @@ var require_util$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
13207
13157
  exports.removeUndefinedValuesInObject = removeUndefinedValuesInObject;
13208
13158
  exports.isValidFile = isValidFile;
13209
13159
  exports.getWellKnownCertificateConfigFileLocation = getWellKnownCertificateConfigFileLocation;
13210
- const fs$16 = __require("fs");
13160
+ const fs$17 = __require("fs");
13211
13161
  const os$2 = __require("os");
13212
13162
  const path$13 = __require("path");
13213
13163
  const WELL_KNOWN_CERTIFICATE_CONFIG_FILE = "certificate_config.json";
@@ -13327,7 +13277,7 @@ var require_util$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
13327
13277
  */
13328
13278
  async function isValidFile(filePath) {
13329
13279
  try {
13330
- return (await fs$16.promises.lstat(filePath)).isFile();
13280
+ return (await fs$17.promises.lstat(filePath)).isFile();
13331
13281
  } catch (e) {
13332
13282
  return false;
13333
13283
  }
@@ -15041,10 +14991,10 @@ var require_getCredentials = /* @__PURE__ */ __commonJSMin(((exports) => {
15041
14991
  Object.defineProperty(exports, "__esModule", { value: true });
15042
14992
  exports.getCredentials = getCredentials;
15043
14993
  const path$12 = __require("path");
15044
- const fs$15 = __require("fs");
14994
+ const fs$16 = __require("fs");
15045
14995
  const util_1$1 = __require("util");
15046
14996
  const errorWithCode_1 = require_errorWithCode();
15047
- const readFile = fs$15.readFile ? (0, util_1$1.promisify)(fs$15.readFile) : async () => {
14997
+ const readFile = fs$16.readFile ? (0, util_1$1.promisify)(fs$16.readFile) : async () => {
15048
14998
  throw new errorWithCode_1.ErrorWithCode("use key rather than keyFile.", "MISSING_CREDENTIALS");
15049
14999
  };
15050
15000
  var ExtensionFiles;
@@ -16592,10 +16542,10 @@ var require_filesubjecttokensupplier = /* @__PURE__ */ __commonJSMin(((exports)
16592
16542
  Object.defineProperty(exports, "__esModule", { value: true });
16593
16543
  exports.FileSubjectTokenSupplier = void 0;
16594
16544
  const util_1 = __require("util");
16595
- const fs$14 = __require("fs");
16596
- const readFile = (0, util_1.promisify)(fs$14.readFile ?? (() => {}));
16597
- const realpath = (0, util_1.promisify)(fs$14.realpath ?? (() => {}));
16598
- const lstat = (0, util_1.promisify)(fs$14.lstat ?? (() => {}));
16545
+ const fs$15 = __require("fs");
16546
+ const readFile = (0, util_1.promisify)(fs$15.readFile ?? (() => {}));
16547
+ const realpath = (0, util_1.promisify)(fs$15.realpath ?? (() => {}));
16548
+ const lstat = (0, util_1.promisify)(fs$15.lstat ?? (() => {}));
16599
16549
  /**
16600
16550
  * Internal subject token supplier implementation used when a file location
16601
16551
  * is configured in the credential configuration used to build an {@link IdentityPoolClient}
@@ -16697,7 +16647,7 @@ var require_certificatesubjecttokensupplier = /* @__PURE__ */ __commonJSMin(((ex
16697
16647
  Object.defineProperty(exports, "__esModule", { value: true });
16698
16648
  exports.CertificateSubjectTokenSupplier = exports.InvalidConfigurationError = exports.CertificateSourceUnavailableError = exports.CERTIFICATE_CONFIGURATION_ENV_VARIABLE = void 0;
16699
16649
  const util_1 = require_util$1();
16700
- const fs$13 = __require("fs");
16650
+ const fs$14 = __require("fs");
16701
16651
  const crypto_1 = __require("crypto");
16702
16652
  const https$1 = __require("https");
16703
16653
  exports.CERTIFICATE_CONFIGURATION_ENV_VARIABLE = "GOOGLE_API_CERTIFICATE_CONFIG";
@@ -16792,7 +16742,7 @@ var require_certificatesubjecttokensupplier = /* @__PURE__ */ __commonJSMin(((ex
16792
16742
  const configPath = this.certificateConfigPath;
16793
16743
  let fileContents;
16794
16744
  try {
16795
- fileContents = await fs$13.promises.readFile(configPath, "utf8");
16745
+ fileContents = await fs$14.promises.readFile(configPath, "utf8");
16796
16746
  } catch (err) {
16797
16747
  throw new CertificateSourceUnavailableError(`Failed to read certificate config file at: ${configPath}`);
16798
16748
  }
@@ -16817,13 +16767,13 @@ var require_certificatesubjecttokensupplier = /* @__PURE__ */ __commonJSMin(((ex
16817
16767
  async #getKeyAndCert(certPath, keyPath) {
16818
16768
  let cert, key;
16819
16769
  try {
16820
- cert = await fs$13.promises.readFile(certPath);
16770
+ cert = await fs$14.promises.readFile(certPath);
16821
16771
  new crypto_1.X509Certificate(cert);
16822
16772
  } catch (err) {
16823
16773
  throw new CertificateSourceUnavailableError(`Failed to read certificate file at ${certPath}: ${err instanceof Error ? err.message : String(err)}`);
16824
16774
  }
16825
16775
  try {
16826
- key = await fs$13.promises.readFile(keyPath);
16776
+ key = await fs$14.promises.readFile(keyPath);
16827
16777
  (0, crypto_1.createPrivateKey)(key);
16828
16778
  } catch (err) {
16829
16779
  throw new CertificateSourceUnavailableError(`Failed to read private key file at ${keyPath}: ${err instanceof Error ? err.message : String(err)}`);
@@ -16842,7 +16792,7 @@ var require_certificatesubjecttokensupplier = /* @__PURE__ */ __commonJSMin(((ex
16842
16792
  const leafCert = new crypto_1.X509Certificate(leafCertBuffer);
16843
16793
  if (!this.trustChainPath) return JSON.stringify([leafCert.raw.toString("base64")]);
16844
16794
  try {
16845
- const chainCerts = ((await fs$13.promises.readFile(this.trustChainPath, "utf8")).match(/-----BEGIN CERTIFICATE-----[^-]+-----END CERTIFICATE-----/g) ?? []).map((pem, index) => {
16795
+ const chainCerts = ((await fs$14.promises.readFile(this.trustChainPath, "utf8")).match(/-----BEGIN CERTIFICATE-----[^-]+-----END CERTIFICATE-----/g) ?? []).map((pem, index) => {
16846
16796
  try {
16847
16797
  return new crypto_1.X509Certificate(pem);
16848
16798
  } catch (err) {
@@ -17503,7 +17453,7 @@ var require_pluggable_auth_handler = /* @__PURE__ */ __commonJSMin(((exports) =>
17503
17453
  exports.PluggableAuthHandler = exports.ExecutableError = void 0;
17504
17454
  const executable_response_1 = require_executable_response();
17505
17455
  const childProcess = __require("child_process");
17506
- const fs$12 = __require("fs");
17456
+ const fs$13 = __require("fs");
17507
17457
  /**
17508
17458
  * Error thrown from the executable run by PluggableAuthClient.
17509
17459
  */
@@ -17580,12 +17530,12 @@ var require_pluggable_auth_handler = /* @__PURE__ */ __commonJSMin(((exports) =>
17580
17530
  if (!this.outputFile || this.outputFile.length === 0) return;
17581
17531
  let filePath;
17582
17532
  try {
17583
- filePath = await fs$12.promises.realpath(this.outputFile);
17533
+ filePath = await fs$13.promises.realpath(this.outputFile);
17584
17534
  } catch {
17585
17535
  return;
17586
17536
  }
17587
- if (!(await fs$12.promises.lstat(filePath)).isFile()) return;
17588
- const responseString = await fs$12.promises.readFile(filePath, { encoding: "utf8" });
17537
+ if (!(await fs$13.promises.lstat(filePath)).isFile()) return;
17538
+ const responseString = await fs$13.promises.readFile(filePath, { encoding: "utf8" });
17589
17539
  if (responseString === "") return;
17590
17540
  try {
17591
17541
  const responseJson = JSON.parse(responseString);
@@ -18007,7 +17957,7 @@ var require_googleauth = /* @__PURE__ */ __commonJSMin(((exports) => {
18007
17957
  Object.defineProperty(exports, "__esModule", { value: true });
18008
17958
  exports.GoogleAuth = exports.GoogleAuthExceptionMessages = void 0;
18009
17959
  const child_process_1 = __require("child_process");
18010
- const fs$11 = __require("fs");
17960
+ const fs$12 = __require("fs");
18011
17961
  const gaxios_1 = require_src$3();
18012
17962
  const gcpMetadata = require_src$1();
18013
17963
  const os$1 = __require("os");
@@ -18255,7 +18205,7 @@ var require_googleauth = /* @__PURE__ */ __commonJSMin(((exports) => {
18255
18205
  }
18256
18206
  if (location) {
18257
18207
  location = path$11.join(location, "gcloud", "application_default_credentials.json");
18258
- if (!fs$11.existsSync(location)) location = null;
18208
+ if (!fs$12.existsSync(location)) location = null;
18259
18209
  }
18260
18210
  if (!location) return null;
18261
18211
  return await this._getApplicationCredentialsFromFilePath(location, options);
@@ -18269,13 +18219,13 @@ var require_googleauth = /* @__PURE__ */ __commonJSMin(((exports) => {
18269
18219
  async _getApplicationCredentialsFromFilePath(filePath, options = {}) {
18270
18220
  if (!filePath || filePath.length === 0) throw new Error("The file path is invalid.");
18271
18221
  try {
18272
- filePath = fs$11.realpathSync(filePath);
18273
- if (!fs$11.lstatSync(filePath).isFile()) throw new Error();
18222
+ filePath = fs$12.realpathSync(filePath);
18223
+ if (!fs$12.lstatSync(filePath).isFile()) throw new Error();
18274
18224
  } catch (err) {
18275
18225
  if (err instanceof Error) err.message = `The file at ${filePath} does not exist, or it is not a file. ${err.message}`;
18276
18226
  throw err;
18277
18227
  }
18278
- const readStream = fs$11.createReadStream(filePath);
18228
+ const readStream = fs$12.createReadStream(filePath);
18279
18229
  return this.fromStream(readStream, options);
18280
18230
  }
18281
18231
  /**
@@ -18542,7 +18492,7 @@ var require_googleauth = /* @__PURE__ */ __commonJSMin(((exports) => {
18542
18492
  if (this.jsonContent) return this._cacheClientFromJSON(this.jsonContent, this.clientOptions);
18543
18493
  else if (this.keyFilename) {
18544
18494
  const filePath = path$11.resolve(this.keyFilename);
18545
- const stream = fs$11.createReadStream(filePath);
18495
+ const stream = fs$12.createReadStream(filePath);
18546
18496
  return await this.fromStreamAsync(stream, this.clientOptions);
18547
18497
  } else if (this.apiKey) {
18548
18498
  const client = await this.fromAPIKey(this.apiKey, this.clientOptions);
@@ -37287,7 +37237,7 @@ var NodeUploader = class {
37287
37237
  type: void 0
37288
37238
  };
37289
37239
  if (typeof file === "string") {
37290
- fileStat.size = (await fs$10.stat(file)).size;
37240
+ fileStat.size = (await fs$11.stat(file)).size;
37291
37241
  fileStat.type = this.inferMimeType(file);
37292
37242
  return fileStat;
37293
37243
  } else return await getBlobStat(file);
@@ -37419,7 +37369,7 @@ var NodeUploader = class {
37419
37369
  let fileHandle;
37420
37370
  const fileName = path$1$1.basename(file);
37421
37371
  try {
37422
- fileHandle = await fs$10.open(file, "r");
37372
+ fileHandle = await fs$11.open(file, "r");
37423
37373
  if (!fileHandle) throw new Error(`Failed to open file`);
37424
37374
  fileSize = (await fileHandle.stat()).size;
37425
37375
  while (offset < fileSize) {
@@ -48186,11 +48136,11 @@ var ScreamFiles = class {
48186
48136
  async uploadVideo(input, options) {
48187
48137
  let file;
48188
48138
  if (typeof input === "string") {
48189
- if (!fs$9.existsSync(input)) throw new ChatProviderError(`Video file not found: ${input}`);
48139
+ if (!fs$10.existsSync(input)) throw new ChatProviderError(`Video file not found: ${input}`);
48190
48140
  const filename = path$8.basename(input);
48191
48141
  const mimeType = guessMimeTypeFromExt(filename);
48192
48142
  if (mimeType === void 0 || !mimeType.startsWith("video/")) throw new ChatProviderError(`ScreamFiles.uploadVideo: file extension does not indicate a video type: ${filename}`);
48193
- const data = await fs$9.promises.readFile(input);
48143
+ const data = await fs$10.promises.readFile(input);
48194
48144
  const blob = new Blob([new Uint8Array(data)], { type: mimeType });
48195
48145
  file = new File([blob], filename, { type: mimeType });
48196
48146
  } else {
@@ -49877,7 +49827,7 @@ async function generate(provider, systemPrompt, tools, history, callbacks, optio
49877
49827
  if (options?.signal?.aborted) throwAbortError();
49878
49828
  options?.onRequestStart?.();
49879
49829
  const stream = await provider.generate(systemPrompt, tools, history, options);
49880
- await throwIfAborted(options?.signal, stream);
49830
+ await throwIfAborted$1(options?.signal, stream);
49881
49831
  const idleTimeoutMs = getStreamIdleTimeoutMs();
49882
49832
  const firstItemTimeoutMs = getStreamFirstItemTimeoutMs(idleTimeoutMs);
49883
49833
  const watchedStream = iterateWithIdleTimeout(stream, {
@@ -49888,10 +49838,10 @@ async function generate(provider, systemPrompt, tools, history, callbacks, optio
49888
49838
  firstItemErrorMessage: `Stream stalled — no first token within ${firstItemTimeoutMs ?? 0}ms. Provider: ${provider.name}, model: ${provider.modelName}`
49889
49839
  });
49890
49840
  for await (const part of watchedStream) {
49891
- await throwIfAborted(options?.signal, stream);
49841
+ await throwIfAborted$1(options?.signal, stream);
49892
49842
  if (callbacks?.onMessagePart !== void 0) {
49893
49843
  await callbacks.onMessagePart(deepCopyPart(part));
49894
- await throwIfAborted(options?.signal, stream);
49844
+ await throwIfAborted$1(options?.signal, stream);
49895
49845
  }
49896
49846
  if (isToolCallPart(part) && part.index !== void 0 && !isPendingToolCallAtIndex(pendingPart, part.index)) {
49897
49847
  const arrayIdx = toolCallIndexMap.get(part.index);
@@ -49907,7 +49857,7 @@ async function generate(provider, systemPrompt, tools, history, callbacks, optio
49907
49857
  pendingPart = part;
49908
49858
  }
49909
49859
  }
49910
- await throwIfAborted(options?.signal, stream);
49860
+ await throwIfAborted$1(options?.signal, stream);
49911
49861
  options?.onStreamEnd?.();
49912
49862
  if (pendingPart !== null) flushPart(message, pendingPart, toolCallIndexMap);
49913
49863
  if (message.content.length === 0 && message.toolCalls.length === 0) throw new APIEmptyResponseError(`The API returned an empty response (no content, no tool calls). Provider: ${provider.name}, model: ${provider.modelName}. Common causes: the model's max output tokens are set too low, the model does not support this request format, or the provider/proxy returned an empty stream. Try again, switch models, or check provider settings.`);
@@ -49916,7 +49866,7 @@ async function generate(provider, systemPrompt, tools, history, callbacks, optio
49916
49866
  const hasToolCalls = message.toolCalls.length > 0;
49917
49867
  if (hasThink && !hasText && !hasToolCalls) throw new APIEmptyResponseError(`The API returned a response containing only thinking content without any text or tool calls. This usually indicates the stream was interrupted or the output token budget was exhausted during reasoning. Provider: ${provider.name}, model: ${provider.modelName}. If this persists, reduce reasoning effort or switch to a model with a larger output budget.`);
49918
49868
  if (callbacks?.onToolCall !== void 0) for (const toolCall of message.toolCalls) {
49919
- await throwIfAborted(options?.signal, stream);
49869
+ await throwIfAborted$1(options?.signal, stream);
49920
49870
  await callbacks.onToolCall(toolCall);
49921
49871
  }
49922
49872
  return {
@@ -49933,7 +49883,7 @@ function throwAbortError() {
49933
49883
  async function cancelStream(stream) {
49934
49884
  await stream[Symbol.asyncIterator]().return?.();
49935
49885
  }
49936
- async function throwIfAborted(signal, stream) {
49886
+ async function throwIfAborted$1(signal, stream) {
49937
49887
  if (!signal?.aborted) return;
49938
49888
  if (stream !== void 0) await cancelStream(stream);
49939
49889
  throwAbortError();
@@ -50295,7 +50245,7 @@ async function syncDir(dirPath) {
50295
50245
  */
50296
50246
  function syncFd(fd) {
50297
50247
  return new Promise((resolve, reject) => {
50298
- fs$9.fsync(fd, (err) => {
50248
+ fs$10.fsync(fd, (err) => {
50299
50249
  if (err) {
50300
50250
  reject(err);
50301
50251
  return;
@@ -50903,7 +50853,7 @@ function isAbortError$1(err) {
50903
50853
  if (err instanceof Error) return err.name === "AbortError";
50904
50854
  return false;
50905
50855
  }
50906
- function errorMessage$3(err) {
50856
+ function errorMessage$4(err) {
50907
50857
  if (err instanceof Error) return err.message;
50908
50858
  return String(err);
50909
50859
  }
@@ -62543,8 +62493,8 @@ function parseSkillText(options) {
62543
62493
  if (!isRecord$6(frontmatter)) throw new SkillParseError(`Frontmatter in ${options.skillMdPath} must be a mapping at the top level`);
62544
62494
  const metadata = normalizeMetadata(frontmatter);
62545
62495
  if (!isSupportedSkillType(metadata.type)) throw new UnsupportedSkillTypeError(metadata.type ?? String(frontmatter["type"]));
62546
- const name = nonEmptyString$2(metadata.name);
62547
- const description = nonEmptyString$2(metadata.description);
62496
+ const name = nonEmptyString$1(metadata.name);
62497
+ const description = nonEmptyString$1(metadata.description);
62548
62498
  if (isDirectorySkill && (name === void 0 || description === void 0)) throw new SkillParseError(`Missing required frontmatter field ${name === void 0 ? "\"name\"" : "\"description\""} in ${options.skillMdPath}`);
62549
62499
  const skillPath = resolve$1(options.skillMdPath);
62550
62500
  const content = parsed.body.trim();
@@ -62604,11 +62554,11 @@ function normalizeMetadata(raw) {
62604
62554
  const key = METADATA_ALIASES[rawKey] ?? rawKey;
62605
62555
  out[key] = value;
62606
62556
  }
62607
- const type = nonEmptyString$2(out["type"]);
62557
+ const type = nonEmptyString$1(out["type"]);
62608
62558
  if (type !== void 0) out["type"] = type;
62609
- const name = nonEmptyString$2(out["name"]);
62559
+ const name = nonEmptyString$1(out["name"]);
62610
62560
  if (name !== void 0) out["name"] = name;
62611
- const description = nonEmptyString$2(out["description"]);
62561
+ const description = nonEmptyString$1(out["description"]);
62612
62562
  if (description !== void 0) out["description"] = description;
62613
62563
  return out;
62614
62564
  }
@@ -62650,7 +62600,7 @@ function tokenizeArgs(raw) {
62650
62600
  if (hasContent) out.push(current);
62651
62601
  return out;
62652
62602
  }
62653
- function nonEmptyString$2(value) {
62603
+ function nonEmptyString$1(value) {
62654
62604
  return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
62655
62605
  }
62656
62606
  function isRecord$6(value) {
@@ -66860,7 +66810,7 @@ var require_node_loaders = /* @__PURE__ */ __commonJSMin(((exports, module) => {
66860
66810
  };
66861
66811
  return _setPrototypeOf(o, p);
66862
66812
  }
66863
- var fs$8 = __require("fs");
66813
+ var fs$9 = __require("fs");
66864
66814
  var path$6 = __require("path");
66865
66815
  var Loader = require_loader();
66866
66816
  var PrecompiledLoader = require_precompiled_loader().PrecompiledLoader;
@@ -66884,7 +66834,7 @@ var require_node_loaders = /* @__PURE__ */ __commonJSMin(((exports, module) => {
66884
66834
  } catch (e) {
66885
66835
  throw new Error("watch requires chokidar to be installed");
66886
66836
  }
66887
- var paths = _this.searchPaths.filter(fs$8.existsSync);
66837
+ var paths = _this.searchPaths.filter(fs$9.existsSync);
66888
66838
  var watcher = chokidar.watch(paths);
66889
66839
  watcher.on("all", function(event, fullname) {
66890
66840
  fullname = path$6.resolve(fullname);
@@ -66903,7 +66853,7 @@ var require_node_loaders = /* @__PURE__ */ __commonJSMin(((exports, module) => {
66903
66853
  for (var i = 0; i < paths.length; i++) {
66904
66854
  var basePath = path$6.resolve(paths[i]);
66905
66855
  var p = path$6.resolve(paths[i], name);
66906
- if (p.indexOf(basePath) === 0 && fs$8.existsSync(p)) {
66856
+ if (p.indexOf(basePath) === 0 && fs$9.existsSync(p)) {
66907
66857
  fullpath = p;
66908
66858
  break;
66909
66859
  }
@@ -66911,7 +66861,7 @@ var require_node_loaders = /* @__PURE__ */ __commonJSMin(((exports, module) => {
66911
66861
  if (!fullpath) return null;
66912
66862
  this.pathsToNames[fullpath] = name;
66913
66863
  var source = {
66914
- src: fs$8.readFileSync(fullpath, "utf-8"),
66864
+ src: fs$9.readFileSync(fullpath, "utf-8"),
66915
66865
  path: fullpath,
66916
66866
  noCache: this.noCache
66917
66867
  };
@@ -66959,7 +66909,7 @@ var require_node_loaders = /* @__PURE__ */ __commonJSMin(((exports, module) => {
66959
66909
  }
66960
66910
  this.pathsToNames[fullpath] = name;
66961
66911
  var source = {
66962
- src: fs$8.readFileSync(fullpath, "utf-8"),
66912
+ src: fs$9.readFileSync(fullpath, "utf-8"),
66963
66913
  path: fullpath,
66964
66914
  noCache: this.noCache
66965
66915
  };
@@ -67703,7 +67653,7 @@ var require_precompile_global = /* @__PURE__ */ __commonJSMin(((exports, module)
67703
67653
  //#endregion
67704
67654
  //#region ../../node_modules/.pnpm/nunjucks@3.2.4/node_modules/nunjucks/src/precompile.js
67705
67655
  var require_precompile = /* @__PURE__ */ __commonJSMin(((exports, module) => {
67706
- var fs$7 = __require("fs");
67656
+ var fs$8 = __require("fs");
67707
67657
  var path$4 = __require("path");
67708
67658
  var _prettifyError = require_lib$1()._prettifyError;
67709
67659
  var compiler = require_compiler();
@@ -67728,27 +67678,27 @@ var require_precompile = /* @__PURE__ */ __commonJSMin(((exports, module) => {
67728
67678
  var env = opts.env || new Environment([]);
67729
67679
  var wrapper = opts.wrapper || precompileGlobal;
67730
67680
  if (opts.isString) return precompileString(input, opts);
67731
- var pathStats = fs$7.existsSync(input) && fs$7.statSync(input);
67681
+ var pathStats = fs$8.existsSync(input) && fs$8.statSync(input);
67732
67682
  var precompiled = [];
67733
67683
  var templates = [];
67734
67684
  function addTemplates(dir) {
67735
- fs$7.readdirSync(dir).forEach(function(file) {
67685
+ fs$8.readdirSync(dir).forEach(function(file) {
67736
67686
  var filepath = path$4.join(dir, file);
67737
67687
  var subpath = filepath.substr(path$4.join(input, "/").length);
67738
- var stat = fs$7.statSync(filepath);
67688
+ var stat = fs$8.statSync(filepath);
67739
67689
  if (stat && stat.isDirectory()) {
67740
67690
  subpath += "/";
67741
67691
  if (!match(subpath, opts.exclude)) addTemplates(filepath);
67742
67692
  } else if (match(subpath, opts.include)) templates.push(filepath);
67743
67693
  });
67744
67694
  }
67745
- if (pathStats.isFile()) precompiled.push(_precompile(fs$7.readFileSync(input, "utf-8"), opts.name || input, env));
67695
+ if (pathStats.isFile()) precompiled.push(_precompile(fs$8.readFileSync(input, "utf-8"), opts.name || input, env));
67746
67696
  else if (pathStats.isDirectory()) {
67747
67697
  addTemplates(input);
67748
67698
  for (var i = 0; i < templates.length; i++) {
67749
67699
  var name = templates[i].replace(path$4.join(input, "/"), "");
67750
67700
  try {
67751
- precompiled.push(_precompile(fs$7.readFileSync(templates[i], "utf-8"), name, env));
67701
+ precompiled.push(_precompile(fs$8.readFileSync(templates[i], "utf-8"), name, env));
67752
67702
  } catch (e) {
67753
67703
  if (opts.force) console.error(e);
67754
67704
  else throw e;
@@ -68224,7 +68174,7 @@ var require_pend = /* @__PURE__ */ __commonJSMin(((exports, module) => {
68224
68174
  //#endregion
68225
68175
  //#region ../../node_modules/.pnpm/yauzl@3.3.1/node_modules/yauzl/fd-slicer.js
68226
68176
  var require_fd_slicer = /* @__PURE__ */ __commonJSMin(((exports) => {
68227
- var fs$6 = __require("fs");
68177
+ var fs$7 = __require("fs");
68228
68178
  var util$2 = __require("util");
68229
68179
  var stream = __require("stream");
68230
68180
  var Readable = stream.Readable;
@@ -68244,7 +68194,7 @@ var require_fd_slicer = /* @__PURE__ */ __commonJSMin(((exports) => {
68244
68194
  FdSlicer.prototype.read = function(buffer, offset, length, position, callback) {
68245
68195
  var self = this;
68246
68196
  self.pend.go(function(cb) {
68247
- fs$6.read(self.fd, buffer, offset, length, position, function(err, bytesRead, buffer) {
68197
+ fs$7.read(self.fd, buffer, offset, length, position, function(err, bytesRead, buffer) {
68248
68198
  cb();
68249
68199
  callback(err, bytesRead, buffer);
68250
68200
  });
@@ -68261,7 +68211,7 @@ var require_fd_slicer = /* @__PURE__ */ __commonJSMin(((exports) => {
68261
68211
  self.refCount -= 1;
68262
68212
  if (self.refCount < 0) throw new Error("invalid unref");
68263
68213
  if (self.refCount > 0) return;
68264
- fs$6.close(self.fd, onCloseDone);
68214
+ fs$7.close(self.fd, onCloseDone);
68265
68215
  function onCloseDone(err) {
68266
68216
  if (err) self.emit("error", err);
68267
68217
  else self.emit("close");
@@ -68288,7 +68238,7 @@ var require_fd_slicer = /* @__PURE__ */ __commonJSMin(((exports) => {
68288
68238
  }
68289
68239
  self.context.pend.go(function(cb) {
68290
68240
  var buffer = Buffer.allocUnsafe(toRead);
68291
- fs$6.read(self.context.fd, buffer, 0, toRead, self.pos, function(err, bytesRead) {
68241
+ fs$7.read(self.context.fd, buffer, 0, toRead, self.pos, function(err, bytesRead) {
68292
68242
  if (err) self.destroy(err);
68293
68243
  else if (bytesRead === 0) {
68294
68244
  self.push(null);
@@ -69652,17 +69602,13 @@ const HookDefSchema = z.object({
69652
69602
  command: z.string().min(1),
69653
69603
  timeout: z.number().int().min(1).max(600).optional()
69654
69604
  }).strict();
69655
- const ScreamCliServiceConfigSchema = z.object({
69656
- baseUrl: z.string().optional(),
69657
- apiKey: z.string().optional(),
69658
- oauth: OAuthRefSchema.optional(),
69659
- customHeaders: StringRecordSchema.optional()
69660
- });
69661
69605
  const DuckDuckGoConfigSchema = z.object({ enabled: z.boolean().default(true) });
69606
+ const DomesticSearchConfigSchema = z.object({ enabled: z.boolean().default(true) });
69662
69607
  const ServicesConfigSchema = z.object({
69663
- screamCliSearch: ScreamCliServiceConfigSchema.optional(),
69664
- screamCliFetch: ScreamCliServiceConfigSchema.optional(),
69665
- duckduckgo: DuckDuckGoConfigSchema.optional()
69608
+ duckduckgo: DuckDuckGoConfigSchema.optional(),
69609
+ sogou: DomesticSearchConfigSchema.optional(),
69610
+ so360: DomesticSearchConfigSchema.optional(),
69611
+ baidu: DomesticSearchConfigSchema.optional()
69666
69612
  });
69667
69613
  const McpServerCommonFields = {
69668
69614
  enabled: z.boolean().optional(),
@@ -69736,12 +69682,13 @@ const ThinkingConfigPatchSchema = ThinkingConfigSchema.partial();
69736
69682
  const PermissionConfigPatchSchema = PermissionConfigSchema.partial();
69737
69683
  const LoopControlPatchSchema = LoopControlSchema.partial();
69738
69684
  const BackgroundConfigPatchSchema = BackgroundConfigSchema.partial();
69739
- const ScreamCliServiceConfigPatchSchema = ScreamCliServiceConfigSchema.partial();
69740
69685
  const DuckDuckGoConfigPatchSchema = DuckDuckGoConfigSchema.partial();
69686
+ const DomesticSearchConfigPatchSchema = DomesticSearchConfigSchema.partial();
69741
69687
  const ServicesConfigPatchSchema = z.object({
69742
- screamCliSearch: ScreamCliServiceConfigPatchSchema.optional(),
69743
- screamCliFetch: ScreamCliServiceConfigPatchSchema.optional(),
69744
- duckduckgo: DuckDuckGoConfigPatchSchema.optional()
69688
+ duckduckgo: DuckDuckGoConfigPatchSchema.optional(),
69689
+ sogou: DomesticSearchConfigPatchSchema.optional(),
69690
+ so360: DomesticSearchConfigPatchSchema.optional(),
69691
+ baidu: DomesticSearchConfigPatchSchema.optional()
69745
69692
  });
69746
69693
  const ScreamConfigPatchSchema = z.object({
69747
69694
  providers: z.record(z.string(), ProviderConfigPatchSchema).optional(),
@@ -74223,7 +74170,7 @@ var mi = class {
74223
74170
  K(Hn, Wn, Gn, Zn, (s, t) => {
74224
74171
  if (!t?.length) throw new TypeError("no paths specified to add to archive");
74225
74172
  });
74226
- var dr = (process.env.__FAKE_PLATFORM__ || process.platform) === "win32", { O_CREAT: ur, O_NOFOLLOW: lr, O_TRUNC: mr, O_WRONLY: pr } = I.constants, Er = Number(process.env.__FAKE_FS_O_FILENAME__) || I.constants.UV_FS_O_FILEMAP || 0, Vn = dr && !!Er, $n = 512 * 1024, Xn = Er | mr | ur | pr, cr = !dr && typeof lr == "number" ? lr | mr | ur | pr : null, fs$4 = cr !== null ? () => cr : Vn ? (s) => s < $n ? Xn : "w" : () => "w";
74173
+ var dr = (process.env.__FAKE_PLATFORM__ || process.platform) === "win32", { O_CREAT: ur, O_NOFOLLOW: lr, O_TRUNC: mr, O_WRONLY: pr } = I.constants, Er = Number(process.env.__FAKE_FS_O_FILENAME__) || I.constants.UV_FS_O_FILEMAP || 0, Vn = dr && !!Er, $n = 512 * 1024, Xn = Er | mr | ur | pr, cr = !dr && typeof lr == "number" ? lr | mr | ur | pr : null, fs$5 = cr !== null ? () => cr : Vn ? (s) => s < $n ? Xn : "w" : () => "w";
74227
74174
  var ds = (s, t, e) => {
74228
74175
  try {
74229
74176
  return Vt.lchownSync(s, t, e);
@@ -74591,7 +74538,7 @@ var Or = Symbol("onEntry"), Rs = Symbol("checkFs"), Tr = Symbol("checkFs2"), gs
74591
74538
  }
74592
74539
  [bs](t, e) {
74593
74540
  let i = typeof t.mode == "number" ? t.mode & 4095 : this.fmode, r = new tt(String(t.absolute), {
74594
- flags: fs$4(t.size),
74541
+ flags: fs$5(t.size),
74595
74542
  mode: i,
74596
74543
  autoClose: !1
74597
74544
  });
@@ -74795,7 +74742,7 @@ var Or = Symbol("onEntry"), Rs = Symbol("checkFs"), Tr = Symbol("checkFs2"), gs
74795
74742
  (h || a) && this[O](h || a, t), e();
74796
74743
  }, n;
74797
74744
  try {
74798
- n = Vt.openSync(String(t.absolute), fs$4(t.size), i);
74745
+ n = Vt.openSync(String(t.absolute), fs$5(t.size), i);
74799
74746
  } catch (h) {
74800
74747
  return r(h);
74801
74748
  }
@@ -76163,7 +76110,7 @@ const FTYP_VIDEO_BRANDS = Object.freeze({
76163
76110
  "3gp7": "video/3gpp",
76164
76111
  "3g2": "video/3gpp2"
76165
76112
  });
76166
- function toBuffer(data) {
76113
+ function toBuffer$1(data) {
76167
76114
  return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
76168
76115
  }
76169
76116
  function startsWith(buf, prefix) {
@@ -76178,7 +76125,7 @@ function sniffFtypBrand(header) {
76178
76125
  return header.subarray(8, 12).toString("latin1").toLowerCase().replaceAll(/[\s\u0000]+$/g, "").trim();
76179
76126
  }
76180
76127
  function sniffMediaFromMagic(data) {
76181
- const buf = toBuffer(data);
76128
+ const buf = toBuffer$1(data);
76182
76129
  const header = buf.length > 512 ? buf.subarray(0, 512) : buf;
76183
76130
  if (startsWith(header, [
76184
76131
  137,
@@ -76290,7 +76237,7 @@ function sniffMediaFromMagic(data) {
76290
76237
  * when the supplied buffer is too short to cover it.
76291
76238
  */
76292
76239
  function sniffImageDimensions(data) {
76293
- const buf = toBuffer(data);
76240
+ const buf = toBuffer$1(data);
76294
76241
  if (startsWith(buf, [
76295
76242
  137,
76296
76243
  80,
@@ -76375,7 +76322,7 @@ function detectFileType(path, header) {
76375
76322
  mimeType: VIDEO_MIME_BY_SUFFIX$1[suffix]
76376
76323
  };
76377
76324
  if (header !== void 0) {
76378
- const buf = toBuffer(header);
76325
+ const buf = toBuffer$1(header);
76379
76326
  const sniffed = sniffMediaFromMagic(buf);
76380
76327
  if (sniffed) {
76381
76328
  if (mediaHint) {
@@ -78352,9 +78299,12 @@ var WebSearchTool = class {
78352
78299
  execute: (ctx) => this.execution(args, ctx)
78353
78300
  };
78354
78301
  }
78355
- async execution(args, { toolCallId }) {
78302
+ async execution(args, { signal, toolCallId }) {
78356
78303
  try {
78357
- const opts = { toolCallId };
78304
+ const opts = {
78305
+ signal,
78306
+ toolCallId
78307
+ };
78358
78308
  if (args.limit !== void 0) opts.limit = args.limit;
78359
78309
  if (args.include_content !== void 0) opts.includeContent = args.include_content;
78360
78310
  const results = await this.provider.search(args.query, opts);
@@ -81146,7 +81096,7 @@ async function runHook(command, input, options) {
81146
81096
  detached: process.platform !== "win32"
81147
81097
  });
81148
81098
  } catch (error) {
81149
- return allowResult({ stderr: errorMessage$2(error) });
81099
+ return allowResult({ stderr: errorMessage$3(error) });
81150
81100
  }
81151
81101
  return new Promise((resolve) => {
81152
81102
  let stdout = "";
@@ -81194,7 +81144,7 @@ async function runHook(command, input, options) {
81194
81144
  child.on("error", (error) => {
81195
81145
  settle(allowResult({
81196
81146
  stdout,
81197
- stderr: stderr + errorMessage$2(error)
81147
+ stderr: stderr + errorMessage$3(error)
81198
81148
  }));
81199
81149
  });
81200
81150
  child.on("close", (code) => {
@@ -81290,7 +81240,7 @@ function tryKillProcess(child, signal) {
81290
81240
  function isRecord$5(value) {
81291
81241
  return typeof value === "object" && value !== null && !Array.isArray(value);
81292
81242
  }
81293
- function errorMessage$2(error) {
81243
+ function errorMessage$3(error) {
81294
81244
  return error instanceof Error ? error.message : String(error);
81295
81245
  }
81296
81246
  //#endregion
@@ -92177,6 +92127,7 @@ var ToolScheduler = class {
92177
92127
  //#endregion
92178
92128
  //#region ../../packages/agent-core/src/loop/tool-call.ts
92179
92129
  const GRACE_TIMEOUT_MS = 2e3;
92130
+ const STEER_POLL_INTERVAL_MS = 150;
92180
92131
  const TOOL_OUTPUT_EMPTY = "Tool output is empty.";
92181
92132
  const TOOL_OUTPUT_NON_TEXT = "Tool returned non-text content.";
92182
92133
  const validators = /* @__PURE__ */ new WeakMap();
@@ -92196,15 +92147,29 @@ async function runToolCallBatch(step, response) {
92196
92147
  const scheduler = new ToolScheduler();
92197
92148
  const pendingResults = [];
92198
92149
  let stopTurn = false;
92150
+ const steerController = step.hasPendingSteer !== void 0 ? new AbortController() : void 0;
92151
+ const effectiveStep = steerController === void 0 ? step : {
92152
+ ...step,
92153
+ signal: AbortSignal.any([step.signal, steerController.signal])
92154
+ };
92155
+ const abortOnSteer = () => {
92156
+ if (step.hasPendingSteer?.() === true && steerController !== void 0 && !steerController.signal.aborted) steerController.abort(userCancellationReason());
92157
+ };
92158
+ abortOnSteer();
92159
+ const steerPoll = steerController === void 0 ? void 0 : setInterval(() => {
92160
+ try {
92161
+ abortOnSteer();
92162
+ } catch {}
92163
+ }, STEER_POLL_INTERVAL_MS);
92199
92164
  try {
92200
92165
  for (let index = 0; index < calls.length; index += 1) {
92201
92166
  const call = calls[index];
92202
- if (step.signal.aborted) {
92167
+ if (effectiveStep.signal.aborted) {
92203
92168
  await dispatchToolCall(step, call, call.args);
92204
- pendingResults.push(Promise.resolve(makeErrorToolResult(call, call.args, abortedToolOutput(call.toolName, step.signal))));
92169
+ pendingResults.push(Promise.resolve(makeErrorToolResult(call, call.args, abortedToolOutput(call.toolName, effectiveStep.signal))));
92205
92170
  continue;
92206
92171
  }
92207
- const prepared = await prepareToolCall(step, call);
92172
+ const prepared = await prepareToolCall(effectiveStep, call);
92208
92173
  pendingResults.push(scheduler.add(prepared.task));
92209
92174
  if (prepared.stopBatchAfterThis === true) {
92210
92175
  stopTurn = true;
@@ -92216,7 +92181,7 @@ async function runToolCallBatch(step, response) {
92216
92181
  }
92217
92182
  }
92218
92183
  for (const pendingResult of pendingResults) {
92219
- const result = await finalizePendingToolResult(step, await pendingResult);
92184
+ const result = await finalizePendingToolResult(effectiveStep, await pendingResult);
92220
92185
  if (result.stopTurn === true) stopTurn = true;
92221
92186
  await step.dispatchEvent({
92222
92187
  type: "tool.result",
@@ -92226,6 +92191,7 @@ async function runToolCallBatch(step, response) {
92226
92191
  });
92227
92192
  }
92228
92193
  } finally {
92194
+ if (steerPoll !== void 0) clearInterval(steerPoll);
92229
92195
  await Promise.allSettled(pendingResults);
92230
92196
  }
92231
92197
  return { stopTurn };
@@ -92289,7 +92255,7 @@ function parseToolCallArguments(raw) {
92289
92255
  } catch {
92290
92256
  return {
92291
92257
  success: false,
92292
- error: errorMessage$3(error)
92258
+ error: errorMessage$4(error)
92293
92259
  };
92294
92260
  }
92295
92261
  }
@@ -92369,7 +92335,7 @@ async function prepareToolCall(step, call) {
92369
92335
  toolCallId: call.toolCall.id,
92370
92336
  error
92371
92337
  });
92372
- return settleError(effectiveArgs, error instanceof PathSecurityError ? error.message : `Tool "${call.toolName}" failed to resolve execution: ${errorMessage$3(error)}`);
92338
+ return settleError(effectiveArgs, error instanceof PathSecurityError ? error.message : `Tool "${call.toolName}" failed to resolve execution: ${errorMessage$4(error)}`);
92373
92339
  }
92374
92340
  const displayFields = toolCallDisplayFieldsFromExecution(execution);
92375
92341
  const settleAborted = () => settleError(effectiveArgs, abortedToolOutput(call.toolName, step.signal), displayFields);
@@ -92428,7 +92394,7 @@ async function runPrepareToolExecutionHook(step, call) {
92428
92394
  return {
92429
92395
  kind: "hookFailed",
92430
92396
  args,
92431
- output: `prepareToolExecution hook failed for "${call.toolName}": ${errorMessage$3(error)}`
92397
+ output: `prepareToolExecution hook failed for "${call.toolName}": ${errorMessage$4(error)}`
92432
92398
  };
92433
92399
  }
92434
92400
  const effectiveArgs = hookResult?.updatedArgs ?? args;
@@ -92469,7 +92435,7 @@ async function runAuthorizeToolExecutionHook(step, call, args, execution) {
92469
92435
  };
92470
92436
  return {
92471
92437
  block: true,
92472
- reason: `authorizeToolExecution hook failed for "${call.toolName}": ${errorMessage$3(error)}`
92438
+ reason: `authorizeToolExecution hook failed for "${call.toolName}": ${errorMessage$4(error)}`
92473
92439
  };
92474
92440
  }
92475
92441
  }
@@ -92496,7 +92462,7 @@ async function runRunnableToolCall(step, call, effectiveArgs, metadata, executio
92496
92462
  toolCallId: toolCall.id,
92497
92463
  error
92498
92464
  });
92499
- return makeErrorToolResult(call, effectiveArgs, aborted ? abortedToolOutput(toolName, signal) : `Tool "${toolName}" failed: ${errorMessage$3(error)}`);
92465
+ return makeErrorToolResult(call, effectiveArgs, aborted ? abortedToolOutput(toolName, signal) : `Tool "${toolName}" failed: ${errorMessage$4(error)}`);
92500
92466
  }
92501
92467
  return makeToolResult(call, effectiveArgs, toolResult);
92502
92468
  }
@@ -92528,7 +92494,7 @@ async function finalizePendingToolResult(step, pendingResult) {
92528
92494
  toolCallId: pendingResult.toolCall.id,
92529
92495
  error
92530
92496
  });
92531
- const output = aborted ? `Tool "${pendingResult.toolName}" aborted during finalizeToolResult hook.` : `finalizeToolResult hook failed for "${pendingResult.toolName}": ${errorMessage$3(error)}`;
92497
+ const output = aborted ? `Tool "${pendingResult.toolName}" aborted during finalizeToolResult hook.` : `finalizeToolResult hook failed for "${pendingResult.toolName}": ${errorMessage$4(error)}`;
92532
92498
  return {
92533
92499
  ...pendingResult,
92534
92500
  stopTurn: pendingResult.stopTurn,
@@ -92698,7 +92664,8 @@ async function executeLoopStep(deps) {
92698
92664
  signal,
92699
92665
  turnId,
92700
92666
  currentStep,
92701
- stepUuid
92667
+ stepUuid,
92668
+ hasPendingSteer: deps.hasPendingSteer
92702
92669
  };
92703
92670
  await dispatchEvent({
92704
92671
  type: "step.begin",
@@ -92860,7 +92827,8 @@ async function runTurn(input) {
92860
92827
  log,
92861
92828
  currentStep: steps,
92862
92829
  maxRetryAttempts,
92863
- recordUsage: recordStepUsage
92830
+ recordUsage: recordStepUsage,
92831
+ hasPendingSteer: input.hasPendingSteer
92864
92832
  });
92865
92833
  activeStep = void 0;
92866
92834
  if (stepResult.stopReason === "tool_use") continue;
@@ -92884,7 +92852,7 @@ async function runTurn(input) {
92884
92852
  usage
92885
92853
  };
92886
92854
  }
92887
- dispatchEvent(makeInterruptedEvent(isMaxStepsExceededError(error) ? "max_steps" : "error", steps, activeStep, errorMessage$3(error)));
92855
+ dispatchEvent(makeInterruptedEvent(isMaxStepsExceededError(error) ? "max_steps" : "error", steps, activeStep, errorMessage$4(error)));
92888
92856
  throw error;
92889
92857
  }
92890
92858
  return {
@@ -97914,7 +97882,7 @@ var TurnFlow = class {
97914
97882
  });
97915
97883
  return this.launch(input, origin);
97916
97884
  }
97917
- steer(input, origin = USER_PROMPT_ORIGIN) {
97885
+ steer(input, origin = USER_PROMPT_ORIGIN, opts) {
97918
97886
  this.agent.records.logRecord({
97919
97887
  type: "turn.steer",
97920
97888
  input,
@@ -97923,7 +97891,8 @@ var TurnFlow = class {
97923
97891
  if (this.activeTurn) {
97924
97892
  this.steerBuffer.push({
97925
97893
  input,
97926
- origin
97894
+ origin,
97895
+ interrupt: opts?.interrupt
97927
97896
  });
97928
97897
  return null;
97929
97898
  }
@@ -98245,6 +98214,7 @@ var TurnFlow = class {
98245
98214
  log: this.agent.log,
98246
98215
  maxSteps: loopControl?.maxStepsPerTurn,
98247
98216
  maxRetryAttempts: loopControl?.maxRetriesPerStep,
98217
+ hasPendingSteer: () => this.steerBuffer.some((steer) => steer.origin.kind === "user" && steer.interrupt !== false),
98248
98218
  hooks: {
98249
98219
  beforeStep: async ({ signal: stepSignal, stepNumber }) => {
98250
98220
  this.flushSteerBuffer();
@@ -99236,7 +99206,7 @@ var Agent = class {
99236
99206
  this.turn.prompt(payload.input);
99237
99207
  },
99238
99208
  steer: (payload) => {
99239
- this.turn.steer(payload.input);
99209
+ this.turn.steer(payload.input, USER_PROMPT_ORIGIN, { interrupt: payload.interrupt });
99240
99210
  },
99241
99211
  cancel: (payload) => {
99242
99212
  this.turn.cancel(payload.turnId);
@@ -99968,17 +99938,18 @@ function permissionRuleToToml(rule) {
99968
99938
  }
99969
99939
  function servicesToToml(services, rawServices) {
99970
99940
  const out = cloneRecord(rawServices);
99971
- if (services.screamCliSearch !== void 0) out["scream_cli_search"] = serviceToToml(services.screamCliSearch);
99972
- else delete out["scream_cli_search"];
99973
- if (services.screamCliFetch !== void 0) out["scream_cli_fetch"] = serviceToToml(services.screamCliFetch);
99974
- else delete out["scream_cli_fetch"];
99975
- return out;
99976
- }
99977
- function serviceToToml(service) {
99978
- const out = {};
99979
- for (const [key, value] of Object.entries(service)) if (key === "oauth" && value !== void 0) out[camelToSnake(key)] = oauthToToml(value);
99980
- else if (key === "customHeaders" && value !== void 0) out[camelToSnake(key)] = cloneUnknown(value);
99981
- else setDefined(out, camelToSnake(key), value);
99941
+ for (const key of [
99942
+ "duckduckgo",
99943
+ "sogou",
99944
+ "so360",
99945
+ "baidu"
99946
+ ]) {
99947
+ const toggle = services[key];
99948
+ if (toggle?.enabled !== void 0) out[key] = {
99949
+ ...isPlainObject$1(out[key]) ? out[key] : {},
99950
+ enabled: toggle.enabled
99951
+ };
99952
+ }
99982
99953
  return out;
99983
99954
  }
99984
99955
  function loopControlToToml(loopControl, rawLoopControl) {
@@ -102425,7 +102396,7 @@ function buildMcpHttpHeaders(config, envLookup) {
102425
102396
  var require_windows = /* @__PURE__ */ __commonJSMin(((exports, module) => {
102426
102397
  module.exports = isexe;
102427
102398
  isexe.sync = sync;
102428
- var fs$3 = __require("fs");
102399
+ var fs$4 = __require("fs");
102429
102400
  function checkPathExt(path, options) {
102430
102401
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
102431
102402
  if (!pathext) return true;
@@ -102442,12 +102413,12 @@ var require_windows = /* @__PURE__ */ __commonJSMin(((exports, module) => {
102442
102413
  return checkPathExt(path, options);
102443
102414
  }
102444
102415
  function isexe(path, options, cb) {
102445
- fs$3.stat(path, function(er, stat) {
102416
+ fs$4.stat(path, function(er, stat) {
102446
102417
  cb(er, er ? false : checkStat(stat, path, options));
102447
102418
  });
102448
102419
  }
102449
102420
  function sync(path, options) {
102450
- return checkStat(fs$3.statSync(path), path, options);
102421
+ return checkStat(fs$4.statSync(path), path, options);
102451
102422
  }
102452
102423
  }));
102453
102424
  //#endregion
@@ -102455,14 +102426,14 @@ var require_windows = /* @__PURE__ */ __commonJSMin(((exports, module) => {
102455
102426
  var require_mode = /* @__PURE__ */ __commonJSMin(((exports, module) => {
102456
102427
  module.exports = isexe;
102457
102428
  isexe.sync = sync;
102458
- var fs$2 = __require("fs");
102429
+ var fs$3 = __require("fs");
102459
102430
  function isexe(path, options, cb) {
102460
- fs$2.stat(path, function(er, stat) {
102431
+ fs$3.stat(path, function(er, stat) {
102461
102432
  cb(er, er ? false : checkStat(stat, options));
102462
102433
  });
102463
102434
  }
102464
102435
  function sync(path, options) {
102465
- return checkStat(fs$2.statSync(path), options);
102436
+ return checkStat(fs$3.statSync(path), options);
102466
102437
  }
102467
102438
  function checkStat(stat, options) {
102468
102439
  return stat.isFile() && checkMode(stat, options);
@@ -102677,16 +102648,16 @@ var require_shebang_command = /* @__PURE__ */ __commonJSMin(((exports, module) =
102677
102648
  //#endregion
102678
102649
  //#region ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js
102679
102650
  var require_readShebang = /* @__PURE__ */ __commonJSMin(((exports, module) => {
102680
- const fs$1 = __require("fs");
102651
+ const fs$2 = __require("fs");
102681
102652
  const shebangCommand = require_shebang_command();
102682
102653
  function readShebang(command) {
102683
102654
  const size = 150;
102684
102655
  const buffer = Buffer.alloc(size);
102685
102656
  let fd;
102686
102657
  try {
102687
- fd = fs$1.openSync(command, "r");
102688
- fs$1.readSync(fd, buffer, 0, size, 0);
102689
- fs$1.closeSync(fd);
102658
+ fd = fs$2.openSync(command, "r");
102659
+ fs$2.readSync(fd, buffer, 0, size, 0);
102660
+ fs$2.closeSync(fd);
102690
102661
  } catch (e) {}
102691
102662
  return shebangCommand(buffer.toString());
102692
102663
  }
@@ -117199,6 +117170,243 @@ function Document() {
117199
117170
  illegalConstructor();
117200
117171
  }
117201
117172
  setPrototypeOf(Document, Document$1).prototype = Document$1.prototype;
117173
+ /** `.tmp` files older than this are treated as orphaned writes and swept. */
117174
+ const TMP_ORPHAN_MAX_AGE_MS = 300 * 1e3;
117175
+ function documentConversionCacheDir() {
117176
+ return path$8.join(resolveScreamHome(), "cache", "document-conversion");
117177
+ }
117178
+ function isEnoent(error) {
117179
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
117180
+ }
117181
+ function markitConversionCacheKey(bytes, extension) {
117182
+ const safeExtension = (extension.trim().toLowerCase().replace(/^\.+/, "") || "bin").replace(/[^a-z0-9]+/g, "_") || "bin";
117183
+ const safeVersion = getCoreVersion().replace(/[^a-z0-9]+/gi, "_");
117184
+ const digest = createHash("sha256").update(bytes).digest("hex");
117185
+ return `v${String(1)}-${safeVersion}-${safeExtension}-${digest}`;
117186
+ }
117187
+ function cacheEntryPath(key) {
117188
+ return path$8.join(documentConversionCacheDir(), `${key}.json`);
117189
+ }
117190
+ function errorMessage$2(error) {
117191
+ return error instanceof Error ? error.message : String(error);
117192
+ }
117193
+ function parseCacheEntry(raw) {
117194
+ const parsed = JSON.parse(raw);
117195
+ if (typeof parsed !== "object" || parsed === null) return null;
117196
+ if (!("version" in parsed) || parsed.version !== 1) return null;
117197
+ if (!("content" in parsed) || typeof parsed.content !== "string" || parsed.content.length === 0) return null;
117198
+ return {
117199
+ version: 1,
117200
+ content: parsed.content
117201
+ };
117202
+ }
117203
+ async function readMarkitConversionCache(key) {
117204
+ const target = cacheEntryPath(key);
117205
+ let raw;
117206
+ try {
117207
+ raw = await fs$1.readFile(target, "utf8");
117208
+ } catch (error) {
117209
+ if (!isEnoent(error)) log.debug("document conversion cache read failed", { error: errorMessage$2(error) });
117210
+ return { status: "miss" };
117211
+ }
117212
+ let entry;
117213
+ try {
117214
+ entry = parseCacheEntry(raw);
117215
+ } catch (error) {
117216
+ log.debug("document conversion cache read failed", { error: errorMessage$2(error) });
117217
+ entry = null;
117218
+ }
117219
+ if (entry === null) {
117220
+ await fs$1.rm(target, { force: true }).catch(() => void 0);
117221
+ return { status: "miss" };
117222
+ }
117223
+ return {
117224
+ status: "hit",
117225
+ content: entry.content
117226
+ };
117227
+ }
117228
+ async function pruneMarkitConversionCache(cacheDir) {
117229
+ let names;
117230
+ try {
117231
+ names = await fs$1.readdir(cacheDir);
117232
+ } catch (error) {
117233
+ if (!isEnoent(error)) log.debug("document conversion cache prune failed", { error: errorMessage$2(error) });
117234
+ return;
117235
+ }
117236
+ const now = Date.now();
117237
+ const entries = [];
117238
+ let totalBytes = 0;
117239
+ for (const name of names) {
117240
+ const entryPath = path$8.join(cacheDir, name);
117241
+ let stat;
117242
+ try {
117243
+ stat = await fs$1.stat(entryPath);
117244
+ } catch (error) {
117245
+ if (!isEnoent(error)) log.debug("document conversion cache prune failed", { error: errorMessage$2(error) });
117246
+ continue;
117247
+ }
117248
+ if (!stat.isFile()) continue;
117249
+ if (name.endsWith(".tmp")) {
117250
+ if (now - stat.mtimeMs > TMP_ORPHAN_MAX_AGE_MS) await fs$1.rm(entryPath, { force: true }).catch(() => void 0);
117251
+ continue;
117252
+ }
117253
+ if (!name.endsWith(".json")) continue;
117254
+ entries.push({
117255
+ path: entryPath,
117256
+ size: stat.size,
117257
+ mtimeMs: stat.mtimeMs
117258
+ });
117259
+ totalBytes += stat.size;
117260
+ }
117261
+ if (totalBytes <= 268435456) return;
117262
+ entries.sort((a, b) => a.mtimeMs - b.mtimeMs);
117263
+ for (const entry of entries) {
117264
+ if (totalBytes <= 268435456) break;
117265
+ try {
117266
+ await fs$1.rm(entry.path, { force: true });
117267
+ totalBytes -= entry.size;
117268
+ } catch (error) {
117269
+ if (!isEnoent(error)) log.debug("document conversion cache prune failed", { error: errorMessage$2(error) });
117270
+ }
117271
+ }
117272
+ }
117273
+ async function writeMarkitConversionCache(key, content) {
117274
+ const cacheDir = documentConversionCacheDir();
117275
+ const target = path$8.join(cacheDir, `${key}.json`);
117276
+ const tempPath = path$8.join(cacheDir, `${key}.${String(process.pid)}.${String(Date.now())}.${randomUUID()}.tmp`);
117277
+ const payload = JSON.stringify({
117278
+ version: 1,
117279
+ content
117280
+ });
117281
+ try {
117282
+ await fs$1.mkdir(cacheDir, { recursive: true });
117283
+ await fs$1.writeFile(tempPath, payload);
117284
+ await fs$1.rename(tempPath, target);
117285
+ } catch (error) {
117286
+ await fs$1.rm(tempPath, { force: true }).catch(() => void 0);
117287
+ log.debug("document conversion cache write failed", { error: errorMessage$2(error) });
117288
+ return;
117289
+ }
117290
+ pruneMarkitConversionCache(cacheDir).catch((error) => {
117291
+ log.debug("document conversion cache prune failed", { error: errorMessage$2(error) });
117292
+ });
117293
+ }
117294
+ //#endregion
117295
+ //#region ../../packages/agent-core/src/utils/markit.ts
117296
+ function logMuPdfWasmOutput(stream, values) {
117297
+ const message = values.length === 1 && typeof values[0] === "string" ? values[0] : values.map(String).join(" ");
117298
+ log.debug("mupdf wasm output", {
117299
+ stream,
117300
+ message
117301
+ });
117302
+ }
117303
+ function installMuPdfWasmLogger() {
117304
+ const globalScope = globalThis;
117305
+ const moduleConfig = globalScope.$libmupdf_wasm_Module ?? {};
117306
+ moduleConfig.print = (...values) => logMuPdfWasmOutput("stdout", values);
117307
+ moduleConfig.printErr = (...values) => logMuPdfWasmOutput("stderr", values);
117308
+ globalScope.$libmupdf_wasm_Module = moduleConfig;
117309
+ }
117310
+ installMuPdfWasmLogger();
117311
+ let markit = async () => {
117312
+ const promise = import("./markit-DG5XsAGz.mjs").then(({ Markit }) => {
117313
+ const instance = new Markit();
117314
+ markit = () => instance;
117315
+ return instance;
117316
+ });
117317
+ markit = () => promise;
117318
+ return promise;
117319
+ };
117320
+ function normalizeExtension(extension) {
117321
+ const trimmed = extension.trim().toLowerCase();
117322
+ if (!trimmed) return ".bin";
117323
+ return trimmed.startsWith(".") ? trimmed : `.${trimmed}`;
117324
+ }
117325
+ function normalizeError(error) {
117326
+ if (error instanceof Error && error.message.trim().length > 0) return error.message.trim();
117327
+ return "Conversion failed";
117328
+ }
117329
+ function isAbort(error) {
117330
+ return error instanceof Error && error.name === "AbortError";
117331
+ }
117332
+ async function untilAborted(signal, task) {
117333
+ if (signal === void 0) return task();
117334
+ signal.throwIfAborted();
117335
+ return new Promise((resolve, reject) => {
117336
+ const onAbort = () => {
117337
+ reject(abortError());
117338
+ };
117339
+ signal.addEventListener("abort", onAbort, { once: true });
117340
+ task().then(resolve, reject).finally(() => {
117341
+ signal.removeEventListener("abort", onAbort);
117342
+ });
117343
+ });
117344
+ }
117345
+ async function runMarkitConversion(task, signal) {
117346
+ try {
117347
+ const instance = await markit();
117348
+ return await untilAborted(signal, () => task(instance));
117349
+ } catch (error) {
117350
+ if (isAbort(error)) throw abortError();
117351
+ throw error;
117352
+ }
117353
+ }
117354
+ function finalizeConversion(markdown) {
117355
+ if (typeof markdown === "string" && markdown.length > 0) return {
117356
+ content: markdown,
117357
+ ok: true
117358
+ };
117359
+ return {
117360
+ content: "",
117361
+ ok: false,
117362
+ error: "Conversion produced no output"
117363
+ };
117364
+ }
117365
+ function toBuffer(bytes) {
117366
+ return Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
117367
+ }
117368
+ function throwIfAborted(signal) {
117369
+ if (signal?.aborted) throw abortError();
117370
+ }
117371
+ async function runCachedBufferConversion(bytes, streamInfo, signal, cacheEnabled = true) {
117372
+ const cacheKey = cacheEnabled ? markitConversionCacheKey(bytes, streamInfo.extension ?? streamInfo.mimetype ?? ".bin") : void 0;
117373
+ if (cacheKey !== void 0) {
117374
+ throwIfAborted(signal);
117375
+ const cached = await readMarkitConversionCache(cacheKey);
117376
+ throwIfAborted(signal);
117377
+ if (cached.status === "hit") return {
117378
+ content: cached.content,
117379
+ ok: true,
117380
+ cache: "hit"
117381
+ };
117382
+ }
117383
+ throwIfAborted(signal);
117384
+ let result;
117385
+ try {
117386
+ result = await runMarkitConversion((markitInstance) => markitInstance.convert(toBuffer(bytes), streamInfo), signal);
117387
+ } catch (error) {
117388
+ if (isAbort(error)) throw abortError();
117389
+ return {
117390
+ content: "",
117391
+ ok: false,
117392
+ error: normalizeError(error),
117393
+ cache: cacheEnabled ? "miss" : "skipped"
117394
+ };
117395
+ }
117396
+ const finalized = finalizeConversion(result.markdown);
117397
+ if (finalized.ok && cacheKey !== void 0) await writeMarkitConversionCache(cacheKey, finalized.content);
117398
+ return {
117399
+ ...finalized,
117400
+ cache: cacheEnabled ? "miss" : "skipped"
117401
+ };
117402
+ }
117403
+ async function convertBufferWithMarkit(buffer, extension, signal, options) {
117404
+ const normalizedExtension = normalizeExtension(extension);
117405
+ return runCachedBufferConversion(buffer, {
117406
+ extension: normalizedExtension,
117407
+ filename: `input${normalizedExtension}`
117408
+ }, signal, options?.useCache ?? true);
117409
+ }
117202
117410
  //#endregion
117203
117411
  //#region ../../packages/agent-core/src/tools/providers/local-fetch-url.ts
117204
117412
  /**
@@ -117216,6 +117424,49 @@ setPrototypeOf(Document, Document$1).prototype = Document$1.prototype;
117216
117424
  * common content containers (`<article>` / `<main>` / `<body>`)
117217
117425
  * before throwing a "meaningful content" error.
117218
117426
  */
117427
+ /** Document types the markit engine converts to markdown. */
117428
+ const CONVERTIBLE_EXTENSIONS = new Set([
117429
+ ".pdf",
117430
+ ".docx",
117431
+ ".pptx",
117432
+ ".xlsx",
117433
+ ".epub"
117434
+ ]);
117435
+ const CONVERTIBLE_MIME_PREFIXES = [
117436
+ ["application/pdf", ".pdf"],
117437
+ ["application/x-pdf", ".pdf"],
117438
+ ["application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".docx"],
117439
+ ["application/vnd.openxmlformats-officedocument.presentationml.presentation", ".pptx"],
117440
+ ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".xlsx"],
117441
+ ["application/epub+zip", ".epub"]
117442
+ ];
117443
+ /**
117444
+ * Decide whether a response is a convertible document, from Content-Type
117445
+ * first and the URL path extension as fallback. `confident: true` means the
117446
+ * Content-Type itself declared a document type (conversion failure is a real
117447
+ * error); `confident: false` means only the URL extension matched (an HTML
117448
+ * page at a .pdf URL must fall back to normal extraction).
117449
+ */
117450
+ function resolveDocumentExtension(url, contentType) {
117451
+ for (const [prefix, extension] of CONVERTIBLE_MIME_PREFIXES) if (contentType.startsWith(prefix)) return {
117452
+ extension,
117453
+ confident: true
117454
+ };
117455
+ if (contentType.length > 0 && !contentType.startsWith("application/octet-stream") && !contentType.startsWith("binary/octet-stream")) return;
117456
+ try {
117457
+ const pathname = new URL(url).pathname.toLowerCase();
117458
+ const dot = pathname.lastIndexOf(".");
117459
+ if (dot >= 0) {
117460
+ const extension = pathname.slice(dot);
117461
+ if (CONVERTIBLE_EXTENSIONS.has(extension)) return {
117462
+ extension,
117463
+ confident: false
117464
+ };
117465
+ }
117466
+ } catch {}
117467
+ }
117468
+ /** Hard ceiling for one markit conversion (omp uses 20s; allow slow hosts). */
117469
+ const CONVERSION_TIMEOUT_MS = 3e4;
117219
117470
  const DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36";
117220
117471
  const DEFAULT_MAX_BYTES = 10 * 1024 * 1024;
117221
117472
  const FETCH_TIMEOUT_MS = 3e4;
@@ -117281,7 +117532,7 @@ function isPrivateIp(ip) {
117281
117532
  }
117282
117533
  return lower === "::1" || lower === "::" || lower.startsWith("fe80:") || lower.startsWith("fc") || lower.startsWith("fd");
117283
117534
  }
117284
- function cacheKey$1(url, allowPrivate, maxBytes, userAgent) {
117535
+ function cacheKey(url, allowPrivate, maxBytes, userAgent) {
117285
117536
  return `local:${url}:${String(allowPrivate)}:${String(maxBytes)}:${userAgent}`;
117286
117537
  }
117287
117538
  const defaultDnsLookup = async (hostname) => {
@@ -117303,7 +117554,7 @@ var LocalFetchURLProvider = class {
117303
117554
  this.dnsLookup = options.dnsLookup ?? defaultDnsLookup;
117304
117555
  }
117305
117556
  async fetch(url, _options) {
117306
- const key = cacheKey$1(url, this.allowPrivateAddresses, this.maxBytes, this.userAgent);
117557
+ const key = cacheKey(url, this.allowPrivateAddresses, this.maxBytes, this.userAgent);
117307
117558
  const cached = this.cache.get(key);
117308
117559
  if (cached !== void 0) return cached;
117309
117560
  await assertSafeFetchTarget(url, this.allowPrivateAddresses, this.dnsLookup);
@@ -117329,10 +117580,16 @@ var LocalFetchURLProvider = class {
117329
117580
  throw new Error(`Response body too large: ${String(cl)} bytes exceeds maxBytes (${String(this.maxBytes)}).`);
117330
117581
  }
117331
117582
  }
117583
+ const contentType = (response.headers.get("content-type") ?? "").toLowerCase();
117584
+ const documentExtension = resolveDocumentExtension(url, contentType);
117585
+ if (documentExtension !== void 0) return this.fetchDocument(response, documentExtension.extension, contentType, documentExtension.confident);
117332
117586
  const body = await response.text();
117333
117587
  const actualBytes = Buffer.byteLength(body, "utf8");
117334
117588
  if (actualBytes > this.maxBytes) throw new Error(`Response body too large: ${String(actualBytes)} bytes exceeds maxBytes (${String(this.maxBytes)}).`);
117335
- const contentType = (response.headers.get("content-type") ?? "").toLowerCase();
117589
+ return this.extractTextResponse(body, contentType);
117590
+ }
117591
+ /** Text-path extraction shared by the normal flow and the document fallback. */
117592
+ extractTextResponse(body, contentType) {
117336
117593
  if (contentType.startsWith("text/plain") || contentType.startsWith("text/markdown")) return {
117337
117594
  content: body,
117338
117595
  kind: "passthrough"
@@ -117342,6 +117599,28 @@ var LocalFetchURLProvider = class {
117342
117599
  kind: "extracted"
117343
117600
  };
117344
117601
  }
117602
+ /**
117603
+ * Fetch a convertible document (PDF/Office) as binary and convert it to
117604
+ * markdown via the markit engine (lazy-loaded, cached on disk).
117605
+ *
117606
+ * `confident` distinguishes detection strength: a convertible Content-Type
117607
+ * means conversion failure is a real error (corrupt document), while an
117608
+ * extension-only guess (e.g. an HTML viewer page at a .pdf URL) falls back
117609
+ * to the normal text extraction path instead of erroring (omp's behavior).
117610
+ */
117611
+ async fetchDocument(response, extension, contentType, confident) {
117612
+ const bytes = new Uint8Array(await response.arrayBuffer());
117613
+ if (bytes.byteLength > this.maxBytes) throw new Error(`Response body too large: ${String(bytes.byteLength)} bytes exceeds maxBytes (${String(this.maxBytes)}).`);
117614
+ const converted = await convertBufferWithMarkit(bytes, extension, AbortSignal.timeout(CONVERSION_TIMEOUT_MS));
117615
+ if (!converted.ok) {
117616
+ if (!confident) return this.extractTextResponse(new TextDecoder().decode(bytes), contentType);
117617
+ throw new Error(`Document conversion failed (${extension}): ${converted.error ?? "unknown error"}`);
117618
+ }
117619
+ return {
117620
+ content: converted.content,
117621
+ kind: "extracted"
117622
+ };
117623
+ }
117345
117624
  extractMainContent(html) {
117346
117625
  const primary = parseHTML(html);
117347
117626
  try {
@@ -117362,268 +117641,220 @@ var LocalFetchURLProvider = class {
117362
117641
  }
117363
117642
  };
117364
117643
  //#endregion
117365
- //#region ../../packages/agent-core/src/tools/providers/scream-cli-fetch-url.ts
117644
+ //#region ../../packages/agent-core/src/tools/providers/search-timeout.ts
117366
117645
  /**
117367
- * ScreamCliFetchURLProvider host-side UrlFetcher.
117646
+ * Hard timeout for outbound web-search requests (omp pattern).
117368
117647
  *
117369
- * Flow:
117370
- * 1. Try ScreamCli coding-fetch service (POST {url}, Bearer token from a
117371
- * narrow token provider, Accept: text/markdown, host-provided headers).
117372
- * 2. ScreamCli 200 return the body as `extracted` content (the
117373
- * service has already extracted the main page text on its side).
117374
- * 3. Any ScreamCli failure — non-200, network error, or token
117375
- * refresh failure delegate to `localFallback`, forwarding its
117376
- * content kind, so the LLM still gets *something* when the service
117377
- * is down.
117378
- * 4. If localFallback also throws → propagate that error.
117379
- */
117380
- function cacheKey(baseUrl, url) {
117381
- return `cli:${baseUrl}:${url}`;
117382
- }
117383
- var ScreamCliFetchURLProvider = class {
117384
- tokenProvider;
117385
- apiKey;
117386
- baseUrl;
117387
- defaultHeaders;
117388
- customHeaders;
117389
- localFallback;
117390
- fetchImpl;
117391
- cache;
117392
- constructor(options) {
117393
- this.tokenProvider = options.tokenProvider;
117394
- this.apiKey = options.apiKey;
117395
- this.baseUrl = options.baseUrl;
117396
- this.defaultHeaders = options.defaultHeaders ?? {};
117397
- this.customHeaders = options.customHeaders ?? {};
117398
- this.localFallback = options.localFallback;
117399
- this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
117400
- this.cache = options.cache ?? new FetchCache();
117401
- }
117402
- async fetch(url, options) {
117403
- const key = cacheKey(this.baseUrl, url);
117404
- const cached = this.cache.get(key);
117405
- if (cached !== void 0) return cached;
117406
- const result = await this.fetchFresh(url, options?.toolCallId);
117407
- this.cache.set(key, result);
117408
- return result;
117409
- }
117410
- async fetchFresh(url, toolCallId) {
117411
- try {
117412
- return {
117413
- content: await this.fetchViaScreamCli(url, toolCallId),
117414
- kind: "extracted"
117415
- };
117416
- } catch {
117417
- return this.localFallback.fetch(url, { toolCallId });
117418
- }
117419
- }
117420
- async fetchViaScreamCli(url, toolCallId) {
117421
- const bodyJson = JSON.stringify({ url });
117422
- const response = await this.post(bodyJson, toolCallId);
117423
- if (response.status !== 200) {
117424
- let detail = "";
117425
- try {
117426
- detail = await response.text();
117427
- } catch {}
117428
- throw new HttpFetchError(response.status, `ScreamCli fetch request failed: HTTP ${String(response.status)}. ${detail}`.trim());
117429
- }
117430
- return response.text();
117431
- }
117432
- async post(bodyJson, toolCallId) {
117433
- const accessToken = await this.resolveApiKey();
117434
- return this.fetchImpl(this.baseUrl, {
117435
- method: "POST",
117436
- headers: {
117437
- ...this.defaultHeaders,
117438
- Authorization: `Bearer ${accessToken}`,
117439
- Accept: "text/markdown",
117440
- "Content-Type": "application/json",
117441
- ...toolCallId !== void 0 && toolCallId.length > 0 ? { "X-Msh-Tool-Call-Id": toolCallId } : {},
117442
- ...this.customHeaders
117443
- },
117444
- body: bodyJson
117445
- });
117446
- }
117447
- async resolveApiKey() {
117448
- if (this.tokenProvider !== void 0) try {
117449
- const token = await this.tokenProvider.getAccessToken();
117450
- if (token.trim().length > 0) return token;
117451
- if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
117452
- } catch (error) {
117453
- if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
117454
- throw error;
117455
- }
117456
- if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
117457
- throw new Error("ScreamCli fetch service is not configured: missing API key or token provider.");
117458
- }
117459
- };
117648
+ * A stalled TCP/TLS connection can keep an undici fetch pending for minutes;
117649
+ * composing the caller's signal with a fixed ceiling guarantees the request
117650
+ * settles. Fires as a `TimeoutError` DOMException, which the tool layer maps
117651
+ * to "Search timed out".
117652
+ */
117653
+ const SEARCH_HARD_TIMEOUT_MS = 6e4;
117654
+ function withHardTimeout(signal, ms = SEARCH_HARD_TIMEOUT_MS) {
117655
+ const timeout = AbortSignal.timeout(ms);
117656
+ return signal !== void 0 ? AbortSignal.any([signal, timeout]) : timeout;
117657
+ }
117460
117658
  //#endregion
117461
117659
  //#region ../../packages/agent-core/src/tools/providers/duckduckgo-search.ts
117660
+ const DUCKDUCKGO_HTML_URL = "https://html.duckduckgo.com/html/";
117661
+ /**
117662
+ * Browser-like UA so DDG serves the standard results page instead of the
117663
+ * mobile/noscript variants, and is less likely to trip bot detection.
117664
+ * DDG answers automation it suspects with HTTP 202 plus an anomaly modal;
117665
+ * the body check (not the status) is the reliable signal.
117666
+ */
117667
+ const BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36";
117462
117668
  var DuckDuckGoSearchProvider = class {
117669
+ name = "duckduckgo";
117463
117670
  fetchImpl;
117464
117671
  constructor(options = {}) {
117465
117672
  this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
117466
117673
  }
117467
117674
  async search(query, options) {
117468
117675
  const limit = options?.limit ?? 5;
117469
- let results = await this.searchViaApi(query, limit);
117470
- if (results.length === 0) results = await this.searchViaLite(query, limit);
117471
- return results;
117472
- }
117473
- /**
117474
- * Query the DuckDuckGo Instant Answer API.
117475
- *
117476
- * Returns up to `limit` results built from the abstract and flattened
117477
- * RelatedTopics. Returns `[]` on any failure (network error, non-2xx,
117478
- * parse failure) so callers can fall through to the next method.
117479
- */
117480
- async searchViaApi(query, limit) {
117481
- const url = `https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json&no_html=1&skip_disambig=1`;
117482
- let response;
117483
- try {
117484
- response = await this.fetchImpl(url);
117485
- } catch {
117486
- return [];
117487
- }
117488
- if (response.status !== 200) {
117489
- await drainBody(response);
117490
- return [];
117491
- }
117492
- let json;
117493
- try {
117494
- json = await response.json();
117495
- } catch {
117496
- return [];
117497
- }
117498
- return this.buildApiResults(json, limit);
117499
- }
117500
- buildApiResults(json, limit) {
117501
- const results = [];
117502
- if (json.AbstractText && json.AbstractURL && json.AbstractText.trim().length > 0) results.push({
117503
- title: json.Heading && json.Heading.trim().length > 0 ? json.Heading : json.AbstractSource ?? "DuckDuckGo",
117504
- url: json.AbstractURL,
117505
- snippet: json.AbstractText
117506
- });
117507
- if (json.RelatedTopics && json.RelatedTopics.length > 0) {
117508
- const flattened = flattenRelatedTopics(json.RelatedTopics);
117509
- for (const topic of flattened) {
117510
- if (results.length >= limit) break;
117511
- const url = topic.FirstURL ?? "";
117512
- const text = topic.Text ?? "";
117513
- if (url === "" || text === "") continue;
117514
- results.push({
117515
- title: text,
117516
- url,
117517
- snippet: text
117518
- });
117519
- }
117520
- }
117521
- return results.slice(0, limit);
117522
- }
117523
- /**
117524
- * Scrape DuckDuckGo Lite search results.
117525
- *
117526
- * Lite is a minimal HTML table with no JavaScript, designed for basic
117527
- * browsers. It may return a CAPTCHA page from datacenter IPs — in that
117528
- * case we return `[]` so the caller can fall through.
117529
- */
117530
- async searchViaLite(query, limit) {
117531
- const url = `https://lite.duckduckgo.com/lite/?q=${encodeURIComponent(query)}`;
117532
- let response;
117533
- try {
117534
- response = await this.fetchImpl(url);
117535
- } catch {
117536
- return [];
117537
- }
117538
- if (response.status !== 200) {
117539
- await drainBody(response);
117540
- return [];
117541
- }
117542
- let html;
117543
- try {
117544
- html = await response.text();
117545
- } catch {
117546
- return [];
117547
- }
117548
- if (isCaptchaPage(html)) return [];
117549
- return parseLiteResults(html, limit);
117676
+ const form = new URLSearchParams({
117677
+ q: query,
117678
+ kl: "us-en"
117679
+ });
117680
+ form.set("b", "");
117681
+ const response = await this.fetchImpl(DUCKDUCKGO_HTML_URL, {
117682
+ method: "POST",
117683
+ body: form.toString(),
117684
+ headers: {
117685
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
117686
+ "Accept-Language": "en,en-US;q=0.9",
117687
+ "Content-Type": "application/x-www-form-urlencoded",
117688
+ Referer: "https://html.duckduckgo.com/",
117689
+ "Upgrade-Insecure-Requests": "1",
117690
+ "User-Agent": BROWSER_USER_AGENT
117691
+ },
117692
+ signal: withHardTimeout(options?.signal)
117693
+ });
117694
+ const html = await response.text();
117695
+ if (!response.ok && response.status !== 202) throw new Error(`DuckDuckGo request failed: HTTP ${String(response.status)}`);
117696
+ if (isAnomalyResponse(html)) throw new Error("DuckDuckGo blocked the request with a bot-detection challenge (common from datacenter/shared-egress IPs)");
117697
+ return parseHtmlResults(html).slice(0, limit);
117550
117698
  }
117551
117699
  };
117552
- /**
117553
- * Recursively flatten DuckDuckGo RelatedTopics.
117554
- *
117555
- * The API nests topics in two ways:
117556
- * - A direct topic has `FirstURL` + `Text` (may lack `Topics`).
117557
- * - A category group has a `Name` and nested `Topics[]`.
117558
- * We walk both shapes, returning a flat list of leaf topics.
117559
- */
117560
- function flattenRelatedTopics(topics) {
117561
- const out = [];
117562
- for (const topic of topics) if (topic.Topics && topic.Topics.length > 0) out.push(...flattenRelatedTopics(topic.Topics));
117563
- else if (topic.FirstURL) out.push(topic);
117564
- return out;
117700
+ /** `true` when DDG returned the bot-challenge modal instead of results. */
117701
+ function isAnomalyResponse(html) {
117702
+ return html.includes("anomaly-modal") || html.includes("anomaly.js");
117565
117703
  }
117566
117704
  /**
117567
- * Return true if the HTML looks like a DuckDuckGo bot-detection / CAPTCHA page.
117705
+ * Each result lives in a `<div class="result …">` container with
117706
+ * `<a class="result__a">` for the title link and an optional
117707
+ * `<a|div|span class="result__snippet">` sibling for the preview text.
117708
+ * Sponsored rows, missing snippets, and the pagination row are tolerated.
117568
117709
  */
117569
- function isCaptchaPage(html) {
117570
- return html.includes("anomaly-modal") || html.includes("challenge-form") || html.includes("g-recaptcha") || html.includes("data-testid=\"anomaly-modal\"");
117710
+ const RESULT_BLOCK_RE = /<div\b[^>]*\bclass="[^"]*\bresult\b[^"]*"[^>]*>([\s\S]*?)(?=<div\b[^>]*\bclass="[^"]*\bresult\b|<div\b[^>]*\bclass="[^"]*\bnav-link\b|$)/g;
117711
+ const RESULT_TITLE_RE = /<a\b[^>]*\bclass="[^"]*\bresult__a\b[^"]*"[^>]*\bhref="([^"]+)"[^>]*>([\s\S]*?)<\/a>/;
117712
+ const RESULT_SNIPPET_RE = /<(?:a|div|span)\b[^>]*\bclass="[^"]*\bresult__snippet\b[^"]*"[^>]*>([\s\S]*?)<\/(?:a|div|span)>/;
117713
+ /** Strip inline tags (DDG wraps query terms in `<b>`) and decode entities. */
117714
+ function decodeHtmlText$1(value) {
117715
+ return value.replace(/<[^>]*>/g, " ").replace(/&#(\d+);/g, (_, code) => String.fromCharCode(Number(code))).replace(/&#x([0-9a-f]+);/gi, (_, code) => String.fromCharCode(Number.parseInt(code, 16))).replace(/&nbsp;/gi, " ").replace(/&amp;/gi, "&").replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, "\"").replace(/&#39;|&apos;/gi, "'").replace(/\s+/g, " ").trim();
117571
117716
  }
117572
117717
  /**
117573
- * DuckDuckGo Lite result rows are inside `<tr>` blocks with a predictable
117574
- * anchor shape. This regex extracts the `<a class="result-link"...>` block
117575
- * and the following snippet text from the same row.
117576
- *
117577
- * The Lite markup is intentionally minimal (no JS, no CSS classes beyond a
117578
- * handful of utility names), so a DOM parser is unnecessary overhead.
117718
+ * Resolve a DDG result href to the underlying target URL. DDG routes
117719
+ * outbound clicks through `//duckduckgo.com/l/?uddg=<encoded>`; handles
117720
+ * redirect wrappers, protocol-relative links, and plain absolute URLs.
117579
117721
  */
117580
- const LITE_RESULT_RE = /<a\s[^>]*?\bclass\s*=\s*["'][^"']*result-link[^"']*["'][^>]*?\bhref\s*=\s*["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi;
117581
- const LITE_SNIPPET_RE = /<td\s[^>]*?\bclass\s*=\s*["'][^"']*result-snippet[^"']*["'][^>]*>([\s\S]*?)<\/td>/gi;
117582
- /** Strip HTML tags and decode common HTML entities. */
117583
- function stripHtmlAndDecode(text) {
117584
- return text.replaceAll(/<[^>]*>/g, "").replaceAll("&amp;", "&").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&quot;", "\"").replaceAll("&#x27;", "'").replaceAll("&#39;", "'").replaceAll("&nbsp;", " ").trim();
117585
- }
117586
- /** Unwrap a DuckDuckGo redirect URL to the real target if possible. */
117587
- function unwrapDdgRedirect(url) {
117588
- const uddgMatch = url.match(/[?&]uddg=([^&]+)/);
117589
- if (uddgMatch?.[1]) try {
117590
- return decodeURIComponent(uddgMatch[1]);
117722
+ function unwrapResultUrl(href) {
117723
+ if (href === "") return void 0;
117724
+ const decoded = href.replace(/&amp;/gi, "&");
117725
+ const wrapMatch = decoded.match(/[?&]uddg=([^&]+)/);
117726
+ if (wrapMatch?.[1] !== void 0) try {
117727
+ return decodeURIComponent(wrapMatch[1]);
117591
117728
  } catch {
117592
- return url;
117729
+ return;
117593
117730
  }
117594
- return url;
117731
+ if (decoded.startsWith("//")) return `https:${decoded}`;
117732
+ if (decoded.startsWith("http://") || decoded.startsWith("https://")) return decoded;
117595
117733
  }
117596
- function parseLiteResults(html, limit) {
117734
+ function parseHtmlResults(html) {
117597
117735
  const results = [];
117598
- const linkMatches = html.matchAll(LITE_RESULT_RE);
117599
- for (const match of linkMatches) {
117600
- if (results.length >= limit) break;
117601
- const href = unwrapDdgRedirect(match[1] ?? "");
117602
- const title = stripHtmlAndDecode(match[2] ?? "");
117603
- if (href === "" || title === "") continue;
117736
+ const seen = /* @__PURE__ */ new Set();
117737
+ for (const match of html.matchAll(RESULT_BLOCK_RE)) {
117738
+ const block = match[1] ?? "";
117739
+ const title = RESULT_TITLE_RE.exec(block);
117740
+ if (title === null) continue;
117741
+ const url = unwrapResultUrl(title[1] ?? "");
117742
+ if (url === void 0 || seen.has(url)) continue;
117743
+ const titleText = decodeHtmlText$1(title[2] ?? "");
117744
+ if (titleText === "") continue;
117745
+ seen.add(url);
117746
+ const snippet = RESULT_SNIPPET_RE.exec(block);
117747
+ const snippetText = snippet !== null ? decodeHtmlText$1(snippet[1] ?? "") : "";
117604
117748
  results.push({
117605
- title,
117606
- url: href,
117607
- snippet: title
117749
+ title: titleText,
117750
+ url,
117751
+ snippet: snippetText !== "" ? snippetText : titleText
117608
117752
  });
117609
117753
  }
117610
- const snippetMatches = html.matchAll(LITE_SNIPPET_RE);
117611
- let snippetIndex = 0;
117612
- for (const match of snippetMatches) {
117613
- if (snippetIndex >= results.length) break;
117614
- const snippet = stripHtmlAndDecode(match[1] ?? "");
117615
- const entry = results[snippetIndex];
117616
- if (entry && snippet.length > 0) entry.snippet = snippet;
117617
- snippetIndex++;
117618
- }
117619
117754
  return results;
117620
117755
  }
117621
- /** Drain a response body so the connection can be reused. */
117622
- async function drainBody(response) {
117623
- try {
117624
- await response.text();
117625
- } catch {}
117756
+ //#endregion
117757
+ //#region ../../packages/agent-core/src/tools/providers/domestic-search.ts
117758
+ const BROWSER_HEADERS = {
117759
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
117760
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
117761
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36"
117762
+ };
117763
+ /** Strip inline tags and decode the common named/numeric entities. */
117764
+ function decodeHtmlText(value) {
117765
+ return value.replace(/<[^>]*>/g, " ").replace(/&#(\d+);/g, (_, code) => String.fromCharCode(Number(code))).replace(/&#x([0-9a-f]+);/gi, (_, code) => String.fromCharCode(Number.parseInt(code, 16))).replace(/&nbsp;/gi, " ").replace(/&amp;/gi, "&").replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, "\"").replace(/&#39;|&apos;/gi, "'").replace(/\s+/g, " ").trim();
117626
117766
  }
117767
+ async function searchEngine(spec, fetchImpl, query, options) {
117768
+ const limit = options?.limit ?? 5;
117769
+ const response = await fetchImpl(spec.url(query), {
117770
+ headers: BROWSER_HEADERS,
117771
+ signal: withHardTimeout(options?.signal)
117772
+ });
117773
+ const html = await response.text();
117774
+ if (!response.ok) throw new Error(`${spec.label} request failed: HTTP ${String(response.status)}`);
117775
+ if (spec.botMarkers.some((m) => html.includes(m))) throw new Error(`${spec.label} blocked the request with an anti-bot challenge`);
117776
+ const results = [];
117777
+ const seen = /* @__PURE__ */ new Set();
117778
+ for (const match of html.matchAll(spec.blockRe)) {
117779
+ if (results.length >= limit) break;
117780
+ const block = match[1] ?? "";
117781
+ const title = spec.titleRe.exec(block);
117782
+ if (title === null) continue;
117783
+ const rawHref = (title[1] ?? "").replace(/&amp;/gi, "&");
117784
+ const url = spec.normalizeUrl !== void 0 ? spec.normalizeUrl(rawHref) : rawHref;
117785
+ if (url === void 0 || url === "" || seen.has(url)) continue;
117786
+ const titleText = decodeHtmlText(title[2] ?? "");
117787
+ if (titleText === "") continue;
117788
+ seen.add(url);
117789
+ const snippetMatch = spec.snippetRe === void 0 ? null : spec.snippetRe.exec(block);
117790
+ const snippetText = snippetMatch !== null ? decodeHtmlText(snippetMatch[1] ?? "") : "";
117791
+ results.push({
117792
+ title: titleText,
117793
+ url,
117794
+ snippet: snippetText !== "" ? snippetText : titleText
117795
+ });
117796
+ }
117797
+ return results;
117798
+ }
117799
+ const SOGOU = {
117800
+ label: "Sogou",
117801
+ url: (q) => `https://www.sogou.com/web?query=${encodeURIComponent(q)}`,
117802
+ blockRe: /<div\b[^>]*\bclass="[^"]*\b(?:vrwrap|rb)\b[^"]*"[^>]*>([\s\S]*?)(?=<div\b[^>]*\bclass="[^"]*\b(?:vrwrap|rb)\b|$)/g,
117803
+ titleRe: /<h3[^>]*>[\s\S]*?<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/,
117804
+ snippetRe: /<(?:p|div)\b[^>]*\bclass="[^"]*\b(?:str-text|str_info|text-layout)\b[^"]*"[^>]*>([\s\S]*?)<\/(?:p|div)>/,
117805
+ normalizeUrl: (href) => {
117806
+ if (href.startsWith("/link?")) return `https://www.sogou.com${href}`;
117807
+ if (href.includes("sogou.com/sogou?")) return void 0;
117808
+ if (href.startsWith("http://") || href.startsWith("https://")) return href;
117809
+ },
117810
+ botMarkers: ["antispider", "异常访问请求"]
117811
+ };
117812
+ var SogouSearchProvider = class {
117813
+ name = "sogou";
117814
+ fetchImpl;
117815
+ constructor(options = {}) {
117816
+ this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
117817
+ }
117818
+ search(query, options) {
117819
+ return searchEngine(SOGOU, this.fetchImpl, query, options);
117820
+ }
117821
+ };
117822
+ const SO360 = {
117823
+ label: "360 Search",
117824
+ url: (q) => `https://www.so.com/s?q=${encodeURIComponent(q)}`,
117825
+ blockRe: /<li\b[^>]*\bclass="[^"]*\bres-list\b[^"]*"[^>]*>([\s\S]*?)(?=<li\b[^>]*\bclass="[^"]*\bres-list\b|$)/g,
117826
+ titleRe: /<h3[^>]*class="[^"]*\bres-title\b[^"]*"[^>]*>[\s\S]*?<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/,
117827
+ snippetRe: /<p\b[^>]*\bclass="[^"]*\bres-desc\b[^"]*"[^>]*>([\s\S]*?)<\/p>/,
117828
+ botMarkers: ["安全验证"]
117829
+ };
117830
+ var So360SearchProvider = class {
117831
+ name = "360";
117832
+ fetchImpl;
117833
+ constructor(options = {}) {
117834
+ this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
117835
+ }
117836
+ search(query, options) {
117837
+ return searchEngine(SO360, this.fetchImpl, query, options);
117838
+ }
117839
+ };
117840
+ const BAIDU = {
117841
+ label: "Baidu",
117842
+ url: (q) => `https://www.baidu.com/s?wd=${encodeURIComponent(q)}`,
117843
+ blockRe: /<div\b[^>]*\bclass="[^"]*\bc-container\b[^"]*"[^>]*>([\s\S]*?)(?=<div\b[^>]*\bclass="[^"]*\bc-container\b|$)/g,
117844
+ titleRe: /<h3[^>]*class="[^"]*\bt\b[^"]*"[^>]*>[\s\S]*?<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/,
117845
+ snippetRe: /<(?:span|div)\b[^>]*\bclass="[^"]*\b(?:c-abstract|content-right)[^"]*\b[^"]*"[^>]*>([\s\S]*?)<\/(?:span|div)>/,
117846
+ botMarkers: ["百度安全验证", "wappass.baidu.com"]
117847
+ };
117848
+ var BaiduSearchProvider = class {
117849
+ name = "baidu";
117850
+ fetchImpl;
117851
+ constructor(options = {}) {
117852
+ this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
117853
+ }
117854
+ search(query, options) {
117855
+ return searchEngine(BAIDU, this.fetchImpl, query, options);
117856
+ }
117857
+ };
117627
117858
  //#endregion
117628
117859
  //#region ../../packages/agent-core/src/tools/providers/fallback-search.ts
117629
117860
  var FallbackSearchProvider = class {
@@ -117633,94 +117864,31 @@ var FallbackSearchProvider = class {
117633
117864
  this.providers = providers;
117634
117865
  }
117635
117866
  async search(query, options) {
117636
- for (const provider of this.providers) try {
117637
- const results = await provider.search(query, options);
117638
- if (results.length > 0) return results;
117639
- } catch {
117640
- continue;
117641
- }
117642
- return [];
117643
- }
117644
- };
117645
- //#endregion
117646
- //#region ../../packages/agent-core/src/tools/providers/scream-cli-web-search.ts
117647
- var ScreamCliWebSearchProvider = class {
117648
- tokenProvider;
117649
- apiKey;
117650
- baseUrl;
117651
- defaultHeaders;
117652
- customHeaders;
117653
- fetchImpl;
117654
- constructor(options) {
117655
- this.tokenProvider = options.tokenProvider;
117656
- this.apiKey = options.apiKey;
117657
- this.baseUrl = options.baseUrl;
117658
- this.defaultHeaders = options.defaultHeaders ?? {};
117659
- this.customHeaders = options.customHeaders ?? {};
117660
- this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
117661
- }
117662
- async search(query, options) {
117663
- const body = {
117664
- text_query: query,
117665
- limit: options?.limit ?? 5,
117666
- enable_page_crawling: options?.includeContent ?? false,
117667
- timeout_seconds: 30
117668
- };
117669
- const bodyJson = JSON.stringify(body);
117670
- const toolCallId = options?.toolCallId;
117671
- const response = await this.post(bodyJson, toolCallId);
117672
- if (response.status === 401) {
117673
- const detail = await safeReadText(response);
117674
- throw new Error(`ScreamCli search request failed: HTTP 401 (auth/unauthorized). ${detail}`.trim());
117675
- }
117676
- if (response.status !== 200) {
117677
- const detail = await safeReadText(response);
117678
- throw new Error(`ScreamCli search request failed: HTTP ${String(response.status)}. ${detail}`.trim());
117679
- }
117680
- const json = await response.json();
117681
- return (Array.isArray(json.search_results) ? json.search_results : []).map((r) => {
117682
- const out = {
117683
- title: r.title ?? "",
117684
- url: r.url ?? "",
117685
- snippet: r.snippet ?? ""
117686
- };
117687
- if (typeof r.date === "string" && r.date.length > 0) out.date = r.date;
117688
- if (typeof r.content === "string" && r.content.length > 0) out.content = r.content;
117689
- return out;
117690
- });
117691
- }
117692
- async post(bodyJson, toolCallId) {
117693
- const accessToken = await this.resolveApiKey();
117694
- return this.fetchImpl(this.baseUrl, {
117695
- method: "POST",
117696
- headers: {
117697
- ...this.defaultHeaders,
117698
- Authorization: `Bearer ${accessToken}`,
117699
- "Content-Type": "application/json",
117700
- ...toolCallId !== void 0 && toolCallId.length > 0 ? { "X-Msh-Tool-Call-Id": toolCallId } : {},
117701
- ...this.customHeaders
117702
- },
117703
- body: bodyJson
117704
- });
117705
- }
117706
- async resolveApiKey() {
117707
- if (this.tokenProvider !== void 0) try {
117708
- return await this.tokenProvider.getAccessToken();
117709
- } catch (error) {
117710
- if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
117711
- throw error;
117867
+ const failures = [];
117868
+ for (const [index, provider] of this.providers.entries()) {
117869
+ options?.signal?.throwIfAborted();
117870
+ try {
117871
+ const results = await provider.search(query, options);
117872
+ if (results.length > 0) return results;
117873
+ failures.push({
117874
+ provider: provider.name ?? `provider ${String(index + 1)}`,
117875
+ reason: "no results"
117876
+ });
117877
+ } catch (error) {
117878
+ options?.signal?.throwIfAborted();
117879
+ failures.push({
117880
+ provider: provider.name ?? `provider ${String(index + 1)}`,
117881
+ reason: error instanceof Error ? error.message : String(error)
117882
+ });
117883
+ }
117712
117884
  }
117713
- if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
117714
- throw new Error("ScreamCli search service is not configured: missing API key or token provider.");
117885
+ if (failures.every((f) => f.reason === "no results")) return [];
117886
+ const last = failures[failures.length - 1];
117887
+ if (this.providers.length === 1 && last !== void 0) throw new Error(last.reason);
117888
+ const summary = failures.map((f) => `${f.provider}: ${f.reason}`).join("; ");
117889
+ throw new Error(`All web search providers failed — ${summary}`);
117715
117890
  }
117716
117891
  };
117717
- async function safeReadText(response) {
117718
- try {
117719
- return await response.text();
117720
- } catch {
117721
- return "";
117722
- }
117723
- }
117724
117892
  //#endregion
117725
117893
  //#region ../../packages/agent-core/src/session/export/manifest.ts
117726
117894
  const WIRE_PROTOCOL_VERSION = "1.3";
@@ -118910,7 +119078,7 @@ function providerApiKey(provider) {
118910
119078
  case "openai_responses": return providerValue(provider.apiKey, provider.env, "OPENAI_API_KEY");
118911
119079
  case "scream": return providerValue(provider.apiKey, provider.env, "SCREAM_API_KEY");
118912
119080
  case "google-genai": return providerValue(provider.apiKey, provider.env, "GOOGLE_API_KEY");
118913
- case "vertexai": return nonEmptyString$1(provider.apiKey) ?? envValue(provider.env, "VERTEXAI_API_KEY") ?? envValue(provider.env, "GOOGLE_API_KEY");
119081
+ case "vertexai": return nonEmptyString(provider.apiKey) ?? envValue(provider.env, "VERTEXAI_API_KEY") ?? envValue(provider.env, "GOOGLE_API_KEY");
118914
119082
  default: {
118915
119083
  const exhaustive = provider.type;
118916
119084
  throw new ScreamError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
@@ -118927,21 +119095,21 @@ function vertexAILocation(provider) {
118927
119095
  return envValue(provider.env, "GOOGLE_CLOUD_LOCATION") ?? locationFromVertexAIBaseUrl(provider.baseUrl);
118928
119096
  }
118929
119097
  function providerValue(configured, env, envKey) {
118930
- return nonEmptyString$1(configured) ?? envValue(env, envKey);
119098
+ return nonEmptyString(configured) ?? envValue(env, envKey);
118931
119099
  }
118932
119100
  function envValue(env, key) {
118933
- return nonEmptyString$1(env?.[key]);
119101
+ return nonEmptyString(env?.[key]);
118934
119102
  }
118935
- function nonEmptyString$1(value) {
119103
+ function nonEmptyString(value) {
118936
119104
  const trimmed = value?.trim();
118937
119105
  return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
118938
119106
  }
118939
119107
  function locationFromVertexAIBaseUrl(baseUrl) {
118940
- const url = nonEmptyString$1(baseUrl);
119108
+ const url = nonEmptyString(baseUrl);
118941
119109
  if (url === void 0) return void 0;
118942
119110
  try {
118943
119111
  const host = new URL(url).hostname;
118944
- return host.endsWith("-aiplatform.googleapis.com") ? nonEmptyString$1(host.slice(0, -26)) : void 0;
119112
+ return host.endsWith("-aiplatform.googleapis.com") ? nonEmptyString(host.slice(0, -26)) : void 0;
118945
119113
  } catch {
118946
119114
  return;
118947
119115
  }
@@ -120486,7 +120654,6 @@ function waitForSpawn(child) {
120486
120654
  new AsyncLocalStorage();
120487
120655
  //#endregion
120488
120656
  //#region ../../packages/agent-core/src/rpc/core-impl.ts
120489
- const SCREAM_CODE_PROVIDER_NAME = "managed:scream-code";
120490
120657
  var ScreamCore = class {
120491
120658
  rpcClient;
120492
120659
  sdk;
@@ -120967,11 +121134,7 @@ var ScreamCore = class {
120967
121134
  }
120968
121135
  async resolveRuntime(config) {
120969
121136
  if (this.runtime !== void 0) return this.runtime;
120970
- const runtime = await createRuntimeConfig({
120971
- config,
120972
- screamRequestHeaders: this.screamRequestHeaders,
120973
- resolveOAuthTokenProvider: this.resolveOAuthTokenProvider
120974
- });
121137
+ const runtime = await createRuntimeConfig({ config });
120975
121138
  this.runtime = runtime;
120976
121139
  return runtime;
120977
121140
  }
@@ -121028,42 +121191,20 @@ var ScreamCore = class {
121028
121191
  }
121029
121192
  };
121030
121193
  async function createRuntimeConfig(input) {
121031
- const fetchCache = new FetchCache();
121032
- const localFetcher = new LocalFetchURLProvider({ cache: fetchCache });
121033
- const fetchService = input.config.services?.screamCliFetch;
121034
121194
  return {
121035
- urlFetcher: fetchService?.baseUrl === void 0 ? localFetcher : new ScreamCliFetchURLProvider({
121036
- baseUrl: fetchService.baseUrl,
121037
- localFallback: localFetcher,
121038
- defaultHeaders: input.screamRequestHeaders,
121039
- cache: fetchCache,
121040
- ...serviceCredentials(fetchService, input.resolveOAuthTokenProvider)
121041
- }),
121195
+ urlFetcher: new LocalFetchURLProvider({ cache: new FetchCache() }),
121042
121196
  webSearcher: buildWebSearcher(input)
121043
121197
  };
121044
121198
  }
121045
121199
  function buildWebSearcher(input) {
121046
- const searchService = input.config.services?.screamCliSearch;
121047
- const ddgEnabled = input.config.services?.duckduckgo?.enabled !== false;
121048
- const screamProvider = searchService?.baseUrl !== void 0 ? new ScreamCliWebSearchProvider({
121049
- baseUrl: searchService.baseUrl,
121050
- defaultHeaders: input.screamRequestHeaders,
121051
- ...serviceCredentials(searchService, input.resolveOAuthTokenProvider)
121052
- }) : void 0;
121053
- const ddgProvider = ddgEnabled ? new DuckDuckGoSearchProvider() : void 0;
121054
- if (screamProvider && ddgProvider) return new FallbackSearchProvider([screamProvider, ddgProvider]);
121055
- return screamProvider ?? ddgProvider;
121056
- }
121057
- function serviceCredentials(service, resolveOAuthTokenProvider) {
121058
- return {
121059
- apiKey: nonEmptyString(service.apiKey),
121060
- tokenProvider: service.oauth !== void 0 ? resolveOAuthTokenProvider?.(SCREAM_CODE_PROVIDER_NAME, service.oauth) : void 0,
121061
- customHeaders: service.customHeaders
121062
- };
121063
- }
121064
- function nonEmptyString(value) {
121065
- const trimmed = value?.trim();
121066
- return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
121200
+ const services = input.config.services;
121201
+ const providers = [];
121202
+ if (services?.duckduckgo?.enabled !== false) providers.push(new DuckDuckGoSearchProvider());
121203
+ if (services?.sogou?.enabled !== false) providers.push(new SogouSearchProvider());
121204
+ if (services?.so360?.enabled !== false) providers.push(new So360SearchProvider());
121205
+ if (services?.baidu?.enabled !== false) providers.push(new BaiduSearchProvider());
121206
+ if (providers.length === 0) return void 0;
121207
+ return providers.length === 1 ? providers[0] : new FallbackSearchProvider(providers);
121067
121208
  }
121068
121209
  function requiredWorkDir(operation, value) {
121069
121210
  if (typeof value !== "string" || value.trim() === "") throw new ScreamError(ErrorCodes.REQUEST_WORK_DIR_REQUIRED, `${operation} requires workDir`);
@@ -121264,7 +121405,8 @@ var SDKRpcClient = class {
121264
121405
  return (await this.getRpc()).steer({
121265
121406
  sessionId: input.sessionId,
121266
121407
  agentId: this.interactiveAgentId,
121267
- input: input.input
121408
+ input: input.input,
121409
+ interrupt: input.interrupt
121268
121410
  });
121269
121411
  }
121270
121412
  async generateAgentsMd(input) {
@@ -121756,11 +121898,12 @@ var Session = class {
121756
121898
  input: normalizePromptInput(input)
121757
121899
  });
121758
121900
  }
121759
- async steer(input) {
121901
+ async steer(input, opts) {
121760
121902
  this.ensureOpen();
121761
121903
  await this.rpc.steer({
121762
121904
  sessionId: this.id,
121763
- input: normalizePromptInput(input)
121905
+ input: normalizePromptInput(input),
121906
+ interrupt: opts?.interrupt
121764
121907
  });
121765
121908
  }
121766
121909
  async init(targetDir) {
@@ -122527,7 +122670,7 @@ function optionalBuildString(value) {
122527
122670
  return typeof value === "string" && value.length > 0 ? value : void 0;
122528
122671
  }
122529
122672
  const SCREAM_BUILD_INFO = {
122530
- version: optionalBuildString("0.10.1"),
122673
+ version: optionalBuildString("0.10.3"),
122531
122674
  channel: optionalBuildString(""),
122532
122675
  commit: optionalBuildString(""),
122533
122676
  buildTarget: optionalBuildString("darwin-arm64")
@@ -123434,15 +123577,13 @@ const BRAILLE_SPINNER_FRAMES = [
123434
123577
  "⠇",
123435
123578
  "⠏"
123436
123579
  ];
123437
- const MOON_SPINNER_FRAMES = [
123438
- "💬",
123439
- "🗯️",
123440
- "🫯",
123441
- "💭",
123442
- "💬",
123443
- "🗯️",
123444
- "🫯",
123445
- "💭"
123580
+ const PIXEL_PULSE_FRAMES = [
123581
+ "",
123582
+ "",
123583
+ "",
123584
+ "",
123585
+ "",
123586
+ ""
123446
123587
  ];
123447
123588
  const PULSE_WAVE_FRAMES = [
123448
123589
  {
@@ -123877,12 +124018,13 @@ function isManagedUsageProvider(providerKey) {
123877
124018
  //#region src/tui/constant/streaming.ts
123878
124019
  const STREAMING_ARGS_FIELD_RE = /"(path|file_path|command|pattern|query|url|description|title|name)"\s*:\s*"((?:\\.|[^"\\])*)"/g;
123879
124020
  const STREAMING_ARGS_PREVIEW_MAX_CHARS = 8 * 1024;
124021
+ const STREAMING_ARGS_BUFFER_MAX_CHARS = 1024 * 1024;
123880
124022
  //#endregion
123881
124023
  //#region src/tui/utils/event-payload.ts
123882
124024
  function appendStreamingArgsPreview(current, next) {
123883
- const existing = (current ?? "").slice(0, STREAMING_ARGS_PREVIEW_MAX_CHARS);
124025
+ const existing = (current ?? "").slice(0, STREAMING_ARGS_BUFFER_MAX_CHARS);
123884
124026
  if (next === null || next === void 0 || next.length === 0) return existing;
123885
- const remaining = STREAMING_ARGS_PREVIEW_MAX_CHARS - existing.length;
124027
+ const remaining = STREAMING_ARGS_BUFFER_MAX_CHARS - existing.length;
123886
124028
  if (remaining <= 0) return existing;
123887
124029
  return `${existing}${next.slice(0, remaining)}`;
123888
124030
  }
@@ -125808,7 +125950,7 @@ var FooterComponent = class {
125808
125950
  gitCache;
125809
125951
  gitCacheWorkDir;
125810
125952
  transientHint = null;
125811
- shimmerTimer = null;
125953
+ statusTimer = null;
125812
125954
  /**
125813
125955
  * Non-terminal background-task counts split by kind so the footer can
125814
125956
  * render two distinct badges. `bashTasks` covers `bash-*` BPM tasks
@@ -125827,15 +125969,13 @@ var FooterComponent = class {
125827
125969
  this.gitCache = createGitStatusCache(state.workDir, { onChange: this.onGitStatusChange });
125828
125970
  }
125829
125971
  setState(state) {
125830
- const wasThinking = this.state?.streamingPhase === "thinking";
125972
+ const prevPhase = this.state?.streamingPhase;
125831
125973
  if (state.workDir !== this.gitCacheWorkDir) {
125832
125974
  this.gitCacheWorkDir = state.workDir;
125833
125975
  this.gitCache = createGitStatusCache(state.workDir, { onChange: this.onGitStatusChange });
125834
125976
  }
125835
125977
  this.state = state;
125836
- const isThinking = state.streamingPhase === "thinking";
125837
- if (isThinking && !wasThinking) this.#startShimmer();
125838
- else if (!isThinking && wasThinking) this.#stopShimmer();
125978
+ if (state.streamingPhase !== prevPhase) this.#restartStatusTimer(state.streamingPhase);
125839
125979
  }
125840
125980
  setColors(colors) {
125841
125981
  this.colors = colors;
@@ -125860,29 +126000,29 @@ var FooterComponent = class {
125860
126000
  }
125861
126001
  invalidate() {}
125862
126002
  /**
125863
- * Stop the shimmer animation timer. Idempotent — safe to call even when
126003
+ * Stop the status timer. Idempotent — safe to call even when
125864
126004
  * the timer isn't running. Call this when the component is disposed.
125865
126005
  */
125866
126006
  dispose() {
125867
- this.#stopShimmer();
126007
+ this.#stopStatusTimer();
125868
126008
  }
125869
- #startShimmer() {
125870
- if (this.shimmerTimer) return;
125871
- this.shimmerTimer = setInterval(() => {
125872
- this.ui.requestRender();
125873
- }, 1e3 / 30);
126009
+ #restartStatusTimer(phase) {
126010
+ this.#stopStatusTimer();
126011
+ if (phase === "idle") return;
126012
+ const intervalMs = phase === "thinking" ? 1e3 / 30 : SPINNER_TICK_MS;
126013
+ this.statusTimer = setInterval(() => {
126014
+ this.ui.requestComponentRender(this);
126015
+ }, intervalMs);
125874
126016
  }
125875
- #stopShimmer() {
125876
- if (!this.shimmerTimer) return;
125877
- clearInterval(this.shimmerTimer);
125878
- this.shimmerTimer = null;
126017
+ #stopStatusTimer() {
126018
+ if (!this.statusTimer) return;
126019
+ clearInterval(this.statusTimer);
126020
+ this.statusTimer = null;
125879
126021
  }
125880
126022
  render(width) {
125881
126023
  const colors = this.colors;
125882
126024
  const state = this.state;
125883
126025
  const left = [];
125884
- if (state.permissionMode === "auto") left.push(chalk.hex(colors.warning).bold(t("badge.auto")));
125885
- if (state.permissionMode === "yolo") left.push(chalk.hex(colors.warning).bold(t("badge.yes")));
125886
126026
  if (state.planMode !== "off") {
125887
126027
  const isFusion = state.planMode === "fusionplan";
125888
126028
  left.push(chalk.hex(isFusion ? colors.fusionPlanMode : colors.planMode).bold(isFusion ? t("badge.fusion") : t("badge.plan")));
@@ -126214,6 +126354,21 @@ const lightColors = {
126214
126354
  function getColorPalette(theme) {
126215
126355
  return theme === "dark" ? darkColors : lightColors;
126216
126356
  }
126357
+ /**
126358
+ * True when a hex background is light enough to need dark text on top
126359
+ * (relative luminance above 0.5). Shared by badge/tag renderers so a
126360
+ * fluorescent-green block never gets unreadable white text.
126361
+ */
126362
+ function isLightBgHex(hex) {
126363
+ const r = parseInt(hex.slice(1, 3), 16);
126364
+ const g = parseInt(hex.slice(3, 5), 16);
126365
+ const b = parseInt(hex.slice(5, 7), 16);
126366
+ return (.299 * r + .587 * g + .114 * b) / 255 > .5;
126367
+ }
126368
+ /** Foreground colour with readable contrast on the given hex background. */
126369
+ function contrastTextHex(bgHex) {
126370
+ return isLightBgHex(bgHex) ? "#000000" : "#FFFFFF";
126371
+ }
126217
126372
  //#endregion
126218
126373
  //#region src/tui/theme/styles.ts
126219
126374
  /**
@@ -126766,7 +126921,6 @@ async function handleYoloCommand(host, args) {
126766
126921
  }
126767
126922
  await session.setPermission("yolo");
126768
126923
  host.setAppState({ permissionMode: "yolo" });
126769
- host.showNotice(t("config.yolo_on"), t("config.yolo_on_desc"));
126770
126924
  return;
126771
126925
  }
126772
126926
  if (subcmd === "off") {
@@ -126776,17 +126930,14 @@ async function handleYoloCommand(host, args) {
126776
126930
  }
126777
126931
  await session.setPermission("manual");
126778
126932
  host.setAppState({ permissionMode: "manual" });
126779
- host.showNotice(t("config.yolo_off"));
126780
126933
  return;
126781
126934
  }
126782
126935
  if (currentMode === "yolo") {
126783
126936
  await session.setPermission("manual");
126784
126937
  host.setAppState({ permissionMode: "manual" });
126785
- host.showNotice(t("config.yolo_off"));
126786
126938
  } else {
126787
126939
  await session.setPermission("yolo");
126788
126940
  host.setAppState({ permissionMode: "yolo" });
126789
- host.showNotice(t("config.yolo_toggle_on"), t("config.yolo_toggle_on_desc"));
126790
126941
  }
126791
126942
  }
126792
126943
  async function handleAutoCommand(host, args) {
@@ -126804,7 +126955,6 @@ async function handleAutoCommand(host, args) {
126804
126955
  }
126805
126956
  await session.setPermission("auto");
126806
126957
  host.setAppState({ permissionMode: "auto" });
126807
- host.showNotice(t("config.auto_on"), t("config.auto_on_desc"));
126808
126958
  return;
126809
126959
  }
126810
126960
  if (subcmd === "off") {
@@ -126814,17 +126964,14 @@ async function handleAutoCommand(host, args) {
126814
126964
  }
126815
126965
  await session.setPermission("manual");
126816
126966
  host.setAppState({ permissionMode: "manual" });
126817
- host.showNotice(t("config.auto_off"));
126818
126967
  return;
126819
126968
  }
126820
126969
  if (currentMode === "auto") {
126821
126970
  await session.setPermission("manual");
126822
126971
  host.setAppState({ permissionMode: "manual" });
126823
- host.showNotice(t("config.auto_off"));
126824
126972
  } else {
126825
126973
  await session.setPermission("auto");
126826
126974
  host.setAppState({ permissionMode: "auto" });
126827
- host.showNotice(t("config.auto_toggle_on"), t("config.auto_toggle_on_desc"));
126828
126975
  }
126829
126976
  }
126830
126977
  async function handleWolfpackCommand(host, args) {
@@ -127827,7 +127974,7 @@ async function createGoal(host, parsed) {
127827
127974
  await showGoalConfigWizard(host, session, parsed.objective, parsed.replace);
127828
127975
  }
127829
127976
  async function showGoalConfigWizard(host, session, objective, replace) {
127830
- const { TextInputDialogComponent } = await import("./text-input-dialog-Dk5xzj4F.mjs");
127977
+ const { TextInputDialogComponent } = await import("./text-input-dialog-C8_8qYYi.mjs");
127831
127978
  const turnInput = await promptNumber(host, TextInputDialogComponent, {
127832
127979
  title: t("goal.wizard_title", { objective }),
127833
127980
  subtitle: t("goal.budget_turns_hint"),
@@ -128576,7 +128723,7 @@ var AssistantMessageComponent = class {
128576
128723
  const age = Date.now() - (this.fadeStartMs ?? 0);
128577
128724
  this.cachedWidth = void 0;
128578
128725
  this.cachedLines = void 0;
128579
- this.ui?.requestRender();
128726
+ this.ui?.requestComponentRender(this);
128580
128727
  if (age >= 1200) this.stopFade();
128581
128728
  }, FADE_TICK_MS);
128582
128729
  this.ui.requestRender();
@@ -129057,7 +129204,7 @@ var ThinkingComponent = class {
129057
129204
  if (this.ui === void 0 || this.spinnerInterval !== void 0) return;
129058
129205
  this.spinnerInterval = setInterval(() => {
129059
129206
  this.spinnerFrame = (this.spinnerFrame + 1) % BRAILLE_SPINNER_FRAMES.length;
129060
- this.ui?.requestRender();
129207
+ this.ui?.requestComponentRender(this);
129061
129208
  }, 80);
129062
129209
  }
129063
129210
  stopSpinner() {
@@ -130244,23 +130391,20 @@ function unescapeJsonString(s) {
130244
130391
  });
130245
130392
  }
130246
130393
  /**
130247
- * Pull the live value of a JSON string field out of partially-streamed
130248
- * arguments, even if the closing quote hasn't arrived yet. Handles the
130249
- * common JSON string escapes so `\n` in a streamed `content` becomes a
130250
- * real newline we can highlight. Returns `undefined` if the field hasn't
130251
- * started streaming yet.
130394
+ * Scan a JSON string value starting at `start` (just after the opening
130395
+ * quote), unescaping into plain text. Stops at the closing quote or at
130396
+ * `end`, tolerating truncation (an incomplete escape at the boundary
130397
+ * simply ends the scan). Shared by the partial-args extractor and the
130398
+ * Write streaming tail preview.
130252
130399
  */
130253
- function extractPartialStringField(text, key) {
130254
- const match = new RegExp(`"${key}"\\s*:\\s*"`).exec(text);
130255
- if (match === null) return void 0;
130256
- const start = match.index + match[0].length;
130400
+ function unescapeJsonStringValue(text, start, end = text.length) {
130257
130401
  let out = "";
130258
130402
  let i = start;
130259
- while (i < text.length) {
130403
+ while (i < end) {
130260
130404
  const ch = text[i];
130261
130405
  if (ch === "\\") {
130262
130406
  const next = text[i + 1];
130263
- if (next === void 0) return out;
130407
+ if (next === void 0 || i + 1 >= end) return out;
130264
130408
  switch (next) {
130265
130409
  case "n":
130266
130410
  out += "\n";
@@ -130287,7 +130431,7 @@ function extractPartialStringField(text, key) {
130287
130431
  out += "/";
130288
130432
  break;
130289
130433
  case "u": {
130290
- if (i + 5 >= text.length) return out;
130434
+ if (i + 5 >= end) return out;
130291
130435
  const hex = text.slice(i + 2, i + 6);
130292
130436
  const code = Number.parseInt(hex, 16);
130293
130437
  if (Number.isNaN(code)) return out;
@@ -130306,6 +130450,16 @@ function extractPartialStringField(text, key) {
130306
130450
  }
130307
130451
  return out;
130308
130452
  }
130453
+ /**
130454
+ * Pull the live value of a JSON string field out of partially-streamed
130455
+ * arguments, even if the closing quote hasn't arrived yet. Returns
130456
+ * `undefined` if the field hasn't started streaming yet.
130457
+ */
130458
+ function extractPartialStringField(text, key) {
130459
+ const match = new RegExp(`"${key}"\\s*:\\s*"`).exec(text);
130460
+ if (match === null) return void 0;
130461
+ return unescapeJsonStringValue(text, match.index + match[0].length);
130462
+ }
130309
130463
  function parseArgsPreview(value) {
130310
130464
  const previewText = value.slice(0, STREAMING_ARGS_PREVIEW_MAX_CHARS);
130311
130465
  if (previewText.trim().length === 0) return {};
@@ -130448,6 +130602,11 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
130448
130602
  subagentEndedAtMs;
130449
130603
  progressLines = [];
130450
130604
  static MAX_PROGRESS_LINES = 24;
130605
+ writeStreamContentStart = -1;
130606
+ writeStreamNlScanOffset = 0;
130607
+ writeStreamNlCount = 0;
130608
+ writeStreamLang;
130609
+ static WRITE_STREAM_TAIL_RAW_CHARS = 4096;
130451
130610
  /**
130452
130611
  * Registered by a group container (`AgentGroupComponent` or
130453
130612
  * `ReadGroupComponent`) when this component is borrowed as a hidden state
@@ -131217,21 +131376,12 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
131217
131376
  const name = this.toolCall.name;
131218
131377
  const previewText = streamText.slice(0, STREAMING_ARGS_PREVIEW_MAX_CHARS);
131219
131378
  if (name === "Write") {
131220
- const content = extractPartialStringField(previewText, "content");
131221
- if (content === void 0 || content.length === 0) return;
131222
- const allLines = highlightLines(content, langFromPath(extractPartialStringField(previewText, "file_path") ?? extractPartialStringField(previewText, "path") ?? ""));
131223
- const maxLines = 10;
131224
- const scrollLines = allLines.length > maxLines ? allLines.slice(allLines.length - maxLines) : allLines;
131225
- for (const [i, line] of scrollLines.entries()) {
131226
- const originalLineNumber = allLines.length > maxLines ? allLines.length - maxLines + i : i;
131227
- const lineNum = chalk.dim(String(originalLineNumber + 1).padStart(4) + " ");
131228
- this.addChild(new Text(lineNum + line, 2, 0));
131229
- }
131379
+ this.buildWriteStreamingPreview(streamText);
131230
131380
  return;
131231
131381
  }
131232
131382
  if (name === "Edit") {
131233
131383
  const filePath = extractPartialStringField(previewText, "file_path") ?? extractPartialStringField(previewText, "path") ?? "";
131234
- const bytes = Buffer.byteLength(previewText, "utf8");
131384
+ const bytes = Buffer.byteLength(streamText, "utf8");
131235
131385
  const startedAtMs = this.toolCall.streamingStartedAtMs;
131236
131386
  const elapsedSeconds = startedAtMs === void 0 ? 0 : Math.max(0, Math.floor((Date.now() - startedAtMs) / 1e3));
131237
131387
  const progress = t("toolcall.preparing_changes", {
@@ -131253,6 +131403,54 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
131253
131403
  }));
131254
131404
  }
131255
131405
  }
131406
+ /**
131407
+ * Live Write preview: renders the last COMMAND_PREVIEW_LINES lines of the
131408
+ * file being written, reading from the TAIL of the accumulating args JSON
131409
+ * so the window keeps scrolling for arbitrarily large files (the old
131410
+ * head-bounded preview visibly froze once args passed 8KB). Line numbers
131411
+ * stay exact via an incremental newline counter. Per-frame cost is
131412
+ * O(delta + tail), independent of total file size.
131413
+ */
131414
+ buildWriteStreamingPreview(streamText) {
131415
+ if (streamText.length < this.writeStreamNlScanOffset) {
131416
+ this.writeStreamContentStart = -1;
131417
+ this.writeStreamLang = void 0;
131418
+ }
131419
+ if (this.writeStreamContentStart < 0) {
131420
+ const opener = /"content"\s*:\s*"/.exec(streamText);
131421
+ if (opener === null) return;
131422
+ this.writeStreamContentStart = opener.index + opener[0].length;
131423
+ this.writeStreamNlScanOffset = this.writeStreamContentStart;
131424
+ this.writeStreamNlCount = 0;
131425
+ const filePath = extractPartialStringField(streamText, "file_path") ?? extractPartialStringField(streamText, "path") ?? "";
131426
+ this.writeStreamLang = langFromPath(filePath);
131427
+ }
131428
+ const contentStart = this.writeStreamContentStart;
131429
+ const tailStart = Math.max(contentStart, streamText.length - ToolCallComponent.WRITE_STREAM_TAIL_RAW_CHARS);
131430
+ while (this.writeStreamNlScanOffset < tailStart) {
131431
+ const idx = streamText.indexOf("\\n", this.writeStreamNlScanOffset);
131432
+ if (idx === -1 || idx >= tailStart) break;
131433
+ this.writeStreamNlCount++;
131434
+ this.writeStreamNlScanOffset = idx + 2;
131435
+ }
131436
+ let fragmentStart = tailStart;
131437
+ if (tailStart > contentStart) {
131438
+ const firstNl = streamText.indexOf("\\n", tailStart);
131439
+ if (firstNl === -1) return;
131440
+ fragmentStart = firstNl + 2;
131441
+ }
131442
+ const fragmentEnd = Math.min(streamText.length, fragmentStart + ToolCallComponent.WRITE_STREAM_TAIL_RAW_CHARS);
131443
+ const fragment = unescapeJsonStringValue(streamText, fragmentStart, fragmentEnd);
131444
+ if (fragment.length === 0) return;
131445
+ const lines = highlightLines(fragment, this.writeStreamLang);
131446
+ const displayLines = lines.slice(-10);
131447
+ const firstFragmentLineNo = this.writeStreamNlCount + (tailStart > contentStart ? 2 : 1);
131448
+ const skipped = lines.length - displayLines.length;
131449
+ for (const [i, line] of displayLines.entries()) {
131450
+ const lineNum = chalk.dim(String(firstFragmentLineNo + skipped + i).padStart(4) + " ");
131451
+ this.addChild(new Text(lineNum + line, 2, 0));
131452
+ }
131453
+ }
131256
131454
  buildPlanPreview() {
131257
131455
  const plan = this.resolvePlanForPreview();
131258
131456
  if (plan.length === 0) return;
@@ -133194,15 +133392,11 @@ var MoonLoader = class extends Text {
133194
133392
  currentFrame = 0;
133195
133393
  intervalId = null;
133196
133394
  ui;
133197
- frames;
133198
- interval;
133199
133395
  colorFn;
133200
133396
  label;
133201
- constructor(ui, style = "moon", colorFn, label = "") {
133397
+ constructor(ui, colorFn, label = "") {
133202
133398
  super("", 1, 0);
133203
133399
  this.ui = ui;
133204
- this.frames = style === "moon" ? [...MOON_SPINNER_FRAMES] : [...BRAILLE_SPINNER_FRAMES];
133205
- this.interval = style === "moon" ? 120 : 80;
133206
133400
  this.colorFn = colorFn;
133207
133401
  this.label = label;
133208
133402
  this.start();
@@ -133210,9 +133404,9 @@ var MoonLoader = class extends Text {
133210
133404
  start() {
133211
133405
  this.updateDisplay();
133212
133406
  this.intervalId = setInterval(() => {
133213
- this.currentFrame = (this.currentFrame + 1) % this.frames.length;
133407
+ this.currentFrame = (this.currentFrame + 1) % BRAILLE_SPINNER_FRAMES.length;
133214
133408
  this.updateDisplay();
133215
- }, this.interval);
133409
+ }, 80);
133216
133410
  }
133217
133411
  stop() {
133218
133412
  if (this.intervalId) {
@@ -133229,10 +133423,10 @@ var MoonLoader = class extends Text {
133229
133423
  this.updateDisplay();
133230
133424
  }
133231
133425
  updateDisplay() {
133232
- const frame = this.frames[this.currentFrame];
133426
+ const frame = BRAILLE_SPINNER_FRAMES[this.currentFrame];
133233
133427
  const coloredFrame = this.colorFn ? this.colorFn(frame) : frame;
133234
133428
  this.setText(this.label ? `${coloredFrame} ${this.label}` : coloredFrame);
133235
- this.ui.requestRender();
133429
+ this.ui.requestComponentRender(this);
133236
133430
  }
133237
133431
  };
133238
133432
  //#endregion
@@ -133438,7 +133632,7 @@ var SkillCenterLoadingComponent = class extends Container {
133438
133632
  this.label = label;
133439
133633
  this.host = host;
133440
133634
  const tint = (s) => chalk.hex(host.state.theme.colors.primary)(s);
133441
- this.loader = new MoonLoader(host.state.ui, "braille", tint, this.label);
133635
+ this.loader = new MoonLoader(host.state.ui, tint, this.label);
133442
133636
  this.addChild(new Spacer(1));
133443
133637
  this.addChild(this.loader);
133444
133638
  }
@@ -137023,6 +137217,16 @@ var EditorKeyboardController = class {
137023
137217
  host.restoreEditor();
137024
137218
  return;
137025
137219
  }
137220
+ if (host.state.queuedMessages.length > 0) {
137221
+ const restored = host.state.queuedMessages.map((m) => m.text.trim()).filter((text) => text.length > 0);
137222
+ host.clearQueuedMessages();
137223
+ const existing = host.state.editor.getText();
137224
+ const parts = [...restored, existing].filter((s) => s.trim().length > 0);
137225
+ host.state.editor.setText(parts.join("\n"));
137226
+ host.updateQueueDisplay();
137227
+ host.state.ui.requestRender();
137228
+ return;
137229
+ }
137026
137230
  if (host.state.appState.isCompacting) {
137027
137231
  this.cancelCurrentCompaction();
137028
137232
  return;
@@ -137817,6 +138021,7 @@ var SessionEventHandler = class {
137817
138021
  handleStepCompleted(event) {
137818
138022
  this.host.streamingUI.flushNow();
137819
138023
  this.maybeShowDebugTiming(event);
138024
+ this.drainQueuedMessagesIntoSteer();
137820
138025
  if (event.finishReason !== "max_tokens") return;
137821
138026
  const title = this.host.streamingUI.markStepTruncated(String(event.turnId), event.step) > 0 ? t("handler.max_tokens_truncated") : t("handler.max_tokens_no_tool");
137822
138027
  const detail = this.isAnthropicSessionActive() ? t("handler.max_tokens_hint") : void 0;
@@ -137827,6 +138032,40 @@ var SessionEventHandler = class {
137827
138032
  const text = formatStepDebugTiming(event);
137828
138033
  if (text !== void 0) this.host.showStatus(text);
137829
138034
  }
138035
+ /**
138036
+ * Auto-insert queued messages at the step boundary. The current step has
138037
+ * finished, so queued input is steered in with interrupt: false — it
138038
+ * never kills in-flight tools; it reaches the model at the next call.
138039
+ * Race-safe by design: if the turn already ended between the event and
138040
+ * the steer call, the core launches a fresh turn with the message.
138041
+ * (Ctrl+S remains the immediate-interrupt path.)
138042
+ *
138043
+ * Skipped while `deferUserMessages` is set: /init and make-skill run
138044
+ * system-triggered flows whose steps must not receive user input.
138045
+ */
138046
+ drainQueuedMessagesIntoSteer() {
138047
+ const { state } = this.host;
138048
+ const session = this.host.session;
138049
+ if (session === void 0 || state.appState.isCompacting || this.host.deferUserMessages) return;
138050
+ const items = state.queuedMessages;
138051
+ if (items.length === 0) return;
138052
+ state.queuedMessages = [];
138053
+ this.host.updateQueueDisplay();
138054
+ for (const item of items) {
138055
+ this.host.appendTranscriptEntry({
138056
+ id: nextTranscriptId(),
138057
+ kind: "user",
138058
+ turnId: this.host.streamingUI.getTurnContext().turnId,
138059
+ renderMode: "plain",
138060
+ content: item.text,
138061
+ ...item.imageAttachmentIds !== void 0 && item.imageAttachmentIds.length > 0 ? { imageAttachmentIds: item.imageAttachmentIds } : {}
138062
+ });
138063
+ session.steer(item.parts ?? item.text, { interrupt: false }).catch((error) => {
138064
+ const message = formatErrorMessage(error);
138065
+ this.host.showError(t("input.guide_failed", { message }));
138066
+ });
138067
+ }
138068
+ }
137830
138069
  isAnthropicSessionActive() {
137831
138070
  const { state } = this.host;
137832
138071
  const providerKey = state.appState.availableModels[state.appState.model]?.provider;
@@ -138009,7 +138248,7 @@ var SessionEventHandler = class {
138009
138248
  return;
138010
138249
  }
138011
138250
  const tint = (s) => chalk.hex(state.theme.colors.textMuted)(s);
138012
- const spinner = new MoonLoader(state.ui, "braille", tint, label);
138251
+ const spinner = new MoonLoader(state.ui, tint, label);
138013
138252
  state.transcriptContainer.addChild(spinner);
138014
138253
  this.mcpServerStatusSpinners.set(name, spinner);
138015
138254
  state.ui.requestRender();
@@ -141148,7 +141387,7 @@ var TranscriptController = class TranscriptController {
141148
141387
  }
141149
141388
  showProgressSpinner(label) {
141150
141389
  const tint = (s) => chalk.hex(this.host.state.theme.colors.primary)(s);
141151
- const spinner = new MoonLoader(this.host.state.ui, "braille", tint, label);
141390
+ const spinner = new MoonLoader(this.host.state.ui, tint, label);
141152
141391
  const spacer = new Spacer(1);
141153
141392
  const container = this.host.state.transcriptContainer;
141154
141393
  container.addChild(spacer);
@@ -141506,7 +141745,7 @@ var PulseWaveLoader = class extends Text {
141506
141745
  2
141507
141746
  ].map((idx) => this.renderCell(idx, step.active, step.forward));
141508
141747
  this.setText(cells.join(" "));
141509
- this.ui.requestRender();
141748
+ this.ui.requestComponentRender(this);
141510
141749
  }
141511
141750
  renderCell(index, active, forward) {
141512
141751
  const distance = forward ? active - index : index - active;
@@ -141763,7 +142002,7 @@ var LifecycleController = class LifecycleController {
141763
142002
  this.stopPulseWave();
141764
142003
  break;
141765
142004
  case "composing": {
141766
- const spinner = this.ensureActivitySpinner("braille", "working...", (s) => chalk.hex(state.theme.colors.primary)(s));
142005
+ const spinner = this.ensureActivitySpinner("working...", (s) => chalk.hex(state.theme.colors.primary)(s));
141767
142006
  state.activityContainer.addChild(new ActivityPaneComponent({
141768
142007
  mode: "composing",
141769
142008
  spinner
@@ -141805,14 +142044,10 @@ var LifecycleController = class LifecycleController {
141805
142044
  this.host.state.terminal.setProgress(active);
141806
142045
  this.host.state.terminalState.progressActive = active;
141807
142046
  }
141808
- ensureActivitySpinner(style, label = "", colorFn) {
141809
- if (this.host.state.activitySpinner?.style !== style) this.stopActivitySpinner();
142047
+ ensureActivitySpinner(label = "", colorFn) {
141810
142048
  if (this.host.state.activitySpinner === null) {
141811
- const instance = new MoonLoader(this.host.state.ui, style, colorFn, label);
141812
- this.host.state.activitySpinner = {
141813
- instance,
141814
- style
141815
- };
142049
+ const instance = new MoonLoader(this.host.state.ui, colorFn, label);
142050
+ this.host.state.activitySpinner = { instance };
141816
142051
  return instance;
141817
142052
  }
141818
142053
  this.host.state.activitySpinner.instance.setLabel(label);
@@ -143105,6 +143340,8 @@ var CustomEditor = class extends Editor {
143105
143340
  thinking = false;
143106
143341
  /** Current thinking effort level (e.g. low, medium, high). Used to annotate the think label. */
143107
143342
  thinkingLevel = "off";
143343
+ /** Current permission mode — always shown as a badge at the top-left of the input box border. */
143344
+ permissionMode = "manual";
143108
143345
  /** Current border colour hex — kept in sync with borderColor by the host. */
143109
143346
  borderHex = "";
143110
143347
  consumingPaste = false;
@@ -143165,7 +143402,13 @@ var CustomEditor = class extends Editor {
143165
143402
  const withPrompt = injectPromptSymbol(firstContent);
143166
143403
  if (withPrompt !== void 0) lines[firstContentIdx] = withPrompt;
143167
143404
  }
143168
- if (this.thinking) injectThinkLabel(lines, width, this.thinkingLevel, this.borderColor ?? ((s) => s), this.borderHex);
143405
+ injectBorderBadges(lines, width, {
143406
+ mode: this.permissionMode,
143407
+ thinking: this.thinking,
143408
+ thinkingLevel: this.thinkingLevel,
143409
+ paint: this.borderColor ?? ((s) => s),
143410
+ borderHex: this.borderHex
143411
+ });
143169
143412
  return lines;
143170
143413
  }
143171
143414
  handleInput(data) {
@@ -143298,34 +143541,47 @@ function injectPromptSymbol(line) {
143298
143541
  return "> " + line.slice(2);
143299
143542
  }
143300
143543
  const THINK_LABEL_MIN_WIDTH = 14;
143544
+ const MODE_BADGE_MIN_WIDTH = 10;
143301
143545
  function isLightBg(hex) {
143302
143546
  const r = parseInt(hex.slice(1, 3), 16);
143303
143547
  const g = parseInt(hex.slice(3, 5), 16);
143304
143548
  const b = parseInt(hex.slice(5, 7), 16);
143305
143549
  return (.299 * r + .587 * g + .114 * b) / 255 > .5;
143306
143550
  }
143551
+ function makeBadge(label, bgHex) {
143552
+ return bgHex ? chalk.bgHex(bgHex).hex(isLightBg(bgHex) ? "#000000" : "#FFFFFF")(label) : chalk.bgBlack.white(label);
143553
+ }
143307
143554
  /**
143308
- * Embed a small "think" badge into the top border line of the editor.
143309
- * The label only appears when the active model has thinking enabled.
143310
- * Works with pi-tui's flat two-line design (no side borders).
143311
- *
143312
- * The badge uses the border colour as background with black text for
143313
- * maximum contrast against the bright border colours (yellow-green,
143314
- * cyan, amber) used across themes.
143555
+ * Compose the editor's top border line with up to two badges: the
143556
+ * permission mode badge at the left (always shown) and the think badge at
143557
+ * the right (only when thinking is enabled). Each badge is a solid colour
143558
+ * block with black/white text picked by background luminance. When the
143559
+ * editor is scrolled, pi-tui's `↑ N more` indicator keeps the line — it
143560
+ * signals hidden content, which matters more than the badges. On narrow
143561
+ * terminals the think badge drops first, then the mode badge.
143315
143562
  */
143316
- function injectThinkLabel(lines, width, thinkingLevel, paint, borderHex) {
143317
- if (width < THINK_LABEL_MIN_WIDTH) return;
143563
+ function injectBorderBadges(lines, width, opts) {
143318
143564
  const topIdx = lines.findIndex((line) => {
143319
143565
  const plain = stripSgr(line);
143320
143566
  return plain.length > 0 && plain[0] === "─";
143321
143567
  });
143322
143568
  if (topIdx === -1) return;
143323
- const label = thinkingLevel !== "off" ? ` Think ${thinkingLevel} ` : " Think ";
143324
- const badge = borderHex ? chalk.bgHex(borderHex).hex(isLightBg(borderHex) ? "#000000" : "#FFFFFF")(label) : chalk.bgBlack.white(label);
143325
- const badgeVis = visibleWidth(badge);
143326
- const leftDashCount = width - 1 - badgeVis;
143327
- if (leftDashCount < 1) return;
143328
- lines[topIdx] = paint("─".repeat(leftDashCount)) + badge + paint("─");
143569
+ if (stripSgr(lines[topIdx]).includes("")) return;
143570
+ const { paint } = opts;
143571
+ let left = "";
143572
+ let right = paint("─");
143573
+ if (width >= MODE_BADGE_MIN_WIDTH) {
143574
+ const badgeText = ` ${opts.mode} `;
143575
+ left = paint("──") + (opts.borderHex ? chalk.hex(opts.borderHex).bold(badgeText) : paint(badgeText));
143576
+ }
143577
+ if (opts.thinking && width >= THINK_LABEL_MIN_WIDTH) right = makeBadge(opts.thinkingLevel !== "off" ? ` Think ${opts.thinkingLevel} ` : " Think ", opts.borderHex) + paint("─");
143578
+ const fill = width - visibleWidth(left) - visibleWidth(right);
143579
+ if (fill < 0) {
143580
+ const modeOnlyFill = width - visibleWidth(left) - 1;
143581
+ lines[topIdx] = modeOnlyFill >= 0 ? left + paint("─".repeat(modeOnlyFill)) + paint("─") : paint("─".repeat(width));
143582
+ return;
143583
+ }
143584
+ lines[topIdx] = left + paint("─".repeat(fill)) + right;
143329
143585
  }
143330
143586
  //#endregion
143331
143587
  //#region src/tui/utils/terminal-state.ts
@@ -143566,6 +143822,7 @@ function createTUIState(options) {
143566
143822
  const editor = new CustomEditor(ui, theme.colors);
143567
143823
  editor.thinking = initialAppState.thinkingLevel !== "off";
143568
143824
  editor.thinkingLevel = initialAppState.thinkingLevel;
143825
+ editor.permissionMode = initialAppState.permissionMode ?? "manual";
143569
143826
  return {
143570
143827
  ui,
143571
143828
  terminal,
@@ -144406,7 +144663,10 @@ var ApprovalPanelComponent = class extends Container {
144406
144663
  onToggleToolOutput;
144407
144664
  onTogglePlanExpand;
144408
144665
  onOpenPreview;
144409
- constructor(request, onResponse, colors, onToggleToolOutput, onTogglePlanExpand, onOpenPreview) {
144666
+ ui;
144667
+ animFrame = 0;
144668
+ intervalId = null;
144669
+ constructor(request, onResponse, colors, onToggleToolOutput, onTogglePlanExpand, onOpenPreview, ui) {
144410
144670
  super();
144411
144671
  this.request = request;
144412
144672
  this.onResponse = onResponse;
@@ -144414,6 +144674,7 @@ var ApprovalPanelComponent = class extends Container {
144414
144674
  this.onToggleToolOutput = onToggleToolOutput;
144415
144675
  this.onTogglePlanExpand = onTogglePlanExpand;
144416
144676
  this.onOpenPreview = onOpenPreview;
144677
+ this.ui = ui;
144417
144678
  this.feedbackInput.onSubmit = (value) => {
144418
144679
  this.submit(this.selectedIndex, value);
144419
144680
  };
@@ -144421,10 +144682,22 @@ var ApprovalPanelComponent = class extends Container {
144421
144682
  this.feedbackMode = false;
144422
144683
  this.feedbackInput.setValue("");
144423
144684
  };
144685
+ if (this.ui !== void 0) this.intervalId = setInterval(() => {
144686
+ this.animFrame = (this.animFrame + 1) % PIXEL_PULSE_FRAMES.length;
144687
+ this.ui?.requestComponentRender(this);
144688
+ }, 100);
144689
+ }
144690
+ /** Stop the selection pulse. Idempotent; called on submit and on hide. */
144691
+ stop() {
144692
+ if (this.intervalId !== null) {
144693
+ clearInterval(this.intervalId);
144694
+ this.intervalId = null;
144695
+ }
144424
144696
  }
144425
144697
  submit(index, feedback = "") {
144426
144698
  const option = this.choiceAt(index);
144427
144699
  if (!option) return;
144700
+ this.stop();
144428
144701
  this.onResponse({
144429
144702
  response: option.response,
144430
144703
  feedback: feedback || void 0,
@@ -144441,6 +144714,7 @@ var ApprovalPanelComponent = class extends Container {
144441
144714
  }
144442
144715
  handleInput(data) {
144443
144716
  if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c")) || matchesKey(data, Key.ctrl("d"))) {
144717
+ this.stop();
144444
144718
  this.onResponse({ response: "rejected" });
144445
144719
  return;
144446
144720
  }
@@ -144491,11 +144765,9 @@ var ApprovalPanelComponent = class extends Container {
144491
144765
  this.feedbackInput.focused = this.focused && this.feedbackMode;
144492
144766
  const { data } = this.request;
144493
144767
  const blockStyles = makeBlockStyles(this.colors);
144494
- const borderColor = chalk.hex(this.colors.borderFocus);
144495
- const borderColorBold = chalk.bold.hex(this.colors.borderFocus);
144496
- const selectColorBold = chalk.bold.hex(this.colors.accent);
144768
+ const borderColor = chalk.hex(this.colors.primary);
144769
+ const borderColorBold = chalk.bold.hex(this.colors.primary);
144497
144770
  const dim = chalk.hex(this.colors.textDim);
144498
- const strong = chalk.hex(this.colors.textStrong);
144499
144771
  const horizontalBar = borderColor("─".repeat(width));
144500
144772
  const indent = (s) => ` ${s}`;
144501
144773
  const title = headerFor(data.tool_name);
@@ -144520,8 +144792,11 @@ var ApprovalPanelComponent = class extends Container {
144520
144792
  const num = idx + 1;
144521
144793
  const labelWithNum = `${String(num)}. ${option.label}`;
144522
144794
  if (this.feedbackMode && option.requires_feedback === true && isSelected) lines.push(indent(this.renderInlineFeedbackLine(width - 2, labelWithNum)));
144523
- else if (isSelected) lines.push(indent(`${selectColorBold("▶")} ${selectColorBold(labelWithNum)}`));
144524
- else lines.push(indent(strong(` ${labelWithNum}`)));
144795
+ else if (isSelected) {
144796
+ const indicator = chalk.hex(this.colors.primary)(PIXEL_PULSE_FRAMES[this.animFrame]);
144797
+ const selectedLabel = chalk.bold.hex(this.colors.primary)(labelWithNum);
144798
+ lines.push(indent(`${indicator} ${selectedLabel}`));
144799
+ } else lines.push(indent(dim(` ${labelWithNum}`)));
144525
144800
  }
144526
144801
  lines.push("");
144527
144802
  if (this.feedbackMode) lines.push(indent(dim(t("approval.feedback_hint"))));
@@ -144550,8 +144825,7 @@ var ApprovalPanelComponent = class extends Container {
144550
144825
  if (this.selectedIndex < 0 || this.selectedIndex >= count) this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, count - 1));
144551
144826
  }
144552
144827
  renderInlineFeedbackLine(width, labelWithNum) {
144553
- const selectColorBold = chalk.bold.hex(this.colors.accent);
144554
- const prefix = `${selectColorBold("▶")} ${selectColorBold(labelWithNum)} `;
144828
+ const prefix = `${chalk.hex(this.colors.primary)(PIXEL_PULSE_FRAMES[this.animFrame])} ${chalk.bold.hex(this.colors.primary)(labelWithNum)} `;
144555
144829
  const inputWidth = Math.max(4, width - visibleWidth(prefix) + 2);
144556
144830
  const inputLine = this.feedbackInput.render(inputWidth)[0] ?? "> ";
144557
144831
  return prefix + (inputLine.startsWith("> ") ? inputLine.slice(2) : inputLine);
@@ -145522,7 +145796,10 @@ var QuestionDialogComponent = class extends Container {
145522
145796
  answers;
145523
145797
  onToggleToolOutput;
145524
145798
  onTogglePlanExpand;
145525
- constructor(request, onAnswer, colors, maxVisibleOptions = 6, onToggleToolOutput, onTogglePlanExpand) {
145799
+ ui;
145800
+ animFrame = 0;
145801
+ intervalId = null;
145802
+ constructor(request, onAnswer, colors, maxVisibleOptions = 6, onToggleToolOutput, onTogglePlanExpand, ui) {
145526
145803
  super();
145527
145804
  this.request = request;
145528
145805
  this.onAnswer = onAnswer;
@@ -145530,6 +145807,7 @@ var QuestionDialogComponent = class extends Container {
145530
145807
  this.maxVisibleOptions = maxVisibleOptions;
145531
145808
  this.onToggleToolOutput = onToggleToolOutput;
145532
145809
  this.onTogglePlanExpand = onTogglePlanExpand;
145810
+ this.ui = ui;
145533
145811
  this.otherInput.onSubmit = (value) => {
145534
145812
  this.commitOtherInput(value, "enter");
145535
145813
  };
@@ -145540,14 +145818,33 @@ var QuestionDialogComponent = class extends Container {
145540
145818
  this.otherDrafts = Array.from({ length: total }, () => "");
145541
145819
  this.committedOtherValues = Array.from({ length: total }, () => void 0);
145542
145820
  this.answers = Array.from({ length: total }, () => void 0);
145821
+ if (this.ui !== void 0) this.intervalId = setInterval(() => {
145822
+ this.animFrame = (this.animFrame + 1) % PIXEL_PULSE_FRAMES.length;
145823
+ this.ui?.requestComponentRender(this);
145824
+ }, 100);
145825
+ }
145826
+ /** Stop the wizard pulse. Idempotent; called on respond and on hide. */
145827
+ stop() {
145828
+ if (this.intervalId !== null) {
145829
+ clearInterval(this.intervalId);
145830
+ this.intervalId = null;
145831
+ }
145832
+ }
145833
+ respond(response) {
145834
+ this.stop();
145835
+ this.onAnswer(response);
145836
+ }
145837
+ /** Breathing pixel block in the brand fluorescent green. */
145838
+ pulseBlock() {
145839
+ return chalk.hex(this.colors.primary)(PIXEL_PULSE_FRAMES[this.animFrame]);
145543
145840
  }
145544
145841
  handleInput(data) {
145545
145842
  if (matchesKey(data, Key.escape)) {
145546
- this.onAnswer({ answers: [] });
145843
+ this.respond({ answers: [] });
145547
145844
  return;
145548
145845
  }
145549
145846
  if (matchesKey(data, Key.ctrl("c")) || matchesKey(data, Key.ctrl("d"))) {
145550
- this.onAnswer({ answers: [] });
145847
+ this.respond({ answers: [] });
145551
145848
  return;
145552
145849
  }
145553
145850
  if (matchesKey(data, Key.ctrl("o"))) {
@@ -145772,7 +146069,7 @@ var QuestionDialogComponent = class extends Container {
145772
146069
  }
145773
146070
  executeSubmitAction(actionIdx, method) {
145774
146071
  if (actionIdx === 1) {
145775
- this.onAnswer({ answers: [] });
146072
+ this.respond({ answers: [] });
145776
146073
  return;
145777
146074
  }
145778
146075
  this.reviewMessage = void 0;
@@ -145784,7 +146081,7 @@ var QuestionDialogComponent = class extends Container {
145784
146081
  const answer = this.answers[i];
145785
146082
  if (answer !== void 0 && answer.length > 0) out[i] = answer;
145786
146083
  }
145787
- this.onAnswer({
146084
+ this.respond({
145788
146085
  answers: out,
145789
146086
  method: this.lastAnswerMethod ?? method
145790
146087
  });
@@ -145840,20 +146137,23 @@ var QuestionDialogComponent = class extends Container {
145840
146137
  const label = this.renderOptionLabel(questionIdx, option, isCursor);
145841
146138
  let tone;
145842
146139
  let prefix;
146140
+ const pulse = this.pulseBlock();
146141
+ const primaryBold = chalk.hex(this.colors.primary).bold;
145843
146142
  if (question.multi_select) {
145844
- prefix = ` [${isSelected ? "✓" : " "}] `;
146143
+ const checked = isSelected ? "✓" : " ";
146144
+ prefix = isCursor ? ` ${pulse} [${checked}] ` : ` [${checked}] `;
145845
146145
  if (isSelected && isCursor) tone = (s) => success.bold(s);
145846
146146
  else if (isSelected) tone = success;
145847
- else if (isCursor) tone = accent;
146147
+ else if (isCursor) tone = primaryBold;
145848
146148
  else tone = dim;
145849
146149
  } else if (isSelected && this.isAnswered(questionIdx)) {
145850
- prefix = isCursor ? ` [${String(num)}] ` : ` [${String(num)}] `;
146150
+ prefix = isCursor ? ` ${pulse} ${String(num)}. ` : `${String(num)}. `;
145851
146151
  tone = isCursor ? (s) => success.bold(s) : success;
145852
146152
  } else if (isCursor) {
145853
- prefix = ` [${String(num)}] `;
145854
- tone = accent;
146153
+ prefix = ` ${pulse} ${String(num)}. `;
146154
+ tone = primaryBold;
145855
146155
  } else {
145856
- prefix = ` [${String(num)}] `;
146156
+ prefix = ` ${String(num)}. `;
145857
146157
  tone = dim;
145858
146158
  }
145859
146159
  const continuation = " ".repeat(visibleWidth(prefix));
@@ -145903,8 +146203,8 @@ var QuestionDialogComponent = class extends Container {
145903
146203
  const label = t(SUBMIT_ACTIONS_KEYS[i]);
145904
146204
  if (label === void 0) continue;
145905
146205
  const num = i + 1;
145906
- if (i === this.submitActionIdx) lines.push(accent(` [${String(num)}] ${label}`));
145907
- else lines.push(dim(` [${String(num)}] ${label}`));
146206
+ if (i === this.submitActionIdx) lines.push(chalk.hex(this.colors.primary).bold(` ${this.pulseBlock()} ${String(num)}. ${label}`));
146207
+ else lines.push(dim(` ${String(num)}. ${label}`));
145908
146208
  }
145909
146209
  lines.push("");
145910
146210
  lines.push(this.buildSubmitHint(dim));
@@ -145913,18 +146213,18 @@ var QuestionDialogComponent = class extends Container {
145913
146213
  }
145914
146214
  pushTabs(lines) {
145915
146215
  const dim = chalk.hex(this.colors.textDim);
145916
- const active = chalk.bgHex(this.colors.primary).hex(this.colors.text).bold;
146216
+ const active = chalk.bgHex(this.colors.primary).hex(contrastTextHex(this.colors.primary)).bold;
145917
146217
  const tabs = [];
145918
146218
  for (let i = 0; i < this.request.data.questions.length; i++) {
145919
146219
  const question = this.request.data.questions[i];
145920
146220
  if (question === void 0) continue;
145921
146221
  const label = question.header !== void 0 && question.header.length > 0 ? question.header : `Q${String(i + 1)}`;
145922
- if (i === this.currentTab) tabs.push(active(` ${label} `));
146222
+ if (i === this.currentTab) tabs.push(active(` ${PIXEL_PULSE_FRAMES[this.animFrame]} ${label} `));
145923
146223
  else if (this.isAnswered(i)) tabs.push(chalk.hex(this.colors.success)(`(✓) ${label}`));
145924
146224
  else tabs.push(dim(`(○) ${label}`));
145925
146225
  }
145926
146226
  const submitLabel = t("question.submit");
145927
- if (this.isSubmitTab()) tabs.push(active(` ${submitLabel} `));
146227
+ if (this.isSubmitTab()) tabs.push(active(` ${PIXEL_PULSE_FRAMES[this.animFrame]} ${submitLabel} `));
145928
146228
  else tabs.push(dim(` ${submitLabel} `));
145929
146229
  lines.push(` ${tabs.join(" ")}`);
145930
146230
  }
@@ -146010,12 +146310,13 @@ var QuestionDialogComponent = class extends Container {
146010
146310
  const question = this.request.data.questions[questionIdx];
146011
146311
  if (question === void 0) return option.label;
146012
146312
  let prefix;
146313
+ const pulse = this.pulseBlock();
146013
146314
  if (question.multi_select) {
146014
- const body = ` [${isSelected ? "✓" : " "}] ${option.label}: `;
146015
- prefix = isSelected ? chalk.hex(this.colors.success).bold(body) : chalk.hex(this.colors.primary)(body);
146315
+ const body = ` ${pulse} [${isSelected ? "✓" : " "}] ${option.label}: `;
146316
+ prefix = isSelected ? chalk.hex(this.colors.success).bold(body) : chalk.hex(this.colors.primary).bold(body);
146016
146317
  } else {
146017
- const body = ` [${String(num)}] ${option.label}: `;
146018
- prefix = isSelected && this.isAnswered(questionIdx) ? chalk.hex(this.colors.success).bold(body) : chalk.hex(this.colors.primary)(body);
146318
+ const body = ` ${pulse} ${String(num)}. ${option.label}: `;
146319
+ prefix = isSelected && this.isAnswered(questionIdx) ? chalk.hex(this.colors.success).bold(body) : chalk.hex(this.colors.primary).bold(body);
146019
146320
  }
146020
146321
  const inputWidth = Math.max(4, width - visibleWidth(prefix) + 2);
146021
146322
  const inputLine = this.otherInput.render(inputWidth)[0] ?? "> ";
@@ -146049,6 +146350,7 @@ var QuestionDialogComponent = class extends Container {
146049
146350
  var DialogManager = class {
146050
146351
  host;
146051
146352
  activeApprovalPanel;
146353
+ activeQuestionDialog;
146052
146354
  approvalPreview;
146053
146355
  constructor(host) {
146054
146356
  this.host = host;
@@ -146181,12 +146483,14 @@ var DialogManager = class {
146181
146483
  this.restoreEditor();
146182
146484
  }
146183
146485
  showApprovalPanel(payload) {
146486
+ this.activeApprovalPanel?.stop();
146184
146487
  this.host.patchLivePane({ pendingApproval: { data: payload } });
146185
146488
  notifyTerminalOnce(this.host.state, `approval:${payload.id}`, {
146186
146489
  title: t("dialog.approval_title"),
146187
146490
  body: payload.tool_name
146188
146491
  });
146189
146492
  const panel = new ApprovalPanelComponent({ data: payload }, (response) => {
146493
+ panel.stop();
146190
146494
  this.host.approvalController.respond(adaptPanelResponse(response));
146191
146495
  }, this.host.state.theme.colors, () => {
146192
146496
  this.host.toggleToolOutputExpansion();
@@ -146194,12 +146498,13 @@ var DialogManager = class {
146194
146498
  this.host.togglePlanExpansion();
146195
146499
  }, (block) => {
146196
146500
  this.openApprovalPreview(panel, block);
146197
- });
146501
+ }, this.host.state.ui);
146198
146502
  this.activeApprovalPanel = panel;
146199
146503
  this.mountEditorReplacement(panel);
146200
146504
  }
146201
146505
  hideApprovalPanel() {
146202
146506
  if (this.approvalPreview !== void 0) this.closeApprovalPreview();
146507
+ this.activeApprovalPanel?.stop();
146203
146508
  this.activeApprovalPanel = void 0;
146204
146509
  this.host.patchLivePane({ pendingApproval: null });
146205
146510
  this.restoreEditor();
@@ -146234,21 +146539,26 @@ var DialogManager = class {
146234
146539
  this.host.state.ui.requestRender(true);
146235
146540
  }
146236
146541
  showQuestionDialog(payload) {
146542
+ this.activeQuestionDialog?.stop();
146237
146543
  this.host.patchLivePane({ pendingQuestion: { data: payload } });
146238
146544
  notifyTerminalOnce(this.host.state, `question:${payload.id}`, {
146239
146545
  title: t("dialog.question_title"),
146240
146546
  body: payload.questions[0]?.question
146241
146547
  });
146242
146548
  const dialog = new QuestionDialogComponent({ data: payload }, (response) => {
146549
+ dialog.stop();
146243
146550
  this.host.questionController.respond(response);
146244
146551
  }, this.host.state.theme.colors, void 0, () => {
146245
146552
  this.host.toggleToolOutputExpansion();
146246
146553
  }, () => {
146247
146554
  this.host.togglePlanExpansion();
146248
- });
146555
+ }, this.host.state.ui);
146556
+ this.activeQuestionDialog = dialog;
146249
146557
  this.mountEditorReplacement(dialog);
146250
146558
  }
146251
146559
  hideQuestionDialog() {
146560
+ this.activeQuestionDialog?.stop();
146561
+ this.activeQuestionDialog = void 0;
146252
146562
  this.host.patchLivePane({ pendingQuestion: null });
146253
146563
  this.restoreEditor();
146254
146564
  }
@@ -146661,6 +146971,7 @@ var ScreamTUI = class {
146661
146971
  this.state.editor.thinking = patch.thinkingLevel !== "off";
146662
146972
  this.state.editor.thinkingLevel = patch.thinkingLevel ?? "off";
146663
146973
  }
146974
+ if ("permissionMode" in patch) this.state.editor.permissionMode = patch.permissionMode ?? "manual";
146664
146975
  if ("streamingPhase" in patch && patch.streamingPhase !== "idle") {
146665
146976
  this.transcriptController.stopWelcomeBreathing();
146666
146977
  this.inputController.stopBreathingForStreaming();
@@ -148055,7 +148366,7 @@ async function runStreamJson(opts) {
148055
148366
  });
148056
148367
  session.prompt(userText).catch((error) => {
148057
148368
  const msg = error instanceof Error ? error.message : String(error);
148058
- if (msg.includes("insufficient tool messages") || msg.includes("tool_calls") && msg.includes("followed by tool messages")) {
148369
+ if (isOrphanedToolCallError(error)) {
148059
148370
  log.warn("stream-json: resetting session after tool call mismatch", {
148060
148371
  sessionId: session?.id,
148061
148372
  error: msg