@mindstudio-ai/remy 0.1.36 → 0.1.37
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 +398 -238
- package/dist/index.js +402 -242
- package/dist/prompt/sources/frontend-design-notes.md +15 -27
- package/dist/prompt/static/team.md +25 -0
- package/dist/subagents/.notes-background-agents.md +36 -64
- package/dist/subagents/designExpert/prompts/images.md +8 -2
- package/package.json +1 -1
package/dist/headless.js
CHANGED
|
@@ -593,6 +593,31 @@ async function* streamChatWithRetry(params, options) {
|
|
|
593
593
|
return;
|
|
594
594
|
}
|
|
595
595
|
}
|
|
596
|
+
var FALLBACK_ACK = "[Message sent to agent. Agent is working in the background and will report back with its results when finished.]";
|
|
597
|
+
async function generateBackgroundAck(params) {
|
|
598
|
+
const url = `${params.apiConfig.baseUrl}/_internal/v2/agent/remy/generate-ack`;
|
|
599
|
+
try {
|
|
600
|
+
const res = await fetch(url, {
|
|
601
|
+
method: "POST",
|
|
602
|
+
headers: {
|
|
603
|
+
"Content-Type": "application/json",
|
|
604
|
+
Authorization: `Bearer ${params.apiConfig.apiKey}`
|
|
605
|
+
},
|
|
606
|
+
body: JSON.stringify({
|
|
607
|
+
agentName: params.agentName,
|
|
608
|
+
task: params.task
|
|
609
|
+
}),
|
|
610
|
+
signal: AbortSignal.timeout(2e4)
|
|
611
|
+
});
|
|
612
|
+
if (!res.ok) {
|
|
613
|
+
return FALLBACK_ACK;
|
|
614
|
+
}
|
|
615
|
+
const data = await res.json();
|
|
616
|
+
return data.message || FALLBACK_ACK;
|
|
617
|
+
} catch (err) {
|
|
618
|
+
return FALLBACK_ACK;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
596
621
|
|
|
597
622
|
// src/tools/spec/readSpec.ts
|
|
598
623
|
import fs5 from "fs/promises";
|
|
@@ -1275,8 +1300,12 @@ function runCli(cmd, options) {
|
|
|
1275
1300
|
const maxBuffer = options?.maxBuffer ?? 1024 * 1024;
|
|
1276
1301
|
const cmdWithLogs = options?.jsonLogs && !cmd.includes("--json-logs") ? cmd.replace(/^(mindstudio\s+\S+)/, "$1 --json-logs") : cmd;
|
|
1277
1302
|
const child = spawn("sh", ["-c", cmdWithLogs], {
|
|
1278
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
1303
|
+
stdio: [options?.stdin ? "pipe" : "ignore", "pipe", "pipe"]
|
|
1279
1304
|
});
|
|
1305
|
+
if (options?.stdin) {
|
|
1306
|
+
child.stdin.write(options.stdin);
|
|
1307
|
+
child.stdin.end();
|
|
1308
|
+
}
|
|
1280
1309
|
const logs = [];
|
|
1281
1310
|
let stdout = "";
|
|
1282
1311
|
let stderr = "";
|
|
@@ -1430,20 +1459,27 @@ var searchGoogleTool = {
|
|
|
1430
1459
|
}
|
|
1431
1460
|
};
|
|
1432
1461
|
|
|
1433
|
-
// src/tools/common/
|
|
1434
|
-
var
|
|
1462
|
+
// src/tools/common/setProjectMetadata.ts
|
|
1463
|
+
var setProjectMetadataTool = {
|
|
1435
1464
|
definition: {
|
|
1436
|
-
name: "
|
|
1437
|
-
description:
|
|
1465
|
+
name: "setProjectMetadata",
|
|
1466
|
+
description: "Set project metadata. Can update any combination of: display name, app icon, and Open Graph share image. Provide only the fields you want to change.",
|
|
1438
1467
|
inputSchema: {
|
|
1439
1468
|
type: "object",
|
|
1440
1469
|
properties: {
|
|
1441
1470
|
name: {
|
|
1442
1471
|
type: "string",
|
|
1443
|
-
description: "
|
|
1472
|
+
description: "Project display name. Keep it short (2-4 words). Use the app's actual name if the user mentioned one, otherwise pick something descriptive."
|
|
1473
|
+
},
|
|
1474
|
+
iconUrl: {
|
|
1475
|
+
type: "string",
|
|
1476
|
+
description: "URL for the app icon (square."
|
|
1477
|
+
},
|
|
1478
|
+
openGraphShareImageUrl: {
|
|
1479
|
+
type: "string",
|
|
1480
|
+
description: "URL for the Open Graph share image (1200x630)."
|
|
1444
1481
|
}
|
|
1445
|
-
}
|
|
1446
|
-
required: ["name"]
|
|
1482
|
+
}
|
|
1447
1483
|
}
|
|
1448
1484
|
},
|
|
1449
1485
|
async execute() {
|
|
@@ -2066,7 +2102,7 @@ var restartProcessTool = {
|
|
|
2066
2102
|
var runScenarioTool = {
|
|
2067
2103
|
definition: {
|
|
2068
2104
|
name: "runScenario",
|
|
2069
|
-
description: "Run a scenario to seed the dev database with test data. Truncates all tables first, then executes the seed function and impersonates the scenario roles. Blocks until complete. Scenario IDs are defined in mindstudio.json. If it fails, check .logs/tunnel.log or .logs/requests.ndjson for details.",
|
|
2105
|
+
description: "Run a scenario to seed the dev database with test data. Truncates all tables first, then executes the seed function and impersonates the scenario roles. Blocks until complete. Scenario IDs are defined in mindstudio.json. If it fails, check .logs/tunnel.log or .logs/requests.ndjson for details. Return synchronously - no need to sleep before checking results.",
|
|
2070
2106
|
inputSchema: {
|
|
2071
2107
|
type: "object",
|
|
2072
2108
|
properties: {
|
|
@@ -2087,7 +2123,7 @@ var runScenarioTool = {
|
|
|
2087
2123
|
var runMethodTool = {
|
|
2088
2124
|
definition: {
|
|
2089
2125
|
name: "runMethod",
|
|
2090
|
-
description: "Run a method in the dev environment and return the result. Use for testing methods after writing or modifying them. Returns output, captured console output, errors with stack traces, and duration. If it fails, check .logs/tunnel.log or .logs/requests.ndjson for more details.",
|
|
2126
|
+
description: "Run a method in the dev environment and return the result. Use for testing methods after writing or modifying them. Returns output, captured console output, errors with stack traces, and duration. If it fails, check .logs/tunnel.log or .logs/requests.ndjson for more details. Return synchronously - no need to sleep before checking results.",
|
|
2091
2127
|
inputSchema: {
|
|
2092
2128
|
type: "object",
|
|
2093
2129
|
properties: {
|
|
@@ -2249,6 +2285,12 @@ function startStatusWatcher(config) {
|
|
|
2249
2285
|
// src/subagents/common/cleanMessages.ts
|
|
2250
2286
|
function cleanMessagesForApi(messages) {
|
|
2251
2287
|
return messages.map((msg) => {
|
|
2288
|
+
if (msg.role === "user" && typeof msg.content === "string" && msg.content.startsWith("@@automated::")) {
|
|
2289
|
+
return {
|
|
2290
|
+
...msg,
|
|
2291
|
+
content: msg.content.replace(/^@@automated::[^@]*@@\n?/, "")
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2252
2294
|
if (!Array.isArray(msg.content)) {
|
|
2253
2295
|
return msg;
|
|
2254
2296
|
}
|
|
@@ -2286,238 +2328,264 @@ async function runSubAgent(config) {
|
|
|
2286
2328
|
apiConfig,
|
|
2287
2329
|
model,
|
|
2288
2330
|
subAgentId,
|
|
2289
|
-
signal,
|
|
2331
|
+
signal: parentSignal,
|
|
2290
2332
|
parentToolId,
|
|
2291
2333
|
onEvent,
|
|
2292
2334
|
resolveExternalTool,
|
|
2293
|
-
toolRegistry
|
|
2335
|
+
toolRegistry,
|
|
2336
|
+
background,
|
|
2337
|
+
onBackgroundComplete
|
|
2294
2338
|
} = config;
|
|
2339
|
+
const bgAbort = background ? new AbortController() : null;
|
|
2340
|
+
const signal = background ? bgAbort.signal : parentSignal;
|
|
2295
2341
|
const emit2 = (e) => {
|
|
2296
2342
|
onEvent({ ...e, parentToolId });
|
|
2297
2343
|
};
|
|
2298
|
-
const
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
let lastToolResult = "";
|
|
2315
|
-
while (true) {
|
|
2316
|
-
if (signal?.aborted) {
|
|
2317
|
-
return abortResult([]);
|
|
2344
|
+
const run = async () => {
|
|
2345
|
+
const messages = [{ role: "user", content: task }];
|
|
2346
|
+
function getPartialText(blocks) {
|
|
2347
|
+
return blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
2348
|
+
}
|
|
2349
|
+
function abortResult(blocks) {
|
|
2350
|
+
if (signal?.reason === "graceful") {
|
|
2351
|
+
const partial = getPartialText(blocks);
|
|
2352
|
+
return {
|
|
2353
|
+
text: partial ? `[INTERRUPTED - PARTIAL OUTPUT RETRIEVED] Note that partial output may include thinking text or other unfinalized decisions. It is NOT an authoritative response from this agent.
|
|
2354
|
+
|
|
2355
|
+
${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
2356
|
+
messages
|
|
2357
|
+
};
|
|
2358
|
+
}
|
|
2359
|
+
return { text: "Error: cancelled", messages };
|
|
2318
2360
|
}
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2361
|
+
let lastToolResult = "";
|
|
2362
|
+
while (true) {
|
|
2363
|
+
if (signal?.aborted) {
|
|
2364
|
+
return abortResult([]);
|
|
2365
|
+
}
|
|
2366
|
+
const contentBlocks = [];
|
|
2367
|
+
let thinkingStartedAt = 0;
|
|
2368
|
+
let stopReason = "end_turn";
|
|
2369
|
+
let currentToolNames = "";
|
|
2370
|
+
const statusWatcher = startStatusWatcher({
|
|
2371
|
+
apiConfig,
|
|
2372
|
+
getContext: () => ({
|
|
2373
|
+
assistantText: getPartialText(contentBlocks),
|
|
2374
|
+
lastToolName: currentToolNames || void 0,
|
|
2375
|
+
lastToolResult: lastToolResult || void 0
|
|
2376
|
+
}),
|
|
2377
|
+
onStatus: (label) => emit2({ type: "status", message: label }),
|
|
2378
|
+
signal
|
|
2379
|
+
});
|
|
2380
|
+
const fullSystem = `${system}
|
|
2334
2381
|
|
|
2335
2382
|
Current date/time: ${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}`;
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
contentBlocks.push({
|
|
2356
|
-
type: "text",
|
|
2357
|
-
text: event.text,
|
|
2358
|
-
startedAt: event.ts
|
|
2359
|
-
});
|
|
2360
|
-
}
|
|
2361
|
-
emit2({ type: "text", text: event.text });
|
|
2383
|
+
try {
|
|
2384
|
+
for await (const event of streamChatWithRetry(
|
|
2385
|
+
{
|
|
2386
|
+
...apiConfig,
|
|
2387
|
+
model,
|
|
2388
|
+
subAgentId,
|
|
2389
|
+
system: fullSystem,
|
|
2390
|
+
messages: cleanMessagesForApi(messages),
|
|
2391
|
+
tools: tools2,
|
|
2392
|
+
signal
|
|
2393
|
+
},
|
|
2394
|
+
{
|
|
2395
|
+
onRetry: (attempt) => emit2({
|
|
2396
|
+
type: "status",
|
|
2397
|
+
message: `Lost connection, retrying (attempt ${attempt + 2} of 3)...`
|
|
2398
|
+
})
|
|
2399
|
+
}
|
|
2400
|
+
)) {
|
|
2401
|
+
if (signal?.aborted) {
|
|
2362
2402
|
break;
|
|
2363
2403
|
}
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2404
|
+
switch (event.type) {
|
|
2405
|
+
case "text": {
|
|
2406
|
+
const lastBlock = contentBlocks.at(-1);
|
|
2407
|
+
if (lastBlock?.type === "text") {
|
|
2408
|
+
lastBlock.text += event.text;
|
|
2409
|
+
} else {
|
|
2410
|
+
contentBlocks.push({
|
|
2411
|
+
type: "text",
|
|
2412
|
+
text: event.text,
|
|
2413
|
+
startedAt: event.ts
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2416
|
+
emit2({ type: "text", text: event.text });
|
|
2417
|
+
break;
|
|
2367
2418
|
}
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
type: "thinking",
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2419
|
+
case "thinking":
|
|
2420
|
+
if (!thinkingStartedAt) {
|
|
2421
|
+
thinkingStartedAt = event.ts;
|
|
2422
|
+
}
|
|
2423
|
+
emit2({ type: "thinking", text: event.text });
|
|
2424
|
+
break;
|
|
2425
|
+
case "thinking_complete":
|
|
2426
|
+
contentBlocks.push({
|
|
2427
|
+
type: "thinking",
|
|
2428
|
+
thinking: event.thinking,
|
|
2429
|
+
signature: event.signature,
|
|
2430
|
+
startedAt: thinkingStartedAt,
|
|
2431
|
+
completedAt: event.ts
|
|
2432
|
+
});
|
|
2433
|
+
thinkingStartedAt = 0;
|
|
2434
|
+
break;
|
|
2435
|
+
case "tool_use":
|
|
2436
|
+
contentBlocks.push({
|
|
2437
|
+
type: "tool",
|
|
2438
|
+
id: event.id,
|
|
2439
|
+
name: event.name,
|
|
2440
|
+
input: event.input,
|
|
2441
|
+
startedAt: Date.now()
|
|
2442
|
+
});
|
|
2443
|
+
emit2({
|
|
2444
|
+
type: "tool_start",
|
|
2445
|
+
id: event.id,
|
|
2446
|
+
name: event.name,
|
|
2447
|
+
input: event.input
|
|
2448
|
+
});
|
|
2449
|
+
break;
|
|
2450
|
+
case "done":
|
|
2451
|
+
stopReason = event.stopReason;
|
|
2452
|
+
break;
|
|
2453
|
+
case "error":
|
|
2454
|
+
return { text: `Error: ${event.error}`, messages };
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
} catch (err) {
|
|
2458
|
+
if (!signal?.aborted) {
|
|
2459
|
+
throw err;
|
|
2400
2460
|
}
|
|
2401
2461
|
}
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2462
|
+
if (signal?.aborted) {
|
|
2463
|
+
statusWatcher.stop();
|
|
2464
|
+
return abortResult(contentBlocks);
|
|
2405
2465
|
}
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
currentToolNames = toolCalls.map((tc) => tc.name).join(", ");
|
|
2429
|
-
const results = await Promise.all(
|
|
2430
|
-
toolCalls.map(async (tc) => {
|
|
2431
|
-
if (signal?.aborted) {
|
|
2432
|
-
return { id: tc.id, result: "Error: cancelled", isError: true };
|
|
2433
|
-
}
|
|
2434
|
-
let settle;
|
|
2435
|
-
const resultPromise = new Promise((res) => {
|
|
2436
|
-
settle = (result, isError) => res({ id: tc.id, result, isError });
|
|
2437
|
-
});
|
|
2438
|
-
let toolAbort = new AbortController();
|
|
2439
|
-
const cascadeAbort = () => toolAbort.abort();
|
|
2440
|
-
signal?.addEventListener("abort", cascadeAbort, { once: true });
|
|
2441
|
-
let settled = false;
|
|
2442
|
-
const safeSettle = (result, isError) => {
|
|
2443
|
-
if (settled) {
|
|
2444
|
-
return;
|
|
2466
|
+
messages.push({
|
|
2467
|
+
role: "assistant",
|
|
2468
|
+
content: contentBlocks
|
|
2469
|
+
});
|
|
2470
|
+
const toolCalls = contentBlocks.filter(
|
|
2471
|
+
(b) => b.type === "tool"
|
|
2472
|
+
);
|
|
2473
|
+
if (stopReason !== "tool_use" || toolCalls.length === 0) {
|
|
2474
|
+
statusWatcher.stop();
|
|
2475
|
+
const text = getPartialText(contentBlocks);
|
|
2476
|
+
return { text, messages };
|
|
2477
|
+
}
|
|
2478
|
+
log.info("Sub-agent executing tools", {
|
|
2479
|
+
parentToolId,
|
|
2480
|
+
count: toolCalls.length,
|
|
2481
|
+
tools: toolCalls.map((tc) => tc.name)
|
|
2482
|
+
});
|
|
2483
|
+
currentToolNames = toolCalls.map((tc) => tc.name).join(", ");
|
|
2484
|
+
const results = await Promise.all(
|
|
2485
|
+
toolCalls.map(async (tc) => {
|
|
2486
|
+
if (signal?.aborted) {
|
|
2487
|
+
return { id: tc.id, result: "Error: cancelled", isError: true };
|
|
2445
2488
|
}
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2489
|
+
let settle;
|
|
2490
|
+
const resultPromise = new Promise((res) => {
|
|
2491
|
+
settle = (result, isError) => res({ id: tc.id, result, isError });
|
|
2492
|
+
});
|
|
2493
|
+
let toolAbort = new AbortController();
|
|
2494
|
+
const cascadeAbort = () => toolAbort.abort();
|
|
2495
|
+
signal?.addEventListener("abort", cascadeAbort, { once: true });
|
|
2496
|
+
let settled = false;
|
|
2497
|
+
const safeSettle = (result, isError) => {
|
|
2498
|
+
if (settled) {
|
|
2499
|
+
return;
|
|
2500
|
+
}
|
|
2501
|
+
settled = true;
|
|
2502
|
+
signal?.removeEventListener("abort", cascadeAbort);
|
|
2503
|
+
settle(result, isError);
|
|
2504
|
+
};
|
|
2505
|
+
const run2 = async (input) => {
|
|
2506
|
+
try {
|
|
2507
|
+
let result;
|
|
2508
|
+
if (externalTools.has(tc.name) && resolveExternalTool) {
|
|
2509
|
+
result = await resolveExternalTool(tc.id, tc.name, input);
|
|
2510
|
+
} else {
|
|
2511
|
+
const onLog = (line) => emit2({
|
|
2512
|
+
type: "tool_input_delta",
|
|
2513
|
+
id: tc.id,
|
|
2514
|
+
name: tc.name,
|
|
2515
|
+
result: line
|
|
2516
|
+
});
|
|
2517
|
+
result = await executeTool2(tc.name, input, tc.id, onLog);
|
|
2518
|
+
}
|
|
2519
|
+
safeSettle(result, result.startsWith("Error"));
|
|
2520
|
+
} catch (err) {
|
|
2521
|
+
safeSettle(`Error: ${err.message}`, true);
|
|
2522
|
+
}
|
|
2523
|
+
};
|
|
2524
|
+
const entry = {
|
|
2525
|
+
id: tc.id,
|
|
2526
|
+
name: tc.name,
|
|
2527
|
+
input: tc.input,
|
|
2528
|
+
parentToolId,
|
|
2529
|
+
abortController: toolAbort,
|
|
2530
|
+
startedAt: Date.now(),
|
|
2531
|
+
settle: safeSettle,
|
|
2532
|
+
rerun: (newInput) => {
|
|
2533
|
+
settled = false;
|
|
2534
|
+
toolAbort = new AbortController();
|
|
2535
|
+
signal?.addEventListener("abort", () => toolAbort.abort(), {
|
|
2536
|
+
once: true
|
|
2461
2537
|
});
|
|
2462
|
-
|
|
2538
|
+
entry.abortController = toolAbort;
|
|
2539
|
+
entry.input = newInput;
|
|
2540
|
+
run2(newInput);
|
|
2463
2541
|
}
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
settled = false;
|
|
2479
|
-
toolAbort = new AbortController();
|
|
2480
|
-
signal?.addEventListener("abort", () => toolAbort.abort(), {
|
|
2481
|
-
once: true
|
|
2482
|
-
});
|
|
2483
|
-
entry.abortController = toolAbort;
|
|
2484
|
-
entry.input = newInput;
|
|
2485
|
-
run(newInput);
|
|
2486
|
-
}
|
|
2487
|
-
};
|
|
2488
|
-
toolRegistry?.register(entry);
|
|
2489
|
-
run(tc.input);
|
|
2490
|
-
const r = await resultPromise;
|
|
2491
|
-
toolRegistry?.unregister(tc.id);
|
|
2492
|
-
emit2({
|
|
2493
|
-
type: "tool_done",
|
|
2494
|
-
id: tc.id,
|
|
2495
|
-
name: tc.name,
|
|
2496
|
-
result: r.result,
|
|
2497
|
-
isError: r.isError
|
|
2498
|
-
});
|
|
2499
|
-
return r;
|
|
2500
|
-
})
|
|
2501
|
-
);
|
|
2502
|
-
statusWatcher.stop();
|
|
2503
|
-
lastToolResult = results.at(-1)?.result ?? "";
|
|
2504
|
-
for (const r of results) {
|
|
2505
|
-
const block = contentBlocks.find(
|
|
2506
|
-
(b) => b.type === "tool" && b.id === r.id
|
|
2542
|
+
};
|
|
2543
|
+
toolRegistry?.register(entry);
|
|
2544
|
+
run2(tc.input);
|
|
2545
|
+
const r = await resultPromise;
|
|
2546
|
+
toolRegistry?.unregister(tc.id);
|
|
2547
|
+
emit2({
|
|
2548
|
+
type: "tool_done",
|
|
2549
|
+
id: tc.id,
|
|
2550
|
+
name: tc.name,
|
|
2551
|
+
result: r.result,
|
|
2552
|
+
isError: r.isError
|
|
2553
|
+
});
|
|
2554
|
+
return r;
|
|
2555
|
+
})
|
|
2507
2556
|
);
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
block
|
|
2557
|
+
statusWatcher.stop();
|
|
2558
|
+
lastToolResult = results.at(-1)?.result ?? "";
|
|
2559
|
+
for (const r of results) {
|
|
2560
|
+
const block = contentBlocks.find(
|
|
2561
|
+
(b) => b.type === "tool" && b.id === r.id
|
|
2562
|
+
);
|
|
2563
|
+
if (block?.type === "tool") {
|
|
2564
|
+
block.result = r.result;
|
|
2565
|
+
block.isError = r.isError;
|
|
2566
|
+
block.completedAt = Date.now();
|
|
2567
|
+
}
|
|
2568
|
+
messages.push({
|
|
2569
|
+
role: "user",
|
|
2570
|
+
content: r.result,
|
|
2571
|
+
toolCallId: r.id,
|
|
2572
|
+
isToolError: r.isError
|
|
2573
|
+
});
|
|
2512
2574
|
}
|
|
2513
|
-
messages.push({
|
|
2514
|
-
role: "user",
|
|
2515
|
-
content: r.result,
|
|
2516
|
-
toolCallId: r.id,
|
|
2517
|
-
isToolError: r.isError
|
|
2518
|
-
});
|
|
2519
2575
|
}
|
|
2576
|
+
};
|
|
2577
|
+
if (!background) {
|
|
2578
|
+
return run();
|
|
2520
2579
|
}
|
|
2580
|
+
const ack = await generateBackgroundAck({
|
|
2581
|
+
apiConfig,
|
|
2582
|
+
agentName: subAgentId || "agent",
|
|
2583
|
+
task
|
|
2584
|
+
});
|
|
2585
|
+
run().then((finalResult) => onBackgroundComplete?.(finalResult)).catch(
|
|
2586
|
+
(err) => onBackgroundComplete?.({ text: `Error: ${err.message}`, messages: [] })
|
|
2587
|
+
);
|
|
2588
|
+
return { text: ack, messages: [], backgrounded: true };
|
|
2521
2589
|
}
|
|
2522
2590
|
|
|
2523
2591
|
// src/subagents/browserAutomation/tools.ts
|
|
@@ -2870,9 +2938,7 @@ async function execute3(input, onLog) {
|
|
|
2870
2938
|
`mindstudio analyze-image --prompt ${JSON.stringify(analysisPrompt)} --image-url ${JSON.stringify(imageUrl)} --output-key analysis --no-meta`,
|
|
2871
2939
|
{ timeout: 2e5, onLog }
|
|
2872
2940
|
);
|
|
2873
|
-
return
|
|
2874
|
-
|
|
2875
|
-
${analysis}`;
|
|
2941
|
+
return JSON.stringify({ url: imageUrl, analysis });
|
|
2876
2942
|
}
|
|
2877
2943
|
|
|
2878
2944
|
// src/subagents/designExpert/tools/analyzeImage.ts
|
|
@@ -2884,7 +2950,7 @@ __export(analyzeImage_exports, {
|
|
|
2884
2950
|
var DEFAULT_PROMPT = "Describe everything visible in this image \u2014 every element, its position, its size relative to the frame, its colors, its content. Be comprhensive, thorough and spatial. After the inventory, note anything that looks visually broken (overlapping elements, clipped text, misaligned components). Respond only with your analysis as Markdown and absolutely no other text. Do not use emojis - use unicode if you need symbols.";
|
|
2885
2951
|
var definition4 = {
|
|
2886
2952
|
name: "analyzeImage",
|
|
2887
|
-
description: "Analyze an image by URL. Returns
|
|
2953
|
+
description: "Analyze an image by URL using a vision model. Returns an objective description of what is visible \u2014 shapes, colors, layout, text, artifacts. Use for factual inventory of image contents, not for subjective design judgment - the vision model providing the analysis has no sense of design. You are the design expert - use the analysis tool for factual inventory, then apply your own expertise for quality and suitability assessments.",
|
|
2888
2954
|
inputSchema: {
|
|
2889
2955
|
type: "object",
|
|
2890
2956
|
properties: {
|
|
@@ -2982,10 +3048,12 @@ async function seedreamGenerate(opts) {
|
|
|
2982
3048
|
}
|
|
2983
3049
|
}
|
|
2984
3050
|
}));
|
|
2985
|
-
const batchResult = await runCli(
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
3051
|
+
const batchResult = await runCli(`mindstudio batch --no-meta`, {
|
|
3052
|
+
jsonLogs: true,
|
|
3053
|
+
timeout: 2e5,
|
|
3054
|
+
onLog,
|
|
3055
|
+
stdin: JSON.stringify(steps)
|
|
3056
|
+
});
|
|
2989
3057
|
try {
|
|
2990
3058
|
const parsed = JSON.parse(batchResult);
|
|
2991
3059
|
imageUrls = parsed.map(
|
|
@@ -3358,6 +3426,10 @@ var designExpertTool = {
|
|
|
3358
3426
|
task: {
|
|
3359
3427
|
type: "string",
|
|
3360
3428
|
description: "What you need, in natural language. Include context about the project when relevant."
|
|
3429
|
+
},
|
|
3430
|
+
background: {
|
|
3431
|
+
type: "boolean",
|
|
3432
|
+
description: "Run in background \u2014 returns immediately and doesn't block while continuing to do work in the background. Reports results when finished working."
|
|
3361
3433
|
}
|
|
3362
3434
|
},
|
|
3363
3435
|
required: ["task"]
|
|
@@ -3380,7 +3452,16 @@ var designExpertTool = {
|
|
|
3380
3452
|
parentToolId: context.toolCallId,
|
|
3381
3453
|
onEvent: context.onEvent,
|
|
3382
3454
|
resolveExternalTool: context.resolveExternalTool,
|
|
3383
|
-
toolRegistry: context.toolRegistry
|
|
3455
|
+
toolRegistry: context.toolRegistry,
|
|
3456
|
+
background: input.background,
|
|
3457
|
+
onBackgroundComplete: input.background ? (bgResult) => {
|
|
3458
|
+
context.onBackgroundComplete?.(
|
|
3459
|
+
context.toolCallId,
|
|
3460
|
+
"visualDesignExpert",
|
|
3461
|
+
bgResult.text,
|
|
3462
|
+
bgResult.messages
|
|
3463
|
+
);
|
|
3464
|
+
} : void 0
|
|
3384
3465
|
});
|
|
3385
3466
|
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
3386
3467
|
return result.text;
|
|
@@ -3641,6 +3722,10 @@ var productVisionTool = {
|
|
|
3641
3722
|
task: {
|
|
3642
3723
|
type: "string",
|
|
3643
3724
|
description: "Brief description of what happened or what is needed. Do not specify how many items to create, what topics to cover, or how to think. The agent reads the spec files and decides for itself."
|
|
3725
|
+
},
|
|
3726
|
+
background: {
|
|
3727
|
+
type: "boolean",
|
|
3728
|
+
description: "Run in background \u2014 returns immediately and doesn't block while continuing to do work in the background. Reports results when finished working."
|
|
3644
3729
|
}
|
|
3645
3730
|
},
|
|
3646
3731
|
required: ["task"]
|
|
@@ -3663,7 +3748,16 @@ var productVisionTool = {
|
|
|
3663
3748
|
parentToolId: context.toolCallId,
|
|
3664
3749
|
onEvent: context.onEvent,
|
|
3665
3750
|
resolveExternalTool: context.resolveExternalTool,
|
|
3666
|
-
toolRegistry: context.toolRegistry
|
|
3751
|
+
toolRegistry: context.toolRegistry,
|
|
3752
|
+
background: input.background,
|
|
3753
|
+
onBackgroundComplete: input.background ? (bgResult) => {
|
|
3754
|
+
context.onBackgroundComplete?.(
|
|
3755
|
+
context.toolCallId,
|
|
3756
|
+
"productVision",
|
|
3757
|
+
bgResult.text,
|
|
3758
|
+
bgResult.messages
|
|
3759
|
+
);
|
|
3760
|
+
} : void 0
|
|
3667
3761
|
});
|
|
3668
3762
|
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
3669
3763
|
return result.text;
|
|
@@ -3840,7 +3934,7 @@ function getCommonTools() {
|
|
|
3840
3934
|
askMindStudioSdkTool,
|
|
3841
3935
|
fetchUrlTool,
|
|
3842
3936
|
searchGoogleTool,
|
|
3843
|
-
|
|
3937
|
+
setProjectMetadataTool,
|
|
3844
3938
|
designExpertTool,
|
|
3845
3939
|
productVisionTool,
|
|
3846
3940
|
codeSanityCheckTool
|
|
@@ -4167,7 +4261,7 @@ var EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
|
4167
4261
|
"runScenario",
|
|
4168
4262
|
"runMethod",
|
|
4169
4263
|
"browserCommand",
|
|
4170
|
-
"
|
|
4264
|
+
"setProjectMetadata"
|
|
4171
4265
|
]);
|
|
4172
4266
|
function createAgentState() {
|
|
4173
4267
|
return { messages: [] };
|
|
@@ -4185,7 +4279,8 @@ async function runTurn(params) {
|
|
|
4185
4279
|
onEvent,
|
|
4186
4280
|
resolveExternalTool,
|
|
4187
4281
|
hidden,
|
|
4188
|
-
toolRegistry
|
|
4282
|
+
toolRegistry,
|
|
4283
|
+
onBackgroundComplete
|
|
4189
4284
|
} = params;
|
|
4190
4285
|
const tools2 = getToolDefinitions(onboardingState);
|
|
4191
4286
|
log.info("Turn started", {
|
|
@@ -4198,8 +4293,7 @@ async function runTurn(params) {
|
|
|
4198
4293
|
}
|
|
4199
4294
|
});
|
|
4200
4295
|
onEvent({ type: "turn_started" });
|
|
4201
|
-
const
|
|
4202
|
-
const userMsg = { role: "user", content: cleanMessage };
|
|
4296
|
+
const userMsg = { role: "user", content: userMessage };
|
|
4203
4297
|
if (hidden) {
|
|
4204
4298
|
userMsg.hidden = true;
|
|
4205
4299
|
}
|
|
@@ -4213,7 +4307,7 @@ async function runTurn(params) {
|
|
|
4213
4307
|
state.messages.push(userMsg);
|
|
4214
4308
|
const STATUS_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
|
|
4215
4309
|
"setProjectOnboardingState",
|
|
4216
|
-
"
|
|
4310
|
+
"setProjectMetadata",
|
|
4217
4311
|
"clearSyncStatus",
|
|
4218
4312
|
"editsFinished"
|
|
4219
4313
|
]);
|
|
@@ -4400,7 +4494,8 @@ async function runTurn(params) {
|
|
|
4400
4494
|
id: event.id,
|
|
4401
4495
|
name: event.name,
|
|
4402
4496
|
input: event.input,
|
|
4403
|
-
startedAt: event.ts
|
|
4497
|
+
startedAt: event.ts,
|
|
4498
|
+
...event.input.background && { background: true }
|
|
4404
4499
|
});
|
|
4405
4500
|
const acc = toolInputAccumulators.get(event.id);
|
|
4406
4501
|
const tool = getToolByName(event.name);
|
|
@@ -4522,6 +4617,7 @@ async function runTurn(params) {
|
|
|
4522
4617
|
toolCallId: tc.id,
|
|
4523
4618
|
subAgentMessages,
|
|
4524
4619
|
toolRegistry,
|
|
4620
|
+
onBackgroundComplete,
|
|
4525
4621
|
onLog: (line) => wrappedOnEvent({
|
|
4526
4622
|
type: "tool_input_delta",
|
|
4527
4623
|
id: tc.id,
|
|
@@ -4745,6 +4841,54 @@ async function startHeadless(opts = {}) {
|
|
|
4745
4841
|
const pendingTools = /* @__PURE__ */ new Map();
|
|
4746
4842
|
const earlyResults = /* @__PURE__ */ new Map();
|
|
4747
4843
|
const toolRegistry = new ToolRegistry();
|
|
4844
|
+
const backgroundQueue = [];
|
|
4845
|
+
function flushBackgroundQueue() {
|
|
4846
|
+
if (backgroundQueue.length === 0) {
|
|
4847
|
+
return;
|
|
4848
|
+
}
|
|
4849
|
+
const results = backgroundQueue.splice(0);
|
|
4850
|
+
const xmlParts = results.map(
|
|
4851
|
+
(r) => `<tool_result id="${r.toolCallId}" name="${r.name}">
|
|
4852
|
+
${r.result}
|
|
4853
|
+
</tool_result>`
|
|
4854
|
+
).join("\n\n");
|
|
4855
|
+
const message = `@@automated::background_results@@
|
|
4856
|
+
<background_results>
|
|
4857
|
+
${xmlParts}
|
|
4858
|
+
</background_results>`;
|
|
4859
|
+
handleMessage({ action: "message", text: message }, void 0);
|
|
4860
|
+
}
|
|
4861
|
+
function onBackgroundComplete(toolCallId, name, result, subAgentMessages) {
|
|
4862
|
+
for (const msg of state.messages) {
|
|
4863
|
+
if (!Array.isArray(msg.content)) {
|
|
4864
|
+
continue;
|
|
4865
|
+
}
|
|
4866
|
+
for (const block of msg.content) {
|
|
4867
|
+
if (block.type === "tool" && block.id === toolCallId) {
|
|
4868
|
+
block.backgroundResult = result;
|
|
4869
|
+
block.completedAt = Date.now();
|
|
4870
|
+
if (subAgentMessages) {
|
|
4871
|
+
block.subAgentMessages = subAgentMessages;
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4876
|
+
onEvent({
|
|
4877
|
+
type: "tool_background_complete",
|
|
4878
|
+
id: toolCallId,
|
|
4879
|
+
name,
|
|
4880
|
+
result
|
|
4881
|
+
});
|
|
4882
|
+
backgroundQueue.push({
|
|
4883
|
+
toolCallId,
|
|
4884
|
+
name,
|
|
4885
|
+
result,
|
|
4886
|
+
completedAt: Date.now()
|
|
4887
|
+
});
|
|
4888
|
+
if (!running) {
|
|
4889
|
+
flushBackgroundQueue();
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4748
4892
|
const USER_FACING_TOOLS = /* @__PURE__ */ new Set([
|
|
4749
4893
|
"promptUser",
|
|
4750
4894
|
"confirmDestructiveAction",
|
|
@@ -4785,6 +4929,7 @@ async function startHeadless(opts = {}) {
|
|
|
4785
4929
|
case "turn_done":
|
|
4786
4930
|
completedEmitted = true;
|
|
4787
4931
|
emit("completed", { success: true }, rid);
|
|
4932
|
+
setTimeout(() => flushBackgroundQueue(), 0);
|
|
4788
4933
|
return;
|
|
4789
4934
|
case "turn_cancelled":
|
|
4790
4935
|
completedEmitted = true;
|
|
@@ -4831,6 +4976,7 @@ async function startHeadless(opts = {}) {
|
|
|
4831
4976
|
name: e.name,
|
|
4832
4977
|
input: e.input,
|
|
4833
4978
|
...e.partial && { partial: true },
|
|
4979
|
+
...e.background && { background: true },
|
|
4834
4980
|
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4835
4981
|
},
|
|
4836
4982
|
rid
|
|
@@ -4849,6 +4995,18 @@ async function startHeadless(opts = {}) {
|
|
|
4849
4995
|
rid
|
|
4850
4996
|
);
|
|
4851
4997
|
return;
|
|
4998
|
+
case "tool_background_complete":
|
|
4999
|
+
emit(
|
|
5000
|
+
"tool_background_complete",
|
|
5001
|
+
{
|
|
5002
|
+
id: e.id,
|
|
5003
|
+
name: e.name,
|
|
5004
|
+
result: e.result,
|
|
5005
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
5006
|
+
},
|
|
5007
|
+
rid
|
|
5008
|
+
);
|
|
5009
|
+
return;
|
|
4852
5010
|
case "tool_stopped":
|
|
4853
5011
|
emit(
|
|
4854
5012
|
"tool_stopped",
|
|
@@ -4916,6 +5074,7 @@ async function startHeadless(opts = {}) {
|
|
|
4916
5074
|
}
|
|
4917
5075
|
let userMessage = parsed.text ?? "";
|
|
4918
5076
|
const isCommand = !!parsed.runCommand;
|
|
5077
|
+
const isHidden = isCommand || !!parsed.hidden;
|
|
4919
5078
|
if (parsed.runCommand === "sync") {
|
|
4920
5079
|
userMessage = loadActionPrompt("sync");
|
|
4921
5080
|
} else if (parsed.runCommand === "publish") {
|
|
@@ -4940,8 +5099,9 @@ async function startHeadless(opts = {}) {
|
|
|
4940
5099
|
signal: currentAbort.signal,
|
|
4941
5100
|
onEvent,
|
|
4942
5101
|
resolveExternalTool,
|
|
4943
|
-
hidden:
|
|
4944
|
-
toolRegistry
|
|
5102
|
+
hidden: isHidden,
|
|
5103
|
+
toolRegistry,
|
|
5104
|
+
onBackgroundComplete
|
|
4945
5105
|
});
|
|
4946
5106
|
if (!completedEmitted) {
|
|
4947
5107
|
emit(
|