@mindstudio-ai/remy 0.1.35 → 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 +723 -336
- package/dist/index.js +743 -348
- package/dist/prompt/compiled/media-cdn.md +1 -1
- 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: {
|
|
@@ -2109,7 +2145,7 @@ var runMethodTool = {
|
|
|
2109
2145
|
};
|
|
2110
2146
|
|
|
2111
2147
|
// src/tools/_helpers/screenshot.ts
|
|
2112
|
-
var SCREENSHOT_ANALYSIS_PROMPT = "Describe everything visible on screen from top to bottom \u2014 every element, its position, its size relative to the viewport, its colors, its content. Be thorough and spatial. After the inventory, note anything that looks visually broken (overlapping elements, clipped text, misaligned components).";
|
|
2148
|
+
var SCREENSHOT_ANALYSIS_PROMPT = "Describe everything visible on screen from top to bottom \u2014 every element, its position, its size relative to the viewport, its colors, its content. Be comprehensive, 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.";
|
|
2113
2149
|
async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
2114
2150
|
let prompt;
|
|
2115
2151
|
let onLog;
|
|
@@ -2167,9 +2203,94 @@ var screenshotTool = {
|
|
|
2167
2203
|
}
|
|
2168
2204
|
};
|
|
2169
2205
|
|
|
2206
|
+
// src/statusWatcher.ts
|
|
2207
|
+
function startStatusWatcher(config) {
|
|
2208
|
+
const { apiConfig, getContext, onStatus, interval = 3e3, signal } = config;
|
|
2209
|
+
let lastLabel = "";
|
|
2210
|
+
let inflight = false;
|
|
2211
|
+
let stopped = false;
|
|
2212
|
+
const url = `${apiConfig.baseUrl}/_internal/v2/agent/remy/generate-status`;
|
|
2213
|
+
async function tick() {
|
|
2214
|
+
if (stopped || signal?.aborted || inflight) {
|
|
2215
|
+
return;
|
|
2216
|
+
}
|
|
2217
|
+
inflight = true;
|
|
2218
|
+
try {
|
|
2219
|
+
const ctx = getContext();
|
|
2220
|
+
if (!ctx.assistantText && !ctx.lastToolName) {
|
|
2221
|
+
log.debug("Status watcher: no context, skipping");
|
|
2222
|
+
return;
|
|
2223
|
+
}
|
|
2224
|
+
log.debug("Status watcher: requesting label", {
|
|
2225
|
+
textLength: ctx.assistantText.length,
|
|
2226
|
+
lastToolName: ctx.lastToolName
|
|
2227
|
+
});
|
|
2228
|
+
const res = await fetch(url, {
|
|
2229
|
+
method: "POST",
|
|
2230
|
+
headers: {
|
|
2231
|
+
"Content-Type": "application/json",
|
|
2232
|
+
Authorization: `Bearer ${apiConfig.apiKey}`
|
|
2233
|
+
},
|
|
2234
|
+
body: JSON.stringify({
|
|
2235
|
+
assistantText: ctx.assistantText.slice(-500),
|
|
2236
|
+
lastToolName: ctx.lastToolName,
|
|
2237
|
+
lastToolResult: ctx.lastToolResult?.slice(-200),
|
|
2238
|
+
onboardingState: ctx.onboardingState,
|
|
2239
|
+
userMessage: ctx.userMessage?.slice(-200)
|
|
2240
|
+
}),
|
|
2241
|
+
signal
|
|
2242
|
+
});
|
|
2243
|
+
if (!res.ok) {
|
|
2244
|
+
log.debug("Status watcher: endpoint returned non-ok", {
|
|
2245
|
+
status: res.status
|
|
2246
|
+
});
|
|
2247
|
+
return;
|
|
2248
|
+
}
|
|
2249
|
+
const data = await res.json();
|
|
2250
|
+
if (!data.label) {
|
|
2251
|
+
log.debug("Status watcher: no label in response");
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
if (data.label === lastLabel) {
|
|
2255
|
+
log.debug("Status watcher: duplicate label, skipping", {
|
|
2256
|
+
label: data.label
|
|
2257
|
+
});
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
lastLabel = data.label;
|
|
2261
|
+
if (stopped) {
|
|
2262
|
+
return;
|
|
2263
|
+
}
|
|
2264
|
+
log.debug("Status watcher: emitting", { label: data.label });
|
|
2265
|
+
onStatus(data.label);
|
|
2266
|
+
} catch (err) {
|
|
2267
|
+
log.debug("Status watcher: error", { error: err?.message ?? "unknown" });
|
|
2268
|
+
} finally {
|
|
2269
|
+
inflight = false;
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
const timer = setInterval(tick, interval);
|
|
2273
|
+
tick().catch(() => {
|
|
2274
|
+
});
|
|
2275
|
+
log.debug("Status watcher started", { interval });
|
|
2276
|
+
return {
|
|
2277
|
+
stop() {
|
|
2278
|
+
stopped = true;
|
|
2279
|
+
clearInterval(timer);
|
|
2280
|
+
log.debug("Status watcher stopped");
|
|
2281
|
+
}
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2170
2285
|
// src/subagents/common/cleanMessages.ts
|
|
2171
2286
|
function cleanMessagesForApi(messages) {
|
|
2172
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
|
+
}
|
|
2173
2294
|
if (!Array.isArray(msg.content)) {
|
|
2174
2295
|
return msg;
|
|
2175
2296
|
}
|
|
@@ -2207,171 +2328,264 @@ async function runSubAgent(config) {
|
|
|
2207
2328
|
apiConfig,
|
|
2208
2329
|
model,
|
|
2209
2330
|
subAgentId,
|
|
2210
|
-
signal,
|
|
2331
|
+
signal: parentSignal,
|
|
2211
2332
|
parentToolId,
|
|
2212
2333
|
onEvent,
|
|
2213
|
-
resolveExternalTool
|
|
2334
|
+
resolveExternalTool,
|
|
2335
|
+
toolRegistry,
|
|
2336
|
+
background,
|
|
2337
|
+
onBackgroundComplete
|
|
2214
2338
|
} = config;
|
|
2339
|
+
const bgAbort = background ? new AbortController() : null;
|
|
2340
|
+
const signal = background ? bgAbort.signal : parentSignal;
|
|
2215
2341
|
const emit2 = (e) => {
|
|
2216
2342
|
onEvent({ ...e, parentToolId });
|
|
2217
2343
|
};
|
|
2218
|
-
const
|
|
2219
|
-
|
|
2220
|
-
|
|
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
|
+
}
|
|
2221
2359
|
return { text: "Error: cancelled", messages };
|
|
2222
2360
|
}
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
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}
|
|
2227
2381
|
|
|
2228
2382
|
Current date/time: ${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}`;
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
contentBlocks.push({
|
|
2249
|
-
type: "text",
|
|
2250
|
-
text: event.text,
|
|
2251
|
-
startedAt: event.ts
|
|
2252
|
-
});
|
|
2253
|
-
}
|
|
2254
|
-
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) {
|
|
2255
2402
|
break;
|
|
2256
2403
|
}
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
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;
|
|
2260
2418
|
}
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
type: "thinking",
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
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;
|
|
2293
2460
|
}
|
|
2294
2461
|
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2462
|
+
if (signal?.aborted) {
|
|
2463
|
+
statusWatcher.stop();
|
|
2464
|
+
return abortResult(contentBlocks);
|
|
2298
2465
|
}
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
if (signal?.aborted) {
|
|
2322
|
-
return { id: tc.id, result: "Error: cancelled", isError: true };
|
|
2323
|
-
}
|
|
2324
|
-
try {
|
|
2325
|
-
let result;
|
|
2326
|
-
if (externalTools.has(tc.name) && resolveExternalTool) {
|
|
2327
|
-
result = await resolveExternalTool(tc.id, tc.name, tc.input);
|
|
2328
|
-
} else {
|
|
2329
|
-
const onLog = (line) => emit2({
|
|
2330
|
-
type: "tool_input_delta",
|
|
2331
|
-
id: tc.id,
|
|
2332
|
-
name: tc.name,
|
|
2333
|
-
result: line
|
|
2334
|
-
});
|
|
2335
|
-
result = await executeTool2(tc.name, tc.input, tc.id, onLog);
|
|
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 };
|
|
2336
2488
|
}
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
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 = {
|
|
2340
2525
|
id: tc.id,
|
|
2341
2526
|
name: tc.name,
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
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
|
|
2537
|
+
});
|
|
2538
|
+
entry.abortController = toolAbort;
|
|
2539
|
+
entry.input = newInput;
|
|
2540
|
+
run2(newInput);
|
|
2541
|
+
}
|
|
2542
|
+
};
|
|
2543
|
+
toolRegistry?.register(entry);
|
|
2544
|
+
run2(tc.input);
|
|
2545
|
+
const r = await resultPromise;
|
|
2546
|
+
toolRegistry?.unregister(tc.id);
|
|
2348
2547
|
emit2({
|
|
2349
2548
|
type: "tool_done",
|
|
2350
2549
|
id: tc.id,
|
|
2351
2550
|
name: tc.name,
|
|
2352
|
-
result:
|
|
2353
|
-
isError:
|
|
2551
|
+
result: r.result,
|
|
2552
|
+
isError: r.isError
|
|
2354
2553
|
});
|
|
2355
|
-
return
|
|
2356
|
-
}
|
|
2357
|
-
})
|
|
2358
|
-
);
|
|
2359
|
-
for (const r of results) {
|
|
2360
|
-
const block = contentBlocks.find(
|
|
2361
|
-
(b) => b.type === "tool" && b.id === r.id
|
|
2554
|
+
return r;
|
|
2555
|
+
})
|
|
2362
2556
|
);
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
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
|
+
});
|
|
2366
2574
|
}
|
|
2367
|
-
messages.push({
|
|
2368
|
-
role: "user",
|
|
2369
|
-
content: r.result,
|
|
2370
|
-
toolCallId: r.id,
|
|
2371
|
-
isToolError: r.isError
|
|
2372
|
-
});
|
|
2373
2575
|
}
|
|
2576
|
+
};
|
|
2577
|
+
if (!background) {
|
|
2578
|
+
return run();
|
|
2374
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 };
|
|
2375
2589
|
}
|
|
2376
2590
|
|
|
2377
2591
|
// src/subagents/browserAutomation/tools.ts
|
|
@@ -2594,7 +2808,8 @@ var browserAutomationTool = {
|
|
|
2594
2808
|
}
|
|
2595
2809
|
}
|
|
2596
2810
|
return result2;
|
|
2597
|
-
}
|
|
2811
|
+
},
|
|
2812
|
+
toolRegistry: context.toolRegistry
|
|
2598
2813
|
});
|
|
2599
2814
|
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
2600
2815
|
return result.text;
|
|
@@ -2684,7 +2899,7 @@ Brief description of the types used on the page. If you can identify the actual
|
|
|
2684
2899
|
## Techniques
|
|
2685
2900
|
Identify the specific design moves that make this page interesting and unique, described in terms of how a designer with a technical background would write them down as notes in their notebook for inspiration. Focus only on the non-obvious, hard-to-think-of techniques \u2014 the things that make this page gallery-worthy. Skip basics like "high contrast CTA" or "generous whitespace" that any competent designer already knows.
|
|
2686
2901
|
|
|
2687
|
-
Respond only with
|
|
2902
|
+
Respond only with your analysis as Markdown and absolutely no other text. Do not use emojis - use unicode if you need symbols.
|
|
2688
2903
|
`;
|
|
2689
2904
|
var definition3 = {
|
|
2690
2905
|
name: "analyzeDesign",
|
|
@@ -2723,9 +2938,7 @@ async function execute3(input, onLog) {
|
|
|
2723
2938
|
`mindstudio analyze-image --prompt ${JSON.stringify(analysisPrompt)} --image-url ${JSON.stringify(imageUrl)} --output-key analysis --no-meta`,
|
|
2724
2939
|
{ timeout: 2e5, onLog }
|
|
2725
2940
|
);
|
|
2726
|
-
return
|
|
2727
|
-
|
|
2728
|
-
${analysis}`;
|
|
2941
|
+
return JSON.stringify({ url: imageUrl, analysis });
|
|
2729
2942
|
}
|
|
2730
2943
|
|
|
2731
2944
|
// src/subagents/designExpert/tools/analyzeImage.ts
|
|
@@ -2734,10 +2947,10 @@ __export(analyzeImage_exports, {
|
|
|
2734
2947
|
definition: () => definition4,
|
|
2735
2948
|
execute: () => execute4
|
|
2736
2949
|
});
|
|
2737
|
-
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 thorough and spatial. After the inventory, note anything that looks visually broken (overlapping elements, clipped text, misaligned components).";
|
|
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.";
|
|
2738
2951
|
var definition4 = {
|
|
2739
2952
|
name: "analyzeImage",
|
|
2740
|
-
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.",
|
|
2741
2954
|
inputSchema: {
|
|
2742
2955
|
type: "object",
|
|
2743
2956
|
properties: {
|
|
@@ -2801,7 +3014,7 @@ __export(generateImages_exports, {
|
|
|
2801
3014
|
});
|
|
2802
3015
|
|
|
2803
3016
|
// src/subagents/designExpert/tools/_seedream.ts
|
|
2804
|
-
var ANALYZE_PROMPT = "You are reviewing this image for a visual designer sourcing assets for a project. Describe: what the image depicts, the mood and color palette, how the lighting and composition work, whether there are any issues (unwanted text, artifacts, distortions), and how it could be used in a layout (hero background, feature section, card texture, etc). Be concise and practical.";
|
|
3017
|
+
var ANALYZE_PROMPT = "You are reviewing this image for a visual designer sourcing assets for a project. Describe: what the image depicts, the mood and color palette, how the lighting and composition work, whether there are any issues (unwanted text, artifacts, distortions), and how it could be used in a layout (hero background, feature section, card texture, etc). Be concise and practical. Respond only with your analysis as Markdown and absolutely no other text. Do not use emojis - use unicode if you need symbols.";
|
|
2805
3018
|
async function seedreamGenerate(opts) {
|
|
2806
3019
|
const { prompts, sourceImages, transparentBackground, onLog } = opts;
|
|
2807
3020
|
const width = opts.width || 2048;
|
|
@@ -2835,13 +3048,15 @@ async function seedreamGenerate(opts) {
|
|
|
2835
3048
|
}
|
|
2836
3049
|
}
|
|
2837
3050
|
}));
|
|
2838
|
-
const batchResult = await runCli(
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
3051
|
+
const batchResult = await runCli(`mindstudio batch --no-meta`, {
|
|
3052
|
+
jsonLogs: true,
|
|
3053
|
+
timeout: 2e5,
|
|
3054
|
+
onLog,
|
|
3055
|
+
stdin: JSON.stringify(steps)
|
|
3056
|
+
});
|
|
2842
3057
|
try {
|
|
2843
3058
|
const parsed = JSON.parse(batchResult);
|
|
2844
|
-
imageUrls = parsed.
|
|
3059
|
+
imageUrls = parsed.map(
|
|
2845
3060
|
(r) => r.output?.imageUrl ?? `Error: ${r.error}`
|
|
2846
3061
|
);
|
|
2847
3062
|
} catch {
|
|
@@ -3211,6 +3426,10 @@ var designExpertTool = {
|
|
|
3211
3426
|
task: {
|
|
3212
3427
|
type: "string",
|
|
3213
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."
|
|
3214
3433
|
}
|
|
3215
3434
|
},
|
|
3216
3435
|
required: ["task"]
|
|
@@ -3232,7 +3451,17 @@ var designExpertTool = {
|
|
|
3232
3451
|
signal: context.signal,
|
|
3233
3452
|
parentToolId: context.toolCallId,
|
|
3234
3453
|
onEvent: context.onEvent,
|
|
3235
|
-
resolveExternalTool: context.resolveExternalTool
|
|
3454
|
+
resolveExternalTool: context.resolveExternalTool,
|
|
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
|
|
3236
3465
|
});
|
|
3237
3466
|
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
3238
3467
|
return result.text;
|
|
@@ -3493,6 +3722,10 @@ var productVisionTool = {
|
|
|
3493
3722
|
task: {
|
|
3494
3723
|
type: "string",
|
|
3495
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."
|
|
3496
3729
|
}
|
|
3497
3730
|
},
|
|
3498
3731
|
required: ["task"]
|
|
@@ -3514,7 +3747,17 @@ var productVisionTool = {
|
|
|
3514
3747
|
signal: context.signal,
|
|
3515
3748
|
parentToolId: context.toolCallId,
|
|
3516
3749
|
onEvent: context.onEvent,
|
|
3517
|
-
resolveExternalTool: context.resolveExternalTool
|
|
3750
|
+
resolveExternalTool: context.resolveExternalTool,
|
|
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
|
|
3518
3761
|
});
|
|
3519
3762
|
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
3520
3763
|
return result.text;
|
|
@@ -3651,7 +3894,8 @@ var codeSanityCheckTool = {
|
|
|
3651
3894
|
signal: context.signal,
|
|
3652
3895
|
parentToolId: context.toolCallId,
|
|
3653
3896
|
onEvent: context.onEvent,
|
|
3654
|
-
resolveExternalTool: context.resolveExternalTool
|
|
3897
|
+
resolveExternalTool: context.resolveExternalTool,
|
|
3898
|
+
toolRegistry: context.toolRegistry
|
|
3655
3899
|
});
|
|
3656
3900
|
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
3657
3901
|
return result.text;
|
|
@@ -3690,7 +3934,7 @@ function getCommonTools() {
|
|
|
3690
3934
|
askMindStudioSdkTool,
|
|
3691
3935
|
fetchUrlTool,
|
|
3692
3936
|
searchGoogleTool,
|
|
3693
|
-
|
|
3937
|
+
setProjectMetadataTool,
|
|
3694
3938
|
designExpertTool,
|
|
3695
3939
|
productVisionTool,
|
|
3696
3940
|
codeSanityCheckTool
|
|
@@ -3971,85 +4215,6 @@ function parsePartialJson(jsonString) {
|
|
|
3971
4215
|
return parseAny();
|
|
3972
4216
|
}
|
|
3973
4217
|
|
|
3974
|
-
// src/statusWatcher.ts
|
|
3975
|
-
function startStatusWatcher(config) {
|
|
3976
|
-
const { apiConfig, getContext, onStatus, interval = 3e3, signal } = config;
|
|
3977
|
-
let lastLabel = "";
|
|
3978
|
-
let inflight = false;
|
|
3979
|
-
let stopped = false;
|
|
3980
|
-
const url = `${apiConfig.baseUrl}/_internal/v2/agent/remy/generate-status`;
|
|
3981
|
-
async function tick() {
|
|
3982
|
-
if (stopped || signal?.aborted || inflight) {
|
|
3983
|
-
return;
|
|
3984
|
-
}
|
|
3985
|
-
inflight = true;
|
|
3986
|
-
try {
|
|
3987
|
-
const ctx = getContext();
|
|
3988
|
-
if (!ctx.assistantText && !ctx.lastToolName) {
|
|
3989
|
-
log.debug("Status watcher: no context, skipping");
|
|
3990
|
-
return;
|
|
3991
|
-
}
|
|
3992
|
-
log.debug("Status watcher: requesting label", {
|
|
3993
|
-
textLength: ctx.assistantText.length,
|
|
3994
|
-
lastToolName: ctx.lastToolName
|
|
3995
|
-
});
|
|
3996
|
-
const res = await fetch(url, {
|
|
3997
|
-
method: "POST",
|
|
3998
|
-
headers: {
|
|
3999
|
-
"Content-Type": "application/json",
|
|
4000
|
-
Authorization: `Bearer ${apiConfig.apiKey}`
|
|
4001
|
-
},
|
|
4002
|
-
body: JSON.stringify({
|
|
4003
|
-
assistantText: ctx.assistantText.slice(-500),
|
|
4004
|
-
lastToolName: ctx.lastToolName,
|
|
4005
|
-
lastToolResult: ctx.lastToolResult?.slice(-200),
|
|
4006
|
-
onboardingState: ctx.onboardingState,
|
|
4007
|
-
userMessage: ctx.userMessage?.slice(-200)
|
|
4008
|
-
}),
|
|
4009
|
-
signal
|
|
4010
|
-
});
|
|
4011
|
-
if (!res.ok) {
|
|
4012
|
-
log.debug("Status watcher: endpoint returned non-ok", {
|
|
4013
|
-
status: res.status
|
|
4014
|
-
});
|
|
4015
|
-
return;
|
|
4016
|
-
}
|
|
4017
|
-
const data = await res.json();
|
|
4018
|
-
if (!data.label) {
|
|
4019
|
-
log.debug("Status watcher: no label in response");
|
|
4020
|
-
return;
|
|
4021
|
-
}
|
|
4022
|
-
if (data.label === lastLabel) {
|
|
4023
|
-
log.debug("Status watcher: duplicate label, skipping", {
|
|
4024
|
-
label: data.label
|
|
4025
|
-
});
|
|
4026
|
-
return;
|
|
4027
|
-
}
|
|
4028
|
-
lastLabel = data.label;
|
|
4029
|
-
if (stopped) {
|
|
4030
|
-
return;
|
|
4031
|
-
}
|
|
4032
|
-
log.debug("Status watcher: emitting", { label: data.label });
|
|
4033
|
-
onStatus(data.label);
|
|
4034
|
-
} catch (err) {
|
|
4035
|
-
log.debug("Status watcher: error", { error: err?.message ?? "unknown" });
|
|
4036
|
-
} finally {
|
|
4037
|
-
inflight = false;
|
|
4038
|
-
}
|
|
4039
|
-
}
|
|
4040
|
-
const timer = setInterval(tick, interval);
|
|
4041
|
-
tick().catch(() => {
|
|
4042
|
-
});
|
|
4043
|
-
log.debug("Status watcher started", { interval });
|
|
4044
|
-
return {
|
|
4045
|
-
stop() {
|
|
4046
|
-
stopped = true;
|
|
4047
|
-
clearInterval(timer);
|
|
4048
|
-
log.debug("Status watcher stopped");
|
|
4049
|
-
}
|
|
4050
|
-
};
|
|
4051
|
-
}
|
|
4052
|
-
|
|
4053
4218
|
// src/errors.ts
|
|
4054
4219
|
var patterns = [
|
|
4055
4220
|
[
|
|
@@ -4096,7 +4261,7 @@ var EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
|
4096
4261
|
"runScenario",
|
|
4097
4262
|
"runMethod",
|
|
4098
4263
|
"browserCommand",
|
|
4099
|
-
"
|
|
4264
|
+
"setProjectMetadata"
|
|
4100
4265
|
]);
|
|
4101
4266
|
function createAgentState() {
|
|
4102
4267
|
return { messages: [] };
|
|
@@ -4113,7 +4278,9 @@ async function runTurn(params) {
|
|
|
4113
4278
|
signal,
|
|
4114
4279
|
onEvent,
|
|
4115
4280
|
resolveExternalTool,
|
|
4116
|
-
hidden
|
|
4281
|
+
hidden,
|
|
4282
|
+
toolRegistry,
|
|
4283
|
+
onBackgroundComplete
|
|
4117
4284
|
} = params;
|
|
4118
4285
|
const tools2 = getToolDefinitions(onboardingState);
|
|
4119
4286
|
log.info("Turn started", {
|
|
@@ -4126,8 +4293,7 @@ async function runTurn(params) {
|
|
|
4126
4293
|
}
|
|
4127
4294
|
});
|
|
4128
4295
|
onEvent({ type: "turn_started" });
|
|
4129
|
-
const
|
|
4130
|
-
const userMsg = { role: "user", content: cleanMessage };
|
|
4296
|
+
const userMsg = { role: "user", content: userMessage };
|
|
4131
4297
|
if (hidden) {
|
|
4132
4298
|
userMsg.hidden = true;
|
|
4133
4299
|
}
|
|
@@ -4141,7 +4307,7 @@ async function runTurn(params) {
|
|
|
4141
4307
|
state.messages.push(userMsg);
|
|
4142
4308
|
const STATUS_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
|
|
4143
4309
|
"setProjectOnboardingState",
|
|
4144
|
-
"
|
|
4310
|
+
"setProjectMetadata",
|
|
4145
4311
|
"clearSyncStatus",
|
|
4146
4312
|
"editsFinished"
|
|
4147
4313
|
]);
|
|
@@ -4166,6 +4332,20 @@ async function runTurn(params) {
|
|
|
4166
4332
|
let thinkingStartedAt = 0;
|
|
4167
4333
|
const toolInputAccumulators = /* @__PURE__ */ new Map();
|
|
4168
4334
|
let stopReason = "end_turn";
|
|
4335
|
+
let subAgentText = "";
|
|
4336
|
+
let currentToolNames = "";
|
|
4337
|
+
const statusWatcher = startStatusWatcher({
|
|
4338
|
+
apiConfig,
|
|
4339
|
+
getContext: () => ({
|
|
4340
|
+
assistantText: subAgentText || getTextContent(contentBlocks).slice(-500),
|
|
4341
|
+
lastToolName: currentToolNames || getToolCalls(contentBlocks).filter((tc) => !STATUS_EXCLUDED_TOOLS.has(tc.name)).at(-1)?.name || lastCompletedTools || void 0,
|
|
4342
|
+
lastToolResult: lastCompletedResult || void 0,
|
|
4343
|
+
onboardingState,
|
|
4344
|
+
userMessage
|
|
4345
|
+
}),
|
|
4346
|
+
onStatus: (label) => onEvent({ type: "status", message: label }),
|
|
4347
|
+
signal
|
|
4348
|
+
});
|
|
4169
4349
|
async function handlePartialInput(acc, id, name, partial) {
|
|
4170
4350
|
const tool = getToolByName(name);
|
|
4171
4351
|
if (!tool?.streaming) {
|
|
@@ -4229,18 +4409,6 @@ async function runTurn(params) {
|
|
|
4229
4409
|
onEvent({ type: "tool_input_delta", id, name, result: content });
|
|
4230
4410
|
}
|
|
4231
4411
|
}
|
|
4232
|
-
const statusWatcher = startStatusWatcher({
|
|
4233
|
-
apiConfig,
|
|
4234
|
-
getContext: () => ({
|
|
4235
|
-
assistantText: getTextContent(contentBlocks).slice(-500),
|
|
4236
|
-
lastToolName: getToolCalls(contentBlocks).filter((tc) => !STATUS_EXCLUDED_TOOLS.has(tc.name)).at(-1)?.name || lastCompletedTools || void 0,
|
|
4237
|
-
lastToolResult: lastCompletedResult || void 0,
|
|
4238
|
-
onboardingState,
|
|
4239
|
-
userMessage
|
|
4240
|
-
}),
|
|
4241
|
-
onStatus: (label) => onEvent({ type: "status", message: label }),
|
|
4242
|
-
signal
|
|
4243
|
-
});
|
|
4244
4412
|
try {
|
|
4245
4413
|
for await (const event of streamChatWithRetry(
|
|
4246
4414
|
{
|
|
@@ -4326,7 +4494,8 @@ async function runTurn(params) {
|
|
|
4326
4494
|
id: event.id,
|
|
4327
4495
|
name: event.name,
|
|
4328
4496
|
input: event.input,
|
|
4329
|
-
startedAt: event.ts
|
|
4497
|
+
startedAt: event.ts,
|
|
4498
|
+
...event.input.background && { background: true }
|
|
4330
4499
|
});
|
|
4331
4500
|
const acc = toolInputAccumulators.get(event.id);
|
|
4332
4501
|
const tool = getToolByName(event.name);
|
|
@@ -4361,10 +4530,9 @@ async function runTurn(params) {
|
|
|
4361
4530
|
} else {
|
|
4362
4531
|
throw err;
|
|
4363
4532
|
}
|
|
4364
|
-
} finally {
|
|
4365
|
-
statusWatcher.stop();
|
|
4366
4533
|
}
|
|
4367
4534
|
if (signal?.aborted) {
|
|
4535
|
+
statusWatcher.stop();
|
|
4368
4536
|
if (contentBlocks.length > 0) {
|
|
4369
4537
|
contentBlocks.push({
|
|
4370
4538
|
type: "text",
|
|
@@ -4386,6 +4554,7 @@ async function runTurn(params) {
|
|
|
4386
4554
|
});
|
|
4387
4555
|
const toolCalls = getToolCalls(contentBlocks);
|
|
4388
4556
|
if (stopReason !== "tool_use" || toolCalls.length === 0) {
|
|
4557
|
+
statusWatcher.stop();
|
|
4389
4558
|
saveSession(state);
|
|
4390
4559
|
onEvent({ type: "turn_done" });
|
|
4391
4560
|
return;
|
|
@@ -4394,8 +4563,7 @@ async function runTurn(params) {
|
|
|
4394
4563
|
count: toolCalls.length,
|
|
4395
4564
|
tools: toolCalls.map((tc) => tc.name)
|
|
4396
4565
|
});
|
|
4397
|
-
|
|
4398
|
-
const origOnEvent = onEvent;
|
|
4566
|
+
currentToolNames = toolCalls.filter((tc) => !STATUS_EXCLUDED_TOOLS.has(tc.name)).map((tc) => tc.name).join(", ");
|
|
4399
4567
|
const wrappedOnEvent = (e) => {
|
|
4400
4568
|
if ("parentToolId" in e && e.parentToolId) {
|
|
4401
4569
|
if (e.type === "text") {
|
|
@@ -4404,86 +4572,104 @@ async function runTurn(params) {
|
|
|
4404
4572
|
subAgentText = `Using ${e.name}`;
|
|
4405
4573
|
}
|
|
4406
4574
|
}
|
|
4407
|
-
|
|
4575
|
+
onEvent(e);
|
|
4408
4576
|
};
|
|
4409
|
-
const toolStatusWatcher = startStatusWatcher({
|
|
4410
|
-
apiConfig,
|
|
4411
|
-
getContext: () => ({
|
|
4412
|
-
assistantText: subAgentText || getTextContent(contentBlocks).slice(-500),
|
|
4413
|
-
lastToolName: toolCalls.filter((tc) => !STATUS_EXCLUDED_TOOLS.has(tc.name)).map((tc) => tc.name).join(", ") || void 0,
|
|
4414
|
-
lastToolResult: lastCompletedResult || void 0,
|
|
4415
|
-
onboardingState,
|
|
4416
|
-
userMessage
|
|
4417
|
-
}),
|
|
4418
|
-
onStatus: (label) => origOnEvent({ type: "status", message: label }),
|
|
4419
|
-
signal
|
|
4420
|
-
});
|
|
4421
4577
|
const subAgentMessages = /* @__PURE__ */ new Map();
|
|
4422
4578
|
const results = await Promise.all(
|
|
4423
4579
|
toolCalls.map(async (tc) => {
|
|
4424
4580
|
if (signal?.aborted) {
|
|
4425
|
-
return {
|
|
4426
|
-
id: tc.id,
|
|
4427
|
-
result: "Error: cancelled",
|
|
4428
|
-
isError: true
|
|
4429
|
-
};
|
|
4581
|
+
return { id: tc.id, result: "Error: cancelled", isError: true };
|
|
4430
4582
|
}
|
|
4431
4583
|
const toolStart = Date.now();
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4584
|
+
let settle;
|
|
4585
|
+
const resultPromise = new Promise((res) => {
|
|
4586
|
+
settle = (result, isError) => res({ id: tc.id, result, isError });
|
|
4587
|
+
});
|
|
4588
|
+
let toolAbort = new AbortController();
|
|
4589
|
+
const cascadeAbort = () => toolAbort.abort();
|
|
4590
|
+
signal?.addEventListener("abort", cascadeAbort, { once: true });
|
|
4591
|
+
let settled = false;
|
|
4592
|
+
const safeSettle = (result, isError) => {
|
|
4593
|
+
if (settled) {
|
|
4594
|
+
return;
|
|
4595
|
+
}
|
|
4596
|
+
settled = true;
|
|
4597
|
+
signal?.removeEventListener("abort", cascadeAbort);
|
|
4598
|
+
settle(result, isError);
|
|
4599
|
+
};
|
|
4600
|
+
const run = async (input) => {
|
|
4601
|
+
try {
|
|
4602
|
+
let result;
|
|
4603
|
+
if (EXTERNAL_TOOLS.has(tc.name) && resolveExternalTool) {
|
|
4604
|
+
saveSession(state);
|
|
4605
|
+
log.info("Waiting for external tool result", {
|
|
4453
4606
|
name: tc.name,
|
|
4454
|
-
|
|
4455
|
-
})
|
|
4607
|
+
id: tc.id
|
|
4608
|
+
});
|
|
4609
|
+
result = await resolveExternalTool(tc.id, tc.name, input);
|
|
4610
|
+
} else {
|
|
4611
|
+
result = await executeTool(tc.name, input, {
|
|
4612
|
+
apiConfig,
|
|
4613
|
+
model,
|
|
4614
|
+
signal: toolAbort.signal,
|
|
4615
|
+
onEvent: wrappedOnEvent,
|
|
4616
|
+
resolveExternalTool,
|
|
4617
|
+
toolCallId: tc.id,
|
|
4618
|
+
subAgentMessages,
|
|
4619
|
+
toolRegistry,
|
|
4620
|
+
onBackgroundComplete,
|
|
4621
|
+
onLog: (line) => wrappedOnEvent({
|
|
4622
|
+
type: "tool_input_delta",
|
|
4623
|
+
id: tc.id,
|
|
4624
|
+
name: tc.name,
|
|
4625
|
+
result: line
|
|
4626
|
+
})
|
|
4627
|
+
});
|
|
4628
|
+
}
|
|
4629
|
+
safeSettle(result, result.startsWith("Error"));
|
|
4630
|
+
} catch (err) {
|
|
4631
|
+
safeSettle(`Error: ${err.message}`, true);
|
|
4632
|
+
}
|
|
4633
|
+
};
|
|
4634
|
+
const entry = {
|
|
4635
|
+
id: tc.id,
|
|
4636
|
+
name: tc.name,
|
|
4637
|
+
input: tc.input,
|
|
4638
|
+
abortController: toolAbort,
|
|
4639
|
+
startedAt: toolStart,
|
|
4640
|
+
settle: safeSettle,
|
|
4641
|
+
rerun: (newInput) => {
|
|
4642
|
+
settled = false;
|
|
4643
|
+
toolAbort = new AbortController();
|
|
4644
|
+
signal?.addEventListener("abort", () => toolAbort.abort(), {
|
|
4645
|
+
once: true
|
|
4456
4646
|
});
|
|
4647
|
+
entry.abortController = toolAbort;
|
|
4648
|
+
entry.input = newInput;
|
|
4649
|
+
run(newInput);
|
|
4457
4650
|
}
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
id: tc.id,
|
|
4478
|
-
name: tc.name,
|
|
4479
|
-
result: errorMsg,
|
|
4480
|
-
isError: true
|
|
4481
|
-
});
|
|
4482
|
-
return { id: tc.id, result: errorMsg, isError: true };
|
|
4483
|
-
}
|
|
4651
|
+
};
|
|
4652
|
+
toolRegistry?.register(entry);
|
|
4653
|
+
run(tc.input);
|
|
4654
|
+
const r = await resultPromise;
|
|
4655
|
+
toolRegistry?.unregister(tc.id);
|
|
4656
|
+
log.info("Tool completed", {
|
|
4657
|
+
name: tc.name,
|
|
4658
|
+
elapsed: `${Date.now() - toolStart}ms`,
|
|
4659
|
+
isError: r.isError,
|
|
4660
|
+
resultLength: r.result.length
|
|
4661
|
+
});
|
|
4662
|
+
onEvent({
|
|
4663
|
+
type: "tool_done",
|
|
4664
|
+
id: tc.id,
|
|
4665
|
+
name: tc.name,
|
|
4666
|
+
result: r.result,
|
|
4667
|
+
isError: r.isError
|
|
4668
|
+
});
|
|
4669
|
+
return r;
|
|
4484
4670
|
})
|
|
4485
4671
|
);
|
|
4486
|
-
|
|
4672
|
+
statusWatcher.stop();
|
|
4487
4673
|
for (const r of results) {
|
|
4488
4674
|
const block = contentBlocks.find(
|
|
4489
4675
|
(b) => b.type === "tool" && b.id === r.id
|
|
@@ -4491,6 +4677,7 @@ async function runTurn(params) {
|
|
|
4491
4677
|
if (block?.type === "tool") {
|
|
4492
4678
|
block.result = r.result;
|
|
4493
4679
|
block.isError = r.isError;
|
|
4680
|
+
block.completedAt = Date.now();
|
|
4494
4681
|
const msgs = subAgentMessages.get(r.id);
|
|
4495
4682
|
if (msgs) {
|
|
4496
4683
|
block.subAgentMessages = msgs;
|
|
@@ -4515,6 +4702,78 @@ async function runTurn(params) {
|
|
|
4515
4702
|
}
|
|
4516
4703
|
}
|
|
4517
4704
|
|
|
4705
|
+
// src/toolRegistry.ts
|
|
4706
|
+
var ToolRegistry = class {
|
|
4707
|
+
entries = /* @__PURE__ */ new Map();
|
|
4708
|
+
onEvent;
|
|
4709
|
+
register(entry) {
|
|
4710
|
+
this.entries.set(entry.id, entry);
|
|
4711
|
+
}
|
|
4712
|
+
unregister(id) {
|
|
4713
|
+
this.entries.delete(id);
|
|
4714
|
+
}
|
|
4715
|
+
get(id) {
|
|
4716
|
+
return this.entries.get(id);
|
|
4717
|
+
}
|
|
4718
|
+
/**
|
|
4719
|
+
* Stop a running tool.
|
|
4720
|
+
*
|
|
4721
|
+
* - graceful: abort and settle with [INTERRUPTED] + partial result
|
|
4722
|
+
* - hard: abort and settle with a generic error
|
|
4723
|
+
*
|
|
4724
|
+
* Returns true if the tool was found and stopped.
|
|
4725
|
+
*/
|
|
4726
|
+
stop(id, mode) {
|
|
4727
|
+
const entry = this.entries.get(id);
|
|
4728
|
+
if (!entry) {
|
|
4729
|
+
return false;
|
|
4730
|
+
}
|
|
4731
|
+
entry.abortController.abort(mode);
|
|
4732
|
+
if (mode === "graceful") {
|
|
4733
|
+
const partial = entry.getPartialResult?.() ?? "";
|
|
4734
|
+
const result = partial ? `[INTERRUPTED]
|
|
4735
|
+
|
|
4736
|
+
${partial}` : "[INTERRUPTED] Tool execution was stopped.";
|
|
4737
|
+
entry.settle(result, false);
|
|
4738
|
+
} else {
|
|
4739
|
+
entry.settle("Error: tool was cancelled", true);
|
|
4740
|
+
}
|
|
4741
|
+
this.onEvent?.({
|
|
4742
|
+
type: "tool_stopped",
|
|
4743
|
+
id: entry.id,
|
|
4744
|
+
name: entry.name,
|
|
4745
|
+
mode,
|
|
4746
|
+
...entry.parentToolId && { parentToolId: entry.parentToolId }
|
|
4747
|
+
});
|
|
4748
|
+
this.entries.delete(id);
|
|
4749
|
+
return true;
|
|
4750
|
+
}
|
|
4751
|
+
/**
|
|
4752
|
+
* Restart a running tool with the same or patched input.
|
|
4753
|
+
* The original controllable promise stays pending and settles
|
|
4754
|
+
* when the new execution finishes.
|
|
4755
|
+
*
|
|
4756
|
+
* Returns true if the tool was found and restarted.
|
|
4757
|
+
*/
|
|
4758
|
+
restart(id, patchedInput) {
|
|
4759
|
+
const entry = this.entries.get(id);
|
|
4760
|
+
if (!entry) {
|
|
4761
|
+
return false;
|
|
4762
|
+
}
|
|
4763
|
+
entry.abortController.abort("restart");
|
|
4764
|
+
const newInput = patchedInput ? { ...entry.input, ...patchedInput } : entry.input;
|
|
4765
|
+
this.onEvent?.({
|
|
4766
|
+
type: "tool_restarted",
|
|
4767
|
+
id: entry.id,
|
|
4768
|
+
name: entry.name,
|
|
4769
|
+
input: newInput,
|
|
4770
|
+
...entry.parentToolId && { parentToolId: entry.parentToolId }
|
|
4771
|
+
});
|
|
4772
|
+
entry.rerun(newInput);
|
|
4773
|
+
return true;
|
|
4774
|
+
}
|
|
4775
|
+
};
|
|
4776
|
+
|
|
4518
4777
|
// src/headless.ts
|
|
4519
4778
|
function loadActionPrompt(name) {
|
|
4520
4779
|
return readAsset("prompt", "actions", `${name}.md`);
|
|
@@ -4581,6 +4840,55 @@ async function startHeadless(opts = {}) {
|
|
|
4581
4840
|
const EXTERNAL_TOOL_TIMEOUT_MS = 3e5;
|
|
4582
4841
|
const pendingTools = /* @__PURE__ */ new Map();
|
|
4583
4842
|
const earlyResults = /* @__PURE__ */ new Map();
|
|
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
|
+
}
|
|
4584
4892
|
const USER_FACING_TOOLS = /* @__PURE__ */ new Set([
|
|
4585
4893
|
"promptUser",
|
|
4586
4894
|
"confirmDestructiveAction",
|
|
@@ -4621,6 +4929,7 @@ async function startHeadless(opts = {}) {
|
|
|
4621
4929
|
case "turn_done":
|
|
4622
4930
|
completedEmitted = true;
|
|
4623
4931
|
emit("completed", { success: true }, rid);
|
|
4932
|
+
setTimeout(() => flushBackgroundQueue(), 0);
|
|
4624
4933
|
return;
|
|
4625
4934
|
case "turn_cancelled":
|
|
4626
4935
|
completedEmitted = true;
|
|
@@ -4667,6 +4976,7 @@ async function startHeadless(opts = {}) {
|
|
|
4667
4976
|
name: e.name,
|
|
4668
4977
|
input: e.input,
|
|
4669
4978
|
...e.partial && { partial: true },
|
|
4979
|
+
...e.background && { background: true },
|
|
4670
4980
|
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4671
4981
|
},
|
|
4672
4982
|
rid
|
|
@@ -4685,14 +4995,58 @@ async function startHeadless(opts = {}) {
|
|
|
4685
4995
|
rid
|
|
4686
4996
|
);
|
|
4687
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;
|
|
5010
|
+
case "tool_stopped":
|
|
5011
|
+
emit(
|
|
5012
|
+
"tool_stopped",
|
|
5013
|
+
{
|
|
5014
|
+
id: e.id,
|
|
5015
|
+
name: e.name,
|
|
5016
|
+
mode: e.mode,
|
|
5017
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
5018
|
+
},
|
|
5019
|
+
rid
|
|
5020
|
+
);
|
|
5021
|
+
return;
|
|
5022
|
+
case "tool_restarted":
|
|
5023
|
+
emit(
|
|
5024
|
+
"tool_restarted",
|
|
5025
|
+
{
|
|
5026
|
+
id: e.id,
|
|
5027
|
+
name: e.name,
|
|
5028
|
+
input: e.input,
|
|
5029
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
5030
|
+
},
|
|
5031
|
+
rid
|
|
5032
|
+
);
|
|
5033
|
+
return;
|
|
4688
5034
|
case "status":
|
|
4689
|
-
emit(
|
|
5035
|
+
emit(
|
|
5036
|
+
"status",
|
|
5037
|
+
{
|
|
5038
|
+
message: e.message,
|
|
5039
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
5040
|
+
},
|
|
5041
|
+
rid
|
|
5042
|
+
);
|
|
4690
5043
|
return;
|
|
4691
5044
|
case "error":
|
|
4692
5045
|
emit("error", { error: e.error }, rid);
|
|
4693
5046
|
return;
|
|
4694
5047
|
}
|
|
4695
5048
|
}
|
|
5049
|
+
toolRegistry.onEvent = onEvent;
|
|
4696
5050
|
async function handleMessage(parsed, requestId) {
|
|
4697
5051
|
if (running) {
|
|
4698
5052
|
emit(
|
|
@@ -4720,6 +5074,7 @@ async function startHeadless(opts = {}) {
|
|
|
4720
5074
|
}
|
|
4721
5075
|
let userMessage = parsed.text ?? "";
|
|
4722
5076
|
const isCommand = !!parsed.runCommand;
|
|
5077
|
+
const isHidden = isCommand || !!parsed.hidden;
|
|
4723
5078
|
if (parsed.runCommand === "sync") {
|
|
4724
5079
|
userMessage = loadActionPrompt("sync");
|
|
4725
5080
|
} else if (parsed.runCommand === "publish") {
|
|
@@ -4744,7 +5099,9 @@ async function startHeadless(opts = {}) {
|
|
|
4744
5099
|
signal: currentAbort.signal,
|
|
4745
5100
|
onEvent,
|
|
4746
5101
|
resolveExternalTool,
|
|
4747
|
-
hidden:
|
|
5102
|
+
hidden: isHidden,
|
|
5103
|
+
toolRegistry,
|
|
5104
|
+
onBackgroundComplete
|
|
4748
5105
|
});
|
|
4749
5106
|
if (!completedEmitted) {
|
|
4750
5107
|
emit(
|
|
@@ -4798,6 +5155,36 @@ async function startHeadless(opts = {}) {
|
|
|
4798
5155
|
emit("completed", { success: true }, requestId);
|
|
4799
5156
|
return;
|
|
4800
5157
|
}
|
|
5158
|
+
if (action === "stop_tool") {
|
|
5159
|
+
const id = parsed.id;
|
|
5160
|
+
const mode = parsed.mode ?? "hard";
|
|
5161
|
+
const found = toolRegistry.stop(id, mode);
|
|
5162
|
+
if (found) {
|
|
5163
|
+
emit("completed", { success: true }, requestId);
|
|
5164
|
+
} else {
|
|
5165
|
+
emit(
|
|
5166
|
+
"completed",
|
|
5167
|
+
{ success: false, error: "Tool not found" },
|
|
5168
|
+
requestId
|
|
5169
|
+
);
|
|
5170
|
+
}
|
|
5171
|
+
return;
|
|
5172
|
+
}
|
|
5173
|
+
if (action === "restart_tool") {
|
|
5174
|
+
const id = parsed.id;
|
|
5175
|
+
const patchedInput = parsed.input;
|
|
5176
|
+
const found = toolRegistry.restart(id, patchedInput);
|
|
5177
|
+
if (found) {
|
|
5178
|
+
emit("completed", { success: true }, requestId);
|
|
5179
|
+
} else {
|
|
5180
|
+
emit(
|
|
5181
|
+
"completed",
|
|
5182
|
+
{ success: false, error: "Tool not found" },
|
|
5183
|
+
requestId
|
|
5184
|
+
);
|
|
5185
|
+
}
|
|
5186
|
+
return;
|
|
5187
|
+
}
|
|
4801
5188
|
if (action === "message") {
|
|
4802
5189
|
await handleMessage(parsed, requestId);
|
|
4803
5190
|
return;
|