@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/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 parts = [`[${timestamp()}]`, level.toUpperCase().padEnd(5), msg];
31
+ const entry = {
32
+ ts: Date.now(),
33
+ level,
34
+ module,
35
+ msg
36
+ };
35
37
  if (data) {
36
- parts.push(JSON.stringify(truncateValues(data)));
38
+ Object.assign(entry, truncateValues(data));
37
39
  }
38
- writeFn(parts.join(" "));
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
- fd = fs.openSync(".remy-debug.log", "a");
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, log;
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("POST agent/chat", {
99
- url,
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.info("Request aborted by signal");
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(`Response ${res.status}`, { ttfb: `${ttfb}ms` });
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", { status: res.status, error: errorMessage });
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", { elapsed: `${Date.now() - startTime}ms` });
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
- elapsed: `${elapsed}ms`,
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.info("Retrying after backoff", { backoffMs: backoff });
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
- log.info("Sidecar configured", { url });
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
- log.error("Sidecar error", { endpoint, status: res.status });
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
- log.error("Sidecar connection error", { endpoint, error: err.message });
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 (err) {
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
- log.info("Sub-agent executing tools", {
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 run();
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
- run().then((finalResult) => onBackgroundComplete?.(finalResult)).catch(
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
- log.debug("Failed to parse batch analysis result", {
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/_seedream.ts
3064
- async function seedreamGenerate(opts) {
3065
- const { prompts, sourceImages, transparentBackground, onLog } = opts;
3066
- const width = opts.width || 2048;
3067
- const height = opts.height || 2048;
3068
- const config = { width, height };
3069
- if (sourceImages?.length) {
3070
- config.images = sourceImages;
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 (prompts.length === 1) {
3133
+ if (enhancedPrompts.length === 1) {
3074
3134
  const step = JSON.stringify({
3075
- prompt: prompts[0],
3135
+ prompt: enhancedPrompts[0],
3076
3136
  imageModelOverride: {
3077
- model: "seedream-4.5",
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 = prompts.map((prompt) => ({
3147
+ const steps = enhancedPrompts.map((prompt) => ({
3088
3148
  stepType: "generateImage",
3089
3149
  step: {
3090
3150
  prompt,
3091
3151
  imageModelOverride: {
3092
- model: "seedream-4.5",
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 { prompt: prompts[i], error: url };
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 { url, prompt: prompts[i], analysis, width, height };
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 init_seedream = __esm({
3142
- "src/subagents/designExpert/tools/_seedream.ts"() {
3211
+ var init_imageGenerator = __esm({
3212
+ "src/subagents/designExpert/tools/images/imageGenerator.ts"() {
3143
3213
  "use strict";
3144
3214
  init_runCli();
3145
- ANALYZE_PROMPT = "You are reviewing this image for a visual designer sourcing assets for a project. Describe: what the image depicts, the mood and color palette, how the lighting and composition work, whether there are any issues (unwanted text, artifacts, distortions), and how it could be used in a layout (hero background, feature section, card texture, etc). Be concise and practical. Respond only with your analysis as Markdown and absolutely no other text. Do not use emojis - use unicode if you need symbols.";
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 seedreamGenerate({
3227
+ return generateImageAssets({
3157
3228
  prompts: input.prompts,
3158
- width: input.width,
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
- init_seedream();
3238
+ init_imageGenerator();
3169
3239
  definition6 = {
3170
3240
  name: "generateImages",
3171
- description: "Generate images using AI. 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.",
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 generation prompts. Be detailed: describe style, mood, composition, colors. Multiple prompts run in parallel."
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
- height: {
3187
- type: "number",
3188
- description: "Image height in pixels. Default 2048. Range: 2048-4096."
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 seedreamGenerate({
3275
+ return generateImageAssets({
3209
3276
  prompts: input.prompts,
3210
3277
  sourceImages: input.sourceImages,
3211
- width: input.width,
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
- init_seedream();
3287
+ init_imageGenerator();
3222
3288
  definition7 = {
3223
3289
  name: "editImages",
3224
- description: "Edit or transform existing images using AI. 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 ferences into something new. Returns CDN URLs with analysis.",
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 prompts describing how to transform the source images. Multiple prompts run in parallel, each using the same source images."
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
- width: {
3243
- type: "number",
3244
- description: "Output width in pixels. Default 2048. Range: 2048-4096."
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
- A random sample from a curated font library. Use these as starting points for font selection.
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, 30);
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 what the bar looks like. These are real sites that made it onto curated design galleries because they did something bold, intentional, and memorable. Use them as inspiration and let the takeaways guide your work. Your designs should feel like they belong in this company.
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, 20);
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 Pattern References
3571
+ ## UI Case Studies
3513
3572
 
3514
- There are real app screens from well-designed products, sourced and curated by hand as a reference by a desigher. Use them as inspiration and let the takeaways guide your work. Your designs should feel like they belong in this company.
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("{{design_references}}", getDesignReferencesSample()).replace("{{ui_patterns}}", getUiInspirationSample());
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
- "design_references",
3562
- "ui_patterns"
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
- } catch {
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
- log.info("Turn started", {
4545
- messageLength: userMessage.length,
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
- log.info("Tool call received", {
4763
- id: event.id,
4764
- name: event.name,
4765
- wasStreamed,
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
- log.info("Executing tools", {
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
- log.info("Waiting for external tool result", {
4864
- name: tc.name,
4865
- id: tc.id
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
- log.info("Tool completed", {
4950
+ log6.info("Tool completed", {
4951
+ requestId,
4952
+ toolCallId: tc.id,
4915
4953
  name: tc.name,
4916
- elapsed: `${Date.now() - toolStart}ms`,
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
- log.debug("Loaded config file", { path: CONFIG_PATH });
5291
+ log7.debug("Loaded config file", { path: CONFIG_PATH });
5254
5292
  return JSON.parse(raw);
5255
5293
  } catch (err) {
5256
- log.debug("No config file found", {
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
- log.error("No API key found");
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
- log.info("Config resolved", {
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
- console.log("");
6157
- console.log("remy debug info");
6158
- console.log("\u2500".repeat(40));
6159
- console.log(` version: ${pkg.version}`);
6160
- console.log(` node: ${process.version}`);
6161
- console.log(` platform: ${os2.platform()} ${os2.arch()}`);
6162
- console.log(` os: ${os2.type()} ${os2.release()}`);
6163
- console.log(` cwd: ${process.cwd()}`);
6164
- console.log(` bin: ${process.argv[1]}`);
6165
- console.log(` model: ${flags.model || "(default)"}`);
6166
- console.log(` base url: ${config.baseUrl}`);
6167
- console.log(` api key: ${keyPreview}`);
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) {