@mindstudio-ai/remy 0.1.274 → 0.1.276
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 +332 -70
- package/dist/index.js +354 -79
- package/dist/prompt/static/team.md +1 -1
- package/dist/subagents/designExpert/prompts/images.md +11 -1
- package/dist/subagents/designExpert/prompts/instructions.md +1 -0
- package/dist/subagents/designExpert/tools/images/enhance-image-prompt.md +7 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -270,7 +270,7 @@ function isRetryableError(error) {
|
|
|
270
270
|
/Unable to download/i.test(error);
|
|
271
271
|
}
|
|
272
272
|
function sleep(ms) {
|
|
273
|
-
return new Promise((
|
|
273
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
274
274
|
}
|
|
275
275
|
async function* streamChatWithRetry(params, options) {
|
|
276
276
|
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
@@ -1203,7 +1203,7 @@ function formatCliResult(r) {
|
|
|
1203
1203
|
return logBlock + body + truncNote;
|
|
1204
1204
|
}
|
|
1205
1205
|
function runCli(command, args2, options) {
|
|
1206
|
-
return new Promise((
|
|
1206
|
+
return new Promise((resolve4) => {
|
|
1207
1207
|
const timeout = options?.timeout ?? 6e4;
|
|
1208
1208
|
const maxBuffer = options?.maxBuffer ?? 1024 * 1024;
|
|
1209
1209
|
let finalArgs = args2;
|
|
@@ -1232,7 +1232,7 @@ function runCli(command, args2, options) {
|
|
|
1232
1232
|
if (killTimer) {
|
|
1233
1233
|
clearTimeout(killTimer);
|
|
1234
1234
|
}
|
|
1235
|
-
|
|
1235
|
+
resolve4(result);
|
|
1236
1236
|
};
|
|
1237
1237
|
const child = spawn(command, finalArgs, {
|
|
1238
1238
|
stdio: [options?.stdin ? "pipe" : "ignore", "pipe", "pipe"]
|
|
@@ -2173,7 +2173,7 @@ var init_surfaces = __esm({
|
|
|
2173
2173
|
userPickable: true
|
|
2174
2174
|
},
|
|
2175
2175
|
imageGeneration: {
|
|
2176
|
-
default: "
|
|
2176
|
+
default: "gpt-image-2",
|
|
2177
2177
|
label: "Image Generation",
|
|
2178
2178
|
description: "Creates images for your product \u2014 icons, illustrations, photos, and any other visual assets.",
|
|
2179
2179
|
modelType: "image_generation",
|
|
@@ -2409,22 +2409,74 @@ var init_cleanMessages = __esm({
|
|
|
2409
2409
|
}
|
|
2410
2410
|
});
|
|
2411
2411
|
|
|
2412
|
-
// src/
|
|
2413
|
-
function capToolResult(result) {
|
|
2412
|
+
// src/historyLimits.ts
|
|
2413
|
+
function capToolResult(result, maxBytes = MAX_TOOL_RESULT_BYTES) {
|
|
2414
2414
|
const total = Buffer.byteLength(result, "utf-8");
|
|
2415
|
-
if (total <=
|
|
2415
|
+
if (total <= maxBytes) {
|
|
2416
2416
|
return result;
|
|
2417
2417
|
}
|
|
2418
|
-
const head = Buffer.from(result, "utf-8").subarray(0,
|
|
2418
|
+
const head = Buffer.from(result, "utf-8").subarray(0, maxBytes).toString("utf-8");
|
|
2419
2419
|
return head + `
|
|
2420
2420
|
|
|
2421
|
-
(tool result truncated at ${(
|
|
2421
|
+
(tool result truncated at ${(maxBytes / 1024).toFixed(0)}KB of ${(total / 1024).toFixed(0)}KB \u2014 too large to keep in context. Narrow the call (select fewer fields, paginate, or query a subset) instead of fetching everything.)`;
|
|
2422
2422
|
}
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2423
|
+
function capSubAgentTranscript(messages) {
|
|
2424
|
+
for (const msg of messages) {
|
|
2425
|
+
capMessageForHistory(msg, MAX_SUBAGENT_RESULT_BYTES);
|
|
2426
|
+
}
|
|
2427
|
+
const sizes = messages.map(
|
|
2428
|
+
(m) => Buffer.byteLength(JSON.stringify(m), "utf-8") + 1
|
|
2429
|
+
);
|
|
2430
|
+
let total = sizes.reduce((a, b) => a + b, 0);
|
|
2431
|
+
if (total <= MAX_SUBAGENT_TRANSCRIPT_BYTES) {
|
|
2432
|
+
return messages;
|
|
2433
|
+
}
|
|
2434
|
+
let start = 0;
|
|
2435
|
+
while (start < messages.length - 1 && total > MAX_SUBAGENT_TRANSCRIPT_BYTES) {
|
|
2436
|
+
total -= sizes[start];
|
|
2437
|
+
start++;
|
|
2438
|
+
}
|
|
2439
|
+
while (start < messages.length - 1 && messages[start].role === "user" && messages[start].toolCallId) {
|
|
2440
|
+
start++;
|
|
2441
|
+
}
|
|
2442
|
+
return messages.slice(start);
|
|
2443
|
+
}
|
|
2444
|
+
function capMessageForHistory(msg, maxBytes = MAX_TOOL_RESULT_BYTES) {
|
|
2445
|
+
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
2446
|
+
for (const block of msg.content) {
|
|
2447
|
+
if (block.type !== "tool") {
|
|
2448
|
+
continue;
|
|
2449
|
+
}
|
|
2450
|
+
if (typeof block.result === "string") {
|
|
2451
|
+
block.result = capToolResult(block.result, maxBytes);
|
|
2452
|
+
}
|
|
2453
|
+
if (typeof block.backgroundResult === "string") {
|
|
2454
|
+
block.backgroundResult = capToolResult(
|
|
2455
|
+
block.backgroundResult,
|
|
2456
|
+
maxBytes
|
|
2457
|
+
);
|
|
2458
|
+
}
|
|
2459
|
+
if (Array.isArray(block.subAgentMessages)) {
|
|
2460
|
+
block.subAgentMessages = capSubAgentTranscript(block.subAgentMessages);
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
} else if (msg.role === "user" && msg.toolCallId && typeof msg.content === "string") {
|
|
2464
|
+
msg.content = capToolResult(msg.content, maxBytes);
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
var MAX_TOOL_RESULT_BYTES, MAX_SUBAGENT_RESULT_BYTES, MAX_SUBAGENT_TRANSCRIPT_BYTES, HISTORY_PAGE_MAX_BYTES, HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT, ROTATE_THRESHOLD_BYTES, RETAIN_TAIL_BYTES, ARCHIVE_RETENTION_BYTES;
|
|
2468
|
+
var init_historyLimits = __esm({
|
|
2469
|
+
"src/historyLimits.ts"() {
|
|
2426
2470
|
"use strict";
|
|
2427
2471
|
MAX_TOOL_RESULT_BYTES = 256 * 1024;
|
|
2472
|
+
MAX_SUBAGENT_RESULT_BYTES = 32 * 1024;
|
|
2473
|
+
MAX_SUBAGENT_TRANSCRIPT_BYTES = 512 * 1024;
|
|
2474
|
+
HISTORY_PAGE_MAX_BYTES = 2 * 1024 * 1024;
|
|
2475
|
+
HISTORY_DEFAULT_LIMIT = 500;
|
|
2476
|
+
HISTORY_MAX_LIMIT = 2e3;
|
|
2477
|
+
ROTATE_THRESHOLD_BYTES = 32 * 1024 * 1024;
|
|
2478
|
+
RETAIN_TAIL_BYTES = 16 * 1024 * 1024;
|
|
2479
|
+
ARCHIVE_RETENTION_BYTES = 64 * 1024 * 1024;
|
|
2428
2480
|
}
|
|
2429
2481
|
});
|
|
2430
2482
|
|
|
@@ -2457,22 +2509,11 @@ function loadSession(state) {
|
|
|
2457
2509
|
}
|
|
2458
2510
|
return false;
|
|
2459
2511
|
}
|
|
2460
|
-
function capOversizedResults(msg) {
|
|
2461
|
-
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
2462
|
-
for (const block of msg.content) {
|
|
2463
|
-
if (block.type === "tool" && typeof block.result === "string") {
|
|
2464
|
-
block.result = capToolResult(block.result);
|
|
2465
|
-
}
|
|
2466
|
-
}
|
|
2467
|
-
} else if (msg.role === "user" && msg.toolCallId && typeof msg.content === "string") {
|
|
2468
|
-
msg.content = capToolResult(msg.content);
|
|
2469
|
-
}
|
|
2470
|
-
}
|
|
2471
2512
|
function sanitizeMessages(messages) {
|
|
2472
2513
|
const result = [];
|
|
2473
2514
|
for (let i = 0; i < messages.length; i++) {
|
|
2474
2515
|
const msg = messages[i];
|
|
2475
|
-
|
|
2516
|
+
capMessageForHistory(msg);
|
|
2476
2517
|
result.push(msg);
|
|
2477
2518
|
if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
|
|
2478
2519
|
continue;
|
|
@@ -2584,6 +2625,9 @@ function parseArchive(name) {
|
|
|
2584
2625
|
const raw = fs10.readFileSync(path4.join(ARCHIVE_DIR, name), "utf-8");
|
|
2585
2626
|
const data = JSON.parse(raw);
|
|
2586
2627
|
const messages = Array.isArray(data?.messages) ? data.messages : [];
|
|
2628
|
+
for (const msg of messages) {
|
|
2629
|
+
capMessageForHistory(msg);
|
|
2630
|
+
}
|
|
2587
2631
|
archiveCountCache.set(name, messages.length);
|
|
2588
2632
|
archiveMsgCache.set(name, messages);
|
|
2589
2633
|
while (archiveMsgCache.size > ARCHIVE_MSG_CACHE_MAX) {
|
|
@@ -2694,6 +2738,29 @@ function getHistoryPage(state, opts) {
|
|
|
2694
2738
|
messages.push(state.messages[i]);
|
|
2695
2739
|
}
|
|
2696
2740
|
}
|
|
2741
|
+
if (messages.length > 1) {
|
|
2742
|
+
let bytes = 0;
|
|
2743
|
+
let cut = 0;
|
|
2744
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2745
|
+
bytes += Buffer.byteLength(JSON.stringify(messages[i]), "utf-8") + 1;
|
|
2746
|
+
if (bytes > HISTORY_PAGE_MAX_BYTES && i < messages.length - 1) {
|
|
2747
|
+
cut = i + 1;
|
|
2748
|
+
break;
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
if (cut > 0) {
|
|
2752
|
+
while (cut < messages.length - 1 && messages[cut]?.role === "user" && messages[cut]?.toolCallId) {
|
|
2753
|
+
cut++;
|
|
2754
|
+
}
|
|
2755
|
+
messages.splice(0, cut);
|
|
2756
|
+
startIndex += cut;
|
|
2757
|
+
log3.info("History page trimmed to byte budget", {
|
|
2758
|
+
dropped: cut,
|
|
2759
|
+
kept: messages.length,
|
|
2760
|
+
startIndex
|
|
2761
|
+
});
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2697
2764
|
return { messages, startIndex, endIndex, totalMessageCount: total };
|
|
2698
2765
|
}
|
|
2699
2766
|
function rotate(state) {
|
|
@@ -2755,7 +2822,7 @@ function clearSession(state) {
|
|
|
2755
2822
|
});
|
|
2756
2823
|
}
|
|
2757
2824
|
}
|
|
2758
|
-
var log3, SESSION_FILE, ARCHIVE_DIR,
|
|
2825
|
+
var log3, SESSION_FILE, ARCHIVE_DIR, ARCHIVE_NAME_RE, archiveSortKey, ARCHIVE_COUNT_RE, archiveCountCache, archiveMsgCache, ARCHIVE_MSG_CACHE_MAX;
|
|
2759
2826
|
var init_session = __esm({
|
|
2760
2827
|
"src/session.ts"() {
|
|
2761
2828
|
"use strict";
|
|
@@ -2763,18 +2830,13 @@ var init_session = __esm({
|
|
|
2763
2830
|
init_logger();
|
|
2764
2831
|
init_compaction();
|
|
2765
2832
|
init_cleanMessages();
|
|
2766
|
-
|
|
2833
|
+
init_historyLimits();
|
|
2767
2834
|
log3 = createLogger("session");
|
|
2768
2835
|
SESSION_FILE = ".remy-session.json";
|
|
2769
2836
|
ARCHIVE_DIR = ".logs/sessions";
|
|
2770
|
-
ROTATE_THRESHOLD_BYTES = 32 * 1024 * 1024;
|
|
2771
|
-
RETAIN_TAIL_BYTES = 16 * 1024 * 1024;
|
|
2772
|
-
ARCHIVE_RETENTION_BYTES = 64 * 1024 * 1024;
|
|
2773
2837
|
ARCHIVE_NAME_RE = /^(cleared|rotated)-.*\.json$/;
|
|
2774
2838
|
archiveSortKey = (name) => name.replace(/^(cleared|rotated)-/, "");
|
|
2775
2839
|
ARCHIVE_COUNT_RE = /\.c(\d+)\.json$/;
|
|
2776
|
-
HISTORY_DEFAULT_LIMIT = 500;
|
|
2777
|
-
HISTORY_MAX_LIMIT = 2e3;
|
|
2778
2840
|
archiveCountCache = /* @__PURE__ */ new Map();
|
|
2779
2841
|
archiveMsgCache = /* @__PURE__ */ new Map();
|
|
2780
2842
|
ARCHIVE_MSG_CACHE_MAX = 3;
|
|
@@ -2963,9 +3025,9 @@ function parseFrontmatter(content) {
|
|
|
2963
3025
|
}
|
|
2964
3026
|
const fields = {};
|
|
2965
3027
|
for (const line of match[1].split("\n")) {
|
|
2966
|
-
const
|
|
2967
|
-
if (
|
|
2968
|
-
fields[line.slice(0,
|
|
3028
|
+
const sep2 = line.indexOf(":");
|
|
3029
|
+
if (sep2 > 0) {
|
|
3030
|
+
fields[line.slice(0, sep2).trim()] = line.slice(sep2 + 1).trim();
|
|
2969
3031
|
}
|
|
2970
3032
|
}
|
|
2971
3033
|
return fields;
|
|
@@ -3420,7 +3482,7 @@ var init_bash = __esm({
|
|
|
3420
3482
|
async execute(input, context) {
|
|
3421
3483
|
const maxLines = input.maxLines === 0 ? Infinity : input.maxLines || DEFAULT_MAX_LINES2;
|
|
3422
3484
|
const timeoutMs = input.timeout ? input.timeout * 1e3 : DEFAULT_TIMEOUT_MS;
|
|
3423
|
-
return new Promise((
|
|
3485
|
+
return new Promise((resolve4) => {
|
|
3424
3486
|
const child = spawn2("sh", ["-c", input.command], {
|
|
3425
3487
|
// Pinned rather than inherited. `undefined` here means "wherever the
|
|
3426
3488
|
// process happens to be", which is the project root only by luck.
|
|
@@ -3445,9 +3507,9 @@ var init_bash = __esm({
|
|
|
3445
3507
|
clearTimeout(timer);
|
|
3446
3508
|
if (!output) {
|
|
3447
3509
|
if (code && code !== 0) {
|
|
3448
|
-
|
|
3510
|
+
resolve4(`Error: process exited with code ${code}`);
|
|
3449
3511
|
} else {
|
|
3450
|
-
|
|
3512
|
+
resolve4("(no output)");
|
|
3451
3513
|
}
|
|
3452
3514
|
return;
|
|
3453
3515
|
}
|
|
@@ -3473,18 +3535,18 @@ var init_bash = __esm({
|
|
|
3473
3535
|
`${(MAX_OUTPUT_BYTES / 1024).toFixed(0)}KB of ${(totalBytes / 1024).toFixed(0)}KB`
|
|
3474
3536
|
);
|
|
3475
3537
|
}
|
|
3476
|
-
|
|
3538
|
+
resolve4(
|
|
3477
3539
|
truncated + `
|
|
3478
3540
|
|
|
3479
3541
|
(truncated at ${reasons.join(" / ")} \u2014 narrow the command (grep, head/tail, smaller paths) instead of increasing limits)`
|
|
3480
3542
|
);
|
|
3481
3543
|
} else {
|
|
3482
|
-
|
|
3544
|
+
resolve4(output);
|
|
3483
3545
|
}
|
|
3484
3546
|
});
|
|
3485
3547
|
child.on("error", (err) => {
|
|
3486
3548
|
clearTimeout(timer);
|
|
3487
|
-
|
|
3549
|
+
resolve4(`Error: ${err.message}`);
|
|
3488
3550
|
});
|
|
3489
3551
|
});
|
|
3490
3552
|
}
|
|
@@ -3607,17 +3669,17 @@ var init_grep = __esm({
|
|
|
3607
3669
|
}
|
|
3608
3670
|
const rgCmd = `rg ${rgFlags}${globFlag} '${escaped}' ${searchPath}`;
|
|
3609
3671
|
const grepCmd = `grep ${grepFlags} '${escaped}' ${searchPath} --include='*.ts' --include='*.tsx' --include='*.js' --include='*.json' --include='*.md'`;
|
|
3610
|
-
return new Promise((
|
|
3672
|
+
return new Promise((resolve4) => {
|
|
3611
3673
|
exec(rgCmd, { maxBuffer: 512 * 1024 }, (err, stdout) => {
|
|
3612
3674
|
if (stdout?.trim()) {
|
|
3613
|
-
|
|
3675
|
+
resolve4(formatResults(stdout, max, mode));
|
|
3614
3676
|
return;
|
|
3615
3677
|
}
|
|
3616
3678
|
exec(grepCmd, { maxBuffer: 512 * 1024 }, (_err, grepStdout) => {
|
|
3617
3679
|
if (grepStdout?.trim()) {
|
|
3618
|
-
|
|
3680
|
+
resolve4(formatResults(grepStdout, max, mode));
|
|
3619
3681
|
} else {
|
|
3620
|
-
|
|
3682
|
+
resolve4("No matches found.");
|
|
3621
3683
|
}
|
|
3622
3684
|
});
|
|
3623
3685
|
});
|
|
@@ -3979,7 +4041,7 @@ var init_restartProcess = __esm({
|
|
|
3979
4041
|
async execute(input) {
|
|
3980
4042
|
const data = await lspRequest("/restart-process", { name: input.name });
|
|
3981
4043
|
if (data.ok) {
|
|
3982
|
-
await new Promise((
|
|
4044
|
+
await new Promise((resolve4) => setTimeout(resolve4, 5e3));
|
|
3983
4045
|
return `Restarted ${input.name}.`;
|
|
3984
4046
|
}
|
|
3985
4047
|
return `Error: unexpected response: ${JSON.stringify(data)}`;
|
|
@@ -4237,6 +4299,26 @@ ${opts.styleMap}
|
|
|
4237
4299
|
${ANALYSIS_RESPONSE_FORMAT}`;
|
|
4238
4300
|
return p;
|
|
4239
4301
|
}
|
|
4302
|
+
async function renderHtmlViaSidecar(opts) {
|
|
4303
|
+
const result = await sidecarRequest(
|
|
4304
|
+
"/render-html",
|
|
4305
|
+
{
|
|
4306
|
+
html: opts.html,
|
|
4307
|
+
width: opts.width,
|
|
4308
|
+
height: opts.height,
|
|
4309
|
+
...opts.transparent ? { transparent: true } : {},
|
|
4310
|
+
...opts.scale != null ? { scale: opts.scale } : {}
|
|
4311
|
+
},
|
|
4312
|
+
{ timeout: VIEWPORT_CAPTURE_TIMEOUT_MS }
|
|
4313
|
+
);
|
|
4314
|
+
const url = result?.url;
|
|
4315
|
+
if (!url) {
|
|
4316
|
+
throw new Error(
|
|
4317
|
+
`No URL in sidecar render response. The browser may not be ready yet. Response: ${JSON.stringify(result)}`
|
|
4318
|
+
);
|
|
4319
|
+
}
|
|
4320
|
+
return { url, width: result.width, height: result.height };
|
|
4321
|
+
}
|
|
4240
4322
|
async function streamScreenshotAnalysis(opts) {
|
|
4241
4323
|
const { image, prompt, styleMap, onLog, model, apiConfig } = opts;
|
|
4242
4324
|
const url = await resolveImageRef(image, apiConfig);
|
|
@@ -4920,7 +5002,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
4920
5002
|
block.completedAt = Date.now();
|
|
4921
5003
|
const innerMsgs = subAgentMessages.get(r.id);
|
|
4922
5004
|
if (innerMsgs) {
|
|
4923
|
-
block.subAgentMessages = innerMsgs;
|
|
5005
|
+
block.subAgentMessages = capSubAgentTranscript(innerMsgs);
|
|
4924
5006
|
}
|
|
4925
5007
|
if (captureArtifacts?.includes(block.name) && !r.isError) {
|
|
4926
5008
|
try {
|
|
@@ -5008,7 +5090,7 @@ var init_runner = __esm({
|
|
|
5008
5090
|
init_logger();
|
|
5009
5091
|
init_usageLedger();
|
|
5010
5092
|
init_toolRegistry();
|
|
5011
|
-
|
|
5093
|
+
init_historyLimits();
|
|
5012
5094
|
init_statusWatcher();
|
|
5013
5095
|
init_cleanMessages();
|
|
5014
5096
|
log8 = createLogger("sub-agent");
|
|
@@ -5218,10 +5300,10 @@ function parseFrontmatter2(filePath) {
|
|
|
5218
5300
|
}
|
|
5219
5301
|
const fm = {};
|
|
5220
5302
|
for (const line of match[1].split("\n")) {
|
|
5221
|
-
const
|
|
5222
|
-
if (
|
|
5223
|
-
const key = line.slice(0,
|
|
5224
|
-
const val = line.slice(
|
|
5303
|
+
const sep2 = line.indexOf(":");
|
|
5304
|
+
if (sep2 > 0) {
|
|
5305
|
+
const key = line.slice(0, sep2).trim();
|
|
5306
|
+
const val = line.slice(sep2 + 1).trim();
|
|
5225
5307
|
fm[key] = val;
|
|
5226
5308
|
}
|
|
5227
5309
|
}
|
|
@@ -5615,7 +5697,7 @@ var init_screenshot2 = __esm({
|
|
|
5615
5697
|
init_surfaces();
|
|
5616
5698
|
screenshotDefinition = {
|
|
5617
5699
|
name: "screenshot",
|
|
5618
|
-
description: "Capture a screenshot of the app preview and get a description of what's on screen. Choose `fullPage`: `false` captures just the visible viewport (fast \u2014 for a specific section the page is scrolled to), `true` captures the entire page top-to-bottom (slower \u2014 for overall composition or content past the fold). Captures the settled page state \u2014 it cannot catch animations, transitions, or transient state. The analysis is not precise about every detail \u2014 for example it cannot reliably identify specific fonts by name, only describe what the letterforms look like. Optionally provide specific questions about what you're looking for. Use a bulleted list to ask many questions at once. To ask additional questions about a screenshot you have already captured, pass its URL as `imageUrl` to skip recapture; `imageUrl` also accepts the disk path of an image file (a user upload, a saved asset) to analyze that instead of the preview. If the screenshot requires interaction first (logging in, clicking a tab, dismissing a modal, scrolling to a section), use the instructions param to describe the steps. To
|
|
5700
|
+
description: "Capture a screenshot of the app preview and get a description of what's on screen. Choose `fullPage`: `false` captures just the visible viewport (fast \u2014 for a specific section the page is scrolled to), `true` captures the entire page top-to-bottom (slower \u2014 for overall composition or content past the fold). Captures the settled page state \u2014 it cannot catch animations, transitions, or transient state. The analysis is not precise about every detail \u2014 for example it cannot reliably identify specific fonts by name, only describe what the letterforms look like. Optionally provide specific questions about what you're looking for. Use a bulleted list to ask many questions at once. To ask additional questions about a screenshot you have already captured, pass its URL as `imageUrl` to skip recapture; `imageUrl` also accepts the disk path of an image file (a user upload, a saved asset) to analyze that instead of the preview. If the screenshot requires interaction first (logging in, clicking a tab, dismissing a modal, scrolling to a section), use the instructions param to describe the steps. To capture at exact pixel dimensions, set `width` and `height`: the tool clips to exactly that viewport and returns the image URL.",
|
|
5619
5701
|
inputSchema: {
|
|
5620
5702
|
type: "object",
|
|
5621
5703
|
properties: {
|
|
@@ -5646,7 +5728,7 @@ var init_screenshot2 = __esm({
|
|
|
5646
5728
|
format: {
|
|
5647
5729
|
type: "string",
|
|
5648
5730
|
enum: ["png", "jpeg"],
|
|
5649
|
-
description: "Output image format. Defaults to 'jpeg'. Use 'png' for crisp flat graphics
|
|
5731
|
+
description: "Output image format. Defaults to 'jpeg'. Use 'png' for crisp flat graphics where JPEG artifacts show on sharp type and edges."
|
|
5650
5732
|
},
|
|
5651
5733
|
instructions: {
|
|
5652
5734
|
type: "string",
|
|
@@ -5954,6 +6036,20 @@ var init_enhancePrompt = __esm({
|
|
|
5954
6036
|
});
|
|
5955
6037
|
|
|
5956
6038
|
// src/subagents/designExpert/tools/images/imageGenerator.ts
|
|
6039
|
+
function snapToOpenAiImageSize(width, height) {
|
|
6040
|
+
let best = OPENAI_IMAGE_SIZES[0];
|
|
6041
|
+
let bestScore = Infinity;
|
|
6042
|
+
for (const [w, h] of OPENAI_IMAGE_SIZES) {
|
|
6043
|
+
const aspectDiff = Math.abs(Math.log(w / h / (width / height)));
|
|
6044
|
+
const areaDiff = Math.abs(Math.log(w * h / (width * height)));
|
|
6045
|
+
const score = aspectDiff * 8 + areaDiff;
|
|
6046
|
+
if (score < bestScore) {
|
|
6047
|
+
bestScore = score;
|
|
6048
|
+
best = [w, h];
|
|
6049
|
+
}
|
|
6050
|
+
}
|
|
6051
|
+
return `${best[0]}x${best[1]}`;
|
|
6052
|
+
}
|
|
5957
6053
|
async function generateImageAssets(opts) {
|
|
5958
6054
|
const {
|
|
5959
6055
|
prompts,
|
|
@@ -5968,7 +6064,11 @@ async function generateImageAssets(opts) {
|
|
|
5968
6064
|
const sourceImages = opts.sourceImages?.length ? await resolveImageRefs(opts.sourceImages, apiConfig) : void 0;
|
|
5969
6065
|
const width = opts.width || 2048;
|
|
5970
6066
|
const height = opts.height || 2048;
|
|
5971
|
-
const config = {
|
|
6067
|
+
const config = {
|
|
6068
|
+
width,
|
|
6069
|
+
height,
|
|
6070
|
+
size: snapToOpenAiImageSize(width, height)
|
|
6071
|
+
};
|
|
5972
6072
|
if (sourceImages?.length) {
|
|
5973
6073
|
const [firstImage] = sourceImages;
|
|
5974
6074
|
config.images = sourceImages;
|
|
@@ -6087,7 +6187,7 @@ async function generateImageAssets(opts) {
|
|
|
6087
6187
|
);
|
|
6088
6188
|
return JSON.stringify({ images });
|
|
6089
6189
|
}
|
|
6090
|
-
var ANALYZE_PROMPT;
|
|
6190
|
+
var ANALYZE_PROMPT, OPENAI_IMAGE_SIZES;
|
|
6091
6191
|
var init_imageGenerator = __esm({
|
|
6092
6192
|
"src/subagents/designExpert/tools/images/imageGenerator.ts"() {
|
|
6093
6193
|
"use strict";
|
|
@@ -6095,7 +6195,22 @@ var init_imageGenerator = __esm({
|
|
|
6095
6195
|
init_analyzeImage();
|
|
6096
6196
|
init_uploadImage();
|
|
6097
6197
|
init_enhancePrompt();
|
|
6098
|
-
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 (starting with the title "Asset Review") and absolutely no other text. Do not use emojis - use unicode if you need symbols.';
|
|
6198
|
+
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), whether the artwork extends fully to all four edges of the canvas or sits inset (call out any baked-in border, frame, rounded-corner mask, drop-shadow margin, or mockup presentation such as an icon rendered on a background or device), 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 (starting with the title "Asset Review") and absolutely no other text. Do not use emojis - use unicode if you need symbols.';
|
|
6199
|
+
OPENAI_IMAGE_SIZES = [
|
|
6200
|
+
[768, 1024],
|
|
6201
|
+
[1024, 768],
|
|
6202
|
+
[1024, 1024],
|
|
6203
|
+
[1024, 1536],
|
|
6204
|
+
[1536, 1024],
|
|
6205
|
+
[1280, 2560],
|
|
6206
|
+
[2560, 1280],
|
|
6207
|
+
[1440, 2560],
|
|
6208
|
+
[2560, 1440],
|
|
6209
|
+
[1792, 2400],
|
|
6210
|
+
[2400, 1792],
|
|
6211
|
+
[2400, 2880],
|
|
6212
|
+
[2560, 2560]
|
|
6213
|
+
];
|
|
6099
6214
|
}
|
|
6100
6215
|
});
|
|
6101
6216
|
|
|
@@ -6252,6 +6367,150 @@ var init_editImages = __esm({
|
|
|
6252
6367
|
}
|
|
6253
6368
|
});
|
|
6254
6369
|
|
|
6370
|
+
// src/subagents/designExpert/tools/images/renderImage.ts
|
|
6371
|
+
var renderImage_exports = {};
|
|
6372
|
+
__export(renderImage_exports, {
|
|
6373
|
+
definition: () => definition7,
|
|
6374
|
+
execute: () => execute7
|
|
6375
|
+
});
|
|
6376
|
+
import { mkdir, unlink, writeFile } from "fs/promises";
|
|
6377
|
+
import { tmpdir } from "os";
|
|
6378
|
+
import { dirname, join, resolve as resolve2, sep } from "path";
|
|
6379
|
+
import { randomUUID } from "crypto";
|
|
6380
|
+
async function execute7(input, onLog, context) {
|
|
6381
|
+
const html = typeof input.html === "string" ? input.html : "";
|
|
6382
|
+
const width = Math.round(Number(input.width));
|
|
6383
|
+
const height = Math.round(Number(input.height));
|
|
6384
|
+
if (!html || !Number.isFinite(width) || !Number.isFinite(height) || width < MIN_DIMENSION || width > MAX_DIMENSION || height < MIN_DIMENSION || height > MAX_DIMENSION) {
|
|
6385
|
+
return `Error: renderImage requires an html string plus width/height between ${MIN_DIMENSION} and ${MAX_DIMENSION}.`;
|
|
6386
|
+
}
|
|
6387
|
+
const release = await acquireBrowserLock();
|
|
6388
|
+
let rendered;
|
|
6389
|
+
try {
|
|
6390
|
+
onLog?.("Rendering document in the sandbox browser...");
|
|
6391
|
+
rendered = await renderHtmlViaSidecar({
|
|
6392
|
+
html,
|
|
6393
|
+
width,
|
|
6394
|
+
height,
|
|
6395
|
+
transparent: input.transparentBackground === true,
|
|
6396
|
+
scale: typeof input.scale === "number" ? input.scale : void 0
|
|
6397
|
+
});
|
|
6398
|
+
} catch (err) {
|
|
6399
|
+
return `Error: render failed: ${err?.message ?? err}`;
|
|
6400
|
+
} finally {
|
|
6401
|
+
release();
|
|
6402
|
+
}
|
|
6403
|
+
let bytes;
|
|
6404
|
+
try {
|
|
6405
|
+
const res = await fetch(rendered.url);
|
|
6406
|
+
if (!res.ok) {
|
|
6407
|
+
throw new Error(`fetch returned ${res.status}`);
|
|
6408
|
+
}
|
|
6409
|
+
bytes = Buffer.from(await res.arrayBuffer());
|
|
6410
|
+
} catch (err) {
|
|
6411
|
+
return `Error: rendered but could not download the capture (${err?.message ?? err}). Temporary URL: ${rendered.url}`;
|
|
6412
|
+
}
|
|
6413
|
+
let savedPath;
|
|
6414
|
+
if (typeof input.savePath === "string" && input.savePath) {
|
|
6415
|
+
const absolute = resolve2(PROJECT_ROOT, input.savePath);
|
|
6416
|
+
if (!absolute.startsWith(PROJECT_ROOT + sep)) {
|
|
6417
|
+
return "Error: savePath must resolve inside the project.";
|
|
6418
|
+
}
|
|
6419
|
+
await mkdir(dirname(absolute), { recursive: true });
|
|
6420
|
+
await writeFile(absolute, bytes);
|
|
6421
|
+
savedPath = input.savePath;
|
|
6422
|
+
}
|
|
6423
|
+
onLog?.("Hosting the capture...");
|
|
6424
|
+
const tmpPath = join(tmpdir(), `render-${randomUUID()}.png`);
|
|
6425
|
+
let url = rendered.url;
|
|
6426
|
+
let temporary = true;
|
|
6427
|
+
try {
|
|
6428
|
+
await writeFile(tmpPath, bytes);
|
|
6429
|
+
const upload = await runMindstudioCliResult(["upload", tmpPath], {
|
|
6430
|
+
timeout: 6e4,
|
|
6431
|
+
onLog,
|
|
6432
|
+
caller: "designExpert"
|
|
6433
|
+
});
|
|
6434
|
+
const match = upload.ok ? upload.value.match(/https:\/\/\S+/g) : null;
|
|
6435
|
+
if (match?.length) {
|
|
6436
|
+
url = match[match.length - 1];
|
|
6437
|
+
temporary = false;
|
|
6438
|
+
}
|
|
6439
|
+
} finally {
|
|
6440
|
+
await unlink(tmpPath).catch(() => {
|
|
6441
|
+
});
|
|
6442
|
+
}
|
|
6443
|
+
const analysis = await analyzeImage({
|
|
6444
|
+
prompt: RENDER_ANALYZE_PROMPT,
|
|
6445
|
+
image: url,
|
|
6446
|
+
onLog,
|
|
6447
|
+
model: resolveModel("imageAnalysis", context?.models, context?.model)
|
|
6448
|
+
}).then((r) => r.analysis).catch((err) => `Could not review this image: ${err.message}`);
|
|
6449
|
+
return JSON.stringify({
|
|
6450
|
+
images: [
|
|
6451
|
+
{
|
|
6452
|
+
url,
|
|
6453
|
+
...temporary ? {
|
|
6454
|
+
note: "Durable hosting failed \u2014 this URL is dev-session scratch storage and may expire. Do not use it for app metadata; retry if a durable URL is needed."
|
|
6455
|
+
} : {},
|
|
6456
|
+
...savedPath ? { savedPath } : {},
|
|
6457
|
+
analysis,
|
|
6458
|
+
width: rendered.width,
|
|
6459
|
+
height: rendered.height
|
|
6460
|
+
}
|
|
6461
|
+
]
|
|
6462
|
+
});
|
|
6463
|
+
}
|
|
6464
|
+
var MIN_DIMENSION, MAX_DIMENSION, RENDER_ANALYZE_PROMPT, definition7;
|
|
6465
|
+
var init_renderImage = __esm({
|
|
6466
|
+
"src/subagents/designExpert/tools/images/renderImage.ts"() {
|
|
6467
|
+
"use strict";
|
|
6468
|
+
init_screenshot();
|
|
6469
|
+
init_browserLock();
|
|
6470
|
+
init_runMindstudioCli();
|
|
6471
|
+
init_analyzeImage();
|
|
6472
|
+
init_surfaces();
|
|
6473
|
+
init_projectRoot();
|
|
6474
|
+
MIN_DIMENSION = 16;
|
|
6475
|
+
MAX_DIMENSION = 4096;
|
|
6476
|
+
RENDER_ANALYZE_PROMPT = 'You are reviewing a browser-rendered graphic (composed from HTML/CSS by a designer) for fidelity. Report: whether the composition fills the full canvas or leaves unintended gaps at any edge, any clipped or overflowing text, whether custom webfonts appear to have loaded (distinctive letterforms vs generic fallback serif/sans), any misalignment or uneven spacing, any unintended scrollbars or default-styling artifacts, and \u2014 if the background is transparent \u2014 any fringing or stray opaque pixels at the edges. Then briefly describe the overall composition and how polished it looks. Be concise and practical. Respond only with your analysis as Markdown (starting with the title "Render Review") and absolutely no other text. Do not use emojis - use unicode if you need symbols.';
|
|
6477
|
+
definition7 = {
|
|
6478
|
+
name: "renderImage",
|
|
6479
|
+
description: "Render a self-contained HTML document in a real browser and capture it as a hosted PNG at exact pixel dimensions, with a fidelity review included. Deterministic \u2014 exact hex colors, real loaded webfonts, precise geometry \u2014 unlike generateImages, which is an image model. Use for token-exact graphics: Open Graph share cards, wordmarks, flat/geometric icon tiles, badges, any composition where letterforms and spacing carry the design. Compose with HTML/CSS (link webfonts from CDNs \u2014 the renderer waits for them to load); inline existing SVG markup when needed, but never hand-write new SVG path data.",
|
|
6480
|
+
inputSchema: {
|
|
6481
|
+
type: "object",
|
|
6482
|
+
properties: {
|
|
6483
|
+
html: {
|
|
6484
|
+
type: "string",
|
|
6485
|
+
description: "A complete, self-contained HTML document sized to fill the viewport (style html/body to the full dimensions with margin 0). External webfonts and images from CDNs are fine \u2014 loading is awaited before capture."
|
|
6486
|
+
},
|
|
6487
|
+
width: {
|
|
6488
|
+
type: "number",
|
|
6489
|
+
description: "Viewport width in CSS pixels (e.g. 1200 for an OG card, 512 for an icon tile). Range: 16-4096."
|
|
6490
|
+
},
|
|
6491
|
+
height: {
|
|
6492
|
+
type: "number",
|
|
6493
|
+
description: "Viewport height in CSS pixels. Range: 16-4096."
|
|
6494
|
+
},
|
|
6495
|
+
scale: {
|
|
6496
|
+
type: "number",
|
|
6497
|
+
description: "Device scale factor, 1-3. Output pixels = css \xD7 scale. Use 2 for crisp icon masters (e.g. a 512\xD7512 document captured at 1024\xD71024)."
|
|
6498
|
+
},
|
|
6499
|
+
transparentBackground: {
|
|
6500
|
+
type: "boolean",
|
|
6501
|
+
description: "Capture with true alpha: leave the document background transparent (no background on html/body) and the PNG keeps it. No background-removal model involved."
|
|
6502
|
+
},
|
|
6503
|
+
savePath: {
|
|
6504
|
+
type: "string",
|
|
6505
|
+
description: "Optional project-relative path to also save the PNG into the app (e.g. 'dist/interfaces/web/public/og-image.png' so the deployed site self-hosts it)."
|
|
6506
|
+
}
|
|
6507
|
+
},
|
|
6508
|
+
required: ["html", "width", "height"]
|
|
6509
|
+
}
|
|
6510
|
+
};
|
|
6511
|
+
}
|
|
6512
|
+
});
|
|
6513
|
+
|
|
6255
6514
|
// src/subagents/copyEditor/tools.ts
|
|
6256
6515
|
var COPY_EDITOR_TOOLS;
|
|
6257
6516
|
var init_tools3 = __esm({
|
|
@@ -6326,18 +6585,18 @@ var init_copyEditor = __esm({
|
|
|
6326
6585
|
// src/subagents/designExpert/tools/polishCopy.ts
|
|
6327
6586
|
var polishCopy_exports = {};
|
|
6328
6587
|
__export(polishCopy_exports, {
|
|
6329
|
-
definition: () =>
|
|
6330
|
-
execute: () =>
|
|
6588
|
+
definition: () => definition8,
|
|
6589
|
+
execute: () => execute8
|
|
6331
6590
|
});
|
|
6332
|
-
async function
|
|
6591
|
+
async function execute8(input, _onLog, context) {
|
|
6333
6592
|
return copyEditorTool.execute(input, context);
|
|
6334
6593
|
}
|
|
6335
|
-
var
|
|
6594
|
+
var definition8;
|
|
6336
6595
|
var init_polishCopy = __esm({
|
|
6337
6596
|
"src/subagents/designExpert/tools/polishCopy.ts"() {
|
|
6338
6597
|
"use strict";
|
|
6339
6598
|
init_copyEditor();
|
|
6340
|
-
|
|
6599
|
+
definition8 = {
|
|
6341
6600
|
name: "polishCopy",
|
|
6342
6601
|
description: "Hand off any user-facing copy you've written \u2014 headlines, captions, labels, body text \u2014 and get back a sharper version: better built for its audience and free of the fingerprints that make writing read as AI. It elevates how the copy communicates without inventing facts or claims you didn't give it. Give it the text plus what it's for (where it appears, the audience).",
|
|
6343
6602
|
inputSchema: {
|
|
@@ -6375,6 +6634,7 @@ var init_tools4 = __esm({
|
|
|
6375
6634
|
init_analyzeImage2();
|
|
6376
6635
|
init_generateImages();
|
|
6377
6636
|
init_editImages();
|
|
6637
|
+
init_renderImage();
|
|
6378
6638
|
init_polishCopy();
|
|
6379
6639
|
init_screenshot2();
|
|
6380
6640
|
tools = {
|
|
@@ -6388,6 +6648,7 @@ var init_tools4 = __esm({
|
|
|
6388
6648
|
screenshot: { definition: screenshotDefinition, execute: executeScreenshot },
|
|
6389
6649
|
generateImages: generateImages_exports,
|
|
6390
6650
|
editImages: editImages_exports,
|
|
6651
|
+
renderImage: renderImage_exports,
|
|
6391
6652
|
polishCopy: polishCopy_exports
|
|
6392
6653
|
};
|
|
6393
6654
|
DESIGN_EXPERT_TOOLS = [
|
|
@@ -6930,13 +7191,13 @@ var init_tools5 = __esm({
|
|
|
6930
7191
|
// src/subagents/productVision/executor.ts
|
|
6931
7192
|
import fs18 from "fs";
|
|
6932
7193
|
import path10 from "path";
|
|
6933
|
-
function
|
|
7194
|
+
function resolve3(filePath) {
|
|
6934
7195
|
return path10.join(ROADMAP_DIR, filePath);
|
|
6935
7196
|
}
|
|
6936
7197
|
async function executeVisionTool(name, input, context) {
|
|
6937
7198
|
switch (name) {
|
|
6938
7199
|
case "writeFile": {
|
|
6939
|
-
const filePath =
|
|
7200
|
+
const filePath = resolve3(input.path);
|
|
6940
7201
|
try {
|
|
6941
7202
|
fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
6942
7203
|
let oldContent = null;
|
|
@@ -6954,7 +7215,7 @@ ${unifiedDiff(filePath, oldContent ?? "", input.content)}`;
|
|
|
6954
7215
|
}
|
|
6955
7216
|
}
|
|
6956
7217
|
case "deleteFile": {
|
|
6957
|
-
const filePath =
|
|
7218
|
+
const filePath = resolve3(input.path);
|
|
6958
7219
|
try {
|
|
6959
7220
|
if (!fs18.existsSync(filePath)) {
|
|
6960
7221
|
return `Error: ${filePath} does not exist`;
|
|
@@ -6971,7 +7232,7 @@ ${unifiedDiff(filePath, oldContent, "")}`;
|
|
|
6971
7232
|
if (!context) {
|
|
6972
7233
|
return "Error: writePitchDeck requires execution context for design expert delegation";
|
|
6973
7234
|
}
|
|
6974
|
-
const filePath =
|
|
7235
|
+
const filePath = resolve3("pitch.html");
|
|
6975
7236
|
try {
|
|
6976
7237
|
fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
6977
7238
|
const exists = fs18.existsSync(filePath);
|
|
@@ -8142,14 +8403,14 @@ function buildCorpus() {
|
|
|
8142
8403
|
});
|
|
8143
8404
|
}
|
|
8144
8405
|
}
|
|
8145
|
-
const
|
|
8406
|
+
const sep2 = "\n\n---\n\n";
|
|
8146
8407
|
const sections = [];
|
|
8147
8408
|
let usedChars = 0;
|
|
8148
8409
|
for (const { path: p, content } of files) {
|
|
8149
8410
|
const section = `## File: ${p}
|
|
8150
8411
|
|
|
8151
8412
|
${content}`;
|
|
8152
|
-
const added = section.length + (sections.length > 0 ?
|
|
8413
|
+
const added = section.length + (sections.length > 0 ? sep2.length : 0);
|
|
8153
8414
|
if (sections.length > 0 && usedChars + added > BRAND_CORPUS_CHAR_LIMIT) {
|
|
8154
8415
|
sections.push(
|
|
8155
8416
|
`(brand corpus truncated: included ${sections.length} of ${files.length} files, ~${(usedChars / 1024).toFixed(0)}KB; brand-relevant files were prioritized.)`
|
|
@@ -8159,7 +8420,7 @@ ${content}`;
|
|
|
8159
8420
|
sections.push(section);
|
|
8160
8421
|
usedChars += added;
|
|
8161
8422
|
}
|
|
8162
|
-
return sections.join(
|
|
8423
|
+
return sections.join(sep2);
|
|
8163
8424
|
}
|
|
8164
8425
|
function headSlice(content) {
|
|
8165
8426
|
if (content.length <= HEAD_SLICE_CHARS) {
|
|
@@ -8439,6 +8700,8 @@ async function runTurn(params) {
|
|
|
8439
8700
|
let lastCallInputTokens = 0;
|
|
8440
8701
|
let lastCallCacheCreation = 0;
|
|
8441
8702
|
let lastCallCacheRead = 0;
|
|
8703
|
+
let abnormalStopRecoveries = 0;
|
|
8704
|
+
const MAX_ABNORMAL_STOP_RECOVERIES = 2;
|
|
8442
8705
|
const statusWatcher = isFirstMessage ? { stop() {
|
|
8443
8706
|
}, pause() {
|
|
8444
8707
|
}, resume() {
|
|
@@ -8782,6 +9045,18 @@ async function runTurn(params) {
|
|
|
8782
9045
|
});
|
|
8783
9046
|
}
|
|
8784
9047
|
const toolCalls = getToolCalls(contentBlocks);
|
|
9048
|
+
if (toolCalls.length === 0 && (stopReason === "repetition" || stopReason === "max_tokens") && abnormalStopRecoveries < MAX_ABNORMAL_STOP_RECOVERIES && !signal?.aborted) {
|
|
9049
|
+
abnormalStopRecoveries++;
|
|
9050
|
+
log13.warn("Abnormal stop \u2014 nudging model to continue", {
|
|
9051
|
+
requestId,
|
|
9052
|
+
stopReason,
|
|
9053
|
+
attempt: abnormalStopRecoveries
|
|
9054
|
+
});
|
|
9055
|
+
const nudge = "Your previous response was cut off \u2014 it degenerated into repeated text or hit the output limit, and the repeated portion was removed. Reassess where you are in the task and continue from where you left off. Prefer a tool call over restating what you were about to do.";
|
|
9056
|
+
state.messages.push({ role: "user", content: nudge, hidden: true });
|
|
9057
|
+
onEvent({ type: "user_message", text: nudge, hidden: true });
|
|
9058
|
+
continue;
|
|
9059
|
+
}
|
|
8785
9060
|
if (stopReason !== "tool_use" || toolCalls.length === 0) {
|
|
8786
9061
|
statusWatcher.stop();
|
|
8787
9062
|
saveSession(state);
|
|
@@ -8948,7 +9223,7 @@ async function runTurn(params) {
|
|
|
8948
9223
|
block.completedAt = Date.now();
|
|
8949
9224
|
const msgs = subAgentMessages.get(r.id);
|
|
8950
9225
|
if (msgs) {
|
|
8951
|
-
block.subAgentMessages = msgs;
|
|
9226
|
+
block.subAgentMessages = capSubAgentTranscript(msgs);
|
|
8952
9227
|
}
|
|
8953
9228
|
}
|
|
8954
9229
|
}
|
|
@@ -9007,7 +9282,7 @@ var init_agent = __esm({
|
|
|
9007
9282
|
init_trigger2();
|
|
9008
9283
|
init_surfaces();
|
|
9009
9284
|
init_toolRegistry();
|
|
9010
|
-
|
|
9285
|
+
init_historyLimits();
|
|
9011
9286
|
log13 = createLogger("agent");
|
|
9012
9287
|
BRAND_TRIGGERING_TOOLS = /* @__PURE__ */ new Set([
|
|
9013
9288
|
"writeSpec",
|
|
@@ -9276,8 +9551,8 @@ var init_config = __esm({
|
|
|
9276
9551
|
|
|
9277
9552
|
// src/headless/attachments.ts
|
|
9278
9553
|
import { mkdirSync, existsSync } from "fs";
|
|
9279
|
-
import { writeFile } from "fs/promises";
|
|
9280
|
-
import { basename as basename2, join, extname as extname2 } from "path";
|
|
9554
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
9555
|
+
import { basename as basename2, join as join2, extname as extname2 } from "path";
|
|
9281
9556
|
function filenameFromUrl(url) {
|
|
9282
9557
|
try {
|
|
9283
9558
|
const pathname = new URL(url).pathname;
|
|
@@ -9288,7 +9563,7 @@ function filenameFromUrl(url) {
|
|
|
9288
9563
|
}
|
|
9289
9564
|
}
|
|
9290
9565
|
function resolveUniqueFilename(name, claimed) {
|
|
9291
|
-
const isFree = (candidate) => !claimed.has(candidate) && !existsSync(
|
|
9566
|
+
const isFree = (candidate) => !claimed.has(candidate) && !existsSync(join2(UPLOADS_DIR, candidate));
|
|
9292
9567
|
if (isFree(name)) {
|
|
9293
9568
|
return name;
|
|
9294
9569
|
}
|
|
@@ -9322,7 +9597,7 @@ async function persistAttachments(attachments) {
|
|
|
9322
9597
|
const results = await Promise.allSettled(
|
|
9323
9598
|
nonVoice.map(async (att, i) => {
|
|
9324
9599
|
const name = names[i];
|
|
9325
|
-
const localPath =
|
|
9600
|
+
const localPath = join2(UPLOADS_DIR, name);
|
|
9326
9601
|
const res = await fetch(att.url, {
|
|
9327
9602
|
signal: AbortSignal.timeout(3e4)
|
|
9328
9603
|
});
|
|
@@ -9330,7 +9605,7 @@ async function persistAttachments(attachments) {
|
|
|
9330
9605
|
throw new Error(`HTTP ${res.status} downloading ${att.url}`);
|
|
9331
9606
|
}
|
|
9332
9607
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
9333
|
-
await
|
|
9608
|
+
await writeFile2(localPath, buffer);
|
|
9334
9609
|
log15.info("Attachment saved", {
|
|
9335
9610
|
filename: name,
|
|
9336
9611
|
path: localPath,
|
|
@@ -9344,7 +9619,7 @@ async function persistAttachments(attachments) {
|
|
|
9344
9619
|
});
|
|
9345
9620
|
if (textRes.ok) {
|
|
9346
9621
|
extractedTextPath = `${localPath}.txt`;
|
|
9347
|
-
await
|
|
9622
|
+
await writeFile2(extractedTextPath, await textRes.text(), "utf-8");
|
|
9348
9623
|
log15.info("Extracted text saved", { path: extractedTextPath });
|
|
9349
9624
|
}
|
|
9350
9625
|
} catch {
|
|
@@ -9988,17 +10263,17 @@ var init_headless = __esm({
|
|
|
9988
10263
|
return Promise.resolve(early);
|
|
9989
10264
|
}
|
|
9990
10265
|
const shouldTimeout = !USER_FACING_TOOLS.has(name);
|
|
9991
|
-
return new Promise((
|
|
10266
|
+
return new Promise((resolve4) => {
|
|
9992
10267
|
const timeout = shouldTimeout ? setTimeout(() => {
|
|
9993
10268
|
this.pendingTools.delete(id);
|
|
9994
|
-
|
|
10269
|
+
resolve4(
|
|
9995
10270
|
"Error: Tool timed out \u2014 no response from the app environment after 5 minutes."
|
|
9996
10271
|
);
|
|
9997
10272
|
}, EXTERNAL_TOOL_TIMEOUT_MS) : void 0;
|
|
9998
10273
|
this.pendingTools.set(id, {
|
|
9999
10274
|
resolve: (result) => {
|
|
10000
10275
|
clearTimeout(timeout);
|
|
10001
|
-
|
|
10276
|
+
resolve4(result);
|
|
10002
10277
|
},
|
|
10003
10278
|
timeout
|
|
10004
10279
|
});
|