@mindstudio-ai/remy 0.1.126 → 0.1.127

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.
@@ -12,6 +12,6 @@ If approved:
12
12
  - Stage and commit any uncommitted changes with a clean, descriptive commit message
13
13
  - Push to main
14
14
  - Use `mindstudio-prod releases status --wait` to poll the build until it completes. Let the user know it's deploying, then report back when it's live.
15
- - Once deployed, offer to help with next steps: setting up a custom domain (`mindstudio-prod domains`), checking for errors (`mindstudio-prod requests stats`), seeding production data (`mindstudio-prod db`), managing env vars/secrets, or anything else they need for launch.
15
+ - Once deployed, offer to help with next steps. This includes technical steps likesetting up a custom domain (`mindstudio-prod domains`), checking for errors (`mindstudio-prod requests stats`), seeding production data (`mindstudio-prod db`), managing env vars/secrets, or anything else they need for launch. It also includes going above and beyond and helping holistically. If it's the initial deploy, offer to help create collateral to announce the launch (e.g., an image for sharing on social media, text copy for a post, etc); if it's a meaningful incremental update, an annoucement post or something similar - go above and beyond here to help the user see that you care about the product from end-to-end, not just writing code! They will be appreciative, grateful, and pleased with your creativity here. Refer to the design guidance in the spec for how to talk about the product, and consider consulting the design expert to generate images or other marketing collateral.
16
16
 
17
17
  If dismissed, acknowledge and do nothing.
package/dist/headless.js CHANGED
@@ -583,13 +583,13 @@ async function* streamChat(params) {
583
583
  }
584
584
  }
585
585
  }
586
- var MAX_RETRIES = 3;
586
+ var MAX_RETRIES = 5;
587
587
  var INITIAL_BACKOFF_MS = 1e3;
588
588
  function isRetryableError(error) {
589
589
  return /Network error/i.test(error) || /HTTP 5\d\d/i.test(error) || /Stream stalled/i.test(error) || /overloaded/i.test(error) || /terminated/i.test(error);
590
590
  }
591
591
  function sleep(ms) {
592
- return new Promise((resolve) => setTimeout(resolve, ms));
592
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
593
593
  }
