@mindstudio-ai/remy 0.1.175 → 0.1.176

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/headless.js CHANGED
@@ -83,6 +83,7 @@ function resolveConfig(flags) {
83
83
  const env = file.environments?.[activeEnv];
84
84
  const apiKey = flags?.apiKey || process.env.MINDSTUDIO_API_KEY || env?.apiKey || "";
85
85
  const baseUrl2 = flags?.baseUrl || process.env.MINDSTUDIO_BASE_URL || env?.apiBaseUrl || DEFAULT_BASE_URL;
86
+ const appId = process.env.MINDSTUDIO_APP_ID || void 0;
86
87
  if (!apiKey) {
87
88
  log.error("No API key found");
88
89
  throw new Error(
@@ -93,9 +94,10 @@ function resolveConfig(flags) {
93
94
  log.info("Config resolved", {
94
95
  baseUrl: baseUrl2,
95
96
  keySource,
96
- environment: activeEnv
97
+ environment: activeEnv,
98
+ appId
97
99
  });
98
- return { apiKey, baseUrl: baseUrl2 };
100
+ return { apiKey, baseUrl: baseUrl2, appId };
99
101
  }
100
102
 
101
103
  // src/assets.ts
@@ -521,8 +523,10 @@ async function* streamChat(params) {
521
523
  ...subAgentId && { subAgentId },
522
524
  durationMs: elapsed,
523
525
  stopReason: event.stopReason,
526
+ modelId: event.modelId,
524
527
  inputTokens: event.usage.inputTokens,
525
- outputTokens: event.usage.outputTokens
528
+ outputTokens: event.usage.outputTokens,
529
+ cost: event.cost
526
530
  });
527
531
  } else if (event.type === "error") {
528
532
  log2.error("SSE error event", {
@@ -611,6 +615,7 @@ async function generateBackgroundAck(params) {
611
615
  Authorization: `Bearer ${params.apiConfig.apiKey}`
612
616
  },
613
617
  body: JSON.stringify({
618
+ appId: params.apiConfig.appId,
614
619
  agentName: params.agentName,
615
620
  task: params.task
616
621
  }),
@@ -626,6 +631,24 @@ async function generateBackgroundAck(params) {
626
631
  }
627
632
  }
628
633
 
634
+ // src/usageLedger.ts
635
+ import fs5 from "fs";
636
+ var LEDGER_FILE = ".logs/usage.ndjson";
637
+ var fd = null;
638
+ function nanoToDollars(nano) {
639
+ return typeof nano === "number" ? nano / 1e9 : void 0;
640
+ }
641
+ function recordUsage(entry) {
642
+ try {
643
+ if (fd === null) {
644
+ fs5.mkdirSync(".logs", { recursive: true });
645
+ fd = fs5.openSync(LEDGER_FILE, "a");
646
+ }
647
+ fs5.writeSync(fd, JSON.stringify(entry) + "\n");
648
+ } catch {
649
+ }
650
+ }
651
+
629
652
  // src/compaction/index.ts
630
653
  var log3 = createLogger("compaction");
631
654
  var CONVERSATION_SUMMARY_PROMPT = readAsset("compaction", "conversation.md");
@@ -840,6 +863,7 @@ async function generateSummary(apiConfig, name, compactionPrompt, messagesToSumm
840
863
  Conversation to summarize:
841
864
 
842
865
  ${serialized}` : serialized;
866
+ const iterStart = Date.now();
843
867
  for await (const event of streamChat({
844
868
  ...apiConfig,
845
869
  subAgentId: "conversationSummarizer",
@@ -849,6 +873,20 @@ ${serialized}` : serialized;
849
873
  })) {
850
874
  if (event.type === "text") {
851
875
  summaryText += event.text;
876
+ } else if (event.type === "done") {
877
+ recordUsage({
878
+ ts: Date.now(),
879
+ agentName: "conversationSummarizer",
880
+ modelId: event.modelId,
881
+ inputTokens: event.usage.inputTokens,
882
+ outputTokens: event.usage.outputTokens,
883
+ cacheCreationTokens: event.usage.cacheCreationTokens,
884
+ cacheReadTokens: event.usage.cacheReadTokens,
885
+ cost: nanoToDollars(event.cost),
886
+ billingEvents: event.billingEvents,
887
+ durationMs: Date.now() - iterStart,
888
+ toolNames: []
889
+ });
852
890
  } else if (event.type === "error") {
853
891
  log3.error("Summary generation failed", { name, error: event.error });
854
892
  return null;
@@ -863,7 +901,7 @@ ${serialized}` : serialized;
863
901
  }
864
902
 
865
903
  // src/tools/spec/readSpec.ts
866
- import fs5 from "fs/promises";
904
+ import fs6 from "fs/promises";
867
905
 
868
906
  // src/tools/spec/_helpers.ts
869
907
  var HEADING_RE = /^(#{1,6})\s+(.+)$/;
@@ -983,7 +1021,7 @@ var readSpecTool = {
983
1021
  return `Error: ${err.message}`;
984
1022
  }
985
1023
  try {
986
- const content = await fs5.readFile(input.path, "utf-8");
1024
+ const content = await fs6.readFile(input.path, "utf-8");
987
1025
  const allLines = content.split("\n");
988
1026
  const totalLines = allLines.length;
989
1027
  const maxLines = input.maxLines === 0 ? Infinity : input.maxLines || DEFAULT_MAX_LINES;
@@ -1011,7 +1049,7 @@ var readSpecTool = {
1011
1049
  };
1012
1050
 
1013
1051
  // src/tools/spec/writeSpec.ts
1014
- import fs6 from "fs/promises";
1052
+ import fs7 from "fs/promises";
1015
1053
  import path4 from "path";
1016
1054
 
1017
1055
  // src/tools/_helpers/diff.ts
@@ -1088,7 +1126,7 @@ var writeSpecTool = {
1088
1126
  },
1089
1127
  streaming: {
1090
1128
  transform: async (partial) => {
1091
- const oldContent = await fs6.readFile(partial.path, "utf-8").catch(() => "");
1129
+ const oldContent = await fs7.readFile(partial.path, "utf-8").catch(() => "");
1092
1130
  const lineCount = partial.content.split("\n").length;
1093
1131
  return `Writing ${partial.path} (${lineCount} lines)
1094
1132
  ${unifiedDiff(partial.path, oldContent, partial.content)}`;
@@ -1102,13 +1140,13 @@ ${unifiedDiff(partial.path, oldContent, partial.content)}`;
1102
1140
  }
1103
1141
  const release = await acquireFileLock(input.path);
1104
1142
  try {
1105
- await fs6.mkdir(path4.dirname(input.path), { recursive: true });
1143
+ await fs7.mkdir(path4.dirname(input.path), { recursive: true });
1106
1144
  let oldContent = null;
1107
1145
  try {
1108
- oldContent = await fs6.readFile(input.path, "utf-8");
1146
+ oldContent = await fs7.readFile(input.path, "utf-8");
1109
1147
  } catch {
1110
1148
  }
1111
- await fs6.writeFile(input.path, input.content, "utf-8");
1149
+ await fs7.writeFile(input.path, input.content, "utf-8");
1112
1150
  const lineCount = input.content.split("\n").length;
1113
1151
  const label = oldContent !== null ? "Wrote" : "Created";
1114
1152
  return `${label} ${input.path} (${lineCount} lines)
@@ -1122,7 +1160,7 @@ ${unifiedDiff(input.path, oldContent ?? "", input.content)}`;
1122
1160
  };
1123
1161
 
1124
1162
  // src/tools/spec/editSpec.ts
1125
- import fs7 from "fs/promises";
1163
+ import fs8 from "fs/promises";
1126
1164
  var editSpecTool = {
1127
1165
  clearable: true,
1128
1166
  definition: {
@@ -1172,7 +1210,7 @@ var editSpecTool = {
1172
1210
  try {
1173
1211
  let originalContent;
1174
1212
  try {
1175
- originalContent = await fs7.readFile(input.path, "utf-8");
1213
+ originalContent = await fs8.readFile(input.path, "utf-8");
1176
1214
  } catch (err) {
1177
1215
  return `Error reading file: ${err.message}`;
1178
1216
  }
@@ -1228,7 +1266,7 @@ ${tree}`;
1228
1266
  content = lines.join("\n");
1229
1267
  }
1230
1268
  try {
1231
- await fs7.writeFile(input.path, content, "utf-8");
1269
+ await fs8.writeFile(input.path, content, "utf-8");
1232
1270
  } catch (err) {
1233
1271
  return `Error writing file: ${err.message}`;
1234
1272
  }
@@ -1240,7 +1278,7 @@ ${tree}`;
1240
1278
  };
1241
1279
 
1242
1280
  // src/tools/spec/listSpecFiles.ts
1243
- import fs8 from "fs/promises";
1281
+ import fs9 from "fs/promises";
1244
1282
  import path5 from "path";
1245
1283
  var listSpecFilesTool = {
1246
1284
  clearable: false,
@@ -1270,7 +1308,7 @@ var listSpecFilesTool = {
1270
1308
  };
1271
1309
  async function listRecursive(dir) {
1272
1310
  const results = [];
1273
- const entries = await fs8.readdir(dir, { withFileTypes: true });
1311
+ const entries = await fs9.readdir(dir, { withFileTypes: true });
1274
1312
  entries.sort((a, b) => {
1275
1313
  if (a.isDirectory() && !b.isDirectory()) {
1276
1314
  return -1;
@@ -1316,7 +1354,7 @@ var presentPublishPlanTool = {
1316
1354
  };
1317
1355
 
1318
1356
  // src/tools/spec/writePlan.ts
1319
- import fs9 from "fs/promises";
1357
+ import fs10 from "fs/promises";
1320
1358
  var PLAN_FILE = ".remy-plan.md";
1321
1359
  var writePlanTool = {
1322
1360
  clearable: false,
@@ -1341,13 +1379,13 @@ status: pending
1341
1379
  ---
1342
1380
 
1343
1381
  ${content}`;
1344
- await fs9.writeFile(PLAN_FILE, file, "utf-8");
1382
+ await fs10.writeFile(PLAN_FILE, file, "utf-8");
1345
1383
  return "Plan written to .remy-plan.md. Waiting for user approval.";
1346
1384
  }
1347
1385
  };
1348
1386
 
1349
1387
  // src/tools/spec/updatePlanStatus.ts
1350
- import fs10 from "fs/promises";
1388
+ import fs11 from "fs/promises";
1351
1389
  var PLAN_FILE2 = ".remy-plan.md";
1352
1390
  var updatePlanStatusTool = {
1353
1391
  clearable: false,
@@ -1370,15 +1408,15 @@ var updatePlanStatusTool = {
1370
1408
  const status = input.status;
1371
1409
  let content;
1372
1410
  try {
1373
- content = await fs10.readFile(PLAN_FILE2, "utf-8");
1411
+ content = await fs11.readFile(PLAN_FILE2, "utf-8");
1374
1412
  } catch {
1375
1413
  return "No plan file found.";
1376
1414
  }
1377
1415
  if (status === "rejected") {
1378
- await fs10.unlink(PLAN_FILE2);
1416
+ await fs11.unlink(PLAN_FILE2);
1379
1417
  return "Plan rejected and removed.";
1380
1418
  }
1381
- await fs10.writeFile(
1419
+ await fs11.writeFile(
1382
1420
  PLAN_FILE2,
1383
1421
  content.replace(/^status:\s*\w+/m, `status: ${status}`),
1384
1422
  "utf-8"
@@ -1684,6 +1722,90 @@ var askMindStudioSdkTool = {
1684
1722
  }
1685
1723
  };
1686
1724
 
1725
+ // src/subagents/common/runMindstudioCli.ts
1726
+ function stripFlags(args) {
1727
+ const out = [];
1728
+ for (let i = 0; i < args.length; i++) {
1729
+ const arg = args[i];
1730
+ if (arg === "--no-meta") {
1731
+ continue;
1732
+ }
1733
+ if (arg === "--output-key") {
1734
+ i++;
1735
+ continue;
1736
+ }
1737
+ out.push(arg);
1738
+ }
1739
+ return out;
1740
+ }
1741
+ async function runMindstudioCli(args, options) {
1742
+ const cleanArgs = stripFlags(args);
1743
+ const cliAction = args[0];
1744
+ const agentName = options?.caller ?? "mindstudio-cli";
1745
+ const start = Date.now();
1746
+ const raw = await runCli("mindstudio", cleanArgs, options);
1747
+ let envelope;
1748
+ try {
1749
+ envelope = JSON.parse(raw);
1750
+ } catch {
1751
+ return raw;
1752
+ }
1753
+ if (envelope && typeof envelope === "object" && Array.isArray(envelope.results)) {
1754
+ const durationMs = Date.now() - start;
1755
+ for (const step of envelope.results) {
1756
+ if (typeof step?.billingCost === "number") {
1757
+ recordUsage({
1758
+ ts: Date.now(),
1759
+ agentName,
1760
+ cliAction: `${cliAction}:${step.stepType ?? "step"}`,
1761
+ cost: nanoToDollars(step.billingCost),
1762
+ inputTokens: 0,
1763
+ outputTokens: 0,
1764
+ durationMs,
1765
+ toolNames: []
1766
+ });
1767
+ }
1768
+ }
1769
+ return JSON.stringify(envelope.results);
1770
+ }
1771
+ if (typeof envelope?.$billingCost === "number") {
1772
+ recordUsage({
1773
+ ts: Date.now(),
1774
+ agentName,
1775
+ cliAction,
1776
+ cost: nanoToDollars(envelope.$billingCost),
1777
+ billingEvents: envelope.$billingEvents,
1778
+ // CLI billing isn't expressed as input/output tokens for most actions
1779
+ // (image gen is per-image, scrape per-page, etc). `numUnits` inside each
1780
+ // billingEvent carries the per-event unit count.
1781
+ inputTokens: 0,
1782
+ outputTokens: 0,
1783
+ durationMs: Date.now() - start,
1784
+ toolNames: []
1785
+ });
1786
+ }
1787
+ if (options?.outputKey) {
1788
+ const v = envelope?.[options.outputKey];
1789
+ if (v === void 0 || v === null) {
1790
+ return JSON.stringify(stripDollarKeys(envelope));
1791
+ }
1792
+ return typeof v === "string" ? v : JSON.stringify(v);
1793
+ }
1794
+ return JSON.stringify(stripDollarKeys(envelope));
1795
+ }
1796
+ function stripDollarKeys(envelope) {
1797
+ if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
1798
+ return envelope;
1799
+ }
1800
+ const out = {};
1801
+ for (const [k, v] of Object.entries(envelope)) {
1802
+ if (!k.startsWith("$")) {
1803
+ out[k] = v;
1804
+ }
1805
+ }
1806
+ return out;
1807
+ }
1808
+
1687
1809
  // src/tools/common/searchGoogle.ts
1688
1810
  var searchGoogleTool = {
1689
1811
  clearable: false,
@@ -1703,19 +1825,9 @@ var searchGoogleTool = {
1703
1825
  },
1704
1826
  async execute(input, context) {
1705
1827
  const query = input.query;
1706
- return runCli(
1707
- "mindstudio",
1708
- [
1709
- "search-google",
1710
- "--query",
1711
- query,
1712
- "--export-type",
1713
- "json",
1714
- "--output-key",
1715
- "results",
1716
- "--no-meta"
1717
- ],
1718
- { maxBuffer: 512 * 1024, onLog: context?.onLog }
1828
+ return runMindstudioCli(
1829
+ ["search-google", "--query", query, "--export-type", "json"],
1830
+ { outputKey: "results", maxBuffer: 512 * 1024, onLog: context?.onLog }
1719
1831
  );
1720
1832
  }
1721
1833
  };
@@ -1779,7 +1891,7 @@ var compactConversationTool = {
1779
1891
  };
1780
1892
 
1781
1893
  // src/tools/code/readFile.ts
1782
- import fs11 from "fs/promises";
1894
+ import fs12 from "fs/promises";
1783
1895
  var DEFAULT_MAX_LINES2 = 500;
1784
1896
  function isBinary(buffer) {
1785
1897
  const sample = buffer.subarray(0, 8192);
@@ -1816,7 +1928,7 @@ var readFileTool = {
1816
1928
  },
1817
1929
  async execute(input) {
1818
1930
  try {
1819
- const buffer = await fs11.readFile(input.path);
1931
+ const buffer = await fs12.readFile(input.path);
1820
1932
  if (isBinary(buffer)) {
1821
1933
  const size = buffer.length;
1822
1934
  const unit = size > 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)}MB` : `${(size / 1024).toFixed(1)}KB`;
@@ -1850,7 +1962,7 @@ var readFileTool = {
1850
1962
  };
1851
1963
 
1852
1964
  // src/tools/code/writeFile.ts
1853
- import fs12 from "fs/promises";
1965
+ import fs13 from "fs/promises";
1854
1966
  import path6 from "path";
1855
1967
  var writeFileTool = {
1856
1968
  clearable: true,
@@ -1888,7 +2000,7 @@ var writeFileTool = {
1888
2000
  lastNewlineCount = newlineCount;
1889
2001
  const lastNewline = partial.content.lastIndexOf("\n");
1890
2002
  const completeContent = partial.content.substring(0, lastNewline + 1);
1891
- const oldContent = await fs12.readFile(partial.path, "utf-8").catch(() => "");
2003
+ const oldContent = await fs13.readFile(partial.path, "utf-8").catch(() => "");
1892
2004
  return `Writing ${partial.path} (${newlineCount} lines)
1893
2005
  ${unifiedDiff(partial.path, oldContent, completeContent)}`;
1894
2006
  }
@@ -1897,13 +2009,13 @@ ${unifiedDiff(partial.path, oldContent, completeContent)}`;
1897
2009
  async execute(input) {
1898
2010
  const release = await acquireFileLock(input.path);
1899
2011
  try {
1900
- await fs12.mkdir(path6.dirname(input.path), { recursive: true });
2012
+ await fs13.mkdir(path6.dirname(input.path), { recursive: true });
1901
2013
  let oldContent = null;
1902
2014
  try {
1903
- oldContent = await fs12.readFile(input.path, "utf-8");
2015
+ oldContent = await fs13.readFile(input.path, "utf-8");
1904
2016
  } catch {
1905
2017
  }
1906
- await fs12.writeFile(input.path, input.content, "utf-8");
2018
+ await fs13.writeFile(input.path, input.content, "utf-8");
1907
2019
  const lineCount = input.content.split("\n").length;
1908
2020
  const label = oldContent !== null ? "Wrote" : "Created";
1909
2021
  return `${label} ${input.path} (${lineCount} lines)
@@ -1917,7 +2029,7 @@ ${unifiedDiff(input.path, oldContent ?? "", input.content)}`;
1917
2029
  };
1918
2030
 
1919
2031
  // src/tools/code/editFile/index.ts
1920
- import fs13 from "fs/promises";
2032
+ import fs14 from "fs/promises";
1921
2033
 
1922
2034
  // src/tools/code/editFile/_helpers.ts
1923
2035
  function buildLineOffsets(content) {
@@ -2030,7 +2142,7 @@ var editFileTool = {
2030
2142
  async execute(input) {
2031
2143
  const release = await acquireFileLock(input.path);
2032
2144
  try {
2033
- const content = await fs13.readFile(input.path, "utf-8");
2145
+ const content = await fs14.readFile(input.path, "utf-8");
2034
2146
  const { old_string, new_string, replace_all } = input;
2035
2147
  const occurrences = findOccurrences(content, old_string);
2036
2148
  if (replace_all) {
@@ -2046,7 +2158,7 @@ var editFileTool = {
2046
2158
  new_string
2047
2159
  );
2048
2160
  }
2049
- await fs13.writeFile(input.path, updated, "utf-8");
2161
+ await fs14.writeFile(input.path, updated, "utf-8");
2050
2162
  return `Replaced ${occurrences.length} occurrence${occurrences.length > 1 ? "s" : ""} in ${input.path}
2051
2163
  ${unifiedDiff(input.path, content, updated)}`;
2052
2164
  }
@@ -2057,7 +2169,7 @@ ${unifiedDiff(input.path, content, updated)}`;
2057
2169
  old_string.length,
2058
2170
  new_string
2059
2171
  );
2060
- await fs13.writeFile(input.path, updated, "utf-8");
2172
+ await fs14.writeFile(input.path, updated, "utf-8");
2061
2173
  return `Updated ${input.path}
2062
2174
  ${unifiedDiff(input.path, content, updated)}`;
2063
2175
  }
@@ -2073,7 +2185,7 @@ ${unifiedDiff(input.path, content, updated)}`;
2073
2185
  flex.matchedText.length,
2074
2186
  new_string
2075
2187
  );
2076
- await fs13.writeFile(input.path, updated, "utf-8");
2188
+ await fs14.writeFile(input.path, updated, "utf-8");
2077
2189
  return `Updated ${input.path} (matched with flexible whitespace at line ${flex.line})
2078
2190
  ${unifiedDiff(input.path, content, updated)}`;
2079
2191
  }
@@ -2305,12 +2417,12 @@ var globTool = {
2305
2417
  };
2306
2418
 
2307
2419
  // src/tools/code/listDir.ts
2308
- import fs14 from "fs/promises";
2420
+ import fs15 from "fs/promises";
2309
2421
  import path7 from "path";
2310
2422
  var EXCLUDE = /* @__PURE__ */ new Set([".git", "node_modules"]);
2311
2423
  var MAX_CHILDREN = 15;
2312
2424
  async function readAndSort(dirPath) {
2313
- const entries = await fs14.readdir(dirPath, { withFileTypes: true });
2425
+ const entries = await fs15.readdir(dirPath, { withFileTypes: true });
2314
2426
  return entries.filter((e) => !EXCLUDE.has(e.name)).sort((a, b) => {
2315
2427
  if (a.isDirectory() && !b.isDirectory()) {
2316
2428
  return -1;
@@ -2351,7 +2463,7 @@ function formatSize(bytes) {
2351
2463
  }
2352
2464
  async function formatFile(dirPath, name, indent) {
2353
2465
  try {
2354
- const stat = await fs14.stat(path7.join(dirPath, name));
2466
+ const stat = await fs15.stat(path7.join(dirPath, name));
2355
2467
  return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat.size)}`;
2356
2468
  } catch {
2357
2469
  return `${indent}${name}`;
@@ -2661,8 +2773,7 @@ var VISION_MODEL_OVERRIDE = {
2661
2773
  };
2662
2774
  async function analyzeImage(params) {
2663
2775
  const { prompt, imageUrl, timeout = 2e5, onLog } = params;
2664
- return runCli(
2665
- "mindstudio",
2776
+ return runMindstudioCli(
2666
2777
  [
2667
2778
  "analyze-image",
2668
2779
  "--prompt",
@@ -2670,12 +2781,9 @@ async function analyzeImage(params) {
2670
2781
  "--image-url",
2671
2782
  imageUrl,
2672
2783
  "--vision-model-override",
2673
- JSON.stringify(VISION_MODEL_OVERRIDE),
2674
- "--output-key",
2675
- "analysis",
2676
- "--no-meta"
2784
+ JSON.stringify(VISION_MODEL_OVERRIDE)
2677
2785
  ],
2678
- { timeout, onLog }
2786
+ { outputKey: "analysis", timeout, onLog }
2679
2787
  );
2680
2788
  }
2681
2789
 
@@ -2719,11 +2827,11 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
2719
2827
  let prompt;
2720
2828
  let existingUrl;
2721
2829
  let onLog;
2722
- let path11;
2830
+ let path12;
2723
2831
  if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
2724
2832
  prompt = promptOrOptions.prompt;
2725
2833
  existingUrl = promptOrOptions.imageUrl;
2726
- path11 = promptOrOptions.path;
2834
+ path12 = promptOrOptions.path;
2727
2835
  onLog = promptOrOptions.onLog;
2728
2836
  } else {
2729
2837
  prompt = promptOrOptions;
@@ -2735,7 +2843,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
2735
2843
  } else {
2736
2844
  const ssResult = await sidecarRequest(
2737
2845
  "/screenshot-full-page",
2738
- path11 ? { path: path11 } : void 0,
2846
+ path12 ? { path: path12 } : void 0,
2739
2847
  { timeout: 12e4 }
2740
2848
  );
2741
2849
  url = ssResult?.url || ssResult?.screenshotUrl;
@@ -2792,7 +2900,7 @@ function startStatusWatcher(config) {
2792
2900
  "Content-Type": "application/json",
2793
2901
  Authorization: `Bearer ${apiConfig.apiKey}`
2794
2902
  },
2795
- body: JSON.stringify({ context }),
2903
+ body: JSON.stringify({ appId: apiConfig.appId, context }),
2796
2904
  signal
2797
2905
  });
2798
2906
  if (!res.ok || stopped) {
@@ -2984,8 +3092,14 @@ ${content}` : attachmentHeader;
2984
3092
  const text = blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
2985
3093
  const toolCalls = blocks.filter((b) => b.type === "tool").map((b) => ({ id: b.id, name: b.name, input: b.input }));
2986
3094
  const thinking = blocks.filter(
2987
- (b) => b.type === "thinking"
2988
- ).map((b) => ({ thinking: b.thinking, signature: b.signature }));
3095
+ (b) => b.type === "thinking" || b.type === "redacted_thinking"
3096
+ ).map(
3097
+ (b) => b.type === "thinking" ? {
3098
+ type: "thinking",
3099
+ thinking: b.thinking,
3100
+ signature: b.signature
3101
+ } : { type: "redacted_thinking", data: b.data }
3102
+ );
2989
3103
  const cleaned2 = {
2990
3104
  role: msg.role,
2991
3105
  content: text
@@ -3075,10 +3189,13 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
3075
3189
  if (signal?.aborted) {
3076
3190
  return abortResult([]);
3077
3191
  }
3192
+ const iterStart = Date.now();
3078
3193
  const contentBlocks = [];
3079
3194
  let thinkingStartedAt = 0;
3195
+ let lastThinkingRelatedStartedAt;
3080
3196
  let stopReason = "end_turn";
3081
3197
  let currentToolNames = "";
3198
+ let lastUsage;
3082
3199
  const statusWatcher = startStatusWatcher({
3083
3200
  apiConfig,
3084
3201
  getContext: () => {
@@ -3153,8 +3270,20 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
3153
3270
  startedAt: thinkingStartedAt,
3154
3271
  completedAt: event.ts
3155
3272
  });
3273
+ lastThinkingRelatedStartedAt = thinkingStartedAt;
3156
3274
  thinkingStartedAt = 0;
3157
3275
  break;
3276
+ case "redacted_thinking_complete": {
3277
+ const startedAt = lastThinkingRelatedStartedAt !== void 0 ? lastThinkingRelatedStartedAt + 1 : event.ts;
3278
+ contentBlocks.push({
3279
+ type: "redacted_thinking",
3280
+ data: event.data,
3281
+ startedAt,
3282
+ completedAt: event.ts
3283
+ });
3284
+ lastThinkingRelatedStartedAt = startedAt;
3285
+ break;
3286
+ }
3158
3287
  case "tool_use":
3159
3288
  contentBlocks.push({
3160
3289
  type: "tool",
@@ -3172,6 +3301,30 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
3172
3301
  break;
3173
3302
  case "done":
3174
3303
  stopReason = event.stopReason;
3304
+ lastUsage = {
3305
+ inputTokens: event.usage.inputTokens,
3306
+ outputTokens: event.usage.outputTokens,
3307
+ cacheCreationTokens: event.usage.cacheCreationTokens,
3308
+ cacheReadTokens: event.usage.cacheReadTokens,
3309
+ llmCalls: 1
3310
+ };
3311
+ recordUsage({
3312
+ ts: Date.now(),
3313
+ requestId,
3314
+ agentName: subAgentId || "sub-agent",
3315
+ parentToolId,
3316
+ modelId: event.modelId,
3317
+ inputTokens: event.usage.inputTokens,
3318
+ outputTokens: event.usage.outputTokens,
3319
+ cacheCreationTokens: event.usage.cacheCreationTokens,
3320
+ cacheReadTokens: event.usage.cacheReadTokens,
3321
+ cost: nanoToDollars(event.cost),
3322
+ billingEvents: event.billingEvents,
3323
+ durationMs: Date.now() - iterStart,
3324
+ toolNames: contentBlocks.filter(
3325
+ (b) => b.type === "tool"
3326
+ ).map((b) => b.name)
3327
+ });
3175
3328
  break;
3176
3329
  case "error":
3177
3330
  return {
@@ -3191,7 +3344,8 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
3191
3344
  }
3192
3345
  messages.push({
3193
3346
  role: "assistant",
3194
- content: contentBlocks
3347
+ content: contentBlocks,
3348
+ ...lastUsage ? { usage: lastUsage } : {}
3195
3349
  });
3196
3350
  const toolCalls = contentBlocks.filter(
3197
3351
  (b) => b.type === "tool"
@@ -3517,11 +3671,11 @@ var BROWSER_TOOLS = [
3517
3671
  var BROWSER_EXTERNAL_TOOLS = /* @__PURE__ */ new Set(["browserCommand"]);
3518
3672
 
3519
3673
  // src/subagents/browserAutomation/prompt.ts
3520
- import fs15 from "fs";
3674
+ import fs16 from "fs";
3521
3675
  var BASE_PROMPT = readAsset("subagents/browserAutomation", "prompt.md");
3522
3676
  function getBrowserAutomationPrompt() {
3523
3677
  try {
3524
- const appSpec = fs15.readFileSync("src/app.md", "utf-8").trim();
3678
+ const appSpec = fs16.readFileSync("src/app.md", "utf-8").trim();
3525
3679
  return `${BASE_PROMPT}
3526
3680
 
3527
3681
  <!-- cache_breakpoint -->
@@ -3620,10 +3774,9 @@ var browserAutomationTool = {
3620
3774
  visionModelOverride: VISION_MODEL_OVERRIDE
3621
3775
  }
3622
3776
  }));
3623
- const batchResult = await runCli(
3624
- "mindstudio",
3625
- ["batch", "--no-meta", JSON.stringify(batchInput)],
3626
- { timeout: 2e5 }
3777
+ const batchResult = await runMindstudioCli(
3778
+ ["batch", JSON.stringify(batchInput)],
3779
+ { timeout: 2e5, caller: "browserAutomation" }
3627
3780
  );
3628
3781
  try {
3629
3782
  const analyses = JSON.parse(batchResult);
@@ -3846,19 +3999,9 @@ var definition = {
3846
3999
  }
3847
4000
  };
3848
4001
  async function execute(input, onLog) {
3849
- return runCli(
3850
- "mindstudio",
3851
- [
3852
- "search-google",
3853
- "--query",
3854
- input.query,
3855
- "--export-type",
3856
- "json",
3857
- "--output-key",
3858
- "results",
3859
- "--no-meta"
3860
- ],
3861
- { onLog }
4002
+ return runMindstudioCli(
4003
+ ["search-google", "--query", input.query, "--export-type", "json"],
4004
+ { outputKey: "results", onLog, caller: "designExpert" }
3862
4005
  );
3863
4006
  }
3864
4007
 
@@ -3888,17 +4031,15 @@ async function execute2(input, onLog) {
3888
4031
  if (input.screenshot) {
3889
4032
  pageOptions.screenshot = true;
3890
4033
  }
3891
- return runCli(
3892
- "mindstudio",
4034
+ return runMindstudioCli(
3893
4035
  [
3894
4036
  "scrape-url",
3895
4037
  "--url",
3896
4038
  input.url,
3897
4039
  "--page-options",
3898
- JSON.stringify(pageOptions),
3899
- "--no-meta"
4040
+ JSON.stringify(pageOptions)
3900
4041
  ],
3901
- { onLog }
4042
+ { onLog, caller: "designExpert" }
3902
4043
  );
3903
4044
  }
3904
4045
 
@@ -3956,8 +4097,7 @@ async function execute3(input, onLog) {
3956
4097
  const isImageUrl = /\.(png|jpe?g|webp|gif|svg|avif)(\?|$)/i.test(url);
3957
4098
  let imageUrl = url;
3958
4099
  if (!isImageUrl) {
3959
- const ssUrl = await runCli(
3960
- "mindstudio",
4100
+ const ssUrl = await runMindstudioCli(
3961
4101
  [
3962
4102
  "screenshot-url",
3963
4103
  "--url",
@@ -3967,12 +4107,14 @@ async function execute3(input, onLog) {
3967
4107
  "--width",
3968
4108
  "1440",
3969
4109
  "--delay",
3970
- "2000",
3971
- "--output-key",
3972
- "screenshotUrl",
3973
- "--no-meta"
4110
+ "2000"
3974
4111
  ],
3975
- { timeout: 12e4, onLog }
4112
+ {
4113
+ outputKey: "screenshotUrl",
4114
+ timeout: 12e4,
4115
+ onLog,
4116
+ caller: "designExpert"
4117
+ }
3976
4118
  );
3977
4119
  if (ssUrl.startsWith("Error")) {
3978
4120
  return `Could not screenshot ${url}: ${ssUrl}`;
@@ -4130,19 +4272,15 @@ ${context}
4130
4272
  <brief>
4131
4273
  ${brief}
4132
4274
  </brief>`;
4133
- const enhanced = await runCli(
4134
- "mindstudio",
4275
+ const enhanced = await runMindstudioCli(
4135
4276
  [
4136
4277
  "generate-text",
4137
4278
  "--message",
4138
4279
  message,
4139
4280
  "--model-override",
4140
- JSON.stringify(MODEL_OVERRIDE),
4141
- "--output-key",
4142
- "content",
4143
- "--no-meta"
4281
+ JSON.stringify(MODEL_OVERRIDE)
4144
4282
  ],
4145
- { timeout: 6e4, onLog }
4283
+ { outputKey: "content", timeout: 6e4, onLog, caller: "designExpert" }
4146
4284
  );
4147
4285
  return enhanced.trim();
4148
4286
  }
@@ -4178,11 +4316,14 @@ async function generateImageAssets(opts) {
4178
4316
  config
4179
4317
  }
4180
4318
  });
4181
- const url = await runCli(
4182
- "mindstudio",
4183
- ["generate-image", "--output-key", "imageUrl", "--no-meta"],
4184
- { jsonLogs: true, timeout: 2e5, onLog, stdin: step }
4185
- );
4319
+ const url = await runMindstudioCli(["generate-image"], {
4320
+ outputKey: "imageUrl",
4321
+ jsonLogs: true,
4322
+ timeout: 2e5,
4323
+ onLog,
4324
+ stdin: step,
4325
+ caller: "designExpert"
4326
+ });
4186
4327
  imageUrls = [url];
4187
4328
  } else {
4188
4329
  const steps = enhancedPrompts.map((prompt) => ({
@@ -4195,11 +4336,12 @@ async function generateImageAssets(opts) {
4195
4336
  }
4196
4337
  }
4197
4338
  }));
4198
- const batchResult = await runCli("mindstudio", ["batch", "--no-meta"], {
4339
+ const batchResult = await runMindstudioCli(["batch"], {
4199
4340
  jsonLogs: true,
4200
4341
  timeout: 2e5,
4201
4342
  onLog,
4202
- stdin: JSON.stringify(steps)
4343
+ stdin: JSON.stringify(steps),
4344
+ caller: "designExpert"
4203
4345
  });
4204
4346
  try {
4205
4347
  const parsed = JSON.parse(batchResult);
@@ -4216,17 +4358,14 @@ async function generateImageAssets(opts) {
4216
4358
  if (url.startsWith("Error")) {
4217
4359
  return url;
4218
4360
  }
4219
- const result = await runCli(
4220
- "mindstudio",
4221
- [
4222
- "remove-background-from-image",
4223
- "--image-url",
4224
- url,
4225
- "--output-key",
4226
- "imageUrl",
4227
- "--no-meta"
4228
- ],
4229
- { timeout: 2e5, onLog }
4361
+ const result = await runMindstudioCli(
4362
+ ["remove-background-from-image", "--image-url", url],
4363
+ {
4364
+ outputKey: "imageUrl",
4365
+ timeout: 2e5,
4366
+ onLog,
4367
+ caller: "designExpert"
4368
+ }
4230
4369
  );
4231
4370
  return result.startsWith("Error") ? url : result;
4232
4371
  })
@@ -4378,12 +4517,12 @@ async function executeDesignExpertTool(name, input, context, toolCallId, onLog)
4378
4517
  }
4379
4518
 
4380
4519
  // src/subagents/common/context.ts
4381
- import fs16 from "fs";
4520
+ import fs17 from "fs";
4382
4521
  import path8 from "path";
4383
4522
  function walkMdFiles2(dir, skip) {
4384
4523
  const files = [];
4385
4524
  try {
4386
- for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
4525
+ for (const entry of fs17.readdirSync(dir, { withFileTypes: true })) {
4387
4526
  const full = path8.join(dir, entry.name);
4388
4527
  if (entry.isDirectory()) {
4389
4528
  if (!skip?.has(entry.name)) {
@@ -4399,7 +4538,7 @@ function walkMdFiles2(dir, skip) {
4399
4538
  }
4400
4539
  function parseFrontmatter2(filePath) {
4401
4540
  try {
4402
- const content = fs16.readFileSync(filePath, "utf-8");
4541
+ const content = fs17.readFileSync(filePath, "utf-8");
4403
4542
  const match = content.match(/^---\n([\s\S]*?)\n---/);
4404
4543
  if (!match) {
4405
4544
  return {};
@@ -4445,7 +4584,7 @@ function loadRoadmapIndex() {
4445
4584
  const parts = [];
4446
4585
  try {
4447
4586
  const indexJson = JSON.parse(
4448
- fs16.readFileSync("src/roadmap/index.json", "utf-8")
4587
+ fs17.readFileSync("src/roadmap/index.json", "utf-8")
4449
4588
  );
4450
4589
  if (indexJson.lanes?.length > 0) {
4451
4590
  const laneLines = indexJson.lanes.map(
@@ -4548,7 +4687,7 @@ The first-party SDK (@mindstudio-ai/agent) provides access to 200+ AI models (Op
4548
4687
  }
4549
4688
 
4550
4689
  // src/subagents/designExpert/data/sampleCache.ts
4551
- import fs17 from "fs";
4690
+ import fs18 from "fs";
4552
4691
  var SAMPLE_FILE = ".remy-design-sample.json";
4553
4692
  var cached = null;
4554
4693
  function generateIndices(poolSize, sampleSize) {
@@ -4562,14 +4701,14 @@ function generateIndices(poolSize, sampleSize) {
4562
4701
  }
4563
4702
  function load() {
4564
4703
  try {
4565
- return JSON.parse(fs17.readFileSync(SAMPLE_FILE, "utf-8"));
4704
+ return JSON.parse(fs18.readFileSync(SAMPLE_FILE, "utf-8"));
4566
4705
  } catch {
4567
4706
  return null;
4568
4707
  }
4569
4708
  }
4570
4709
  function save(indices) {
4571
4710
  try {
4572
- fs17.writeFileSync(SAMPLE_FILE, JSON.stringify(indices));
4711
+ fs18.writeFileSync(SAMPLE_FILE, JSON.stringify(indices));
4573
4712
  } catch {
4574
4713
  }
4575
4714
  }
@@ -4898,7 +5037,7 @@ var VISION_TOOLS = [
4898
5037
  ];
4899
5038
 
4900
5039
  // src/subagents/productVision/executor.ts
4901
- import fs18 from "fs";
5040
+ import fs19 from "fs";
4902
5041
  import path9 from "path";
4903
5042
  var ROADMAP_DIR = "src/roadmap";
4904
5043
  var PITCH_DECK_SHELL = readAsset(
@@ -4913,13 +5052,13 @@ async function executeVisionTool(name, input, context) {
4913
5052
  case "writeFile": {
4914
5053
  const filePath = resolve(input.path);
4915
5054
  try {
4916
- fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
5055
+ fs19.mkdirSync(ROADMAP_DIR, { recursive: true });
4917
5056
  let oldContent = null;
4918
5057
  try {
4919
- oldContent = fs18.readFileSync(filePath, "utf-8");
5058
+ oldContent = fs19.readFileSync(filePath, "utf-8");
4920
5059
  } catch {
4921
5060
  }
4922
- fs18.writeFileSync(filePath, input.content, "utf-8");
5061
+ fs19.writeFileSync(filePath, input.content, "utf-8");
4923
5062
  const lineCount = input.content.split("\n").length;
4924
5063
  const label = oldContent !== null ? "Wrote" : "Created";
4925
5064
  return `${label} ${filePath} (${lineCount} lines)
@@ -4931,11 +5070,11 @@ ${unifiedDiff(filePath, oldContent ?? "", input.content)}`;
4931
5070
  case "deleteFile": {
4932
5071
  const filePath = resolve(input.path);
4933
5072
  try {
4934
- if (!fs18.existsSync(filePath)) {
5073
+ if (!fs19.existsSync(filePath)) {
4935
5074
  return `Error: ${filePath} does not exist`;
4936
5075
  }
4937
- const oldContent = fs18.readFileSync(filePath, "utf-8");
4938
- fs18.unlinkSync(filePath);
5076
+ const oldContent = fs19.readFileSync(filePath, "utf-8");
5077
+ fs19.unlinkSync(filePath);
4939
5078
  return `Deleted ${filePath}
4940
5079
  ${unifiedDiff(filePath, oldContent, "")}`;
4941
5080
  } catch (err) {
@@ -4948,8 +5087,8 @@ ${unifiedDiff(filePath, oldContent, "")}`;
4948
5087
  }
4949
5088
  const filePath = resolve("pitch.html");
4950
5089
  try {
4951
- fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
4952
- const existing = fs18.existsSync(filePath) ? fs18.readFileSync(filePath, "utf-8").trim() : "";
5090
+ fs19.mkdirSync(ROADMAP_DIR, { recursive: true });
5091
+ const existing = fs19.existsSync(filePath) ? fs19.readFileSync(filePath, "utf-8").trim() : "";
4953
5092
  const currentDeck = existing || PITCH_DECK_SHELL;
4954
5093
  const task = `
4955
5094
  <pitch_content>${input.task}</pitch_content>
@@ -4974,7 +5113,7 @@ Respond only with the complete HTML file and absolutely no other text. Your resp
4974
5113
  /```(?:html|wireframe)\n([\s\S]*?)```/
4975
5114
  );
4976
5115
  const html = htmlMatch ? htmlMatch[1].trim() : result;
4977
- fs18.writeFileSync(filePath, html, "utf-8");
5116
+ fs19.writeFileSync(filePath, html, "utf-8");
4978
5117
  return `Pitch deck written successfully.`;
4979
5118
  } catch (err) {
4980
5119
  return `Error generating pitch deck: ${err.message}`;
@@ -5191,15 +5330,13 @@ var scrapeWebUrlTool = {
5191
5330
  if (screenshot) {
5192
5331
  pageOptions.screenshot = true;
5193
5332
  }
5194
- return runCli(
5195
- "mindstudio",
5333
+ return runMindstudioCli(
5196
5334
  [
5197
5335
  "scrape-url",
5198
5336
  "--url",
5199
5337
  url,
5200
5338
  "--page-options",
5201
- JSON.stringify(pageOptions),
5202
- "--no-meta"
5339
+ JSON.stringify(pageOptions)
5203
5340
  ],
5204
5341
  { onLog: context?.onLog }
5205
5342
  );
@@ -5307,7 +5444,7 @@ function triggerCompaction(state, apiConfig, opts = {}) {
5307
5444
  }
5308
5445
 
5309
5446
  // src/brandExtraction/index.ts
5310
- import fs19 from "fs";
5447
+ import fs20 from "fs";
5311
5448
  import path10 from "path";
5312
5449
  import { createHash } from "crypto";
5313
5450
  var log8 = createLogger("brandExtraction");
@@ -5356,7 +5493,7 @@ function sha256(input) {
5356
5493
  }
5357
5494
  function readSafe(filePath) {
5358
5495
  try {
5359
- return fs19.readFileSync(filePath, "utf-8");
5496
+ return fs20.readFileSync(filePath, "utf-8");
5360
5497
  } catch {
5361
5498
  return "";
5362
5499
  }
@@ -5364,7 +5501,7 @@ function readSafe(filePath) {
5364
5501
  function walkMdFiles3(dir) {
5365
5502
  const results = [];
5366
5503
  try {
5367
- const entries = fs19.readdirSync(dir, { withFileTypes: true });
5504
+ const entries = fs20.readdirSync(dir, { withFileTypes: true });
5368
5505
  for (const entry of entries) {
5369
5506
  const full = path10.join(dir, entry.name);
5370
5507
  if (entry.isDirectory()) {
@@ -5379,7 +5516,7 @@ function walkMdFiles3(dir) {
5379
5516
  }
5380
5517
  function parseFrontmatter3(filePath) {
5381
5518
  try {
5382
- const content = fs19.readFileSync(filePath, "utf-8");
5519
+ const content = fs20.readFileSync(filePath, "utf-8");
5383
5520
  const match = content.match(/^---\n([\s\S]*?)\n---/);
5384
5521
  if (!match) {
5385
5522
  return { type: "" };
@@ -5398,6 +5535,7 @@ async function extractBrand(apiConfig) {
5398
5535
  return { version: 1 };
5399
5536
  }
5400
5537
  let responseText = "";
5538
+ const iterStart = Date.now();
5401
5539
  try {
5402
5540
  for await (const event of streamChat({
5403
5541
  ...apiConfig,
@@ -5408,6 +5546,20 @@ async function extractBrand(apiConfig) {
5408
5546
  })) {
5409
5547
  if (event.type === "text") {
5410
5548
  responseText += event.text;
5549
+ } else if (event.type === "done") {
5550
+ recordUsage({
5551
+ ts: Date.now(),
5552
+ agentName: "brandExtractor",
5553
+ modelId: event.modelId,
5554
+ inputTokens: event.usage.inputTokens,
5555
+ outputTokens: event.usage.outputTokens,
5556
+ cacheCreationTokens: event.usage.cacheCreationTokens,
5557
+ cacheReadTokens: event.usage.cacheReadTokens,
5558
+ cost: nanoToDollars(event.cost),
5559
+ billingEvents: event.billingEvents,
5560
+ durationMs: Date.now() - iterStart,
5561
+ toolNames: []
5562
+ });
5411
5563
  } else if (event.type === "error") {
5412
5564
  log8.error("Brand extraction stream error", { error: event.error });
5413
5565
  return null;
@@ -5542,14 +5694,14 @@ function pickFont(raw) {
5542
5694
  }
5543
5695
  function persistBrand(brand, inputHash) {
5544
5696
  const tmp = `${BRAND_FILE}.tmp`;
5545
- fs19.writeFileSync(tmp, JSON.stringify(brand, null, 2), "utf-8");
5546
- fs19.renameSync(tmp, BRAND_FILE);
5697
+ fs20.writeFileSync(tmp, JSON.stringify(brand, null, 2), "utf-8");
5698
+ fs20.renameSync(tmp, BRAND_FILE);
5547
5699
  const cache = { inputHash, generatedAt: Date.now() };
5548
- fs19.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
5700
+ fs20.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
5549
5701
  }
5550
5702
  function readCache() {
5551
5703
  try {
5552
- const raw = fs19.readFileSync(CACHE_FILE, "utf-8");
5704
+ const raw = fs20.readFileSync(CACHE_FILE, "utf-8");
5553
5705
  const parsed = JSON.parse(raw);
5554
5706
  if (parsed && typeof parsed.inputHash === "string" && typeof parsed.generatedAt === "number") {
5555
5707
  return parsed;
@@ -5582,12 +5734,14 @@ function triggerBrandExtraction(apiConfig) {
5582
5734
  }
5583
5735
 
5584
5736
  // src/session.ts
5585
- import fs20 from "fs";
5737
+ import fs21 from "fs";
5738
+ import path11 from "path";
5586
5739
  var log10 = createLogger("session");
5587
5740
  var SESSION_FILE = ".remy-session.json";
5741
+ var ARCHIVE_DIR = ".logs/sessions";
5588
5742
  function loadSession(state) {
5589
5743
  try {
5590
- const raw = fs20.readFileSync(SESSION_FILE, "utf-8");
5744
+ const raw = fs21.readFileSync(SESSION_FILE, "utf-8");
5591
5745
  const data = JSON.parse(raw);
5592
5746
  if (Array.isArray(data.messages) && data.messages.length > 0) {
5593
5747
  state.messages = sanitizeMessages(data.messages);
@@ -5636,7 +5790,7 @@ function sanitizeMessages(messages) {
5636
5790
  }
5637
5791
  function saveSession(state) {
5638
5792
  try {
5639
- fs20.writeFileSync(
5793
+ fs21.writeFileSync(
5640
5794
  SESSION_FILE,
5641
5795
  JSON.stringify({ messages: state.messages }, null, 2),
5642
5796
  "utf-8"
@@ -5649,8 +5803,21 @@ function saveSession(state) {
5649
5803
  function clearSession(state) {
5650
5804
  state.messages = [];
5651
5805
  try {
5652
- fs20.unlinkSync(SESSION_FILE);
5653
- } catch {
5806
+ if (fs21.existsSync(SESSION_FILE)) {
5807
+ fs21.mkdirSync(ARCHIVE_DIR, { recursive: true });
5808
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5809
+ const dest = path11.join(ARCHIVE_DIR, `cleared-${ts}.json`);
5810
+ fs21.renameSync(SESSION_FILE, dest);
5811
+ log10.info("Session archived on clear", { dest });
5812
+ }
5813
+ } catch (err) {
5814
+ log10.warn("Session archive on clear failed, deleting instead", {
5815
+ error: err.message
5816
+ });
5817
+ try {
5818
+ fs21.unlinkSync(SESSION_FILE);
5819
+ } catch {
5820
+ }
5654
5821
  }
5655
5822
  }
5656
5823
 
@@ -5960,9 +6127,11 @@ async function runTurn(params) {
5960
6127
  saveSession(state);
5961
6128
  return;
5962
6129
  }
6130
+ const iterStart = Date.now();
5963
6131
  const contentBlocks = [];
5964
6132
  const thinkingBlockStartTimes = [];
5965
6133
  let thinkingCompleteCount = 0;
6134
+ let lastThinkingRelatedStartedAt;
5966
6135
  let textBlockOpen = false;
5967
6136
  const toolInputAccumulators = /* @__PURE__ */ new Map();
5968
6137
  let stopReason = "end_turn";
@@ -6094,16 +6263,30 @@ async function runTurn(params) {
6094
6263
  }
6095
6264
  onEvent({ type: "thinking", text: event.text });
6096
6265
  break;
6097
- case "thinking_complete":
6266
+ case "thinking_complete": {
6267
+ const startedAt = thinkingBlockStartTimes[thinkingCompleteCount] ?? event.ts;
6098
6268
  contentBlocks.push({
6099
6269
  type: "thinking",
6100
6270
  thinking: event.thinking,
6101
6271
  signature: event.signature,
6102
- startedAt: thinkingBlockStartTimes[thinkingCompleteCount] ?? event.ts,
6272
+ startedAt,
6103
6273
  completedAt: event.ts
6104
6274
  });
6105
6275
  thinkingCompleteCount++;
6276
+ lastThinkingRelatedStartedAt = startedAt;
6277
+ break;
6278
+ }
6279
+ case "redacted_thinking_complete": {
6280
+ const startedAt = lastThinkingRelatedStartedAt !== void 0 ? lastThinkingRelatedStartedAt + 1 : event.ts;
6281
+ contentBlocks.push({
6282
+ type: "redacted_thinking",
6283
+ data: event.data,
6284
+ startedAt,
6285
+ completedAt: event.ts
6286
+ });
6287
+ lastThinkingRelatedStartedAt = startedAt;
6106
6288
  break;
6289
+ }
6107
6290
  case "tool_input_delta": {
6108
6291
  const acc = getOrCreateAccumulator2(event.id, event.name);
6109
6292
  acc.json += event.delta;
@@ -6157,6 +6340,22 @@ async function runTurn(params) {
6157
6340
  turnOutputTokens += event.usage.outputTokens;
6158
6341
  turnCacheCreation += lastCallCacheCreation;
6159
6342
  turnCacheRead += lastCallCacheRead;
6343
+ recordUsage({
6344
+ ts: Date.now(),
6345
+ requestId,
6346
+ agentName: "parent",
6347
+ modelId: event.modelId,
6348
+ inputTokens: event.usage.inputTokens,
6349
+ outputTokens: event.usage.outputTokens,
6350
+ cacheCreationTokens: event.usage.cacheCreationTokens,
6351
+ cacheReadTokens: event.usage.cacheReadTokens,
6352
+ cost: nanoToDollars(event.cost),
6353
+ billingEvents: event.billingEvents,
6354
+ durationMs: Date.now() - iterStart,
6355
+ toolNames: contentBlocks.filter(
6356
+ (b) => b.type === "tool"
6357
+ ).map((b) => b.name)
6358
+ });
6160
6359
  break;
6161
6360
  case "error":
6162
6361
  onEvent({ type: "error", error: friendlyError(event.error) });