@mindstudio-ai/remy 0.1.175 → 0.1.177

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/index.js CHANGED
@@ -55,14 +55,14 @@ function initLoggerHeadless(level = "info") {
55
55
  }
56
56
  function initLoggerInteractive(level = "error") {
57
57
  currentLevel = LEVELS[level];
58
- let fd = null;
58
+ let fd2 = null;
59
59
  writeFn = (line) => {
60
60
  try {
61
- if (fd === null) {
61
+ if (fd2 === null) {
62
62
  fs.mkdirSync(".logs", { recursive: true });
63
- fd = fs.openSync(".logs/agent.ndjson", "a");
63
+ fd2 = fs.openSync(".logs/agent.ndjson", "a");
64
64
  }
65
- fs.writeSync(fd, line + "\n");
65
+ fs.writeSync(fd2, line + "\n");
66
66
  } catch {
67
67
  }
68
68
  };
@@ -209,8 +209,10 @@ async function* streamChat(params) {
209
209
  ...subAgentId && { subAgentId },
210
210
  durationMs: elapsed,
211
211
  stopReason: event.stopReason,
212
+ modelId: event.modelId,
212
213
  inputTokens: event.usage.inputTokens,
213
- outputTokens: event.usage.outputTokens
214
+ outputTokens: event.usage.outputTokens,
215
+ cost: event.cost
214
216
  });
215
217
  } else if (event.type === "error") {
216
218
  log.error("SSE error event", {
@@ -296,6 +298,7 @@ async function generateBackgroundAck(params) {
296
298
  Authorization: `Bearer ${params.apiConfig.apiKey}`
297
299
  },
298
300
  body: JSON.stringify({
301
+ appId: params.apiConfig.appId,
299
302
  agentName: params.agentName,
300
303
  task: params.task
301
304
  }),
@@ -1237,12 +1240,127 @@ var init_sdkConsultant = __esm({
1237
1240
  }
1238
1241
  });
1239
1242
 
1243
+ // src/usageLedger.ts
1244
+ import fs8 from "fs";
1245
+ function nanoToDollars(nano) {
1246
+ return typeof nano === "number" ? nano / 1e9 : void 0;
1247
+ }
1248
+ function recordUsage(entry) {
1249
+ try {
1250
+ if (fd === null) {
1251
+ fs8.mkdirSync(".logs", { recursive: true });
1252
+ fd = fs8.openSync(LEDGER_FILE, "a");
1253
+ }
1254
+ fs8.writeSync(fd, JSON.stringify(entry) + "\n");
1255
+ } catch {
1256
+ }
1257
+ }
1258
+ var LEDGER_FILE, fd;
1259
+ var init_usageLedger = __esm({
1260
+ "src/usageLedger.ts"() {
1261
+ "use strict";
1262
+ LEDGER_FILE = ".logs/usage.ndjson";
1263
+ fd = null;
1264
+ }
1265
+ });
1266
+
1267
+ // src/subagents/common/runMindstudioCli.ts
1268
+ function stripFlags(args2) {
1269
+ const out = [];
1270
+ for (let i = 0; i < args2.length; i++) {
1271
+ const arg = args2[i];
1272
+ if (arg === "--no-meta") {
1273
+ continue;
1274
+ }
1275
+ if (arg === "--output-key") {
1276
+ i++;
1277
+ continue;
1278
+ }
1279
+ out.push(arg);
1280
+ }
1281
+ return out;
1282
+ }
1283
+ async function runMindstudioCli(args2, options) {
1284
+ const cleanArgs = stripFlags(args2);
1285
+ const cliAction = args2[0];
1286
+ const agentName = options?.caller ?? "mindstudio-cli";
1287
+ const start = Date.now();
1288
+ const raw = await runCli("mindstudio", cleanArgs, options);
1289
+ let envelope;
1290
+ try {
1291
+ envelope = JSON.parse(raw);
1292
+ } catch {
1293
+ return raw;
1294
+ }
1295
+ if (envelope && typeof envelope === "object" && Array.isArray(envelope.results)) {
1296
+ const durationMs = Date.now() - start;
1297
+ for (const step of envelope.results) {
1298
+ if (typeof step?.billingCost === "number") {
1299
+ recordUsage({
1300
+ ts: Date.now(),
1301
+ agentName,
1302
+ cliAction: `${cliAction}:${step.stepType ?? "step"}`,
1303
+ cost: nanoToDollars(step.billingCost),
1304
+ inputTokens: 0,
1305
+ outputTokens: 0,
1306
+ durationMs,
1307
+ toolNames: []
1308
+ });
1309
+ }
1310
+ }
1311
+ return JSON.stringify(envelope.results);
1312
+ }
1313
+ if (typeof envelope?.$billingCost === "number") {
1314
+ recordUsage({
1315
+ ts: Date.now(),
1316
+ agentName,
1317
+ cliAction,
1318
+ cost: nanoToDollars(envelope.$billingCost),
1319
+ billingEvents: envelope.$billingEvents,
1320
+ // CLI billing isn't expressed as input/output tokens for most actions
1321
+ // (image gen is per-image, scrape per-page, etc). `numUnits` inside each
1322
+ // billingEvent carries the per-event unit count.
1323
+ inputTokens: 0,
1324
+ outputTokens: 0,
1325
+ durationMs: Date.now() - start,
1326
+ toolNames: []
1327
+ });
1328
+ }
1329
+ if (options?.outputKey) {
1330
+ const v = envelope?.[options.outputKey];
1331
+ if (v === void 0 || v === null) {
1332
+ return JSON.stringify(stripDollarKeys(envelope));
1333
+ }
1334
+ return typeof v === "string" ? v : JSON.stringify(v);
1335
+ }
1336
+ return JSON.stringify(stripDollarKeys(envelope));
1337
+ }
1338
+ function stripDollarKeys(envelope) {
1339
+ if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
1340
+ return envelope;
1341
+ }
1342
+ const out = {};
1343
+ for (const [k, v] of Object.entries(envelope)) {
1344
+ if (!k.startsWith("$")) {
1345
+ out[k] = v;
1346
+ }
1347
+ }
1348
+ return out;
1349
+ }
1350
+ var init_runMindstudioCli = __esm({
1351
+ "src/subagents/common/runMindstudioCli.ts"() {
1352
+ "use strict";
1353
+ init_runCli();
1354
+ init_usageLedger();
1355
+ }
1356
+ });
1357
+
1240
1358
  // src/tools/common/searchGoogle.ts
1241
1359
  var searchGoogleTool;
1242
1360
  var init_searchGoogle = __esm({
1243
1361
  "src/tools/common/searchGoogle.ts"() {
1244
1362
  "use strict";
1245
- init_runCli();
1363
+ init_runMindstudioCli();
1246
1364
  searchGoogleTool = {
1247
1365
  clearable: false,
1248
1366
  definition: {
@@ -1261,19 +1379,9 @@ var init_searchGoogle = __esm({
1261
1379
  },
1262
1380
  async execute(input, context) {
1263
1381
  const query = input.query;
1264
- return runCli(
1265
- "mindstudio",
1266
- [
1267
- "search-google",
1268
- "--query",
1269
- query,
1270
- "--export-type",
1271
- "json",
1272
- "--output-key",
1273
- "results",
1274
- "--no-meta"
1275
- ],
1276
- { maxBuffer: 512 * 1024, onLog: context?.onLog }
1382
+ return runMindstudioCli(
1383
+ ["search-google", "--query", query, "--export-type", "json"],
1384
+ { outputKey: "results", maxBuffer: 512 * 1024, onLog: context?.onLog }
1277
1385
  );
1278
1386
  }
1279
1387
  };
@@ -1320,12 +1428,12 @@ var init_setProjectMetadata = __esm({
1320
1428
  });
1321
1429
 
1322
1430
  // src/assets.ts
1323
- import fs8 from "fs";
1431
+ import fs9 from "fs";
1324
1432
  import path3 from "path";
1325
1433
  function findRoot(start) {
1326
1434
  let dir = start;
1327
1435
  while (dir !== path3.dirname(dir)) {
1328
- if (fs8.existsSync(path3.join(dir, "package.json"))) {
1436
+ if (fs9.existsSync(path3.join(dir, "package.json"))) {
1329
1437
  return dir;
1330
1438
  }
1331
1439
  dir = path3.dirname(dir);
@@ -1338,7 +1446,7 @@ function assetPath(...segments) {
1338
1446
  function readAsset(...segments) {
1339
1447
  const full = assetPath(...segments);
1340
1448
  try {
1341
- return fs8.readFileSync(full, "utf-8").trim();
1449
+ return fs9.readFileSync(full, "utf-8").trim();
1342
1450
  } catch {
1343
1451
  throw new Error(`Required asset missing: ${full}`);
1344
1452
  }
@@ -1346,7 +1454,7 @@ function readAsset(...segments) {
1346
1454
  function readJsonAsset(fallback, ...segments) {
1347
1455
  const full = assetPath(...segments);
1348
1456
  try {
1349
- return JSON.parse(fs8.readFileSync(full, "utf-8"));
1457
+ return JSON.parse(fs9.readFileSync(full, "utf-8"));
1350
1458
  } catch {
1351
1459
  return fallback;
1352
1460
  }
@@ -1358,7 +1466,7 @@ var init_assets = __esm({
1358
1466
  ROOT = findRoot(
1359
1467
  import.meta.dirname ?? path3.dirname(new URL(import.meta.url).pathname)
1360
1468
  );
1361
- ASSETS_BASE = fs8.existsSync(path3.join(ROOT, "dist", "prompt")) ? path3.join(ROOT, "dist") : path3.join(ROOT, "src");
1469
+ ASSETS_BASE = fs9.existsSync(path3.join(ROOT, "dist", "prompt")) ? path3.join(ROOT, "dist") : path3.join(ROOT, "src");
1362
1470
  }
1363
1471
  });
1364
1472
 
@@ -1571,6 +1679,7 @@ async function generateSummary(apiConfig, name, compactionPrompt, messagesToSumm
1571
1679
  Conversation to summarize:
1572
1680
 
1573
1681
  ${serialized}` : serialized;
1682
+ const iterStart = Date.now();
1574
1683
  for await (const event of streamChat({
1575
1684
  ...apiConfig,
1576
1685
  subAgentId: "conversationSummarizer",
@@ -1580,6 +1689,20 @@ ${serialized}` : serialized;
1580
1689
  })) {
1581
1690
  if (event.type === "text") {
1582
1691
  summaryText += event.text;
1692
+ } else if (event.type === "done") {
1693
+ recordUsage({
1694
+ ts: Date.now(),
1695
+ agentName: "conversationSummarizer",
1696
+ modelId: event.modelId,
1697
+ inputTokens: event.usage.inputTokens,
1698
+ outputTokens: event.usage.outputTokens,
1699
+ cacheCreationTokens: event.usage.cacheCreationTokens,
1700
+ cacheReadTokens: event.usage.cacheReadTokens,
1701
+ cost: nanoToDollars(event.cost),
1702
+ billingEvents: event.billingEvents,
1703
+ durationMs: Date.now() - iterStart,
1704
+ toolNames: []
1705
+ });
1583
1706
  } else if (event.type === "error") {
1584
1707
  log2.error("Summary generation failed", { name, error: event.error });
1585
1708
  return null;
@@ -1599,6 +1722,7 @@ var init_compaction = __esm({
1599
1722
  init_api();
1600
1723
  init_assets();
1601
1724
  init_logger();
1725
+ init_usageLedger();
1602
1726
  log2 = createLogger("compaction");
1603
1727
  CONVERSATION_SUMMARY_PROMPT = readAsset("compaction", "conversation.md");
1604
1728
  SUBAGENT_SUMMARY_PROMPT = readAsset("compaction", "subagent.md");
@@ -1608,11 +1732,11 @@ var init_compaction = __esm({
1608
1732
  });
1609
1733
 
1610
1734
  // src/prompt/static/projectContext.ts
1611
- import fs9 from "fs";
1735
+ import fs10 from "fs";
1612
1736
  import path4 from "path";
1613
1737
  function loadProjectManifest() {
1614
1738
  try {
1615
- const manifest = fs9.readFileSync("mindstudio.json", "utf-8");
1739
+ const manifest = fs10.readFileSync("mindstudio.json", "utf-8");
1616
1740
  return `
1617
1741
  ## Project Manifest (mindstudio.json)
1618
1742
  \`\`\`json
@@ -1653,7 +1777,7 @@ ${entries.join("\n")}`;
1653
1777
  function walkMdFiles(dir) {
1654
1778
  const results = [];
1655
1779
  try {
1656
- const entries = fs9.readdirSync(dir, { withFileTypes: true });
1780
+ const entries = fs10.readdirSync(dir, { withFileTypes: true });
1657
1781
  for (const entry of entries) {
1658
1782
  const full = path4.join(dir, entry.name);
1659
1783
  if (entry.isDirectory()) {
@@ -1668,7 +1792,7 @@ function walkMdFiles(dir) {
1668
1792
  }
1669
1793
  function parseFrontmatter(filePath) {
1670
1794
  try {
1671
- const content = fs9.readFileSync(filePath, "utf-8");
1795
+ const content = fs10.readFileSync(filePath, "utf-8");
1672
1796
  const match = content.match(/^---\n([\s\S]*?)\n---/);
1673
1797
  if (!match) {
1674
1798
  return { name: "", description: "", type: "" };
@@ -1684,7 +1808,7 @@ function parseFrontmatter(filePath) {
1684
1808
  }
1685
1809
  function loadPlanStatus() {
1686
1810
  try {
1687
- const content = fs9.readFileSync(".remy-plan.md", "utf-8");
1811
+ const content = fs10.readFileSync(".remy-plan.md", "utf-8");
1688
1812
  const match = content.match(/^---\n([\s\S]*?)\n---/);
1689
1813
  const status = match?.[1]?.match(/^status:\s*(.+)$/m)?.[1]?.trim();
1690
1814
  if (status === "pending") {
@@ -1706,7 +1830,7 @@ The user has approved your implementation plan in .remy-plan.md. You may referen
1706
1830
  }
1707
1831
  function loadProjectFileListing() {
1708
1832
  try {
1709
- const entries = fs9.readdirSync(".", { withFileTypes: true });
1833
+ const entries = fs10.readdirSync(".", { withFileTypes: true });
1710
1834
  const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
1711
1835
  if (a.isDirectory() && !b.isDirectory()) {
1712
1836
  return -1;
@@ -1961,7 +2085,7 @@ var init_compactConversation = __esm({
1961
2085
  });
1962
2086
 
1963
2087
  // src/tools/code/readFile.ts
1964
- import fs10 from "fs/promises";
2088
+ import fs11 from "fs/promises";
1965
2089
  function isBinary(buffer) {
1966
2090
  const sample = buffer.subarray(0, 8192);
1967
2091
  for (let i = 0; i < sample.length; i++) {
@@ -2002,7 +2126,7 @@ var init_readFile = __esm({
2002
2126
  },
2003
2127
  async execute(input) {
2004
2128
  try {
2005
- const buffer = await fs10.readFile(input.path);
2129
+ const buffer = await fs11.readFile(input.path);
2006
2130
  if (isBinary(buffer)) {
2007
2131
  const size = buffer.length;
2008
2132
  const unit = size > 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)}MB` : `${(size / 1024).toFixed(1)}KB`;
@@ -2038,7 +2162,7 @@ var init_readFile = __esm({
2038
2162
  });
2039
2163
 
2040
2164
  // src/tools/code/writeFile.ts
2041
- import fs11 from "fs/promises";
2165
+ import fs12 from "fs/promises";
2042
2166
  import path5 from "path";
2043
2167
  var writeFileTool;
2044
2168
  var init_writeFile = __esm({
@@ -2082,7 +2206,7 @@ var init_writeFile = __esm({
2082
2206
  lastNewlineCount = newlineCount;
2083
2207
  const lastNewline = partial.content.lastIndexOf("\n");
2084
2208
  const completeContent = partial.content.substring(0, lastNewline + 1);
2085
- const oldContent = await fs11.readFile(partial.path, "utf-8").catch(() => "");
2209
+ const oldContent = await fs12.readFile(partial.path, "utf-8").catch(() => "");
2086
2210
  return `Writing ${partial.path} (${newlineCount} lines)
2087
2211
  ${unifiedDiff(partial.path, oldContent, completeContent)}`;
2088
2212
  }
@@ -2091,13 +2215,13 @@ ${unifiedDiff(partial.path, oldContent, completeContent)}`;
2091
2215
  async execute(input) {
2092
2216
  const release = await acquireFileLock(input.path);
2093
2217
  try {
2094
- await fs11.mkdir(path5.dirname(input.path), { recursive: true });
2218
+ await fs12.mkdir(path5.dirname(input.path), { recursive: true });
2095
2219
  let oldContent = null;
2096
2220
  try {
2097
- oldContent = await fs11.readFile(input.path, "utf-8");
2221
+ oldContent = await fs12.readFile(input.path, "utf-8");
2098
2222
  } catch {
2099
2223
  }
2100
- await fs11.writeFile(input.path, input.content, "utf-8");
2224
+ await fs12.writeFile(input.path, input.content, "utf-8");
2101
2225
  const lineCount = input.content.split("\n").length;
2102
2226
  const label = oldContent !== null ? "Wrote" : "Created";
2103
2227
  return `${label} ${input.path} (${lineCount} lines)
@@ -2197,7 +2321,7 @@ var init_helpers2 = __esm({
2197
2321
  });
2198
2322
 
2199
2323
  // src/tools/code/editFile/index.ts
2200
- import fs12 from "fs/promises";
2324
+ import fs13 from "fs/promises";
2201
2325
  var editFileTool;
2202
2326
  var init_editFile = __esm({
2203
2327
  "src/tools/code/editFile/index.ts"() {
@@ -2236,7 +2360,7 @@ var init_editFile = __esm({
2236
2360
  async execute(input) {
2237
2361
  const release = await acquireFileLock(input.path);
2238
2362
  try {
2239
- const content = await fs12.readFile(input.path, "utf-8");
2363
+ const content = await fs13.readFile(input.path, "utf-8");
2240
2364
  const { old_string, new_string, replace_all } = input;
2241
2365
  const occurrences = findOccurrences(content, old_string);
2242
2366
  if (replace_all) {
@@ -2252,7 +2376,7 @@ var init_editFile = __esm({
2252
2376
  new_string
2253
2377
  );
2254
2378
  }
2255
- await fs12.writeFile(input.path, updated, "utf-8");
2379
+ await fs13.writeFile(input.path, updated, "utf-8");
2256
2380
  return `Replaced ${occurrences.length} occurrence${occurrences.length > 1 ? "s" : ""} in ${input.path}
2257
2381
  ${unifiedDiff(input.path, content, updated)}`;
2258
2382
  }
@@ -2263,7 +2387,7 @@ ${unifiedDiff(input.path, content, updated)}`;
2263
2387
  old_string.length,
2264
2388
  new_string
2265
2389
  );
2266
- await fs12.writeFile(input.path, updated, "utf-8");
2390
+ await fs13.writeFile(input.path, updated, "utf-8");
2267
2391
  return `Updated ${input.path}
2268
2392
  ${unifiedDiff(input.path, content, updated)}`;
2269
2393
  }
@@ -2279,7 +2403,7 @@ ${unifiedDiff(input.path, content, updated)}`;
2279
2403
  flex.matchedText.length,
2280
2404
  new_string
2281
2405
  );
2282
- await fs12.writeFile(input.path, updated, "utf-8");
2406
+ await fs13.writeFile(input.path, updated, "utf-8");
2283
2407
  return `Updated ${input.path} (matched with flexible whitespace at line ${flex.line})
2284
2408
  ${unifiedDiff(input.path, content, updated)}`;
2285
2409
  }
@@ -2531,10 +2655,10 @@ var init_glob = __esm({
2531
2655
  });
2532
2656
 
2533
2657
  // src/tools/code/listDir.ts
2534
- import fs13 from "fs/promises";
2658
+ import fs14 from "fs/promises";
2535
2659
  import path6 from "path";
2536
2660
  async function readAndSort(dirPath) {
2537
- const entries = await fs13.readdir(dirPath, { withFileTypes: true });
2661
+ const entries = await fs14.readdir(dirPath, { withFileTypes: true });
2538
2662
  return entries.filter((e) => !EXCLUDE.has(e.name)).sort((a, b) => {
2539
2663
  if (a.isDirectory() && !b.isDirectory()) {
2540
2664
  return -1;
@@ -2575,7 +2699,7 @@ function formatSize(bytes) {
2575
2699
  }
2576
2700
  async function formatFile(dirPath, name, indent) {
2577
2701
  try {
2578
- const stat = await fs13.stat(path6.join(dirPath, name));
2702
+ const stat = await fs14.stat(path6.join(dirPath, name));
2579
2703
  return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat.size)}`;
2580
2704
  } catch {
2581
2705
  return `${indent}${name}`;
@@ -2940,8 +3064,7 @@ var init_queryDatabase = __esm({
2940
3064
  // src/subagents/common/analyzeImage.ts
2941
3065
  async function analyzeImage(params) {
2942
3066
  const { prompt, imageUrl, timeout = 2e5, onLog } = params;
2943
- return runCli(
2944
- "mindstudio",
3067
+ return runMindstudioCli(
2945
3068
  [
2946
3069
  "analyze-image",
2947
3070
  "--prompt",
@@ -2949,19 +3072,16 @@ async function analyzeImage(params) {
2949
3072
  "--image-url",
2950
3073
  imageUrl,
2951
3074
  "--vision-model-override",
2952
- JSON.stringify(VISION_MODEL_OVERRIDE),
2953
- "--output-key",
2954
- "analysis",
2955
- "--no-meta"
3075
+ JSON.stringify(VISION_MODEL_OVERRIDE)
2956
3076
  ],
2957
- { timeout, onLog }
3077
+ { outputKey: "analysis", timeout, onLog }
2958
3078
  );
2959
3079
  }
2960
3080
  var VISION_MODEL, VISION_MODEL_OVERRIDE;
2961
3081
  var init_analyzeImage = __esm({
2962
3082
  "src/subagents/common/analyzeImage.ts"() {
2963
3083
  "use strict";
2964
- init_runCli();
3084
+ init_runMindstudioCli();
2965
3085
  VISION_MODEL = "claude-4-6-sonnet";
2966
3086
  VISION_MODEL_OVERRIDE = {
2967
3087
  model: VISION_MODEL,
@@ -3006,11 +3126,11 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
3006
3126
  let prompt;
3007
3127
  let existingUrl;
3008
3128
  let onLog;
3009
- let path12;
3129
+ let path13;
3010
3130
  if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
3011
3131
  prompt = promptOrOptions.prompt;
3012
3132
  existingUrl = promptOrOptions.imageUrl;
3013
- path12 = promptOrOptions.path;
3133
+ path13 = promptOrOptions.path;
3014
3134
  onLog = promptOrOptions.onLog;
3015
3135
  } else {
3016
3136
  prompt = promptOrOptions;
@@ -3022,7 +3142,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
3022
3142
  } else {
3023
3143
  const ssResult = await sidecarRequest(
3024
3144
  "/screenshot-full-page",
3025
- path12 ? { path: path12 } : void 0,
3145
+ path13 ? { path: path13 } : void 0,
3026
3146
  { timeout: 12e4 }
3027
3147
  );
3028
3148
  url = ssResult?.url || ssResult?.screenshotUrl;
@@ -3097,7 +3217,7 @@ function startStatusWatcher(config) {
3097
3217
  "Content-Type": "application/json",
3098
3218
  Authorization: `Bearer ${apiConfig.apiKey}`
3099
3219
  },
3100
- body: JSON.stringify({ context }),
3220
+ body: JSON.stringify({ appId: apiConfig.appId, context }),
3101
3221
  signal
3102
3222
  });
3103
3223
  if (!res.ok || stopped) {
@@ -3299,8 +3419,14 @@ ${content}` : attachmentHeader;
3299
3419
  const text = blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
3300
3420
  const toolCalls = blocks.filter((b) => b.type === "tool").map((b) => ({ id: b.id, name: b.name, input: b.input }));
3301
3421
  const thinking = blocks.filter(
3302
- (b) => b.type === "thinking"
3303
- ).map((b) => ({ thinking: b.thinking, signature: b.signature }));
3422
+ (b) => b.type === "thinking" || b.type === "redacted_thinking"
3423
+ ).map(
3424
+ (b) => b.type === "thinking" ? {
3425
+ type: "thinking",
3426
+ thinking: b.thinking,
3427
+ signature: b.signature
3428
+ } : { type: "redacted_thinking", data: b.data }
3429
+ );
3304
3430
  const cleaned2 = {
3305
3431
  role: msg.role,
3306
3432
  content: text
@@ -3395,10 +3521,13 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
3395
3521
  if (signal?.aborted) {
3396
3522
  return abortResult([]);
3397
3523
  }
3524
+ const iterStart = Date.now();
3398
3525
  const contentBlocks = [];
3399
3526
  let thinkingStartedAt = 0;
3527
+ let lastThinkingRelatedStartedAt;
3400
3528
  let stopReason = "end_turn";
3401
3529
  let currentToolNames = "";
3530
+ let lastUsage;
3402
3531
  const statusWatcher = startStatusWatcher({
3403
3532
  apiConfig,
3404
3533
  getContext: () => {
@@ -3437,7 +3566,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
3437
3566
  {
3438
3567
  onRetry: (attempt) => emit({
3439
3568
  type: "status",
3440
- message: `Lost connection, retrying (attempt ${attempt + 2} of 3)...`
3569
+ message: `Lost connection, retrying (attempt ${attempt + 2} of 3)`
3441
3570
  })
3442
3571
  }
3443
3572
  )) {
@@ -3473,8 +3602,20 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
3473
3602
  startedAt: thinkingStartedAt,
3474
3603
  completedAt: event.ts
3475
3604
  });
3605
+ lastThinkingRelatedStartedAt = thinkingStartedAt;
3476
3606
  thinkingStartedAt = 0;
3477
3607
  break;
3608
+ case "redacted_thinking_complete": {
3609
+ const startedAt = lastThinkingRelatedStartedAt !== void 0 ? lastThinkingRelatedStartedAt + 1 : event.ts;
3610
+ contentBlocks.push({
3611
+ type: "redacted_thinking",
3612
+ data: event.data,
3613
+ startedAt,
3614
+ completedAt: event.ts
3615
+ });
3616
+ lastThinkingRelatedStartedAt = startedAt;
3617
+ break;
3618
+ }
3478
3619
  case "tool_use":
3479
3620
  contentBlocks.push({
3480
3621
  type: "tool",
@@ -3492,6 +3633,30 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
3492
3633
  break;
3493
3634
  case "done":
3494
3635
  stopReason = event.stopReason;
3636
+ lastUsage = {
3637
+ inputTokens: event.usage.inputTokens,
3638
+ outputTokens: event.usage.outputTokens,
3639
+ cacheCreationTokens: event.usage.cacheCreationTokens,
3640
+ cacheReadTokens: event.usage.cacheReadTokens,
3641
+ llmCalls: 1
3642
+ };
3643
+ recordUsage({
3644
+ ts: Date.now(),
3645
+ requestId,
3646
+ agentName: subAgentId || "sub-agent",
3647
+ parentToolId,
3648
+ modelId: event.modelId,
3649
+ inputTokens: event.usage.inputTokens,
3650
+ outputTokens: event.usage.outputTokens,
3651
+ cacheCreationTokens: event.usage.cacheCreationTokens,
3652
+ cacheReadTokens: event.usage.cacheReadTokens,
3653
+ cost: nanoToDollars(event.cost),
3654
+ billingEvents: event.billingEvents,
3655
+ durationMs: Date.now() - iterStart,
3656
+ toolNames: contentBlocks.filter(
3657
+ (b) => b.type === "tool"
3658
+ ).map((b) => b.name)
3659
+ });
3495
3660
  break;
3496
3661
  case "error":
3497
3662
  return {
@@ -3511,7 +3676,8 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
3511
3676
  }
3512
3677
  messages.push({
3513
3678
  role: "assistant",
3514
- content: contentBlocks
3679
+ content: contentBlocks,
3680
+ ...lastUsage ? { usage: lastUsage } : {}
3515
3681
  });
3516
3682
  const toolCalls = contentBlocks.filter(
3517
3683
  (b) => b.type === "tool"
@@ -3708,6 +3874,7 @@ var init_runner = __esm({
3708
3874
  "use strict";
3709
3875
  init_api();
3710
3876
  init_logger();
3877
+ init_usageLedger();
3711
3878
  init_statusWatcher();
3712
3879
  init_cleanMessages();
3713
3880
  log5 = createLogger("sub-agent");
@@ -3854,10 +4021,10 @@ var init_tools = __esm({
3854
4021
  });
3855
4022
 
3856
4023
  // src/subagents/browserAutomation/prompt.ts
3857
- import fs14 from "fs";
4024
+ import fs15 from "fs";
3858
4025
  function getBrowserAutomationPrompt() {
3859
4026
  try {
3860
- const appSpec = fs14.readFileSync("src/app.md", "utf-8").trim();
4027
+ const appSpec = fs15.readFileSync("src/app.md", "utf-8").trim();
3861
4028
  return `${BASE_PROMPT}
3862
4029
 
3863
4030
  <!-- cache_breakpoint -->
@@ -3889,7 +4056,7 @@ var init_browserAutomation = __esm({
3889
4056
  init_sidecar();
3890
4057
  init_browserLock();
3891
4058
  init_screenshot();
3892
- init_runCli();
4059
+ init_runMindstudioCli();
3893
4060
  init_analyzeImage();
3894
4061
  init_logger();
3895
4062
  log6 = createLogger("browser-automation");
@@ -3977,10 +4144,9 @@ var init_browserAutomation = __esm({
3977
4144
  visionModelOverride: VISION_MODEL_OVERRIDE
3978
4145
  }
3979
4146
  }));
3980
- const batchResult = await runCli(
3981
- "mindstudio",
3982
- ["batch", "--no-meta", JSON.stringify(batchInput)],
3983
- { timeout: 2e5 }
4147
+ const batchResult = await runMindstudioCli(
4148
+ ["batch", JSON.stringify(batchInput)],
4149
+ { timeout: 2e5, caller: "browserAutomation" }
3984
4150
  );
3985
4151
  try {
3986
4152
  const analyses = JSON.parse(batchResult);
@@ -4205,26 +4371,16 @@ __export(searchGoogle_exports, {
4205
4371
  execute: () => execute
4206
4372
  });
4207
4373
  async function execute(input, onLog) {
4208
- return runCli(
4209
- "mindstudio",
4210
- [
4211
- "search-google",
4212
- "--query",
4213
- input.query,
4214
- "--export-type",
4215
- "json",
4216
- "--output-key",
4217
- "results",
4218
- "--no-meta"
4219
- ],
4220
- { onLog }
4374
+ return runMindstudioCli(
4375
+ ["search-google", "--query", input.query, "--export-type", "json"],
4376
+ { outputKey: "results", onLog, caller: "designExpert" }
4221
4377
  );
4222
4378
  }
4223
4379
  var definition;
4224
4380
  var init_searchGoogle2 = __esm({
4225
4381
  "src/subagents/designExpert/tools/searchGoogle.ts"() {
4226
4382
  "use strict";
4227
- init_runCli();
4383
+ init_runMindstudioCli();
4228
4384
  definition = {
4229
4385
  clearable: false,
4230
4386
  name: "searchGoogle",
@@ -4254,24 +4410,22 @@ async function execute2(input, onLog) {
4254
4410
  if (input.screenshot) {
4255
4411
  pageOptions.screenshot = true;
4256
4412
  }
4257
- return runCli(
4258
- "mindstudio",
4413
+ return runMindstudioCli(
4259
4414
  [
4260
4415
  "scrape-url",
4261
4416
  "--url",
4262
4417
  input.url,
4263
4418
  "--page-options",
4264
- JSON.stringify(pageOptions),
4265
- "--no-meta"
4419
+ JSON.stringify(pageOptions)
4266
4420
  ],
4267
- { onLog }
4421
+ { onLog, caller: "designExpert" }
4268
4422
  );
4269
4423
  }
4270
4424
  var definition2;
4271
4425
  var init_scrapeWebUrl = __esm({
4272
4426
  "src/subagents/designExpert/tools/scrapeWebUrl.ts"() {
4273
4427
  "use strict";
4274
- init_runCli();
4428
+ init_runMindstudioCli();
4275
4429
  definition2 = {
4276
4430
  clearable: false,
4277
4431
  name: "scrapeWebUrl",
@@ -4302,8 +4456,7 @@ async function execute3(input, onLog) {
4302
4456
  const isImageUrl = /\.(png|jpe?g|webp|gif|svg|avif)(\?|$)/i.test(url);
4303
4457
  let imageUrl = url;
4304
4458
  if (!isImageUrl) {
4305
- const ssUrl = await runCli(
4306
- "mindstudio",
4459
+ const ssUrl = await runMindstudioCli(
4307
4460
  [
4308
4461
  "screenshot-url",
4309
4462
  "--url",
@@ -4313,12 +4466,14 @@ async function execute3(input, onLog) {
4313
4466
  "--width",
4314
4467
  "1440",
4315
4468
  "--delay",
4316
- "2000",
4317
- "--output-key",
4318
- "screenshotUrl",
4319
- "--no-meta"
4469
+ "2000"
4320
4470
  ],
4321
- { timeout: 12e4, onLog }
4471
+ {
4472
+ outputKey: "screenshotUrl",
4473
+ timeout: 12e4,
4474
+ onLog,
4475
+ caller: "designExpert"
4476
+ }
4322
4477
  );
4323
4478
  if (ssUrl.startsWith("Error")) {
4324
4479
  return `Could not screenshot ${url}: ${ssUrl}`;
@@ -4336,7 +4491,7 @@ var DESIGN_REFERENCE_PROMPT, definition3;
4336
4491
  var init_analyzeDesign = __esm({
4337
4492
  "src/subagents/designExpert/tools/analyzeDesign.ts"() {
4338
4493
  "use strict";
4339
- init_runCli();
4494
+ init_runMindstudioCli();
4340
4495
  init_analyzeImage();
4341
4496
  DESIGN_REFERENCE_PROMPT = `
4342
4497
  You are analyzing a screenshot of a real website or app for a designer's personal technique/inspiration reference notes.
@@ -4528,19 +4683,15 @@ ${context}
4528
4683
  <brief>
4529
4684
  ${brief}
4530
4685
  </brief>`;
4531
- const enhanced = await runCli(
4532
- "mindstudio",
4686
+ const enhanced = await runMindstudioCli(
4533
4687
  [
4534
4688
  "generate-text",
4535
4689
  "--message",
4536
4690
  message,
4537
4691
  "--model-override",
4538
- JSON.stringify(MODEL_OVERRIDE),
4539
- "--output-key",
4540
- "content",
4541
- "--no-meta"
4692
+ JSON.stringify(MODEL_OVERRIDE)
4542
4693
  ],
4543
- { timeout: 6e4, onLog }
4694
+ { outputKey: "content", timeout: 6e4, onLog, caller: "designExpert" }
4544
4695
  );
4545
4696
  return enhanced.trim();
4546
4697
  }
@@ -4548,7 +4699,7 @@ var ENHANCE_MODEL, MODEL_OVERRIDE, SYSTEM_PROMPT;
4548
4699
  var init_enhancePrompt = __esm({
4549
4700
  "src/subagents/designExpert/tools/images/enhancePrompt.ts"() {
4550
4701
  "use strict";
4551
- init_runCli();
4702
+ init_runMindstudioCli();
4552
4703
  init_assets();
4553
4704
  ENHANCE_MODEL = "claude-4-6-sonnet";
4554
4705
  MODEL_OVERRIDE = {
@@ -4591,11 +4742,14 @@ async function generateImageAssets(opts) {
4591
4742
  config
4592
4743
  }
4593
4744
  });
4594
- const url = await runCli(
4595
- "mindstudio",
4596
- ["generate-image", "--output-key", "imageUrl", "--no-meta"],
4597
- { jsonLogs: true, timeout: 2e5, onLog, stdin: step }
4598
- );
4745
+ const url = await runMindstudioCli(["generate-image"], {
4746
+ outputKey: "imageUrl",
4747
+ jsonLogs: true,
4748
+ timeout: 2e5,
4749
+ onLog,
4750
+ stdin: step,
4751
+ caller: "designExpert"
4752
+ });
4599
4753
  imageUrls = [url];
4600
4754
  } else {
4601
4755
  const steps = enhancedPrompts.map((prompt) => ({
@@ -4608,11 +4762,12 @@ async function generateImageAssets(opts) {
4608
4762
  }
4609
4763
  }
4610
4764
  }));
4611
- const batchResult = await runCli("mindstudio", ["batch", "--no-meta"], {
4765
+ const batchResult = await runMindstudioCli(["batch"], {
4612
4766
  jsonLogs: true,
4613
4767
  timeout: 2e5,
4614
4768
  onLog,
4615
- stdin: JSON.stringify(steps)
4769
+ stdin: JSON.stringify(steps),
4770
+ caller: "designExpert"
4616
4771
  });
4617
4772
  try {
4618
4773
  const parsed = JSON.parse(batchResult);
@@ -4629,17 +4784,14 @@ async function generateImageAssets(opts) {
4629
4784
  if (url.startsWith("Error")) {
4630
4785
  return url;
4631
4786
  }
4632
- const result = await runCli(
4633
- "mindstudio",
4634
- [
4635
- "remove-background-from-image",
4636
- "--image-url",
4637
- url,
4638
- "--output-key",
4639
- "imageUrl",
4640
- "--no-meta"
4641
- ],
4642
- { timeout: 2e5, onLog }
4787
+ const result = await runMindstudioCli(
4788
+ ["remove-background-from-image", "--image-url", url],
4789
+ {
4790
+ outputKey: "imageUrl",
4791
+ timeout: 2e5,
4792
+ onLog,
4793
+ caller: "designExpert"
4794
+ }
4643
4795
  );
4644
4796
  return result.startsWith("Error") ? url : result;
4645
4797
  })
@@ -4675,7 +4827,7 @@ var ANALYZE_PROMPT;
4675
4827
  var init_imageGenerator = __esm({
4676
4828
  "src/subagents/designExpert/tools/images/imageGenerator.ts"() {
4677
4829
  "use strict";
4678
- init_runCli();
4830
+ init_runMindstudioCli();
4679
4831
  init_analyzeImage();
4680
4832
  init_enhancePrompt();
4681
4833
  ANALYZE_PROMPT = 'You are reviewing this image for a visual designer sourcing assets for a project. Describe: what the image depicts, the mood and color palette, how the lighting and composition work, any text present in the image, whether there are any issues (artifacts, distortions), and how it could be used in a layout for an app or website. Be concise and practical. Respond only with your analysis as Markdown (starting with the title "Asset Review") and absolutely no other text. Do not use emojis - use unicode if you need symbols.';
@@ -4835,12 +4987,12 @@ var init_tools3 = __esm({
4835
4987
  });
4836
4988
 
4837
4989
  // src/subagents/common/context.ts
4838
- import fs15 from "fs";
4990
+ import fs16 from "fs";
4839
4991
  import path7 from "path";
4840
4992
  function walkMdFiles2(dir, skip) {
4841
4993
  const files = [];
4842
4994
  try {
4843
- for (const entry of fs15.readdirSync(dir, { withFileTypes: true })) {
4995
+ for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
4844
4996
  const full = path7.join(dir, entry.name);
4845
4997
  if (entry.isDirectory()) {
4846
4998
  if (!skip?.has(entry.name)) {
@@ -4856,7 +5008,7 @@ function walkMdFiles2(dir, skip) {
4856
5008
  }
4857
5009
  function parseFrontmatter2(filePath) {
4858
5010
  try {
4859
- const content = fs15.readFileSync(filePath, "utf-8");
5011
+ const content = fs16.readFileSync(filePath, "utf-8");
4860
5012
  const match = content.match(/^---\n([\s\S]*?)\n---/);
4861
5013
  if (!match) {
4862
5014
  return {};
@@ -4902,7 +5054,7 @@ function loadRoadmapIndex() {
4902
5054
  const parts = [];
4903
5055
  try {
4904
5056
  const indexJson = JSON.parse(
4905
- fs15.readFileSync("src/roadmap/index.json", "utf-8")
5057
+ fs16.readFileSync("src/roadmap/index.json", "utf-8")
4906
5058
  );
4907
5059
  if (indexJson.lanes?.length > 0) {
4908
5060
  const laneLines = indexJson.lanes.map(
@@ -5010,7 +5162,7 @@ var init_context = __esm({
5010
5162
  });
5011
5163
 
5012
5164
  // src/subagents/designExpert/data/sampleCache.ts
5013
- import fs16 from "fs";
5165
+ import fs17 from "fs";
5014
5166
  function generateIndices(poolSize, sampleSize) {
5015
5167
  const n = Math.min(sampleSize, poolSize);
5016
5168
  const indices = Array.from({ length: poolSize }, (_, i) => i);
@@ -5022,14 +5174,14 @@ function generateIndices(poolSize, sampleSize) {
5022
5174
  }
5023
5175
  function load() {
5024
5176
  try {
5025
- return JSON.parse(fs16.readFileSync(SAMPLE_FILE, "utf-8"));
5177
+ return JSON.parse(fs17.readFileSync(SAMPLE_FILE, "utf-8"));
5026
5178
  } catch {
5027
5179
  return null;
5028
5180
  }
5029
5181
  }
5030
5182
  function save(indices) {
5031
5183
  try {
5032
- fs16.writeFileSync(SAMPLE_FILE, JSON.stringify(indices));
5184
+ fs17.writeFileSync(SAMPLE_FILE, JSON.stringify(indices));
5033
5185
  } catch {
5034
5186
  }
5035
5187
  }
@@ -5426,7 +5578,7 @@ var init_tools4 = __esm({
5426
5578
  });
5427
5579
 
5428
5580
  // src/subagents/productVision/executor.ts
5429
- import fs17 from "fs";
5581
+ import fs18 from "fs";
5430
5582
  import path8 from "path";
5431
5583
  function resolve(filePath) {
5432
5584
  return path8.join(ROADMAP_DIR, filePath);
@@ -5436,13 +5588,13 @@ async function executeVisionTool(name, input, context) {
5436
5588
  case "writeFile": {
5437
5589
  const filePath = resolve(input.path);
5438
5590
  try {
5439
- fs17.mkdirSync(ROADMAP_DIR, { recursive: true });
5591
+ fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
5440
5592
  let oldContent = null;
5441
5593
  try {
5442
- oldContent = fs17.readFileSync(filePath, "utf-8");
5594
+ oldContent = fs18.readFileSync(filePath, "utf-8");
5443
5595
  } catch {
5444
5596
  }
5445
- fs17.writeFileSync(filePath, input.content, "utf-8");
5597
+ fs18.writeFileSync(filePath, input.content, "utf-8");
5446
5598
  const lineCount = input.content.split("\n").length;
5447
5599
  const label = oldContent !== null ? "Wrote" : "Created";
5448
5600
  return `${label} ${filePath} (${lineCount} lines)
@@ -5454,11 +5606,11 @@ ${unifiedDiff(filePath, oldContent ?? "", input.content)}`;
5454
5606
  case "deleteFile": {
5455
5607
  const filePath = resolve(input.path);
5456
5608
  try {
5457
- if (!fs17.existsSync(filePath)) {
5609
+ if (!fs18.existsSync(filePath)) {
5458
5610
  return `Error: ${filePath} does not exist`;
5459
5611
  }
5460
- const oldContent = fs17.readFileSync(filePath, "utf-8");
5461
- fs17.unlinkSync(filePath);
5612
+ const oldContent = fs18.readFileSync(filePath, "utf-8");
5613
+ fs18.unlinkSync(filePath);
5462
5614
  return `Deleted ${filePath}
5463
5615
  ${unifiedDiff(filePath, oldContent, "")}`;
5464
5616
  } catch (err) {
@@ -5471,8 +5623,8 @@ ${unifiedDiff(filePath, oldContent, "")}`;
5471
5623
  }
5472
5624
  const filePath = resolve("pitch.html");
5473
5625
  try {
5474
- fs17.mkdirSync(ROADMAP_DIR, { recursive: true });
5475
- const existing = fs17.existsSync(filePath) ? fs17.readFileSync(filePath, "utf-8").trim() : "";
5626
+ fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
5627
+ const existing = fs18.existsSync(filePath) ? fs18.readFileSync(filePath, "utf-8").trim() : "";
5476
5628
  const currentDeck = existing || PITCH_DECK_SHELL;
5477
5629
  const task = `
5478
5630
  <pitch_content>${input.task}</pitch_content>
@@ -5497,7 +5649,7 @@ Respond only with the complete HTML file and absolutely no other text. Your resp
5497
5649
  /```(?:html|wireframe)\n([\s\S]*?)```/
5498
5650
  );
5499
5651
  const html = htmlMatch ? htmlMatch[1].trim() : result;
5500
- fs17.writeFileSync(filePath, html, "utf-8");
5652
+ fs18.writeFileSync(filePath, html, "utf-8");
5501
5653
  return `Pitch deck written successfully.`;
5502
5654
  } catch (err) {
5503
5655
  return `Error generating pitch deck: ${err.message}`;
@@ -5746,7 +5898,7 @@ var scrapeWebUrlTool;
5746
5898
  var init_scrapeWebUrl2 = __esm({
5747
5899
  "src/tools/common/scrapeWebUrl.ts"() {
5748
5900
  "use strict";
5749
- init_runCli();
5901
+ init_runMindstudioCli();
5750
5902
  scrapeWebUrlTool = {
5751
5903
  clearable: false,
5752
5904
  definition: {
@@ -5774,15 +5926,13 @@ var init_scrapeWebUrl2 = __esm({
5774
5926
  if (screenshot) {
5775
5927
  pageOptions.screenshot = true;
5776
5928
  }
5777
- return runCli(
5778
- "mindstudio",
5929
+ return runMindstudioCli(
5779
5930
  [
5780
5931
  "scrape-url",
5781
5932
  "--url",
5782
5933
  url,
5783
5934
  "--page-options",
5784
- JSON.stringify(pageOptions),
5785
- "--no-meta"
5935
+ JSON.stringify(pageOptions)
5786
5936
  ],
5787
5937
  { onLog: context?.onLog }
5788
5938
  );
@@ -5892,10 +6042,11 @@ var init_tools6 = __esm({
5892
6042
  });
5893
6043
 
5894
6044
  // src/session.ts
5895
- import fs18 from "fs";
6045
+ import fs19 from "fs";
6046
+ import path9 from "path";
5896
6047
  function loadSession(state) {
5897
6048
  try {
5898
- const raw = fs18.readFileSync(SESSION_FILE, "utf-8");
6049
+ const raw = fs19.readFileSync(SESSION_FILE, "utf-8");
5899
6050
  const data = JSON.parse(raw);
5900
6051
  if (Array.isArray(data.messages) && data.messages.length > 0) {
5901
6052
  state.messages = sanitizeMessages(data.messages);
@@ -5944,7 +6095,7 @@ function sanitizeMessages(messages) {
5944
6095
  }
5945
6096
  function saveSession(state) {
5946
6097
  try {
5947
- fs18.writeFileSync(
6098
+ fs19.writeFileSync(
5948
6099
  SESSION_FILE,
5949
6100
  JSON.stringify({ messages: state.messages }, null, 2),
5950
6101
  "utf-8"
@@ -5957,17 +6108,31 @@ function saveSession(state) {
5957
6108
  function clearSession(state) {
5958
6109
  state.messages = [];
5959
6110
  try {
5960
- fs18.unlinkSync(SESSION_FILE);
5961
- } catch {
6111
+ if (fs19.existsSync(SESSION_FILE)) {
6112
+ fs19.mkdirSync(ARCHIVE_DIR, { recursive: true });
6113
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
6114
+ const dest = path9.join(ARCHIVE_DIR, `cleared-${ts}.json`);
6115
+ fs19.renameSync(SESSION_FILE, dest);
6116
+ log7.info("Session archived on clear", { dest });
6117
+ }
6118
+ } catch (err) {
6119
+ log7.warn("Session archive on clear failed, deleting instead", {
6120
+ error: err.message
6121
+ });
6122
+ try {
6123
+ fs19.unlinkSync(SESSION_FILE);
6124
+ } catch {
6125
+ }
5962
6126
  }
5963
6127
  }
5964
- var log7, SESSION_FILE;
6128
+ var log7, SESSION_FILE, ARCHIVE_DIR;
5965
6129
  var init_session = __esm({
5966
6130
  "src/session.ts"() {
5967
6131
  "use strict";
5968
6132
  init_logger();
5969
6133
  log7 = createLogger("session");
5970
6134
  SESSION_FILE = ".remy-session.json";
6135
+ ARCHIVE_DIR = ".logs/sessions";
5971
6136
  }
5972
6137
  });
5973
6138
 
@@ -6177,8 +6342,8 @@ var init_errors = __esm({
6177
6342
  });
6178
6343
 
6179
6344
  // src/brandExtraction/index.ts
6180
- import fs19 from "fs";
6181
- import path9 from "path";
6345
+ import fs20 from "fs";
6346
+ import path10 from "path";
6182
6347
  import { createHash } from "crypto";
6183
6348
  async function runExtraction(apiConfig) {
6184
6349
  const inputHash = computeInputHash();
@@ -6200,7 +6365,7 @@ async function runExtraction(apiConfig) {
6200
6365
  function computeInputHash() {
6201
6366
  const entries = [];
6202
6367
  for (const filePath of walkMdFiles3("src")) {
6203
- if (filePath === path9.join("src", "app.md")) {
6368
+ if (filePath === path10.join("src", "app.md")) {
6204
6369
  entries.push({ path: filePath, content: readSafe(filePath) });
6205
6370
  continue;
6206
6371
  }
@@ -6222,7 +6387,7 @@ function sha256(input) {
6222
6387
  }
6223
6388
  function readSafe(filePath) {
6224
6389
  try {
6225
- return fs19.readFileSync(filePath, "utf-8");
6390
+ return fs20.readFileSync(filePath, "utf-8");
6226
6391
  } catch {
6227
6392
  return "";
6228
6393
  }
@@ -6230,9 +6395,9 @@ function readSafe(filePath) {
6230
6395
  function walkMdFiles3(dir) {
6231
6396
  const results = [];
6232
6397
  try {
6233
- const entries = fs19.readdirSync(dir, { withFileTypes: true });
6398
+ const entries = fs20.readdirSync(dir, { withFileTypes: true });
6234
6399
  for (const entry of entries) {
6235
- const full = path9.join(dir, entry.name);
6400
+ const full = path10.join(dir, entry.name);
6236
6401
  if (entry.isDirectory()) {
6237
6402
  results.push(...walkMdFiles3(full));
6238
6403
  } else if (entry.name.endsWith(".md")) {
@@ -6245,7 +6410,7 @@ function walkMdFiles3(dir) {
6245
6410
  }
6246
6411
  function parseFrontmatter3(filePath) {
6247
6412
  try {
6248
- const content = fs19.readFileSync(filePath, "utf-8");
6413
+ const content = fs20.readFileSync(filePath, "utf-8");
6249
6414
  const match = content.match(/^---\n([\s\S]*?)\n---/);
6250
6415
  if (!match) {
6251
6416
  return { type: "" };
@@ -6264,6 +6429,7 @@ async function extractBrand(apiConfig) {
6264
6429
  return { version: 1 };
6265
6430
  }
6266
6431
  let responseText = "";
6432
+ const iterStart = Date.now();
6267
6433
  try {
6268
6434
  for await (const event of streamChat({
6269
6435
  ...apiConfig,
@@ -6274,6 +6440,20 @@ async function extractBrand(apiConfig) {
6274
6440
  })) {
6275
6441
  if (event.type === "text") {
6276
6442
  responseText += event.text;
6443
+ } else if (event.type === "done") {
6444
+ recordUsage({
6445
+ ts: Date.now(),
6446
+ agentName: "brandExtractor",
6447
+ modelId: event.modelId,
6448
+ inputTokens: event.usage.inputTokens,
6449
+ outputTokens: event.usage.outputTokens,
6450
+ cacheCreationTokens: event.usage.cacheCreationTokens,
6451
+ cacheReadTokens: event.usage.cacheReadTokens,
6452
+ cost: nanoToDollars(event.cost),
6453
+ billingEvents: event.billingEvents,
6454
+ durationMs: Date.now() - iterStart,
6455
+ toolNames: []
6456
+ });
6277
6457
  } else if (event.type === "error") {
6278
6458
  log8.error("Brand extraction stream error", { error: event.error });
6279
6459
  return null;
@@ -6408,14 +6588,14 @@ function pickFont(raw) {
6408
6588
  }
6409
6589
  function persistBrand(brand, inputHash) {
6410
6590
  const tmp = `${BRAND_FILE}.tmp`;
6411
- fs19.writeFileSync(tmp, JSON.stringify(brand, null, 2), "utf-8");
6412
- fs19.renameSync(tmp, BRAND_FILE);
6591
+ fs20.writeFileSync(tmp, JSON.stringify(brand, null, 2), "utf-8");
6592
+ fs20.renameSync(tmp, BRAND_FILE);
6413
6593
  const cache = { inputHash, generatedAt: Date.now() };
6414
- fs19.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
6594
+ fs20.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
6415
6595
  }
6416
6596
  function readCache() {
6417
6597
  try {
6418
- const raw = fs19.readFileSync(CACHE_FILE, "utf-8");
6598
+ const raw = fs20.readFileSync(CACHE_FILE, "utf-8");
6419
6599
  const parsed = JSON.parse(raw);
6420
6600
  if (parsed && typeof parsed.inputHash === "string" && typeof parsed.generatedAt === "number") {
6421
6601
  return parsed;
@@ -6432,6 +6612,7 @@ var init_brandExtraction = __esm({
6432
6612
  init_api();
6433
6613
  init_assets();
6434
6614
  init_logger();
6615
+ init_usageLedger();
6435
6616
  log8 = createLogger("brandExtraction");
6436
6617
  EXTRACT_PROMPT = readAsset("brandExtraction", "extract.md");
6437
6618
  BRAND_FILE = ".remy-brand.json";
@@ -6563,9 +6744,11 @@ async function runTurn(params) {
6563
6744
  saveSession(state);
6564
6745
  return;
6565
6746
  }
6747
+ const iterStart = Date.now();
6566
6748
  const contentBlocks = [];
6567
6749
  const thinkingBlockStartTimes = [];
6568
6750
  let thinkingCompleteCount = 0;
6751
+ let lastThinkingRelatedStartedAt;
6569
6752
  let textBlockOpen = false;
6570
6753
  const toolInputAccumulators = /* @__PURE__ */ new Map();
6571
6754
  let stopReason = "end_turn";
@@ -6666,7 +6849,7 @@ async function runTurn(params) {
6666
6849
  onRetry: (attempt) => {
6667
6850
  onEvent({
6668
6851
  type: "status",
6669
- message: `Lost connection, retrying (attempt ${attempt + 2} of 3)...`
6852
+ message: `Lost connection, retrying (attempt ${attempt + 2} of 3)`
6670
6853
  });
6671
6854
  }
6672
6855
  }
@@ -6697,16 +6880,30 @@ async function runTurn(params) {
6697
6880
  }
6698
6881
  onEvent({ type: "thinking", text: event.text });
6699
6882
  break;
6700
- case "thinking_complete":
6883
+ case "thinking_complete": {
6884
+ const startedAt = thinkingBlockStartTimes[thinkingCompleteCount] ?? event.ts;
6701
6885
  contentBlocks.push({
6702
6886
  type: "thinking",
6703
6887
  thinking: event.thinking,
6704
6888
  signature: event.signature,
6705
- startedAt: thinkingBlockStartTimes[thinkingCompleteCount] ?? event.ts,
6889
+ startedAt,
6706
6890
  completedAt: event.ts
6707
6891
  });
6708
6892
  thinkingCompleteCount++;
6893
+ lastThinkingRelatedStartedAt = startedAt;
6894
+ break;
6895
+ }
6896
+ case "redacted_thinking_complete": {
6897
+ const startedAt = lastThinkingRelatedStartedAt !== void 0 ? lastThinkingRelatedStartedAt + 1 : event.ts;
6898
+ contentBlocks.push({
6899
+ type: "redacted_thinking",
6900
+ data: event.data,
6901
+ startedAt,
6902
+ completedAt: event.ts
6903
+ });
6904
+ lastThinkingRelatedStartedAt = startedAt;
6709
6905
  break;
6906
+ }
6710
6907
  case "tool_input_delta": {
6711
6908
  const acc = getOrCreateAccumulator2(event.id, event.name);
6712
6909
  acc.json += event.delta;
@@ -6760,6 +6957,22 @@ async function runTurn(params) {
6760
6957
  turnOutputTokens += event.usage.outputTokens;
6761
6958
  turnCacheCreation += lastCallCacheCreation;
6762
6959
  turnCacheRead += lastCallCacheRead;
6960
+ recordUsage({
6961
+ ts: Date.now(),
6962
+ requestId,
6963
+ agentName: "parent",
6964
+ modelId: event.modelId,
6965
+ inputTokens: event.usage.inputTokens,
6966
+ outputTokens: event.usage.outputTokens,
6967
+ cacheCreationTokens: event.usage.cacheCreationTokens,
6968
+ cacheReadTokens: event.usage.cacheReadTokens,
6969
+ cost: nanoToDollars(event.cost),
6970
+ billingEvents: event.billingEvents,
6971
+ durationMs: Date.now() - iterStart,
6972
+ toolNames: contentBlocks.filter(
6973
+ (b) => b.type === "tool"
6974
+ ).map((b) => b.name)
6975
+ });
6763
6976
  break;
6764
6977
  case "error":
6765
6978
  onEvent({ type: "error", error: friendlyError(event.error) });
@@ -7006,6 +7219,7 @@ var init_agent = __esm({
7006
7219
  init_tools6();
7007
7220
  init_session();
7008
7221
  init_logger();
7222
+ init_usageLedger();
7009
7223
  init_parsePartialJson();
7010
7224
  init_statusWatcher();
7011
7225
  init_errors();
@@ -7035,12 +7249,12 @@ var init_agent = __esm({
7035
7249
  });
7036
7250
 
7037
7251
  // src/config.ts
7038
- import fs20 from "fs";
7039
- import path10 from "path";
7252
+ import fs21 from "fs";
7253
+ import path11 from "path";
7040
7254
  import os from "os";
7041
7255
  function loadConfigFile() {
7042
7256
  try {
7043
- const raw = fs20.readFileSync(CONFIG_PATH, "utf-8");
7257
+ const raw = fs21.readFileSync(CONFIG_PATH, "utf-8");
7044
7258
  log11.debug("Loaded config file", { path: CONFIG_PATH });
7045
7259
  return JSON.parse(raw);
7046
7260
  } catch (err) {
@@ -7057,6 +7271,7 @@ function resolveConfig(flags2) {
7057
7271
  const env = file.environments?.[activeEnv];
7058
7272
  const apiKey = flags2?.apiKey || process.env.MINDSTUDIO_API_KEY || env?.apiKey || "";
7059
7273
  const baseUrl2 = flags2?.baseUrl || process.env.MINDSTUDIO_BASE_URL || env?.apiBaseUrl || DEFAULT_BASE_URL;
7274
+ const appId = process.env.MINDSTUDIO_APP_ID || void 0;
7060
7275
  if (!apiKey) {
7061
7276
  log11.error("No API key found");
7062
7277
  throw new Error(
@@ -7067,9 +7282,10 @@ function resolveConfig(flags2) {
7067
7282
  log11.info("Config resolved", {
7068
7283
  baseUrl: baseUrl2,
7069
7284
  keySource,
7070
- environment: activeEnv
7285
+ environment: activeEnv,
7286
+ appId
7071
7287
  });
7072
- return { apiKey, baseUrl: baseUrl2 };
7288
+ return { apiKey, baseUrl: baseUrl2, appId };
7073
7289
  }
7074
7290
  var log11, CONFIG_PATH, DEFAULT_BASE_URL;
7075
7291
  var init_config = __esm({
@@ -7077,7 +7293,7 @@ var init_config = __esm({
7077
7293
  "use strict";
7078
7294
  init_logger();
7079
7295
  log11 = createLogger("config");
7080
- CONFIG_PATH = path10.join(
7296
+ CONFIG_PATH = path11.join(
7081
7297
  os.homedir(),
7082
7298
  ".mindstudio-local-tunnel",
7083
7299
  "config.json"
@@ -8323,8 +8539,8 @@ var init_headless = __esm({
8323
8539
  // src/index.tsx
8324
8540
  import { render } from "ink";
8325
8541
  import os2 from "os";
8326
- import fs21 from "fs";
8327
- import path11 from "path";
8542
+ import fs22 from "fs";
8543
+ import path12 from "path";
8328
8544
 
8329
8545
  // src/tui/App.tsx
8330
8546
  import { useState as useState2, useCallback, useRef } from "react";
@@ -8642,8 +8858,8 @@ for (let i = 0; i < args.length; i++) {
8642
8858
  var startupLog = createLogger("startup");
8643
8859
  function printDebugInfo(config) {
8644
8860
  const pkg = JSON.parse(
8645
- fs21.readFileSync(
8646
- path11.join(import.meta.dirname, "..", "package.json"),
8861
+ fs22.readFileSync(
8862
+ path12.join(import.meta.dirname, "..", "package.json"),
8647
8863
  "utf-8"
8648
8864
  )
8649
8865
  );