@mindstudio-ai/remy 0.1.319 → 0.1.321
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/automatedActions/reviewExistingProject.md +7 -0
- package/dist/headless.js +270 -29
- package/dist/index.js +319 -42
- package/dist/prompt/skills/dataSources.md +158 -5
- package/dist/prompt/static/coding.md +1 -1
- package/dist/prompt/static/intake.md +1 -1
- package/dist/prompt/static/team.md +7 -1
- package/dist/subagents/browserAutomation/prompt.md +1 -1
- package/dist/subagents/reviewExistingProject/prompt.md +43 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -824,6 +824,9 @@ async function listRecursive(dir) {
|
|
|
824
824
|
for (const entry of entries) {
|
|
825
825
|
const fullPath = path2.join(dir, entry.name);
|
|
826
826
|
if (entry.isDirectory()) {
|
|
827
|
+
if (entry.name.startsWith(".")) {
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
827
830
|
results.push(`${fullPath}/`);
|
|
828
831
|
results.push(...await listRecursive(fullPath));
|
|
829
832
|
} else {
|
|
@@ -1064,7 +1067,7 @@ var init_promptUser = __esm({
|
|
|
1064
1067
|
type: {
|
|
1065
1068
|
type: "string",
|
|
1066
1069
|
enum: ["select", "checklist", "text", "file"],
|
|
1067
|
-
description: 'select: pick one from a list. checklist: pick one or more from a list. The user can always provide a custom "Other" answer for select and checklist questions, so there is no need to include an "Other" option. text: free-form input. file:
|
|
1070
|
+
description: 'select: pick one from a list. checklist: pick one or more from a list. The user can always provide a custom "Other" answer for select and checklist questions, so there is no need to include an "Other" option. text: free-form input. file: the user hands over files, a folder, or a paste (set `multiple: true` to take several). Each answer arrives in one of two shapes. A local path (string) under src/.user-uploads/: the file is already on disk, documents may have an extracted-text sidecar at <path>.txt, and a folder arrives as src/.user-uploads/<folder>.zip for you to unpack with bash. Or, when the user brought a dataset (a folder over about 100 MB or 500 files, or any single file over 100 MB), a store landing object { kind: "store", store, prefix, files, bytes, failed } \u2014 the files were streamed into the app\'s private file store `uploads` under that prefix and are NOT on disk; read them in place with `remy-admin datasources inspect --store <store> --prefix <prefix>` and `remy-admin files get`, never download the whole set. Pass image paths straight to analyzeImage / screenshot tools.'
|
|
1068
1071
|
},
|
|
1069
1072
|
helpText: {
|
|
1070
1073
|
type: "string",
|
|
@@ -1099,7 +1102,7 @@ var init_promptUser = __esm({
|
|
|
1099
1102
|
},
|
|
1100
1103
|
multiple: {
|
|
1101
1104
|
type: "boolean",
|
|
1102
|
-
description:
|
|
1105
|
+
description: 'For file type: allow several files or folders (the answer is an array of local paths and/or store landings). Defaults to false. Set it whenever you ask for "a few examples" or "a sample of your data".'
|
|
1103
1106
|
},
|
|
1104
1107
|
format: {
|
|
1105
1108
|
type: "string",
|
|
@@ -1947,7 +1950,7 @@ var init_compaction = __esm({
|
|
|
1947
1950
|
init_api();
|
|
1948
1951
|
init_assets();
|
|
1949
1952
|
init_sentinel();
|
|
1950
|
-
|
|
1953
|
+
init_tools10();
|
|
1951
1954
|
init_logger();
|
|
1952
1955
|
init_usageLedger();
|
|
1953
1956
|
log2 = createLogger("compaction");
|
|
@@ -2058,6 +2061,13 @@ var init_surfaces = __esm({
|
|
|
2058
2061
|
modelType: "text",
|
|
2059
2062
|
userPickable: true
|
|
2060
2063
|
},
|
|
2064
|
+
reviewExistingProject: {
|
|
2065
|
+
default: "claude-5-sonnet",
|
|
2066
|
+
label: "Existing Project Review",
|
|
2067
|
+
description: "Reviews a project you bring from another tool and reports what is worth carrying forward.",
|
|
2068
|
+
modelType: "text",
|
|
2069
|
+
userPickable: true
|
|
2070
|
+
},
|
|
2061
2071
|
copyEditor: {
|
|
2062
2072
|
default: "claude-5-sonnet",
|
|
2063
2073
|
label: "Copy Agent",
|
|
@@ -2332,6 +2342,53 @@ var init_cleanMessages = __esm({
|
|
|
2332
2342
|
}
|
|
2333
2343
|
});
|
|
2334
2344
|
|
|
2345
|
+
// src/recording.ts
|
|
2346
|
+
function liftRecording(result) {
|
|
2347
|
+
if (!result.includes('"recording"')) {
|
|
2348
|
+
return { result };
|
|
2349
|
+
}
|
|
2350
|
+
let parsed;
|
|
2351
|
+
try {
|
|
2352
|
+
parsed = JSON.parse(result);
|
|
2353
|
+
} catch {
|
|
2354
|
+
return { result };
|
|
2355
|
+
}
|
|
2356
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2357
|
+
return { result };
|
|
2358
|
+
}
|
|
2359
|
+
const obj = parsed;
|
|
2360
|
+
const r = obj.recording;
|
|
2361
|
+
if (!r || typeof r !== "object") {
|
|
2362
|
+
return { result };
|
|
2363
|
+
}
|
|
2364
|
+
const ref = r;
|
|
2365
|
+
if (typeof ref.path !== "string" || typeof ref.sessionId !== "string" || typeof ref.runId !== "string" || typeof ref.seq !== "number") {
|
|
2366
|
+
return { result };
|
|
2367
|
+
}
|
|
2368
|
+
const recording = {
|
|
2369
|
+
path: ref.path,
|
|
2370
|
+
sessionId: ref.sessionId,
|
|
2371
|
+
runId: ref.runId,
|
|
2372
|
+
seq: ref.seq,
|
|
2373
|
+
containsSnapshot: ref.containsSnapshot === true,
|
|
2374
|
+
startTs: typeof ref.startTs === "number" ? ref.startTs : 0,
|
|
2375
|
+
endTs: typeof ref.endTs === "number" ? ref.endTs : 0,
|
|
2376
|
+
width: typeof ref.width === "number" ? ref.width : 0,
|
|
2377
|
+
height: typeof ref.height === "number" ? ref.height : 0
|
|
2378
|
+
};
|
|
2379
|
+
const rest = { ...obj };
|
|
2380
|
+
delete rest.recording;
|
|
2381
|
+
return {
|
|
2382
|
+
result: JSON.stringify({ ...rest, recorded: true }),
|
|
2383
|
+
recording
|
|
2384
|
+
};
|
|
2385
|
+
}
|
|
2386
|
+
var init_recording = __esm({
|
|
2387
|
+
"src/recording.ts"() {
|
|
2388
|
+
"use strict";
|
|
2389
|
+
}
|
|
2390
|
+
});
|
|
2391
|
+
|
|
2335
2392
|
// src/historyLimits.ts
|
|
2336
2393
|
function capToolResult(result, maxBytes = MAX_TOOL_RESULT_BYTES) {
|
|
2337
2394
|
const total = Buffer.byteLength(result, "utf-8");
|
|
@@ -2371,6 +2428,13 @@ function capMessageForHistory(msg, maxBytes = MAX_TOOL_RESULT_BYTES) {
|
|
|
2371
2428
|
continue;
|
|
2372
2429
|
}
|
|
2373
2430
|
if (typeof block.result === "string") {
|
|
2431
|
+
if (block.name === "browserCommand" && !block.recording) {
|
|
2432
|
+
const lifted = liftRecording(block.result);
|
|
2433
|
+
if (lifted.recording) {
|
|
2434
|
+
block.result = lifted.result;
|
|
2435
|
+
block.recording = lifted.recording;
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2374
2438
|
block.result = capToolResult(block.result, maxBytes);
|
|
2375
2439
|
}
|
|
2376
2440
|
if (typeof block.backgroundResult === "string") {
|
|
@@ -2391,6 +2455,7 @@ var MAX_TOOL_RESULT_BYTES, MAX_SUBAGENT_RESULT_BYTES, MAX_SUBAGENT_TRANSCRIPT_BY
|
|
|
2391
2455
|
var init_historyLimits = __esm({
|
|
2392
2456
|
"src/historyLimits.ts"() {
|
|
2393
2457
|
"use strict";
|
|
2458
|
+
init_recording();
|
|
2394
2459
|
MAX_TOOL_RESULT_BYTES = 256 * 1024;
|
|
2395
2460
|
MAX_SUBAGENT_RESULT_BYTES = 32 * 1024;
|
|
2396
2461
|
MAX_SUBAGENT_TRANSCRIPT_BYTES = 512 * 1024;
|
|
@@ -3761,7 +3826,7 @@ async function readAndSort(dirPath) {
|
|
|
3761
3826
|
async function collapsePath(basePath, name) {
|
|
3762
3827
|
let display = name;
|
|
3763
3828
|
let current = path8.join(basePath, name);
|
|
3764
|
-
|
|
3829
|
+
while (true) {
|
|
3765
3830
|
let children;
|
|
3766
3831
|
try {
|
|
3767
3832
|
children = await readAndSort(current);
|
|
@@ -3788,8 +3853,8 @@ function formatSize(bytes) {
|
|
|
3788
3853
|
}
|
|
3789
3854
|
async function formatFile(dirPath, name, indent) {
|
|
3790
3855
|
try {
|
|
3791
|
-
const
|
|
3792
|
-
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(
|
|
3856
|
+
const stat6 = await fs15.stat(path8.join(dirPath, name));
|
|
3857
|
+
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat6.size)}`;
|
|
3793
3858
|
} catch {
|
|
3794
3859
|
return `${indent}${name}`;
|
|
3795
3860
|
}
|
|
@@ -5093,25 +5158,36 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
5093
5158
|
}
|
|
5094
5159
|
let settle;
|
|
5095
5160
|
const resultPromise = new Promise((res) => {
|
|
5096
|
-
settle = (result, isError) => res({
|
|
5161
|
+
settle = (result, isError, recording) => res({
|
|
5162
|
+
id: tc.id,
|
|
5163
|
+
result,
|
|
5164
|
+
isError,
|
|
5165
|
+
...recording ? { recording } : {}
|
|
5166
|
+
});
|
|
5097
5167
|
});
|
|
5098
5168
|
let toolAbort = new AbortController();
|
|
5099
5169
|
const cascadeAbort = () => toolAbort.abort();
|
|
5100
5170
|
signal?.addEventListener("abort", cascadeAbort, { once: true });
|
|
5101
5171
|
let settled = false;
|
|
5102
|
-
const safeSettle = (result, isError) => {
|
|
5172
|
+
const safeSettle = (result, isError, recording) => {
|
|
5103
5173
|
if (settled) {
|
|
5104
5174
|
return;
|
|
5105
5175
|
}
|
|
5106
5176
|
settled = true;
|
|
5107
5177
|
signal?.removeEventListener("abort", cascadeAbort);
|
|
5108
|
-
settle(result, isError);
|
|
5178
|
+
settle(result, isError, recording);
|
|
5109
5179
|
};
|
|
5110
5180
|
const run2 = async (input) => {
|
|
5111
5181
|
try {
|
|
5112
5182
|
let result;
|
|
5183
|
+
let recording;
|
|
5113
5184
|
if (externalTools.has(tc.name) && resolveExternalTool) {
|
|
5114
5185
|
result = await resolveExternalTool(tc.id, tc.name, input);
|
|
5186
|
+
if (tc.name === "browserCommand") {
|
|
5187
|
+
const lifted = liftRecording(result);
|
|
5188
|
+
result = lifted.result;
|
|
5189
|
+
recording = lifted.recording;
|
|
5190
|
+
}
|
|
5115
5191
|
} else {
|
|
5116
5192
|
const onLog = (line) => emit({
|
|
5117
5193
|
type: "tool_input_delta",
|
|
@@ -5127,7 +5203,11 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
5127
5203
|
subAgentMessages
|
|
5128
5204
|
);
|
|
5129
5205
|
}
|
|
5130
|
-
safeSettle(
|
|
5206
|
+
safeSettle(
|
|
5207
|
+
capToolResult(result),
|
|
5208
|
+
result.startsWith("Error"),
|
|
5209
|
+
recording
|
|
5210
|
+
);
|
|
5131
5211
|
} catch (err) {
|
|
5132
5212
|
safeSettle(`Error: ${err.message}`, true);
|
|
5133
5213
|
}
|
|
@@ -5169,7 +5249,8 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
5169
5249
|
id: tc.id,
|
|
5170
5250
|
name: tc.name,
|
|
5171
5251
|
result: r.result,
|
|
5172
|
-
isError: r.isError
|
|
5252
|
+
isError: r.isError,
|
|
5253
|
+
...r.recording ? { recording: r.recording } : {}
|
|
5173
5254
|
});
|
|
5174
5255
|
return r;
|
|
5175
5256
|
})
|
|
@@ -5183,6 +5264,9 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
5183
5264
|
block.result = r.result;
|
|
5184
5265
|
block.isError = r.isError;
|
|
5185
5266
|
block.completedAt = Date.now();
|
|
5267
|
+
if (r.recording) {
|
|
5268
|
+
block.recording = r.recording;
|
|
5269
|
+
}
|
|
5186
5270
|
const innerMsgs = subAgentMessages.get(r.id);
|
|
5187
5271
|
if (innerMsgs) {
|
|
5188
5272
|
block.subAgentMessages = capSubAgentTranscript(innerMsgs);
|
|
@@ -5274,6 +5358,7 @@ var init_runner = __esm({
|
|
|
5274
5358
|
init_usageLedger();
|
|
5275
5359
|
init_toolRegistry();
|
|
5276
5360
|
init_historyLimits();
|
|
5361
|
+
init_recording();
|
|
5277
5362
|
init_statusWatcher();
|
|
5278
5363
|
init_cleanMessages();
|
|
5279
5364
|
log8 = createLogger("sub-agent");
|
|
@@ -5343,7 +5428,7 @@ var init_tools2 = __esm({
|
|
|
5343
5428
|
},
|
|
5344
5429
|
{
|
|
5345
5430
|
name: "browserCommand",
|
|
5346
|
-
description: "Interact with the app's live preview by sending browser commands. Commands execute sequentially with an animated cursor. Always start with a snapshot to see the current state and get ref identifiers. The result includes a snapshot field with the final page state after all steps complete. On error, the failing step has an error field and execution stops. Batches that contain an interactive step (click, type, select)
|
|
5431
|
+
description: "Interact with the app's live preview by sending browser commands. Commands execute sequentially with an animated cursor. Always start with a snapshot to see the current state and get ref identifiers. The result includes a snapshot field with the final page state after all steps complete. On error, the failing step has an error field and execution stops. Batches that contain an interactive step (click, type, select) are recorded for the user to replay and return `recorded: true`. Timeout: 120s.",
|
|
5347
5432
|
inputSchema: {
|
|
5348
5433
|
type: "object",
|
|
5349
5434
|
properties: {
|
|
@@ -5463,7 +5548,7 @@ function walkMdFiles(dir, skip) {
|
|
|
5463
5548
|
for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
|
|
5464
5549
|
const full = path9.join(dir, entry.name);
|
|
5465
5550
|
if (entry.isDirectory()) {
|
|
5466
|
-
if (!skip?.has(entry.name)) {
|
|
5551
|
+
if (!skip?.has(entry.name) && !entry.name.startsWith(".")) {
|
|
5467
5552
|
files.push(...walkMdFiles(full, skip));
|
|
5468
5553
|
}
|
|
5469
5554
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -5772,8 +5857,14 @@ async function runBrowserAutomation(task, context, opts) {
|
|
|
5772
5857
|
});
|
|
5773
5858
|
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
5774
5859
|
const preferred = opts?.capture === "viewport" ? lastCapture.viewport ?? lastCapture.fullPage : lastCapture.fullPage ?? lastCapture.viewport;
|
|
5860
|
+
const recorded = result.messages.some(
|
|
5861
|
+
(m) => m.role === "assistant" && Array.isArray(m.content) && m.content.some(
|
|
5862
|
+
(b) => b.type === "tool" && b.name === "browserCommand" && !!b.recording
|
|
5863
|
+
)
|
|
5864
|
+
);
|
|
5775
5865
|
return {
|
|
5776
5866
|
text: result.text,
|
|
5867
|
+
recorded,
|
|
5777
5868
|
...preferred?.url ? { screenshot: preferred } : {}
|
|
5778
5869
|
};
|
|
5779
5870
|
} finally {
|
|
@@ -5784,7 +5875,7 @@ var log9, CAPTURE_COMMANDS, browserAutomationTool;
|
|
|
5784
5875
|
var init_browserAutomation = __esm({
|
|
5785
5876
|
"src/subagents/browserAutomation/index.ts"() {
|
|
5786
5877
|
"use strict";
|
|
5787
|
-
|
|
5878
|
+
init_tools10();
|
|
5788
5879
|
init_runner();
|
|
5789
5880
|
init_tools2();
|
|
5790
5881
|
init_tools();
|
|
@@ -5818,12 +5909,18 @@ var init_browserAutomation = __esm({
|
|
|
5818
5909
|
return "Error: browser automation requires execution context (only available in headless mode)";
|
|
5819
5910
|
}
|
|
5820
5911
|
const result = await runBrowserAutomation(input.task, context);
|
|
5912
|
+
let text = result.text;
|
|
5821
5913
|
if (result.screenshot) {
|
|
5822
|
-
|
|
5914
|
+
text += `
|
|
5823
5915
|
|
|
5824
5916
|
`;
|
|
5825
5917
|
}
|
|
5826
|
-
|
|
5918
|
+
if (result.recorded) {
|
|
5919
|
+
text += `
|
|
5920
|
+
|
|
5921
|
+
Replay of this run: `;
|
|
5922
|
+
}
|
|
5923
|
+
return text;
|
|
5827
5924
|
}
|
|
5828
5925
|
};
|
|
5829
5926
|
}
|
|
@@ -6082,7 +6179,7 @@ var init_research = __esm({
|
|
|
6082
6179
|
init_assets();
|
|
6083
6180
|
init_runner();
|
|
6084
6181
|
init_context();
|
|
6085
|
-
|
|
6182
|
+
init_tools10();
|
|
6086
6183
|
init_tools3();
|
|
6087
6184
|
init_surfaces();
|
|
6088
6185
|
BASE_PROMPT2 = readAsset("subagents/research", "prompt.md");
|
|
@@ -6950,7 +7047,7 @@ var init_copyEditor = __esm({
|
|
|
6950
7047
|
init_assets();
|
|
6951
7048
|
init_runner();
|
|
6952
7049
|
init_context();
|
|
6953
|
-
|
|
7050
|
+
init_tools10();
|
|
6954
7051
|
init_tools4();
|
|
6955
7052
|
init_surfaces();
|
|
6956
7053
|
BASE_PROMPT3 = readAsset("subagents/copyEditor", "prompt.md");
|
|
@@ -7280,7 +7377,7 @@ var research, tools, DESIGN_EXPERT_TOOLS;
|
|
|
7280
7377
|
var init_tools5 = __esm({
|
|
7281
7378
|
"src/subagents/designExpert/tools/index.ts"() {
|
|
7282
7379
|
"use strict";
|
|
7283
|
-
|
|
7380
|
+
init_tools10();
|
|
7284
7381
|
init_tools();
|
|
7285
7382
|
init_research();
|
|
7286
7383
|
init_scrapeWebUrl();
|
|
@@ -7809,7 +7906,7 @@ var DESCRIPTION, RENDER_WRITE_TOOL_NAMES, DESIGN_EXPERT_RENDER_TOOLS, designExpe
|
|
|
7809
7906
|
var init_designExpert = __esm({
|
|
7810
7907
|
"src/subagents/designExpert/index.ts"() {
|
|
7811
7908
|
"use strict";
|
|
7812
|
-
|
|
7909
|
+
init_tools10();
|
|
7813
7910
|
init_runner();
|
|
7814
7911
|
init_tools5();
|
|
7815
7912
|
init_tools();
|
|
@@ -8062,7 +8159,7 @@ var productVisionTool;
|
|
|
8062
8159
|
var init_productVision = __esm({
|
|
8063
8160
|
"src/subagents/productVision/index.ts"() {
|
|
8064
8161
|
"use strict";
|
|
8065
|
-
|
|
8162
|
+
init_tools10();
|
|
8066
8163
|
init_runner();
|
|
8067
8164
|
init_tools6();
|
|
8068
8165
|
init_tools();
|
|
@@ -8217,7 +8314,7 @@ var init_codeSanityCheck = __esm({
|
|
|
8217
8314
|
init_assets();
|
|
8218
8315
|
init_runner();
|
|
8219
8316
|
init_context();
|
|
8220
|
-
|
|
8317
|
+
init_tools10();
|
|
8221
8318
|
init_tools7();
|
|
8222
8319
|
init_research();
|
|
8223
8320
|
init_tools3();
|
|
@@ -8454,7 +8551,7 @@ var init_specSync = __esm({
|
|
|
8454
8551
|
init_assets();
|
|
8455
8552
|
init_runner();
|
|
8456
8553
|
init_context();
|
|
8457
|
-
|
|
8554
|
+
init_tools10();
|
|
8458
8555
|
init_writeBuildOverview();
|
|
8459
8556
|
init_tools8();
|
|
8460
8557
|
init_lock();
|
|
@@ -8560,6 +8657,166 @@ After reconciling the spec, refresh the Build Overview: author the complete upda
|
|
|
8560
8657
|
}
|
|
8561
8658
|
});
|
|
8562
8659
|
|
|
8660
|
+
// src/subagents/reviewExistingProject/tools.ts
|
|
8661
|
+
var REVIEW_TOOLS;
|
|
8662
|
+
var init_tools9 = __esm({
|
|
8663
|
+
"src/subagents/reviewExistingProject/tools.ts"() {
|
|
8664
|
+
"use strict";
|
|
8665
|
+
init_tools();
|
|
8666
|
+
init_bash();
|
|
8667
|
+
REVIEW_TOOLS = [
|
|
8668
|
+
...COMMON_READ_TOOLS,
|
|
8669
|
+
bashTool.definition
|
|
8670
|
+
];
|
|
8671
|
+
}
|
|
8672
|
+
});
|
|
8673
|
+
|
|
8674
|
+
// src/subagents/reviewExistingProject/validatePaths.ts
|
|
8675
|
+
import { stat as stat4 } from "fs/promises";
|
|
8676
|
+
import { join as join5 } from "path";
|
|
8677
|
+
async function validateUploadPaths(text) {
|
|
8678
|
+
const refs = /* @__PURE__ */ new Set();
|
|
8679
|
+
for (const m of text.matchAll(UPLOAD_REF_RE)) {
|
|
8680
|
+
refs.add(m[1]);
|
|
8681
|
+
}
|
|
8682
|
+
if (refs.size === 0) {
|
|
8683
|
+
return null;
|
|
8684
|
+
}
|
|
8685
|
+
const missing = [];
|
|
8686
|
+
for (const ref of refs) {
|
|
8687
|
+
const exists = await stat4(join5(PROJECT_ROOT, ref)).then(
|
|
8688
|
+
() => true,
|
|
8689
|
+
() => false
|
|
8690
|
+
);
|
|
8691
|
+
if (!exists) {
|
|
8692
|
+
missing.push(ref);
|
|
8693
|
+
}
|
|
8694
|
+
}
|
|
8695
|
+
if (missing.length === 0) {
|
|
8696
|
+
return null;
|
|
8697
|
+
}
|
|
8698
|
+
return [
|
|
8699
|
+
`Your review cites paths under src/.user-uploads/ that do not exist on disk:`,
|
|
8700
|
+
...missing.map((p) => `- ${p}`),
|
|
8701
|
+
``,
|
|
8702
|
+
`Check the real location with listDir \u2014 the trimmed tree may have landed under a different name, or the original archive may already have been deleted \u2014 and correct or remove each path. Then send your complete review again from the top \u2014 it fully replaces your previous response, so include everything, not just the fixes.`
|
|
8703
|
+
].join("\n");
|
|
8704
|
+
}
|
|
8705
|
+
var UPLOAD_REF_RE;
|
|
8706
|
+
var init_validatePaths = __esm({
|
|
8707
|
+
"src/subagents/reviewExistingProject/validatePaths.ts"() {
|
|
8708
|
+
"use strict";
|
|
8709
|
+
init_projectRoot();
|
|
8710
|
+
UPLOAD_REF_RE = /`(src\/\.user-uploads\/[^`\n]+?)\/?`/g;
|
|
8711
|
+
}
|
|
8712
|
+
});
|
|
8713
|
+
|
|
8714
|
+
// src/subagents/reviewExistingProject/index.ts
|
|
8715
|
+
function buildTask(input) {
|
|
8716
|
+
const parts = [];
|
|
8717
|
+
const paths = (input.paths ?? []).filter(
|
|
8718
|
+
(p) => typeof p === "string" && p.trim()
|
|
8719
|
+
);
|
|
8720
|
+
if (paths.length > 0) {
|
|
8721
|
+
parts.push(
|
|
8722
|
+
`Uploads (local paths):
|
|
8723
|
+
${paths.map((p) => `- ${p.trim()}`).join("\n")}`
|
|
8724
|
+
);
|
|
8725
|
+
}
|
|
8726
|
+
if (input.repoUrl?.trim()) {
|
|
8727
|
+
parts.push(`Repository URL: ${input.repoUrl.trim()}`);
|
|
8728
|
+
}
|
|
8729
|
+
if (input.context?.trim()) {
|
|
8730
|
+
parts.push(`Context from the conversation: ${input.context.trim()}`);
|
|
8731
|
+
}
|
|
8732
|
+
return parts.join("\n\n");
|
|
8733
|
+
}
|
|
8734
|
+
async function runReviewExistingProject(input, context) {
|
|
8735
|
+
const task = buildTask(input);
|
|
8736
|
+
if (!task) {
|
|
8737
|
+
return "Error: reviewExistingProject needs at least one upload path or a repository URL.";
|
|
8738
|
+
}
|
|
8739
|
+
const specIndex = loadSpecIndex();
|
|
8740
|
+
const parts = [BASE_PROMPT7, loadPlatformBrief()];
|
|
8741
|
+
parts.push("<!-- cache_breakpoint -->");
|
|
8742
|
+
if (specIndex) {
|
|
8743
|
+
parts.push(specIndex);
|
|
8744
|
+
}
|
|
8745
|
+
const system = parts.join("\n\n");
|
|
8746
|
+
const result = await runSubAgent({
|
|
8747
|
+
system,
|
|
8748
|
+
task,
|
|
8749
|
+
tools: REVIEW_TOOLS,
|
|
8750
|
+
externalTools: /* @__PURE__ */ new Set(),
|
|
8751
|
+
executeTool: (name, toolInput, toolCallId, onLog, sams) => {
|
|
8752
|
+
const childCtx = toolCallId ? {
|
|
8753
|
+
...deriveContext(context, toolCallId, onLog),
|
|
8754
|
+
subAgentMessages: sams
|
|
8755
|
+
} : context;
|
|
8756
|
+
return executeTool(name, toolInput, childCtx);
|
|
8757
|
+
},
|
|
8758
|
+
apiConfig: context.apiConfig,
|
|
8759
|
+
model: resolveModel("reviewExistingProject", context.models, context.model),
|
|
8760
|
+
subAgentId: "reviewExistingProject",
|
|
8761
|
+
signal: context.signal,
|
|
8762
|
+
parentToolId: context.toolCallId,
|
|
8763
|
+
requestId: context.requestId,
|
|
8764
|
+
onEvent: context.onEvent,
|
|
8765
|
+
resolveExternalTool: context.resolveExternalTool,
|
|
8766
|
+
toolRegistry: context.toolRegistry,
|
|
8767
|
+
// Every quoted src/.user-uploads/ path in the review must exist on disk
|
|
8768
|
+
// (see validatePaths.ts) — the paths are the part the caller acts on.
|
|
8769
|
+
validateResult: (text) => validateUploadPaths(text)
|
|
8770
|
+
});
|
|
8771
|
+
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
8772
|
+
return result.text;
|
|
8773
|
+
}
|
|
8774
|
+
var BASE_PROMPT7, reviewExistingProjectTool;
|
|
8775
|
+
var init_reviewExistingProject = __esm({
|
|
8776
|
+
"src/subagents/reviewExistingProject/index.ts"() {
|
|
8777
|
+
"use strict";
|
|
8778
|
+
init_assets();
|
|
8779
|
+
init_runner();
|
|
8780
|
+
init_context();
|
|
8781
|
+
init_tools10();
|
|
8782
|
+
init_tools9();
|
|
8783
|
+
init_validatePaths();
|
|
8784
|
+
init_surfaces();
|
|
8785
|
+
BASE_PROMPT7 = readAsset("subagents/reviewExistingProject", "prompt.md");
|
|
8786
|
+
reviewExistingProjectTool = {
|
|
8787
|
+
definition: {
|
|
8788
|
+
name: "reviewExistingProject",
|
|
8789
|
+
description: "Your reviewer for anything a user brings from a prior/extant project or attempt at a project: a zip of a codebase from another coding agent, an export from a vibe coding platform, a folder of specs and docs, a public git URL. It unpacks the upload, strips the noise, lands the trimmed tree in src/.user-uploads/, and returns a short review: what the project was trying to be, the materials worth carrying forward with their paths, what to ignore, state signals, and the questions only the user can answer. Treat the review as evidence of what the user wants, never as a spec. Do not open the archive yourself.",
|
|
8790
|
+
inputSchema: {
|
|
8791
|
+
type: "object",
|
|
8792
|
+
properties: {
|
|
8793
|
+
paths: {
|
|
8794
|
+
type: "array",
|
|
8795
|
+
items: { type: "string" },
|
|
8796
|
+
description: "Local paths under src/.user-uploads/ to the uploaded archive(s) and/or loose files, exactly as given in the attachment header."
|
|
8797
|
+
},
|
|
8798
|
+
context: {
|
|
8799
|
+
type: "string",
|
|
8800
|
+
description: "One or two lines about the user and what they said they want. The reviewer cannot see the conversation."
|
|
8801
|
+
},
|
|
8802
|
+
repoUrl: {
|
|
8803
|
+
type: "string",
|
|
8804
|
+
description: "A public git repository URL to shallow-clone and review, instead of or alongside the uploads."
|
|
8805
|
+
}
|
|
8806
|
+
},
|
|
8807
|
+
required: []
|
|
8808
|
+
}
|
|
8809
|
+
},
|
|
8810
|
+
async execute(input, context) {
|
|
8811
|
+
if (!context) {
|
|
8812
|
+
return "Error: reviewExistingProject requires execution context";
|
|
8813
|
+
}
|
|
8814
|
+
return runReviewExistingProject(input, context);
|
|
8815
|
+
}
|
|
8816
|
+
};
|
|
8817
|
+
}
|
|
8818
|
+
});
|
|
8819
|
+
|
|
8563
8820
|
// src/tools/common/scrapeWebUrl.ts
|
|
8564
8821
|
var scrapeWebUrlTool;
|
|
8565
8822
|
var init_scrapeWebUrl2 = __esm({
|
|
@@ -8630,7 +8887,7 @@ function executeTool(name, input, context) {
|
|
|
8630
8887
|
return tool.execute(input, context);
|
|
8631
8888
|
}
|
|
8632
8889
|
var ALL_TOOLS, SUBAGENT_TOOL_NAMES;
|
|
8633
|
-
var
|
|
8890
|
+
var init_tools10 = __esm({
|
|
8634
8891
|
"src/tools/index.ts"() {
|
|
8635
8892
|
"use strict";
|
|
8636
8893
|
init_readSpec();
|
|
@@ -8669,6 +8926,7 @@ var init_tools9 = __esm({
|
|
|
8669
8926
|
init_copyEditor();
|
|
8670
8927
|
init_specSync();
|
|
8671
8928
|
init_research();
|
|
8929
|
+
init_reviewExistingProject();
|
|
8672
8930
|
init_scrapeWebUrl2();
|
|
8673
8931
|
init_writeBuildOverview();
|
|
8674
8932
|
ALL_TOOLS = [
|
|
@@ -8717,7 +8975,8 @@ var init_tools9 = __esm({
|
|
|
8717
8975
|
// new tool goes at the end to leave every existing session's prefix intact.
|
|
8718
8976
|
loadSkillTool,
|
|
8719
8977
|
testJewelTool,
|
|
8720
|
-
researchTool
|
|
8978
|
+
researchTool,
|
|
8979
|
+
reviewExistingProjectTool
|
|
8721
8980
|
];
|
|
8722
8981
|
SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
8723
8982
|
"visualDesignExpert",
|
|
@@ -8727,7 +8986,8 @@ var init_tools9 = __esm({
|
|
|
8727
8986
|
"specSync",
|
|
8728
8987
|
"runAutomatedBrowserTest",
|
|
8729
8988
|
"askMindStudioSdk",
|
|
8730
|
-
"research"
|
|
8989
|
+
"research",
|
|
8990
|
+
"reviewExistingProject"
|
|
8731
8991
|
]);
|
|
8732
8992
|
}
|
|
8733
8993
|
});
|
|
@@ -9244,7 +9504,9 @@ function walkMdFiles2(dir) {
|
|
|
9244
9504
|
for (const entry of entries) {
|
|
9245
9505
|
const full = path11.join(dir, entry.name);
|
|
9246
9506
|
if (entry.isDirectory()) {
|
|
9247
|
-
|
|
9507
|
+
if (!entry.name.startsWith(".")) {
|
|
9508
|
+
results.push(...walkMdFiles2(full));
|
|
9509
|
+
}
|
|
9248
9510
|
} else if (entry.name.endsWith(".md")) {
|
|
9249
9511
|
results.push(full);
|
|
9250
9512
|
}
|
|
@@ -10239,7 +10501,7 @@ var init_agent = __esm({
|
|
|
10239
10501
|
"src/agent.ts"() {
|
|
10240
10502
|
"use strict";
|
|
10241
10503
|
init_api();
|
|
10242
|
-
|
|
10504
|
+
init_tools10();
|
|
10243
10505
|
init_session();
|
|
10244
10506
|
init_logger();
|
|
10245
10507
|
init_usageLedger();
|
|
@@ -10526,9 +10788,11 @@ var init_config = __esm({
|
|
|
10526
10788
|
});
|
|
10527
10789
|
|
|
10528
10790
|
// src/headless/attachments.ts
|
|
10529
|
-
import { mkdirSync, existsSync } from "fs";
|
|
10530
|
-
import { writeFile as writeFile3 } from "fs/promises";
|
|
10531
|
-
import {
|
|
10791
|
+
import { mkdirSync, existsSync, createWriteStream } from "fs";
|
|
10792
|
+
import { writeFile as writeFile3, stat as stat5 } from "fs/promises";
|
|
10793
|
+
import { Readable } from "stream";
|
|
10794
|
+
import { pipeline } from "stream/promises";
|
|
10795
|
+
import { basename as basename2, join as join6, extname as extname3 } from "path";
|
|
10532
10796
|
function filenameFromUrl(url) {
|
|
10533
10797
|
try {
|
|
10534
10798
|
const pathname = new URL(url).pathname;
|
|
@@ -10539,7 +10803,7 @@ function filenameFromUrl(url) {
|
|
|
10539
10803
|
}
|
|
10540
10804
|
}
|
|
10541
10805
|
function resolveUniqueFilename(name, claimed) {
|
|
10542
|
-
const isFree = (candidate) => !claimed.has(candidate) && !existsSync(
|
|
10806
|
+
const isFree = (candidate) => !claimed.has(candidate) && !existsSync(join6(UPLOADS_DIR, candidate));
|
|
10543
10807
|
if (isFree(name)) {
|
|
10544
10808
|
return name;
|
|
10545
10809
|
}
|
|
@@ -10573,19 +10837,23 @@ async function persistAttachmentList(attachments) {
|
|
|
10573
10837
|
const results = await Promise.allSettled(
|
|
10574
10838
|
nonVoice.map(async (att, i) => {
|
|
10575
10839
|
const name = names[i];
|
|
10576
|
-
const localPath =
|
|
10840
|
+
const localPath = join6(UPLOADS_DIR, name);
|
|
10841
|
+
const timeoutMs = isImageAttachment(att) ? IMAGE_DOWNLOAD_TIMEOUT_MS : DOCUMENT_DOWNLOAD_TIMEOUT_MS;
|
|
10577
10842
|
const res = await fetch(att.url, {
|
|
10578
|
-
signal: AbortSignal.timeout(
|
|
10843
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
10579
10844
|
});
|
|
10580
|
-
if (!res.ok) {
|
|
10845
|
+
if (!res.ok || !res.body) {
|
|
10581
10846
|
throw new Error(`HTTP ${res.status} downloading ${att.url}`);
|
|
10582
10847
|
}
|
|
10583
|
-
|
|
10584
|
-
|
|
10848
|
+
await pipeline(
|
|
10849
|
+
Readable.fromWeb(res.body),
|
|
10850
|
+
createWriteStream(localPath)
|
|
10851
|
+
);
|
|
10852
|
+
const { size } = await stat5(localPath);
|
|
10585
10853
|
log16.info("Attachment saved", {
|
|
10586
10854
|
filename: name,
|
|
10587
10855
|
path: localPath,
|
|
10588
|
-
bytes:
|
|
10856
|
+
bytes: size
|
|
10589
10857
|
});
|
|
10590
10858
|
let extractedTextPath;
|
|
10591
10859
|
if (att.extractedTextUrl) {
|
|
@@ -10645,13 +10913,15 @@ function buildUploadHeader(documents, images) {
|
|
|
10645
10913
|
return `[Uploaded files]
|
|
10646
10914
|
${lines.join("\n")}`;
|
|
10647
10915
|
}
|
|
10648
|
-
var log16, UPLOADS_DIR, IMAGE_EXTENSIONS;
|
|
10916
|
+
var log16, UPLOADS_DIR, IMAGE_DOWNLOAD_TIMEOUT_MS, DOCUMENT_DOWNLOAD_TIMEOUT_MS, IMAGE_EXTENSIONS;
|
|
10649
10917
|
var init_attachments = __esm({
|
|
10650
10918
|
"src/headless/attachments.ts"() {
|
|
10651
10919
|
"use strict";
|
|
10652
10920
|
init_logger();
|
|
10653
10921
|
log16 = createLogger("headless:attachments");
|
|
10654
10922
|
UPLOADS_DIR = "src/.user-uploads";
|
|
10923
|
+
IMAGE_DOWNLOAD_TIMEOUT_MS = 3e4;
|
|
10924
|
+
DOCUMENT_DOWNLOAD_TIMEOUT_MS = 3e5;
|
|
10655
10925
|
IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp"]);
|
|
10656
10926
|
}
|
|
10657
10927
|
});
|
|
@@ -10955,7 +11225,7 @@ var init_headless = __esm({
|
|
|
10955
11225
|
init_attachments();
|
|
10956
11226
|
init_planFile();
|
|
10957
11227
|
init_stats();
|
|
10958
|
-
|
|
11228
|
+
init_tools10();
|
|
10959
11229
|
init_messageQueue();
|
|
10960
11230
|
init_resolve();
|
|
10961
11231
|
init_sentinel();
|
|
@@ -11513,6 +11783,7 @@ var init_headless = __esm({
|
|
|
11513
11783
|
name: e.name,
|
|
11514
11784
|
result: e.result,
|
|
11515
11785
|
isError: e.isError,
|
|
11786
|
+
...e.recording && { recording: e.recording },
|
|
11516
11787
|
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
11517
11788
|
},
|
|
11518
11789
|
rid
|
|
@@ -11625,12 +11896,15 @@ var init_headless = __esm({
|
|
|
11625
11896
|
const questions = Array.isArray(input?.questions) ? input.questions : [];
|
|
11626
11897
|
const batch = [];
|
|
11627
11898
|
const slots = [];
|
|
11899
|
+
let landings = 0;
|
|
11628
11900
|
for (const q of questions) {
|
|
11629
11901
|
if (q?.type !== "file" || typeof q.id !== "string") {
|
|
11630
11902
|
continue;
|
|
11631
11903
|
}
|
|
11632
11904
|
const val = answers[q.id];
|
|
11633
|
-
const
|
|
11905
|
+
const all = Array.isArray(val) ? val : [val];
|
|
11906
|
+
landings += all.filter((v) => v && v.kind === "store").length;
|
|
11907
|
+
const items = all.filter(isDescriptor);
|
|
11634
11908
|
if (items.length === 0) {
|
|
11635
11909
|
continue;
|
|
11636
11910
|
}
|
|
@@ -11649,6 +11923,9 @@ var init_headless = __esm({
|
|
|
11649
11923
|
}
|
|
11650
11924
|
}
|
|
11651
11925
|
if (batch.length === 0) {
|
|
11926
|
+
if (landings > 0) {
|
|
11927
|
+
log17.info("promptUser store landings passed through", { landings });
|
|
11928
|
+
}
|
|
11652
11929
|
return raw;
|
|
11653
11930
|
}
|
|
11654
11931
|
let results;
|
|
@@ -11992,7 +12269,7 @@ var init_headless = __esm({
|
|
|
11992
12269
|
* there would strand the chain steps and background results behind it.
|
|
11993
12270
|
*/
|
|
11994
12271
|
async drainQueueLoop() {
|
|
11995
|
-
|
|
12272
|
+
while (true) {
|
|
11996
12273
|
const at = this.queue.firstDeliverableIndex();
|
|
11997
12274
|
if (at === -1) {
|
|
11998
12275
|
return;
|