@mindstudio-ai/remy 0.1.341 → 0.1.342

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.
Files changed (4) hide show
  1. package/README.md +14 -26
  2. package/dist/headless.js +179 -197
  3. package/dist/index.js +8672 -10122
  4. package/package.json +2 -7
package/dist/headless.js CHANGED
@@ -4,8 +4,12 @@ var __export = (target, all) => {
4
4
  __defProp(target, name, { get: all[name], enumerable: true });
5
5
  };
6
6
 
7
+ // src/headless/index.ts
8
+ import os2 from "os";
9
+ import fs22 from "fs";
10
+ import path13 from "path";
11
+
7
12
  // src/logger.ts
8
- import fs from "fs";
9
13
  var LEVELS = {
10
14
  error: 0,
11
15
  warn: 1,
@@ -54,7 +58,7 @@ function createLogger(module) {
54
58
  }
55
59
 
56
60
  // src/config.ts
57
- import fs2 from "fs";
61
+ import fs from "fs";
58
62
  import path from "path";
59
63
  import os from "os";
60
64
  var log = createLogger("config");
@@ -66,7 +70,7 @@ var CONFIG_PATH = path.join(
66
70
  var DEFAULT_BASE_URL = "https://api.mindstudio.ai";
67
71
  function loadConfigFile() {
68
72
  try {
69
- const raw = fs2.readFileSync(CONFIG_PATH, "utf-8");
73
+ const raw = fs.readFileSync(CONFIG_PATH, "utf-8");
70
74
  log.debug("Loaded config file", { path: CONFIG_PATH });
71
75
  return JSON.parse(raw);
72
76
  } catch (err) {
@@ -773,7 +777,7 @@ async function initModelRegistry(config) {
773
777
  }
774
778
 
775
779
  // src/assets.ts
776
- import fs3 from "fs";
780
+ import fs2 from "fs";
777
781
  import path2 from "path";
778
782
  var ASSETS_BASE = import.meta.dirname ?? path2.dirname(new URL(import.meta.url).pathname);
779
783
  function assetPath(...segments) {
@@ -782,7 +786,7 @@ function assetPath(...segments) {
782
786
  function readAsset(...segments) {
783
787
  const full = assetPath(...segments);
784
788
  try {
785
- return fs3.readFileSync(full, "utf-8").trim();
789
+ return fs2.readFileSync(full, "utf-8").trim();
786
790
  } catch {
787
791
  throw new Error(`Required asset missing: ${full}`);
788
792
  }
@@ -790,14 +794,14 @@ function readAsset(...segments) {
790
794
  function readJsonAsset(fallback, ...segments) {
791
795
  const full = assetPath(...segments);
792
796
  try {
793
- return JSON.parse(fs3.readFileSync(full, "utf-8"));
797
+ return JSON.parse(fs2.readFileSync(full, "utf-8"));
794
798
  } catch {
795
799
  return fallback;
796
800
  }
797
801
  }
798
802
 
799
803
  // src/prompt/static/projectContext.ts
800
- import fs4 from "fs";
804
+ import fs3 from "fs";
801
805
 
802
806
  // src/projectRoot.ts
803
807
  var PROJECT_ROOT = process.cwd();
@@ -812,7 +816,7 @@ File paths are relative to this directory. Every tool operates here and bash com
812
816
  }
813
817
  function loadAppIdentity() {
814
818
  try {
815
- const manifest = JSON.parse(fs4.readFileSync("mindstudio.json", "utf-8"));
819
+ const manifest = JSON.parse(fs3.readFileSync("mindstudio.json", "utf-8"));
816
820
  const name = typeof manifest.name === "string" ? manifest.name.trim() : "";
817
821
  if (!name) {
818
822
  return "";
@@ -829,7 +833,7 @@ Full manifest: \`mindstudio.json\` (read it when you need the app's structure, t
829
833
  }
830
834
  function loadPlanStatus(onboardingState) {
831
835
  try {
832
- const content = fs4.readFileSync(".remy-plan.md", "utf-8");
836
+ const content = fs3.readFileSync(".remy-plan.md", "utf-8");
833
837
  const match = content.match(/^---\n([\s\S]*?)\n---/);
834
838
  const status = match?.[1]?.match(/^status:\s*(.+)$/m)?.[1]?.trim();
835
839
  if (status === "pending" && onboardingState === "intake") {
@@ -857,7 +861,7 @@ The user has approved your implementation plan in .remy-plan.md. You may referen
857
861
  }
858
862
 
859
863
  // src/skillCatalog.ts
860
- import fs5 from "fs";
864
+ import fs4 from "fs";
861
865
  import path3 from "path";
862
866
  function parseFrontmatter(content) {
863
867
  const match = content.match(/^---\n([\s\S]*?)\n---/);
@@ -876,7 +880,7 @@ function parseFrontmatter(content) {
876
880
  function buildSkillCatalog(opts) {
877
881
  let files;
878
882
  try {
879
- files = fs5.readdirSync(opts.dir).filter((f) => f.endsWith(".md"));
883
+ files = fs4.readdirSync(opts.dir).filter((f) => f.endsWith(".md"));
880
884
  } catch {
881
885
  files = [];
882
886
  }
@@ -884,7 +888,7 @@ function buildSkillCatalog(opts) {
884
888
  for (const file of files.sort()) {
885
889
  const full = path3.join(opts.dir, file);
886
890
  const id = file.replace(/\.md$/, "");
887
- const fields = parseFrontmatter(fs5.readFileSync(full, "utf-8"));
891
+ const fields = parseFrontmatter(fs4.readFileSync(full, "utf-8"));
888
892
  if (!fields.name || !fields.what || !fields.when) {
889
893
  continue;
890
894
  }
@@ -903,7 +907,7 @@ function buildSkillCatalog(opts) {
903
907
  return skills.find((s) => s.id === id);
904
908
  },
905
909
  readBody(skill) {
906
- return fs5.readFileSync(skill.path, "utf-8").replace(/^---[\s\S]*?---\s*/, "").trim();
910
+ return fs4.readFileSync(skill.path, "utf-8").replace(/^---[\s\S]*?---\s*/, "").trim();
907
911
  },
908
912
  renderCatalogBlock() {
909
913
  if (skills.length === 0) {
@@ -1175,7 +1179,7 @@ function mergeBackgroundResultsMessages(messages) {
1175
1179
  }
1176
1180
 
1177
1181
  // src/tools/spec/readSpec.ts
1178
- import fs6 from "fs/promises";
1182
+ import fs5 from "fs/promises";
1179
1183
 
1180
1184
  // src/tools/spec/_helpers.ts
1181
1185
  function validateSpecPath(filePath) {
@@ -1229,7 +1233,7 @@ var readSpecTool = {
1229
1233
  return `Error: ${err.message}`;
1230
1234
  }
1231
1235
  try {
1232
- const content = await fs6.readFile(input.path, "utf-8");
1236
+ const content = await fs5.readFile(input.path, "utf-8");
1233
1237
  const allLines = content.split("\n");
1234
1238
  const totalLines = allLines.length;
1235
1239
  const maxLines = input.maxLines === 0 ? Infinity : input.maxLines || DEFAULT_MAX_LINES;
@@ -1257,7 +1261,7 @@ var readSpecTool = {
1257
1261
  };
1258
1262
 
1259
1263
  // src/tools/spec/writeSpec.ts
1260
- import fs7 from "fs/promises";
1264
+ import fs6 from "fs/promises";
1261
1265
  import path4 from "path";
1262
1266
 
1263
1267
  // src/tools/_helpers/diff.ts
@@ -1333,7 +1337,7 @@ var writeSpecTool = {
1333
1337
  },
1334
1338
  streaming: {
1335
1339
  transform: async (partial) => {
1336
- const oldContent = await fs7.readFile(partial.path, "utf-8").catch(() => "");
1340
+ const oldContent = await fs6.readFile(partial.path, "utf-8").catch(() => "");
1337
1341
  const lineCount = partial.content.split("\n").length;
1338
1342
  return `Writing ${partial.path} (${lineCount} lines)
1339
1343
  ${unifiedDiff(partial.path, oldContent, partial.content)}`;
@@ -1347,13 +1351,13 @@ ${unifiedDiff(partial.path, oldContent, partial.content)}`;
1347
1351
  }
1348
1352
  const release = await acquireFileLock(input.path);
1349
1353
  try {
1350
- await fs7.mkdir(path4.dirname(input.path), { recursive: true });
1354
+ await fs6.mkdir(path4.dirname(input.path), { recursive: true });
1351
1355
  let oldContent = null;
1352
1356
  try {
1353
- oldContent = await fs7.readFile(input.path, "utf-8");
1357
+ oldContent = await fs6.readFile(input.path, "utf-8");
1354
1358
  } catch {
1355
1359
  }
1356
- await fs7.writeFile(input.path, input.content, "utf-8");
1360
+ await fs6.writeFile(input.path, input.content, "utf-8");
1357
1361
  const lineCount = input.content.split("\n").length;
1358
1362
  const label = oldContent !== null ? "Wrote" : "Created";
1359
1363
  return `${label} ${input.path} (${lineCount} lines)
@@ -1367,7 +1371,7 @@ ${unifiedDiff(input.path, oldContent ?? "", input.content)}`;
1367
1371
  };
1368
1372
 
1369
1373
  // src/tools/spec/editSpec.ts
1370
- import fs8 from "fs/promises";
1374
+ import fs7 from "fs/promises";
1371
1375
 
1372
1376
  // src/tools/code/editFile/_helpers.ts
1373
1377
  function buildLineOffsets(content) {
@@ -1486,7 +1490,7 @@ var editSpecTool = {
1486
1490
  try {
1487
1491
  let content;
1488
1492
  try {
1489
- content = await fs8.readFile(input.path, "utf-8");
1493
+ content = await fs7.readFile(input.path, "utf-8");
1490
1494
  } catch (err) {
1491
1495
  return `Error reading file: ${err.message}`;
1492
1496
  }
@@ -1539,7 +1543,7 @@ var editSpecTool = {
1539
1543
  return `Error: that edit would remove or malform the spec's YAML frontmatter (the leading \`--- \u2026 ---\` block, which holds required fields like \`name\`). Narrow old_string to the body content you meant to change and leave the frontmatter block intact.`;
1540
1544
  }
1541
1545
  try {
1542
- await fs8.writeFile(input.path, updated, "utf-8");
1546
+ await fs7.writeFile(input.path, updated, "utf-8");
1543
1547
  } catch (err) {
1544
1548
  return `Error writing file: ${err.message}`;
1545
1549
  }
@@ -1551,7 +1555,7 @@ var editSpecTool = {
1551
1555
  };
1552
1556
 
1553
1557
  // src/tools/spec/listSpecFiles.ts
1554
- import fs9 from "fs/promises";
1558
+ import fs8 from "fs/promises";
1555
1559
  import path5 from "path";
1556
1560
  var listSpecFilesTool = {
1557
1561
  definition: {
@@ -1580,7 +1584,7 @@ var listSpecFilesTool = {
1580
1584
  };
1581
1585
  async function listRecursive(dir) {
1582
1586
  const results = [];
1583
- const entries = await fs9.readdir(dir, { withFileTypes: true });
1587
+ const entries = await fs8.readdir(dir, { withFileTypes: true });
1584
1588
  entries.sort((a, b) => {
1585
1589
  if (a.isDirectory() && !b.isDirectory()) {
1586
1590
  return -1;
@@ -1628,18 +1632,18 @@ var presentPublishPlanTool = {
1628
1632
  };
1629
1633
 
1630
1634
  // src/atomicWrite.ts
1631
- import fs10 from "fs";
1635
+ import fs9 from "fs";
1632
1636
  import fsp from "fs/promises";
1633
1637
  var writeFileAtomicSync = (file, data) => {
1634
1638
  const tmp = `${file}.${process.pid}.tmp`;
1635
- const fd2 = fs10.openSync(tmp, "w");
1639
+ const fd2 = fs9.openSync(tmp, "w");
1636
1640
  try {
1637
- fs10.writeSync(fd2, data);
1638
- fs10.fsyncSync(fd2);
1641
+ fs9.writeSync(fd2, data);
1642
+ fs9.fsyncSync(fd2);
1639
1643
  } finally {
1640
- fs10.closeSync(fd2);
1644
+ fs9.closeSync(fd2);
1641
1645
  }
1642
- fs10.renameSync(tmp, file);
1646
+ fs9.renameSync(tmp, file);
1643
1647
  };
1644
1648
  var writeFileAtomic = async (file, data) => {
1645
1649
  const tmp = `${file}.${process.pid}.tmp`;
@@ -1683,7 +1687,7 @@ ${content}`;
1683
1687
  };
1684
1688
 
1685
1689
  // src/tools/spec/updatePlanStatus.ts
1686
- import fs11 from "fs/promises";
1690
+ import fs10 from "fs/promises";
1687
1691
  var PLAN_FILE2 = ".remy-plan.md";
1688
1692
  var updatePlanStatusTool = {
1689
1693
  definition: {
@@ -1703,17 +1707,17 @@ var updatePlanStatusTool = {
1703
1707
  },
1704
1708
  async execute(input, context) {
1705
1709
  const status = input.status;
1706
- if (context?.onboardingState === "intake") {
1710
+ if (context.onboardingState === "intake") {
1707
1711
  return `The initial plan isn't approved or rejected through this tool \u2014 the user decides via "Start Building". Keep discussing, and revise the plan with writePlan if they want changes.`;
1708
1712
  }
1709
1713
  let content;
1710
1714
  try {
1711
- content = await fs11.readFile(PLAN_FILE2, "utf-8");
1715
+ content = await fs10.readFile(PLAN_FILE2, "utf-8");
1712
1716
  } catch {
1713
1717
  return "No plan file found.";
1714
1718
  }
1715
1719
  if (status === "rejected") {
1716
- await fs11.unlink(PLAN_FILE2).catch(() => {
1720
+ await fs10.unlink(PLAN_FILE2).catch(() => {
1717
1721
  });
1718
1722
  return "Plan rejected and removed.";
1719
1723
  }
@@ -1852,22 +1856,13 @@ var promptUserTool = {
1852
1856
  };
1853
1857
  }
1854
1858
  },
1855
- async execute(input) {
1856
- const questions = input.questions;
1857
- const lines = questions.map((q) => {
1858
- let line = `- ${q.question}`;
1859
- if (q.type === "select" || q.type === "checklist") {
1860
- const opts = (q.options || []).map(
1861
- (o) => typeof o === "string" ? o : o.label
1862
- );
1863
- line += q.type === "checklist" ? ` (pick one or more: ${opts.join(" / ")})` : ` (${opts.join(" / ")})`;
1864
- } else if (q.type === "file") {
1865
- line += " (upload file)";
1866
- }
1867
- return line;
1868
- });
1869
- return `Please answer these questions:
1870
- ${lines.join("\n")}`;
1859
+ // Unreachable: promptUser is in EXTERNAL_TOOLS, so every call is answered by
1860
+ // the sandbox rather than here. Present because the Tool interface requires
1861
+ // it, and identical to the other nine external tools for that reason. It
1862
+ // used to render the questions as prose for a local CLI — see the note on
1863
+ // runTurn for why a plausible-looking local answer was the dangerous part.
1864
+ async execute() {
1865
+ return "ok";
1871
1866
  }
1872
1867
  };
1873
1868
 
@@ -2067,7 +2062,7 @@ var askMindStudioSdkTool = {
2067
2062
  const result = await runCli("mindstudio", ["ask", query], {
2068
2063
  timeout: 48e4,
2069
2064
  maxBuffer: 512 * 1024,
2070
- onLog: context?.onLog
2065
+ onLog: context.onLog
2071
2066
  });
2072
2067
  return formatCliResult(result);
2073
2068
  }
@@ -2122,8 +2117,8 @@ var compactConversationTool = {
2122
2117
  }
2123
2118
  },
2124
2119
  async execute(_input, context) {
2125
- if (!context?.conversationMessages || !context.apiConfig) {
2126
- return "Error: compaction requires execution context.";
2120
+ if (!context.conversationMessages) {
2121
+ return "Error: compaction requires the conversation history.";
2127
2122
  }
2128
2123
  const { toolCallId, onBackgroundComplete, toolRegistry } = context;
2129
2124
  triggerCompaction(
@@ -2197,7 +2192,7 @@ This reference lives at ${skill.path}. Re-read it with readFile if you need it a
2197
2192
  };
2198
2193
 
2199
2194
  // src/tools/code/readFile.ts
2200
- import fs12 from "fs/promises";
2195
+ import fs11 from "fs/promises";
2201
2196
  var DEFAULT_WINDOW = 500;
2202
2197
  var MAX_BYTES = 64 * 1024;
2203
2198
  function isBinary(buffer) {
@@ -2238,7 +2233,7 @@ var readFileTool = {
2238
2233
  },
2239
2234
  async execute(input) {
2240
2235
  try {
2241
- const buffer = await fs12.readFile(input.path);
2236
+ const buffer = await fs11.readFile(input.path);
2242
2237
  if (isBinary(buffer)) {
2243
2238
  const size = buffer.length;
2244
2239
  const unit = size > 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)}MB` : `${(size / 1024).toFixed(1)}KB`;
@@ -2310,7 +2305,7 @@ var readFileTool = {
2310
2305
  };
2311
2306
 
2312
2307
  // src/tools/code/writeFile.ts
2313
- import fs13 from "fs/promises";
2308
+ import fs12 from "fs/promises";
2314
2309
  import path6 from "path";
2315
2310
  var writeFileTool = {
2316
2311
  definition: {
@@ -2347,7 +2342,7 @@ var writeFileTool = {
2347
2342
  lastNewlineCount = newlineCount;
2348
2343
  const lastNewline = partial.content.lastIndexOf("\n");
2349
2344
  const completeContent = partial.content.substring(0, lastNewline + 1);
2350
- const oldContent = await fs13.readFile(partial.path, "utf-8").catch(() => "");
2345
+ const oldContent = await fs12.readFile(partial.path, "utf-8").catch(() => "");
2351
2346
  return `Writing ${partial.path} (${newlineCount} lines)
2352
2347
  ${unifiedDiff(partial.path, oldContent, completeContent)}`;
2353
2348
  }
@@ -2356,13 +2351,13 @@ ${unifiedDiff(partial.path, oldContent, completeContent)}`;
2356
2351
  async execute(input) {
2357
2352
  const release = await acquireFileLock(input.path);
2358
2353
  try {
2359
- await fs13.mkdir(path6.dirname(input.path), { recursive: true });
2354
+ await fs12.mkdir(path6.dirname(input.path), { recursive: true });
2360
2355
  let oldContent = null;
2361
2356
  try {
2362
- oldContent = await fs13.readFile(input.path, "utf-8");
2357
+ oldContent = await fs12.readFile(input.path, "utf-8");
2363
2358
  } catch {
2364
2359
  }
2365
- await fs13.writeFile(input.path, input.content, "utf-8");
2360
+ await fs12.writeFile(input.path, input.content, "utf-8");
2366
2361
  const lineCount = input.content.split("\n").length;
2367
2362
  const label = oldContent !== null ? "Wrote" : "Created";
2368
2363
  return `${label} ${input.path} (${lineCount} lines)
@@ -2376,7 +2371,7 @@ ${unifiedDiff(input.path, oldContent ?? "", input.content)}`;
2376
2371
  };
2377
2372
 
2378
2373
  // src/tools/code/editFile/index.ts
2379
- import fs14 from "fs/promises";
2374
+ import fs13 from "fs/promises";
2380
2375
  var editFileTool = {
2381
2376
  definition: {
2382
2377
  name: "editFile",
@@ -2407,7 +2402,7 @@ var editFileTool = {
2407
2402
  async execute(input) {
2408
2403
  const release = await acquireFileLock(input.path);
2409
2404
  try {
2410
- const content = await fs14.readFile(input.path, "utf-8");
2405
+ const content = await fs13.readFile(input.path, "utf-8");
2411
2406
  const { old_string, new_string, replace_all } = input;
2412
2407
  const occurrences = findOccurrences(content, old_string);
2413
2408
  if (replace_all) {
@@ -2423,7 +2418,7 @@ var editFileTool = {
2423
2418
  new_string
2424
2419
  );
2425
2420
  }
2426
- await fs14.writeFile(input.path, updated, "utf-8");
2421
+ await fs13.writeFile(input.path, updated, "utf-8");
2427
2422
  return `Replaced ${occurrences.length} occurrence${occurrences.length > 1 ? "s" : ""} in ${input.path}
2428
2423
  ${unifiedDiff(input.path, content, updated)}`;
2429
2424
  }
@@ -2434,7 +2429,7 @@ ${unifiedDiff(input.path, content, updated)}`;
2434
2429
  old_string.length,
2435
2430
  new_string
2436
2431
  );
2437
- await fs14.writeFile(input.path, updated, "utf-8");
2432
+ await fs13.writeFile(input.path, updated, "utf-8");
2438
2433
  return `Updated ${input.path}
2439
2434
  ${unifiedDiff(input.path, content, updated)}`;
2440
2435
  }
@@ -2450,7 +2445,7 @@ ${unifiedDiff(input.path, content, updated)}`;
2450
2445
  flex.matchedText.length,
2451
2446
  new_string
2452
2447
  );
2453
- await fs14.writeFile(input.path, updated, "utf-8");
2448
+ await fs13.writeFile(input.path, updated, "utf-8");
2454
2449
  return `Updated ${input.path} (matched with flexible whitespace at line ${flex.line})
2455
2450
  ${unifiedDiff(input.path, content, updated)}`;
2456
2451
  }
@@ -2522,12 +2517,12 @@ var bashTool = {
2522
2517
  child.stdout.on("data", (chunk) => {
2523
2518
  const text = chunk.toString();
2524
2519
  output += text;
2525
- context?.onLog?.(text);
2520
+ context.onLog?.(text);
2526
2521
  });
2527
2522
  child.stderr.on("data", (chunk) => {
2528
2523
  const text = chunk.toString();
2529
2524
  output += text;
2530
- context?.onLog?.(text);
2525
+ context.onLog?.(text);
2531
2526
  });
2532
2527
  const timer = setTimeout(() => {
2533
2528
  child.kill("SIGTERM");
@@ -2788,12 +2783,12 @@ var globTool = {
2788
2783
  };
2789
2784
 
2790
2785
  // src/tools/code/listDir.ts
2791
- import fs15 from "fs/promises";
2786
+ import fs14 from "fs/promises";
2792
2787
  import path8 from "path";
2793
2788
  var EXCLUDE = /* @__PURE__ */ new Set([".git", "node_modules"]);
2794
2789
  var MAX_CHILDREN = 15;
2795
2790
  async function readAndSort(dirPath) {
2796
- const entries = await fs15.readdir(dirPath, { withFileTypes: true });
2791
+ const entries = await fs14.readdir(dirPath, { withFileTypes: true });
2797
2792
  return entries.filter((e) => !EXCLUDE.has(e.name)).sort((a, b) => {
2798
2793
  if (a.isDirectory() && !b.isDirectory()) {
2799
2794
  return -1;
@@ -2834,7 +2829,7 @@ function formatSize(bytes) {
2834
2829
  }
2835
2830
  async function formatFile(dirPath, name, indent) {
2836
2831
  try {
2837
- const stat6 = await fs15.stat(path8.join(dirPath, name));
2832
+ const stat6 = await fs14.stat(path8.join(dirPath, name));
2838
2833
  return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat6.size)}`;
2839
2834
  } catch {
2840
2835
  return `${indent}${name}`;
@@ -3167,7 +3162,7 @@ var queryDatabaseTool = {
3167
3162
  };
3168
3163
 
3169
3164
  // src/usageLedger.ts
3170
- import fs16 from "fs";
3165
+ import fs15 from "fs";
3171
3166
  var LEDGER_FILE = ".logs/usage.ndjson";
3172
3167
  function thinkingTokensFromBilling(billingEvents, outputTokens) {
3173
3168
  if (!billingEvents?.length) {
@@ -3183,10 +3178,10 @@ function nanoToDollars(nano) {
3183
3178
  function recordUsage(entry) {
3184
3179
  try {
3185
3180
  if (fd === null) {
3186
- fs16.mkdirSync(".logs", { recursive: true });
3187
- fd = fs16.openSync(LEDGER_FILE, "a");
3181
+ fs15.mkdirSync(".logs", { recursive: true });
3182
+ fd = fs15.openSync(LEDGER_FILE, "a");
3188
3183
  }
3189
- fs16.writeSync(fd, JSON.stringify(entry) + "\n");
3184
+ fs15.writeSync(fd, JSON.stringify(entry) + "\n");
3190
3185
  } catch {
3191
3186
  }
3192
3187
  }
@@ -3477,7 +3472,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
3477
3472
  let onLog;
3478
3473
  let model;
3479
3474
  let apiConfig;
3480
- let path13;
3475
+ let path14;
3481
3476
  let fullPage = true;
3482
3477
  let width;
3483
3478
  let height;
@@ -3485,7 +3480,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
3485
3480
  if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
3486
3481
  prompt = promptOrOptions.prompt;
3487
3482
  existingImage = promptOrOptions.image;
3488
- path13 = promptOrOptions.path;
3483
+ path14 = promptOrOptions.path;
3489
3484
  if (promptOrOptions.fullPage !== void 0) {
3490
3485
  fullPage = promptOrOptions.fullPage;
3491
3486
  }
@@ -3509,7 +3504,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
3509
3504
  const ssResult = await sidecarRequest(
3510
3505
  fullPage ? "/screenshot-full-page" : "/screenshot-viewport",
3511
3506
  {
3512
- ...path13 ? { path: path13 } : {},
3507
+ ...path14 ? { path: path14 } : {},
3513
3508
  ...width != null ? { width } : {},
3514
3509
  ...height != null ? { height } : {},
3515
3510
  ...format ? { format } : {}
@@ -4251,7 +4246,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4251
4246
  try {
4252
4247
  let result;
4253
4248
  let recording;
4254
- if (externalTools.has(tc.name) && resolveExternalTool) {
4249
+ if (externalTools.has(tc.name)) {
4255
4250
  result = await resolveExternalTool(tc.id, tc.name, input);
4256
4251
  if (tc.name === "browserCommand") {
4257
4252
  const lifted = liftRecording(result);
@@ -4301,11 +4296,11 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4301
4296
  run2(newInput);
4302
4297
  }
4303
4298
  };
4304
- toolRegistry?.register(entry);
4299
+ toolRegistry.register(entry);
4305
4300
  const toolStart = Date.now();
4306
4301
  run2(tc.input);
4307
4302
  const r = await resultPromise;
4308
- toolRegistry?.unregister(tc.id);
4303
+ toolRegistry.unregister(tc.id);
4309
4304
  log8.info("Tool completed", {
4310
4305
  requestId,
4311
4306
  parentToolId,
@@ -4381,7 +4376,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4381
4376
  return wrapRun();
4382
4377
  }
4383
4378
  log8.info("Sub-agent backgrounded", { requestId, parentToolId, agentName });
4384
- toolRegistry?.register({
4379
+ toolRegistry.register({
4385
4380
  id: parentToolId,
4386
4381
  name: agentName,
4387
4382
  input: { task },
@@ -4407,10 +4402,10 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4407
4402
  }
4408
4403
  };
4409
4404
  runDetached().then((finalResult) => {
4410
- toolRegistry?.unregister(parentToolId);
4405
+ toolRegistry.unregister(parentToolId);
4411
4406
  onBackgroundComplete?.(finalResult);
4412
4407
  }).catch((err) => {
4413
- toolRegistry?.unregister(parentToolId);
4408
+ toolRegistry.unregister(parentToolId);
4414
4409
  onBackgroundComplete?.({ text: `Error: ${err.message}`, messages: [] });
4415
4410
  });
4416
4411
  return { text: ack, messages: [], backgrounded: true };
@@ -4573,12 +4568,12 @@ var BROWSER_TOOLS = [
4573
4568
  var BROWSER_EXTERNAL_TOOLS = /* @__PURE__ */ new Set(["browserCommand"]);
4574
4569
 
4575
4570
  // src/subagents/common/context.ts
4576
- import fs17 from "fs";
4571
+ import fs16 from "fs";
4577
4572
  import path9 from "path";
4578
4573
  function walkMdFiles(dir, skip) {
4579
4574
  const files = [];
4580
4575
  try {
4581
- for (const entry of fs17.readdirSync(dir, { withFileTypes: true })) {
4576
+ for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
4582
4577
  const full = path9.join(dir, entry.name);
4583
4578
  if (entry.isDirectory()) {
4584
4579
  if (!skip?.has(entry.name) && !entry.name.startsWith(".")) {
@@ -4594,7 +4589,7 @@ function walkMdFiles(dir, skip) {
4594
4589
  }
4595
4590
  function parseFrontmatter2(filePath) {
4596
4591
  try {
4597
- const content = fs17.readFileSync(filePath, "utf-8");
4592
+ const content = fs16.readFileSync(filePath, "utf-8");
4598
4593
  const match = content.match(/^---\n([\s\S]*?)\n---/);
4599
4594
  if (!match) {
4600
4595
  return {};
@@ -4640,7 +4635,7 @@ function loadRoadmapIndex() {
4640
4635
  const parts = [];
4641
4636
  try {
4642
4637
  const indexJson = JSON.parse(
4643
- fs17.readFileSync("src/roadmap/index.json", "utf-8")
4638
+ fs16.readFileSync("src/roadmap/index.json", "utf-8")
4644
4639
  );
4645
4640
  if (indexJson.lanes?.length > 0) {
4646
4641
  const laneLines = indexJson.lanes.map(
@@ -4809,9 +4804,6 @@ async function runBrowserAutomation(task, context, opts) {
4809
4804
  requestId: context.requestId,
4810
4805
  onEvent: context.onEvent,
4811
4806
  resolveExternalTool: async (id, name, input) => {
4812
- if (!context.resolveExternalTool) {
4813
- return "Error: no external tool resolver";
4814
- }
4815
4807
  const result2 = await context.resolveExternalTool(id, name, input);
4816
4808
  if (name === "browserCommand") {
4817
4809
  try {
@@ -4910,9 +4902,6 @@ var browserAutomationTool = {
4910
4902
  }
4911
4903
  },
4912
4904
  async execute(input, context) {
4913
- if (!context) {
4914
- return "Error: browser automation requires execution context (only available in headless mode)";
4915
- }
4916
4905
  const result = await runBrowserAutomation(input.task, context);
4917
4906
  let text = result.text;
4918
4907
  if (result.screenshot) {
@@ -4981,7 +4970,7 @@ var screenshotDefinition = {
4981
4970
  };
4982
4971
  async function executeScreenshot(input, onLog, context) {
4983
4972
  const fullPage = input.fullPage === true;
4984
- const model = resolveModel("imageAnalysis", context?.models, context?.model);
4973
+ const model = resolveModel("imageAnalysis", context.models, context.model);
4985
4974
  try {
4986
4975
  if (input.imageUrl) {
4987
4976
  return await captureAndAnalyzeScreenshot({
@@ -4989,10 +4978,10 @@ async function executeScreenshot(input, onLog, context) {
4989
4978
  image: input.imageUrl,
4990
4979
  onLog,
4991
4980
  model,
4992
- apiConfig: context?.apiConfig
4981
+ apiConfig: context.apiConfig
4993
4982
  });
4994
4983
  }
4995
- if (input.instructions && context) {
4984
+ if (input.instructions) {
4996
4985
  const shotKind = fullPage ? "full-page" : "viewport";
4997
4986
  const task = input.path ? `Navigate to "${input.path}", then: ${input.instructions}. After completing these steps, take a ${shotKind} screenshot.` : `${input.instructions}. After completing these steps, take a ${shotKind} screenshot.`;
4998
4987
  const result = await runBrowserAutomation(task, context, {
@@ -5017,7 +5006,7 @@ async function executeScreenshot(input, onLog, context) {
5017
5006
  styleMap,
5018
5007
  onLog,
5019
5008
  model,
5020
- apiConfig: context?.apiConfig
5009
+ apiConfig: context.apiConfig
5021
5010
  });
5022
5011
  }
5023
5012
  const release = await acquireBrowserLock();
@@ -5031,7 +5020,7 @@ async function executeScreenshot(input, onLog, context) {
5031
5020
  format: input.format,
5032
5021
  onLog,
5033
5022
  model,
5034
- apiConfig: context?.apiConfig
5023
+ apiConfig: context.apiConfig
5035
5024
  });
5036
5025
  } finally {
5037
5026
  release();
@@ -5042,7 +5031,7 @@ async function executeScreenshot(input, onLog, context) {
5042
5031
  }
5043
5032
  var screenshotTool = {
5044
5033
  definition: screenshotDefinition,
5045
- execute: (input, context) => executeScreenshot(input, context?.onLog, context)
5034
+ execute: (input, context) => executeScreenshot(input, context.onLog, context)
5046
5035
  };
5047
5036
 
5048
5037
  // src/tools/common/scrapeWebUrl.ts
@@ -5113,7 +5102,7 @@ var scrapeWebUrlTool = {
5113
5102
  return fetchWebPage(input.url, {
5114
5103
  screenshot: true,
5115
5104
  caller: "parent",
5116
- onLog: context?.onLog
5105
+ onLog: context.onLog
5117
5106
  });
5118
5107
  }
5119
5108
  };
@@ -5239,9 +5228,6 @@ var researchTool = {
5239
5228
  }
5240
5229
  },
5241
5230
  async execute(input, context) {
5242
- if (!context) {
5243
- return "Error: research requires execution context";
5244
- }
5245
5231
  return runResearch(input.task, context);
5246
5232
  }
5247
5233
  };
@@ -5398,7 +5384,7 @@ async function execute2(input, onLog, context) {
5398
5384
  prompt: RENDER_ANALYZE_PROMPT,
5399
5385
  image: url,
5400
5386
  onLog,
5401
- model: resolveModel("imageAnalysis", context?.models, context?.model)
5387
+ model: resolveModel("imageAnalysis", context.models, context.model)
5402
5388
  }).then((r) => r.analysis).catch((err) => `Could not review this image: ${err.message}`);
5403
5389
  return JSON.stringify({
5404
5390
  images: [
@@ -5538,9 +5524,9 @@ async function analyze(image, prompt, onLog, context, extra) {
5538
5524
  const analyzed = await analyzeImage({
5539
5525
  prompt,
5540
5526
  image,
5541
- apiConfig: context?.apiConfig,
5527
+ apiConfig: context.apiConfig,
5542
5528
  onLog,
5543
- model: resolveModel("imageAnalysis", context?.models, context?.model)
5529
+ model: resolveModel("imageAnalysis", context.models, context.model)
5544
5530
  });
5545
5531
  return JSON.stringify({
5546
5532
  url: analyzed.url,
@@ -5548,18 +5534,18 @@ async function analyze(image, prompt, onLog, context, extra) {
5548
5534
  ...extra
5549
5535
  });
5550
5536
  }
5551
- async function analyzeLocalHtml(path13, customPrompt, onLog, context) {
5537
+ async function analyzeLocalHtml(path14, customPrompt, onLog, context) {
5552
5538
  let raw;
5553
5539
  try {
5554
- raw = await readFile2(join2(PROJECT_ROOT, path13), "utf8");
5540
+ raw = await readFile2(join2(PROJECT_ROOT, path14), "utf8");
5555
5541
  } catch {
5556
- return `No file at "${path13}". Paths resolve from the project root; list the directory to check the name.`;
5542
+ return `No file at "${path14}". Paths resolve from the project root; list the directory to check the name.`;
5557
5543
  }
5558
5544
  const { name, description, html } = splitFrontmatter(raw);
5559
5545
  if (!html.trim()) {
5560
- return `"${path13}" has no HTML to render.`;
5546
+ return `"${path14}" has no HTML to render.`;
5561
5547
  }
5562
- onLog?.(`Rendering ${path13} in the sandbox browser...`);
5548
+ onLog?.(`Rendering ${path14} in the sandbox browser...`);
5563
5549
  const release = await acquireBrowserLock();
5564
5550
  let rendered;
5565
5551
  try {
@@ -5570,7 +5556,7 @@ async function analyzeLocalHtml(path13, customPrompt, onLog, context) {
5570
5556
  autoHeight: true
5571
5557
  });
5572
5558
  } catch (err) {
5573
- return `Could not render ${path13}: ${err?.message ?? err}`;
5559
+ return `Could not render ${path14}: ${err?.message ?? err}`;
5574
5560
  } finally {
5575
5561
  release();
5576
5562
  }
@@ -5619,9 +5605,9 @@ async function execute4(input, onLog, context) {
5619
5605
  const { url, analysis } = await analyzeImage({
5620
5606
  prompt,
5621
5607
  image: input.imageUrl,
5622
- apiConfig: context?.apiConfig,
5608
+ apiConfig: context.apiConfig,
5623
5609
  onLog,
5624
- model: resolveModel("imageAnalysis", context?.models, context?.model)
5610
+ model: resolveModel("imageAnalysis", context.models, context.model)
5625
5611
  });
5626
5612
  return JSON.stringify({ url, analysis });
5627
5613
  }
@@ -5899,21 +5885,21 @@ async function execute5(input, onLog, context) {
5899
5885
  sourceImages: input.referenceImage ? [input.referenceImage] : void 0,
5900
5886
  enhancePrompts: true,
5901
5887
  onLog,
5902
- apiConfig: context?.apiConfig,
5888
+ apiConfig: context.apiConfig,
5903
5889
  imageGenerationModel: resolveModel(
5904
5890
  "imageGeneration",
5905
- context?.models,
5906
- context?.model
5891
+ context.models,
5892
+ context.model
5907
5893
  ),
5908
5894
  imageAnalysisModel: resolveModel(
5909
5895
  "imageAnalysis",
5910
- context?.models,
5911
- context?.model
5896
+ context.models,
5897
+ context.model
5912
5898
  ),
5913
5899
  imagePromptEnhancerModel: resolveModel(
5914
5900
  "imagePromptEnhancer",
5915
- context?.models,
5916
- context?.model
5901
+ context.models,
5902
+ context.model
5917
5903
  )
5918
5904
  });
5919
5905
  }
@@ -5969,21 +5955,21 @@ async function execute6(input, onLog, context) {
5969
5955
  transparentBackground: input.transparentBackground,
5970
5956
  enhancePrompts: false,
5971
5957
  onLog,
5972
- apiConfig: context?.apiConfig,
5958
+ apiConfig: context.apiConfig,
5973
5959
  imageGenerationModel: resolveModel(
5974
5960
  "imageGeneration",
5975
- context?.models,
5976
- context?.model
5961
+ context.models,
5962
+ context.model
5977
5963
  ),
5978
5964
  imageAnalysisModel: resolveModel(
5979
5965
  "imageAnalysis",
5980
- context?.models,
5981
- context?.model
5966
+ context.models,
5967
+ context.model
5982
5968
  ),
5983
5969
  imagePromptEnhancerModel: resolveModel(
5984
5970
  "imagePromptEnhancer",
5985
- context?.models,
5986
- context?.model
5971
+ context.models,
5972
+ context.model
5987
5973
  )
5988
5974
  });
5989
5975
  }
@@ -6016,9 +6002,6 @@ var copyEditorTool = {
6016
6002
  }
6017
6003
  },
6018
6004
  async execute(input, context) {
6019
- if (!context) {
6020
- return "Error: copy editor requires execution context";
6021
- }
6022
6005
  const specIndex = loadSpecIndex();
6023
6006
  const parts = [BASE_PROMPT3];
6024
6007
  parts.push("<!-- cache_breakpoint -->");
@@ -6232,9 +6215,6 @@ async function uploadMirror(context, slug, content) {
6232
6215
  }
6233
6216
  }
6234
6217
  async function execute9(input, onLog, context) {
6235
- if (!context) {
6236
- return "Error: createWireframe requires execution context";
6237
- }
6238
6218
  const name = String(input.name ?? "");
6239
6219
  const slug = String(input.slug ?? "");
6240
6220
  const description = String(input.description ?? "");
@@ -6295,9 +6275,6 @@ var research = {
6295
6275
  }
6296
6276
  },
6297
6277
  execute: (input, _onLog, context) => {
6298
- if (!context) {
6299
- return Promise.resolve("Error: research requires execution context");
6300
- }
6301
6278
  return runResearch(input.task, context);
6302
6279
  }
6303
6280
  };
@@ -6327,12 +6304,12 @@ async function executeDesignExpertTool(name, input, context, toolCallId, onLog)
6327
6304
  if (!tool) {
6328
6305
  return `Error: unknown tool "${name}"`;
6329
6306
  }
6330
- const childContext = context && toolCallId ? deriveContext(context, toolCallId, onLog) : context;
6307
+ const childContext = toolCallId ? deriveContext(context, toolCallId, onLog) : context;
6331
6308
  return tool.execute(input, onLog, childContext);
6332
6309
  }
6333
6310
 
6334
6311
  // src/subagents/designExpert/data/sampleCache.ts
6335
- import fs18 from "fs";
6312
+ import fs17 from "fs";
6336
6313
  var SAMPLE_FILE = ".remy-design-sample.json";
6337
6314
  var cached2 = null;
6338
6315
  function generateIndices(poolSize, sampleSize) {
@@ -6346,7 +6323,7 @@ function generateIndices(poolSize, sampleSize) {
6346
6323
  }
6347
6324
  function load() {
6348
6325
  try {
6349
- return JSON.parse(fs18.readFileSync(SAMPLE_FILE, "utf-8"));
6326
+ return JSON.parse(fs17.readFileSync(SAMPLE_FILE, "utf-8"));
6350
6327
  } catch {
6351
6328
  return null;
6352
6329
  }
@@ -6703,9 +6680,6 @@ var designExpertTool = {
6703
6680
  }
6704
6681
  },
6705
6682
  async execute(input, context) {
6706
- if (!context) {
6707
- return "Error: visual design expert requires execution context";
6708
- }
6709
6683
  const result = await runDesignExpert(
6710
6684
  {
6711
6685
  task: input.task,
@@ -6773,7 +6747,7 @@ var VISION_TOOLS = [
6773
6747
  ];
6774
6748
 
6775
6749
  // src/subagents/productVision/executor.ts
6776
- import fs19 from "fs";
6750
+ import fs18 from "fs";
6777
6751
  import path10 from "path";
6778
6752
  var ROADMAP_DIR = "src/roadmap";
6779
6753
  var PITCH_DECK_SHELL = readAsset(
@@ -6788,13 +6762,13 @@ async function executeVisionTool(name, input, context) {
6788
6762
  case "writeFile": {
6789
6763
  const filePath = resolve3(input.path);
6790
6764
  try {
6791
- fs19.mkdirSync(ROADMAP_DIR, { recursive: true });
6765
+ fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
6792
6766
  let oldContent = null;
6793
6767
  try {
6794
- oldContent = fs19.readFileSync(filePath, "utf-8");
6768
+ oldContent = fs18.readFileSync(filePath, "utf-8");
6795
6769
  } catch {
6796
6770
  }
6797
- fs19.writeFileSync(filePath, input.content, "utf-8");
6771
+ fs18.writeFileSync(filePath, input.content, "utf-8");
6798
6772
  const lineCount = input.content.split("\n").length;
6799
6773
  const label = oldContent !== null ? "Wrote" : "Created";
6800
6774
  return `${label} ${filePath} (${lineCount} lines)
@@ -6806,11 +6780,11 @@ ${unifiedDiff(filePath, oldContent ?? "", input.content)}`;
6806
6780
  case "deleteFile": {
6807
6781
  const filePath = resolve3(input.path);
6808
6782
  try {
6809
- if (!fs19.existsSync(filePath)) {
6783
+ if (!fs18.existsSync(filePath)) {
6810
6784
  return `Error: ${filePath} does not exist`;
6811
6785
  }
6812
- const oldContent = fs19.readFileSync(filePath, "utf-8");
6813
- fs19.unlinkSync(filePath);
6786
+ const oldContent = fs18.readFileSync(filePath, "utf-8");
6787
+ fs18.unlinkSync(filePath);
6814
6788
  return `Deleted ${filePath}
6815
6789
  ${unifiedDiff(filePath, oldContent, "")}`;
6816
6790
  } catch (err) {
@@ -6818,14 +6792,11 @@ ${unifiedDiff(filePath, oldContent, "")}`;
6818
6792
  }
6819
6793
  }
6820
6794
  case "writePitchDeck": {
6821
- if (!context) {
6822
- return "Error: writePitchDeck requires execution context for design expert delegation";
6823
- }
6824
6795
  const filePath = resolve3("pitch.html");
6825
6796
  try {
6826
- fs19.mkdirSync(ROADMAP_DIR, { recursive: true });
6827
- const exists = fs19.existsSync(filePath);
6828
- const before = exists ? fs19.statSync(filePath).mtimeMs : null;
6797
+ fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
6798
+ const exists = fs18.existsSync(filePath);
6799
+ const before = exists ? fs18.statSync(filePath).mtimeMs : null;
6829
6800
  const delivery = exists ? `### Your deliverable
6830
6801
  The pitch deck already exists at \`${filePath}\`. Read it, then update it for the new <pitch_content>, keeping the presentation scaffolding intact \u2014 change only what needs to change.
6831
6802
 
@@ -6855,11 +6826,11 @@ Maintain the bones of the presentation scaffolding. Always keep the progress bar
6855
6826
  ${delivery}`;
6856
6827
  const result = await runDesignExpertRender({ task }, context);
6857
6828
  context.subAgentMessages?.set(context.toolCallId, result.messages);
6858
- if (!fs19.existsSync(filePath)) {
6829
+ if (!fs18.existsSync(filePath)) {
6859
6830
  return `Error: the design expert did not write ${filePath}. Its reply was:
6860
6831
  ${result.text}`;
6861
6832
  }
6862
- if (before !== null && fs19.statSync(filePath).mtimeMs === before) {
6833
+ if (before !== null && fs18.statSync(filePath).mtimeMs === before) {
6863
6834
  return `Error: the pitch deck at ${filePath} was not modified. The design expert's reply was:
6864
6835
  ${result.text}`;
6865
6836
  }
@@ -6910,9 +6881,6 @@ var productVisionTool = {
6910
6881
  }
6911
6882
  },
6912
6883
  async execute(input, context) {
6913
- if (!context) {
6914
- return "Error: product vision requires execution context";
6915
- }
6916
6884
  const history = context.conversationMessages ? getSubAgentHistory(context.conversationMessages, "productVision") : [];
6917
6885
  const result = await runSubAgent({
6918
6886
  system: getProductVisionPrompt(),
@@ -7038,9 +7006,6 @@ var codeSanityCheckTool = {
7038
7006
  }
7039
7007
  },
7040
7008
  async execute(input, context) {
7041
- if (!context) {
7042
- return "Error: code sanity check requires execution context";
7043
- }
7044
7009
  const specIndex = loadSpecIndex();
7045
7010
  const parts = [BASE_PROMPT5, loadPlatformBrief()];
7046
7011
  parts.push("<!-- cache_breakpoint -->");
@@ -7086,7 +7051,7 @@ var codeSanityCheckTool = {
7086
7051
  };
7087
7052
 
7088
7053
  // src/tools/spec/writeBuildOverview.ts
7089
- import fs20 from "fs";
7054
+ import fs19 from "fs";
7090
7055
  var OVERVIEW_FILE = "src/overview.html";
7091
7056
  var DESIGN_BRIEF = `We are building the Build Overview for this app \u2014 the home page of its Spec tab. It is a calm, dense, one-page reference of everything the app actually contains, including the parts the user can't see. It renders flush inside the Spec tab's content panel (the IDE supplies the surrounding nav).
7092
7057
 
@@ -7146,7 +7111,7 @@ async function renderBuildOverview(content, context, opts) {
7146
7111
  if (!content) {
7147
7112
  return "Error: writeBuildOverview requires non-empty `content` (the overview copy).";
7148
7113
  }
7149
- const exists = fs20.existsSync(OVERVIEW_FILE);
7114
+ const exists = fs19.existsSync(OVERVIEW_FILE);
7150
7115
  const task = `<overview_copy>${content}</overview_copy>
7151
7116
 
7152
7117
  ${DESIGN_BRIEF}
@@ -7163,7 +7128,7 @@ ${exists ? refreshDelivery() : initialDelivery()}`;
7163
7128
  }
7164
7129
  const result = await runDesignExpertRender({ task }, context);
7165
7130
  context.subAgentMessages?.set(context.toolCallId, result.messages);
7166
- if (!fs20.existsSync(OVERVIEW_FILE)) {
7131
+ if (!fs19.existsSync(OVERVIEW_FILE)) {
7167
7132
  return `Error: the design expert did not write ${OVERVIEW_FILE}. Its reply was:
7168
7133
  ${result.text}`;
7169
7134
  }
@@ -7188,11 +7153,8 @@ var buildOverviewTool = {
7188
7153
  }
7189
7154
  },
7190
7155
  async execute(input, context) {
7191
- if (!context) {
7192
- return "Error: writeBuildOverview requires execution context for design expert delegation";
7193
- }
7194
7156
  const content = (input.content ?? "").trim();
7195
- const background = Boolean(content) && fs20.existsSync(OVERVIEW_FILE);
7157
+ const background = Boolean(content) && fs19.existsSync(OVERVIEW_FILE);
7196
7158
  if (background) {
7197
7159
  input.background = true;
7198
7160
  }
@@ -7252,9 +7214,6 @@ var specSyncTool = {
7252
7214
  }
7253
7215
  },
7254
7216
  async execute(input, context) {
7255
- if (!context) {
7256
- return "Error: spec sync requires execution context";
7257
- }
7258
7217
  if (context.onboardingState !== "onboardingFinished") {
7259
7218
  return "Spec sync runs only after the build is finished (onboardingFinished). During intake and the initial build you author the spec directly, so there is nothing to reconcile yet.";
7260
7219
  }
@@ -7445,9 +7404,6 @@ var reviewExistingProjectTool = {
7445
7404
  }
7446
7405
  },
7447
7406
  async execute(input, context) {
7448
- if (!context) {
7449
- return "Error: reviewExistingProject requires execution context";
7450
- }
7451
7407
  return runReviewExistingProject(input, context);
7452
7408
  }
7453
7409
  };
@@ -7949,7 +7905,7 @@ Write the summary of the conversation above, following your instructions.`;
7949
7905
  }
7950
7906
 
7951
7907
  // src/session.ts
7952
- import fs21 from "fs";
7908
+ import fs20 from "fs";
7953
7909
  import path11 from "path";
7954
7910
  var log12 = createLogger("session");
7955
7911
  var SESSION_FILE = ".remy-session.json";
@@ -7963,7 +7919,7 @@ var ARCHIVE_MSG_CACHE_MAX = 3;
7963
7919
  function loadSession(state) {
7964
7920
  pruneArchives();
7965
7921
  try {
7966
- const raw = fs21.readFileSync(SESSION_FILE, "utf-8");
7922
+ const raw = fs20.readFileSync(SESSION_FILE, "utf-8");
7967
7923
  const data = JSON.parse(raw);
7968
7924
  if (data.models && typeof data.models === "object") {
7969
7925
  state.models = data.models;
@@ -7979,7 +7935,7 @@ function loadSession(state) {
7979
7935
  } catch {
7980
7936
  try {
7981
7937
  const quarantine = `${SESSION_FILE}.corrupt-${Date.now()}`;
7982
- fs21.renameSync(SESSION_FILE, quarantine);
7938
+ fs20.renameSync(SESSION_FILE, quarantine);
7983
7939
  log12.warn(`Session file unreadable \u2014 quarantined to ${quarantine}`);
7984
7940
  } catch {
7985
7941
  }
@@ -8031,12 +7987,12 @@ function buildPayload(state) {
8031
7987
  return payload;
8032
7988
  }
8033
7989
  function archiveMessages(messages, label, models) {
8034
- fs21.mkdirSync(ARCHIVE_DIR, { recursive: true });
7990
+ fs20.mkdirSync(ARCHIVE_DIR, { recursive: true });
8035
7991
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
8036
7992
  const count = messages.length;
8037
7993
  let dest = path11.join(ARCHIVE_DIR, `${label}-${ts}.c${count}.json`);
8038
7994
  let n = 1;
8039
- while (fs21.existsSync(dest)) {
7995
+ while (fs20.existsSync(dest)) {
8040
7996
  dest = path11.join(ARCHIVE_DIR, `${label}-${ts}-${n++}.c${count}.json`);
8041
7997
  }
8042
7998
  const payload = { messages };
@@ -8051,13 +8007,13 @@ function archiveMessages(messages, label, models) {
8051
8007
  }
8052
8008
  function pruneArchives() {
8053
8009
  try {
8054
- const entries = fs21.readdirSync(ARCHIVE_DIR).filter((name) => ARCHIVE_NAME_RE.test(name));
8010
+ const entries = fs20.readdirSync(ARCHIVE_DIR).filter((name) => ARCHIVE_NAME_RE.test(name));
8055
8011
  if (entries.length <= 1) {
8056
8012
  return;
8057
8013
  }
8058
8014
  const archives = entries.map((name) => ({
8059
8015
  name,
8060
- size: fs21.statSync(path11.join(ARCHIVE_DIR, name)).size
8016
+ size: fs20.statSync(path11.join(ARCHIVE_DIR, name)).size
8061
8017
  })).sort(
8062
8018
  (a, b) => archiveSortKey(b.name).localeCompare(archiveSortKey(a.name))
8063
8019
  );
@@ -8075,7 +8031,7 @@ function pruneArchives() {
8075
8031
  let freed = 0;
8076
8032
  for (let i = cut; i < archives.length; i++) {
8077
8033
  try {
8078
- fs21.unlinkSync(path11.join(ARCHIVE_DIR, archives[i].name));
8034
+ fs20.unlinkSync(path11.join(ARCHIVE_DIR, archives[i].name));
8079
8035
  freed += archives[i].size;
8080
8036
  removed++;
8081
8037
  } catch {
@@ -8099,7 +8055,7 @@ function parseArchive(name) {
8099
8055
  return cached3;
8100
8056
  }
8101
8057
  try {
8102
- const raw = fs21.readFileSync(path11.join(ARCHIVE_DIR, name), "utf-8");
8058
+ const raw = fs20.readFileSync(path11.join(ARCHIVE_DIR, name), "utf-8");
8103
8059
  const data = JSON.parse(raw);
8104
8060
  const messages = Array.isArray(data?.messages) ? data.messages : [];
8105
8061
  for (const msg of messages) {
@@ -8140,7 +8096,7 @@ function readArchiveMessages(name) {
8140
8096
  function listConversationArchives() {
8141
8097
  let names;
8142
8098
  try {
8143
- names = fs21.readdirSync(ARCHIVE_DIR).filter((n) => ARCHIVE_NAME_RE.test(n));
8099
+ names = fs20.readdirSync(ARCHIVE_DIR).filter((n) => ARCHIVE_NAME_RE.test(n));
8144
8100
  } catch {
8145
8101
  return { slots: [], archivedCount: 0 };
8146
8102
  }
@@ -8426,7 +8382,7 @@ function triggerCompaction(state, apiConfig, opts = {}) {
8426
8382
  }
8427
8383
 
8428
8384
  // src/brandExtraction/index.ts
8429
- import fs22 from "fs";
8385
+ import fs21 from "fs";
8430
8386
  import path12 from "path";
8431
8387
  import { createHash } from "crypto";
8432
8388
  var log14 = createLogger("brandExtraction");
@@ -8477,7 +8433,7 @@ function sha256(input) {
8477
8433
  }
8478
8434
  function readSafe(filePath) {
8479
8435
  try {
8480
- return fs22.readFileSync(filePath, "utf-8");
8436
+ return fs21.readFileSync(filePath, "utf-8");
8481
8437
  } catch {
8482
8438
  return "";
8483
8439
  }
@@ -8503,7 +8459,7 @@ function readBrandManifest() {
8503
8459
  function walkMdFiles2(dir) {
8504
8460
  const results = [];
8505
8461
  try {
8506
- const entries = fs22.readdirSync(dir, { withFileTypes: true });
8462
+ const entries = fs21.readdirSync(dir, { withFileTypes: true });
8507
8463
  for (const entry of entries) {
8508
8464
  const full = path12.join(dir, entry.name);
8509
8465
  if (entry.isDirectory()) {
@@ -8520,7 +8476,7 @@ function walkMdFiles2(dir) {
8520
8476
  }
8521
8477
  function parseFrontmatter3(filePath) {
8522
8478
  try {
8523
- const content = fs22.readFileSync(filePath, "utf-8");
8479
+ const content = fs21.readFileSync(filePath, "utf-8");
8524
8480
  const match = content.match(/^---\n([\s\S]*?)\n---/);
8525
8481
  if (!match) {
8526
8482
  return { type: "" };
@@ -8738,7 +8694,7 @@ function persistBrand(brand, inputHash) {
8738
8694
  }
8739
8695
  function readCache() {
8740
8696
  try {
8741
- const raw = fs22.readFileSync(CACHE_FILE, "utf-8");
8697
+ const raw = fs21.readFileSync(CACHE_FILE, "utf-8");
8742
8698
  const parsed = JSON.parse(raw);
8743
8699
  if (parsed && typeof parsed.inputHash === "string" && typeof parsed.generatedAt === "number") {
8744
8700
  return parsed;
@@ -10009,7 +9965,7 @@ async function runTurn(params) {
10009
9965
  const run = async (input) => {
10010
9966
  try {
10011
9967
  let result;
10012
- if (EXTERNAL_TOOLS.has(tc.name) && resolveExternalTool) {
9968
+ if (EXTERNAL_TOOLS.has(tc.name)) {
10013
9969
  saveSession(state);
10014
9970
  log16.info("Waiting for external tool result", {
10015
9971
  requestId,
@@ -10073,11 +10029,11 @@ async function runTurn(params) {
10073
10029
  run(newInput);
10074
10030
  }
10075
10031
  };
10076
- toolRegistry?.register(entry);
10032
+ toolRegistry.register(entry);
10077
10033
  run(tc.input);
10078
10034
  const r = await resultPromise;
10079
10035
  if (!isBackgroundCall(tc)) {
10080
- toolRegistry?.unregister(tc.id);
10036
+ toolRegistry.unregister(tc.id);
10081
10037
  }
10082
10038
  log16.info("Tool completed", {
10083
10039
  requestId,
@@ -10135,7 +10091,7 @@ async function runTurn(params) {
10135
10091
  midTurnCompactions++;
10136
10092
  await compactNow(`context ${lastCallInputTokens} > ${forceCompactAt}`);
10137
10093
  }
10138
- if (takeSteering && !signal?.aborted) {
10094
+ if (!signal?.aborted) {
10139
10095
  const injected = (await takeSteering()).filter(
10140
10096
  (e) => e.text.trim().length > 0 || (e.attachments?.length ?? 0) > 0
10141
10097
  );
@@ -10708,6 +10664,31 @@ var MessageQueue = class {
10708
10664
 
10709
10665
  // src/headless/index.ts
10710
10666
  var log19 = createLogger("headless");
10667
+ var startupLog = createLogger("startup");
10668
+ function logStartup(config, model, flagApiKey) {
10669
+ let version = "(unknown)";
10670
+ try {
10671
+ version = JSON.parse(
10672
+ fs22.readFileSync(
10673
+ path13.join(import.meta.dirname, "..", "package.json"),
10674
+ "utf-8"
10675
+ )
10676
+ ).version;
10677
+ } catch {
10678
+ }
10679
+ startupLog.info("Startup", {
10680
+ version,
10681
+ node: process.version,
10682
+ platform: `${os2.platform()} ${os2.arch()}`,
10683
+ os: `${os2.type()} ${os2.release()}`,
10684
+ cwd: process.cwd(),
10685
+ bin: process.argv[1],
10686
+ model: model || "(default)",
10687
+ baseUrl: config.baseUrl,
10688
+ apiKey: config.apiKey ? `${config.apiKey.slice(0, 8)}...${config.apiKey.slice(-4)}` : "(none)",
10689
+ keySource: flagApiKey ? "cli flag" : process.env.MINDSTUDIO_API_KEY ? "env var" : "config file"
10690
+ });
10691
+ }
10711
10692
  var EXTERNAL_TOOL_TIMEOUT_MS = 3e5;
10712
10693
  var LONG_RUNNING_TOOLS = /* @__PURE__ */ new Set(["runMethod", "testJewel"]);
10713
10694
  var LONG_RUNNING_TOOL_TIMEOUT_MS = 18e5;
@@ -10816,6 +10797,7 @@ var HeadlessSession = class {
10816
10797
  apiKey: this.opts.apiKey,
10817
10798
  baseUrl: this.opts.baseUrl
10818
10799
  });
10800
+ logStartup(this.config, this.opts.model, this.opts.apiKey);
10819
10801
  await initModelRegistry(this.config);
10820
10802
  await initOrgContext(this.config);
10821
10803
  const resumed = loadSession(this.state);