@mindstudio-ai/remy 0.1.40 → 0.1.41
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 +240 -192
- package/dist/index.js +282 -223
- 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/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/index.js
CHANGED
|
@@ -11,9 +11,6 @@ var __export = (target, all) => {
|
|
|
11
11
|
|
|
12
12
|
// src/logger.ts
|
|
13
13
|
import fs from "fs";
|
|
14
|
-
function timestamp() {
|
|
15
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
16
|
-
}
|
|
17
14
|
function truncateValues(obj) {
|
|
18
15
|
const result = {};
|
|
19
16
|
for (const [key, value] of Object.entries(obj)) {
|
|
@@ -27,15 +24,28 @@ function truncateValues(obj) {
|
|
|
27
24
|
}
|
|
28
25
|
return result;
|
|
29
26
|
}
|
|
30
|
-
function write(level, msg, data) {
|
|
27
|
+
function write(level, module, msg, data) {
|
|
31
28
|
if (LEVELS[level] > currentLevel) {
|
|
32
29
|
return;
|
|
33
30
|
}
|
|
34
|
-
const
|
|
31
|
+
const entry = {
|
|
32
|
+
ts: Date.now(),
|
|
33
|
+
level,
|
|
34
|
+
module,
|
|
35
|
+
msg
|
|
36
|
+
};
|
|
35
37
|
if (data) {
|
|
36
|
-
|
|
38
|
+
Object.assign(entry, truncateValues(data));
|
|
37
39
|
}
|
|
38
|
-
writeFn(
|
|
40
|
+
writeFn(JSON.stringify(entry));
|
|
41
|
+
}
|
|
42
|
+
function createLogger(module) {
|
|
43
|
+
return {
|
|
44
|
+
error: (msg, data) => write("error", module, msg, data),
|
|
45
|
+
warn: (msg, data) => write("warn", module, msg, data),
|
|
46
|
+
info: (msg, data) => write("info", module, msg, data),
|
|
47
|
+
debug: (msg, data) => write("debug", module, msg, data)
|
|
48
|
+
};
|
|
39
49
|
}
|
|
40
50
|
function initLoggerHeadless(level = "info") {
|
|
41
51
|
currentLevel = LEVELS[level];
|
|
@@ -49,14 +59,15 @@ function initLoggerInteractive(level = "error") {
|
|
|
49
59
|
writeFn = (line) => {
|
|
50
60
|
try {
|
|
51
61
|
if (fd === null) {
|
|
52
|
-
|
|
62
|
+
fs.mkdirSync(".logs", { recursive: true });
|
|
63
|
+
fd = fs.openSync(".logs/agent.ndjson", "a");
|
|
53
64
|
}
|
|
54
65
|
fs.writeSync(fd, line + "\n");
|
|
55
66
|
} catch {
|
|
56
67
|
}
|
|
57
68
|
};
|
|
58
69
|
}
|
|
59
|
-
var LEVELS, currentLevel, writeFn, MAX_VALUE_LENGTH
|
|
70
|
+
var LEVELS, currentLevel, writeFn, MAX_VALUE_LENGTH;
|
|
60
71
|
var init_logger = __esm({
|
|
61
72
|
"src/logger.ts"() {
|
|
62
73
|
"use strict";
|
|
@@ -70,43 +81,22 @@ var init_logger = __esm({
|
|
|
70
81
|
writeFn = () => {
|
|
71
82
|
};
|
|
72
83
|
MAX_VALUE_LENGTH = 200;
|
|
73
|
-
log = {
|
|
74
|
-
error(msg, data) {
|
|
75
|
-
write("error", msg, data);
|
|
76
|
-
},
|
|
77
|
-
warn(msg, data) {
|
|
78
|
-
write("warn", msg, data);
|
|
79
|
-
},
|
|
80
|
-
info(msg, data) {
|
|
81
|
-
write("info", msg, data);
|
|
82
|
-
},
|
|
83
|
-
debug(msg, data) {
|
|
84
|
-
write("debug", msg, data);
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
84
|
}
|
|
88
85
|
});
|
|
89
86
|
|
|
90
87
|
// src/api.ts
|
|
91
88
|
async function* streamChat(params) {
|
|
92
|
-
const { baseUrl: baseUrl2, apiKey, signal, ...body } = params;
|
|
89
|
+
const { baseUrl: baseUrl2, apiKey, signal, requestId, ...body } = params;
|
|
93
90
|
const url = `${baseUrl2}/_internal/v2/agent/remy/chat`;
|
|
94
91
|
const startTime = Date.now();
|
|
95
92
|
const messagesWithAttachments = body.messages.filter(
|
|
96
93
|
(m) => m.attachments && m.attachments.length > 0
|
|
97
94
|
);
|
|
98
|
-
log.info("
|
|
99
|
-
|
|
95
|
+
log.info("API request", {
|
|
96
|
+
requestId,
|
|
100
97
|
model: body.model,
|
|
101
98
|
messageCount: body.messages.length,
|
|
102
|
-
toolCount: body.tools.length
|
|
103
|
-
...messagesWithAttachments.length > 0 && {
|
|
104
|
-
attachments: messagesWithAttachments.map((m) => ({
|
|
105
|
-
role: m.role,
|
|
106
|
-
attachmentCount: m.attachments.length,
|
|
107
|
-
urls: m.attachments.map((a) => a.url)
|
|
108
|
-
}))
|
|
109
|
-
}
|
|
99
|
+
toolCount: body.tools.length
|
|
110
100
|
});
|
|
111
101
|
let res;
|
|
112
102
|
try {
|
|
@@ -121,15 +111,15 @@ async function* streamChat(params) {
|
|
|
121
111
|
});
|
|
122
112
|
} catch (err) {
|
|
123
113
|
if (signal?.aborted) {
|
|
124
|
-
log.
|
|
114
|
+
log.warn("Request aborted", { requestId });
|
|
125
115
|
throw err;
|
|
126
116
|
}
|
|
127
|
-
log.error("Network error", { error: err.message });
|
|
117
|
+
log.error("Network error", { requestId, error: err.message });
|
|
128
118
|
yield { type: "error", error: `Network error: ${err.message}` };
|
|
129
119
|
return;
|
|
130
120
|
}
|
|
131
121
|
const ttfb = Date.now() - startTime;
|
|
132
|
-
log.info(
|
|
122
|
+
log.info("API response", { requestId, status: res.status, ttfbMs: ttfb });
|
|
133
123
|
if (!res.ok) {
|
|
134
124
|
let errorMessage = `HTTP ${res.status}`;
|
|
135
125
|
try {
|
|
@@ -142,7 +132,11 @@ async function* streamChat(params) {
|
|
|
142
132
|
}
|
|
143
133
|
} catch {
|
|
144
134
|
}
|
|
145
|
-
log.error("API error", {
|
|
135
|
+
log.error("API error", {
|
|
136
|
+
requestId,
|
|
137
|
+
status: res.status,
|
|
138
|
+
error: errorMessage
|
|
139
|
+
});
|
|
146
140
|
yield { type: "error", error: errorMessage };
|
|
147
141
|
return;
|
|
148
142
|
}
|
|
@@ -167,7 +161,10 @@ async function* streamChat(params) {
|
|
|
167
161
|
} catch {
|
|
168
162
|
clearTimeout(stallTimer);
|
|
169
163
|
await reader.cancel();
|
|
170
|
-
log.error("Stream stalled", {
|
|
164
|
+
log.error("Stream stalled", {
|
|
165
|
+
requestId,
|
|
166
|
+
durationMs: Date.now() - startTime
|
|
167
|
+
});
|
|
171
168
|
yield {
|
|
172
169
|
type: "error",
|
|
173
170
|
error: "Stream stalled \u2014 no data received for 5 minutes"
|
|
@@ -190,7 +187,8 @@ async function* streamChat(params) {
|
|
|
190
187
|
if (event.type === "done") {
|
|
191
188
|
const elapsed = Date.now() - startTime;
|
|
192
189
|
log.info("Stream complete", {
|
|
193
|
-
|
|
190
|
+
requestId,
|
|
191
|
+
durationMs: elapsed,
|
|
194
192
|
stopReason: event.stopReason,
|
|
195
193
|
inputTokens: event.usage.inputTokens,
|
|
196
194
|
outputTokens: event.usage.outputTokens
|
|
@@ -221,11 +219,6 @@ async function* streamChatWithRetry(params, options) {
|
|
|
221
219
|
for await (const event of streamChat(params)) {
|
|
222
220
|
if (event.type === "error") {
|
|
223
221
|
if (isRetryableError(event.error) && attempt < MAX_RETRIES - 1) {
|
|
224
|
-
log.warn("Retryable error, will retry", {
|
|
225
|
-
attempt: attempt + 1,
|
|
226
|
-
maxRetries: MAX_RETRIES,
|
|
227
|
-
error: event.error
|
|
228
|
-
});
|
|
229
222
|
options?.onRetry?.(attempt, event.error);
|
|
230
223
|
retryableFailure = true;
|
|
231
224
|
break;
|
|
@@ -240,7 +233,12 @@ async function* streamChatWithRetry(params, options) {
|
|
|
240
233
|
return;
|
|
241
234
|
}
|
|
242
235
|
const backoff = INITIAL_BACKOFF_MS * 2 ** attempt;
|
|
243
|
-
log.
|
|
236
|
+
log.warn("Retrying", {
|
|
237
|
+
requestId: params.requestId,
|
|
238
|
+
attempt: attempt + 1,
|
|
239
|
+
maxRetries: MAX_RETRIES,
|
|
240
|
+
backoffMs: backoff
|
|
241
|
+
});
|
|
244
242
|
await sleep(backoff);
|
|
245
243
|
continue;
|
|
246
244
|
}
|
|
@@ -274,11 +272,12 @@ async function generateBackgroundAck(params) {
|
|
|
274
272
|
return FALLBACK_ACK;
|
|
275
273
|
}
|
|
276
274
|
}
|
|
277
|
-
var MAX_RETRIES, INITIAL_BACKOFF_MS, FALLBACK_ACK;
|
|
275
|
+
var log, MAX_RETRIES, INITIAL_BACKOFF_MS, FALLBACK_ACK;
|
|
278
276
|
var init_api = __esm({
|
|
279
277
|
"src/api.ts"() {
|
|
280
278
|
"use strict";
|
|
281
279
|
init_logger();
|
|
280
|
+
log = createLogger("api");
|
|
282
281
|
MAX_RETRIES = 3;
|
|
283
282
|
INITIAL_BACKOFF_MS = 1e3;
|
|
284
283
|
FALLBACK_ACK = "[Message sent to agent. Agent is working in the background and will report back with its results when finished.]";
|
|
@@ -1858,7 +1857,7 @@ var init_editsFinished = __esm({
|
|
|
1858
1857
|
// src/tools/_helpers/sidecar.ts
|
|
1859
1858
|
function setSidecarBaseUrl(url) {
|
|
1860
1859
|
baseUrl = url;
|
|
1861
|
-
|
|
1860
|
+
log2.info("Configured", { url });
|
|
1862
1861
|
}
|
|
1863
1862
|
function isSidecarConfigured() {
|
|
1864
1863
|
return baseUrl !== null;
|
|
@@ -1868,7 +1867,6 @@ async function sidecarRequest(endpoint, body = {}, options) {
|
|
|
1868
1867
|
throw new Error("Sidecar not available");
|
|
1869
1868
|
}
|
|
1870
1869
|
const url = `${baseUrl}${endpoint}`;
|
|
1871
|
-
log.debug("Sidecar request", { endpoint, body });
|
|
1872
1870
|
try {
|
|
1873
1871
|
const res = await fetch(url, {
|
|
1874
1872
|
method: "POST",
|
|
@@ -1877,7 +1875,7 @@ async function sidecarRequest(endpoint, body = {}, options) {
|
|
|
1877
1875
|
signal: options?.timeout ? AbortSignal.timeout(options.timeout) : void 0
|
|
1878
1876
|
});
|
|
1879
1877
|
if (!res.ok) {
|
|
1880
|
-
|
|
1878
|
+
log2.error("Sidecar error", { endpoint, status: res.status });
|
|
1881
1879
|
throw new Error(`Sidecar error: ${res.status}`);
|
|
1882
1880
|
}
|
|
1883
1881
|
return res.json();
|
|
@@ -1885,15 +1883,16 @@ async function sidecarRequest(endpoint, body = {}, options) {
|
|
|
1885
1883
|
if (err.message.startsWith("Sidecar error")) {
|
|
1886
1884
|
throw err;
|
|
1887
1885
|
}
|
|
1888
|
-
|
|
1886
|
+
log2.error("Sidecar connection error", { endpoint, error: err.message });
|
|
1889
1887
|
throw new Error(`Sidecar connection error: ${err.message}`);
|
|
1890
1888
|
}
|
|
1891
1889
|
}
|
|
1892
|
-
var baseUrl;
|
|
1890
|
+
var log2, baseUrl;
|
|
1893
1891
|
var init_sidecar = __esm({
|
|
1894
1892
|
"src/tools/_helpers/sidecar.ts"() {
|
|
1895
1893
|
"use strict";
|
|
1896
1894
|
init_logger();
|
|
1895
|
+
log2 = createLogger("sidecar");
|
|
1897
1896
|
baseUrl = null;
|
|
1898
1897
|
}
|
|
1899
1898
|
});
|
|
@@ -2071,7 +2070,6 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
2071
2070
|
const ssResult = await sidecarRequest("/screenshot-full-page", void 0, {
|
|
2072
2071
|
timeout: 12e4
|
|
2073
2072
|
});
|
|
2074
|
-
log.debug("Screenshot response", { ssResult });
|
|
2075
2073
|
const url = ssResult?.url || ssResult?.screenshotUrl;
|
|
2076
2074
|
if (!url) {
|
|
2077
2075
|
throw new Error(
|
|
@@ -2094,7 +2092,6 @@ var init_screenshot = __esm({
|
|
|
2094
2092
|
"use strict";
|
|
2095
2093
|
init_sidecar();
|
|
2096
2094
|
init_runCli();
|
|
2097
|
-
init_logger();
|
|
2098
2095
|
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.";
|
|
2099
2096
|
}
|
|
2100
2097
|
});
|
|
@@ -2148,13 +2145,8 @@ function startStatusWatcher(config) {
|
|
|
2148
2145
|
try {
|
|
2149
2146
|
const ctx = getContext();
|
|
2150
2147
|
if (!ctx.assistantText && !ctx.lastToolName) {
|
|
2151
|
-
log.debug("Status watcher: no context, skipping");
|
|
2152
2148
|
return;
|
|
2153
2149
|
}
|
|
2154
|
-
log.debug("Status watcher: requesting label", {
|
|
2155
|
-
textLength: ctx.assistantText.length,
|
|
2156
|
-
lastToolName: ctx.lastToolName
|
|
2157
|
-
});
|
|
2158
2150
|
const res = await fetch(url, {
|
|
2159
2151
|
method: "POST",
|
|
2160
2152
|
headers: {
|
|
@@ -2171,30 +2163,18 @@ function startStatusWatcher(config) {
|
|
|
2171
2163
|
signal
|
|
2172
2164
|
});
|
|
2173
2165
|
if (!res.ok) {
|
|
2174
|
-
log.debug("Status watcher: endpoint returned non-ok", {
|
|
2175
|
-
status: res.status
|
|
2176
|
-
});
|
|
2177
2166
|
return;
|
|
2178
2167
|
}
|
|
2179
2168
|
const data = await res.json();
|
|
2180
|
-
if (!data.label) {
|
|
2181
|
-
log.debug("Status watcher: no label in response");
|
|
2182
|
-
return;
|
|
2183
|
-
}
|
|
2184
|
-
if (data.label === lastLabel) {
|
|
2185
|
-
log.debug("Status watcher: duplicate label, skipping", {
|
|
2186
|
-
label: data.label
|
|
2187
|
-
});
|
|
2169
|
+
if (!data.label || data.label === lastLabel) {
|
|
2188
2170
|
return;
|
|
2189
2171
|
}
|
|
2190
2172
|
lastLabel = data.label;
|
|
2191
2173
|
if (stopped) {
|
|
2192
2174
|
return;
|
|
2193
2175
|
}
|
|
2194
|
-
log.debug("Status watcher: emitting", { label: data.label });
|
|
2195
2176
|
onStatus(data.label);
|
|
2196
|
-
} catch
|
|
2197
|
-
log.debug("Status watcher: error", { error: err?.message ?? "unknown" });
|
|
2177
|
+
} catch {
|
|
2198
2178
|
} finally {
|
|
2199
2179
|
inflight = false;
|
|
2200
2180
|
}
|
|
@@ -2202,19 +2182,16 @@ function startStatusWatcher(config) {
|
|
|
2202
2182
|
const timer = setInterval(tick, interval);
|
|
2203
2183
|
tick().catch(() => {
|
|
2204
2184
|
});
|
|
2205
|
-
log.debug("Status watcher started", { interval });
|
|
2206
2185
|
return {
|
|
2207
2186
|
stop() {
|
|
2208
2187
|
stopped = true;
|
|
2209
2188
|
clearInterval(timer);
|
|
2210
|
-
log.debug("Status watcher stopped");
|
|
2211
2189
|
}
|
|
2212
2190
|
};
|
|
2213
2191
|
}
|
|
2214
2192
|
var init_statusWatcher = __esm({
|
|
2215
2193
|
"src/statusWatcher.ts"() {
|
|
2216
2194
|
"use strict";
|
|
2217
|
-
init_logger();
|
|
2218
2195
|
}
|
|
2219
2196
|
});
|
|
2220
2197
|
|
|
@@ -2274,14 +2251,19 @@ async function runSubAgent(config) {
|
|
|
2274
2251
|
onEvent,
|
|
2275
2252
|
resolveExternalTool,
|
|
2276
2253
|
toolRegistry,
|
|
2254
|
+
requestId,
|
|
2277
2255
|
background,
|
|
2278
2256
|
onBackgroundComplete
|
|
2279
2257
|
} = config;
|
|
2280
2258
|
const bgAbort = background ? new AbortController() : null;
|
|
2281
2259
|
const signal = background ? bgAbort.signal : parentSignal;
|
|
2260
|
+
const agentName = subAgentId || "sub-agent";
|
|
2261
|
+
const runStart = Date.now();
|
|
2262
|
+
log3.info("Sub-agent started", { requestId, parentToolId, agentName });
|
|
2282
2263
|
const emit2 = (e) => {
|
|
2283
2264
|
onEvent({ ...e, parentToolId });
|
|
2284
2265
|
};
|
|
2266
|
+
let turns = 0;
|
|
2285
2267
|
const run = async () => {
|
|
2286
2268
|
const messages = [{ role: "user", content: task }];
|
|
2287
2269
|
function getPartialText(blocks) {
|
|
@@ -2301,6 +2283,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
2301
2283
|
}
|
|
2302
2284
|
let lastToolResult = "";
|
|
2303
2285
|
while (true) {
|
|
2286
|
+
turns++;
|
|
2304
2287
|
if (signal?.aborted) {
|
|
2305
2288
|
return abortResult([]);
|
|
2306
2289
|
}
|
|
@@ -2326,6 +2309,7 @@ Current date/time: ${(/* @__PURE__ */ new Date()).toISOString().replace("T", " "
|
|
|
2326
2309
|
{
|
|
2327
2310
|
...apiConfig,
|
|
2328
2311
|
model,
|
|
2312
|
+
requestId,
|
|
2329
2313
|
subAgentId,
|
|
2330
2314
|
system: fullSystem,
|
|
2331
2315
|
messages: cleanMessagesForApi(messages),
|
|
@@ -2416,7 +2400,8 @@ Current date/time: ${(/* @__PURE__ */ new Date()).toISOString().replace("T", " "
|
|
|
2416
2400
|
const text = getPartialText(contentBlocks);
|
|
2417
2401
|
return { text, messages };
|
|
2418
2402
|
}
|
|
2419
|
-
|
|
2403
|
+
log3.info("Tools executing", {
|
|
2404
|
+
requestId,
|
|
2420
2405
|
parentToolId,
|
|
2421
2406
|
count: toolCalls.length,
|
|
2422
2407
|
tools: toolCalls.map((tc) => tc.name)
|
|
@@ -2515,19 +2500,42 @@ Current date/time: ${(/* @__PURE__ */ new Date()).toISOString().replace("T", " "
|
|
|
2515
2500
|
}
|
|
2516
2501
|
}
|
|
2517
2502
|
};
|
|
2503
|
+
const wrapRun = async () => {
|
|
2504
|
+
try {
|
|
2505
|
+
const result = await run();
|
|
2506
|
+
log3.info("Sub-agent complete", {
|
|
2507
|
+
requestId,
|
|
2508
|
+
parentToolId,
|
|
2509
|
+
agentName,
|
|
2510
|
+
durationMs: Date.now() - runStart,
|
|
2511
|
+
turns
|
|
2512
|
+
});
|
|
2513
|
+
return result;
|
|
2514
|
+
} catch (err) {
|
|
2515
|
+
log3.warn("Sub-agent error", {
|
|
2516
|
+
requestId,
|
|
2517
|
+
parentToolId,
|
|
2518
|
+
agentName,
|
|
2519
|
+
error: err.message
|
|
2520
|
+
});
|
|
2521
|
+
throw err;
|
|
2522
|
+
}
|
|
2523
|
+
};
|
|
2518
2524
|
if (!background) {
|
|
2519
|
-
return
|
|
2525
|
+
return wrapRun();
|
|
2520
2526
|
}
|
|
2527
|
+
log3.info("Sub-agent backgrounded", { requestId, parentToolId, agentName });
|
|
2521
2528
|
const ack = await generateBackgroundAck({
|
|
2522
2529
|
apiConfig,
|
|
2523
2530
|
agentName: subAgentId || "agent",
|
|
2524
2531
|
task
|
|
2525
2532
|
});
|
|
2526
|
-
|
|
2533
|
+
wrapRun().then((finalResult) => onBackgroundComplete?.(finalResult)).catch(
|
|
2527
2534
|
(err) => onBackgroundComplete?.({ text: `Error: ${err.message}`, messages: [] })
|
|
2528
2535
|
);
|
|
2529
2536
|
return { text: ack, messages: [], backgrounded: true };
|
|
2530
2537
|
}
|
|
2538
|
+
var log3;
|
|
2531
2539
|
var init_runner = __esm({
|
|
2532
2540
|
"src/subagents/runner.ts"() {
|
|
2533
2541
|
"use strict";
|
|
@@ -2535,6 +2543,7 @@ var init_runner = __esm({
|
|
|
2535
2543
|
init_logger();
|
|
2536
2544
|
init_statusWatcher();
|
|
2537
2545
|
init_cleanMessages();
|
|
2546
|
+
log3 = createLogger("sub-agent");
|
|
2538
2547
|
}
|
|
2539
2548
|
});
|
|
2540
2549
|
|
|
@@ -2711,7 +2720,7 @@ var init_prompt = __esm({
|
|
|
2711
2720
|
});
|
|
2712
2721
|
|
|
2713
2722
|
// src/subagents/browserAutomation/index.ts
|
|
2714
|
-
var browserAutomationTool;
|
|
2723
|
+
var log4, browserAutomationTool;
|
|
2715
2724
|
var init_browserAutomation = __esm({
|
|
2716
2725
|
"src/subagents/browserAutomation/index.ts"() {
|
|
2717
2726
|
"use strict";
|
|
@@ -2722,6 +2731,7 @@ var init_browserAutomation = __esm({
|
|
|
2722
2731
|
init_screenshot();
|
|
2723
2732
|
init_runCli();
|
|
2724
2733
|
init_logger();
|
|
2734
|
+
log4 = createLogger("browser-automation");
|
|
2725
2735
|
browserAutomationTool = {
|
|
2726
2736
|
definition: {
|
|
2727
2737
|
name: "runAutomatedBrowserTest",
|
|
@@ -2781,6 +2791,7 @@ var init_browserAutomation = __esm({
|
|
|
2781
2791
|
subAgentId: "browserAutomation",
|
|
2782
2792
|
signal: context.signal,
|
|
2783
2793
|
parentToolId: context.toolCallId,
|
|
2794
|
+
requestId: context.requestId,
|
|
2784
2795
|
onEvent: context.onEvent,
|
|
2785
2796
|
resolveExternalTool: async (id, name, input2) => {
|
|
2786
2797
|
if (!context.resolveExternalTool) {
|
|
@@ -2815,7 +2826,7 @@ var init_browserAutomation = __esm({
|
|
|
2815
2826
|
}
|
|
2816
2827
|
}
|
|
2817
2828
|
} catch {
|
|
2818
|
-
|
|
2829
|
+
log4.debug("Failed to parse batch analysis result", {
|
|
2819
2830
|
batchResult
|
|
2820
2831
|
});
|
|
2821
2832
|
}
|
|
@@ -2944,6 +2955,8 @@ You are analyzing a screenshot of a real website or app for a designer's persona
|
|
|
2944
2955
|
|
|
2945
2956
|
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?
|
|
2946
2957
|
|
|
2958
|
+
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.
|
|
2959
|
+
|
|
2947
2960
|
Then, provide the following analysis:
|
|
2948
2961
|
|
|
2949
2962
|
## Context
|
|
@@ -3060,21 +3073,68 @@ var init_screenshot3 = __esm({
|
|
|
3060
3073
|
}
|
|
3061
3074
|
});
|
|
3062
3075
|
|
|
3063
|
-
// src/subagents/designExpert/tools/
|
|
3064
|
-
async function
|
|
3065
|
-
const {
|
|
3066
|
-
const
|
|
3067
|
-
const
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3076
|
+
// src/subagents/designExpert/tools/images/enhancePrompt.ts
|
|
3077
|
+
async function enhanceImagePrompt(params) {
|
|
3078
|
+
const { brief, aspectRatio, transparentBackground, onLog } = params;
|
|
3079
|
+
const orientation = aspectRatio === "1:1" ? "square" : ["16:9", "4:3", "3:2"].includes(aspectRatio) ? "landscape" : "portrait";
|
|
3080
|
+
const contextParts = [
|
|
3081
|
+
`Aspect ratio: ${aspectRatio} (${orientation})`
|
|
3082
|
+
];
|
|
3083
|
+
if (transparentBackground) {
|
|
3084
|
+
contextParts.push(
|
|
3085
|
+
"Transparent background: yes \u2014 the background will be removed. Focus on the subject as an isolated element."
|
|
3086
|
+
);
|
|
3087
|
+
}
|
|
3088
|
+
const message = `<context>
|
|
3089
|
+
${contextParts.join("\n")}
|
|
3090
|
+
</context>
|
|
3091
|
+
|
|
3092
|
+
<brief>
|
|
3093
|
+
${brief}
|
|
3094
|
+
</brief>`;
|
|
3095
|
+
const enhanced = await runCli(
|
|
3096
|
+
`mindstudio generate-text --prompt ${JSON.stringify(SYSTEM_PROMPT)} --message ${JSON.stringify(message)} --output-key enhanced --no-meta`,
|
|
3097
|
+
{ timeout: 6e4, onLog }
|
|
3098
|
+
);
|
|
3099
|
+
return enhanced.trim();
|
|
3100
|
+
}
|
|
3101
|
+
var SYSTEM_PROMPT;
|
|
3102
|
+
var init_enhancePrompt = __esm({
|
|
3103
|
+
"src/subagents/designExpert/tools/images/enhancePrompt.ts"() {
|
|
3104
|
+
"use strict";
|
|
3105
|
+
init_runCli();
|
|
3106
|
+
init_assets();
|
|
3107
|
+
SYSTEM_PROMPT = readAsset(
|
|
3108
|
+
"subagents/designExpert/tools/images/enhance-image-prompt.md"
|
|
3109
|
+
);
|
|
3071
3110
|
}
|
|
3111
|
+
});
|
|
3112
|
+
|
|
3113
|
+
// src/subagents/designExpert/tools/images/imageGenerator.ts
|
|
3114
|
+
async function generateImageAssets(opts) {
|
|
3115
|
+
const { prompts, sourceImages, transparentBackground, onLog } = opts;
|
|
3116
|
+
const aspectRatio = opts.aspectRatio || "1:1";
|
|
3117
|
+
const config = {
|
|
3118
|
+
aspect_ratio: aspectRatio,
|
|
3119
|
+
...sourceImages?.length && { source_images: sourceImages }
|
|
3120
|
+
};
|
|
3121
|
+
const isEdit = !!sourceImages?.length;
|
|
3122
|
+
const enhancedPrompts = isEdit ? prompts : await Promise.all(
|
|
3123
|
+
prompts.map(
|
|
3124
|
+
(brief) => enhanceImagePrompt({
|
|
3125
|
+
brief,
|
|
3126
|
+
aspectRatio,
|
|
3127
|
+
transparentBackground,
|
|
3128
|
+
onLog
|
|
3129
|
+
})
|
|
3130
|
+
)
|
|
3131
|
+
);
|
|
3072
3132
|
let imageUrls;
|
|
3073
|
-
if (
|
|
3133
|
+
if (enhancedPrompts.length === 1) {
|
|
3074
3134
|
const step = JSON.stringify({
|
|
3075
|
-
prompt:
|
|
3135
|
+
prompt: enhancedPrompts[0],
|
|
3076
3136
|
imageModelOverride: {
|
|
3077
|
-
model: "
|
|
3137
|
+
model: "gemini-3.1-flash-image",
|
|
3078
3138
|
config
|
|
3079
3139
|
}
|
|
3080
3140
|
});
|
|
@@ -3084,12 +3144,12 @@ async function seedreamGenerate(opts) {
|
|
|
3084
3144
|
);
|
|
3085
3145
|
imageUrls = [url];
|
|
3086
3146
|
} else {
|
|
3087
|
-
const steps =
|
|
3147
|
+
const steps = enhancedPrompts.map((prompt) => ({
|
|
3088
3148
|
stepType: "generateImage",
|
|
3089
3149
|
step: {
|
|
3090
3150
|
prompt,
|
|
3091
3151
|
imageModelOverride: {
|
|
3092
|
-
model: "
|
|
3152
|
+
model: "gemini-3.1-flash-image",
|
|
3093
3153
|
config
|
|
3094
3154
|
}
|
|
3095
3155
|
}
|
|
@@ -3126,49 +3186,59 @@ async function seedreamGenerate(opts) {
|
|
|
3126
3186
|
const images = await Promise.all(
|
|
3127
3187
|
imageUrls.map(async (url, i) => {
|
|
3128
3188
|
if (url.startsWith("Error")) {
|
|
3129
|
-
return {
|
|
3189
|
+
return {
|
|
3190
|
+
prompt: prompts[i],
|
|
3191
|
+
...!isEdit && { enhancedPrompt: enhancedPrompts[i] },
|
|
3192
|
+
error: url
|
|
3193
|
+
};
|
|
3130
3194
|
}
|
|
3131
3195
|
const analysis = await runCli(
|
|
3132
3196
|
`mindstudio analyze-image --prompt ${JSON.stringify(ANALYZE_PROMPT)} --image-url ${JSON.stringify(url)} --output-key analysis --no-meta`,
|
|
3133
3197
|
{ timeout: 2e5, onLog }
|
|
3134
3198
|
);
|
|
3135
|
-
return {
|
|
3199
|
+
return {
|
|
3200
|
+
url,
|
|
3201
|
+
prompt: prompts[i],
|
|
3202
|
+
...!isEdit && { enhancedPrompt: enhancedPrompts[i] },
|
|
3203
|
+
analysis,
|
|
3204
|
+
aspectRatio
|
|
3205
|
+
};
|
|
3136
3206
|
})
|
|
3137
3207
|
);
|
|
3138
3208
|
return JSON.stringify({ images });
|
|
3139
3209
|
}
|
|
3140
3210
|
var ANALYZE_PROMPT;
|
|
3141
|
-
var
|
|
3142
|
-
"src/subagents/designExpert/tools/
|
|
3211
|
+
var init_imageGenerator = __esm({
|
|
3212
|
+
"src/subagents/designExpert/tools/images/imageGenerator.ts"() {
|
|
3143
3213
|
"use strict";
|
|
3144
3214
|
init_runCli();
|
|
3145
|
-
|
|
3215
|
+
init_enhancePrompt();
|
|
3216
|
+
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.";
|
|
3146
3217
|
}
|
|
3147
3218
|
});
|
|
3148
3219
|
|
|
3149
|
-
// src/subagents/designExpert/tools/generateImages.ts
|
|
3220
|
+
// src/subagents/designExpert/tools/images/generateImages.ts
|
|
3150
3221
|
var generateImages_exports = {};
|
|
3151
3222
|
__export(generateImages_exports, {
|
|
3152
3223
|
definition: () => definition6,
|
|
3153
3224
|
execute: () => execute6
|
|
3154
3225
|
});
|
|
3155
3226
|
async function execute6(input, onLog) {
|
|
3156
|
-
return
|
|
3227
|
+
return generateImageAssets({
|
|
3157
3228
|
prompts: input.prompts,
|
|
3158
|
-
|
|
3159
|
-
height: input.height,
|
|
3229
|
+
aspectRatio: input.aspectRatio,
|
|
3160
3230
|
transparentBackground: input.transparentBackground,
|
|
3161
3231
|
onLog
|
|
3162
3232
|
});
|
|
3163
3233
|
}
|
|
3164
3234
|
var definition6;
|
|
3165
3235
|
var init_generateImages = __esm({
|
|
3166
|
-
"src/subagents/designExpert/tools/generateImages.ts"() {
|
|
3236
|
+
"src/subagents/designExpert/tools/images/generateImages.ts"() {
|
|
3167
3237
|
"use strict";
|
|
3168
|
-
|
|
3238
|
+
init_imageGenerator();
|
|
3169
3239
|
definition6 = {
|
|
3170
3240
|
name: "generateImages",
|
|
3171
|
-
description: "Generate images
|
|
3241
|
+
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.",
|
|
3172
3242
|
inputSchema: {
|
|
3173
3243
|
type: "object",
|
|
3174
3244
|
properties: {
|
|
@@ -3177,15 +3247,12 @@ var init_generateImages = __esm({
|
|
|
3177
3247
|
items: {
|
|
3178
3248
|
type: "string"
|
|
3179
3249
|
},
|
|
3180
|
-
description: "One or more image
|
|
3181
|
-
},
|
|
3182
|
-
width: {
|
|
3183
|
-
type: "number",
|
|
3184
|
-
description: "Image width in pixels. Default 2048. Range: 2048-4096."
|
|
3250
|
+
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."
|
|
3185
3251
|
},
|
|
3186
|
-
|
|
3187
|
-
type: "
|
|
3188
|
-
|
|
3252
|
+
aspectRatio: {
|
|
3253
|
+
type: "string",
|
|
3254
|
+
enum: ["1:1", "16:9", "9:16", "3:4", "4:3", "2:3", "3:2"],
|
|
3255
|
+
description: "Aspect ratio. Default 1:1."
|
|
3189
3256
|
},
|
|
3190
3257
|
transparentBackground: {
|
|
3191
3258
|
type: "boolean",
|
|
@@ -3198,30 +3265,29 @@ var init_generateImages = __esm({
|
|
|
3198
3265
|
}
|
|
3199
3266
|
});
|
|
3200
3267
|
|
|
3201
|
-
// src/subagents/designExpert/tools/editImages.ts
|
|
3268
|
+
// src/subagents/designExpert/tools/images/editImages.ts
|
|
3202
3269
|
var editImages_exports = {};
|
|
3203
3270
|
__export(editImages_exports, {
|
|
3204
3271
|
definition: () => definition7,
|
|
3205
3272
|
execute: () => execute7
|
|
3206
3273
|
});
|
|
3207
3274
|
async function execute7(input, onLog) {
|
|
3208
|
-
return
|
|
3275
|
+
return generateImageAssets({
|
|
3209
3276
|
prompts: input.prompts,
|
|
3210
3277
|
sourceImages: input.sourceImages,
|
|
3211
|
-
|
|
3212
|
-
height: input.height,
|
|
3278
|
+
aspectRatio: input.aspectRatio,
|
|
3213
3279
|
transparentBackground: input.transparentBackground,
|
|
3214
3280
|
onLog
|
|
3215
3281
|
});
|
|
3216
3282
|
}
|
|
3217
3283
|
var definition7;
|
|
3218
3284
|
var init_editImages = __esm({
|
|
3219
|
-
"src/subagents/designExpert/tools/editImages.ts"() {
|
|
3285
|
+
"src/subagents/designExpert/tools/images/editImages.ts"() {
|
|
3220
3286
|
"use strict";
|
|
3221
|
-
|
|
3287
|
+
init_imageGenerator();
|
|
3222
3288
|
definition7 = {
|
|
3223
3289
|
name: "editImages",
|
|
3224
|
-
description: "Edit or transform existing images
|
|
3290
|
+
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.",
|
|
3225
3291
|
inputSchema: {
|
|
3226
3292
|
type: "object",
|
|
3227
3293
|
properties: {
|
|
@@ -3230,7 +3296,7 @@ var init_editImages = __esm({
|
|
|
3230
3296
|
items: {
|
|
3231
3297
|
type: "string"
|
|
3232
3298
|
},
|
|
3233
|
-
description: "One or more edit
|
|
3299
|
+
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."
|
|
3234
3300
|
},
|
|
3235
3301
|
sourceImages: {
|
|
3236
3302
|
type: "array",
|
|
@@ -3239,13 +3305,10 @@ var init_editImages = __esm({
|
|
|
3239
3305
|
},
|
|
3240
3306
|
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."
|
|
3241
3307
|
},
|
|
3242
|
-
|
|
3243
|
-
type: "
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
height: {
|
|
3247
|
-
type: "number",
|
|
3248
|
-
description: "Output height in pixels. Default 2048. Range: 2048-4096."
|
|
3308
|
+
aspectRatio: {
|
|
3309
|
+
type: "string",
|
|
3310
|
+
enum: ["1:1", "16:9", "9:16", "3:4", "4:3", "2:3", "3:2"],
|
|
3311
|
+
description: "Output aspect ratio. Default 1:1."
|
|
3249
3312
|
},
|
|
3250
3313
|
transparentBackground: {
|
|
3251
3314
|
type: "boolean",
|
|
@@ -3429,15 +3492,11 @@ function getFontLibrarySample() {
|
|
|
3429
3492
|
return `
|
|
3430
3493
|
## Font Library
|
|
3431
3494
|
|
|
3432
|
-
|
|
3495
|
+
This is your personal library of fonts you love. Use it as a starting point when thinking about anything related to typography.
|
|
3433
3496
|
|
|
3434
3497
|
### Fonts
|
|
3435
3498
|
|
|
3436
|
-
${fontList}
|
|
3437
|
-
|
|
3438
|
-
### Pairings
|
|
3439
|
-
|
|
3440
|
-
${pairingList}`.trim();
|
|
3499
|
+
${fontList}`.trim();
|
|
3441
3500
|
}
|
|
3442
3501
|
var fontData;
|
|
3443
3502
|
var init_getFontLibrarySample = __esm({
|
|
@@ -3464,16 +3523,16 @@ function sample2(arr, n) {
|
|
|
3464
3523
|
return copy.slice(0, n);
|
|
3465
3524
|
}
|
|
3466
3525
|
function getDesignReferencesSample() {
|
|
3467
|
-
const images = sample2(inspirationImages,
|
|
3526
|
+
const images = sample2(inspirationImages, 25);
|
|
3468
3527
|
if (!images.length) {
|
|
3469
3528
|
return "";
|
|
3470
3529
|
}
|
|
3471
3530
|
const imageList = images.map((img, i) => `### Reference ${i + 1}
|
|
3472
3531
|
${img.analysis}`).join("\n\n");
|
|
3473
3532
|
return `
|
|
3474
|
-
## Design References
|
|
3533
|
+
## Visual Design References
|
|
3475
3534
|
|
|
3476
|
-
This is
|
|
3535
|
+
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.
|
|
3477
3536
|
|
|
3478
3537
|
${imageList}`.trim();
|
|
3479
3538
|
}
|
|
@@ -3502,16 +3561,16 @@ function sample3(arr, n) {
|
|
|
3502
3561
|
return copy.slice(0, n);
|
|
3503
3562
|
}
|
|
3504
3563
|
function getUiInspirationSample() {
|
|
3505
|
-
const screens = sample3(uiScreens,
|
|
3564
|
+
const screens = sample3(uiScreens, 25);
|
|
3506
3565
|
if (!screens.length) {
|
|
3507
3566
|
return "";
|
|
3508
3567
|
}
|
|
3509
3568
|
const screenList = screens.map((s, i) => `### Screen ${i + 1}
|
|
3510
3569
|
${s.analysis}`).join("\n\n");
|
|
3511
3570
|
return `
|
|
3512
|
-
## UI
|
|
3571
|
+
## UI Case Studies
|
|
3513
3572
|
|
|
3514
|
-
|
|
3573
|
+
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.
|
|
3515
3574
|
|
|
3516
3575
|
${screenList}`.trim();
|
|
3517
3576
|
}
|
|
@@ -3534,7 +3593,7 @@ function getDesignExpertPrompt() {
|
|
|
3534
3593
|
let prompt = PROMPT_TEMPLATE.replace(
|
|
3535
3594
|
"{{font_library}}",
|
|
3536
3595
|
getFontLibrarySample()
|
|
3537
|
-
).replace("{{
|
|
3596
|
+
).replace("{{visual_design_references}}", getDesignReferencesSample()).replace("{{ui_case_studies}}", getUiInspirationSample());
|
|
3538
3597
|
if (specContext) {
|
|
3539
3598
|
prompt += `
|
|
3540
3599
|
|
|
@@ -3558,8 +3617,8 @@ var init_prompt2 = __esm({
|
|
|
3558
3617
|
SUBAGENT = "subagents/designExpert";
|
|
3559
3618
|
RUNTIME_PLACEHOLDERS = /* @__PURE__ */ new Set([
|
|
3560
3619
|
"font_library",
|
|
3561
|
-
"
|
|
3562
|
-
"
|
|
3620
|
+
"visual_design_references",
|
|
3621
|
+
"ui_case_studies"
|
|
3563
3622
|
]);
|
|
3564
3623
|
PROMPT_TEMPLATE = readAsset(SUBAGENT, "prompt.md").replace(/\{\{([^}]+)\}\}/g, (match, key) => {
|
|
3565
3624
|
const k = key.trim();
|
|
@@ -3577,7 +3636,7 @@ var init_designExpert = __esm({
|
|
|
3577
3636
|
init_tools2();
|
|
3578
3637
|
init_prompt2();
|
|
3579
3638
|
DESCRIPTION = `
|
|
3580
|
-
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.
|
|
3639
|
+
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.
|
|
3581
3640
|
`.trim();
|
|
3582
3641
|
designExpertTool = {
|
|
3583
3642
|
definition: {
|
|
@@ -3613,6 +3672,7 @@ Visual design expert. Describe the situation and what you need \u2014 the agent
|
|
|
3613
3672
|
subAgentId: "visualDesignExpert",
|
|
3614
3673
|
signal: context.signal,
|
|
3615
3674
|
parentToolId: context.toolCallId,
|
|
3675
|
+
requestId: context.requestId,
|
|
3616
3676
|
onEvent: context.onEvent,
|
|
3617
3677
|
resolveExternalTool: context.resolveExternalTool,
|
|
3618
3678
|
toolRegistry: context.toolRegistry,
|
|
@@ -3942,6 +4002,7 @@ var init_productVision = __esm({
|
|
|
3942
4002
|
subAgentId: "productVision",
|
|
3943
4003
|
signal: context.signal,
|
|
3944
4004
|
parentToolId: context.toolCallId,
|
|
4005
|
+
requestId: context.requestId,
|
|
3945
4006
|
onEvent: context.onEvent,
|
|
3946
4007
|
resolveExternalTool: context.resolveExternalTool,
|
|
3947
4008
|
toolRegistry: context.toolRegistry,
|
|
@@ -4106,6 +4167,7 @@ var init_codeSanityCheck = __esm({
|
|
|
4106
4167
|
subAgentId: "codeSanityCheck",
|
|
4107
4168
|
signal: context.signal,
|
|
4108
4169
|
parentToolId: context.toolCallId,
|
|
4170
|
+
requestId: context.requestId,
|
|
4109
4171
|
onEvent: context.onEvent,
|
|
4110
4172
|
resolveExternalTool: context.resolveExternalTool,
|
|
4111
4173
|
toolRegistry: context.toolRegistry
|
|
@@ -4244,6 +4306,7 @@ function loadSession(state) {
|
|
|
4244
4306
|
const data = JSON.parse(raw);
|
|
4245
4307
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
4246
4308
|
state.messages = sanitizeMessages(data.messages);
|
|
4309
|
+
log5.info("Session loaded", { messageCount: state.messages.length });
|
|
4247
4310
|
return true;
|
|
4248
4311
|
}
|
|
4249
4312
|
} catch {
|
|
@@ -4293,7 +4356,9 @@ function saveSession(state) {
|
|
|
4293
4356
|
JSON.stringify({ messages: state.messages }, null, 2),
|
|
4294
4357
|
"utf-8"
|
|
4295
4358
|
);
|
|
4296
|
-
|
|
4359
|
+
log5.info("Session saved", { messageCount: state.messages.length });
|
|
4360
|
+
} catch (err) {
|
|
4361
|
+
log5.warn("Session save failed", { error: err.message });
|
|
4297
4362
|
}
|
|
4298
4363
|
}
|
|
4299
4364
|
function clearSession(state) {
|
|
@@ -4303,10 +4368,12 @@ function clearSession(state) {
|
|
|
4303
4368
|
} catch {
|
|
4304
4369
|
}
|
|
4305
4370
|
}
|
|
4306
|
-
var SESSION_FILE;
|
|
4371
|
+
var log5, SESSION_FILE;
|
|
4307
4372
|
var init_session = __esm({
|
|
4308
4373
|
"src/session.ts"() {
|
|
4309
4374
|
"use strict";
|
|
4375
|
+
init_logger();
|
|
4376
|
+
log5 = createLogger("session");
|
|
4310
4377
|
SESSION_FILE = ".remy-session.json";
|
|
4311
4378
|
}
|
|
4312
4379
|
});
|
|
@@ -4537,17 +4604,17 @@ async function runTurn(params) {
|
|
|
4537
4604
|
onEvent,
|
|
4538
4605
|
resolveExternalTool,
|
|
4539
4606
|
hidden,
|
|
4607
|
+
requestId,
|
|
4540
4608
|
toolRegistry,
|
|
4541
4609
|
onBackgroundComplete
|
|
4542
4610
|
} = params;
|
|
4543
4611
|
const tools2 = getToolDefinitions(onboardingState);
|
|
4544
|
-
|
|
4545
|
-
|
|
4612
|
+
log6.info("Turn started", {
|
|
4613
|
+
requestId,
|
|
4614
|
+
model,
|
|
4546
4615
|
toolCount: tools2.length,
|
|
4547
|
-
tools: tools2.map((t) => t.name),
|
|
4548
4616
|
...attachments && attachments.length > 0 && {
|
|
4549
|
-
attachmentCount: attachments.length
|
|
4550
|
-
attachmentUrls: attachments.map((a) => a.url)
|
|
4617
|
+
attachmentCount: attachments.length
|
|
4551
4618
|
}
|
|
4552
4619
|
});
|
|
4553
4620
|
onEvent({ type: "turn_started" });
|
|
@@ -4557,10 +4624,6 @@ async function runTurn(params) {
|
|
|
4557
4624
|
}
|
|
4558
4625
|
if (attachments && attachments.length > 0) {
|
|
4559
4626
|
userMsg.attachments = attachments;
|
|
4560
|
-
log.debug("Attachments added to user message", {
|
|
4561
|
-
count: attachments.length,
|
|
4562
|
-
urls: attachments.map((a) => a.url)
|
|
4563
|
-
});
|
|
4564
4627
|
}
|
|
4565
4628
|
state.messages.push(userMsg);
|
|
4566
4629
|
const STATUS_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -4621,11 +4684,6 @@ async function runTurn(params) {
|
|
|
4621
4684
|
}
|
|
4622
4685
|
acc.lastEmittedCount = result.emittedCount;
|
|
4623
4686
|
acc.started = true;
|
|
4624
|
-
log.debug("Streaming partial tool_start", {
|
|
4625
|
-
id,
|
|
4626
|
-
name,
|
|
4627
|
-
emittedCount: result.emittedCount
|
|
4628
|
-
});
|
|
4629
4687
|
onEvent({
|
|
4630
4688
|
type: "tool_start",
|
|
4631
4689
|
id,
|
|
@@ -4641,10 +4699,6 @@ async function runTurn(params) {
|
|
|
4641
4699
|
}
|
|
4642
4700
|
if (!acc.started) {
|
|
4643
4701
|
acc.started = true;
|
|
4644
|
-
log.debug("Streaming content tool: emitting early tool_start", {
|
|
4645
|
-
id,
|
|
4646
|
-
name
|
|
4647
|
-
});
|
|
4648
4702
|
onEvent({ type: "tool_start", id, name, input: partial });
|
|
4649
4703
|
}
|
|
4650
4704
|
if (transform) {
|
|
@@ -4652,18 +4706,8 @@ async function runTurn(params) {
|
|
|
4652
4706
|
if (result === null) {
|
|
4653
4707
|
return;
|
|
4654
4708
|
}
|
|
4655
|
-
log.debug("Streaming content tool: emitting tool_input_delta", {
|
|
4656
|
-
id,
|
|
4657
|
-
name,
|
|
4658
|
-
resultLength: result.length
|
|
4659
|
-
});
|
|
4660
4709
|
onEvent({ type: "tool_input_delta", id, name, result });
|
|
4661
4710
|
} else {
|
|
4662
|
-
log.debug("Streaming content tool: emitting tool_input_delta", {
|
|
4663
|
-
id,
|
|
4664
|
-
name,
|
|
4665
|
-
contentLength: content.length
|
|
4666
|
-
});
|
|
4667
4711
|
onEvent({ type: "tool_input_delta", id, name, result: content });
|
|
4668
4712
|
}
|
|
4669
4713
|
}
|
|
@@ -4672,6 +4716,7 @@ async function runTurn(params) {
|
|
|
4672
4716
|
{
|
|
4673
4717
|
...apiConfig,
|
|
4674
4718
|
model,
|
|
4719
|
+
requestId,
|
|
4675
4720
|
system,
|
|
4676
4721
|
messages: cleanMessagesForApi(state.messages),
|
|
4677
4722
|
tools: tools2,
|
|
@@ -4723,12 +4768,6 @@ async function runTurn(params) {
|
|
|
4723
4768
|
case "tool_input_delta": {
|
|
4724
4769
|
const acc = getOrCreateAccumulator2(event.id, event.name);
|
|
4725
4770
|
acc.json += event.delta;
|
|
4726
|
-
log.debug("Received tool_input_delta", {
|
|
4727
|
-
id: event.id,
|
|
4728
|
-
name: event.name,
|
|
4729
|
-
deltaLength: event.delta.length,
|
|
4730
|
-
accumulatedLength: acc.json.length
|
|
4731
|
-
});
|
|
4732
4771
|
try {
|
|
4733
4772
|
const partial = parsePartialJson(acc.json);
|
|
4734
4773
|
await handlePartialInput(acc, event.id, event.name, partial);
|
|
@@ -4738,11 +4777,6 @@ async function runTurn(params) {
|
|
|
4738
4777
|
}
|
|
4739
4778
|
case "tool_input_args": {
|
|
4740
4779
|
const acc = getOrCreateAccumulator2(event.id, event.name);
|
|
4741
|
-
log.debug("Received tool_input_args", {
|
|
4742
|
-
id: event.id,
|
|
4743
|
-
name: event.name,
|
|
4744
|
-
keys: Object.keys(event.args)
|
|
4745
|
-
});
|
|
4746
4780
|
await handlePartialInput(acc, event.id, event.name, event.args);
|
|
4747
4781
|
break;
|
|
4748
4782
|
}
|
|
@@ -4759,11 +4793,10 @@ async function runTurn(params) {
|
|
|
4759
4793
|
const tool = getToolByName(event.name);
|
|
4760
4794
|
const wasStreamed = acc?.started ?? false;
|
|
4761
4795
|
const isInputStreaming = !!tool?.streaming?.partialInput;
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
isInputStreaming
|
|
4796
|
+
log6.info("Tool received", {
|
|
4797
|
+
requestId,
|
|
4798
|
+
toolCallId: event.id,
|
|
4799
|
+
name: event.name
|
|
4767
4800
|
});
|
|
4768
4801
|
if (!wasStreamed || isInputStreaming) {
|
|
4769
4802
|
onEvent({
|
|
@@ -4817,7 +4850,8 @@ async function runTurn(params) {
|
|
|
4817
4850
|
onEvent({ type: "turn_done" });
|
|
4818
4851
|
return;
|
|
4819
4852
|
}
|
|
4820
|
-
|
|
4853
|
+
log6.info("Tools executing", {
|
|
4854
|
+
requestId,
|
|
4821
4855
|
count: toolCalls.length,
|
|
4822
4856
|
tools: toolCalls.map((tc) => tc.name)
|
|
4823
4857
|
});
|
|
@@ -4860,9 +4894,10 @@ async function runTurn(params) {
|
|
|
4860
4894
|
let result;
|
|
4861
4895
|
if (EXTERNAL_TOOLS.has(tc.name) && resolveExternalTool) {
|
|
4862
4896
|
saveSession(state);
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4897
|
+
log6.info("Waiting for external tool result", {
|
|
4898
|
+
requestId,
|
|
4899
|
+
toolCallId: tc.id,
|
|
4900
|
+
name: tc.name
|
|
4866
4901
|
});
|
|
4867
4902
|
result = await resolveExternalTool(tc.id, tc.name, input);
|
|
4868
4903
|
} else {
|
|
@@ -4873,6 +4908,7 @@ async function runTurn(params) {
|
|
|
4873
4908
|
onEvent: wrappedOnEvent,
|
|
4874
4909
|
resolveExternalTool,
|
|
4875
4910
|
toolCallId: tc.id,
|
|
4911
|
+
requestId,
|
|
4876
4912
|
subAgentMessages,
|
|
4877
4913
|
toolRegistry,
|
|
4878
4914
|
onBackgroundComplete,
|
|
@@ -4911,11 +4947,12 @@ async function runTurn(params) {
|
|
|
4911
4947
|
run(tc.input);
|
|
4912
4948
|
const r = await resultPromise;
|
|
4913
4949
|
toolRegistry?.unregister(tc.id);
|
|
4914
|
-
|
|
4950
|
+
log6.info("Tool completed", {
|
|
4951
|
+
requestId,
|
|
4952
|
+
toolCallId: tc.id,
|
|
4915
4953
|
name: tc.name,
|
|
4916
|
-
|
|
4917
|
-
isError: r.isError
|
|
4918
|
-
resultLength: r.result.length
|
|
4954
|
+
durationMs: Date.now() - toolStart,
|
|
4955
|
+
isError: r.isError
|
|
4919
4956
|
});
|
|
4920
4957
|
onEvent({
|
|
4921
4958
|
type: "tool_done",
|
|
@@ -4959,7 +4996,7 @@ async function runTurn(params) {
|
|
|
4959
4996
|
}
|
|
4960
4997
|
}
|
|
4961
4998
|
}
|
|
4962
|
-
var EXTERNAL_TOOLS;
|
|
4999
|
+
var log6, EXTERNAL_TOOLS;
|
|
4963
5000
|
var init_agent = __esm({
|
|
4964
5001
|
"src/agent.ts"() {
|
|
4965
5002
|
"use strict";
|
|
@@ -4971,6 +5008,7 @@ var init_agent = __esm({
|
|
|
4971
5008
|
init_statusWatcher();
|
|
4972
5009
|
init_errors();
|
|
4973
5010
|
init_cleanMessages();
|
|
5011
|
+
log6 = createLogger("agent");
|
|
4974
5012
|
EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
4975
5013
|
"promptUser",
|
|
4976
5014
|
"setProjectOnboardingState",
|
|
@@ -5250,10 +5288,10 @@ import os from "os";
|
|
|
5250
5288
|
function loadConfigFile() {
|
|
5251
5289
|
try {
|
|
5252
5290
|
const raw = fs17.readFileSync(CONFIG_PATH, "utf-8");
|
|
5253
|
-
|
|
5291
|
+
log7.debug("Loaded config file", { path: CONFIG_PATH });
|
|
5254
5292
|
return JSON.parse(raw);
|
|
5255
5293
|
} catch (err) {
|
|
5256
|
-
|
|
5294
|
+
log7.debug("No config file found", {
|
|
5257
5295
|
path: CONFIG_PATH,
|
|
5258
5296
|
error: err.message
|
|
5259
5297
|
});
|
|
@@ -5267,24 +5305,25 @@ function resolveConfig(flags2) {
|
|
|
5267
5305
|
const apiKey = flags2?.apiKey || process.env.MINDSTUDIO_API_KEY || env?.apiKey || "";
|
|
5268
5306
|
const baseUrl2 = flags2?.baseUrl || process.env.MINDSTUDIO_BASE_URL || env?.apiBaseUrl || DEFAULT_BASE_URL;
|
|
5269
5307
|
if (!apiKey) {
|
|
5270
|
-
|
|
5308
|
+
log7.error("No API key found");
|
|
5271
5309
|
throw new Error(
|
|
5272
5310
|
"No API key found. Set MINDSTUDIO_API_KEY or configure ~/.mindstudio-local-tunnel/config.json."
|
|
5273
5311
|
);
|
|
5274
5312
|
}
|
|
5275
5313
|
const keySource = flags2?.apiKey ? "cli flag" : process.env.MINDSTUDIO_API_KEY ? "env var" : "config file";
|
|
5276
|
-
|
|
5314
|
+
log7.info("Config resolved", {
|
|
5277
5315
|
baseUrl: baseUrl2,
|
|
5278
5316
|
keySource,
|
|
5279
5317
|
environment: activeEnv
|
|
5280
5318
|
});
|
|
5281
5319
|
return { apiKey, baseUrl: baseUrl2 };
|
|
5282
5320
|
}
|
|
5283
|
-
var CONFIG_PATH, DEFAULT_BASE_URL;
|
|
5321
|
+
var log7, CONFIG_PATH, DEFAULT_BASE_URL;
|
|
5284
5322
|
var init_config = __esm({
|
|
5285
5323
|
"src/config.ts"() {
|
|
5286
5324
|
"use strict";
|
|
5287
5325
|
init_logger();
|
|
5326
|
+
log7 = createLogger("config");
|
|
5288
5327
|
CONFIG_PATH = path8.join(
|
|
5289
5328
|
os.homedir(),
|
|
5290
5329
|
".mindstudio-local-tunnel",
|
|
@@ -5295,10 +5334,12 @@ var init_config = __esm({
|
|
|
5295
5334
|
});
|
|
5296
5335
|
|
|
5297
5336
|
// src/toolRegistry.ts
|
|
5298
|
-
var ToolRegistry;
|
|
5337
|
+
var log8, ToolRegistry;
|
|
5299
5338
|
var init_toolRegistry = __esm({
|
|
5300
5339
|
"src/toolRegistry.ts"() {
|
|
5301
5340
|
"use strict";
|
|
5341
|
+
init_logger();
|
|
5342
|
+
log8 = createLogger("tool-registry");
|
|
5302
5343
|
ToolRegistry = class {
|
|
5303
5344
|
entries = /* @__PURE__ */ new Map();
|
|
5304
5345
|
onEvent;
|
|
@@ -5324,6 +5365,7 @@ var init_toolRegistry = __esm({
|
|
|
5324
5365
|
if (!entry) {
|
|
5325
5366
|
return false;
|
|
5326
5367
|
}
|
|
5368
|
+
log8.info("Tool stopped", { toolCallId: id, name: entry.name, mode });
|
|
5327
5369
|
entry.abortController.abort(mode);
|
|
5328
5370
|
if (mode === "graceful") {
|
|
5329
5371
|
const partial = entry.getPartialResult?.() ?? "";
|
|
@@ -5356,6 +5398,7 @@ ${partial}` : "[INTERRUPTED] Tool execution was stopped.";
|
|
|
5356
5398
|
if (!entry) {
|
|
5357
5399
|
return false;
|
|
5358
5400
|
}
|
|
5401
|
+
log8.info("Tool restarted", { toolCallId: id, name: entry.name });
|
|
5359
5402
|
entry.abortController.abort("restart");
|
|
5360
5403
|
const newInput = patchedInput ? { ...entry.input, ...patchedInput } : entry.input;
|
|
5361
5404
|
this.onEvent?.({
|
|
@@ -5476,6 +5519,11 @@ ${xmlParts}
|
|
|
5476
5519
|
}
|
|
5477
5520
|
}
|
|
5478
5521
|
}
|
|
5522
|
+
log9.info("Background complete", {
|
|
5523
|
+
toolCallId,
|
|
5524
|
+
name,
|
|
5525
|
+
requestId: currentRequestId
|
|
5526
|
+
});
|
|
5479
5527
|
onEvent({
|
|
5480
5528
|
type: "tool_background_complete",
|
|
5481
5529
|
id: toolCallId,
|
|
@@ -5668,6 +5716,7 @@ ${xmlParts}
|
|
|
5668
5716
|
currentRequestId = requestId;
|
|
5669
5717
|
currentAbort = new AbortController();
|
|
5670
5718
|
completedEmitted = false;
|
|
5719
|
+
const turnStart = Date.now();
|
|
5671
5720
|
const attachments = parsed.attachments;
|
|
5672
5721
|
if (attachments?.length) {
|
|
5673
5722
|
console.warn(
|
|
@@ -5699,6 +5748,7 @@ ${xmlParts}
|
|
|
5699
5748
|
system,
|
|
5700
5749
|
model: opts.model,
|
|
5701
5750
|
onboardingState,
|
|
5751
|
+
requestId,
|
|
5702
5752
|
signal: currentAbort.signal,
|
|
5703
5753
|
onEvent,
|
|
5704
5754
|
resolveExternalTool,
|
|
@@ -5713,11 +5763,20 @@ ${xmlParts}
|
|
|
5713
5763
|
requestId
|
|
5714
5764
|
);
|
|
5715
5765
|
}
|
|
5766
|
+
log9.info("Turn complete", {
|
|
5767
|
+
requestId,
|
|
5768
|
+
durationMs: Date.now() - turnStart
|
|
5769
|
+
});
|
|
5716
5770
|
} catch (err) {
|
|
5717
5771
|
if (!completedEmitted) {
|
|
5718
5772
|
emit("error", { error: err.message }, requestId);
|
|
5719
5773
|
emit("completed", { success: false, error: err.message }, requestId);
|
|
5720
5774
|
}
|
|
5775
|
+
log9.warn("Command failed", {
|
|
5776
|
+
action: "message",
|
|
5777
|
+
requestId,
|
|
5778
|
+
error: err.message
|
|
5779
|
+
});
|
|
5721
5780
|
}
|
|
5722
5781
|
currentAbort = null;
|
|
5723
5782
|
currentRequestId = void 0;
|
|
@@ -5733,6 +5792,7 @@ ${xmlParts}
|
|
|
5733
5792
|
return;
|
|
5734
5793
|
}
|
|
5735
5794
|
const { action, requestId } = parsed;
|
|
5795
|
+
log9.info("Command received", { action, requestId });
|
|
5736
5796
|
if (action === "tool_result" && parsed.id) {
|
|
5737
5797
|
const id = parsed.id;
|
|
5738
5798
|
const result = parsed.result ?? "";
|
|
@@ -5813,16 +5873,19 @@ ${xmlParts}
|
|
|
5813
5873
|
process.on("SIGINT", shutdown);
|
|
5814
5874
|
emit("ready");
|
|
5815
5875
|
}
|
|
5876
|
+
var log9;
|
|
5816
5877
|
var init_headless = __esm({
|
|
5817
5878
|
"src/headless.ts"() {
|
|
5818
5879
|
"use strict";
|
|
5819
5880
|
init_assets();
|
|
5881
|
+
init_logger();
|
|
5820
5882
|
init_config();
|
|
5821
5883
|
init_prompt4();
|
|
5822
5884
|
init_lsp();
|
|
5823
5885
|
init_agent();
|
|
5824
5886
|
init_session();
|
|
5825
5887
|
init_toolRegistry();
|
|
5888
|
+
log9 = createLogger("headless");
|
|
5826
5889
|
}
|
|
5827
5890
|
});
|
|
5828
5891
|
|
|
@@ -6145,6 +6208,7 @@ for (let i = 0; i < args.length; i++) {
|
|
|
6145
6208
|
headless = true;
|
|
6146
6209
|
}
|
|
6147
6210
|
}
|
|
6211
|
+
var startupLog = createLogger("startup");
|
|
6148
6212
|
function printDebugInfo(config) {
|
|
6149
6213
|
const pkg = JSON.parse(
|
|
6150
6214
|
fs18.readFileSync(
|
|
@@ -6153,23 +6217,18 @@ function printDebugInfo(config) {
|
|
|
6153
6217
|
)
|
|
6154
6218
|
);
|
|
6155
6219
|
const keyPreview = config.apiKey ? `${config.apiKey.slice(0, 8)}...${config.apiKey.slice(-4)}` : "(none)";
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
console.log(
|
|
6169
|
-
` key source: ${flags.apiKey ? "cli flag" : process.env.MINDSTUDIO_API_KEY ? "env var" : "config file"}`
|
|
6170
|
-
);
|
|
6171
|
-
console.log("\u2500".repeat(40));
|
|
6172
|
-
console.log("");
|
|
6220
|
+
startupLog.info("Startup", {
|
|
6221
|
+
version: pkg.version,
|
|
6222
|
+
node: process.version,
|
|
6223
|
+
platform: `${os2.platform()} ${os2.arch()}`,
|
|
6224
|
+
os: `${os2.type()} ${os2.release()}`,
|
|
6225
|
+
cwd: process.cwd(),
|
|
6226
|
+
bin: process.argv[1],
|
|
6227
|
+
model: flags.model || "(default)",
|
|
6228
|
+
baseUrl: config.baseUrl,
|
|
6229
|
+
apiKey: keyPreview,
|
|
6230
|
+
keySource: flags.apiKey ? "cli flag" : process.env.MINDSTUDIO_API_KEY ? "env var" : "config file"
|
|
6231
|
+
});
|
|
6173
6232
|
}
|
|
6174
6233
|
var logLevel = flags.logLevel || void 0;
|
|
6175
6234
|
if (headless) {
|