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