scream-code 0.5.12 → 0.5.13

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.
@@ -20,13 +20,13 @@ import { finished, pipeline as pipeline$1 } from "node:stream/promises";
20
20
  import * as vs from "zlib";
21
21
  import Qr from "zlib";
22
22
  import * as fs$10 from "node:fs";
23
- import Vt, { appendFileSync, chmodSync, closeSync, constants, createReadStream, createWriteStream as createWriteStream$1, existsSync, fsyncSync, mkdirSync, openSync, promises, readFileSync, readSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
23
+ import Vt, { appendFileSync, chmodSync, closeSync, constants, createReadStream, createWriteStream as createWriteStream$1, existsSync, fsyncSync, mkdirSync, openSync, promises, readFileSync, readSync, renameSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
24
24
  import * as path$9 from "node:path";
25
25
  import path$1, { basename, dirname as dirname$1, extname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
26
26
  import { z } from "zod";
27
27
  import { DatabaseSync } from "node:sqlite";
28
28
  import * as nodeOs from "node:os";
29
- import { arch, homedir, hostname, platform, release, tmpdir, type } from "node:os";
29
+ import { arch, homedir, hostname, release, tmpdir, type } from "node:os";
30
30
  import { EventEmitter as EventEmitter$1 } from "node:events";
31
31
  import { StringDecoder } from "node:string_decoder";
32
32
  import Pi from "assert";
@@ -216,8 +216,8 @@ const parse$7 = function(p) {
216
216
  * Error codes for Scream Core's public error protocol.
217
217
  *
218
218
  * `ErrorCodes` is the source of truth for every code Scream Core may emit.
219
- * Downstream consumers (SDK, RPC clients, telemetry, agent-facing docs)
220
- * should depend on these string values rather than on class identity.
219
+ * Downstream consumers (SDK, RPC clients, agent-facing docs) should depend on
220
+ * these string values rather than on class identity.
221
221
  *
222
222
  * Codes follow `domain.reason`. Adding a code is a minor change; renaming
223
223
  * or removing one is a major change.
@@ -939,7 +939,7 @@ let uuid4$2 = function() {
939
939
  };
940
940
  //#endregion
941
941
  //#region ../../node_modules/.pnpm/@anthropic-ai+sdk@0.95.2_zod@4.4.3/node_modules/@anthropic-ai/sdk/internal/errors.mjs
942
- function isAbortError$6(err) {
942
+ function isAbortError$4(err) {
943
943
  return typeof err === "object" && err !== null && ("name" in err && err.name === "AbortError" || "message" in err && String(err.message).includes("FetchRequestCanceledException"));
944
944
  }
945
945
  const castToError$2 = (err) => {
@@ -2438,7 +2438,7 @@ var Stream$5 = class Stream$5 {
2438
2438
  }
2439
2439
  done = true;
2440
2440
  } catch (e) {
2441
- if (isAbortError$6(e)) return;
2441
+ if (isAbortError$4(e)) return;
2442
2442
  throw e;
2443
2443
  } finally {
2444
2444
  if (!done) controller.abort();
@@ -2469,7 +2469,7 @@ var Stream$5 = class Stream$5 {
2469
2469
  }
2470
2470
  done = true;
2471
2471
  } catch (e) {
2472
- if (isAbortError$6(e)) return;
2472
+ if (isAbortError$4(e)) return;
2473
2473
  throw e;
2474
2474
  } finally {
2475
2475
  if (!done) controller.abort();
@@ -5076,7 +5076,7 @@ var BetaMessageStream = class BetaMessageStream {
5076
5076
  _BetaMessageStream_logger.set(this, void 0);
5077
5077
  _BetaMessageStream_handleError.set(this, (error) => {
5078
5078
  __classPrivateFieldSet$1(this, _BetaMessageStream_errored, true, "f");
5079
- if (isAbortError$6(error)) error = new APIUserAbortError$2();
5079
+ if (isAbortError$4(error)) error = new APIUserAbortError$2();
5080
5080
  if (error instanceof APIUserAbortError$2) {
5081
5081
  __classPrivateFieldSet$1(this, _BetaMessageStream_aborted, true, "f");
5082
5082
  return this._emit("abort", error);
@@ -7003,7 +7003,7 @@ var MessageStream = class MessageStream {
7003
7003
  _MessageStream_logger.set(this, void 0);
7004
7004
  _MessageStream_handleError.set(this, (error) => {
7005
7005
  __classPrivateFieldSet$1(this, _MessageStream_errored, true, "f");
7006
- if (isAbortError$6(error)) error = new APIUserAbortError$2();
7006
+ if (isAbortError$4(error)) error = new APIUserAbortError$2();
7007
7007
  if (error instanceof APIUserAbortError$2) {
7008
7008
  __classPrivateFieldSet$1(this, _MessageStream_aborted, true, "f");
7009
7009
  return this._emit("abort", error);
@@ -8095,7 +8095,7 @@ var BaseAnthropic = class {
8095
8095
  if (response instanceof globalThis.Error) {
8096
8096
  const retryMessage = `retrying, ${retriesRemaining} attempts remaining`;
8097
8097
  if (options.signal?.aborted) throw new APIUserAbortError$2();
8098
- const isTimeout = isAbortError$6(response) || /timed? ?out/i.test(String(response) + ("cause" in response ? String(response.cause) : ""));
8098
+ const isTimeout = isAbortError$4(response) || /timed? ?out/i.test(String(response) + ("cause" in response ? String(response.cause) : ""));
8099
8099
  if (retriesRemaining) {
8100
8100
  loggerFor$2(this).info(`[${requestLogID}] connection ${isTimeout ? "timed out" : "failed"} - ${retryMessage}`);
8101
8101
  loggerFor$2(this).debug(`[${requestLogID}] connection ${isTimeout ? "timed out" : "failed"} (${retryMessage})`, formatRequestDetails$2({
@@ -8790,16 +8790,24 @@ const CACHEABLE_TYPES = new Set([
8790
8790
  "server_tool_use",
8791
8791
  "web_search_tool_result"
8792
8792
  ]);
8793
- function injectCacheControlOnLastBlock(messages) {
8794
- const lastMessage = messages.at(-1);
8795
- if (lastMessage === void 0) return;
8796
- const content = lastMessage.content;
8793
+ function injectCacheControlOnLastBlock(messages, messageIndex) {
8794
+ const message = messages[messageIndex ?? messages.length - 1];
8795
+ if (message === void 0) return;
8796
+ const content = message.content;
8797
8797
  if (!Array.isArray(content) || content.length === 0) return;
8798
8798
  const lastBlock = content.at(-1);
8799
8799
  if (lastBlock === void 0) return;
8800
8800
  if (CACHEABLE_TYPES.has(lastBlock.type)) lastBlock.cache_control = CACHE_CONTROL;
8801
8801
  }
8802
8802
  /**
8803
+ * Inject cache_control on the last block of the second-to-last message,
8804
+ * when there is enough budget for an extra breakpoint.
8805
+ */
8806
+ function injectCacheControlOnPenultimateMessage(messages) {
8807
+ if (messages.length < 2) return;
8808
+ injectCacheControlOnLastBlock(messages, messages.length - 2);
8809
+ }
8810
+ /**
8803
8811
  * Check whether a MessageParam is a user message whose content consists
8804
8812
  * entirely of `tool_result` blocks.
8805
8813
  *
@@ -9185,6 +9193,7 @@ var AnthropicChatProvider = class {
9185
9193
  if (last !== void 0 && isToolResultOnly(last) && isToolResultOnly(converted)) last.content = [...last.content, ...converted.content];
9186
9194
  else messages.push(converted);
9187
9195
  }
9196
+ injectCacheControlOnPenultimateMessage(messages);
9188
9197
  injectCacheControlOnLastBlock(messages);
9189
9198
  const kwargs = {};
9190
9199
  if (this._generationKwargs.max_tokens !== void 0) kwargs["max_tokens"] = this._generationKwargs.max_tokens;
@@ -34834,7 +34843,7 @@ const uuid4$1 = () => uuid4Internal();
34834
34843
  * Copyright 2025 Google LLC
34835
34844
  * SPDX-License-Identifier: Apache-2.0
34836
34845
  */
34837
- function isAbortError$5(err) {
34846
+ function isAbortError$3(err) {
34838
34847
  return typeof err === "object" && err !== null && ("name" in err && err.name === "AbortError" || "message" in err && String(err.message).includes("FetchRequestCanceledException"));
34839
34848
  }
34840
34849
  const castToError$1 = (err) => {
@@ -35571,7 +35580,7 @@ var Stream$1 = class Stream$1 {
35571
35580
  }
35572
35581
  done = true;
35573
35582
  } catch (e) {
35574
- if (isAbortError$5(e)) return yield __await(void 0);
35583
+ if (isAbortError$3(e)) return yield __await(void 0);
35575
35584
  throw e;
35576
35585
  } finally {
35577
35586
  if (!done) controller.abort();
@@ -35636,7 +35645,7 @@ var Stream$1 = class Stream$1 {
35636
35645
  }
35637
35646
  done = true;
35638
35647
  } catch (e) {
35639
- if (isAbortError$5(e)) return yield __await(void 0);
35648
+ if (isAbortError$3(e)) return yield __await(void 0);
35640
35649
  throw e;
35641
35650
  } finally {
35642
35651
  if (!done) controller.abort();
@@ -36162,7 +36171,7 @@ var BaseGeminiNextGenAPIClient = class BaseGeminiNextGenAPIClient {
36162
36171
  if (response instanceof globalThis.Error) {
36163
36172
  const retryMessage = `retrying, ${retriesRemaining} attempts remaining`;
36164
36173
  if ((_d = options.signal) === null || _d === void 0 ? void 0 : _d.aborted) throw new APIUserAbortError$1();
36165
- const isTimeout = isAbortError$5(response) || /timed? ?out/i.test(String(response) + ("cause" in response ? String(response.cause) : ""));
36174
+ const isTimeout = isAbortError$3(response) || /timed? ?out/i.test(String(response) + ("cause" in response ? String(response.cause) : ""));
36166
36175
  if (retriesRemaining) {
36167
36176
  loggerFor$1(this).info(`[${requestLogID}] connection ${isTimeout ? "timed out" : "failed"} - ${retryMessage}`);
36168
36177
  loggerFor$1(this).debug(`[${requestLogID}] connection ${isTimeout ? "timed out" : "failed"} (${retryMessage})`, formatRequestDetails$1({
@@ -38685,7 +38694,7 @@ let uuid4 = function() {
38685
38694
  };
38686
38695
  //#endregion
38687
38696
  //#region ../../node_modules/.pnpm/openai@6.39.1_ws@8.21.0_zod@4.4.3/node_modules/openai/internal/errors.mjs
38688
- function isAbortError$4(err) {
38697
+ function isAbortError$2(err) {
38689
38698
  return typeof err === "object" && err !== null && ("name" in err && err.name === "AbortError" || "message" in err && String(err.message).includes("FetchRequestCanceledException"));
38690
38699
  }
38691
38700
  const castToError = (err) => {
@@ -39514,7 +39523,7 @@ var Stream = class Stream {
39514
39523
  }
39515
39524
  done = true;
39516
39525
  } catch (e) {
39517
- if (isAbortError$4(e)) return;
39526
+ if (isAbortError$2(e)) return;
39518
39527
  throw e;
39519
39528
  } finally {
39520
39529
  if (!done) controller.abort();
@@ -39545,7 +39554,7 @@ var Stream = class Stream {
39545
39554
  }
39546
39555
  done = true;
39547
39556
  } catch (e) {
39548
- if (isAbortError$4(e)) return;
39557
+ if (isAbortError$2(e)) return;
39549
39558
  throw e;
39550
39559
  } finally {
39551
39560
  if (!done) controller.abort();
@@ -47724,7 +47733,7 @@ var OpenAI = class {
47724
47733
  if (response instanceof globalThis.Error) {
47725
47734
  const retryMessage = `retrying, ${retriesRemaining} attempts remaining`;
47726
47735
  if (options.signal?.aborted) throw new APIUserAbortError();
47727
- const isTimeout = isAbortError$4(response) || /timed? ?out/i.test(String(response) + ("cause" in response ? String(response.cause) : ""));
47736
+ const isTimeout = isAbortError$2(response) || /timed? ?out/i.test(String(response) + ("cause" in response ? String(response.cause) : ""));
47728
47737
  if (retriesRemaining) {
47729
47738
  loggerFor(this).info(`[${requestLogID}] connection ${isTimeout ? "timed out" : "failed"} - ${retryMessage}`);
47730
47739
  loggerFor(this).debug(`[${requestLogID}] connection ${isTimeout ? "timed out" : "failed"} (${retryMessage})`, formatRequestDetails({
@@ -50695,16 +50704,6 @@ function resolveGlobalLogPath(homeDir) {
50695
50704
  return join$1(homeDir, "logs", "scream-code.log");
50696
50705
  }
50697
50706
  //#endregion
50698
- //#region ../../packages/agent-core/src/telemetry.ts
50699
- const noopTelemetryClient = {
50700
- track: () => {},
50701
- withContext: () => noopTelemetryClient,
50702
- setContext: () => {}
50703
- };
50704
- function withTelemetryContext$1(telemetry, patch) {
50705
- return telemetry.withContext?.(patch) ?? telemetry;
50706
- }
50707
- //#endregion
50708
50707
  //#region ../../packages/agent-core/src/utils/tokens.ts
50709
50708
  /**
50710
50709
  * Estimate token count from text using a character-based heuristic.
@@ -50760,7 +50759,7 @@ function createMaxStepsExceededError(maxSteps, message) {
50760
50759
  function isMaxStepsExceededError(error) {
50761
50760
  return isScreamError(error) && error.code === ErrorCodes.LOOP_MAX_STEPS_EXCEEDED;
50762
50761
  }
50763
- function isAbortError$3(err) {
50762
+ function isAbortError$1(err) {
50764
50763
  if (err instanceof Error) return err.name === "AbortError";
50765
50764
  return false;
50766
50765
  }
@@ -51556,7 +51555,7 @@ var BackgroundProcessManager = class {
51556
51555
  this.appendOutput(entry, r.result);
51557
51556
  await this.finalizeTerminal(entry, "completed", 0);
51558
51557
  }).catch(async (error) => {
51559
- if (entry.stopRequested && isAbortError$3(error)) {
51558
+ if (entry.stopRequested && isAbortError$1(error)) {
51560
51559
  await this.finalizeTerminal(entry, "killed", null);
51561
51560
  return;
51562
51561
  }
@@ -54727,7 +54726,6 @@ var CronCreateTool = class {
54727
54726
  const ideal = computeNextCronRun(parsed, nowMs);
54728
54727
  const nextFireAt = ideal === null ? null : recurring ? jitteredNextCronRunMs(task, parsed, ideal) : oneShotJitteredNextCronRunMs(task, ideal);
54729
54728
  const humanSchedule = cronToHuman(parsed);
54730
- this.manager.emitScheduled(task);
54731
54729
  return {
54732
54730
  output: formatOutput({
54733
54731
  id: task.id,
@@ -54777,13 +54775,12 @@ var cron_delete_default = "Cancel a scheduled cron job by id.\n\nUse this tool t
54777
54775
  * thought it deleted. Surfacing `isError: true` lets the model
54778
54776
  * correct itself (typically by calling `CronList` again).
54779
54777
  *
54780
- * Why the manager is not consulted for telemetry on the not-found
54781
- * branch:
54778
+ * Why the manager is not consulted on the not-found branch:
54782
54779
  *
54783
- * - `cron_deleted` records an actual state change. Emitting it on a
54784
- * miss would inflate the metric and break parity with `cron_create`
54785
- * (which never fires on a rejected schedule). The branch is fully
54786
- * observable through tool-call telemetry already.
54780
+ * - `cron_deleted` records an actual state change. Reporting success on a
54781
+ * miss would silently teach the model that CronDelete is idempotent
54782
+ * against missing ids, which it is not. The branch is fully observable
54783
+ * through the tool result already.
54787
54784
  *
54788
54785
  * Refresh-cron pattern this tool participates in:
54789
54786
  *
@@ -54824,7 +54821,6 @@ var CronDeleteTool = class {
54824
54821
  isError: true,
54825
54822
  output: `No cron job with id ${args.id}.`
54826
54823
  };
54827
- this.manager.emitDeleted(args.id);
54828
54824
  return {
54829
54825
  output: `Deleted cron job ${args.id}.`,
54830
54826
  isError: false
@@ -55018,7 +55014,7 @@ function normalizePath(path) {
55018
55014
  }
55019
55015
  //#endregion
55020
55016
  //#region ../../packages/agent-core/src/utils/abort.ts
55021
- function abortError$1() {
55017
+ function abortError() {
55022
55018
  const error = /* @__PURE__ */ new Error("Aborted");
55023
55019
  error.name = "AbortError";
55024
55020
  return error;
@@ -55051,7 +55047,7 @@ function abortable(promise, signal) {
55051
55047
  signal.throwIfAborted();
55052
55048
  return new Promise((resolve, reject) => {
55053
55049
  const onAbort = () => {
55054
- reject(abortError$1());
55050
+ reject(abortError());
55055
55051
  };
55056
55052
  signal.addEventListener("abort", onAbort, { once: true });
55057
55053
  promise.then(resolve, reject).finally(() => {
@@ -55078,7 +55074,7 @@ function createDeadlineAbortSignal(source, timeoutMs) {
55078
55074
  let didTimeout = false;
55079
55075
  let timeout = setTimeout(() => {
55080
55076
  didTimeout = true;
55081
- controller.abort(abortError$1());
55077
+ controller.abort(abortError());
55082
55078
  }, timeoutMs);
55083
55079
  return {
55084
55080
  signal: controller.signal,
@@ -55300,7 +55296,7 @@ var AgentTool = class {
55300
55296
  let message;
55301
55297
  if (foregroundDeadline?.timedOut() === true && args.timeout !== void 0) message = `Agent timed out after ${args.timeout}s.`;
55302
55298
  else if (isUserCancellation(signal.reason)) message = "The user manually interrupted this subagent (and any sibling agents launched alongside it). This was a deliberate user action, not a system error, a timeout, or a capacity/concurrency limit. Do not retry automatically or speculate about why it failed — wait for the user's next instruction.";
55303
- else if (isAbortError$3(error)) message = "The subagent was stopped before it finished.";
55299
+ else if (isAbortError$1(error)) message = "The subagent was stopped before it finished.";
55304
55300
  else message = error instanceof Error ? error.message : String(error);
55305
55301
  return {
55306
55302
  output: [
@@ -55317,7 +55313,7 @@ var AgentTool = class {
55317
55313
  let message;
55318
55314
  if (foregroundDeadline?.timedOut() === true && args.timeout !== void 0) message = `Agent timed out after ${args.timeout}s.`;
55319
55315
  else if (isUserCancellation(signal.reason)) message = "The user manually interrupted this subagent (and any sibling agents launched alongside it). This was a deliberate user action, not a system error, a timeout, or a capacity/concurrency limit. Do not retry automatically or speculate about why it failed — wait for the user's next instruction.";
55320
- else if (isAbortError$3(error)) message = "The subagent was stopped before it finished.";
55316
+ else if (isAbortError$1(error)) message = "The subagent was stopped before it finished.";
55321
55317
  else message = error instanceof Error ? error.message : String(error);
55322
55318
  return {
55323
55319
  output: `subagent error: ${message}`,
@@ -55397,19 +55393,13 @@ var AskUserQuestionTool = class {
55397
55393
  multiSelect: q.multi_select
55398
55394
  }))
55399
55395
  }, { signal }));
55400
- if (normalized === null || Object.keys(normalized.answers).length === 0) {
55401
- this.agent.telemetry.track("question_dismissed");
55402
- return dismissedQuestionResult();
55403
- }
55404
- const properties = { answered: Object.keys(normalized.answers).length };
55405
- if (normalized.method !== void 0) properties["method"] = normalized.method;
55406
- this.agent.telemetry.track("question_answered", properties);
55396
+ if (normalized === null || Object.keys(normalized.answers).length === 0) return dismissedQuestionResult();
55407
55397
  return {
55408
55398
  isError: false,
55409
55399
  output: JSON.stringify({ answers: normalized.answers })
55410
55400
  };
55411
55401
  } catch (error) {
55412
- if (isAbortError$3(error) || signal.aborted) throw error;
55402
+ if (isAbortError$1(error) || signal.aborted) throw error;
55413
55403
  if (error instanceof ScreamError && error.code === ErrorCodes.NOT_IMPLEMENTED) return {
55414
55404
  isError: true,
55415
55405
  output: QUESTION_UNSUPPORTED_FAILURE_MESSAGE
@@ -56683,6 +56673,8 @@ var MemoryMemoStore = class MemoryMemoStore {
56683
56673
  tags TEXT NOT NULL DEFAULT '[]'
56684
56674
  );
56685
56675
 
56676
+ CREATE INDEX IF NOT EXISTS idx_memos_project_dir ON memos(project_dir);
56677
+
56686
56678
  CREATE VIRTUAL TABLE IF NOT EXISTS memos_fts USING fts5(
56687
56679
  user_need,
56688
56680
  approach,
@@ -56698,6 +56690,8 @@ var MemoryMemoStore = class MemoryMemoStore {
56698
56690
  model TEXT NOT NULL DEFAULT 'bge-small-zh-v1.5',
56699
56691
  created_at INTEGER NOT NULL
56700
56692
  );
56693
+
56694
+ CREATE INDEX IF NOT EXISTS idx_memory_embeddings_created_at ON memory_embeddings(created_at DESC);
56701
56695
  `);
56702
56696
  this.migrateSchema();
56703
56697
  }
@@ -56706,6 +56700,10 @@ var MemoryMemoStore = class MemoryMemoStore {
56706
56700
  const info = this.db.prepare("PRAGMA table_info(memos)").all();
56707
56701
  if (!info.some((col) => col.name === "project_dir")) this.db.exec("ALTER TABLE memos ADD COLUMN project_dir TEXT NOT NULL DEFAULT ''");
56708
56702
  if (!info.some((col) => col.name === "tags")) this.db.exec("ALTER TABLE memos ADD COLUMN tags TEXT NOT NULL DEFAULT '[]'");
56703
+ this.db.exec(`
56704
+ CREATE INDEX IF NOT EXISTS idx_memos_project_dir ON memos(project_dir);
56705
+ CREATE INDEX IF NOT EXISTS idx_memory_embeddings_created_at ON memory_embeddings(created_at DESC);
56706
+ `);
56709
56707
  }
56710
56708
  async migrateFromJsonl() {
56711
56709
  const markerPath = join$1(this.projectDir, "memory", SQLITE_MIGRATION_MARKER);
@@ -56871,14 +56869,44 @@ var MemoryMemoStore = class MemoryMemoStore {
56871
56869
  /**
56872
56870
  * Search memos by vector similarity. Returns memos sorted by cosine
56873
56871
  * similarity (highest first). Falls back to empty if no embeddings exist.
56872
+ *
56873
+ * Performance notes:
56874
+ * - candidateLimit bounds the SQL query so we never load every embedding.
56875
+ * - recencyCutoffDays lets callers ignore very old memos.
56876
+ * - projectDir is pushed into the SQL JOIN so unrelated projects are not
56877
+ * considered at all.
56874
56878
  */
56875
56879
  async searchByVector(queryEmbedding, options) {
56876
56880
  await this.init();
56877
56881
  if (this.db === void 0) return [];
56878
- const rows = this.db.prepare("SELECT memory_id, embedding_json FROM memory_embeddings ORDER BY created_at DESC").all();
56879
- if (rows.length === 0) return [];
56880
56882
  const limit = options?.candidateLimit ?? 200;
56881
56883
  const projectDir = options?.projectDir;
56884
+ const recencyCutoffDays = options?.recencyCutoffDays;
56885
+ const cutoffMs = recencyCutoffDays !== void 0 && recencyCutoffDays > 0 ? Date.now() - recencyCutoffDays * 24 * 60 * 60 * 1e3 : void 0;
56886
+ let rows;
56887
+ if (projectDir !== void 0) {
56888
+ const stmt = cutoffMs === void 0 ? this.db.prepare(`
56889
+ SELECT e.memory_id, e.embedding_json
56890
+ FROM memory_embeddings e
56891
+ JOIN memos m ON m.id = e.memory_id
56892
+ WHERE (m.project_dir = ? OR m.project_dir = '')
56893
+ ORDER BY e.created_at DESC
56894
+ LIMIT ?
56895
+ `) : this.db.prepare(`
56896
+ SELECT e.memory_id, e.embedding_json
56897
+ FROM memory_embeddings e
56898
+ JOIN memos m ON m.id = e.memory_id
56899
+ WHERE (m.project_dir = ? OR m.project_dir = '')
56900
+ AND e.created_at > ?
56901
+ ORDER BY e.created_at DESC
56902
+ LIMIT ?
56903
+ `);
56904
+ rows = cutoffMs === void 0 ? stmt.all(projectDir, limit) : stmt.all(projectDir, cutoffMs, limit);
56905
+ } else {
56906
+ const stmt = cutoffMs === void 0 ? this.db.prepare("SELECT memory_id, embedding_json FROM memory_embeddings ORDER BY created_at DESC LIMIT ?") : this.db.prepare("SELECT memory_id, embedding_json FROM memory_embeddings WHERE created_at > ? ORDER BY created_at DESC LIMIT ?");
56907
+ rows = cutoffMs === void 0 ? stmt.all(limit) : stmt.all(cutoffMs, limit);
56908
+ }
56909
+ if (rows.length === 0) return [];
56882
56910
  const scored = [];
56883
56911
  for (const row of rows) try {
56884
56912
  const vec = new Float32Array(JSON.parse(row.embedding_json));
@@ -56893,13 +56921,10 @@ var MemoryMemoStore = class MemoryMemoStore {
56893
56921
  const results = [];
56894
56922
  for (const { id, score } of topScored) {
56895
56923
  const row = this.db.prepare("SELECT * FROM memos WHERE id = ?").get(id);
56896
- if (row !== void 0) {
56897
- if (projectDir !== void 0 && row["project_dir"] !== projectDir && row["project_dir"] !== "") continue;
56898
- results.push({
56899
- memo: rowToMemo(row),
56900
- score
56901
- });
56902
- }
56924
+ if (row !== void 0) results.push({
56925
+ memo: rowToMemo(row),
56926
+ score
56927
+ });
56903
56928
  }
56904
56929
  return results;
56905
56930
  }
@@ -57622,6 +57647,28 @@ var MemoryEditTool = class {
57622
57647
  //#region ../../packages/agent-core/src/tools/builtin/memory/memory-lookup.ts
57623
57648
  const DEFAULT_LIMIT = 5;
57624
57649
  const MAX_LIMIT = 20;
57650
+ const QUERY_EMBEDDING_CACHE_MAX_SIZE = 50;
57651
+ const QUERY_EMBEDDING_CACHE_TTL_MS = 6e4;
57652
+ const queryEmbeddingCache = /* @__PURE__ */ new Map();
57653
+ function getCachedQueryEmbedding(key) {
57654
+ const entry = queryEmbeddingCache.get(key);
57655
+ if (entry === void 0) return void 0;
57656
+ if (Date.now() > entry.expiresAt) {
57657
+ queryEmbeddingCache.delete(key);
57658
+ return;
57659
+ }
57660
+ return entry.vec;
57661
+ }
57662
+ function setCachedQueryEmbedding(key, vec) {
57663
+ if (queryEmbeddingCache.size >= QUERY_EMBEDDING_CACHE_MAX_SIZE) {
57664
+ const oldest = queryEmbeddingCache.keys().next().value;
57665
+ if (oldest !== void 0) queryEmbeddingCache.delete(oldest);
57666
+ }
57667
+ queryEmbeddingCache.set(key, {
57668
+ vec,
57669
+ expiresAt: Date.now() + QUERY_EMBEDDING_CACHE_TTL_MS
57670
+ });
57671
+ }
57625
57672
  const DEFAULT_MIN_SCORE = .2;
57626
57673
  const MemoryLookupInputSchema = z.object({
57627
57674
  query: z.string().min(1).describe("Search query describing the current task, error, approach, or keywords to look up in the memory memo store."),
@@ -57671,11 +57718,19 @@ var MemoryLookupTool = class {
57671
57718
  let vectorScores;
57672
57719
  const engine = store.getEmbeddingEngine();
57673
57720
  if (engine?.available && store.hasEmbeddings()) try {
57674
- const queryVecs = await engine.embedBatch([query]);
57675
- if (queryVecs !== null && queryVecs.length > 0) {
57676
- const vectorResults = await store.searchByVector(queryVecs[0], {
57721
+ let queryVec = getCachedQueryEmbedding(query);
57722
+ if (queryVec === void 0) {
57723
+ const queryVecs = await engine.embedBatch([query]);
57724
+ if (queryVecs !== null && queryVecs.length > 0) {
57725
+ queryVec = queryVecs[0];
57726
+ setCachedQueryEmbedding(query, queryVec);
57727
+ }
57728
+ }
57729
+ if (queryVec !== void 0) {
57730
+ const vectorResults = await store.searchByVector(queryVec, {
57677
57731
  candidateLimit,
57678
- projectDir
57732
+ projectDir,
57733
+ recencyCutoffDays: 90
57679
57734
  });
57680
57735
  if (vectorResults.length > 0) vectorScores = new Map(vectorResults.map((r) => [r.memo.id, r.score]));
57681
57736
  }
@@ -57780,7 +57835,7 @@ const SEVERITY_LABELS = [
57780
57835
  "Information",
57781
57836
  "Hint"
57782
57837
  ];
57783
- const DEFAULT_REQUEST_TIMEOUT_MS$1 = 12e4;
57838
+ const DEFAULT_REQUEST_TIMEOUT_MS = 12e4;
57784
57839
  var LspClient = class {
57785
57840
  command;
57786
57841
  workspaceRoot;
@@ -57884,8 +57939,8 @@ var LspClient = class {
57884
57939
  return new Promise((resolve, reject) => {
57885
57940
  const timer = setTimeout(() => {
57886
57941
  this.pending.delete(id);
57887
- reject(/* @__PURE__ */ new Error(`LSP request '${method}' timed out after ${DEFAULT_REQUEST_TIMEOUT_MS$1}ms`));
57888
- }, DEFAULT_REQUEST_TIMEOUT_MS$1);
57942
+ reject(/* @__PURE__ */ new Error(`LSP request '${method}' timed out after ${DEFAULT_REQUEST_TIMEOUT_MS}ms`));
57943
+ }, DEFAULT_REQUEST_TIMEOUT_MS);
57889
57944
  this.pending.set(id, {
57890
57945
  resolve,
57891
57946
  reject,
@@ -67278,7 +67333,7 @@ var WolfPackTool = class {
67278
67333
  };
67279
67334
  } catch (error) {
67280
67335
  let message;
67281
- if (isAbortError$3(error)) message = "The subagent was stopped before it finished.";
67336
+ if (isAbortError$1(error)) message = "The subagent was stopped before it finished.";
67282
67337
  else message = error instanceof Error ? error.message : String(error);
67283
67338
  return {
67284
67339
  item,
@@ -72254,7 +72309,7 @@ var GrepTool = class {
72254
72309
  try {
72255
72310
  rgPath = (await ensureRgPath({ signal })).path;
72256
72311
  } catch (error) {
72257
- if (isAbortError$3(error)) return {
72312
+ if (isAbortError$1(error)) return {
72258
72313
  isError: true,
72259
72314
  output: "Grep aborted"
72260
72315
  };
@@ -73833,7 +73888,6 @@ var EnterPlanModeTool = class {
73833
73888
  output: `Failed to enter plan mode: ${error instanceof Error ? error.message : "Failed to enter plan mode."}`
73834
73889
  };
73835
73890
  }
73836
- this.agent.telemetry.track("plan_enter_resolved", { outcome: "auto_approved" });
73837
73891
  return { output: enteredPlanModeMessage(this.agent.planMode.planFilePath) };
73838
73892
  }
73839
73893
  };
@@ -73913,17 +73967,15 @@ var ExitPlanModeTool = class {
73913
73967
  if (args.options !== void 0 && args.options.length >= 2) display.options = args.options;
73914
73968
  return display;
73915
73969
  }
73916
- async execution(args) {
73970
+ async execution(_args) {
73917
73971
  if (!this.agent.planMode.isActive) return {
73918
73972
  isError: true,
73919
73973
  output: "ExitPlanMode can only be called while plan mode is active. Use EnterPlanMode (or /plan) first."
73920
73974
  };
73921
73975
  const resolvedPlan = await this.resolvePlan();
73922
73976
  if (!resolvedPlan.ok) return resolvedPlan.error;
73923
- this.agent.telemetry.track("plan_submitted", { has_options: args.options !== void 0 && args.options.length >= 2 });
73924
73977
  const failed = this.exitPlanMode();
73925
73978
  if (failed !== void 0) return failed;
73926
- this.agent.telemetry.track("plan_resolved", { outcome: "auto_approved" });
73927
73979
  return {
73928
73980
  isError: false,
73929
73981
  output: `Exited plan mode. ${formatPlanForOutput(resolvedPlan.plan, resolvedPlan.path)}`
@@ -74647,7 +74699,6 @@ var BackgroundManager = class extends BackgroundProcessManager {
74647
74699
  type: "background.task.started",
74648
74700
  info
74649
74701
  });
74650
- this.agent.telemetry.track("background_task_created", { kind: info.taskId.startsWith("agent-") ? "agent" : "bash" });
74651
74702
  return;
74652
74703
  case "updated":
74653
74704
  this.agent.emitEvent({
@@ -74655,22 +74706,12 @@ var BackgroundManager = class extends BackgroundProcessManager {
74655
74706
  info
74656
74707
  });
74657
74708
  return;
74658
- case "terminated": {
74709
+ case "terminated":
74659
74710
  this.agent.emitEvent({
74660
74711
  type: "background.task.terminated",
74661
74712
  info
74662
74713
  });
74663
- const success = info.status === "completed";
74664
- const duration_s = info.endedAt !== null ? (info.endedAt - info.startedAt) / 1e3 : null;
74665
- const properties = {
74666
- kind: info.taskId.startsWith("agent-") ? "agent" : "bash",
74667
- success,
74668
- duration_s
74669
- };
74670
- if (!success) properties["reason"] = info.timedOut === true ? "timeout" : info.status === "killed" ? "killed" : "error";
74671
- this.agent.telemetry.track("background_task_completed", properties);
74672
74714
  return;
74673
- }
74674
74715
  }
74675
74716
  });
74676
74717
  }
@@ -74873,7 +74914,7 @@ async function chatWithRetry(input) {
74873
74914
  }
74874
74915
  }
74875
74916
  function logRequestFailure(input, error, attempt, maxAttempts) {
74876
- if (isAbortError$3(error) || input.params.signal.aborted) return;
74917
+ if (isAbortError$1(error) || input.params.signal.aborted) return;
74877
74918
  input.log?.warn("llm request failed", {
74878
74919
  turnStep: `${input.turnId}.${String(input.currentStep)}`,
74879
74920
  attempt: `${String(attempt)}/${String(maxAttempts)}`,
@@ -75199,8 +75240,6 @@ var FullCompaction = class {
75199
75240
  });
75200
75241
  const active = {
75201
75242
  abortController,
75202
- startedAt: Date.now(),
75203
- telemetryTrigger: compactionTelemetryTrigger(data.source, data.instruction),
75204
75243
  promise: Promise.resolve(),
75205
75244
  blockedByTurn: false
75206
75245
  };
@@ -75299,13 +75338,11 @@ var FullCompaction = class {
75299
75338
  signal.removeEventListener("abort", onAbort);
75300
75339
  }
75301
75340
  }
75302
- async compactionWorker(signal, data, initialCompactedCount) {
75303
- const startedAt = Date.now();
75341
+ async compactionWorker(signal, data, compactedCount) {
75304
75342
  const originalHistory = [...this.agent.context.history];
75305
75343
  const tokensBefore = estimateTokensForMessages(originalHistory);
75306
75344
  let retryCount = 0;
75307
75345
  try {
75308
- let compactedCount = initialCompactedCount;
75309
75346
  await this.triggerPreCompactHook(data, tokensBefore, signal);
75310
75347
  const model = this.agent.config.model;
75311
75348
  const delays = retryBackoffDelays(5);
@@ -75351,16 +75388,6 @@ var FullCompaction = class {
75351
75388
  tokensBefore,
75352
75389
  tokensAfter
75353
75390
  };
75354
- const active = this.compacting;
75355
- this.agent.telemetry.track("compaction_finished", {
75356
- trigger_type: active.telemetryTrigger,
75357
- before_tokens: result.tokensBefore,
75358
- after_tokens: result.tokensAfter,
75359
- duration_ms: Date.now() - active.startedAt,
75360
- compacted_count: result.compactedCount,
75361
- retry_count: retryCount,
75362
- ...usage
75363
- });
75364
75391
  this.markCompleted();
75365
75392
  this.agent.emitEvent({
75366
75393
  type: "compaction.completed",
@@ -75371,7 +75398,7 @@ var FullCompaction = class {
75371
75398
  this.triggerPostCompactHook(data, result);
75372
75399
  this.consecutiveCompactionFailures = 0;
75373
75400
  } catch (error) {
75374
- if (!isAbortError$3(error)) {
75401
+ if (!isAbortError$1(error)) {
75375
75402
  const blockedByTurn = this.compacting?.blockedByTurn === true;
75376
75403
  this.consecutiveCompactionFailures += 1;
75377
75404
  if (this.consecutiveCompactionFailures >= MAX_CONSECUTIVE_FAILURES) this.agent.emitEvent({
@@ -75391,13 +75418,6 @@ var FullCompaction = class {
75391
75418
  ...payload
75392
75419
  });
75393
75420
  }
75394
- this.agent.telemetry.track("compaction_failed", {
75395
- trigger_type: compactionTelemetryTrigger(data.source, data.instruction),
75396
- before_tokens: tokensBefore,
75397
- duration_ms: Date.now() - startedAt,
75398
- retry_count: retryCount,
75399
- error_type: error instanceof Error ? error.name : "Unknown"
75400
- });
75401
75421
  if (blockedByTurn) {
75402
75422
  if (isScreamError(error) && error.code === ErrorCodes.AUTH_LOGIN_REQUIRED) throw error;
75403
75423
  throw new ScreamError(ErrorCodes.COMPACTION_FAILED, String(error), { cause: error });
@@ -75479,11 +75499,6 @@ function extractCompactionSummary(response) {
75479
75499
  return summary;
75480
75500
  }
75481
75501
  const COMPACTION_INSTRUCTION = (customInstruction = "") => renderPrompt(compaction_instruction_default, { customInstruction });
75482
- function compactionTelemetryTrigger(trigger, instruction) {
75483
- if (trigger === void 0) return "unknown";
75484
- if (trigger === "manual" && instruction !== void 0 && instruction.length > 0) return "manual-with-prompt";
75485
- return trigger;
75486
- }
75487
75502
  //#endregion
75488
75503
  //#region ../../packages/agent-core/src/agent/compaction/micro.ts
75489
75504
  const DEFAULT_CONFIG = {
@@ -75560,20 +75575,8 @@ var MicroCompaction = class {
75560
75575
  const maxContextTokens = this.agent.config.modelCapabilities.max_context_tokens;
75561
75576
  const contextTokens = this.agent.context.tokenCountWithPending;
75562
75577
  if ((maxContextTokens !== void 0 && maxContextTokens > 0 ? contextTokens / maxContextTokens : 1) < config.minContextUsageRatio) return;
75563
- const previousCutoff = this.cutoff;
75564
75578
  const nextCutoff = Math.max(0, history.length - config.keepRecentMessages);
75565
75579
  this.apply(nextCutoff);
75566
- if (previousCutoff !== nextCutoff) {
75567
- const effect = this.measureEffect(history, nextCutoff);
75568
- this.agent.telemetry.track("micro_compaction_applied", {
75569
- previous_cutoff: previousCutoff,
75570
- cutoff: nextCutoff,
75571
- message_count: history.length,
75572
- truncated_count: effect.truncatedToolResultCount,
75573
- before_tokens: effect.beforeTokens,
75574
- after_tokens: effect.afterTokens
75575
- });
75576
- }
75577
75580
  }
75578
75581
  /**
75579
75582
  * Apply micro-compaction to a message list: replace old tool results
@@ -76127,13 +76130,6 @@ function createCronScheduler(opts) {
76127
76130
  };
76128
76131
  }
76129
76132
  //#endregion
76130
- //#region ../../packages/agent-core/src/tools/cron/telemetry-events.ts
76131
- /** Telemetry event names emitted by the cron subsystem. Centralised so a typo can't drift a metric. */
76132
- const CRON_SCHEDULED = "cron_scheduled";
76133
- const CRON_FIRED = "cron_fired";
76134
- const CRON_MISSED = "cron_missed";
76135
- const CRON_DELETED = "cron_deleted";
76136
- //#endregion
76137
76133
  //#region ../../packages/agent-core/src/agent/cron/manager.ts
76138
76134
  /**
76139
76135
  * Threshold past which a recurring task is flagged `stale: true` on its
@@ -76234,7 +76230,7 @@ var CronManager = class {
76234
76230
  * subset of ids that were actually present, matching
76235
76231
  * `SessionCronStore.remove`'s contract — callers (CronDelete /
76236
76232
  * scheduler one-shot cleanup / stale auto-expire) read this to
76237
- * decide whether to emit telemetry.
76233
+ * decide whether to emit a `cron.fired` event.
76238
76234
  *
76239
76235
  * Persistence failures are logged and swallowed; cross-resume the
76240
76236
  * worst case is a ghost entry that gets dropped on the next
@@ -76381,14 +76377,13 @@ var CronManager = class {
76381
76377
  return Number.isFinite(age) && age >= STALE_THRESHOLD_MS;
76382
76378
  }
76383
76379
  /**
76384
- * Translate a scheduler fire into a steer + telemetry event.
76380
+ * Translate a scheduler fire into a steer carrying a `cron_job` origin.
76385
76381
  *
76386
76382
  * `agent.turn.steer` returns the new turnId, or `null` when the input
76387
76383
  * was buffered because a turn is in flight (see turn/index.ts:84).
76388
- * We propagate that as `buffered` on the telemetry props so dashboards
76384
+ * We propagate that as `buffered` on the origin so the rendered envelope
76389
76385
  * can distinguish "fired into a fresh turn" from "fired into a steer
76390
76386
  * buffer that may not run until the user's turn ends".
76391
- *
76392
76387
  * Honours the documented 7-day auto-expire contract for recurring
76393
76388
  * tasks: a stale recurring task gets exactly one final delivery
76394
76389
  * (already issued above) and is then removed from the store. The
@@ -76411,22 +76406,13 @@ var CronManager = class {
76411
76406
  type: "text",
76412
76407
  text: renderCronFireXml(origin, task.prompt)
76413
76408
  }];
76414
- const turnId = this.agent.turn.steer(content, origin);
76409
+ this.agent.turn.steer(content, origin);
76415
76410
  this.agent.emitEvent?.({
76416
76411
  type: "cron.fired",
76417
76412
  origin,
76418
76413
  prompt: task.prompt
76419
76414
  });
76420
- this.agent.telemetry.track(CRON_FIRED, {
76421
- recurring: task.recurring !== false,
76422
- coalesced_count: ctx.coalescedCount,
76423
- stale,
76424
- buffered: turnId === null
76425
- });
76426
- if (stale && task.recurring !== false) {
76427
- this.removeTasks([task.id]);
76428
- this.emitDeleted(task.id);
76429
- }
76415
+ if (stale && task.recurring !== false) this.removeTasks([task.id]);
76430
76416
  }
76431
76417
  /**
76432
76418
  * Reserved hook for an explicit "you missed N fires while offline"
@@ -76455,26 +76441,6 @@ var CronManager = class {
76455
76441
  count: tasks.length
76456
76442
  };
76457
76443
  this.agent.turn.steer(content, origin);
76458
- this.agent.telemetry.track(CRON_MISSED, { count: tasks.length });
76459
- }
76460
- /**
76461
- * Emit `cron_scheduled` for a freshly-added task. Called by
76462
- * `CronCreate` after a successful `store.add(...)`. Kept as an
76463
- * explicit method so the tool layer never reaches into
76464
- * `manager.agent.telemetry` — preserves the "tools see the manager,
76465
- * the manager sees the agent" layering and matches the symmetric
76466
- * `emitDeleted` used by `CronDelete` (P1.6).
76467
- */
76468
- emitScheduled(task) {
76469
- this.agent.telemetry.track(CRON_SCHEDULED, { recurring: task.recurring !== false });
76470
- }
76471
- /**
76472
- * Emit `cron_deleted` for a removed task. Wired up here so P1.6 can
76473
- * land without touching this file again. `task_id` matches the field
76474
- * naming used elsewhere in the telemetry surface (snake_case).
76475
- */
76476
- emitDeleted(taskId) {
76477
- this.agent.telemetry.track(CRON_DELETED, { task_id: taskId });
76478
76444
  }
76479
76445
  /**
76480
76446
  * Wire `SIGUSR1` to a manual `tick()` so bench scripts can advance the
@@ -76502,7 +76468,7 @@ var CronManager = class {
76502
76468
  *
76503
76469
  * The handler swallows any throw from `tick()` because a signal-driven
76504
76470
  * bench tool must never crash the host process; the tick failure mode
76505
- * is already surfaced via telemetry / logs inside the scheduler.
76471
+ * is already surfaced via logs inside the scheduler.
76506
76472
  * Set `SCREAM_CRON_DEBUG=1` to surface the swallowed error to stderr —
76507
76473
  * mirrors `scheduler.ts`'s debugLog pattern so bench debugging can
76508
76474
  * see a bad tick.
@@ -77938,7 +77904,6 @@ var ExitPlanModeReviewAskPermissionPolicy = class {
77938
77904
  const display = context.execution.display;
77939
77905
  if (display?.kind !== "plan_review") return;
77940
77906
  if (display.plan.trim().length === 0) return;
77941
- this.agent.telemetry.track("plan_submitted", { has_options: display.options !== void 0 && display.options.length >= 2 });
77942
77907
  return {
77943
77908
  kind: "ask",
77944
77909
  reason: { has_options: display.options !== void 0 },
@@ -77957,11 +77922,6 @@ var ExitPlanModeReviewAskPermissionPolicy = class {
77957
77922
  kind: "result",
77958
77923
  syntheticResult: failed
77959
77924
  };
77960
- if (result.selectedLabel !== void 0 && result.selectedLabel.length > 0) this.agent.telemetry.track("plan_resolved", {
77961
- outcome: "approved",
77962
- chosen_option: result.selectedLabel
77963
- });
77964
- else this.agent.telemetry.track("plan_resolved", { outcome: "approved" });
77965
77925
  return {
77966
77926
  kind: "result",
77967
77927
  syntheticResult: {
@@ -77971,7 +77931,6 @@ var ExitPlanModeReviewAskPermissionPolicy = class {
77971
77931
  };
77972
77932
  }
77973
77933
  rejectedExitPlanModeApprovalResult(result) {
77974
- this.trackRejectedPlanResolution(result);
77975
77934
  if (result.decision === "cancelled") return {
77976
77935
  kind: "result",
77977
77936
  syntheticResult: {
@@ -78014,25 +77973,6 @@ var ExitPlanModeReviewAskPermissionPolicy = class {
78014
77973
  };
78015
77974
  }
78016
77975
  }
78017
- trackRejectedPlanResolution(result) {
78018
- if (result.decision === "cancelled") {
78019
- this.agent.telemetry.track("plan_resolved", { outcome: "dismissed" });
78020
- return;
78021
- }
78022
- if (result.selectedLabel === "Reject and Exit") {
78023
- this.agent.telemetry.track("plan_resolved", { outcome: "rejected_and_exited" });
78024
- return;
78025
- }
78026
- const feedback = result.feedback ?? "";
78027
- if (result.selectedLabel === "Revise" || feedback.length > 0) {
78028
- this.agent.telemetry.track("plan_resolved", {
78029
- outcome: "revise",
78030
- has_feedback: feedback.length > 0
78031
- });
78032
- return;
78033
- }
78034
- this.agent.telemetry.track("plan_resolved", { outcome: "rejected" });
78035
- }
78036
77976
  };
78037
77977
  function selectedExitPlanModeOption(options, label) {
78038
77978
  if (options === void 0 || label === void 0) return;
@@ -78289,10 +78229,10 @@ function writesOnlyPlanFile(context, planFilePath) {
78289
78229
  //#endregion
78290
78230
  //#region ../../packages/agent-core/src/agent/turn/canonical-args.ts
78291
78231
  /**
78292
- * JSON canonicalization used by tool-call telemetry and dedup.
78232
+ * JSON canonicalization used by tool-call deduplication.
78293
78233
  * Recursively sorts object keys so semantically-equal args produce identical keys.
78294
78234
  */
78295
- function canonicalTelemetryArgs(args) {
78235
+ function canonicalDedupArgs(args) {
78296
78236
  return JSON.stringify(sortJsonValue(args)) ?? String(args);
78297
78237
  }
78298
78238
  function sortJsonValue(value) {
@@ -78628,13 +78568,6 @@ var PermissionManager = class {
78628
78568
  async beforeToolCall(context) {
78629
78569
  const evaluation = await this.evaluatePolicies(context);
78630
78570
  if (evaluation === void 0) return void 0;
78631
- this.agent.telemetry.track("permission_policy_decision", {
78632
- policy_name: evaluation.policyName,
78633
- tool_name: context.toolCall.name,
78634
- permission_mode: this.mode,
78635
- decision: evaluation.result.kind,
78636
- ...evaluation.result.reason
78637
- });
78638
78571
  return this.permissionPolicyResolutionToPrepare(evaluation.result, context, evaluation.policyName);
78639
78572
  }
78640
78573
  async requestToolApproval(context, result, policyName) {
@@ -78680,16 +78613,6 @@ var PermissionManager = class {
78680
78613
  response = await Promise.race([customPromise, timeoutPromise]);
78681
78614
  } catch (error) {
78682
78615
  this.pendingApprovals.delete(approvalId);
78683
- this.agent.telemetry.track("permission_approval_result", {
78684
- policy_name: policyName ?? null,
78685
- tool_name: name,
78686
- permission_mode: this.mode,
78687
- result: "error",
78688
- approval_surface: display.kind,
78689
- duration_ms: Date.now() - startedAt,
78690
- session_cache_written: false,
78691
- has_feedback: false
78692
- });
78693
78616
  const resolved = result.resolveError?.(error);
78694
78617
  return await (resolved === void 0 ? Promise.reject(error) : this.permissionPolicyResolutionToPrepare(resolved, context, policyName));
78695
78618
  } finally {
@@ -78705,16 +78628,6 @@ var PermissionManager = class {
78705
78628
  sessionApprovalRule,
78706
78629
  result: response
78707
78630
  });
78708
- this.agent.telemetry.track("permission_approval_result", {
78709
- policy_name: policyName ?? null,
78710
- tool_name: name,
78711
- permission_mode: this.mode,
78712
- result: response.decision === "approved" && response.scope === "session" ? "approved_for_session" : response.decision,
78713
- approval_surface: display.kind,
78714
- duration_ms: Date.now() - startedAt,
78715
- session_cache_written: sessionApprovalRule !== void 0,
78716
- has_feedback: response.feedback !== void 0 && response.feedback.length > 0
78717
- });
78718
78631
  const resolved = result.resolveApproval?.(response);
78719
78632
  if (resolved !== void 0) return this.permissionPolicyResolutionToPrepare(resolved, context, policyName);
78720
78633
  if (response.decision === "approved") return;
@@ -79894,11 +79807,6 @@ var SkillManager = class {
79894
79807
  skillPath: origin.skillPath,
79895
79808
  skillSource: origin.skillSource
79896
79809
  });
79897
- this.agent.telemetry.track("skill_invoked", {
79898
- skill_name: origin.skillName,
79899
- trigger: origin.trigger
79900
- });
79901
- if (origin.skillType === "flow") this.agent.telemetry.track("flow_invoked", { flow_name: origin.skillName });
79902
79810
  if (input !== void 0) this.agent.turn.prompt(input, origin);
79903
79811
  }
79904
79812
  };
@@ -87679,7 +87587,7 @@ async function runPrepareToolExecutionHook(step, call) {
87679
87587
  llm
87680
87588
  });
87681
87589
  } catch (error) {
87682
- if (isAbortError$3(error) || signal.aborted) return {
87590
+ if (isAbortError$1(error) || signal.aborted) return {
87683
87591
  kind: "hookFailed",
87684
87592
  args,
87685
87593
  output: `Tool "${call.toolName}" was aborted during prepareToolExecution hook`
@@ -87722,7 +87630,7 @@ async function runAuthorizeToolExecutionHook(step, call, args, execution) {
87722
87630
  llm
87723
87631
  });
87724
87632
  } catch (error) {
87725
- if (isAbortError$3(error) || signal.aborted) return {
87633
+ if (isAbortError$1(error) || signal.aborted) return {
87726
87634
  block: true,
87727
87635
  reason: `Tool "${call.toolName}" was aborted during authorizeToolExecution hook`
87728
87636
  };
@@ -87749,7 +87657,7 @@ async function runRunnableToolCall(step, call, effectiveArgs, metadata, executio
87749
87657
  try {
87750
87658
  toolResult = coerceToolResult(await executeTool(step, execution, toolCall, toolName, metadata), toolName);
87751
87659
  } catch (error) {
87752
- const aborted = isAbortError$3(error) || signal.aborted;
87660
+ const aborted = isAbortError$1(error) || signal.aborted;
87753
87661
  if (!aborted) step.log?.warn("tool execution failed", {
87754
87662
  toolName,
87755
87663
  toolCallId: toolCall.id,
@@ -87781,7 +87689,7 @@ async function finalizePendingToolResult(step, pendingResult) {
87781
87689
  result: normalizeToolResult(effectiveResult)
87782
87690
  };
87783
87691
  } catch (error) {
87784
- const aborted = isAbortError$3(error) || signal.aborted;
87692
+ const aborted = isAbortError$1(error) || signal.aborted;
87785
87693
  if (!aborted) step.log?.warn("finalizeToolResult hook failed", {
87786
87694
  toolName: pendingResult.toolName,
87787
87695
  toolCallId: pendingResult.toolCall.id,
@@ -88135,7 +88043,7 @@ async function runTurn(input) {
88135
88043
  }))?.continue) break;
88136
88044
  }
88137
88045
  } catch (error) {
88138
- if (isAbortError$3(error) || signal.aborted) {
88046
+ if (isAbortError$1(error) || signal.aborted) {
88139
88047
  dispatchEvent(makeInterruptedEvent("aborted", steps, activeStep));
88140
88048
  return {
88141
88049
  stopReason: "aborted",
@@ -92970,7 +92878,7 @@ var ToolManager = class {
92970
92878
  //#region ../../packages/agent-core/src/agent/turn/tool-dedup.ts
92971
92879
  const REMINDER_TEXT_1 = "\n\n<system-reminder>\nYou are repeating the exact same tool call with identical parameters. Please carefully analyze the previous result. If the task is not yet complete, try a different method or parameters instead of repeating the same call.\n</system-reminder>";
92972
92880
  function makeReminderText2(toolName, repeatCount, args) {
92973
- const argsStr = canonicalTelemetryArgs(args);
92881
+ const argsStr = canonicalDedupArgs(args);
92974
92882
  return `
92975
92883
 
92976
92884
  <system-reminder>
@@ -92990,7 +92898,7 @@ function makeDeferred() {
92990
92898
  };
92991
92899
  }
92992
92900
  function makeKey(toolName, args) {
92993
- return `${toolName} ${canonicalTelemetryArgs(args)}`;
92901
+ return `${toolName} ${canonicalDedupArgs(args)}`;
92994
92902
  }
92995
92903
  function appendReminder(result, reminderText) {
92996
92904
  const output = result.output;
@@ -93187,13 +93095,7 @@ var TurnFlow = class {
93187
93095
  steerBuffer = [];
93188
93096
  turnId = -1;
93189
93097
  activeTurn = null;
93190
- toolCallStartedAt = /* @__PURE__ */ new Map();
93191
- toolCallDupType = /* @__PURE__ */ new Map();
93192
- stepToolCallKeys = /* @__PURE__ */ new Map();
93193
- telemetryModeByTurn = /* @__PURE__ */ new Map();
93194
93098
  currentStepByTurn = /* @__PURE__ */ new Map();
93195
- interruptedTelemetryTurnIds = /* @__PURE__ */ new Set();
93196
- stepFailureByTurn = /* @__PURE__ */ new Map();
93197
93099
  currentStep = 0;
93198
93100
  constructor(agent) {
93199
93101
  this.agent = agent;
@@ -93385,13 +93287,8 @@ var TurnFlow = class {
93385
93287
  */
93386
93288
  async runOneTurn(turnId, input, origin, signal, standalone) {
93387
93289
  this.currentStep = 0;
93388
- this.stepToolCallKeys.clear();
93389
- this.toolCallDupType.clear();
93390
93290
  this.agent.workingSet.decay(turnId);
93391
- const telemetryMode = this.telemetryMode();
93392
- this.telemetryModeByTurn.set(turnId, telemetryMode);
93393
93291
  this.currentStepByTurn.set(turnId, 0);
93394
- this.agent.telemetry.track("turn_started", { mode: telemetryMode });
93395
93292
  this.agent.fullCompaction.resetForTurn();
93396
93293
  this.agent.injection.resetForTurn();
93397
93294
  this.agent.usage.beginTurn();
@@ -93401,7 +93298,6 @@ var TurnFlow = class {
93401
93298
  origin
93402
93299
  });
93403
93300
  this.agent.context.appendUserMessage(input, origin);
93404
- const startedAt = Date.now();
93405
93301
  let ended;
93406
93302
  let completedStopReason;
93407
93303
  let errorEvent;
@@ -93418,7 +93314,7 @@ var TurnFlow = class {
93418
93314
  };
93419
93315
  }
93420
93316
  } catch (error) {
93421
- if (isAbortError$3(error)) ended = {
93317
+ if (isAbortError$1(error)) ended = {
93422
93318
  type: "turn.ended",
93423
93319
  turnId,
93424
93320
  reason: "cancelled"
@@ -93443,30 +93339,13 @@ var TurnFlow = class {
93443
93339
  type: "error",
93444
93340
  ...summary
93445
93341
  };
93446
- if (this.shouldTrackApiError(turnId)) {
93447
- const classification = classifyApiError(error, summary);
93448
- const properties = {
93449
- error_type: classification.errorType,
93450
- model: this.agent.config.model,
93451
- retryable: summary.retryable,
93452
- duration_ms: Date.now() - startedAt
93453
- };
93454
- if (classification.statusCode !== void 0) properties["status_code"] = classification.statusCode;
93455
- const inputTokens = currentTurnInputTokens(this.agent.usage.data().currentTurn);
93456
- if (inputTokens !== void 0) properties["input_tokens"] = inputTokens;
93457
- this.agent.telemetry.track("api_error", properties);
93458
- }
93459
93342
  }
93460
93343
  }
93461
93344
  if (this.currentId === turnId) this.agent.usage.endTurn();
93462
93345
  this.agent.emitEvent(ended);
93463
93346
  if (standalone && this.currentId === turnId) this.activeTurn = null;
93464
93347
  if (errorEvent !== void 0) this.agent.emitEvent(errorEvent);
93465
- if (ended.reason !== "completed") this.trackTurnInterrupted(turnId, this.currentStepByTurn.get(turnId) ?? this.currentStep);
93466
- this.telemetryModeByTurn.delete(turnId);
93467
93348
  this.currentStepByTurn.delete(turnId);
93468
- this.interruptedTelemetryTurnIds.delete(turnId);
93469
- this.stepFailureByTurn.delete(turnId);
93470
93349
  return {
93471
93350
  event: ended,
93472
93351
  stopReason: completedStopReason
@@ -93650,94 +93529,21 @@ var TurnFlow = class {
93650
93529
  this.agent.context.appendLoopEvent(event);
93651
93530
  },
93652
93531
  emitLiveEvent: (event) => {
93653
- this.trackLoopTelemetry(event, turnId);
93532
+ this.updateCurrentStepFromLoopEvent(event, turnId);
93654
93533
  const mapped = mapLoopEvent(event, turnId);
93655
93534
  if (mapped !== void 0) this.agent.emitEvent(mapped);
93656
93535
  }
93657
93536
  });
93658
93537
  }
93659
- trackLoopTelemetry(event, turnId) {
93538
+ updateCurrentStepFromLoopEvent(event, turnId) {
93660
93539
  if (event.type === "step.begin") {
93661
93540
  this.beginTrackedStep(turnId, event.step);
93662
93541
  return;
93663
93542
  }
93664
- if (event.type === "turn.interrupted") {
93665
- if (event.reason === "error" && event.activeStep !== void 0) this.stepFailureByTurn.set(turnId, event);
93666
- this.trackTurnInterrupted(turnId, interruptedStep(event));
93667
- return;
93668
- }
93669
- this.trackToolLifecycle(event, turnId);
93670
93543
  }
93671
93544
  beginTrackedStep(turnId, step) {
93672
93545
  this.currentStepByTurn.set(turnId, step);
93673
93546
  this.currentStep = step;
93674
- if (!this.stepToolCallKeys.has(step)) this.stepToolCallKeys.set(step, /* @__PURE__ */ new Set());
93675
- }
93676
- trackToolLifecycle(event, turnId) {
93677
- if (event.type === "tool.call") {
93678
- const dupType = this.trackDuplicateToolCall(turnId, event.step, event.name, event.args);
93679
- this.toolCallDupType.set(event.toolCallId, dupType === "cross_step" ? "cross_step" : "normal");
93680
- this.toolCallStartedAt.set(event.toolCallId, {
93681
- name: event.name,
93682
- startedAt: Date.now()
93683
- });
93684
- return;
93685
- }
93686
- if (event.type === "tool.result") {
93687
- const started = this.toolCallStartedAt.get(event.toolCallId);
93688
- if (started === void 0) return;
93689
- this.toolCallStartedAt.delete(event.toolCallId);
93690
- const dupType = this.toolCallDupType.get(event.toolCallId) ?? "normal";
93691
- this.toolCallDupType.delete(event.toolCallId);
93692
- const outcome = telemetryToolOutcome(event.result);
93693
- const properties = {
93694
- tool_name: started.name,
93695
- outcome,
93696
- duration_ms: Date.now() - started.startedAt,
93697
- dup_type: dupType
93698
- };
93699
- const errorType = outcome === "error" ? telemetryToolErrorType(event.result) : void 0;
93700
- if (errorType !== void 0) properties["error_type"] = errorType;
93701
- this.agent.telemetry.track("tool_call", properties);
93702
- }
93703
- }
93704
- trackDuplicateToolCall(turnId, step, toolName, args) {
93705
- const argsText = canonicalTelemetryArgs(args);
93706
- const key = `${toolName}\u0000${argsText}`;
93707
- const stepKeys = this.stepToolCallKeys.get(step) ?? /* @__PURE__ */ new Set();
93708
- this.stepToolCallKeys.set(step, stepKeys);
93709
- let dupType;
93710
- if (stepKeys.has(key)) dupType = "same_step";
93711
- else if (this.hasPriorStepToolCallKey(step, key)) dupType = "cross_step";
93712
- stepKeys.add(key);
93713
- if (dupType === void 0) return "normal";
93714
- this.agent.telemetry.track("tool_call_dedup_detected", {
93715
- turn_id: turnId,
93716
- step_no: step,
93717
- tool_name: toolName,
93718
- dup_type: dupType,
93719
- args_hash: createHash("sha256").update(argsText).digest("hex").slice(0, 8)
93720
- });
93721
- return dupType;
93722
- }
93723
- hasPriorStepToolCallKey(step, key) {
93724
- for (const [seenStep, keys] of this.stepToolCallKeys) if (seenStep !== step && keys.has(key)) return true;
93725
- return false;
93726
- }
93727
- trackTurnInterrupted(turnId, atStep) {
93728
- if (this.interruptedTelemetryTurnIds.has(turnId)) return;
93729
- this.interruptedTelemetryTurnIds.add(turnId);
93730
- this.agent.telemetry.track("turn_interrupted", {
93731
- mode: this.telemetryModeByTurn.get(turnId) ?? this.telemetryMode(),
93732
- at_step: atStep
93733
- });
93734
- }
93735
- telemetryMode() {
93736
- return this.agent.planMode.isActive ? "plan" : "agent";
93737
- }
93738
- shouldTrackApiError(turnId) {
93739
- const failure = this.stepFailureByTurn.get(turnId);
93740
- return failure?.reason === "error" && failure.activeStep !== void 0;
93741
93547
  }
93742
93548
  };
93743
93549
  function mapLoopEvent(event, turnId) {
@@ -93842,7 +93648,7 @@ function summarizeTurnError(error, turnId) {
93842
93648
  };
93843
93649
  }
93844
93650
  function toolInputRecord(args) {
93845
- return isPlainRecord(args) ? args : {};
93651
+ return typeof args === "object" && args !== null && !Array.isArray(args) ? args : {};
93846
93652
  }
93847
93653
  function toolOutputText(output) {
93848
93654
  if (typeof output === "string") return output;
@@ -93850,90 +93656,6 @@ function toolOutputText(output) {
93850
93656
  return typeof part === "object" && part !== null && part.type === "text";
93851
93657
  }).map((part) => part.text).join("");
93852
93658
  }
93853
- function interruptedStep(event) {
93854
- return event.activeStep ?? event.attemptedSteps;
93855
- }
93856
- function classifyApiError(error, summary) {
93857
- const statusCode = apiStatusCode(error) ?? summaryStatusCode(summary);
93858
- if (statusCode !== void 0) {
93859
- if (statusCode === 429) return {
93860
- errorType: "rate_limit",
93861
- statusCode
93862
- };
93863
- if (statusCode === 401 || statusCode === 403) return {
93864
- errorType: "auth",
93865
- statusCode
93866
- };
93867
- if (statusCode >= 500) return {
93868
- errorType: "5xx_server",
93869
- statusCode
93870
- };
93871
- if (isContextOverflowStatusError(statusCode, summary.message)) return {
93872
- errorType: "context_overflow",
93873
- statusCode
93874
- };
93875
- if (statusCode >= 400) return {
93876
- errorType: "4xx_client",
93877
- statusCode
93878
- };
93879
- return {
93880
- errorType: "api",
93881
- statusCode
93882
- };
93883
- }
93884
- if (summary.code === ErrorCodes.PROVIDER_RATE_LIMIT) return { errorType: "rate_limit" };
93885
- if (summary.code === ErrorCodes.PROVIDER_AUTH_ERROR) return { errorType: "auth" };
93886
- if (summary.code === ErrorCodes.CONTEXT_OVERFLOW) return { errorType: "context_overflow" };
93887
- if (isApiConnectionError(error, summary)) return { errorType: "network" };
93888
- if (isApiTimeoutError(error, summary)) return { errorType: "timeout" };
93889
- if (isApiEmptyResponseError(error, summary)) return { errorType: "empty_response" };
93890
- return { errorType: "other" };
93891
- }
93892
- function apiStatusCode(error) {
93893
- if (error instanceof APIStatusError) {
93894
- const statusCode = error.statusCode;
93895
- return typeof statusCode === "number" ? statusCode : void 0;
93896
- }
93897
- if (typeof error !== "object" || error === null) return void 0;
93898
- const statusCode = error.statusCode;
93899
- if (typeof statusCode === "number") return statusCode;
93900
- const status = error.status;
93901
- return typeof status === "number" ? status : void 0;
93902
- }
93903
- function summaryStatusCode(summary) {
93904
- const statusCode = summary.details?.["statusCode"];
93905
- return typeof statusCode === "number" ? statusCode : void 0;
93906
- }
93907
- function isApiConnectionError(error, summary) {
93908
- return error instanceof APIConnectionError$3 || summary.name === "APIConnectionError";
93909
- }
93910
- function isApiTimeoutError(error, summary) {
93911
- return error instanceof APITimeoutError || summary.name === "APITimeoutError" || summary.name === "TimeoutError";
93912
- }
93913
- function isApiEmptyResponseError(error, summary) {
93914
- return error instanceof APIEmptyResponseError || summary.name === "APIEmptyResponseError";
93915
- }
93916
- function currentTurnInputTokens(usage) {
93917
- if (usage === void 0) return void 0;
93918
- return inputTotal(usage);
93919
- }
93920
- function telemetryToolOutcome(result) {
93921
- if (result.isError !== true) return "success";
93922
- const text = toolResultText(result).toLowerCase();
93923
- return text.includes("aborted") || text.includes("cancelled") || text.includes("manually interrupted") ? "cancelled" : "error";
93924
- }
93925
- function telemetryToolErrorType(result) {
93926
- const text = toolResultText(result);
93927
- if (text.startsWith("Tool \"") && text.includes("\" not found")) return "ToolNotFound";
93928
- if (text.startsWith("Invalid args for tool \"")) return "ToolInputError";
93929
- if (text.includes("prepareToolExecution hook failed")) return "HookError";
93930
- if (text.includes("finalizeToolResult hook failed")) return "HookError";
93931
- if (text.includes("blocked")) return "ToolBlocked";
93932
- return "ToolError";
93933
- }
93934
- function toolResultText(result) {
93935
- return toolOutputText(result.output);
93936
- }
93937
93659
  /** Extract a short human-readable summary from tool arguments. */
93938
93660
  function summarizeToolArgs(args) {
93939
93661
  if (typeof args !== "object" || args === null) return "";
@@ -94233,7 +93955,6 @@ var Agent = class {
94233
93955
  mcp;
94234
93956
  hooks;
94235
93957
  log;
94236
- telemetry;
94237
93958
  blobStore;
94238
93959
  records;
94239
93960
  fullCompaction;
@@ -94271,7 +93992,6 @@ var Agent = class {
94271
93992
  this.mcp = options.mcp;
94272
93993
  this.hooks = options.hookEngine;
94273
93994
  this.log = options.log ?? log;
94274
- this.telemetry = options.telemetry ?? noopTelemetryClient;
94275
93995
  this.blobStore = options.homedir ? new BlobStore({ blobsDir: join$1(options.homedir, "blobs") }) : void 0;
94276
93996
  this.records = new AgentRecords(this, options.persistence ?? (options.homedir ? new FileSystemAgentRecordPersistence(join$1(options.homedir, "wire.jsonl"), {
94277
93997
  onError: (error) => {
@@ -94394,34 +94114,20 @@ var Agent = class {
94394
94114
  this.turn.prompt(payload.input);
94395
94115
  },
94396
94116
  steer: (payload) => {
94397
- this.telemetry.track("input_steer", { parts: payload.input.length });
94398
94117
  this.turn.steer(payload.input);
94399
94118
  },
94400
94119
  cancel: (payload) => {
94401
- if (this.turn.hasActiveTurn) this.telemetry.track("cancel", { from: "streaming" });
94402
94120
  this.turn.cancel(payload.turnId);
94403
94121
  },
94404
94122
  setThinking: (payload) => {
94405
- const wasEnabled = this.config.thinkingLevel !== "off";
94406
94123
  this.config.update({ thinkingLevel: payload.level });
94407
- const enabled = this.config.thinkingLevel !== "off";
94408
- if (enabled !== wasEnabled) this.telemetry.track("thinking_toggle", { enabled });
94409
94124
  },
94410
94125
  setPermission: (payload) => {
94411
- const wasYolo = this.permission.mode === "yolo";
94412
- const wasAuto = this.permission.mode === "auto";
94413
94126
  this.permission.setMode(payload.mode);
94414
- const enabled = this.permission.mode === "yolo";
94415
- if (enabled !== wasYolo) this.telemetry.track("yolo_toggle", { enabled });
94416
- const afkEnabled = this.permission.mode === "auto";
94417
- if (afkEnabled !== wasAuto) this.telemetry.track("afk_toggle", { enabled: afkEnabled });
94418
94127
  },
94419
94128
  setModel: (payload) => {
94420
94129
  const resolved = this.modelProvider?.resolveProviderConfig(payload.model);
94421
- if (this.config.modelAlias !== payload.model) {
94422
- this.config.update({ modelAlias: payload.model });
94423
- this.telemetry.track("model_switch", { model: payload.model });
94424
- }
94130
+ if (this.config.modelAlias !== payload.model) this.config.update({ modelAlias: payload.model });
94425
94131
  return {
94426
94132
  model: payload.model,
94427
94133
  providerName: resolved?.providerName
@@ -94450,7 +94156,6 @@ var Agent = class {
94450
94156
  });
94451
94157
  },
94452
94158
  cancelCompaction: () => {
94453
- if (this.fullCompaction.isCompacting) this.telemetry.track("cancel", { from: "compacting" });
94454
94159
  this.fullCompaction.cancel();
94455
94160
  },
94456
94161
  registerTool: (payload) => {
@@ -94841,7 +94546,6 @@ const ScreamConfigSchema = z.object({
94841
94546
  extraSkillDirs: z.array(z.string()).optional(),
94842
94547
  loopControl: LoopControlSchema.optional(),
94843
94548
  background: BackgroundConfigSchema.optional(),
94844
- telemetry: z.boolean().optional(),
94845
94549
  raw: z.record(z.string(), z.unknown()).optional()
94846
94550
  });
94847
94551
  const ProviderConfigPatchSchema = ProviderConfigSchema.partial();
@@ -94874,8 +94578,7 @@ const ScreamConfigPatchSchema = z.object({
94874
94578
  mergeAllAvailableSkills: z.boolean().optional(),
94875
94579
  extraSkillDirs: z.array(z.string()).optional(),
94876
94580
  loopControl: LoopControlPatchSchema.optional(),
94877
- background: BackgroundConfigPatchSchema.optional(),
94878
- telemetry: z.boolean().optional()
94581
+ background: BackgroundConfigPatchSchema.optional()
94879
94582
  }).strict();
94880
94583
  function getDefaultConfig() {
94881
94584
  return { providers: {} };
@@ -95299,8 +95002,7 @@ function configToTomlData(config) {
95299
95002
  "defaultPermissionMode",
95300
95003
  "defaultPlanMode",
95301
95004
  "mergeAllAvailableSkills",
95302
- "extraSkillDirs",
95303
- "telemetry"
95005
+ "extraSkillDirs"
95304
95006
  ]) setDefined(out, camelToSnake$1(key), config[key]);
95305
95007
  setRecordSection(out, "providers", config.providers, providerToToml);
95306
95008
  setRecordSection(out, "models", config.models, modelToToml);
@@ -99279,10 +98981,6 @@ var SessionSubagentHost = class {
99279
98981
  description: options.description,
99280
98982
  runInBackground: options.runInBackground
99281
98983
  });
99282
- parent.telemetry.track("subagent_created", {
99283
- subagent_name: profileName,
99284
- run_in_background: options.runInBackground
99285
- });
99286
98984
  try {
99287
98985
  await prepareChild();
99288
98986
  options.signal.throwIfAborted();
@@ -99391,7 +99089,6 @@ const BACKGROUND_KEEP_ALIVE_ON_EXIT_ENV = "SCREAM_CODE_BACKGROUND_KEEP_ALIVE_ON_
99391
99089
  var Session$1 = class {
99392
99090
  options;
99393
99091
  rpc;
99394
- telemetry;
99395
99092
  skills;
99396
99093
  agents = /* @__PURE__ */ new Map();
99397
99094
  mcp;
@@ -99421,7 +99118,6 @@ var Session$1 = class {
99421
99118
  cwd: options.jian.getcwd(),
99422
99119
  sessionId: options.id
99423
99120
  });
99424
- this.telemetry = options.telemetry ?? noopTelemetryClient;
99425
99121
  this.skills = new SkillRegistry({ sessionId: options.id });
99426
99122
  this.mcp = new McpConnectionManager({
99427
99123
  oauthService: new McpOAuthService({ screamHomeDir: options.screamHomeDir }),
@@ -99610,14 +99306,14 @@ var Session$1 = class {
99610
99306
  const totalCount = entries.length;
99611
99307
  if (totalCount === 0) return;
99612
99308
  const connectedCount = entries.filter((entry) => entry.status === "connected").length;
99613
- if (connectedCount > 0) this.telemetry.track("mcp_connected", {
99614
- server_count: connectedCount,
99615
- total_count: totalCount
99309
+ if (connectedCount > 0) this.log.info("mcp servers connected", {
99310
+ connectedCount,
99311
+ totalCount
99616
99312
  });
99617
99313
  const failedCount = entries.filter((entry) => entry.status === "failed").length;
99618
- if (failedCount > 0) this.telemetry.track("mcp_failed", {
99619
- failed_count: failedCount,
99620
- total_count: totalCount
99314
+ if (failedCount > 0) this.log.warn("mcp servers failed", {
99315
+ failedCount,
99316
+ totalCount
99621
99317
  });
99622
99318
  }
99623
99319
  emitInitialMcpLoadError(error) {
@@ -99669,7 +99365,6 @@ var Session$1 = class {
99669
99365
  subagentHost: config.subagentHost ?? new SessionSubagentHost(this, id, this.backgroundTaskTimeoutMs()),
99670
99366
  mcp: this.mcp,
99671
99367
  permission: this.permissionOptions(parentAgentId, config.permission),
99672
- telemetry: this.telemetry,
99673
99368
  log: this.log.createChild({ agentId: id }),
99674
99369
  pluginSessionStarts: type === "main" ? this.options.pluginSessionStarts : void 0
99675
99370
  });
@@ -100954,6 +100649,50 @@ function isScreamNativeBinary() {
100954
100649
  return !path$1.basename(process.execPath).toLowerCase().startsWith("node");
100955
100650
  }
100956
100651
  //#endregion
100652
+ //#region ../../packages/agent-core/src/tools/providers/fetch-cache.ts
100653
+ /**
100654
+ * Simple in-memory LRU cache for URL fetch results.
100655
+ *
100656
+ * The cache keeps up to `maxSize` entries. Entries older than `hardTtlMs`
100657
+ * are treated as a miss and evicted. Entries younger than `softTtlMs` are
100658
+ * returned directly; entries between soft and hard TTL are also returned
100659
+ * (no background refresh) because web content freshness is unpredictable
100660
+ * and a stale result is worse than a slightly older cached result for
100661
+ * agent tooling.
100662
+ */
100663
+ var FetchCache = class {
100664
+ cache = /* @__PURE__ */ new Map();
100665
+ maxSize;
100666
+ hardTtlMs;
100667
+ constructor(options) {
100668
+ this.maxSize = options?.maxSize ?? 100;
100669
+ this.hardTtlMs = options?.hardTtlMs ?? 3600 * 1e3;
100670
+ }
100671
+ get(key) {
100672
+ const entry = this.cache.get(key);
100673
+ if (entry === void 0) return;
100674
+ if (Date.now() - entry.fetchedAt > this.hardTtlMs) {
100675
+ this.cache.delete(key);
100676
+ return;
100677
+ }
100678
+ this.cache.delete(key);
100679
+ this.cache.set(key, entry);
100680
+ return entry.result;
100681
+ }
100682
+ set(key, result) {
100683
+ while (this.cache.size >= this.maxSize && this.cache.size > 0) this.evictLRU();
100684
+ this.cache.set(key, {
100685
+ result,
100686
+ fetchedAt: Date.now()
100687
+ });
100688
+ }
100689
+ evictLRU() {
100690
+ const lru = this.cache.keys().next().value;
100691
+ if (lru === void 0) return;
100692
+ this.cache.delete(lru);
100693
+ }
100694
+ };
100695
+ //#endregion
100957
100696
  //#region ../../node_modules/.pnpm/@mozilla+readability@0.6.0/node_modules/@mozilla/readability/Readability.js
100958
100697
  var require_Readability = /* @__PURE__ */ __commonJSMin(((exports, module) => {
100959
100698
  /**
@@ -113623,9 +113362,9 @@ setPrototypeOf(Document, Document$1).prototype = Document$1.prototype;
113623
113362
  * common content containers (`<article>` / `<main>` / `<body>`)
113624
113363
  * before throwing a "meaningful content" error.
113625
113364
  */
113626
- const parseHTML = parseHTML$1;
113627
113365
  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";
113628
113366
  const DEFAULT_MAX_BYTES = 10 * 1024 * 1024;
113367
+ const parseHTML = parseHTML$1;
113629
113368
  /**
113630
113369
  * SSRF guard — reject non-http(s) schemes and (by default) any hostname
113631
113370
  * that is, or parses as, a private / loopback / link-local / ULA IP
@@ -113661,19 +113400,32 @@ function assertSafeFetchTarget(url, allowPrivate) {
113661
113400
  if (a === 127 || a === 10 || a === 192 && b === 168 || a === 172 && b >= 16 && b <= 31 || a === 169 && b === 254 || a === 0 || a === 100 && b >= 64 && b <= 127) throw new Error(`Refusing to fetch private address: "${host}"`);
113662
113401
  }
113663
113402
  }
113403
+ function cacheKey$1(url, allowPrivate, maxBytes, userAgent) {
113404
+ return `local:${url}:${String(allowPrivate)}:${String(maxBytes)}:${userAgent}`;
113405
+ }
113664
113406
  var LocalFetchURLProvider = class {
113665
113407
  userAgent;
113666
113408
  fetchImpl;
113667
113409
  maxBytes;
113668
113410
  allowPrivateAddresses;
113411
+ cache;
113669
113412
  constructor(options = {}) {
113670
113413
  this.userAgent = options.userAgent ?? DEFAULT_USER_AGENT;
113671
113414
  this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
113672
113415
  this.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
113673
113416
  this.allowPrivateAddresses = options.allowPrivateAddresses ?? false;
113417
+ this.cache = options.cache ?? new FetchCache();
113674
113418
  }
113675
113419
  async fetch(url, _options) {
113676
113420
  assertSafeFetchTarget(url, this.allowPrivateAddresses);
113421
+ const key = cacheKey$1(url, this.allowPrivateAddresses, this.maxBytes, this.userAgent);
113422
+ const cached = this.cache.get(key);
113423
+ if (cached !== void 0) return cached;
113424
+ const result = await this.fetchFresh(url);
113425
+ this.cache.set(key, result);
113426
+ return result;
113427
+ }
113428
+ async fetchFresh(url) {
113677
113429
  const response = await this.fetchImpl(url, {
113678
113430
  method: "GET",
113679
113431
  headers: { "User-Agent": this.userAgent }
@@ -113735,6 +113487,9 @@ var LocalFetchURLProvider = class {
113735
113487
  * is down.
113736
113488
  * 4. If localFallback also throws → propagate that error.
113737
113489
  */
113490
+ function cacheKey(baseUrl, url) {
113491
+ return `cli:${baseUrl}:${url}`;
113492
+ }
113738
113493
  var ScreamCliFetchURLProvider = class {
113739
113494
  tokenProvider;
113740
113495
  apiKey;
@@ -113743,6 +113498,7 @@ var ScreamCliFetchURLProvider = class {
113743
113498
  customHeaders;
113744
113499
  localFallback;
113745
113500
  fetchImpl;
113501
+ cache;
113746
113502
  constructor(options) {
113747
113503
  this.tokenProvider = options.tokenProvider;
113748
113504
  this.apiKey = options.apiKey;
@@ -113751,15 +113507,24 @@ var ScreamCliFetchURLProvider = class {
113751
113507
  this.customHeaders = options.customHeaders ?? {};
113752
113508
  this.localFallback = options.localFallback;
113753
113509
  this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
113510
+ this.cache = options.cache ?? new FetchCache();
113754
113511
  }
113755
113512
  async fetch(url, options) {
113513
+ const key = cacheKey(this.baseUrl, url);
113514
+ const cached = this.cache.get(key);
113515
+ if (cached !== void 0) return cached;
113516
+ const result = await this.fetchFresh(url, options?.toolCallId);
113517
+ this.cache.set(key, result);
113518
+ return result;
113519
+ }
113520
+ async fetchFresh(url, toolCallId) {
113756
113521
  try {
113757
113522
  return {
113758
- content: await this.fetchViaScreamCli(url, options?.toolCallId),
113523
+ content: await this.fetchViaScreamCli(url, toolCallId),
113759
113524
  kind: "extracted"
113760
113525
  };
113761
113526
  } catch {
113762
- return this.localFallback.fetch(url, options ?? {});
113527
+ return this.localFallback.fetch(url, { toolCallId });
113763
113528
  }
113764
113529
  }
113765
113530
  async fetchViaScreamCli(url, toolCallId) {
@@ -116876,7 +116641,6 @@ var ScreamCore = class {
116876
116641
  homeDir;
116877
116642
  configPath;
116878
116643
  sessions = /* @__PURE__ */ new Map();
116879
- telemetry;
116880
116644
  jian;
116881
116645
  runtime;
116882
116646
  config;
@@ -116904,7 +116668,6 @@ var ScreamCore = class {
116904
116668
  this.screamRequestHeaders = options.screamRequestHeaders;
116905
116669
  this.resolveOAuthTokenProvider = options.resolveOAuthTokenProvider;
116906
116670
  this.skillDirs = options.skillDirs ?? [];
116907
- this.telemetry = options.telemetry ?? noopTelemetryClient;
116908
116671
  ensureScreamHome(this.homeDir);
116909
116672
  this.config = loadRuntimeConfig(this.configPath);
116910
116673
  this.sessionStore = new SessionStore(this.homeDir);
@@ -116955,7 +116718,6 @@ var ScreamCore = class {
116955
116718
  permissionRules: config.permission?.rules,
116956
116719
  skills: this.resolveSessionSkillConfig(config),
116957
116720
  mcpConfig,
116958
- telemetry: withTelemetryContext$1(this.telemetry, { sessionId: summary.id }),
116959
116721
  pluginSessionStarts
116960
116722
  });
116961
116723
  try {
@@ -117033,7 +116795,6 @@ var ScreamCore = class {
117033
116795
  permissionRules: config.permission?.rules,
117034
116796
  skills: this.resolveSessionSkillConfig(config),
117035
116797
  mcpConfig,
117036
- telemetry: withTelemetryContext$1(this.telemetry, { sessionId: summary.id }),
117037
116798
  initializeMainAgent: false,
117038
116799
  pluginSessionStarts
117039
116800
  });
@@ -117043,7 +116804,6 @@ var ScreamCore = class {
117043
116804
  await this.refreshSessionRuntimeConfig(session, config);
117044
116805
  } catch (error) {
117045
116806
  await session.close().catch(() => {});
117046
- withTelemetryContext$1(this.telemetry, { sessionId: summary.id }).track("session_load_failed", { reason: telemetryErrorReason(error) });
117047
116807
  throw error;
117048
116808
  }
117049
116809
  this.sessions.set(summary.id, session);
@@ -117373,13 +117133,15 @@ var ScreamCore = class {
117373
117133
  }
117374
117134
  };
117375
117135
  async function createRuntimeConfig(input) {
117376
- const localFetcher = new LocalFetchURLProvider();
117136
+ const fetchCache = new FetchCache();
117137
+ const localFetcher = new LocalFetchURLProvider({ cache: fetchCache });
117377
117138
  const fetchService = input.config.services?.screamCliFetch;
117378
117139
  return {
117379
117140
  urlFetcher: fetchService?.baseUrl === void 0 ? localFetcher : new ScreamCliFetchURLProvider({
117380
117141
  baseUrl: fetchService.baseUrl,
117381
117142
  localFallback: localFetcher,
117382
117143
  defaultHeaders: input.screamRequestHeaders,
117144
+ cache: fetchCache,
117383
117145
  ...serviceCredentials(fetchService, input.resolveOAuthTokenProvider)
117384
117146
  }),
117385
117147
  webSearcher: buildWebSearcher(input)
@@ -117415,11 +117177,6 @@ function requiredWorkDir(operation, value) {
117415
117177
  function createSessionId() {
117416
117178
  return `session_${randomUUID()}`;
117417
117179
  }
117418
- function telemetryErrorReason(error) {
117419
- if (error instanceof ScreamError) return error.code;
117420
- if (error instanceof Error && error.name.length > 0) return error.name;
117421
- return typeof error;
117422
- }
117423
117180
  async function resumeSessionResult(summary, session, warning) {
117424
117181
  const api = new SessionAPIImpl(session);
117425
117182
  const agents = {};
@@ -117504,7 +117261,7 @@ function parsePositiveInt(value) {
117504
117261
  * production state.
117505
117262
  */
117506
117263
  const SCREAM_CODE_PLATFORM = "scream_code_cli";
117507
- function createScreamDeviceId(homeDir, options = {}) {
117264
+ function createScreamDeviceId(homeDir, _options = {}) {
117508
117265
  const deviceIdPath = join(homeDir, "device_id");
117509
117266
  if (existsSync(deviceIdPath)) try {
117510
117267
  const text = readFileSync(deviceIdPath, "utf-8").trim();
@@ -117521,9 +117278,6 @@ function createScreamDeviceId(homeDir, options = {}) {
117521
117278
  mode: 384
117522
117279
  });
117523
117280
  } catch {}
117524
- if (options.onFirstLaunch !== void 0) try {
117525
- options.onFirstLaunch(id);
117526
- } catch {}
117527
117281
  return id;
117528
117282
  }
117529
117283
  function createScreamDeviceHeaders(options) {
@@ -117624,8 +117378,7 @@ var SDKRpcClient = class {
117624
117378
  configPath: options.configPath,
117625
117379
  screamRequestHeaders,
117626
117380
  resolveOAuthTokenProvider: options.resolveOAuthTokenProvider,
117627
- skillDirs: options.skillDirs,
117628
- telemetry: options.telemetry
117381
+ skillDirs: options.skillDirs
117629
117382
  });
117630
117383
  this.ready = sdkRpc(new ClientAPI(this)).then((rpc) => {
117631
117384
  this.rpc = rpc;
@@ -118513,7 +118266,6 @@ var ScreamHarness = class {
118513
118266
  auth;
118514
118267
  identity;
118515
118268
  uiMode;
118516
- telemetry;
118517
118269
  activeSessions = /* @__PURE__ */ new Map();
118518
118270
  rpc;
118519
118271
  constructor(options) {
@@ -118525,7 +118277,6 @@ var ScreamHarness = class {
118525
118277
  configPath: options.configPath
118526
118278
  });
118527
118279
  this.configureLogging();
118528
- this.telemetry = options.telemetry ?? noopTelemetryClient;
118529
118280
  this.auth = new ScreamAuthFacade({
118530
118281
  homeDir: this.homeDir,
118531
118282
  configPath: this.configPath
@@ -118535,8 +118286,7 @@ var ScreamHarness = class {
118535
118286
  configPath: this.configPath,
118536
118287
  identity: this.identity,
118537
118288
  resolveOAuthTokenProvider: this.auth.resolveOAuthTokenProvider,
118538
- skillDirs: options.skillDirs,
118539
- telemetry: this.telemetry
118289
+ skillDirs: options.skillDirs
118540
118290
  });
118541
118291
  }
118542
118292
  configureLogging() {
@@ -118551,12 +118301,6 @@ var ScreamHarness = class {
118551
118301
  set interactiveAgentId(agentId) {
118552
118302
  this.rpc.interactiveAgentId = agentId;
118553
118303
  }
118554
- track(event, properties) {
118555
- this.telemetry.track(event, properties);
118556
- }
118557
- setTelemetryContext(patch) {
118558
- this.telemetry.setContext?.(patch);
118559
- }
118560
118304
  async createSession(options) {
118561
118305
  const { planMode, ...coreOptions } = options;
118562
118306
  const summary = await this.rpc.createSession(coreOptions);
@@ -118571,8 +118315,6 @@ var ScreamHarness = class {
118571
118315
  });
118572
118316
  this.activeSessions.set(session.id, session);
118573
118317
  if (planMode === true) await session.setPlanMode(true);
118574
- this.trackSessionStarted(summary.id, false);
118575
- this.trackSessionEvent(session.id, "session_new");
118576
118318
  return session;
118577
118319
  }
118578
118320
  async resumeSession(input) {
@@ -118590,8 +118332,6 @@ var ScreamHarness = class {
118590
118332
  }
118591
118333
  });
118592
118334
  this.activeSessions.set(session.id, session);
118593
- this.trackSessionStarted(summary.id, true);
118594
- this.trackSessionEvent(session.id, "session_resume");
118595
118335
  return session;
118596
118336
  }
118597
118337
  async forkSession(input) {
@@ -118611,8 +118351,6 @@ var ScreamHarness = class {
118611
118351
  }
118612
118352
  });
118613
118353
  this.activeSessions.set(session.id, session);
118614
- this.trackSessionStarted(summary.id, true);
118615
- this.trackSessionEvent(session.id, "session_fork");
118616
118354
  return session;
118617
118355
  }
118618
118356
  getSession(id) {
@@ -118630,12 +118368,10 @@ var ScreamHarness = class {
118630
118368
  this.activeSessions.get(input.id)?.emitMetaUpdated({ title: input.title });
118631
118369
  }
118632
118370
  async exportSession(input) {
118633
- const result = await this.rpc.exportSession({
118371
+ return await this.rpc.exportSession({
118634
118372
  ...input,
118635
118373
  version: input.version ?? this.identity?.version
118636
118374
  });
118637
- this.trackSessionEvent(input.id, "export");
118638
- return result;
118639
118375
  }
118640
118376
  async listSessions(options = {}) {
118641
118377
  return this.rpc.listSessions(options);
@@ -118666,17 +118402,6 @@ var ScreamHarness = class {
118666
118402
  await getRootLogger().flush();
118667
118403
  } catch {}
118668
118404
  }
118669
- trackSessionEvent(eventSessionId, event) {
118670
- withTelemetryContext$1(this.telemetry, { sessionId: eventSessionId }).track(event);
118671
- }
118672
- trackSessionStarted(eventSessionId, resumed) {
118673
- withTelemetryContext$1(this.telemetry, { sessionId: eventSessionId }).track("session_started", {
118674
- client_name: this.identity?.userAgentProduct ?? null,
118675
- client_version: this.identity?.version ?? null,
118676
- ui_mode: this.uiMode,
118677
- resumed
118678
- });
118679
- }
118680
118405
  };
118681
118406
  const DEFAULT_SESSION_STARTED_UI_MODE = "shell";
118682
118407
  function normalizeSessionId(value) {
@@ -118796,641 +118521,11 @@ function applyCatalogProvider(config, options) {
118796
118521
  return { defaultModel };
118797
118522
  }
118798
118523
  //#endregion
118799
- //#region ../../packages/telemetry/src/types.ts
118800
- function isTelemetryPrimitive(value) {
118801
- return value === null || value === void 0 || typeof value === "boolean" || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
118802
- }
118803
- //#endregion
118804
- //#region ../../packages/telemetry/src/client.ts
118805
- const MAX_QUEUE_SIZE = 1e3;
118806
- var TelemetryClient = class {
118807
- queue = [];
118808
- sink = null;
118809
- deviceId = null;
118810
- sessionId = null;
118811
- disabled = false;
118812
- setContext(input) {
118813
- if (input.deviceId !== void 0) this.deviceId = input.deviceId;
118814
- if (input.sessionId !== void 0) this.sessionId = input.sessionId;
118815
- }
118816
- withContext(input) {
118817
- return new ScopedTelemetryClient(this, input);
118818
- }
118819
- attachSink(sink) {
118820
- if (this.sink !== null && this.sink !== sink) {
118821
- this.sink.stopPeriodicFlush();
118822
- this.sink.flushSync();
118823
- }
118824
- this.sink = sink;
118825
- for (const event of this.queue) {
118826
- const record = toTelemetryEvent(event);
118827
- if (record.device_id === null && event.contextOverrides?.deviceId !== true) record.device_id = this.deviceId;
118828
- if (record.session_id === null && event.contextOverrides?.sessionId !== true) record.session_id = this.sessionId;
118829
- sink.accept(record);
118830
- }
118831
- this.queue = [];
118832
- }
118833
- disable() {
118834
- this.disabled = true;
118835
- this.queue = [];
118836
- if (this.sink !== null) {
118837
- this.sink.stopPeriodicFlush();
118838
- this.sink.clearBuffer();
118839
- this.sink = null;
118840
- }
118841
- }
118842
- enable() {
118843
- this.disabled = false;
118844
- }
118845
- track(event, properties = {}) {
118846
- this.trackWithContext(event, properties, {});
118847
- }
118848
- trackWithContext(event, properties = {}, context) {
118849
- if (this.disabled) return;
118850
- const record = {
118851
- event_id: randomUUID().replaceAll("-", ""),
118852
- device_id: context.deviceId === void 0 ? this.deviceId : context.deviceId,
118853
- session_id: context.sessionId === void 0 ? this.sessionId : context.sessionId,
118854
- event,
118855
- timestamp: Date.now() / 1e3,
118856
- properties: sanitizeProperties(properties),
118857
- contextOverrides: {
118858
- deviceId: context.deviceId !== void 0,
118859
- sessionId: context.sessionId !== void 0
118860
- }
118861
- };
118862
- if (this.sink !== null) {
118863
- this.sink.accept(toTelemetryEvent(record));
118864
- return;
118865
- }
118866
- this.queue.push(record);
118867
- if (this.queue.length > MAX_QUEUE_SIZE) this.queue = this.queue.slice(this.queue.length - MAX_QUEUE_SIZE);
118868
- }
118869
- getSink() {
118870
- return this.sink;
118871
- }
118872
- async flush(signal) {
118873
- await this.sink?.flush(signal);
118874
- }
118875
- flushSync() {
118876
- this.sink?.flushSync();
118877
- }
118878
- async shutdown(options = {}) {
118879
- const sink = this.sink;
118880
- if (sink === null) return;
118881
- sink.stopPeriodicFlush();
118882
- if (options.timeoutMs === void 0) {
118883
- await sink.flush();
118884
- return;
118885
- }
118886
- const controller = new AbortController();
118887
- const timer = setTimeout(() => {
118888
- controller.abort();
118889
- }, options.timeoutMs);
118890
- timer.unref?.();
118891
- try {
118892
- await sink.flush(controller.signal);
118893
- } catch {
118894
- sink.flushSync();
118895
- } finally {
118896
- clearTimeout(timer);
118897
- }
118898
- }
118899
- resetForTests() {
118900
- this.sink?.stopPeriodicFlush();
118901
- this.queue = [];
118902
- this.sink = null;
118903
- this.deviceId = null;
118904
- this.sessionId = null;
118905
- this.disabled = false;
118906
- }
118907
- };
118908
- var ScopedTelemetryClient = class ScopedTelemetryClient extends TelemetryClient {
118909
- parent;
118910
- context;
118911
- constructor(parent, context) {
118912
- super();
118913
- this.parent = parent;
118914
- this.context = context;
118915
- }
118916
- setContext(input) {
118917
- this.parent.setContext(input);
118918
- }
118919
- withContext(input) {
118920
- return new ScopedTelemetryClient(this.parent, mergeContext(this.context, input));
118921
- }
118922
- attachSink(sink) {
118923
- this.parent.attachSink(sink);
118924
- }
118925
- disable() {
118926
- this.parent.disable();
118927
- }
118928
- enable() {
118929
- this.parent.enable();
118930
- }
118931
- track(event, properties = {}) {
118932
- this.parent.trackWithContext(event, properties, this.context);
118933
- }
118934
- getSink() {
118935
- return this.parent.getSink();
118936
- }
118937
- async flush(signal) {
118938
- await this.parent.flush(signal);
118939
- }
118940
- flushSync() {
118941
- this.parent.flushSync();
118942
- }
118943
- async shutdown(options = {}) {
118944
- await this.parent.shutdown(options);
118945
- }
118946
- resetForTests() {
118947
- this.parent.resetForTests();
118948
- }
118949
- };
118950
- const defaultClient = new TelemetryClient();
118951
- function setContext(input) {
118952
- defaultClient.setContext(input);
118953
- }
118954
- function track$1(event, properties = {}) {
118955
- defaultClient.track(event, properties);
118956
- }
118957
- function withContext(input) {
118958
- return defaultClient.withContext(input);
118959
- }
118960
- async function shutdown(options = {}) {
118961
- await defaultClient.shutdown(options);
118962
- }
118963
- function getDefaultTelemetryClient() {
118964
- return defaultClient;
118965
- }
118966
- function mergeContext(base, patch) {
118967
- return {
118968
- deviceId: patch.deviceId === void 0 ? base.deviceId : patch.deviceId,
118969
- sessionId: patch.sessionId === void 0 ? base.sessionId : patch.sessionId
118970
- };
118971
- }
118972
- function toTelemetryEvent(event) {
118973
- return {
118974
- event_id: event.event_id,
118975
- device_id: event.device_id,
118976
- session_id: event.session_id,
118977
- event: event.event,
118978
- timestamp: event.timestamp,
118979
- properties: event.properties
118980
- };
118981
- }
118982
- function sanitizeProperties(input) {
118983
- const out = {};
118984
- for (const [key, value] of Object.entries(input)) if (isTelemetryPrimitive(value)) out[key] = value;
118985
- return out;
118986
- }
118987
- //#endregion
118988
- //#region ../../packages/telemetry/src/sink.ts
118989
- const DEFAULT_FLUSH_INTERVAL_MS = 3e4;
118990
- const DEFAULT_FLUSH_THRESHOLD = 50;
118991
- var EventSink = class {
118992
- transport;
118993
- context;
118994
- flushIntervalMs;
118995
- flushThreshold;
118996
- buffer = [];
118997
- flushTimer = null;
118998
- constructor(options) {
118999
- this.transport = options.transport;
119000
- this.context = buildContext(options.context);
119001
- this.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
119002
- this.flushThreshold = options.flushThreshold ?? DEFAULT_FLUSH_THRESHOLD;
119003
- }
119004
- accept(event) {
119005
- const enriched = {
119006
- ...event,
119007
- context: { ...this.context }
119008
- };
119009
- this.buffer.push(enriched);
119010
- if (this.buffer.length >= this.flushThreshold) this.flush().catch(() => {});
119011
- }
119012
- startPeriodicFlush() {
119013
- if (this.flushTimer !== null) return;
119014
- this.flushTimer = setInterval(() => {
119015
- this.flush().catch(() => {});
119016
- }, this.flushIntervalMs);
119017
- this.flushTimer.unref?.();
119018
- }
119019
- stopPeriodicFlush() {
119020
- if (this.flushTimer === null) return;
119021
- clearInterval(this.flushTimer);
119022
- this.flushTimer = null;
119023
- }
119024
- async retryDiskEvents() {
119025
- await this.transport.retryDiskEvents();
119026
- }
119027
- clearBuffer() {
119028
- this.buffer = [];
119029
- }
119030
- async flush(signal) {
119031
- if (this.buffer.length === 0) return;
119032
- const events = this.buffer;
119033
- this.buffer = [];
119034
- await this.transport.send(events, signal);
119035
- }
119036
- flushSync() {
119037
- if (this.buffer.length === 0) return;
119038
- const events = this.buffer;
119039
- this.buffer = [];
119040
- try {
119041
- this.transport.saveToDisk(events);
119042
- } catch {}
119043
- }
119044
- };
119045
- function buildContext(options) {
119046
- const env = options.env ?? process.env;
119047
- const context = {
119048
- app_name: options.appName,
119049
- version: options.version,
119050
- runtime: "node",
119051
- platform: platform(),
119052
- arch: arch(),
119053
- node_version: process.versions.node,
119054
- os_version: release(),
119055
- ci: env["CI"] !== void 0,
119056
- locale: options.locale ?? env["LANG"] ?? "",
119057
- terminal: options.terminal ?? env["TERM_PROGRAM"] ?? "",
119058
- ui_mode: options.uiMode ?? "shell"
119059
- };
119060
- setPrimitive(context, "model", options.model);
119061
- setPrimitive(context, "build_sha", options.buildSha);
119062
- return context;
119063
- }
119064
- function setPrimitive(target, key, value) {
119065
- if (value === void 0) return;
119066
- if (typeof value === "string" && value.length === 0) return;
119067
- target[key] = value;
119068
- }
119069
- const SERVER_EVENT_PREFIX = "kfc_";
119070
- const USER_ID_PREFIX = "kfc_device_id_";
119071
- const RETRY_BACKOFFS_MS = [
119072
- 1e3,
119073
- 4e3,
119074
- 16e3
119075
- ];
119076
- const DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
119077
- var AsyncTransport = class {
119078
- homeDir;
119079
- deviceId;
119080
- endpoint;
119081
- getAccessToken;
119082
- fetchImpl;
119083
- retryBackoffsMs;
119084
- requestTimeoutMs;
119085
- sleepImpl;
119086
- now;
119087
- constructor(options) {
119088
- this.homeDir = options.homeDir;
119089
- this.deviceId = options.deviceId;
119090
- this.endpoint = options.endpoint ?? "https://telemetry-logs.scream.com/v1/event";
119091
- this.getAccessToken = options.getAccessToken ?? null;
119092
- this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
119093
- this.retryBackoffsMs = options.retryBackoffsMs ?? RETRY_BACKOFFS_MS;
119094
- this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
119095
- this.sleepImpl = options.sleep ?? abortableSleep;
119096
- this.now = options.now ?? Date.now;
119097
- }
119098
- async send(events, signal) {
119099
- if (events.length === 0) return;
119100
- let savedToDisk = false;
119101
- const saveEventsToDisk = () => {
119102
- if (savedToDisk) return;
119103
- this.saveToDisk(events);
119104
- savedToDisk = true;
119105
- };
119106
- if (signal?.aborted === true) {
119107
- saveEventsToDisk();
119108
- throw abortError();
119109
- }
119110
- let payload;
119111
- try {
119112
- payload = buildPayload(events, this.deviceId);
119113
- } catch {
119114
- return;
119115
- }
119116
- try {
119117
- for (let attempt = 0; attempt <= this.retryBackoffsMs.length; attempt++) try {
119118
- await this.sendHttp(payload, signal);
119119
- return;
119120
- } catch (error) {
119121
- if (isSignalAborted(signal) || isAbortError$2(error)) {
119122
- saveEventsToDisk();
119123
- throw error;
119124
- }
119125
- if (!(error instanceof TransientTelemetryError)) break;
119126
- const backoff = this.retryBackoffsMs[attempt];
119127
- if (backoff === void 0) break;
119128
- await this.sleepImpl(backoff, signal);
119129
- }
119130
- } catch (error) {
119131
- if (isSignalAborted(signal) || isAbortError$2(error)) {
119132
- saveEventsToDisk();
119133
- throw error;
119134
- }
119135
- }
119136
- saveEventsToDisk();
119137
- }
119138
- saveToDisk(events) {
119139
- if (events.length === 0) return;
119140
- const path = join(this.telemetryDir(), `failed_${randomBytes(6).toString("hex")}.jsonl`);
119141
- writeFileSync(path, events.map((event) => JSON.stringify(event)).join("\n") + "\n", {
119142
- encoding: "utf-8",
119143
- mode: 384,
119144
- flag: "wx"
119145
- });
119146
- try {
119147
- chmodSync(path, 384);
119148
- } catch {}
119149
- }
119150
- async retryDiskEvents() {
119151
- let entries;
119152
- try {
119153
- entries = readdirSync(this.telemetryDir());
119154
- } catch {
119155
- return;
119156
- }
119157
- const now = this.now();
119158
- for (const entry of entries) {
119159
- if (!entry.startsWith("failed_") || !entry.endsWith(".jsonl")) continue;
119160
- const path = join(this.telemetryDir(), entry);
119161
- try {
119162
- if (now - statSync(path).mtimeMs > 6048e5) {
119163
- unlinkSync(path);
119164
- continue;
119165
- }
119166
- } catch {
119167
- continue;
119168
- }
119169
- let events;
119170
- let payload;
119171
- try {
119172
- events = readJsonl(path);
119173
- payload = buildPayload(events, this.deviceId);
119174
- } catch (error) {
119175
- if (error instanceof SyntaxError || error instanceof TypeError) try {
119176
- unlinkSync(path);
119177
- } catch {}
119178
- continue;
119179
- }
119180
- try {
119181
- await this.sendHttp(payload);
119182
- unlinkSync(path);
119183
- } catch (error) {
119184
- if (error instanceof TransientTelemetryError) continue;
119185
- }
119186
- }
119187
- }
119188
- async sendHttp(payload, signal) {
119189
- const token = this.getAccessToken === null ? null : await this.getAccessToken();
119190
- const headers = { "Content-Type": "application/json" };
119191
- if (token !== null && token.length > 0) headers["Authorization"] = `Bearer ${token}`;
119192
- const response = await this.post(payload, headers, signal);
119193
- if (response.status === 401 && headers["Authorization"] !== void 0) {
119194
- delete headers["Authorization"];
119195
- handleStatus((await this.post(payload, headers, signal)).status);
119196
- return;
119197
- }
119198
- handleStatus(response.status);
119199
- }
119200
- async post(payload, headers, signal) {
119201
- try {
119202
- return await fetchWithTimeout(this.fetchImpl, this.endpoint, {
119203
- method: "POST",
119204
- headers: { ...headers },
119205
- body: JSON.stringify(payload)
119206
- }, this.requestTimeoutMs, signal);
119207
- } catch (error) {
119208
- if (signal?.aborted === true || isAbortError$2(error)) throw error;
119209
- throw new TransientTelemetryError(String(error));
119210
- }
119211
- }
119212
- telemetryDir() {
119213
- const path = join(this.homeDir, "telemetry");
119214
- mkdirSync(path, {
119215
- recursive: true,
119216
- mode: 448
119217
- });
119218
- try {
119219
- chmodSync(path, 448);
119220
- } catch {}
119221
- return path;
119222
- }
119223
- };
119224
- var TransientTelemetryError = class extends Error {
119225
- name = "TransientTelemetryError";
119226
- };
119227
- function buildUserId(deviceId) {
119228
- return USER_ID_PREFIX + deviceId;
119229
- }
119230
- function buildPayload(events, deviceId) {
119231
- return {
119232
- user_id: buildUserId(deviceId),
119233
- events: events.map((event) => flattenEvent(applyServerPrefix(event)))
119234
- };
119235
- }
119236
- function applyServerPrefix(event) {
119237
- const name = event.event;
119238
- if (typeof name !== "string" || name.length === 0 || name.startsWith("kfc_")) return event;
119239
- return {
119240
- ...event,
119241
- event: SERVER_EVENT_PREFIX + name
119242
- };
119243
- }
119244
- function flattenEvent(event) {
119245
- const out = {};
119246
- for (const [key, value] of Object.entries(event)) if (key === "properties") flattenNested(out, "property", value);
119247
- else if (key === "context") flattenNested(out, "context", value);
119248
- else {
119249
- assertPrimitive(key, value);
119250
- out[key] = value;
119251
- }
119252
- return out;
119253
- }
119254
- function flattenNested(target, prefix, value) {
119255
- if (value === null || typeof value !== "object" || Array.isArray(value)) return;
119256
- for (const [key, nestedValue] of Object.entries(value)) {
119257
- assertPrimitive(`${prefix}.${key}`, nestedValue);
119258
- target[`${prefix}_${key}`] = nestedValue;
119259
- }
119260
- }
119261
- function assertPrimitive(key, value) {
119262
- if (isTelemetryPrimitive(value)) return;
119263
- throw new TypeError(`telemetry ${key} must be primitive`);
119264
- }
119265
- function handleStatus(status) {
119266
- if (status >= 500 || status === 429) throw new TransientTelemetryError(`HTTP ${String(status)}`);
119267
- if (status >= 400) return;
119268
- }
119269
- function readJsonl(path) {
119270
- const text = readFileSync(path, "utf-8");
119271
- const events = [];
119272
- for (const line of text.split("\n")) {
119273
- const trimmed = line.trim();
119274
- if (trimmed.length === 0) continue;
119275
- events.push(JSON.parse(trimmed));
119276
- }
119277
- return events;
119278
- }
119279
- async function fetchWithTimeout(fetchImpl, url, init, timeoutMs, externalSignal) {
119280
- const controller = new AbortController();
119281
- const abortFromExternal = () => {
119282
- controller.abort(externalSignal?.reason);
119283
- };
119284
- const timeout = setTimeout(() => {
119285
- controller.abort(/* @__PURE__ */ new Error("telemetry request timed out"));
119286
- }, timeoutMs);
119287
- timeout.unref?.();
119288
- if (externalSignal?.aborted === true) abortFromExternal();
119289
- externalSignal?.addEventListener("abort", abortFromExternal, { once: true });
119290
- try {
119291
- return await fetchImpl(url, {
119292
- ...init,
119293
- signal: controller.signal
119294
- });
119295
- } finally {
119296
- clearTimeout(timeout);
119297
- externalSignal?.removeEventListener("abort", abortFromExternal);
119298
- }
119299
- }
119300
- function abortableSleep(ms, signal) {
119301
- if (signal?.aborted === true) return Promise.reject(abortError());
119302
- return new Promise((resolve, reject) => {
119303
- const timer = setTimeout(resolve, ms);
119304
- timer.unref?.();
119305
- const onAbort = () => {
119306
- clearTimeout(timer);
119307
- reject(abortError());
119308
- };
119309
- signal?.addEventListener("abort", onAbort, { once: true });
119310
- });
119311
- }
119312
- function isAbortError$2(error) {
119313
- return error instanceof Error && error.name === "AbortError";
119314
- }
119315
- function isSignalAborted(signal) {
119316
- return signal?.aborted === true;
119317
- }
119318
- function abortError() {
119319
- return new DOMException("The operation was aborted.", "AbortError");
119320
- }
119321
- //#endregion
119322
- //#region ../../packages/telemetry/src/bootstrap.ts
119323
- const TELEMETRY_DISABLE_ENV = "SCREAM_DISABLE_TELEMETRY";
119324
- const TRUE_ENV_VALUES = new Set([
119325
- "1",
119326
- "true",
119327
- "t",
119328
- "yes",
119329
- "y"
119330
- ]);
119331
- function isTelemetryDisabledByEnv(env = process.env) {
119332
- const value = env[TELEMETRY_DISABLE_ENV];
119333
- return value !== void 0 && TRUE_ENV_VALUES.has(value.trim().toLowerCase());
119334
- }
119335
- function shouldEnableTelemetry(input = {}) {
119336
- return input.enabled !== false && !isTelemetryDisabledByEnv(input.env ?? process.env);
119337
- }
119338
- function initializeTelemetry(options) {
119339
- const client = getDefaultTelemetryClient();
119340
- if (!shouldEnableTelemetry({ enabled: options.enabled })) {
119341
- client.disable();
119342
- return;
119343
- }
119344
- client.enable();
119345
- client.setContext({
119346
- deviceId: options.deviceId,
119347
- sessionId: options.sessionId
119348
- });
119349
- const sink = new EventSink({
119350
- transport: new AsyncTransport({
119351
- homeDir: options.homeDir,
119352
- deviceId: options.deviceId,
119353
- getAccessToken: options.getAccessToken
119354
- }),
119355
- context: {
119356
- appName: options.appName,
119357
- version: options.version,
119358
- uiMode: options.uiMode,
119359
- model: options.model,
119360
- buildSha: options.buildSha,
119361
- terminal: options.terminal,
119362
- locale: options.locale
119363
- }
119364
- });
119365
- client.attachSink(sink);
119366
- sink.startPeriodicFlush();
119367
- sink.retryDiskEvents().catch(() => {});
119368
- }
119369
- //#endregion
119370
- //#region ../../packages/telemetry/src/crash.ts
119371
- let phase = "startup";
119372
- let installed = false;
119373
- let installedUncaughtHandler = null;
119374
- function setCrashPhase(nextPhase) {
119375
- phase = nextPhase;
119376
- }
119377
- function installCrashHandlers() {
119378
- return installCrashHandlersForClient(getDefaultTelemetryClient());
119379
- }
119380
- function installCrashHandlersForClient(client) {
119381
- if (installed && installedUncaughtHandler !== null) return () => {
119382
- uninstallCrashHandlers();
119383
- };
119384
- const trackCrash = (errorType, source) => {
119385
- try {
119386
- client.track("crash", {
119387
- error_type: errorType,
119388
- where: phase,
119389
- source
119390
- });
119391
- client.flushSync();
119392
- } catch {}
119393
- };
119394
- installedUncaughtHandler = (error, origin) => {
119395
- if (isAbortError$1(error)) return;
119396
- trackCrash(error.name || error.constructor.name, origin);
119397
- };
119398
- process.on("uncaughtExceptionMonitor", installedUncaughtHandler);
119399
- installed = true;
119400
- return () => {
119401
- uninstallCrashHandlers();
119402
- };
119403
- }
119404
- function uninstallCrashHandlers() {
119405
- if (!installed) return;
119406
- if (installedUncaughtHandler !== null) process.off("uncaughtExceptionMonitor", installedUncaughtHandler);
119407
- installedUncaughtHandler = null;
119408
- installed = false;
119409
- }
119410
- function isAbortError$1(reason) {
119411
- return typeof reason === "object" && reason !== null && "name" in reason && reason.name === "AbortError";
119412
- }
119413
- //#endregion
119414
- //#region ../../packages/telemetry/src/index.ts
119415
- function track(event, properties = {}) {
119416
- track$1(event, properties);
119417
- }
119418
- function setTelemetryContext(patch) {
119419
- setContext(patch);
119420
- }
119421
- function withTelemetryContext(patch) {
119422
- return withContext(patch);
119423
- }
119424
- async function shutdownTelemetry(options = {}) {
119425
- await shutdown(options);
119426
- }
119427
- //#endregion
119428
118524
  //#region src/constant/app.ts
119429
118525
  const PRODUCT_NAME = "Scream Code";
119430
118526
  const CLI_COMMAND_NAME = "scream";
119431
118527
  const CLI_USER_AGENT_PRODUCT = "scream-code-cli";
119432
118528
  const CLI_UI_MODE = "shell";
119433
- const CLI_SHUTDOWN_TIMEOUT_MS = 3e3;
119434
118529
  const SCREAM_CODE_HOME_ENV = "SCREAM_CODE_HOME";
119435
118530
  const SCREAM_CODE_DATA_DIR_NAME = ".scream-code";
119436
118531
  const SCREAM_CODE_UPDATE_DIR_NAME = "updates";
@@ -119500,32 +118595,6 @@ function camelToSnake(s) {
119500
118595
  }
119501
118596
  new Set(Object.keys(ScreamConfigSchema.shape).filter((k) => k !== "raw" && k !== "providers" && k !== "models" && k !== "hooks").map(camelToSnake));
119502
118597
  //#endregion
119503
- //#region src/cli/telemetry.ts
119504
- function createCliTelemetryBootstrap() {
119505
- let firstLaunch = false;
119506
- const homeDir = resolveScreamHome();
119507
- return {
119508
- homeDir,
119509
- deviceId: createScreamDeviceId(homeDir, { onFirstLaunch: () => {
119510
- firstLaunch = true;
119511
- } }),
119512
- firstLaunch
119513
- };
119514
- }
119515
- function initializeCliTelemetry(options) {
119516
- initializeTelemetry({
119517
- homeDir: options.harness.homeDir,
119518
- deviceId: options.bootstrap.deviceId,
119519
- enabled: options.config.telemetry !== false,
119520
- appName: CLI_USER_AGENT_PRODUCT,
119521
- version: options.version,
119522
- uiMode: options.uiMode,
119523
- model: options.model ?? options.config.defaultModel,
119524
- getAccessToken: async () => null
119525
- });
119526
- if (options.bootstrap.firstLaunch) options.harness.track("first_launch");
119527
- }
119528
- //#endregion
119529
118598
  //#region src/cli/update/types.ts
119530
118599
  function emptyUpdateCache() {
119531
118600
  return {
@@ -119695,57 +118764,17 @@ function registerExportCommand(parent, deps) {
119695
118764
  }
119696
118765
  function createDefaultExportDeps(overrides = {}) {
119697
118766
  let harness;
119698
- let telemetryBootstrap;
119699
- let telemetryInitialized = false;
119700
- let telemetryShutdown = false;
119701
118767
  const identity = createScreamCodeHostIdentity();
119702
- const telemetryClient = {
119703
- track,
119704
- withContext: withTelemetryContext,
119705
- setContext: setTelemetryContext
119706
- };
119707
- const getTelemetryBootstrap = () => {
119708
- telemetryBootstrap ??= createCliTelemetryBootstrap();
119709
- return telemetryBootstrap;
119710
- };
119711
118768
  const getHarness = () => {
119712
- const currentTelemetryBootstrap = getTelemetryBootstrap();
119713
118769
  harness ??= new ScreamHarness({
119714
- homeDir: currentTelemetryBootstrap.homeDir,
119715
- identity,
119716
- telemetry: telemetryClient
118770
+ homeDir: resolveScreamHome(),
118771
+ identity
119717
118772
  });
119718
118773
  return harness;
119719
118774
  };
119720
- const initializeDefaultTelemetry = async () => {
119721
- if (telemetryInitialized) return;
119722
- const currentTelemetryBootstrap = getTelemetryBootstrap();
119723
- const currentHarness = getHarness();
119724
- await currentHarness.ensureConfigFile();
119725
- initializeCliTelemetry({
119726
- harness: currentHarness,
119727
- bootstrap: currentTelemetryBootstrap,
119728
- config: await currentHarness.getConfig(),
119729
- version: identity.version,
119730
- uiMode: CLI_UI_MODE
119731
- });
119732
- telemetryInitialized = true;
119733
- };
119734
- const shutdownDefaultTelemetry = async () => {
119735
- if (!telemetryInitialized || telemetryShutdown) return;
119736
- telemetryShutdown = true;
119737
- await shutdownTelemetry({ timeoutMs: CLI_SHUTDOWN_TIMEOUT_MS });
119738
- };
119739
118775
  return {
119740
118776
  listSessions: overrides.listSessions ?? ((workDir) => getHarness().listSessions({ workDir })),
119741
- exportSession: overrides.exportSession ?? (async (input) => {
119742
- await initializeDefaultTelemetry();
119743
- try {
119744
- return await getHarness().exportSession(input);
119745
- } finally {
119746
- await shutdownDefaultTelemetry();
119747
- }
119748
- }),
118777
+ exportSession: overrides.exportSession ?? (async (input) => getHarness().exportSession(input)),
119749
118778
  version: overrides.version ?? identity.version,
119750
118779
  getInstallSource: overrides.getInstallSource ?? (() => Promise.resolve(detectInstallSource())),
119751
118780
  getShellEnv: overrides.getShellEnv ?? detectShellEnvironment,
@@ -119864,23 +118893,15 @@ const PROMPT_MAIN_AGENT_ID = "main";
119864
118893
  const PROMPT_BLOCK_BULLET = "• ";
119865
118894
  const PROMPT_BLOCK_INDENT = " ";
119866
118895
  async function runPrompt(opts, version, io = {}) {
119867
- const startedAt = Date.now();
119868
118896
  const stdout = io.stdout ?? process.stdout;
119869
118897
  const stderr = io.stderr ?? process.stderr;
119870
118898
  const promptProcess = io.process ?? process;
119871
118899
  const workDir = process.cwd();
119872
- const telemetryBootstrap = createCliTelemetryBootstrap();
119873
- const telemetryClient = {
119874
- track,
119875
- withContext: withTelemetryContext,
119876
- setContext: setTelemetryContext
119877
- };
119878
118900
  const harness = new ScreamHarness({
119879
- homeDir: telemetryBootstrap.homeDir,
118901
+ homeDir: resolveScreamHome(),
119880
118902
  identity: createScreamCodeHostIdentity(version),
119881
118903
  uiMode: PROMPT_UI_MODE,
119882
- skillDirs: opts.skillsDirs,
119883
- telemetry: telemetryClient
118904
+ skillDirs: opts.skillsDirs
119884
118905
  });
119885
118906
  log.info("scream-code starting", {
119886
118907
  version,
@@ -119895,11 +118916,9 @@ async function runPrompt(opts, version, io = {}) {
119895
118916
  const cleanupPromptRun = async () => {
119896
118917
  cleanupPromise ??= (async () => {
119897
118918
  removeTerminationCleanup?.();
119898
- setCrashPhase("shutdown");
119899
118919
  try {
119900
118920
  await restorePromptSessionPermission();
119901
118921
  } finally {
119902
- await shutdownTelemetry({ timeoutMs: CLI_SHUTDOWN_TIMEOUT_MS });
119903
118922
  await harness.close();
119904
118923
  }
119905
118924
  })();
@@ -119908,30 +118927,13 @@ async function runPrompt(opts, version, io = {}) {
119908
118927
  removeTerminationCleanup = installPromptTerminationCleanup(promptProcess, cleanupPromptRun);
119909
118928
  try {
119910
118929
  await harness.ensureConfigFile();
119911
- const config = await harness.getConfig();
119912
- const { session, resumed, restorePermission, telemetryModel } = await resolvePromptSession(harness, opts, workDir, config.defaultModel, stderr, (restorePermission) => {
118930
+ const { session, restorePermission } = await resolvePromptSession(harness, opts, workDir, (await harness.getConfig()).defaultModel, stderr, (restorePermission) => {
119913
118931
  restorePromptSessionPermission = restorePermission;
119914
118932
  });
119915
118933
  restorePromptSessionPermission = restorePermission;
119916
- initializeCliTelemetry({
119917
- harness,
119918
- bootstrap: telemetryBootstrap,
119919
- config,
119920
- version,
119921
- uiMode: PROMPT_UI_MODE,
119922
- model: telemetryModel
119923
- });
119924
- setCrashPhase("runtime");
119925
- withTelemetryContext({ sessionId: session.id }).track("started", {
119926
- resumed,
119927
- yolo: false,
119928
- plan: false,
119929
- afk: true
119930
- });
119931
118934
  const outputFormat = opts.outputFormat ?? "text";
119932
118935
  await runPromptTurn(session, opts.prompt, outputFormat, stdout, stderr);
119933
118936
  writeResumeHint(session.id, outputFormat, stdout, stderr);
119934
- withTelemetryContext({ sessionId: session.id }).track("exit", { duration_s: (Date.now() - startedAt) / 1e3 });
119935
118937
  } finally {
119936
118938
  await cleanupPromptRun();
119937
118939
  }
@@ -119948,30 +118950,26 @@ async function resolvePromptSession(harness, opts, workDir, defaultModel, stderr
119948
118950
  throw new Error(`会话 "${opts.session}" 是在其他目录下创建的。`);
119949
118951
  }
119950
118952
  const session = await harness.resumeSession({ id: opts.session });
119951
- const status = await session.getStatus();
119952
- const restorePermission = await forcePromptPermission(session, status.permission, setRestorePermission);
118953
+ const restorePermission = await forcePromptPermission(session, (await session.getStatus()).permission, setRestorePermission);
119953
118954
  if (opts.model !== void 0) await session.setModel(opts.model);
119954
118955
  installHeadlessHandlers(session);
119955
118956
  return {
119956
118957
  session,
119957
118958
  resumed: true,
119958
- restorePermission,
119959
- telemetryModel: configuredModel(opts.model, status.model, defaultModel)
118959
+ restorePermission
119960
118960
  };
119961
118961
  }
119962
118962
  if (opts.continue) {
119963
118963
  const previous = (await harness.listSessions({ workDir }))[0];
119964
118964
  if (previous !== void 0) {
119965
118965
  const session = await harness.resumeSession({ id: previous.id });
119966
- const status = await session.getStatus();
119967
- const restorePermission = await forcePromptPermission(session, status.permission, setRestorePermission);
118966
+ const restorePermission = await forcePromptPermission(session, (await session.getStatus()).permission, setRestorePermission);
119968
118967
  if (opts.model !== void 0) await session.setModel(opts.model);
119969
118968
  installHeadlessHandlers(session);
119970
118969
  return {
119971
118970
  session,
119972
118971
  resumed: true,
119973
- restorePermission,
119974
- telemetryModel: configuredModel(opts.model, status.model, defaultModel)
118972
+ restorePermission
119975
118973
  };
119976
118974
  }
119977
118975
  stderr.write(`"${workDir}" 下没有可继续的会话;正在启动新会话。\n`);
@@ -119986,8 +118984,7 @@ async function resolvePromptSession(harness, opts, workDir, defaultModel, stderr
119986
118984
  return {
119987
118985
  session,
119988
118986
  resumed: false,
119989
- restorePermission: async () => {},
119990
- telemetryModel: model
118987
+ restorePermission: async () => {}
119991
118988
  };
119992
118989
  }
119993
118990
  async function forcePromptPermission(session, previousPermission, setRestorePermission) {
@@ -120044,90 +119041,95 @@ function runPromptTurn(session, prompt, outputFormat, stdout, stderr) {
120044
119041
  const outputWriter = outputFormat === "stream-json" ? new PromptJsonWriter(stdout) : new PromptTranscriptWriter(stdout, stderr);
120045
119042
  let settled = false;
120046
119043
  let unsubscribe;
120047
- return new Promise((resolve, reject) => {
120048
- const finish = (error) => {
120049
- if (settled) return;
120050
- settled = true;
120051
- unsubscribe?.();
120052
- outputWriter.finish();
120053
- if (error !== void 0) {
120054
- reject(error);
119044
+ let resolve;
119045
+ let reject;
119046
+ const promise = new Promise((res, rej) => {
119047
+ resolve = res;
119048
+ reject = rej;
119049
+ });
119050
+ const finish = (error) => {
119051
+ if (settled) return;
119052
+ settled = true;
119053
+ unsubscribe?.();
119054
+ outputWriter.finish();
119055
+ if (error !== void 0) {
119056
+ reject(error);
119057
+ return;
119058
+ }
119059
+ resolve();
119060
+ };
119061
+ unsubscribe = session.onEvent((event) => {
119062
+ if (event.type === "error") {
119063
+ if (event.agentId !== PROMPT_MAIN_AGENT_ID) return;
119064
+ finish(/* @__PURE__ */ new Error(`${event.code}: ${event.message}`));
119065
+ return;
119066
+ }
119067
+ if (event.type === "turn.started" && activeTurnId === void 0) {
119068
+ if (event.agentId !== PROMPT_MAIN_AGENT_ID) return;
119069
+ activeTurnId = event.turnId;
119070
+ activeAgentId = event.agentId;
119071
+ return;
119072
+ }
119073
+ if (activeTurnId === void 0 || activeAgentId === void 0 || !hasTurnId(event) || event.turnId !== activeTurnId || event.agentId !== activeAgentId) return;
119074
+ switch (event.type) {
119075
+ case "turn.step.started":
119076
+ case "turn.step.interrupted":
119077
+ outputWriter.flushAssistant();
120055
119078
  return;
120056
- }
120057
- resolve();
120058
- };
120059
- unsubscribe = session.onEvent((event) => {
120060
- if (event.type === "error") {
120061
- if (event.agentId !== PROMPT_MAIN_AGENT_ID) return;
120062
- finish(/* @__PURE__ */ new Error(`${event.code}: ${event.message}`));
119079
+ case "turn.step.retrying":
119080
+ outputWriter.discardAssistant();
120063
119081
  return;
120064
- }
120065
- if (event.type === "turn.started" && activeTurnId === void 0) {
120066
- if (event.agentId !== PROMPT_MAIN_AGENT_ID) return;
120067
- activeTurnId = event.turnId;
120068
- activeAgentId = event.agentId;
119082
+ case "assistant.delta":
119083
+ outputWriter.writeAssistantDelta(event.delta);
120069
119084
  return;
120070
- }
120071
- if (activeTurnId === void 0 || activeAgentId === void 0 || !hasTurnId(event) || event.turnId !== activeTurnId || event.agentId !== activeAgentId) return;
120072
- switch (event.type) {
120073
- case "turn.step.started":
120074
- case "turn.step.interrupted":
120075
- outputWriter.flushAssistant();
120076
- return;
120077
- case "turn.step.retrying":
120078
- outputWriter.discardAssistant();
120079
- return;
120080
- case "assistant.delta":
120081
- outputWriter.writeAssistantDelta(event.delta);
120082
- return;
120083
- case "hook.result":
120084
- outputWriter.writeHookResult(event);
120085
- return;
120086
- case "thinking.delta":
120087
- outputWriter.writeThinkingDelta(event.delta);
120088
- return;
120089
- case "tool.call.started":
120090
- outputWriter.writeToolCall(event.toolCallId, event.name, event.args);
120091
- return;
120092
- case "tool.call.delta":
120093
- outputWriter.writeToolCallDelta(event.toolCallId, event.name, event.argumentsPart);
120094
- return;
120095
- case "tool.result":
120096
- outputWriter.writeToolResult(event.toolCallId, event.output);
120097
- return;
120098
- case "tool.progress":
120099
- if (event.update.text !== void 0 && event.update.text.length > 0) stderr.write(event.update.text.endsWith("\n") ? event.update.text : `${event.update.text}\n`);
120100
- return;
120101
- case "turn.ended":
120102
- if (event.reason === "completed") {
120103
- finish();
120104
- return;
120105
- }
120106
- finish(new Error(formatTurnEndedFailure(event)));
119085
+ case "hook.result":
119086
+ outputWriter.writeHookResult(event);
119087
+ return;
119088
+ case "thinking.delta":
119089
+ outputWriter.writeThinkingDelta(event.delta);
119090
+ return;
119091
+ case "tool.call.started":
119092
+ outputWriter.writeToolCall(event.toolCallId, event.name, event.args);
119093
+ return;
119094
+ case "tool.call.delta":
119095
+ outputWriter.writeToolCallDelta(event.toolCallId, event.name, event.argumentsPart);
119096
+ return;
119097
+ case "tool.result":
119098
+ outputWriter.writeToolResult(event.toolCallId, event.output);
119099
+ return;
119100
+ case "tool.progress":
119101
+ if (event.update.text !== void 0 && event.update.text.length > 0) stderr.write(event.update.text.endsWith("\n") ? event.update.text : `${event.update.text}\n`);
119102
+ return;
119103
+ case "turn.ended":
119104
+ if (event.reason === "completed") {
119105
+ finish();
120107
119106
  return;
120108
- case "agent.status.updated":
120109
- case "background.task.started":
120110
- case "background.task.terminated":
120111
- case "background.task.updated":
120112
- case "compaction.blocked":
120113
- case "compaction.cancelled":
120114
- case "compaction.completed":
120115
- case "compaction.started":
120116
- case "mcp.server.status":
120117
- case "session.meta.updated":
120118
- case "skill.activated":
120119
- case "subagent.completed":
120120
- case "subagent.failed":
120121
- case "subagent.spawned":
120122
- case "tool.list.updated":
120123
- case "turn.started":
120124
- case "turn.step.completed": return;
120125
- }
120126
- });
120127
- session.prompt(prompt).catch((error) => {
120128
- finish(error instanceof Error ? error : new Error(String(error)));
120129
- });
119107
+ }
119108
+ finish(new Error(formatTurnEndedFailure(event)));
119109
+ return;
119110
+ case "agent.status.updated":
119111
+ case "background.task.started":
119112
+ case "background.task.terminated":
119113
+ case "background.task.updated":
119114
+ case "compaction.blocked":
119115
+ case "compaction.cancelled":
119116
+ case "compaction.completed":
119117
+ case "compaction.started":
119118
+ case "mcp.server.status":
119119
+ case "session.meta.updated":
119120
+ case "skill.activated":
119121
+ case "subagent.completed":
119122
+ case "subagent.failed":
119123
+ case "subagent.spawned":
119124
+ case "tool.list.updated":
119125
+ case "turn.started":
119126
+ case "turn.step.completed": return;
119127
+ }
119128
+ });
119129
+ session.prompt(prompt).catch((error) => {
119130
+ finish(error instanceof Error ? error : new Error(String(error)));
120130
119131
  });
119132
+ return promise;
120131
119133
  }
120132
119134
  var PromptTranscriptWriter = class {
120133
119135
  assistantWriter;
@@ -121757,10 +120759,6 @@ async function handleConnectCommand(host, args) {
121757
120759
  defaultThinking: config.defaultThinking
121758
120760
  });
121759
120761
  await host.authFlow.refreshConfigAfterLogin();
121760
- host.track("config", {
121761
- provider: providerId,
121762
- model: selection.model.id
121763
- });
121764
120762
  host.showStatus(`Connected: ${entry.name ?? providerId} · ${selection.model.id}`);
121765
120763
  }
121766
120764
  async function handleLogoutCommand(host) {
@@ -121794,7 +120792,6 @@ async function handleLogoutCommand(host) {
121794
120792
  availableProviders: updated.providers ?? {}
121795
120793
  });
121796
120794
  }
121797
- host.track("logout", { provider: target });
121798
120795
  host.showStatus(`已删除模型商: ${target}.`);
121799
120796
  }
121800
120797
  async function handleDiyConfig(host) {
@@ -121848,11 +120845,6 @@ async function handleDiyConfig(host) {
121848
120845
  defaultThinking: config.defaultThinking
121849
120846
  });
121850
120847
  await host.authFlow.refreshConfigAfterLogin();
121851
- host.track("config_diy", {
121852
- providerId,
121853
- model: modelId,
121854
- wire
121855
- });
121856
120848
  host.showStatus(`已连接: ${providerId} · ${modelId} (${wire})`);
121857
120849
  }
121858
120850
  //#endregion
@@ -122188,6 +121180,10 @@ const darkColors = {
122188
121180
  textStrong: dark.gray50,
122189
121181
  textDim: dark.gray500,
122190
121182
  textMuted: dark.gray600,
121183
+ mdLink: "#56B6C2",
121184
+ mdCodeBlock: "#9CDCFE",
121185
+ mdCodeBlockBorder: "#5C6370",
121186
+ mdQuote: "#7F848E",
122191
121187
  border: dark.gray700,
122192
121188
  borderFocus: dark.gold400,
122193
121189
  success: dark.green400,
@@ -122213,6 +121209,10 @@ const lightColors = {
122213
121209
  textStrong: light.gray900,
122214
121210
  textDim: light.gray700,
122215
121211
  textMuted: light.gray600,
121212
+ mdLink: "#007A8A",
121213
+ mdCodeBlock: "#1565C0",
121214
+ mdCodeBlockBorder: "#9E9E9E",
121215
+ mdQuote: "#616161",
122216
121216
  border: light.gray500,
122217
121217
  borderFocus: light.gold700,
122218
121218
  success: light.green700,
@@ -122269,17 +121269,16 @@ const HEADING_HASH_PREFIX = /^((?:\u001B\[[0-9;]*m)*)#{1,6}[ \t]+/;
122269
121269
  function createMarkdownTheme(colors) {
122270
121270
  const stripHash = (text) => text.replace(HEADING_HASH_PREFIX, "$1");
122271
121271
  const muted = chalk.hex(colors.textMuted);
122272
- const dim = chalk.hex(colors.textDim);
122273
121272
  const border = chalk.hex(colors.border);
122274
121273
  return {
122275
121274
  heading: (text) => chalk.bold.hex(colors.text)(stripHash(text)),
122276
- link: (text) => chalk.hex(colors.primary)(text),
121275
+ link: (text) => chalk.hex(colors.mdLink)(text),
122277
121276
  linkUrl: (text) => muted(text),
122278
121277
  code: (text) => chalk.hex(colors.primary)(text),
122279
- codeBlock: (text) => text,
122280
- codeBlockBorder: (text) => muted(text),
122281
- quote: (text) => dim(text),
122282
- quoteBorder: (text) => dim(text),
121278
+ codeBlock: (text) => chalk.hex(colors.mdCodeBlock)(text),
121279
+ codeBlockBorder: (text) => chalk.hex(colors.mdCodeBlockBorder)(text),
121280
+ quote: (text) => chalk.hex(colors.mdQuote)(text),
121281
+ quoteBorder: (text) => chalk.hex(colors.mdQuote)(text),
122283
121282
  hr: (text) => border(text),
122284
121283
  listBullet: (text) => chalk.hex(colors.roleAssistant)(text.replace(/^-/, "•")),
122285
121284
  bold: (text) => chalk.bold(text),
@@ -122918,10 +121917,6 @@ async function performModelSwitch(host, alias, thinking) {
122918
121917
  model: alias,
122919
121918
  thinking
122920
121919
  });
122921
- if (session === void 0 && runtimeChanged) {
122922
- if (alias !== prevModel) host.track("model_switch", { model: alias });
122923
- if (thinking !== prevThinking) host.track("thinking_toggle", { enabled: thinking });
122924
- }
122925
121920
  let persisted = false;
122926
121921
  try {
122927
121922
  persisted = await persistModelSelection(host, alias, thinking);
@@ -122974,7 +121969,6 @@ async function applyThemeChoice(host, theme) {
122974
121969
  const resolved = theme === "auto" ? host.state.theme.resolvedTheme : theme;
122975
121970
  host.applyTheme(theme, resolved);
122976
121971
  host.refreshTerminalThemeTracking();
122977
- host.track("theme_switch", { theme });
122978
121972
  const detail = theme === "auto" ? ` (tracking terminal; current: ${resolved})` : "";
122979
121973
  host.showStatus(`Theme set to "${theme}"${detail}.`);
122980
121974
  }
@@ -123358,7 +122352,6 @@ async function handleInitCommand(host) {
123358
122352
  host.beginSessionRequest();
123359
122353
  try {
123360
122354
  await session.init();
123361
- host.track("init_complete");
123362
122355
  host.streamingUI.finalizeTurn((item) => {
123363
122356
  host.sendQueuedMessage(session, item);
123364
122357
  });
@@ -124113,22 +123106,15 @@ var AssistantMessageComponent = class {
124113
123106
  }
124114
123107
  updateContent(text) {
124115
123108
  const trimmedText = text.trim();
124116
- const previousTrimmed = this.lastText.trim();
124117
- if (trimmedText === previousTrimmed) {
123109
+ if (trimmedText === this.lastText.trim()) {
124118
123110
  this.lastText = text;
124119
123111
  return;
124120
123112
  }
124121
123113
  this.lastText = text;
124122
123114
  this.cachedWidth = void 0;
124123
123115
  this.cachedLines = void 0;
124124
- const markdownChild = this.markdownChild;
124125
- if (markdownChild !== void 0 && trimmedText.startsWith(previousTrimmed) && trimmedText.length > previousTrimmed.length) {
124126
- markdownChild.setText(trimmedText);
124127
- return;
124128
- }
124129
- this.contentContainer.clear();
124130
- this.markdownChild = void 0;
124131
- if (trimmedText.length > 0) {
123116
+ if (this.markdownChild !== void 0) this.markdownChild.setText(trimmedText);
123117
+ else if (trimmedText.length > 0) {
124132
123118
  this.markdownChild = new Markdown(trimmedText, 0, 0, this.markdownTheme);
124133
123119
  this.contentContainer.addChild(this.markdownChild);
124134
123120
  }
@@ -128348,10 +127334,6 @@ async function executeSlashCommand(host, input) {
128348
127334
  switch (intent.kind) {
128349
127335
  case "not-command": return;
128350
127336
  case "blocked":
128351
- host.track("input_command_invalid", {
128352
- reason: "blocked",
128353
- command: intent.commandName
128354
- });
128355
127337
  host.showError(slashBusyMessage(intent.commandName, intent.reason));
128356
127338
  return;
128357
127339
  case "skill": {
@@ -128360,10 +127342,6 @@ async function executeSlashCommand(host, input) {
128360
127342
  host.showError(LLM_NOT_SET_MESSAGE);
128361
127343
  return;
128362
127344
  }
128363
- host.track("input_command", {
128364
- command: intent.commandName,
128365
- skill_name: intent.skillName
128366
- });
128367
127345
  host.sendSkillActivation(session, intent.skillName, intent.args);
128368
127346
  return;
128369
127347
  }
@@ -128371,8 +127349,6 @@ async function executeSlashCommand(host, input) {
128371
127349
  host.sendNormalUserInput(intent.input);
128372
127350
  return;
128373
127351
  case "builtin":
128374
- host.track("input_command", { command: intent.name });
128375
- if (intent.name === "new" && parsedCommand?.name === "clear") host.track("clear");
128376
127352
  try {
128377
127353
  const args = intent.name === "goal" && parsedCommand?.name === "goaloff" ? "off" : intent.args;
128378
127354
  await handleBuiltInSlashCommand(host, intent.name, args);
@@ -129304,16 +128280,12 @@ var EditorKeyboardController = class {
129304
128280
  return;
129305
128281
  }
129306
128282
  const next = !host.state.appState.planMode;
129307
- host.track("shortcut_plan_toggle", { enabled: next });
129308
- host.track("shortcut_mode_switch", { to_mode: next ? "plan" : "agent" });
129309
128283
  host.handlePlanToggle(next);
129310
128284
  };
129311
128285
  editor.onOpenExternalEditor = () => {
129312
- host.track("shortcut_editor");
129313
128286
  this.openExternalEditor();
129314
128287
  };
129315
128288
  editor.onToggleToolExpand = () => {
129316
- host.track("shortcut_expand");
129317
128289
  host.toggleToolOutputExpansion();
129318
128290
  };
129319
128291
  editor.onTogglePlanExpand = () => host.togglePlanExpansion();
@@ -129337,15 +128309,6 @@ var EditorKeyboardController = class {
129337
128309
  host.updateQueueDisplay();
129338
128310
  host.state.ui.requestRender();
129339
128311
  };
129340
- editor.onUndo = () => {
129341
- host.track("undo");
129342
- };
129343
- editor.onInsertNewline = () => {
129344
- host.track("shortcut_newline");
129345
- };
129346
- editor.onTextPaste = () => {
129347
- host.track("shortcut_paste", { kind: "text" });
129348
- };
129349
128312
  editor.onUpArrowEmpty = () => {
129350
128313
  if (host.state.appState.streamingPhase === "idle" && !host.state.appState.isCompacting) return false;
129351
128314
  const recalled = host.recallLastQueued();
@@ -129407,7 +128370,6 @@ var EditorKeyboardController = class {
129407
128370
  const attachment = this.imageStore.addVideo(media.mimeType, media.sourcePath, media.filename);
129408
128371
  this.host.state.editor.insertTextAtCursor?.(`${attachment.placeholder} `);
129409
128372
  this.host.state.ui.requestRender();
129410
- this.host.track("shortcut_paste", { kind: "video" });
129411
128373
  return true;
129412
128374
  }
129413
128375
  const meta = parseImageMeta(media.bytes);
@@ -129415,7 +128377,6 @@ var EditorKeyboardController = class {
129415
128377
  const attachment = this.imageStore.addImage(media.bytes, meta.mime, meta.width, meta.height);
129416
128378
  this.host.state.editor.insertTextAtCursor?.(`${attachment.placeholder} `);
129417
128379
  this.host.state.ui.requestRender();
129418
- this.host.track("shortcut_paste", { kind: "image" });
129419
128380
  return true;
129420
128381
  }
129421
128382
  async openExternalEditor() {
@@ -133149,7 +132110,7 @@ var TranscriptController = class TranscriptController {
133149
132110
  committedComponent;
133150
132111
  liveComponentToEntry = /* @__PURE__ */ new Map();
133151
132112
  pendingComponents = /* @__PURE__ */ new Set();
133152
- static LIVE_LIMIT = 50;
132113
+ static LIVE_LIMIT = 150;
133153
132114
  constructor(host) {
133154
132115
  this.host = host;
133155
132116
  }
@@ -133176,6 +132137,7 @@ var TranscriptController = class TranscriptController {
133176
132137
  }
133177
132138
  commit() {
133178
132139
  const { state } = this.host;
132140
+ if (state.appState.streamingPhase !== "idle") return;
133179
132141
  const container = state.transcriptContainer;
133180
132142
  const children = container.children;
133181
132143
  if (children.length <= TranscriptController.LIVE_LIMIT) return;
@@ -133354,6 +132316,7 @@ var TranscriptController = class TranscriptController {
133354
132316
  }
133355
132317
  toggleToolOutputExpansion() {
133356
132318
  const { state } = this.host;
132319
+ state.ui.setClearOnShrink(false);
133357
132320
  state.toolOutputExpanded = !state.toolOutputExpanded;
133358
132321
  const walk = (children) => {
133359
132322
  for (const child of children) {
@@ -133366,6 +132329,7 @@ var TranscriptController = class TranscriptController {
133366
132329
  }
133367
132330
  togglePlanExpansion() {
133368
132331
  const { state } = this.host;
132332
+ state.ui.setClearOnShrink(false);
133369
132333
  const next = !state.planExpanded;
133370
132334
  let toggled = false;
133371
132335
  for (const child of state.transcriptContainer.children) if (isPlanExpandable(child) && child.setPlanExpanded(next)) toggled = true;
@@ -134558,7 +133522,6 @@ var InputController = class {
134558
133522
  parts: options?.parts,
134559
133523
  imageAttachmentIds: options?.imageAttachmentIds !== void 0 && options.imageAttachmentIds.length > 0 ? options.imageAttachmentIds : void 0
134560
133524
  });
134561
- this.host.track("input_queue");
134562
133525
  }
134563
133526
  sendMessageInternal(session, input, options) {
134564
133527
  const imageAttachmentIds = options?.imageAttachmentIds !== void 0 && options.imageAttachmentIds.length > 0 ? options.imageAttachmentIds : void 0;
@@ -135681,9 +134644,6 @@ var CustomEditor = class extends Editor {
135681
134644
  onTogglePlanExpand;
135682
134645
  onOpenExternalEditor;
135683
134646
  onCtrlS;
135684
- onUndo;
135685
- onInsertNewline;
135686
- onTextPaste;
135687
134647
  /**
135688
134648
  * Called when ↑ is pressed in an empty editor. Return `true` to consume
135689
134649
  * the key (e.g. recalled a queued message); return `false` to fall
@@ -135794,10 +134754,7 @@ var CustomEditor = class extends Editor {
135794
134754
  if (this.onPasteImage !== void 0) {
135795
134755
  const handler = this.onPasteImage;
135796
134756
  handler().then((handled) => {
135797
- if (!handled) {
135798
- this.onTextPaste?.();
135799
- super.handleInput.call(this, normalized);
135800
- }
134757
+ if (!handled) super.handleInput.call(this, normalized);
135801
134758
  });
135802
134759
  return;
135803
134760
  }
@@ -135831,10 +134788,8 @@ var CustomEditor = class extends Editor {
135831
134788
  this.onShiftTab?.();
135832
134789
  return;
135833
134790
  }
135834
- if (matchesKey(normalized, Key.ctrl("-"))) this.onUndo?.();
135835
134791
  const newlineInput = getNewlineInput(normalized);
135836
134792
  if (newlineInput !== void 0) {
135837
- this.onInsertNewline?.();
135838
134793
  super.handleInput(newlineInput);
135839
134794
  return;
135840
134795
  }
@@ -136106,6 +135061,7 @@ function createTUIState(options) {
136106
135061
  const theme = createScreamTUIThemeBundle(initialAppState.theme, options.resolvedTheme);
136107
135062
  const terminal = new ProcessTerminal();
136108
135063
  const ui = new TUI(terminal);
135064
+ ui.setClearOnShrink(false);
136109
135065
  const uiAny = ui;
136110
135066
  const originalDoRender = uiAny["doRender"].bind(ui);
136111
135067
  uiAny["doRender"] = () => {
@@ -136643,7 +135599,6 @@ var SessionManager = class {
136643
135599
  async setSession(session) {
136644
135600
  await this.unloadCurrentSession("switching session")?.close({ extractMemories: false });
136645
135601
  this.host.session = session;
136646
- this.host.harness.setTelemetryContext({ sessionId: session.id });
136647
135602
  this.registerSessionHandlers(session);
136648
135603
  }
136649
135604
  async syncRuntimeState(session = this.requireSession()) {
@@ -136682,7 +135637,6 @@ var SessionManager = class {
136682
135637
  this.host.approvalController.cancelAll(reason);
136683
135638
  this.host.questionController.cancelAll(reason);
136684
135639
  this.host.session = void 0;
136685
- this.host.harness.setTelemetryContext({ sessionId: null });
136686
135640
  return previous;
136687
135641
  }
136688
135642
  clearReverseRpcPanels() {
@@ -138780,9 +137734,6 @@ var ScreamTUI = class {
138780
137734
  lifecycleController;
138781
137735
  inputController;
138782
137736
  onExit;
138783
- track(event, properties) {
138784
- this.harness.track(event, properties);
138785
- }
138786
137737
  constructor(harness, startupInput) {
138787
137738
  this.harness = harness;
138788
137739
  const tuiOptions = {
@@ -139503,8 +138454,6 @@ function runLoadingAnimation(theme = "dark") {
139503
138454
  //#endregion
139504
138455
  //#region src/cli/run-shell.ts
139505
138456
  async function runShell(opts, version) {
139506
- const startedAt = Date.now();
139507
- const configStartedAt = startedAt;
139508
138457
  let tuiConfig;
139509
138458
  let configWarning;
139510
138459
  try {
@@ -139516,16 +138465,9 @@ async function runShell(opts, version) {
139516
138465
  }
139517
138466
  const resolvedTheme = tuiConfig.theme === "auto" ? await detectTerminalTheme() : tuiConfig.theme;
139518
138467
  const workDir = process.cwd();
139519
- const telemetryBootstrap = createCliTelemetryBootstrap();
139520
- const telemetryClient = {
139521
- track,
139522
- withContext: withTelemetryContext,
139523
- setContext: setTelemetryContext
139524
- };
139525
138468
  const harness = new ScreamHarness({
139526
- homeDir: telemetryBootstrap.homeDir,
139527
- identity: createScreamCodeHostIdentity(version),
139528
- telemetry: telemetryClient
138469
+ homeDir: resolveScreamHome(),
138470
+ identity: createScreamCodeHostIdentity(version)
139529
138471
  });
139530
138472
  log.info("scream-code starting", {
139531
138473
  version,
@@ -139535,8 +138477,6 @@ async function runShell(opts, version) {
139535
138477
  workDir
139536
138478
  });
139537
138479
  await harness.ensureConfigFile();
139538
- const config = await harness.getConfig();
139539
- const configMs = Date.now() - configStartedAt;
139540
138480
  await harness.preflight();
139541
138481
  await runLoadingAnimation(resolvedTheme);
139542
138482
  const tui = new ScreamTUI(harness, {
@@ -139547,31 +138487,9 @@ async function runShell(opts, version) {
139547
138487
  startupNotice: configWarning,
139548
138488
  resolvedTheme
139549
138489
  });
139550
- initializeCliTelemetry({
139551
- harness,
139552
- bootstrap: telemetryBootstrap,
139553
- config,
139554
- version,
139555
- uiMode: CLI_UI_MODE
139556
- });
139557
- setCrashPhase("runtime");
139558
- const resumed = opts.continue || opts.session !== void 0;
139559
- const trackLifecycleForSession = (sessionId, event, properties) => {
139560
- if (sessionId.length === 0) {
139561
- harness.track(event, properties);
139562
- return;
139563
- }
139564
- withTelemetryContext({ sessionId }).track(event, properties);
139565
- };
139566
- const trackLifecycle = (event, properties) => {
139567
- trackLifecycleForSession(tui.getCurrentSessionId(), event, properties);
139568
- };
139569
138490
  tui.onExit = async (exitCode = 0) => {
139570
138491
  const sessionId = tui.getCurrentSessionId();
139571
138492
  const hasContent = tui.hasSessionContent();
139572
- setCrashPhase("shutdown");
139573
- trackLifecycle("exit", { duration_s: (Date.now() - startedAt) / 1e3 });
139574
- await shutdownTelemetry({ timeoutMs: CLI_SHUTDOWN_TIMEOUT_MS });
139575
138493
  const gutter = " ".repeat(1);
139576
138494
  process.stdout.write(`${gutter}再见!\n`);
139577
138495
  if (sessionId !== "" && hasContent) process.stderr.write(`\n${gutter}恢复此会话:scream -r ${sessionId}\n`);
@@ -139581,28 +138499,8 @@ async function runShell(opts, version) {
139581
138499
  execSync("stty -ixon", { stdio: "ignore" });
139582
138500
  } catch {}
139583
138501
  try {
139584
- const initStartedAt = Date.now();
139585
138502
  await tui.start();
139586
- const initMs = Date.now() - initStartedAt;
139587
- trackLifecycle("started", {
139588
- resumed,
139589
- yolo: opts.yolo,
139590
- auto: opts.auto,
139591
- plan: opts.plan,
139592
- afk: false
139593
- });
139594
- const startupSessionId = tui.getCurrentSessionId();
139595
- const mcpMs = await tui.getStartupMcpMs();
139596
- trackLifecycleForSession(startupSessionId, "startup_perf", {
139597
- duration_ms: Date.now() - startedAt,
139598
- config_ms: configMs,
139599
- init_ms: initMs,
139600
- mcp_ms: mcpMs
139601
- });
139602
138503
  } catch (error) {
139603
- setCrashPhase("shutdown");
139604
- trackLifecycle("exit", { duration_s: (Date.now() - startedAt) / 1e3 });
139605
- await shutdownTelemetry({ timeoutMs: CLI_SHUTDOWN_TIMEOUT_MS });
139606
138504
  await harness.close();
139607
138505
  throw error;
139608
138506
  }
@@ -140109,15 +139007,13 @@ function mapCcConnectMode(mode) {
140109
139007
  }
140110
139008
  }
140111
139009
  async function runStreamJson(opts) {
139010
+ const homeDir = resolveScreamHome();
140112
139011
  const workDir = opts.workDir ?? process.cwd();
140113
- const telemetryBootstrap = createCliTelemetryBootstrap();
140114
- const telemetryClient = { track };
140115
139012
  const harness = new ScreamHarness({
140116
- homeDir: telemetryBootstrap.homeDir,
139013
+ homeDir,
140117
139014
  identity: createScreamCodeHostIdentity("dev"),
140118
139015
  uiMode: "print",
140119
- skillDirs: opts.skillsDirs,
140120
- telemetry: telemetryClient
139016
+ skillDirs: opts.skillsDirs
140121
139017
  });
140122
139018
  const writer = new ClaudeStreamJsonWriter((line) => {
140123
139019
  process.stdout.write(`${line}\n`);
@@ -140213,11 +139109,14 @@ async function runStreamJson(opts) {
140213
139109
  writer.setModel(opts.model ?? config.defaultModel ?? "");
140214
139110
  writer.emitSystem(sessionKey);
140215
139111
  function sendControlRequest(request) {
140216
- return new Promise((resolve) => {
140217
- const reqId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
140218
- pendingApprovals.set(reqId, { resolve });
140219
- writer.emitControlRequest(reqId, request.toolCallId, request.toolName, request.display);
139112
+ let resolve;
139113
+ const promise = new Promise((res) => {
139114
+ resolve = res;
140220
139115
  });
139116
+ const reqId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
139117
+ pendingApprovals.set(reqId, { resolve });
139118
+ writer.emitControlRequest(reqId, request.toolCallId, request.toolName, request.display);
139119
+ return promise;
140221
139120
  }
140222
139121
  switch (opts.permissionMode) {
140223
139122
  case "yolo":
@@ -140242,84 +139141,89 @@ async function runStreamJson(opts) {
140242
139141
  }
140243
139142
  session.setQuestionHandler(() => null);
140244
139143
  }
139144
+ let resolve;
139145
+ let reject;
139146
+ const turnPromise = new Promise((res, rej) => {
139147
+ resolve = res;
139148
+ reject = rej;
139149
+ });
140245
139150
  let activeTurnId;
140246
139151
  let activeAgentId;
140247
139152
  let settled = false;
140248
139153
  let unsubscribe;
140249
- await new Promise((resolve, reject) => {
140250
- const finish = (error) => {
140251
- if (settled) return;
140252
- settled = true;
140253
- unsubscribe?.();
140254
- if (error) {
140255
- writer.emitResult("error", error.message);
140256
- reject(error);
140257
- } else {
140258
- writer.emitResult("success", "");
140259
- resolve();
140260
- }
140261
- };
140262
- unsubscribe = session.onEvent((event) => {
140263
- if (event.type === "error") {
140264
- if (event.agentId !== "main") return;
140265
- finish(/* @__PURE__ */ new Error(`${event.code}: ${event.message}`));
140266
- return;
140267
- }
140268
- if (event.type === "subagent.spawned") {
140269
- subagentNames.set(event.subagentId, event.subagentName);
140270
- writer.writeAssistantDelta(`\n[子任务: ${event.subagentName}]\n`);
140271
- return;
140272
- }
140273
- if (event.type === "subagent.completed") {
140274
- const name = subagentNames.get(event.subagentId) ?? event.subagentId;
140275
- writer.writeAssistantDelta(`\n[子任务完成: ${name}]\n`);
140276
- subagentNames.delete(event.subagentId);
140277
- return;
140278
- }
140279
- if (event.type === "subagent.failed") {
140280
- const name = subagentNames.get(event.subagentId) ?? event.subagentId;
140281
- writer.writeAssistantDelta(`\n[子任务失败: ${name} - ${event.error}]\n`);
140282
- subagentNames.delete(event.subagentId);
140283
- return;
140284
- }
140285
- if (event.type === "turn.started" && activeTurnId === void 0) {
140286
- if (event.agentId !== "main") return;
140287
- activeTurnId = event.turnId;
140288
- activeAgentId = event.agentId;
140289
- return;
140290
- }
140291
- if (activeTurnId === void 0 || activeAgentId === void 0 || !("turnId" in event) || event.turnId !== activeTurnId || event.agentId !== activeAgentId) return;
140292
- const type = event.type;
140293
- if (type === "turn.step.started" || type === "turn.step.interrupted") writer.flushAssistant();
140294
- else if (type === "turn.step.retrying") writer.discardAssistant();
140295
- else if (type === "assistant.delta") writer.writeAssistantDelta(event.delta);
140296
- else if (type === "turn.step.completed") {
140297
- if (event.usage) {
140298
- const inputTotal = (event.usage.inputOther ?? 0) + (event.usage.inputCacheRead ?? 0) + (event.usage.inputCacheCreation ?? 0);
140299
- writer.updateUsage(inputTotal, event.usage.output ?? 0);
140300
- }
140301
- } else if (type === "turn.ended") if (event.reason === "completed") finish();
140302
- else {
140303
- const errMsg = event.error !== void 0 ? `${event.error.code}: ${event.error.message}` : `Turn ended: ${event.reason}`;
140304
- finish(new Error(errMsg));
140305
- }
140306
- });
140307
- session.prompt(userText).catch((error) => {
140308
- const msg = error instanceof Error ? error.message : String(error);
140309
- if (msg.includes("insufficient tool messages") || msg.includes("tool_calls")) {
140310
- log.warn("stream-json: resetting session after tool call mismatch", {
140311
- sessionId: session?.id,
140312
- error: msg
140313
- });
140314
- session?.close().catch(() => {});
140315
- harness.deleteSession(sessionKey).catch(() => {});
140316
- session = void 0;
140317
- finish(/* @__PURE__ */ new Error("会话已自动重置,请重新发送你的消息。"));
140318
- return;
139154
+ const finish = (error) => {
139155
+ if (settled) return;
139156
+ settled = true;
139157
+ unsubscribe?.();
139158
+ if (error) {
139159
+ writer.emitResult("error", error.message);
139160
+ reject(error);
139161
+ } else {
139162
+ writer.emitResult("success", "");
139163
+ resolve();
139164
+ }
139165
+ };
139166
+ unsubscribe = session.onEvent((event) => {
139167
+ if (event.type === "error") {
139168
+ if (event.agentId !== "main") return;
139169
+ finish(/* @__PURE__ */ new Error(`${event.code}: ${event.message}`));
139170
+ return;
139171
+ }
139172
+ if (event.type === "subagent.spawned") {
139173
+ subagentNames.set(event.subagentId, event.subagentName);
139174
+ writer.writeAssistantDelta(`\n[子任务: ${event.subagentName}]\n`);
139175
+ return;
139176
+ }
139177
+ if (event.type === "subagent.completed") {
139178
+ const name = subagentNames.get(event.subagentId) ?? event.subagentId;
139179
+ writer.writeAssistantDelta(`\n[子任务完成: ${name}]\n`);
139180
+ subagentNames.delete(event.subagentId);
139181
+ return;
139182
+ }
139183
+ if (event.type === "subagent.failed") {
139184
+ const name = subagentNames.get(event.subagentId) ?? event.subagentId;
139185
+ writer.writeAssistantDelta(`\n[子任务失败: ${name} - ${event.error}]\n`);
139186
+ subagentNames.delete(event.subagentId);
139187
+ return;
139188
+ }
139189
+ if (event.type === "turn.started" && activeTurnId === void 0) {
139190
+ if (event.agentId !== "main") return;
139191
+ activeTurnId = event.turnId;
139192
+ activeAgentId = event.agentId;
139193
+ return;
139194
+ }
139195
+ if (activeTurnId === void 0 || activeAgentId === void 0 || !("turnId" in event) || event.turnId !== activeTurnId || event.agentId !== activeAgentId) return;
139196
+ const type = event.type;
139197
+ if (type === "turn.step.started" || type === "turn.step.interrupted") writer.flushAssistant();
139198
+ else if (type === "turn.step.retrying") writer.discardAssistant();
139199
+ else if (type === "assistant.delta") writer.writeAssistantDelta(event.delta);
139200
+ else if (type === "turn.step.completed") {
139201
+ if (event.usage) {
139202
+ const inputTotal = (event.usage.inputOther ?? 0) + (event.usage.inputCacheRead ?? 0) + (event.usage.inputCacheCreation ?? 0);
139203
+ writer.updateUsage(inputTotal, event.usage.output ?? 0);
140319
139204
  }
140320
- finish(error instanceof Error ? error : new Error(msg));
140321
- });
139205
+ } else if (type === "turn.ended") if (event.reason === "completed") finish();
139206
+ else {
139207
+ const errMsg = event.error !== void 0 ? `${event.error.code}: ${event.error.message}` : `Turn ended: ${event.reason}`;
139208
+ finish(new Error(errMsg));
139209
+ }
139210
+ });
139211
+ session.prompt(userText).catch((error) => {
139212
+ const msg = error instanceof Error ? error.message : String(error);
139213
+ if (msg.includes("insufficient tool messages") || msg.includes("tool_calls")) {
139214
+ log.warn("stream-json: resetting session after tool call mismatch", {
139215
+ sessionId: session?.id,
139216
+ error: msg
139217
+ });
139218
+ session?.close().catch(() => {});
139219
+ harness.deleteSession(sessionKey).catch(() => {});
139220
+ session = void 0;
139221
+ finish(/* @__PURE__ */ new Error("会话已自动重置,请重新发送你的消息。"));
139222
+ return;
139223
+ }
139224
+ finish(error instanceof Error ? error : new Error(msg));
140322
139225
  });
139226
+ await turnPromise;
140323
139227
  }
140324
139228
  } catch (error) {
140325
139229
  log.error("stream-json: fatal", { error });
@@ -140425,7 +139329,6 @@ async function handleMigrateCommand() {
140425
139329
  }
140426
139330
  function main() {
140427
139331
  initProcessName();
140428
- installCrashHandlers();
140429
139332
  const version = getVersion();
140430
139333
  createProgram(version, (opts) => {
140431
139334
  handleMainCommand(opts, version).catch(async (error) => {