@mindstudio-ai/remy 0.1.340 → 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.
- package/README.md +14 -26
- package/dist/headless.js +180 -198
- package/dist/index.js +8673 -10123
- 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
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1354
|
+
await fs6.mkdir(path4.dirname(input.path), { recursive: true });
|
|
1351
1355
|
let oldContent = null;
|
|
1352
1356
|
try {
|
|
1353
|
-
oldContent = await
|
|
1357
|
+
oldContent = await fs6.readFile(input.path, "utf-8");
|
|
1354
1358
|
} catch {
|
|
1355
1359
|
}
|
|
1356
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
1639
|
+
const fd2 = fs9.openSync(tmp, "w");
|
|
1636
1640
|
try {
|
|
1637
|
-
|
|
1638
|
-
|
|
1641
|
+
fs9.writeSync(fd2, data);
|
|
1642
|
+
fs9.fsyncSync(fd2);
|
|
1639
1643
|
} finally {
|
|
1640
|
-
|
|
1644
|
+
fs9.closeSync(fd2);
|
|
1641
1645
|
}
|
|
1642
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
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
|
|
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
|
|
2126
|
-
return "Error: compaction requires
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2354
|
+
await fs12.mkdir(path6.dirname(input.path), { recursive: true });
|
|
2360
2355
|
let oldContent = null;
|
|
2361
2356
|
try {
|
|
2362
|
-
oldContent = await
|
|
2357
|
+
oldContent = await fs12.readFile(input.path, "utf-8");
|
|
2363
2358
|
} catch {
|
|
2364
2359
|
}
|
|
2365
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
3187
|
-
fd =
|
|
3181
|
+
fs15.mkdirSync(".logs", { recursive: true });
|
|
3182
|
+
fd = fs15.openSync(LEDGER_FILE, "a");
|
|
3188
3183
|
}
|
|
3189
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
...
|
|
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)
|
|
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
|
|
4299
|
+
toolRegistry.register(entry);
|
|
4305
4300
|
const toolStart = Date.now();
|
|
4306
4301
|
run2(tc.input);
|
|
4307
4302
|
const r = await resultPromise;
|
|
4308
|
-
toolRegistry
|
|
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
|
|
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
|
|
4405
|
+
toolRegistry.unregister(parentToolId);
|
|
4411
4406
|
onBackgroundComplete?.(finalResult);
|
|
4412
4407
|
}).catch((err) => {
|
|
4413
|
-
toolRegistry
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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) {
|
|
@@ -4929,7 +4918,7 @@ Replay of this run: `;
|
|
|
4929
4918
|
const { sessionId, startTs, endTs } = result.replay;
|
|
4930
4919
|
text += `
|
|
4931
4920
|
|
|
4932
|
-
To render this replay as an mp4 (returns a public URL): \`remy-admin recordings export --session ${sessionId} --from ${startTs} --to ${endTs}\``;
|
|
4921
|
+
To render this replay as an mp4 (returns a public URL): \`remy-admin qa-recordings export --session ${sessionId} --from ${startTs} --to ${endTs}\``;
|
|
4933
4922
|
}
|
|
4934
4923
|
return text;
|
|
4935
4924
|
}
|
|
@@ -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
|
|
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
|
|
4981
|
+
apiConfig: context.apiConfig
|
|
4993
4982
|
});
|
|
4994
4983
|
}
|
|
4995
|
-
if (input.instructions
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
5527
|
+
apiConfig: context.apiConfig,
|
|
5542
5528
|
onLog,
|
|
5543
|
-
model: resolveModel("imageAnalysis", context
|
|
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(
|
|
5537
|
+
async function analyzeLocalHtml(path14, customPrompt, onLog, context) {
|
|
5552
5538
|
let raw;
|
|
5553
5539
|
try {
|
|
5554
|
-
raw = await readFile2(join2(PROJECT_ROOT,
|
|
5540
|
+
raw = await readFile2(join2(PROJECT_ROOT, path14), "utf8");
|
|
5555
5541
|
} catch {
|
|
5556
|
-
return `No file at "${
|
|
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 `"${
|
|
5546
|
+
return `"${path14}" has no HTML to render.`;
|
|
5561
5547
|
}
|
|
5562
|
-
onLog?.(`Rendering ${
|
|
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 ${
|
|
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
|
|
5608
|
+
apiConfig: context.apiConfig,
|
|
5623
5609
|
onLog,
|
|
5624
|
-
model: resolveModel("imageAnalysis", context
|
|
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
|
|
5888
|
+
apiConfig: context.apiConfig,
|
|
5903
5889
|
imageGenerationModel: resolveModel(
|
|
5904
5890
|
"imageGeneration",
|
|
5905
|
-
context
|
|
5906
|
-
context
|
|
5891
|
+
context.models,
|
|
5892
|
+
context.model
|
|
5907
5893
|
),
|
|
5908
5894
|
imageAnalysisModel: resolveModel(
|
|
5909
5895
|
"imageAnalysis",
|
|
5910
|
-
context
|
|
5911
|
-
context
|
|
5896
|
+
context.models,
|
|
5897
|
+
context.model
|
|
5912
5898
|
),
|
|
5913
5899
|
imagePromptEnhancerModel: resolveModel(
|
|
5914
5900
|
"imagePromptEnhancer",
|
|
5915
|
-
context
|
|
5916
|
-
context
|
|
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
|
|
5958
|
+
apiConfig: context.apiConfig,
|
|
5973
5959
|
imageGenerationModel: resolveModel(
|
|
5974
5960
|
"imageGeneration",
|
|
5975
|
-
context
|
|
5976
|
-
context
|
|
5961
|
+
context.models,
|
|
5962
|
+
context.model
|
|
5977
5963
|
),
|
|
5978
5964
|
imageAnalysisModel: resolveModel(
|
|
5979
5965
|
"imageAnalysis",
|
|
5980
|
-
context
|
|
5981
|
-
context
|
|
5966
|
+
context.models,
|
|
5967
|
+
context.model
|
|
5982
5968
|
),
|
|
5983
5969
|
imagePromptEnhancerModel: resolveModel(
|
|
5984
5970
|
"imagePromptEnhancer",
|
|
5985
|
-
context
|
|
5986
|
-
context
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
6765
|
+
fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
6792
6766
|
let oldContent = null;
|
|
6793
6767
|
try {
|
|
6794
|
-
oldContent =
|
|
6768
|
+
oldContent = fs18.readFileSync(filePath, "utf-8");
|
|
6795
6769
|
} catch {
|
|
6796
6770
|
}
|
|
6797
|
-
|
|
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 (!
|
|
6783
|
+
if (!fs18.existsSync(filePath)) {
|
|
6810
6784
|
return `Error: ${filePath} does not exist`;
|
|
6811
6785
|
}
|
|
6812
|
-
const oldContent =
|
|
6813
|
-
|
|
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
|
-
|
|
6827
|
-
const exists =
|
|
6828
|
-
const before = exists ?
|
|
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 (!
|
|
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 &&
|
|
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
|
|
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 =
|
|
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 (!
|
|
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) &&
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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)
|
|
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
|
|
10032
|
+
toolRegistry.register(entry);
|
|
10077
10033
|
run(tc.input);
|
|
10078
10034
|
const r = await resultPromise;
|
|
10079
10035
|
if (!isBackgroundCall(tc)) {
|
|
10080
|
-
toolRegistry
|
|
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 (
|
|
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);
|