594
594
  async function* streamChatWithRetry(params, options) {
595
595
  for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
@@ -1569,7 +1569,7 @@ var confirmDestructiveActionTool = {
1569
1569
  // src/subagents/common/runCli.ts
1570
1570
  import { spawn } from "child_process";
1571
1571
  function runCli(cmd, options) {
1572
- return new Promise((resolve) => {
1572
+ return new Promise((resolve2) => {
1573
1573
  const timeout = options?.timeout ?? 6e4;
1574
1574
  const maxBuffer = options?.maxBuffer ?? 1024 * 1024;
1575
1575
  const cmdWithLogs = options?.jsonLogs && !cmd.includes("--json-logs") ? cmd.replace(/^(mindstudio\s+\S+)/, "$1 --json-logs") : cmd;
@@ -1635,15 +1635,15 @@ function runCli(cmd, options) {
1635
1635
  const logBlock = !options?.onLog && logs.length > 0 ? logs.join("\n") + "\n\n" : "";
1636
1636
  const out = stdout.trim();
1637
1637
  if (out) {
1638
- resolve(logBlock + out);
1638
+ resolve2(logBlock + out);
1639
1639
  return;
1640
1640
  }
1641
1641
  if (code !== 0 || killed) {
1642
1642
  const errMsg = stderr.trim() || (killed ? "Process timed out" : `Exit code ${code}`);
1643
- resolve(logBlock + `Error: ${errMsg}`);
1643
+ resolve2(logBlock + `Error: ${errMsg}`);
1644
1644
  return;
1645
1645
  }
1646
- resolve(logBlock + "(no response)");
1646
+ resolve2(logBlock + "(no response)");
1647
1647
  });
1648
1648
  });
1649
1649
  }
@@ -2077,7 +2077,7 @@ var bashTool = {
2077
2077
  async execute(input, context) {
2078
2078
  const maxLines = input.maxLines === 0 ? Infinity : input.maxLines || DEFAULT_MAX_LINES3;
2079
2079
  const timeoutMs = input.timeout ? input.timeout * 1e3 : DEFAULT_TIMEOUT_MS;
2080
- return new Promise((resolve) => {
2080
+ return new Promise((resolve2) => {
2081
2081
  const child = spawn2("sh", ["-c", input.command], {
2082
2082
  cwd: input.cwd || void 0,
2083
2083
  env: { ...process.env, FORCE_COLOR: "1" }
@@ -2100,26 +2100,26 @@ var bashTool = {
2100
2100
  clearTimeout(timer);
2101
2101
  if (!output) {
2102
2102
  if (code && code !== 0) {
2103
- resolve(`Error: process exited with code ${code}`);
2103
+ resolve2(`Error: process exited with code ${code}`);
2104
2104
  } else {
2105
- resolve("(no output)");
2105
+ resolve2("(no output)");
2106
2106
  }
2107
2107
  return;
2108
2108
  }
2109
2109
  const lines = output.split("\n");
2110
2110
  if (lines.length > maxLines) {
2111
- resolve(
2111
+ resolve2(
2112
2112
  lines.slice(0, maxLines).join("\n") + `
2113
2113
 
2114
2114
  (truncated at ${maxLines} lines of ${lines.length} total \u2014 increase maxLines to see more)`
2115
2115
  );
2116
2116
  } else {
2117
- resolve(output);
2117
+ resolve2(output);
2118
2118
  }
2119
2119
  });
2120
2120
  child.on("error", (err) => {
2121
2121
  clearTimeout(timer);
2122
- resolve(`Error: ${err.message}`);
2122
+ resolve2(`Error: ${err.message}`);
2123
2123
  });
2124
2124
  });
2125
2125
  }
@@ -2173,17 +2173,17 @@ var grepTool = {
2173
2173
  const escaped = input.pattern.replace(/'/g, "'\\''");
2174
2174
  const rgCmd = `rg -n --no-heading --max-count=${max}${globFlag} '${escaped}' ${searchPath}`;
2175
2175
  const grepCmd = `grep -rn --max-count=${max} '${escaped}' ${searchPath} --include='*.ts' --include='*.tsx' --include='*.js' --include='*.json' --include='*.md'`;
2176
- return new Promise((resolve) => {
2176
+ return new Promise((resolve2) => {
2177
2177
  exec(rgCmd, { maxBuffer: 512 * 1024 }, (err, stdout) => {
2178
2178
  if (stdout?.trim()) {
2179
- resolve(formatResults(stdout, max));
2179
+ resolve2(formatResults(stdout, max));
2180
2180
  return;
2181
2181
  }
2182
2182
  exec(grepCmd, { maxBuffer: 512 * 1024 }, (_err, grepStdout) => {
2183
2183
  if (grepStdout?.trim()) {
2184
- resolve(formatResults(grepStdout, max));
2184
+ resolve2(formatResults(grepStdout, max));
2185
2185
  } else {
2186
- resolve("No matches found.");
2186
+ resolve2("No matches found.");
2187
2187
  }
2188
2188
  });
2189
2189
  });
@@ -2431,7 +2431,7 @@ var restartProcessTool = {
2431
2431
  async execute(input) {
2432
2432
  const data = await lspRequest("/restart-process", { name: input.name });
2433
2433
  if (data.ok) {
2434
- await new Promise((resolve) => setTimeout(resolve, 5e3));
2434
+ await new Promise((resolve2) => setTimeout(resolve2, 5e3));
2435
2435
  return `Restarted ${input.name}.`;
2436
2436
  }
2437
2437
  return `Error: unexpected response: ${JSON.stringify(data)}`;
@@ -2522,7 +2522,7 @@ var queryDatabaseTool = {
2522
2522
  };
2523
2523
 
2524
2524
  // src/subagents/common/analyzeImage.ts
2525
- var VISION_MODEL = "gemini-3-flash";
2525
+ var VISION_MODEL = "claude-4-6-sonnet";
2526
2526
  var VISION_MODEL_OVERRIDE = JSON.stringify({
2527
2527
  model: VISION_MODEL,
2528
2528
  config: { thinkingBudget: "off" }
@@ -3763,10 +3763,10 @@ __export(generateImages_exports, {
3763
3763
  });
3764
3764
 
3765
3765
  // src/subagents/designExpert/tools/images/enhancePrompt.ts
3766
- var ENHANCE_MODEL = "gemini-3-flash";
3766
+ var ENHANCE_MODEL = "claude-4-6-sonnet";
3767
3767
  var MODEL_OVERRIDE = JSON.stringify({
3768
3768
  model: ENHANCE_MODEL,
3769
- config: { thinkingBudget: "off" }
3769
+ config: { reasoning: "false" }
3770
3770
  });
3771
3771
  var SYSTEM_PROMPT = readAsset(
3772
3772
  "subagents/designExpert/tools/images/enhance-image-prompt.md"
@@ -4014,7 +4014,8 @@ async function executeDesignExpertTool(name, input, context, toolCallId, onLog)
4014
4014
  if (!tool) {
4015
4015
  return `Error: unknown tool "${name}"`;
4016
4016
  }
4017
- return tool.execute(input, onLog, context);
4017
+ const childContext = context && toolCallId ? deriveContext(context, toolCallId) : context;
4018
+ return tool.execute(input, onLog, childContext);
4018
4019
  }
4019
4020
 
4020
4021
  // src/subagents/common/context.ts
@@ -4408,97 +4409,71 @@ var designExpertTool = {
4408
4409
  // src/subagents/productVision/tools.ts
4409
4410
  var VISION_TOOLS = [
4410
4411
  {
4411
- name: "writeRoadmapItem",
4412
- description: "Create a new roadmap item in src/roadmap/.",
4412
+ name: "listDir",
4413
+ description: "List files in src/roadmap/.",
4414
+ inputSchema: {
4415
+ type: "object",
4416
+ properties: {}
4417
+ }
4418
+ },
4419
+ {
4420
+ name: "readFile",
4421
+ description: 'Read a file from src/roadmap/. Path is relative to src/roadmap/ (e.g. "index.json", "mute.md").',
4413
4422
  inputSchema: {
4414
4423
  type: "object",
4415
4424
  properties: {
4416
- slug: {
4417
- type: "string",
4418
- description: 'Kebab-case filename (without .md). e.g. "ai-weekly-digest"'
4419
- },
4420
- name: {
4421
- type: "string",
4422
- description: "User-facing feature name."
4423
- },
4424
- description: {
4425
- type: "string",
4426
- description: "Short user-facing summary (1-2 sentences)."
4427
- },
4428
- effort: {
4429
- type: "string",
4430
- enum: ["quick", "small", "medium", "large"]
4431
- },
4432
- requires: {
4433
- type: "array",
4434
- items: { type: "string" },
4435
- description: "Slugs of prerequisite roadmap items. Empty array if independent."
4436
- },
4437
- body: {
4425
+ path: {
4438
4426
  type: "string",
4439
- description: "Full MSFM body: prose description for the user, followed by ~~~annotation~~~ with technical implementation notes."
4427
+ description: "File path relative to src/roadmap/."
4440
4428
  }
4441
4429
  },
4442
- required: ["slug", "name", "description", "effort", "requires", "body"]
4430
+ required: ["path"]
4443
4431
  }
4444
4432
  },
4445
4433
  {
4446
- name: "updateRoadmapItem",
4447
- description: "Update an existing roadmap item. Only include the fields you want to change.",
4434
+ name: "writeFile",
4435
+ description: 'Create or overwrite a file in src/roadmap/. Path is relative to src/roadmap/ (e.g. "index.json", "ai-weekly-digest.md").',
4448
4436
  inputSchema: {
4449
4437
  type: "object",
4450
4438
  properties: {
4451
- slug: {
4452
- type: "string",
4453
- description: "The slug of the item to update (filename without .md)."
4454
- },
4455
- status: {
4456
- type: "string",
4457
- enum: ["done", "in-progress", "not-started"],
4458
- description: "New status."
4459
- },
4460
- name: {
4461
- type: "string",
4462
- description: "Updated feature name."
4463
- },
4464
- description: {
4465
- type: "string",
4466
- description: "Updated summary."
4467
- },
4468
- effort: {
4439
+ path: {
4469
4440
  type: "string",
4470
- enum: ["quick", "small", "medium", "large"],
4471
- description: "Updated effort level."
4441
+ description: "File path relative to src/roadmap/."
4472
4442
  },
4473
- requires: {
4474
- type: "array",
4475
- items: { type: "string" },
4476
- description: "Updated prerequisites."
4477
- },
4478
- body: {
4443
+ content: {
4479
4444
  type: "string",
4480
- description: "Full replacement body (overwrites existing body)."
4481
- },
4482
- appendHistory: {
4445
+ description: "The full content to write to the file."
4446
+ }
4447
+ },
4448
+ required: ["path", "content"]
4449
+ }
4450
+ },
4451
+ {
4452
+ name: "deleteFile",
4453
+ description: "Delete a file from src/roadmap/. Path is relative to src/roadmap/.",
4454
+ inputSchema: {
4455
+ type: "object",
4456
+ properties: {
4457
+ path: {
4483
4458
  type: "string",
4484
- description: 'A history entry to append. Format: "- **2026-03-22** \u2014 Description of what was done."'
4459
+ description: "File path relative to src/roadmap/."
4485
4460
  }
4486
4461
  },
4487
- required: ["slug"]
4462
+ required: ["path"]
4488
4463
  }
4489
4464
  },
4490
4465
  {
4491
- name: "deleteRoadmapItem",
4492
- description: "Remove a roadmap item. Use when an idea is no longer relevant or has been absorbed into another item.",
4466
+ name: "writePitchDeck",
4467
+ description: "Generate a branded HTML pitch deck for the product and save it to src/roadmap/pitch.html. Delegates to the design expert who builds a beautiful self-contained slide deck from your request. Do NOT describe the design or structure of the deck, only provide the copy.",
4493
4468
  inputSchema: {
4494
4469
  type: "object",
4495
4470
  properties: {
4496
- slug: {
4471
+ task: {
4497
4472
  type: "string",
4498
- description: "The slug of the item to delete (filename without .md)."
4473
+ description: "Full description of the pitch deck content. Include the full copy of each slide in detail."
4499
4474
  }
4500
4475
  },
4501
- required: ["slug"]
4476
+ required: ["task"]
4502
4477
  }
4503
4478
  }
4504
4479
  ];
@@ -4507,115 +4482,62 @@ var VISION_TOOLS = [
4507
4482
  import fs16 from "fs";
4508
4483
  import path9 from "path";
4509
4484
  var ROADMAP_DIR = "src/roadmap";
4510
- function formatRequires(requires) {
4511
- return requires.length === 0 ? "[]" : `[${requires.map((r) => `"${r}"`).join(", ")}]`;
4485
+ function resolve(filePath) {
4486
+ return path9.join(ROADMAP_DIR, filePath);
4512
4487
  }
4513
- async function executeVisionTool(name, input) {
4488
+ async function executeVisionTool(name, input, context) {
4514
4489
  switch (name) {
4515
- case "writeRoadmapItem": {
4516
- const {
4517
- slug,
4518
- name: itemName,
4519
- description,
4520
- effort,
4521
- requires,
4522
- body
4523
- } = input;
4524
- const filePath = path9.join(ROADMAP_DIR, `${slug}.md`);
4490
+ case "listDir": {
4525
4491
  try {
4526
4492
  fs16.mkdirSync(ROADMAP_DIR, { recursive: true });
4527
- const oldContent = fs16.existsSync(filePath) ? fs16.readFileSync(filePath, "utf-8") : "";
4528
- const content = `---
4529
- name: ${itemName}
4530
- type: roadmap
4531
- status: ${slug === "mvp" ? "in-progress" : "not-started"}
4532
- description: ${description}
4533
- effort: ${effort}
4534
- requires: ${formatRequires(requires)}
4535
- ---
4536
-
4537
- ${body}
4538
- `;
4539
- fs16.writeFileSync(filePath, content, "utf-8");
4540
- const lineCount = content.split("\n").length;
4541
- const label = oldContent ? "Updated" : "Wrote";
4542
- return `${label} ${filePath} (${lineCount} lines)
4543
- ${unifiedDiff(filePath, oldContent, content)}`;
4493
+ const entries = fs16.readdirSync(ROADMAP_DIR, { withFileTypes: true });
4494
+ const lines = [];
4495
+ const dirs = entries.filter((e) => e.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
4496
+ const files = entries.filter((e) => !e.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
4497
+ for (const d of dirs) {
4498
+ lines.push(`${d.name}/`);
4499
+ }
4500
+ for (const f of files) {
4501
+ const stat = fs16.statSync(resolve(f.name));
4502
+ const size = stat.size < 1024 ? `${stat.size}B` : `${(stat.size / 1024).toFixed(1)}KB`;
4503
+ lines.push(`${f.name} (${size})`);
4504
+ }
4505
+ return lines.length > 0 ? lines.join("\n") : "(empty)";
4544
4506
  } catch (err) {
4545
- return `Error writing ${filePath}: ${err.message}`;
4507
+ return `Error: ${err.message}`;
4546
4508
  }
4547
4509
  }
4548
- case "updateRoadmapItem": {
4549
- const { slug } = input;
4550
- const filePath = path9.join(ROADMAP_DIR, `${slug}.md`);
4510
+ case "readFile": {
4511
+ const filePath = resolve(input.path);
4551
4512
  try {
4552
- if (!fs16.existsSync(filePath)) {
4553
- return `Error: ${filePath} does not exist`;
4554
- }
4555
- const oldContent = fs16.readFileSync(filePath, "utf-8");
4556
- let content = oldContent;
4557
- if (input.status) {
4558
- content = content.replace(
4559
- /^status:\s*.+$/m,
4560
- `status: ${input.status}`
4561
- );
4562
- }
4563
- if (input.name) {
4564
- content = content.replace(/^name:\s*.+$/m, `name: ${input.name}`);
4565
- }
4566
- if (input.description) {
4567
- content = content.replace(
4568
- /^description:\s*.+$/m,
4569
- `description: ${input.description}`
4570
- );
4571
- }
4572
- if (input.effort) {
4573
- content = content.replace(
4574
- /^effort:\s*.+$/m,
4575
- `effort: ${input.effort}`
4576
- );
4577
- }
4578
- if (input.requires) {
4579
- content = content.replace(
4580
- /^requires:\s*.+$/m,
4581
- `requires: ${formatRequires(input.requires)}`
4582
- );
4583
- }
4584
- if (input.body) {
4585
- const endOfFrontmatter = content.indexOf("---", 4);
4586
- if (endOfFrontmatter !== -1) {
4587
- const frontmatter = content.slice(0, endOfFrontmatter + 3);
4588
- content = `${frontmatter}
4589
-
4590
- ${input.body}
4591
- `;
4592
- }
4593
- }
4594
- if (input.appendHistory) {
4595
- if (content.includes("## History")) {
4596
- content = content.trimEnd() + `
4597
- ${input.appendHistory}
4598
- `;
4599
- } else {
4600
- content = content.trimEnd() + `
4601
-
4602
- ## History
4603
-
4604
- ${input.appendHistory}
4605
- `;
4606
- }
4513
+ const content = fs16.readFileSync(filePath, "utf-8");
4514
+ const lines = content.split("\n");
4515
+ const numbered = lines.map((line, i) => `${String(i + 1).padStart(4)} ${line}`).join("\n");
4516
+ return numbered;
4517
+ } catch (err) {
4518
+ return `Error reading ${filePath}: ${err.message}`;
4519
+ }
4520
+ }
4521
+ case "writeFile": {
4522
+ const filePath = resolve(input.path);
4523
+ try {
4524
+ fs16.mkdirSync(ROADMAP_DIR, { recursive: true });
4525
+ let oldContent = null;
4526
+ try {
4527
+ oldContent = fs16.readFileSync(filePath, "utf-8");
4528
+ } catch {
4607
4529
  }
4608
- fs16.writeFileSync(filePath, content, "utf-8");
4609
- const lineCount = content.split("\n").length;
4610
- return `Updated ${filePath} (${lineCount} lines)
4611
- ${unifiedDiff(filePath, oldContent, content)}`;
4530
+ fs16.writeFileSync(filePath, input.content, "utf-8");
4531
+ const lineCount = input.content.split("\n").length;
4532
+ const label = oldContent !== null ? "Wrote" : "Created";
4533
+ return `${label} ${filePath} (${lineCount} lines)
4534
+ ${unifiedDiff(filePath, oldContent ?? "", input.content)}`;
4612
4535
  } catch (err) {
4613
- return `Error updating ${filePath}: ${err.message}`;
4536
+ return `Error writing ${filePath}: ${err.message}`;
4614
4537
  }
4615
4538
  }
4616
- case "deleteRoadmapItem": {
4617
- const { slug } = input;
4618
- const filePath = path9.join(ROADMAP_DIR, `${slug}.md`);
4539
+ case "deleteFile": {
4540
+ const filePath = resolve(input.path);
4619
4541
  try {
4620
4542
  if (!fs16.existsSync(filePath)) {
4621
4543
  return `Error: ${filePath} does not exist`;
@@ -4628,6 +4550,53 @@ ${unifiedDiff(filePath, oldContent, "")}`;
4628
4550
  return `Error deleting ${filePath}: ${err.message}`;
4629
4551
  }
4630
4552
  }
4553
+ case "writePitchDeck": {
4554
+ if (!context) {
4555
+ return "Error: writePitchDeck requires execution context for design expert delegation";
4556
+ }
4557
+ const filePath = resolve("pitch.html");
4558
+ try {
4559
+ fs16.mkdirSync(ROADMAP_DIR, { recursive: true });
4560
+ const existingHtml = fs16.existsSync(filePath) ? fs16.readFileSync(filePath, "utf-8").trim() : "";
4561
+ let task = `
4562
+ <pitch_content>${input.task}</pitch_content>
4563
+
4564
+ We are building the pitch deck for the app. Using the provided <pitch_content>, as well as the app's spec data, think about what would make a compelling, interactive, self-contained horizontally-scrolling HTML slide deck for this product. Keep it simple, clean, powerful. Giant text, large logo, big, bold stats and claims. Edit the content as necessary to create the most impactful, bold, and beautiful slides. This should not feel text heavy, and it should not feel like a landing page - it should feel like a modern interactive presentation that leaves the user wowed by the product.
4565
+
4566
+ ### Rules
4567
+ - The deck must be a single HTML file - it will be rendered in an iFrame.
4568
+ - Keep it simple: 100svh 100vw slides that scroll horizontally using browser scrolling. Must look beautiful on desktop and mobile - use all the principles you know about designing effective landing pages to make a beautiful deck.
4569
+ - The deck must support keyboard navigation left and right, and big clickable arrows to transition.
4570
+ - Animation between slides must be seamless, no flicker or flashing.
4571
+ - Be bold and impactful. Do not be wordy or verbose, no one reads decks with too many words.
4572
+ - Keep it simple: 5-7 slides max. No fluff, just imapct.
4573
+ - Code must be clean, bug free, and easy-to-parse. Use GSAP for animations.
4574
+ `;
4575
+ if (existingHtml) {
4576
+ task += `
4577
+
4578
+ The current pitch deck HTML is below. Refine and update it rather than starting from scratch \u2014 preserve the existing design and structure where it still works, and update the content and slides to reflect the new pitch.
4579
+
4580
+ <existing_pitch_html>${existingHtml}</existing_pitch_html>`;
4581
+ }
4582
+ task += `
4583
+
4584
+ Respond only with the HTML of the deck as a single fenced markdown block and absolutely no other text - your response will be written directly to a file.`;
4585
+ const result = await designExpertTool.execute({ task }, context);
4586
+ const htmlMatch = result.match(
4587
+ /```(?:html|wireframe)\n([\s\S]*?)```/
4588
+ );
4589
+ const html = htmlMatch ? htmlMatch[1].trim() : result;
4590
+ const oldContent = fs16.existsSync(filePath) ? fs16.readFileSync(filePath, "utf-8") : "";
4591
+ fs16.writeFileSync(filePath, html, "utf-8");
4592
+ const lineCount = html.split("\n").length;
4593
+ const label = oldContent ? "Wrote" : "Created";
4594
+ return `${label} ${filePath} (${lineCount} lines)
4595
+ ${unifiedDiff(filePath, oldContent, html)}`;
4596
+ } catch (err) {
4597
+ return `Error generating pitch deck: ${err.message}`;
4598
+ }
4599
+ }
4631
4600
  default:
4632
4601
  return `Error: unknown tool "${name}"`;
4633
4602
  }
@@ -4681,7 +4650,11 @@ var productVisionTool = {
4681
4650
  history: history.length > 0 ? history : void 0,
4682
4651
  tools: VISION_TOOLS,
4683
4652
  externalTools: /* @__PURE__ */ new Set(),
4684
- executeTool: executeVisionTool,
4653
+ executeTool: (name, input2, toolCallId) => executeVisionTool(
4654
+ name,
4655
+ input2,
4656
+ toolCallId ? deriveContext(context, toolCallId) : context
4657
+ ),
4685
4658
  apiConfig: context.apiConfig,
4686
4659
  model: context.model,
4687
4660
  subAgentId: "productVision",
@@ -4883,6 +4856,9 @@ var scrapeWebUrlTool = {
4883
4856
  };
4884
4857
 
4885
4858
  // src/tools/index.ts
4859
+ function deriveContext(parent, toolCallId) {
4860
+ return { ...parent, toolCallId };
4861
+ }
4886
4862
  var ALL_TOOLS = [
4887
4863
  // Common
4888
4864
  setProjectOnboardingStateTool,
@@ -5820,9 +5796,6 @@ function emit(event, data, requestId) {
5820
5796
  }
5821
5797
  process.stdout.write(JSON.stringify(payload) + "\n");
5822
5798
  }
5823
- function handleGetHistory(state) {
5824
- return { messages: state.messages };
5825
- }
5826
5799
  function handleClear(state) {
5827
5800
  clearSession(state);
5828
5801
  return {};
@@ -5967,17 +5940,17 @@ ${xmlParts}
5967
5940
  return Promise.resolve(early);
5968
5941
  }
5969
5942
  const shouldTimeout = !USER_FACING_TOOLS.has(name);
5970
- return new Promise((resolve) => {
5943
+ return new Promise((resolve2) => {
5971
5944
  const timeout = shouldTimeout ? setTimeout(() => {
5972
5945
  pendingTools.delete(id);
5973
- resolve(
5946
+ resolve2(
5974
5947
  "Error: Tool timed out \u2014 no response from the app environment after 5 minutes."
5975
5948
  );
5976
5949
  }, EXTERNAL_TOOL_TIMEOUT_MS) : void 0;
5977
5950
  pendingTools.set(id, {
5978
5951
  resolve: (result) => {
5979
5952
  clearTimeout(timeout);
5980
- resolve(result);
5953
+ resolve2(result);
5981
5954
  },
5982
5955
  timeout
5983
5956
  });
@@ -6250,7 +6223,11 @@ ${xmlParts}
6250
6223
  }
6251
6224
  if (action === "get_history") {
6252
6225
  applyPendingBlockUpdates();
6253
- dispatchSimple(requestId, "history", () => handleGetHistory(state));
6226
+ dispatchSimple(requestId, "history", () => ({
6227
+ messages: state.messages,
6228
+ running,
6229
+ ...running && currentRequestId ? { currentRequestId } : {}
6230
+ }));
6254
6231
  return;
6255
6232
  }
6256
6233
  if (action === "clear") {