@mindstudio-ai/remy 0.1.40 → 0.1.42
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 +271 -208
- package/dist/index.js +326 -243
- package/dist/prompt/compiled/design.md +65 -216
- package/dist/prompt/compiled/interfaces.md +3 -1
- package/dist/prompt/compiled/msfm.md +2 -2
- package/dist/prompt/static/team.md +1 -1
- package/dist/subagents/codeSanityCheck/prompt.md +2 -0
- package/dist/subagents/designExpert/data/sources/dev/index.html +105 -0
- package/dist/subagents/designExpert/data/sources/dev/serve.mjs +45 -0
- package/dist/subagents/designExpert/data/sources/fonts.json +1 -153
- package/dist/subagents/designExpert/data/sources/ui_inspiration.json +81 -81
- package/dist/subagents/designExpert/data/sources/ui_inspiration_compiled.json +162 -162
- package/dist/subagents/designExpert/prompt.md +9 -17
- package/dist/subagents/designExpert/prompts/color.md +1 -1
- package/dist/subagents/designExpert/prompts/components.md +0 -7
- package/dist/subagents/designExpert/prompts/frontend-design-notes.md +1 -0
- package/dist/subagents/designExpert/prompts/identity.md +2 -9
- package/dist/subagents/designExpert/prompts/images.md +2 -8
- package/dist/subagents/designExpert/prompts/instructions.md +22 -0
- package/dist/subagents/designExpert/prompts/layout.md +1 -1
- package/dist/subagents/designExpert/prompts/ui-patterns.md +3 -1
- package/dist/subagents/designExpert/tools/images/enhance-image-prompt.md +48 -0
- package/package.json +1 -1
- package/dist/prompt/sources/frontend-design-notes.md +0 -153
- package/dist/prompt/sources/media-cdn.md +0 -46
package/dist/headless.js
CHANGED
|
@@ -44,11 +44,6 @@ function readJsonAsset(fallback, ...segments) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
// src/config.ts
|
|
48
|
-
import fs3 from "fs";
|
|
49
|
-
import path2 from "path";
|
|
50
|
-
import os from "os";
|
|
51
|
-
|
|
52
47
|
// src/logger.ts
|
|
53
48
|
import fs2 from "fs";
|
|
54
49
|
var LEVELS = {
|
|
@@ -60,9 +55,6 @@ var LEVELS = {
|
|
|
60
55
|
var currentLevel = LEVELS.error;
|
|
61
56
|
var writeFn = () => {
|
|
62
57
|
};
|
|
63
|
-
function timestamp() {
|
|
64
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
65
|
-
}
|
|
66
58
|
var MAX_VALUE_LENGTH = 200;
|
|
67
59
|
function truncateValues(obj) {
|
|
68
60
|
const result = {};
|
|
@@ -77,32 +69,35 @@ function truncateValues(obj) {
|
|
|
77
69
|
}
|
|
78
70
|
return result;
|
|
79
71
|
}
|
|
80
|
-
function write(level, msg, data) {
|
|
72
|
+
function write(level, module, msg, data) {
|
|
81
73
|
if (LEVELS[level] > currentLevel) {
|
|
82
74
|
return;
|
|
83
75
|
}
|
|
84
|
-
const
|
|
76
|
+
const entry = {
|
|
77
|
+
ts: Date.now(),
|
|
78
|
+
level,
|
|
79
|
+
module,
|
|
80
|
+
msg
|
|
81
|
+
};
|
|
85
82
|
if (data) {
|
|
86
|
-
|
|
83
|
+
Object.assign(entry, truncateValues(data));
|
|
87
84
|
}
|
|
88
|
-
writeFn(
|
|
85
|
+
writeFn(JSON.stringify(entry));
|
|
86
|
+
}
|
|
87
|
+
function createLogger(module) {
|
|
88
|
+
return {
|
|
89
|
+
error: (msg, data) => write("error", module, msg, data),
|
|
90
|
+
warn: (msg, data) => write("warn", module, msg, data),
|
|
91
|
+
info: (msg, data) => write("info", module, msg, data),
|
|
92
|
+
debug: (msg, data) => write("debug", module, msg, data)
|
|
93
|
+
};
|
|
89
94
|
}
|
|
90
|
-
var log = {
|
|
91
|
-
error(msg, data) {
|
|
92
|
-
write("error", msg, data);
|
|
93
|
-
},
|
|
94
|
-
warn(msg, data) {
|
|
95
|
-
write("warn", msg, data);
|
|
96
|
-
},
|
|
97
|
-
info(msg, data) {
|
|
98
|
-
write("info", msg, data);
|
|
99
|
-
},
|
|
100
|
-
debug(msg, data) {
|
|
101
|
-
write("debug", msg, data);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
95
|
|
|
105
96
|
// src/config.ts
|
|
97
|
+
import fs3 from "fs";
|
|
98
|
+
import path2 from "path";
|
|
99
|
+
import os from "os";
|
|
100
|
+
var log = createLogger("config");
|
|
106
101
|
var CONFIG_PATH = path2.join(
|
|
107
102
|
os.homedir(),
|
|
108
103
|
".mindstudio-local-tunnel",
|
|
@@ -144,10 +139,11 @@ function resolveConfig(flags) {
|
|
|
144
139
|
}
|
|
145
140
|
|
|
146
141
|
// src/tools/_helpers/sidecar.ts
|
|
142
|
+
var log2 = createLogger("sidecar");
|
|
147
143
|
var baseUrl = null;
|
|
148
144
|
function setSidecarBaseUrl(url) {
|
|
149
145
|
baseUrl = url;
|
|
150
|
-
|
|
146
|
+
log2.info("Configured", { url });
|
|
151
147
|
}
|
|
152
148
|
function isSidecarConfigured() {
|
|
153
149
|
return baseUrl !== null;
|
|
@@ -157,7 +153,6 @@ async function sidecarRequest(endpoint, body = {}, options) {
|
|
|
157
153
|
throw new Error("Sidecar not available");
|
|
158
154
|
}
|
|
159
155
|
const url = `${baseUrl}${endpoint}`;
|
|
160
|
-
log.debug("Sidecar request", { endpoint, body });
|
|
161
156
|
try {
|
|
162
157
|
const res = await fetch(url, {
|
|
163
158
|
method: "POST",
|
|
@@ -166,7 +161,7 @@ async function sidecarRequest(endpoint, body = {}, options) {
|
|
|
166
161
|
signal: options?.timeout ? AbortSignal.timeout(options.timeout) : void 0
|
|
167
162
|
});
|
|
168
163
|
if (!res.ok) {
|
|
169
|
-
|
|
164
|
+
log2.error("Sidecar error", { endpoint, status: res.status });
|
|
170
165
|
throw new Error(`Sidecar error: ${res.status}`);
|
|
171
166
|
}
|
|
172
167
|
return res.json();
|
|
@@ -174,7 +169,7 @@ async function sidecarRequest(endpoint, body = {}, options) {
|
|
|
174
169
|
if (err.message.startsWith("Sidecar error")) {
|
|
175
170
|
throw err;
|
|
176
171
|
}
|
|
177
|
-
|
|
172
|
+
log2.error("Sidecar connection error", { endpoint, error: err.message });
|
|
178
173
|
throw new Error(`Sidecar connection error: ${err.message}`);
|
|
179
174
|
}
|
|
180
175
|
}
|
|
@@ -429,25 +424,19 @@ ${viewContext?.activeFile ? `Active file: ${viewContext.activeFile}` : ""}
|
|
|
429
424
|
}
|
|
430
425
|
|
|
431
426
|
// src/api.ts
|
|
427
|
+
var log3 = createLogger("api");
|
|
432
428
|
async function* streamChat(params) {
|
|
433
|
-
const { baseUrl: baseUrl2, apiKey, signal, ...body } = params;
|
|
429
|
+
const { baseUrl: baseUrl2, apiKey, signal, requestId, ...body } = params;
|
|
434
430
|
const url = `${baseUrl2}/_internal/v2/agent/remy/chat`;
|
|
435
431
|
const startTime = Date.now();
|
|
436
432
|
const messagesWithAttachments = body.messages.filter(
|
|
437
433
|
(m) => m.attachments && m.attachments.length > 0
|
|
438
434
|
);
|
|
439
|
-
|
|
440
|
-
|
|
435
|
+
log3.info("API request", {
|
|
436
|
+
requestId,
|
|
441
437
|
model: body.model,
|
|
442
438
|
messageCount: body.messages.length,
|
|
443
|
-
toolCount: body.tools.length
|
|
444
|
-
...messagesWithAttachments.length > 0 && {
|
|
445
|
-
attachments: messagesWithAttachments.map((m) => ({
|
|
446
|
-
role: m.role,
|
|
447
|
-
attachmentCount: m.attachments.length,
|
|
448
|
-
urls: m.attachments.map((a) => a.url)
|
|
449
|
-
}))
|
|
450
|
-
}
|
|
439
|
+
toolCount: body.tools.length
|
|
451
440
|
});
|
|
452
441
|
let res;
|
|
453
442
|
try {
|
|
@@ -462,15 +451,15 @@ async function* streamChat(params) {
|
|
|
462
451
|
});
|
|
463
452
|
} catch (err) {
|
|
464
453
|
if (signal?.aborted) {
|
|
465
|
-
|
|
454
|
+
log3.warn("Request aborted", { requestId });
|
|
466
455
|
throw err;
|
|
467
456
|
}
|
|
468
|
-
|
|
457
|
+
log3.error("Network error", { requestId, error: err.message });
|
|
469
458
|
yield { type: "error", error: `Network error: ${err.message}` };
|
|
470
459
|
return;
|
|
471
460
|
}
|
|
472
461
|
const ttfb = Date.now() - startTime;
|
|
473
|
-
|
|
462
|
+
log3.info("API response", { requestId, status: res.status, ttfbMs: ttfb });
|
|
474
463
|
if (!res.ok) {
|
|
475
464
|
let errorMessage = `HTTP ${res.status}`;
|
|
476
465
|
try {
|
|
@@ -483,7 +472,11 @@ async function* streamChat(params) {
|
|
|
483
472
|
}
|
|
484
473
|
} catch {
|
|
485
474
|
}
|
|
486
|
-
|
|
475
|
+
log3.error("API error", {
|
|
476
|
+
requestId,
|
|
477
|
+
status: res.status,
|
|
478
|
+
error: errorMessage
|
|
479
|
+
});
|
|
487
480
|
yield { type: "error", error: errorMessage };
|
|
488
481
|
return;
|
|
489
482
|
}
|
|
@@ -508,7 +501,10 @@ async function* streamChat(params) {
|
|
|
508
501
|
} catch {
|
|
509
502
|
clearTimeout(stallTimer);
|
|
510
503
|
await reader.cancel();
|
|
511
|
-
|
|
504
|
+
log3.error("Stream stalled", {
|
|
505
|
+
requestId,
|
|
506
|
+
durationMs: Date.now() - startTime
|
|
507
|
+
});
|
|
512
508
|
yield {
|
|
513
509
|
type: "error",
|
|
514
510
|
error: "Stream stalled \u2014 no data received for 5 minutes"
|
|
@@ -530,8 +526,9 @@ async function* streamChat(params) {
|
|
|
530
526
|
const event = JSON.parse(line.slice(6));
|
|
531
527
|
if (event.type === "done") {
|
|
532
528
|
const elapsed = Date.now() - startTime;
|
|
533
|
-
|
|
534
|
-
|
|
529
|
+
log3.info("Stream complete", {
|
|
530
|
+
requestId,
|
|
531
|
+
durationMs: elapsed,
|
|
535
532
|
stopReason: event.stopReason,
|
|
536
533
|
inputTokens: event.usage.inputTokens,
|
|
537
534
|
outputTokens: event.usage.outputTokens
|
|
@@ -564,11 +561,6 @@ async function* streamChatWithRetry(params, options) {
|
|
|
564
561
|
for await (const event of streamChat(params)) {
|
|
565
562
|
if (event.type === "error") {
|
|
566
563
|
if (isRetryableError(event.error) && attempt < MAX_RETRIES - 1) {
|
|
567
|
-
log.warn("Retryable error, will retry", {
|
|
568
|
-
attempt: attempt + 1,
|
|
569
|
-
maxRetries: MAX_RETRIES,
|
|
570
|
-
error: event.error
|
|
571
|
-
});
|
|
572
564
|
options?.onRetry?.(attempt, event.error);
|
|
573
565
|
retryableFailure = true;
|
|
574
566
|
break;
|
|
@@ -583,7 +575,12 @@ async function* streamChatWithRetry(params, options) {
|
|
|
583
575
|
return;
|
|
584
576
|
}
|
|
585
577
|
const backoff = INITIAL_BACKOFF_MS * 2 ** attempt;
|
|
586
|
-
|
|
578
|
+
log3.warn("Retrying", {
|
|
579
|
+
requestId: params.requestId,
|
|
580
|
+
attempt: attempt + 1,
|
|
581
|
+
maxRetries: MAX_RETRIES,
|
|
582
|
+
backoffMs: backoff
|
|
583
|
+
});
|
|
587
584
|
await sleep(backoff);
|
|
588
585
|
continue;
|
|
589
586
|
}
|
|
@@ -2144,6 +2141,17 @@ var runMethodTool = {
|
|
|
2144
2141
|
}
|
|
2145
2142
|
};
|
|
2146
2143
|
|
|
2144
|
+
// src/subagents/common/analyzeImage.ts
|
|
2145
|
+
var VISION_MODEL = "gemini-3-flash";
|
|
2146
|
+
var VISION_MODEL_OVERRIDE = JSON.stringify({ model: VISION_MODEL });
|
|
2147
|
+
async function analyzeImage(params) {
|
|
2148
|
+
const { prompt, imageUrl, timeout = 2e5, onLog } = params;
|
|
2149
|
+
return runCli(
|
|
2150
|
+
`mindstudio analyze-image --prompt ${JSON.stringify(prompt)} --image-url ${JSON.stringify(imageUrl)} --vision-model-override ${JSON.stringify(VISION_MODEL_OVERRIDE)} --output-key analysis --no-meta`,
|
|
2151
|
+
{ timeout, onLog }
|
|
2152
|
+
);
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2147
2155
|
// src/tools/_helpers/screenshot.ts
|
|
2148
2156
|
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.";
|
|
2149
2157
|
async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
@@ -2158,7 +2166,6 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
2158
2166
|
const ssResult = await sidecarRequest("/screenshot-full-page", void 0, {
|
|
2159
2167
|
timeout: 12e4
|
|
2160
2168
|
});
|
|
2161
|
-
log.debug("Screenshot response", { ssResult });
|
|
2162
2169
|
const url = ssResult?.url || ssResult?.screenshotUrl;
|
|
2163
2170
|
if (!url) {
|
|
2164
2171
|
throw new Error(
|
|
@@ -2169,10 +2176,11 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
2169
2176
|
return url;
|
|
2170
2177
|
}
|
|
2171
2178
|
const analysisPrompt = prompt || SCREENSHOT_ANALYSIS_PROMPT;
|
|
2172
|
-
const analysis = await
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2179
|
+
const analysis = await analyzeImage({
|
|
2180
|
+
prompt: analysisPrompt,
|
|
2181
|
+
imageUrl: url,
|
|
2182
|
+
onLog
|
|
2183
|
+
});
|
|
2176
2184
|
return JSON.stringify({ url, analysis });
|
|
2177
2185
|
}
|
|
2178
2186
|
|
|
@@ -2218,13 +2226,8 @@ function startStatusWatcher(config) {
|
|
|
2218
2226
|
try {
|
|
2219
2227
|
const ctx = getContext();
|
|
2220
2228
|
if (!ctx.assistantText && !ctx.lastToolName) {
|
|
2221
|
-
log.debug("Status watcher: no context, skipping");
|
|
2222
2229
|
return;
|
|
2223
2230
|
}
|
|
2224
|
-
log.debug("Status watcher: requesting label", {
|
|
2225
|
-
textLength: ctx.assistantText.length,
|
|
2226
|
-
lastToolName: ctx.lastToolName
|
|
2227
|
-
});
|
|
2228
2231
|
const res = await fetch(url, {
|
|
2229
2232
|
method: "POST",
|
|
2230
2233
|
headers: {
|
|
@@ -2241,30 +2244,18 @@ function startStatusWatcher(config) {
|
|
|
2241
2244
|
signal
|
|
2242
2245
|
});
|
|
2243
2246
|
if (!res.ok) {
|
|
2244
|
-
log.debug("Status watcher: endpoint returned non-ok", {
|
|
2245
|
-
status: res.status
|
|
2246
|
-
});
|
|
2247
2247
|
return;
|
|
2248
2248
|
}
|
|
2249
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
|
-
});
|
|
2250
|
+
if (!data.label || data.label === lastLabel) {
|
|
2258
2251
|
return;
|
|
2259
2252
|
}
|
|
2260
2253
|
lastLabel = data.label;
|
|
2261
2254
|
if (stopped) {
|
|
2262
2255
|
return;
|
|
2263
2256
|
}
|
|
2264
|
-
log.debug("Status watcher: emitting", { label: data.label });
|
|
2265
2257
|
onStatus(data.label);
|
|
2266
|
-
} catch
|
|
2267
|
-
log.debug("Status watcher: error", { error: err?.message ?? "unknown" });
|
|
2258
|
+
} catch {
|
|
2268
2259
|
} finally {
|
|
2269
2260
|
inflight = false;
|
|
2270
2261
|
}
|
|
@@ -2272,12 +2263,10 @@ function startStatusWatcher(config) {
|
|
|
2272
2263
|
const timer = setInterval(tick, interval);
|
|
2273
2264
|
tick().catch(() => {
|
|
2274
2265
|
});
|
|
2275
|
-
log.debug("Status watcher started", { interval });
|
|
2276
2266
|
return {
|
|
2277
2267
|
stop() {
|
|
2278
2268
|
stopped = true;
|
|
2279
2269
|
clearInterval(timer);
|
|
2280
|
-
log.debug("Status watcher stopped");
|
|
2281
2270
|
}
|
|
2282
2271
|
};
|
|
2283
2272
|
}
|
|
@@ -2318,6 +2307,7 @@ function cleanMessagesForApi(messages) {
|
|
|
2318
2307
|
}
|
|
2319
2308
|
|
|
2320
2309
|
// src/subagents/runner.ts
|
|
2310
|
+
var log4 = createLogger("sub-agent");
|
|
2321
2311
|
async function runSubAgent(config) {
|
|
2322
2312
|
const {
|
|
2323
2313
|
system,
|
|
@@ -2333,14 +2323,19 @@ async function runSubAgent(config) {
|
|
|
2333
2323
|
onEvent,
|
|
2334
2324
|
resolveExternalTool,
|
|
2335
2325
|
toolRegistry,
|
|
2326
|
+
requestId,
|
|
2336
2327
|
background,
|
|
2337
2328
|
onBackgroundComplete
|
|
2338
2329
|
} = config;
|
|
2339
2330
|
const bgAbort = background ? new AbortController() : null;
|
|
2340
2331
|
const signal = background ? bgAbort.signal : parentSignal;
|
|
2332
|
+
const agentName = subAgentId || "sub-agent";
|
|
2333
|
+
const runStart = Date.now();
|
|
2334
|
+
log4.info("Sub-agent started", { requestId, parentToolId, agentName });
|
|
2341
2335
|
const emit2 = (e) => {
|
|
2342
2336
|
onEvent({ ...e, parentToolId });
|
|
2343
2337
|
};
|
|
2338
|
+
let turns = 0;
|
|
2344
2339
|
const run = async () => {
|
|
2345
2340
|
const messages = [{ role: "user", content: task }];
|
|
2346
2341
|
function getPartialText(blocks) {
|
|
@@ -2360,6 +2355,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
2360
2355
|
}
|
|
2361
2356
|
let lastToolResult = "";
|
|
2362
2357
|
while (true) {
|
|
2358
|
+
turns++;
|
|
2363
2359
|
if (signal?.aborted) {
|
|
2364
2360
|
return abortResult([]);
|
|
2365
2361
|
}
|
|
@@ -2385,6 +2381,7 @@ Current date/time: ${(/* @__PURE__ */ new Date()).toISOString().replace("T", " "
|
|
|
2385
2381
|
{
|
|
2386
2382
|
...apiConfig,
|
|
2387
2383
|
model,
|
|
2384
|
+
requestId,
|
|
2388
2385
|
subAgentId,
|
|
2389
2386
|
system: fullSystem,
|
|
2390
2387
|
messages: cleanMessagesForApi(messages),
|
|
@@ -2475,7 +2472,8 @@ Current date/time: ${(/* @__PURE__ */ new Date()).toISOString().replace("T", " "
|
|
|
2475
2472
|
const text = getPartialText(contentBlocks);
|
|
2476
2473
|
return { text, messages };
|
|
2477
2474
|
}
|
|
2478
|
-
|
|
2475
|
+
log4.info("Tools executing", {
|
|
2476
|
+
requestId,
|
|
2479
2477
|
parentToolId,
|
|
2480
2478
|
count: toolCalls.length,
|
|
2481
2479
|
tools: toolCalls.map((tc) => tc.name)
|
|
@@ -2574,15 +2572,37 @@ Current date/time: ${(/* @__PURE__ */ new Date()).toISOString().replace("T", " "
|
|
|
2574
2572
|
}
|
|
2575
2573
|
}
|
|
2576
2574
|
};
|
|
2575
|
+
const wrapRun = async () => {
|
|
2576
|
+
try {
|
|
2577
|
+
const result = await run();
|
|
2578
|
+
log4.info("Sub-agent complete", {
|
|
2579
|
+
requestId,
|
|
2580
|
+
parentToolId,
|
|
2581
|
+
agentName,
|
|
2582
|
+
durationMs: Date.now() - runStart,
|
|
2583
|
+
turns
|
|
2584
|
+
});
|
|
2585
|
+
return result;
|
|
2586
|
+
} catch (err) {
|
|
2587
|
+
log4.warn("Sub-agent error", {
|
|
2588
|
+
requestId,
|
|
2589
|
+
parentToolId,
|
|
2590
|
+
agentName,
|
|
2591
|
+
error: err.message
|
|
2592
|
+
});
|
|
2593
|
+
throw err;
|
|
2594
|
+
}
|
|
2595
|
+
};
|
|
2577
2596
|
if (!background) {
|
|
2578
|
-
return
|
|
2597
|
+
return wrapRun();
|
|
2579
2598
|
}
|
|
2599
|
+
log4.info("Sub-agent backgrounded", { requestId, parentToolId, agentName });
|
|
2580
2600
|
const ack = await generateBackgroundAck({
|
|
2581
2601
|
apiConfig,
|
|
2582
2602
|
agentName: subAgentId || "agent",
|
|
2583
2603
|
task
|
|
2584
2604
|
});
|
|
2585
|
-
|
|
2605
|
+
wrapRun().then((finalResult) => onBackgroundComplete?.(finalResult)).catch(
|
|
2586
2606
|
(err) => onBackgroundComplete?.({ text: `Error: ${err.message}`, messages: [] })
|
|
2587
2607
|
);
|
|
2588
2608
|
return { text: ack, messages: [], backgrounded: true };
|
|
@@ -2705,6 +2725,7 @@ ${appSpec}
|
|
|
2705
2725
|
}
|
|
2706
2726
|
|
|
2707
2727
|
// src/subagents/browserAutomation/index.ts
|
|
2728
|
+
var log5 = createLogger("browser-automation");
|
|
2708
2729
|
var browserAutomationTool = {
|
|
2709
2730
|
definition: {
|
|
2710
2731
|
name: "runAutomatedBrowserTest",
|
|
@@ -2764,6 +2785,7 @@ var browserAutomationTool = {
|
|
|
2764
2785
|
subAgentId: "browserAutomation",
|
|
2765
2786
|
signal: context.signal,
|
|
2766
2787
|
parentToolId: context.toolCallId,
|
|
2788
|
+
requestId: context.requestId,
|
|
2767
2789
|
onEvent: context.onEvent,
|
|
2768
2790
|
resolveExternalTool: async (id, name, input2) => {
|
|
2769
2791
|
if (!context.resolveExternalTool) {
|
|
@@ -2798,7 +2820,7 @@ var browserAutomationTool = {
|
|
|
2798
2820
|
}
|
|
2799
2821
|
}
|
|
2800
2822
|
} catch {
|
|
2801
|
-
|
|
2823
|
+
log5.debug("Failed to parse batch analysis result", {
|
|
2802
2824
|
batchResult
|
|
2803
2825
|
});
|
|
2804
2826
|
}
|
|
@@ -2885,6 +2907,8 @@ You are analyzing a screenshot of a real website or app for a designer's persona
|
|
|
2885
2907
|
|
|
2886
2908
|
Analyze the image and think about what makes the site or app special and unique. What is it doing that is unique, different, original, and creative? What makes it special? What isn't working? What doesn't look or feel good?
|
|
2887
2909
|
|
|
2910
|
+
Important: First, look at the screenshot and use your judgement to identify what the user wants notes about. If the screenshot is of a website for design case studies, or a blog post writeup about a new product or design, assume that the user is interested in a reference for the site/app/product being talked about - it is unlikely they are interested in a design audit of dribble.com, for example.
|
|
2911
|
+
|
|
2888
2912
|
Then, provide the following analysis:
|
|
2889
2913
|
|
|
2890
2914
|
## Context
|
|
@@ -2934,10 +2958,11 @@ async function execute3(input, onLog) {
|
|
|
2934
2958
|
}
|
|
2935
2959
|
imageUrl = ssUrl;
|
|
2936
2960
|
}
|
|
2937
|
-
const analysis = await
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2961
|
+
const analysis = await analyzeImage({
|
|
2962
|
+
prompt: analysisPrompt,
|
|
2963
|
+
imageUrl,
|
|
2964
|
+
onLog
|
|
2965
|
+
});
|
|
2941
2966
|
return JSON.stringify({ url: imageUrl, analysis });
|
|
2942
2967
|
}
|
|
2943
2968
|
|
|
@@ -2969,10 +2994,11 @@ var definition4 = {
|
|
|
2969
2994
|
async function execute4(input, onLog) {
|
|
2970
2995
|
const imageUrl = input.imageUrl;
|
|
2971
2996
|
const prompt = input.prompt || DEFAULT_PROMPT;
|
|
2972
|
-
const analysis = await
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2997
|
+
const analysis = await analyzeImage({
|
|
2998
|
+
prompt,
|
|
2999
|
+
imageUrl,
|
|
3000
|
+
onLog
|
|
3001
|
+
});
|
|
2976
3002
|
return JSON.stringify({ url: imageUrl, analysis });
|
|
2977
3003
|
}
|
|
2978
3004
|
|
|
@@ -3006,29 +3032,68 @@ async function execute5(input, onLog) {
|
|
|
3006
3032
|
}
|
|
3007
3033
|
}
|
|
3008
3034
|
|
|
3009
|
-
// src/subagents/designExpert/tools/generateImages.ts
|
|
3035
|
+
// src/subagents/designExpert/tools/images/generateImages.ts
|
|
3010
3036
|
var generateImages_exports = {};
|
|
3011
3037
|
__export(generateImages_exports, {
|
|
3012
3038
|
definition: () => definition6,
|
|
3013
3039
|
execute: () => execute6
|
|
3014
3040
|
});
|
|
3015
3041
|
|
|
3016
|
-
// src/subagents/designExpert/tools/
|
|
3017
|
-
var
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
const
|
|
3022
|
-
const
|
|
3023
|
-
|
|
3024
|
-
|
|
3042
|
+
// src/subagents/designExpert/tools/images/enhancePrompt.ts
|
|
3043
|
+
var SYSTEM_PROMPT = readAsset(
|
|
3044
|
+
"subagents/designExpert/tools/images/enhance-image-prompt.md"
|
|
3045
|
+
);
|
|
3046
|
+
async function enhanceImagePrompt(params) {
|
|
3047
|
+
const { brief, aspectRatio, transparentBackground, onLog } = params;
|
|
3048
|
+
const orientation = aspectRatio === "1:1" ? "square" : ["16:9", "4:3", "3:2"].includes(aspectRatio) ? "landscape" : "portrait";
|
|
3049
|
+
const contextParts = [
|
|
3050
|
+
`Aspect ratio: ${aspectRatio} (${orientation})`
|
|
3051
|
+
];
|
|
3052
|
+
if (transparentBackground) {
|
|
3053
|
+
contextParts.push(
|
|
3054
|
+
"Transparent background: yes \u2014 the background will be removed. Focus on the subject as an isolated element."
|
|
3055
|
+
);
|
|
3025
3056
|
}
|
|
3057
|
+
const message = `<context>
|
|
3058
|
+
${contextParts.join("\n")}
|
|
3059
|
+
</context>
|
|
3060
|
+
|
|
3061
|
+
<brief>
|
|
3062
|
+
${brief}
|
|
3063
|
+
</brief>`;
|
|
3064
|
+
const enhanced = await runCli(
|
|
3065
|
+
`mindstudio generate-text --prompt ${JSON.stringify(SYSTEM_PROMPT)} --message ${JSON.stringify(message)} --output-key enhanced --no-meta`,
|
|
3066
|
+
{ timeout: 6e4, onLog }
|
|
3067
|
+
);
|
|
3068
|
+
return enhanced.trim();
|
|
3069
|
+
}
|
|
3070
|
+
|
|
3071
|
+
// src/subagents/designExpert/tools/images/imageGenerator.ts
|
|
3072
|
+
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, any text present in the image, whether there are any issues (artifacts, distortions), and how it could be used in a layout for an app or website. 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.";
|
|
3073
|
+
async function generateImageAssets(opts) {
|
|
3074
|
+
const { prompts, sourceImages, transparentBackground, onLog } = opts;
|
|
3075
|
+
const aspectRatio = opts.aspectRatio || "1:1";
|
|
3076
|
+
const config = {
|
|
3077
|
+
aspect_ratio: aspectRatio,
|
|
3078
|
+
...sourceImages?.length && { source_images: sourceImages }
|
|
3079
|
+
};
|
|
3080
|
+
const isEdit = !!sourceImages?.length;
|
|
3081
|
+
const enhancedPrompts = isEdit ? prompts : await Promise.all(
|
|
3082
|
+
prompts.map(
|
|
3083
|
+
(brief) => enhanceImagePrompt({
|
|
3084
|
+
brief,
|
|
3085
|
+
aspectRatio,
|
|
3086
|
+
transparentBackground,
|
|
3087
|
+
onLog
|
|
3088
|
+
})
|
|
3089
|
+
)
|
|
3090
|
+
);
|
|
3026
3091
|
let imageUrls;
|
|
3027
|
-
if (
|
|
3092
|
+
if (enhancedPrompts.length === 1) {
|
|
3028
3093
|
const step = JSON.stringify({
|
|
3029
|
-
prompt:
|
|
3094
|
+
prompt: enhancedPrompts[0],
|
|
3030
3095
|
imageModelOverride: {
|
|
3031
|
-
model: "
|
|
3096
|
+
model: "gemini-3.1-flash-image",
|
|
3032
3097
|
config
|
|
3033
3098
|
}
|
|
3034
3099
|
});
|
|
@@ -3038,12 +3103,12 @@ async function seedreamGenerate(opts) {
|
|
|
3038
3103
|
);
|
|
3039
3104
|
imageUrls = [url];
|
|
3040
3105
|
} else {
|
|
3041
|
-
const steps =
|
|
3106
|
+
const steps = enhancedPrompts.map((prompt) => ({
|
|
3042
3107
|
stepType: "generateImage",
|
|
3043
3108
|
step: {
|
|
3044
3109
|
prompt,
|
|
3045
3110
|
imageModelOverride: {
|
|
3046
|
-
model: "
|
|
3111
|
+
model: "gemini-3.1-flash-image",
|
|
3047
3112
|
config
|
|
3048
3113
|
}
|
|
3049
3114
|
}
|
|
@@ -3080,22 +3145,33 @@ async function seedreamGenerate(opts) {
|
|
|
3080
3145
|
const images = await Promise.all(
|
|
3081
3146
|
imageUrls.map(async (url, i) => {
|
|
3082
3147
|
if (url.startsWith("Error")) {
|
|
3083
|
-
return {
|
|
3148
|
+
return {
|
|
3149
|
+
prompt: prompts[i],
|
|
3150
|
+
...!isEdit && { enhancedPrompt: enhancedPrompts[i] },
|
|
3151
|
+
error: url
|
|
3152
|
+
};
|
|
3084
3153
|
}
|
|
3085
|
-
const analysis = await
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3154
|
+
const analysis = await analyzeImage({
|
|
3155
|
+
prompt: ANALYZE_PROMPT,
|
|
3156
|
+
imageUrl: url,
|
|
3157
|
+
onLog
|
|
3158
|
+
});
|
|
3159
|
+
return {
|
|
3160
|
+
url,
|
|
3161
|
+
prompt: prompts[i],
|
|
3162
|
+
...!isEdit && { enhancedPrompt: enhancedPrompts[i] },
|
|
3163
|
+
analysis,
|
|
3164
|
+
aspectRatio
|
|
3165
|
+
};
|
|
3090
3166
|
})
|
|
3091
3167
|
);
|
|
3092
3168
|
return JSON.stringify({ images });
|
|
3093
3169
|
}
|
|
3094
3170
|
|
|
3095
|
-
// src/subagents/designExpert/tools/generateImages.ts
|
|
3171
|
+
// src/subagents/designExpert/tools/images/generateImages.ts
|
|
3096
3172
|
var definition6 = {
|
|
3097
3173
|
name: "generateImages",
|
|
3098
|
-
description: "Generate images
|
|
3174
|
+
description: "Generate images. Returns CDN URLs with a quality analysis for each image. Produces high-quality results for everything from photorealistic images and abstract/creative visuals. Pass multiple prompts to generate in parallel. No need to analyze images separately after generating \u2014 the analysis is included.",
|
|
3099
3175
|
inputSchema: {
|
|
3100
3176
|
type: "object",
|
|
3101
3177
|
properties: {
|
|
@@ -3104,15 +3180,12 @@ var definition6 = {
|
|
|
3104
3180
|
items: {
|
|
3105
3181
|
type: "string"
|
|
3106
3182
|
},
|
|
3107
|
-
description: "One or more image
|
|
3183
|
+
description: "One or more image briefs describing what you want. Focus on subject, mood, style, and intended use \u2014 the tool optimizes your brief into a model-ready prompt automatically. Multiple briefs run in parallel."
|
|
3108
3184
|
},
|
|
3109
|
-
|
|
3110
|
-
type: "
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
height: {
|
|
3114
|
-
type: "number",
|
|
3115
|
-
description: "Image height in pixels. Default 2048. Range: 2048-4096."
|
|
3185
|
+
aspectRatio: {
|
|
3186
|
+
type: "string",
|
|
3187
|
+
enum: ["1:1", "16:9", "9:16", "3:4", "4:3", "2:3", "3:2"],
|
|
3188
|
+
description: "Aspect ratio. Default 1:1."
|
|
3116
3189
|
},
|
|
3117
3190
|
transparentBackground: {
|
|
3118
3191
|
type: "boolean",
|
|
@@ -3123,16 +3196,15 @@ var definition6 = {
|
|
|
3123
3196
|
}
|
|
3124
3197
|
};
|
|
3125
3198
|
async function execute6(input, onLog) {
|
|
3126
|
-
return
|
|
3199
|
+
return generateImageAssets({
|
|
3127
3200
|
prompts: input.prompts,
|
|
3128
|
-
|
|
3129
|
-
height: input.height,
|
|
3201
|
+
aspectRatio: input.aspectRatio,
|
|
3130
3202
|
transparentBackground: input.transparentBackground,
|
|
3131
3203
|
onLog
|
|
3132
3204
|
});
|
|
3133
3205
|
}
|
|
3134
3206
|
|
|
3135
|
-
// src/subagents/designExpert/tools/editImages.ts
|
|
3207
|
+
// src/subagents/designExpert/tools/images/editImages.ts
|
|
3136
3208
|
var editImages_exports = {};
|
|
3137
3209
|
__export(editImages_exports, {
|
|
3138
3210
|
definition: () => definition7,
|
|
@@ -3140,7 +3212,7 @@ __export(editImages_exports, {
|
|
|
3140
3212
|
});
|
|
3141
3213
|
var definition7 = {
|
|
3142
3214
|
name: "editImages",
|
|
3143
|
-
description: "Edit or transform existing images
|
|
3215
|
+
description: "Edit or transform existing images. Provide one or more source image URLs as reference and a prompt describing the desired edit. Use for compositing, style transfer, subject transformation, blending multiple references, or incorporating one or more references into something new. Returns CDN URLs with analysis.",
|
|
3144
3216
|
inputSchema: {
|
|
3145
3217
|
type: "object",
|
|
3146
3218
|
properties: {
|
|
@@ -3149,7 +3221,7 @@ var definition7 = {
|
|
|
3149
3221
|
items: {
|
|
3150
3222
|
type: "string"
|
|
3151
3223
|
},
|
|
3152
|
-
description: "One or more edit
|
|
3224
|
+
description: "One or more edit briefs describing the desired transformation. Focus on what to change relative to the source material. Multiple briefs run in parallel, each using the same source images."
|
|
3153
3225
|
},
|
|
3154
3226
|
sourceImages: {
|
|
3155
3227
|
type: "array",
|
|
@@ -3158,13 +3230,10 @@ var definition7 = {
|
|
|
3158
3230
|
},
|
|
3159
3231
|
description: "One or more source/reference image URLs. These are used as the basis for the edit \u2014 the AI will use them as reference for style, subject, or composition."
|
|
3160
3232
|
},
|
|
3161
|
-
|
|
3162
|
-
type: "
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
height: {
|
|
3166
|
-
type: "number",
|
|
3167
|
-
description: "Output height in pixels. Default 2048. Range: 2048-4096."
|
|
3233
|
+
aspectRatio: {
|
|
3234
|
+
type: "string",
|
|
3235
|
+
enum: ["1:1", "16:9", "9:16", "3:4", "4:3", "2:3", "3:2"],
|
|
3236
|
+
description: "Output aspect ratio. Default 1:1."
|
|
3168
3237
|
},
|
|
3169
3238
|
transparentBackground: {
|
|
3170
3239
|
type: "boolean",
|
|
@@ -3175,11 +3244,10 @@ var definition7 = {
|
|
|
3175
3244
|
}
|
|
3176
3245
|
};
|
|
3177
3246
|
async function execute7(input, onLog) {
|
|
3178
|
-
return
|
|
3247
|
+
return generateImageAssets({
|
|
3179
3248
|
prompts: input.prompts,
|
|
3180
3249
|
sourceImages: input.sourceImages,
|
|
3181
|
-
|
|
3182
|
-
height: input.height,
|
|
3250
|
+
aspectRatio: input.aspectRatio,
|
|
3183
3251
|
transparentBackground: input.transparentBackground,
|
|
3184
3252
|
onLog
|
|
3185
3253
|
});
|
|
@@ -3345,15 +3413,11 @@ function getFontLibrarySample() {
|
|
|
3345
3413
|
return `
|
|
3346
3414
|
## Font Library
|
|
3347
3415
|
|
|
3348
|
-
|
|
3416
|
+
This is your personal library of fonts you love. Use it as a starting point when thinking about anything related to typography.
|
|
3349
3417
|
|
|
3350
3418
|
### Fonts
|
|
3351
3419
|
|
|
3352
|
-
${fontList}
|
|
3353
|
-
|
|
3354
|
-
### Pairings
|
|
3355
|
-
|
|
3356
|
-
${pairingList}`.trim();
|
|
3420
|
+
${fontList}`.trim();
|
|
3357
3421
|
}
|
|
3358
3422
|
|
|
3359
3423
|
// src/subagents/designExpert/data/getDesignReferencesSample.ts
|
|
@@ -3373,16 +3437,16 @@ function sample2(arr, n) {
|
|
|
3373
3437
|
return copy.slice(0, n);
|
|
3374
3438
|
}
|
|
3375
3439
|
function getDesignReferencesSample() {
|
|
3376
|
-
const images = sample2(inspirationImages,
|
|
3440
|
+
const images = sample2(inspirationImages, 25);
|
|
3377
3441
|
if (!images.length) {
|
|
3378
3442
|
return "";
|
|
3379
3443
|
}
|
|
3380
3444
|
const imageList = images.map((img, i) => `### Reference ${i + 1}
|
|
3381
3445
|
${img.analysis}`).join("\n\n");
|
|
3382
3446
|
return `
|
|
3383
|
-
## Design References
|
|
3447
|
+
## Visual Design References
|
|
3384
3448
|
|
|
3385
|
-
This is
|
|
3449
|
+
This is your personal reference library of visual design you love. The apps and sites featured within made it into your library because they did something bold, intentional, and memorable. Use them as reference, inspiration, and let the takeaways guide your work.
|
|
3386
3450
|
|
|
3387
3451
|
${imageList}`.trim();
|
|
3388
3452
|
}
|
|
@@ -3404,16 +3468,16 @@ function sample3(arr, n) {
|
|
|
3404
3468
|
return copy.slice(0, n);
|
|
3405
3469
|
}
|
|
3406
3470
|
function getUiInspirationSample() {
|
|
3407
|
-
const screens = sample3(uiScreens,
|
|
3471
|
+
const screens = sample3(uiScreens, 25);
|
|
3408
3472
|
if (!screens.length) {
|
|
3409
3473
|
return "";
|
|
3410
3474
|
}
|
|
3411
3475
|
const screenList = screens.map((s, i) => `### Screen ${i + 1}
|
|
3412
3476
|
${s.analysis}`).join("\n\n");
|
|
3413
3477
|
return `
|
|
3414
|
-
## UI
|
|
3478
|
+
## UI Case Studies
|
|
3415
3479
|
|
|
3416
|
-
|
|
3480
|
+
These are your personal notes, collected over the years, about UI patterns you've encountered in the wild that you love. You re-use aspects of them liberally in your work, reference them as ground truths, as well as use them to synthesize new ideas and refine your sense of what good UI feels and looks like. The work you do must always feel like it belongs in this company.
|
|
3417
3481
|
|
|
3418
3482
|
${screenList}`.trim();
|
|
3419
3483
|
}
|
|
@@ -3422,8 +3486,8 @@ ${screenList}`.trim();
|
|
|
3422
3486
|
var SUBAGENT = "subagents/designExpert";
|
|
3423
3487
|
var RUNTIME_PLACEHOLDERS = /* @__PURE__ */ new Set([
|
|
3424
3488
|
"font_library",
|
|
3425
|
-
"
|
|
3426
|
-
"
|
|
3489
|
+
"visual_design_references",
|
|
3490
|
+
"ui_case_studies"
|
|
3427
3491
|
]);
|
|
3428
3492
|
var PROMPT_TEMPLATE = readAsset(SUBAGENT, "prompt.md").replace(/\{\{([^}]+)\}\}/g, (match, key) => {
|
|
3429
3493
|
const k = key.trim();
|
|
@@ -3434,7 +3498,7 @@ function getDesignExpertPrompt() {
|
|
|
3434
3498
|
let prompt = PROMPT_TEMPLATE.replace(
|
|
3435
3499
|
"{{font_library}}",
|
|
3436
3500
|
getFontLibrarySample()
|
|
3437
|
-
).replace("{{
|
|
3501
|
+
).replace("{{visual_design_references}}", getDesignReferencesSample()).replace("{{ui_case_studies}}", getUiInspirationSample());
|
|
3438
3502
|
if (specContext) {
|
|
3439
3503
|
prompt += `
|
|
3440
3504
|
|
|
@@ -3449,7 +3513,7 @@ ${specContext}`;
|
|
|
3449
3513
|
|
|
3450
3514
|
// src/subagents/designExpert/index.ts
|
|
3451
3515
|
var DESCRIPTION = `
|
|
3452
|
-
Visual design expert. Describe the situation and what you need \u2014 the agent decides what to deliver. It reads the spec files automatically. Include relevant user requirements and context it can't get from the spec, but do not list specific deliverables or tell it how to do its job.
|
|
3516
|
+
Visual design expert. Describe the situation and what you need \u2014 the agent decides what to deliver. It reads the spec files automatically. Include relevant user requirements and context it can't get from the spec, but do not list specific deliverables or tell it how to do its job. Do not suggest implementation details or ideas - only relay what is needed.
|
|
3453
3517
|
`.trim();
|
|
3454
3518
|
var designExpertTool = {
|
|
3455
3519
|
definition: {
|
|
@@ -3485,6 +3549,7 @@ var designExpertTool = {
|
|
|
3485
3549
|
subAgentId: "visualDesignExpert",
|
|
3486
3550
|
signal: context.signal,
|
|
3487
3551
|
parentToolId: context.toolCallId,
|
|
3552
|
+
requestId: context.requestId,
|
|
3488
3553
|
onEvent: context.onEvent,
|
|
3489
3554
|
resolveExternalTool: context.resolveExternalTool,
|
|
3490
3555
|
toolRegistry: context.toolRegistry,
|
|
@@ -3781,6 +3846,7 @@ var productVisionTool = {
|
|
|
3781
3846
|
subAgentId: "productVision",
|
|
3782
3847
|
signal: context.signal,
|
|
3783
3848
|
parentToolId: context.toolCallId,
|
|
3849
|
+
requestId: context.requestId,
|
|
3784
3850
|
onEvent: context.onEvent,
|
|
3785
3851
|
resolveExternalTool: context.resolveExternalTool,
|
|
3786
3852
|
toolRegistry: context.toolRegistry,
|
|
@@ -3928,6 +3994,7 @@ var codeSanityCheckTool = {
|
|
|
3928
3994
|
subAgentId: "codeSanityCheck",
|
|
3929
3995
|
signal: context.signal,
|
|
3930
3996
|
parentToolId: context.toolCallId,
|
|
3997
|
+
requestId: context.requestId,
|
|
3931
3998
|
onEvent: context.onEvent,
|
|
3932
3999
|
resolveExternalTool: context.resolveExternalTool,
|
|
3933
4000
|
toolRegistry: context.toolRegistry
|
|
@@ -4020,6 +4087,7 @@ function executeTool(name, input, context) {
|
|
|
4020
4087
|
|
|
4021
4088
|
// src/session.ts
|
|
4022
4089
|
import fs17 from "fs";
|
|
4090
|
+
var log6 = createLogger("session");
|
|
4023
4091
|
var SESSION_FILE = ".remy-session.json";
|
|
4024
4092
|
function loadSession(state) {
|
|
4025
4093
|
try {
|
|
@@ -4027,6 +4095,7 @@ function loadSession(state) {
|
|
|
4027
4095
|
const data = JSON.parse(raw);
|
|
4028
4096
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
4029
4097
|
state.messages = sanitizeMessages(data.messages);
|
|
4098
|
+
log6.info("Session loaded", { messageCount: state.messages.length });
|
|
4030
4099
|
return true;
|
|
4031
4100
|
}
|
|
4032
4101
|
} catch {
|
|
@@ -4076,7 +4145,9 @@ function saveSession(state) {
|
|
|
4076
4145
|
JSON.stringify({ messages: state.messages }, null, 2),
|
|
4077
4146
|
"utf-8"
|
|
4078
4147
|
);
|
|
4079
|
-
|
|
4148
|
+
log6.info("Session saved", { messageCount: state.messages.length });
|
|
4149
|
+
} catch (err) {
|
|
4150
|
+
log6.warn("Session save failed", { error: err.message });
|
|
4080
4151
|
}
|
|
4081
4152
|
}
|
|
4082
4153
|
function clearSession(state) {
|
|
@@ -4277,6 +4348,7 @@ function friendlyError(raw) {
|
|
|
4277
4348
|
}
|
|
4278
4349
|
|
|
4279
4350
|
// src/agent.ts
|
|
4351
|
+
var log7 = createLogger("agent");
|
|
4280
4352
|
function getTextContent(blocks) {
|
|
4281
4353
|
return blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
4282
4354
|
}
|
|
@@ -4314,17 +4386,17 @@ async function runTurn(params) {
|
|
|
4314
4386
|
onEvent,
|
|
4315
4387
|
resolveExternalTool,
|
|
4316
4388
|
hidden,
|
|
4389
|
+
requestId,
|
|
4317
4390
|
toolRegistry,
|
|
4318
4391
|
onBackgroundComplete
|
|
4319
4392
|
} = params;
|
|
4320
4393
|
const tools2 = getToolDefinitions(onboardingState);
|
|
4321
|
-
|
|
4322
|
-
|
|
4394
|
+
log7.info("Turn started", {
|
|
4395
|
+
requestId,
|
|
4396
|
+
model,
|
|
4323
4397
|
toolCount: tools2.length,
|
|
4324
|
-
tools: tools2.map((t) => t.name),
|
|
4325
4398
|
...attachments && attachments.length > 0 && {
|
|
4326
|
-
attachmentCount: attachments.length
|
|
4327
|
-
attachmentUrls: attachments.map((a) => a.url)
|
|
4399
|
+
attachmentCount: attachments.length
|
|
4328
4400
|
}
|
|
4329
4401
|
});
|
|
4330
4402
|
onEvent({ type: "turn_started" });
|
|
@@ -4334,10 +4406,6 @@ async function runTurn(params) {
|
|
|
4334
4406
|
}
|
|
4335
4407
|
if (attachments && attachments.length > 0) {
|
|
4336
4408
|
userMsg.attachments = attachments;
|
|
4337
|
-
log.debug("Attachments added to user message", {
|
|
4338
|
-
count: attachments.length,
|
|
4339
|
-
urls: attachments.map((a) => a.url)
|
|
4340
|
-
});
|
|
4341
4409
|
}
|
|
4342
4410
|
state.messages.push(userMsg);
|
|
4343
4411
|
const STATUS_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -4398,11 +4466,6 @@ async function runTurn(params) {
|
|
|
4398
4466
|
}
|
|
4399
4467
|
acc.lastEmittedCount = result.emittedCount;
|
|
4400
4468
|
acc.started = true;
|
|
4401
|
-
log.debug("Streaming partial tool_start", {
|
|
4402
|
-
id,
|
|
4403
|
-
name,
|
|
4404
|
-
emittedCount: result.emittedCount
|
|
4405
|
-
});
|
|
4406
4469
|
onEvent({
|
|
4407
4470
|
type: "tool_start",
|
|
4408
4471
|
id,
|
|
@@ -4418,10 +4481,6 @@ async function runTurn(params) {
|
|
|
4418
4481
|
}
|
|
4419
4482
|
if (!acc.started) {
|
|
4420
4483
|
acc.started = true;
|
|
4421
|
-
log.debug("Streaming content tool: emitting early tool_start", {
|
|
4422
|
-
id,
|
|
4423
|
-
name
|
|
4424
|
-
});
|
|
4425
4484
|
onEvent({ type: "tool_start", id, name, input: partial });
|
|
4426
4485
|
}
|
|
4427
4486
|
if (transform) {
|
|
@@ -4429,18 +4488,8 @@ async function runTurn(params) {
|
|
|
4429
4488
|
if (result === null) {
|
|
4430
4489
|
return;
|
|
4431
4490
|
}
|
|
4432
|
-
log.debug("Streaming content tool: emitting tool_input_delta", {
|
|
4433
|
-
id,
|
|
4434
|
-
name,
|
|
4435
|
-
resultLength: result.length
|
|
4436
|
-
});
|
|
4437
4491
|
onEvent({ type: "tool_input_delta", id, name, result });
|
|
4438
4492
|
} else {
|
|
4439
|
-
log.debug("Streaming content tool: emitting tool_input_delta", {
|
|
4440
|
-
id,
|
|
4441
|
-
name,
|
|
4442
|
-
contentLength: content.length
|
|
4443
|
-
});
|
|
4444
4493
|
onEvent({ type: "tool_input_delta", id, name, result: content });
|
|
4445
4494
|
}
|
|
4446
4495
|
}
|
|
@@ -4449,6 +4498,7 @@ async function runTurn(params) {
|
|
|
4449
4498
|
{
|
|
4450
4499
|
...apiConfig,
|
|
4451
4500
|
model,
|
|
4501
|
+
requestId,
|
|
4452
4502
|
system,
|
|
4453
4503
|
messages: cleanMessagesForApi(state.messages),
|
|
4454
4504
|
tools: tools2,
|
|
@@ -4500,12 +4550,6 @@ async function runTurn(params) {
|
|
|
4500
4550
|
case "tool_input_delta": {
|
|
4501
4551
|
const acc = getOrCreateAccumulator2(event.id, event.name);
|
|
4502
4552
|
acc.json += event.delta;
|
|
4503
|
-
log.debug("Received tool_input_delta", {
|
|
4504
|
-
id: event.id,
|
|
4505
|
-
name: event.name,
|
|
4506
|
-
deltaLength: event.delta.length,
|
|
4507
|
-
accumulatedLength: acc.json.length
|
|
4508
|
-
});
|
|
4509
4553
|
try {
|
|
4510
4554
|
const partial = parsePartialJson(acc.json);
|
|
4511
4555
|
await handlePartialInput(acc, event.id, event.name, partial);
|
|
@@ -4515,11 +4559,6 @@ async function runTurn(params) {
|
|
|
4515
4559
|
}
|
|
4516
4560
|
case "tool_input_args": {
|
|
4517
4561
|
const acc = getOrCreateAccumulator2(event.id, event.name);
|
|
4518
|
-
log.debug("Received tool_input_args", {
|
|
4519
|
-
id: event.id,
|
|
4520
|
-
name: event.name,
|
|
4521
|
-
keys: Object.keys(event.args)
|
|
4522
|
-
});
|
|
4523
4562
|
await handlePartialInput(acc, event.id, event.name, event.args);
|
|
4524
4563
|
break;
|
|
4525
4564
|
}
|
|
@@ -4536,11 +4575,10 @@ async function runTurn(params) {
|
|
|
4536
4575
|
const tool = getToolByName(event.name);
|
|
4537
4576
|
const wasStreamed = acc?.started ?? false;
|
|
4538
4577
|
const isInputStreaming = !!tool?.streaming?.partialInput;
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
isInputStreaming
|
|
4578
|
+
log7.info("Tool received", {
|
|
4579
|
+
requestId,
|
|
4580
|
+
toolCallId: event.id,
|
|
4581
|
+
name: event.name
|
|
4544
4582
|
});
|
|
4545
4583
|
if (!wasStreamed || isInputStreaming) {
|
|
4546
4584
|
onEvent({
|
|
@@ -4594,7 +4632,8 @@ async function runTurn(params) {
|
|
|
4594
4632
|
onEvent({ type: "turn_done" });
|
|
4595
4633
|
return;
|
|
4596
4634
|
}
|
|
4597
|
-
|
|
4635
|
+
log7.info("Tools executing", {
|
|
4636
|
+
requestId,
|
|
4598
4637
|
count: toolCalls.length,
|
|
4599
4638
|
tools: toolCalls.map((tc) => tc.name)
|
|
4600
4639
|
});
|
|
@@ -4637,9 +4676,10 @@ async function runTurn(params) {
|
|
|
4637
4676
|
let result;
|
|
4638
4677
|
if (EXTERNAL_TOOLS.has(tc.name) && resolveExternalTool) {
|
|
4639
4678
|
saveSession(state);
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4679
|
+
log7.info("Waiting for external tool result", {
|
|
4680
|
+
requestId,
|
|
4681
|
+
toolCallId: tc.id,
|
|
4682
|
+
name: tc.name
|
|
4643
4683
|
});
|
|
4644
4684
|
result = await resolveExternalTool(tc.id, tc.name, input);
|
|
4645
4685
|
} else {
|
|
@@ -4650,6 +4690,7 @@ async function runTurn(params) {
|
|
|
4650
4690
|
onEvent: wrappedOnEvent,
|
|
4651
4691
|
resolveExternalTool,
|
|
4652
4692
|
toolCallId: tc.id,
|
|
4693
|
+
requestId,
|
|
4653
4694
|
subAgentMessages,
|
|
4654
4695
|
toolRegistry,
|
|
4655
4696
|
onBackgroundComplete,
|
|
@@ -4688,11 +4729,12 @@ async function runTurn(params) {
|
|
|
4688
4729
|
run(tc.input);
|
|
4689
4730
|
const r = await resultPromise;
|
|
4690
4731
|
toolRegistry?.unregister(tc.id);
|
|
4691
|
-
|
|
4732
|
+
log7.info("Tool completed", {
|
|
4733
|
+
requestId,
|
|
4734
|
+
toolCallId: tc.id,
|
|
4692
4735
|
name: tc.name,
|
|
4693
|
-
|
|
4694
|
-
isError: r.isError
|
|
4695
|
-
resultLength: r.result.length
|
|
4736
|
+
durationMs: Date.now() - toolStart,
|
|
4737
|
+
isError: r.isError
|
|
4696
4738
|
});
|
|
4697
4739
|
onEvent({
|
|
4698
4740
|
type: "tool_done",
|
|
@@ -4738,6 +4780,7 @@ async function runTurn(params) {
|
|
|
4738
4780
|
}
|
|
4739
4781
|
|
|
4740
4782
|
// src/toolRegistry.ts
|
|
4783
|
+
var log8 = createLogger("tool-registry");
|
|
4741
4784
|
var ToolRegistry = class {
|
|
4742
4785
|
entries = /* @__PURE__ */ new Map();
|
|
4743
4786
|
onEvent;
|
|
@@ -4763,6 +4806,7 @@ var ToolRegistry = class {
|
|
|
4763
4806
|
if (!entry) {
|
|
4764
4807
|
return false;
|
|
4765
4808
|
}
|
|
4809
|
+
log8.info("Tool stopped", { toolCallId: id, name: entry.name, mode });
|
|
4766
4810
|
entry.abortController.abort(mode);
|
|
4767
4811
|
if (mode === "graceful") {
|
|
4768
4812
|
const partial = entry.getPartialResult?.() ?? "";
|
|
@@ -4795,6 +4839,7 @@ ${partial}` : "[INTERRUPTED] Tool execution was stopped.";
|
|
|
4795
4839
|
if (!entry) {
|
|
4796
4840
|
return false;
|
|
4797
4841
|
}
|
|
4842
|
+
log8.info("Tool restarted", { toolCallId: id, name: entry.name });
|
|
4798
4843
|
entry.abortController.abort("restart");
|
|
4799
4844
|
const newInput = patchedInput ? { ...entry.input, ...patchedInput } : entry.input;
|
|
4800
4845
|
this.onEvent?.({
|
|
@@ -4810,6 +4855,7 @@ ${partial}` : "[INTERRUPTED] Tool execution was stopped.";
|
|
|
4810
4855
|
};
|
|
4811
4856
|
|
|
4812
4857
|
// src/headless.ts
|
|
4858
|
+
var log9 = createLogger("headless");
|
|
4813
4859
|
function loadActionPrompt(name) {
|
|
4814
4860
|
return readAsset("prompt", "actions", `${name}.md`);
|
|
4815
4861
|
}
|
|
@@ -4908,6 +4954,11 @@ ${xmlParts}
|
|
|
4908
4954
|
}
|
|
4909
4955
|
}
|
|
4910
4956
|
}
|
|
4957
|
+
log9.info("Background complete", {
|
|
4958
|
+
toolCallId,
|
|
4959
|
+
name,
|
|
4960
|
+
requestId: currentRequestId
|
|
4961
|
+
});
|
|
4911
4962
|
onEvent({
|
|
4912
4963
|
type: "tool_background_complete",
|
|
4913
4964
|
id: toolCallId,
|
|
@@ -5100,6 +5151,7 @@ ${xmlParts}
|
|
|
5100
5151
|
currentRequestId = requestId;
|
|
5101
5152
|
currentAbort = new AbortController();
|
|
5102
5153
|
completedEmitted = false;
|
|
5154
|
+
const turnStart = Date.now();
|
|
5103
5155
|
const attachments = parsed.attachments;
|
|
5104
5156
|
if (attachments?.length) {
|
|
5105
5157
|
console.warn(
|
|
@@ -5131,6 +5183,7 @@ ${xmlParts}
|
|
|
5131
5183
|
system,
|
|
5132
5184
|
model: opts.model,
|
|
5133
5185
|
onboardingState,
|
|
5186
|
+
requestId,
|
|
5134
5187
|
signal: currentAbort.signal,
|
|
5135
5188
|
onEvent,
|
|
5136
5189
|
resolveExternalTool,
|
|
@@ -5145,11 +5198,20 @@ ${xmlParts}
|
|
|
5145
5198
|
requestId
|
|
5146
5199
|
);
|
|
5147
5200
|
}
|
|
5201
|
+
log9.info("Turn complete", {
|
|
5202
|
+
requestId,
|
|
5203
|
+
durationMs: Date.now() - turnStart
|
|
5204
|
+
});
|
|
5148
5205
|
} catch (err) {
|
|
5149
5206
|
if (!completedEmitted) {
|
|
5150
5207
|
emit("error", { error: err.message }, requestId);
|
|
5151
5208
|
emit("completed", { success: false, error: err.message }, requestId);
|
|
5152
5209
|
}
|
|
5210
|
+
log9.warn("Command failed", {
|
|
5211
|
+
action: "message",
|
|
5212
|
+
requestId,
|
|
5213
|
+
error: err.message
|
|
5214
|
+
});
|
|
5153
5215
|
}
|
|
5154
5216
|
currentAbort = null;
|
|
5155
5217
|
currentRequestId = void 0;
|
|
@@ -5165,6 +5227,7 @@ ${xmlParts}
|
|
|
5165
5227
|
return;
|
|
5166
5228
|
}
|
|
5167
5229
|
const { action, requestId } = parsed;
|
|
5230
|
+
log9.info("Command received", { action, requestId });
|
|
5168
5231
|
if (action === "tool_result" && parsed.id) {
|
|
5169
5232
|
const id = parsed.id;
|
|
5170
5233
|
const result = parsed.result ?? "";
|