@mindstudio-ai/remy 0.1.300 → 0.1.301

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 CHANGED
@@ -2757,8 +2757,8 @@ function formatSize(bytes) {
2757
2757
  }
2758
2758
  async function formatFile(dirPath, name, indent) {
2759
2759
  try {
2760
- const stat3 = await fs16.stat(path8.join(dirPath, name));
2761
- return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat3.size)}`;
2760
+ const stat4 = await fs16.stat(path8.join(dirPath, name));
2761
+ return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat4.size)}`;
2762
2762
  } catch {
2763
2763
  return `${indent}${name}`;
2764
2764
  }
@@ -3787,6 +3787,7 @@ async function runSubAgent(config) {
3787
3787
  acquireLock,
3788
3788
  onBackgroundComplete,
3789
3789
  captureArtifacts,
3790
+ validateResult,
3790
3791
  cachePolicy = "run"
3791
3792
  } = config;
3792
3793
  const artifacts = {};
@@ -3807,6 +3808,7 @@ async function runSubAgent(config) {
3807
3808
 
3808
3809
  Current date: ${dateStr}`;
3809
3810
  let turns = 0;
3811
+ let validationRetried = false;
3810
3812
  const run = async () => {
3811
3813
  const historyLen = (history ?? []).length;
3812
3814
  const subAgentMessages = /* @__PURE__ */ new Map();
@@ -4010,8 +4012,41 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4010
4012
  (b) => b.type === "tool"
4011
4013
  );
4012
4014
  if (stopReason !== "tool_use" || toolCalls.length === 0) {
4015
+ let text = getPartialText(contentBlocks);
4016
+ if (validateResult) {
4017
+ let objection = null;
4018
+ try {
4019
+ objection = await validateResult(text, thisInvocation());
4020
+ } catch (err) {
4021
+ log7.warn("Result validator failed, accepting response", {
4022
+ requestId,
4023
+ parentToolId,
4024
+ agentName,
4025
+ error: err.message
4026
+ });
4027
+ }
4028
+ if (objection && !validationRetried && !signal?.aborted) {
4029
+ validationRetried = true;
4030
+ log7.info("Result rejected by validator, retrying once", {
4031
+ requestId,
4032
+ parentToolId,
4033
+ agentName,
4034
+ objection: objection.slice(0, 200)
4035
+ });
4036
+ emit({
4037
+ type: "status",
4038
+ message: "Response failed validation, revising"
4039
+ });
4040
+ messages.push({ role: "user", content: objection });
4041
+ continue;
4042
+ }
4043
+ if (objection) {
4044
+ text = `${text}
4045
+
4046
+ [Response validator: ${objection}]`;
4047
+ }
4048
+ }
4013
4049
  statusWatcher.stop();
4014
- const text = getPartialText(contentBlocks);
4015
4050
  const hasArtifacts = Object.keys(artifacts).length > 0;
4016
4051
  return {
4017
4052
  text,
@@ -6055,6 +6090,38 @@ ${RENDER_TASK_BLOCK}`;
6055
6090
  return prompt;
6056
6091
  }
6057
6092
 
6093
+ // src/subagents/designExpert/validateWireframeRefs.ts
6094
+ import { stat as stat3 } from "fs/promises";
6095
+ import { join as join3 } from "path";
6096
+ var WIREFRAME_REF_RE = /src\/\.wireframes\/[a-z0-9][a-z0-9-]*\.html/g;
6097
+ async function validateWireframeRefs(text) {
6098
+ const refs = [...new Set(text.match(WIREFRAME_REF_RE) ?? [])];
6099
+ if (refs.length === 0) {
6100
+ return null;
6101
+ }
6102
+ const missing = [];
6103
+ for (const ref of refs) {
6104
+ const exists = await stat3(join3(PROJECT_ROOT, ref)).then(
6105
+ (s) => s.isFile(),
6106
+ () => false
6107
+ );
6108
+ if (!exists) {
6109
+ missing.push(ref);
6110
+ }
6111
+ }
6112
+ if (missing.length === 0) {
6113
+ return null;
6114
+ }
6115
+ return [
6116
+ `Your response references wireframe files that do not exist on disk:`,
6117
+ ...missing.map((p) => `- ${p}`),
6118
+ ``,
6119
+ `A wireframe reference is only valid as a receipt handed back by a createWireframe result \u2014 that tool is the only way a wireframe comes to exist. A reference composed in prose points at nothing and renders as a dead preview.`,
6120
+ ``,
6121
+ `For each missing path, either author that wireframe now with createWireframe (use the matching slug so the path is identical) or remove the reference. Then send your complete final response again from the top \u2014 it fully replaces your previous response, so include everything, not just the fixes.`
6122
+ ].join("\n");
6123
+ }
6124
+
6058
6125
  // src/subagents/common/history.ts
6059
6126
  function getSubAgentHistory(messages, subAgentName) {
6060
6127
  let checkpointIdx = -1;
@@ -6143,6 +6210,10 @@ async function runDesignExpert(opts, context) {
6143
6210
  onEvent: context.onEvent,
6144
6211
  resolveExternalTool: context.resolveExternalTool,
6145
6212
  toolRegistry: context.toolRegistry,
6213
+ // Receipt guard: every wireframe reference in the final response must
6214
+ // resolve to a file on disk (see validateWireframeRefs.ts). Covers the
6215
+ // advisor tool, render mode, and both render callers.
6216
+ validateResult: (text) => validateWireframeRefs(text),
6146
6217
  background: opts.background,
6147
6218
  onBackgroundComplete: opts.background ? (bgResult) => {
6148
6219
  context.onBackgroundComplete?.(
@@ -9083,7 +9154,7 @@ async function runTurn(params) {
9083
9154
  // src/headless/attachments.ts
9084
9155
  import { mkdirSync, existsSync } from "fs";
9085
9156
  import { writeFile as writeFile3 } from "fs/promises";
9086
- import { basename as basename2, join as join3, extname as extname2 } from "path";
9157
+ import { basename as basename2, join as join4, extname as extname2 } from "path";
9087
9158
  var log16 = createLogger("headless:attachments");
9088
9159
  var UPLOADS_DIR = "src/.user-uploads";
9089
9160
  function filenameFromUrl(url) {
@@ -9096,7 +9167,7 @@ function filenameFromUrl(url) {
9096
9167
  }
9097
9168
  }
9098
9169
  function resolveUniqueFilename(name, claimed) {
9099
- const isFree = (candidate) => !claimed.has(candidate) && !existsSync(join3(UPLOADS_DIR, candidate));
9170
+ const isFree = (candidate) => !claimed.has(candidate) && !existsSync(join4(UPLOADS_DIR, candidate));
9100
9171
  if (isFree(name)) {
9101
9172
  return name;
9102
9173
  }
@@ -9131,7 +9202,7 @@ async function persistAttachments(attachments) {
9131
9202
  const results = await Promise.allSettled(
9132
9203
  nonVoice.map(async (att, i) => {
9133
9204
  const name = names[i];
9134
- const localPath = join3(UPLOADS_DIR, name);
9205
+ const localPath = join4(UPLOADS_DIR, name);
9135
9206
  const res = await fetch(att.url, {
9136
9207
  signal: AbortSignal.timeout(3e4)
9137
9208
  });
package/dist/index.js CHANGED
@@ -3905,8 +3905,8 @@ function formatSize(bytes) {
3905
3905
  }
3906
3906
  async function formatFile(dirPath, name, indent) {
3907
3907
  try {
3908
- const stat3 = await fs15.stat(path8.join(dirPath, name));
3909
- return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat3.size)}`;
3908
+ const stat4 = await fs15.stat(path8.join(dirPath, name));
3909
+ return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat4.size)}`;
3910
3910
  } catch {
3911
3911
  return `${indent}${name}`;
3912
3912
  }
@@ -4822,6 +4822,7 @@ async function runSubAgent(config) {
4822
4822
  acquireLock,
4823
4823
  onBackgroundComplete,
4824
4824
  captureArtifacts,
4825
+ validateResult,
4825
4826
  cachePolicy = "run"
4826
4827
  } = config;
4827
4828
  const artifacts = {};
@@ -4842,6 +4843,7 @@ async function runSubAgent(config) {
4842
4843
 
4843
4844
  Current date: ${dateStr}`;
4844
4845
  let turns = 0;
4846
+ let validationRetried = false;
4845
4847
  const run = async () => {
4846
4848
  const historyLen = (history ?? []).length;
4847
4849
  const subAgentMessages = /* @__PURE__ */ new Map();
@@ -5045,8 +5047,41 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
5045
5047
  (b) => b.type === "tool"
5046
5048
  );
5047
5049
  if (stopReason !== "tool_use" || toolCalls.length === 0) {
5050
+ let text = getPartialText(contentBlocks);
5051
+ if (validateResult) {
5052
+ let objection = null;
5053
+ try {
5054
+ objection = await validateResult(text, thisInvocation());
5055
+ } catch (err) {
5056
+ log8.warn("Result validator failed, accepting response", {
5057
+ requestId,
5058
+ parentToolId,
5059
+ agentName,
5060
+ error: err.message
5061
+ });
5062
+ }
5063
+ if (objection && !validationRetried && !signal?.aborted) {
5064
+ validationRetried = true;
5065
+ log8.info("Result rejected by validator, retrying once", {
5066
+ requestId,
5067
+ parentToolId,
5068
+ agentName,
5069
+ objection: objection.slice(0, 200)
5070
+ });
5071
+ emit({
5072
+ type: "status",
5073
+ message: "Response failed validation, revising"
5074
+ });
5075
+ messages.push({ role: "user", content: objection });
5076
+ continue;
5077
+ }
5078
+ if (objection) {
5079
+ text = `${text}
5080
+
5081
+ [Response validator: ${objection}]`;
5082
+ }
5083
+ }
5048
5084
  statusWatcher.stop();
5049
- const text = getPartialText(contentBlocks);
5050
5085
  const hasArtifacts = Object.keys(artifacts).length > 0;
5051
5086
  return {
5052
5087
  text,
@@ -7413,6 +7448,45 @@ The guidance about specifying layouts in prose, writing implementation notes, an
7413
7448
  }
7414
7449
  });
7415
7450
 
7451
+ // src/subagents/designExpert/validateWireframeRefs.ts
7452
+ import { stat as stat3 } from "fs/promises";
7453
+ import { join as join3 } from "path";
7454
+ async function validateWireframeRefs(text) {
7455
+ const refs = [...new Set(text.match(WIREFRAME_REF_RE) ?? [])];
7456
+ if (refs.length === 0) {
7457
+ return null;
7458
+ }
7459
+ const missing = [];
7460
+ for (const ref of refs) {
7461
+ const exists = await stat3(join3(PROJECT_ROOT, ref)).then(
7462
+ (s) => s.isFile(),
7463
+ () => false
7464
+ );
7465
+ if (!exists) {
7466
+ missing.push(ref);
7467
+ }
7468
+ }
7469
+ if (missing.length === 0) {
7470
+ return null;
7471
+ }
7472
+ return [
7473
+ `Your response references wireframe files that do not exist on disk:`,
7474
+ ...missing.map((p) => `- ${p}`),
7475
+ ``,
7476
+ `A wireframe reference is only valid as a receipt handed back by a createWireframe result \u2014 that tool is the only way a wireframe comes to exist. A reference composed in prose points at nothing and renders as a dead preview.`,
7477
+ ``,
7478
+ `For each missing path, either author that wireframe now with createWireframe (use the matching slug so the path is identical) or remove the reference. Then send your complete final response again from the top \u2014 it fully replaces your previous response, so include everything, not just the fixes.`
7479
+ ].join("\n");
7480
+ }
7481
+ var WIREFRAME_REF_RE;
7482
+ var init_validateWireframeRefs = __esm({
7483
+ "src/subagents/designExpert/validateWireframeRefs.ts"() {
7484
+ "use strict";
7485
+ init_projectRoot();
7486
+ WIREFRAME_REF_RE = /src\/\.wireframes\/[a-z0-9][a-z0-9-]*\.html/g;
7487
+ }
7488
+ });
7489
+
7416
7490
  // src/subagents/common/history.ts
7417
7491
  function getSubAgentHistory(messages, subAgentName) {
7418
7492
  let checkpointIdx = -1;
@@ -7497,6 +7571,10 @@ async function runDesignExpert(opts, context) {
7497
7571
  onEvent: context.onEvent,
7498
7572
  resolveExternalTool: context.resolveExternalTool,
7499
7573
  toolRegistry: context.toolRegistry,
7574
+ // Receipt guard: every wireframe reference in the final response must
7575
+ // resolve to a file on disk (see validateWireframeRefs.ts). Covers the
7576
+ // advisor tool, render mode, and both render callers.
7577
+ validateResult: (text) => validateWireframeRefs(text),
7500
7578
  background: opts.background,
7501
7579
  onBackgroundComplete: opts.background ? (bgResult) => {
7502
7580
  context.onBackgroundComplete?.(
@@ -7520,6 +7598,7 @@ var init_designExpert = __esm({
7520
7598
  init_tools4();
7521
7599
  init_tools();
7522
7600
  init_prompt2();
7601
+ init_validateWireframeRefs();
7523
7602
  init_history();
7524
7603
  init_surfaces();
7525
7604
  init_writeFile();
@@ -10011,7 +10090,7 @@ var init_config = __esm({
10011
10090
  // src/headless/attachments.ts
10012
10091
  import { mkdirSync, existsSync } from "fs";
10013
10092
  import { writeFile as writeFile3 } from "fs/promises";
10014
- import { basename as basename2, join as join3, extname as extname2 } from "path";
10093
+ import { basename as basename2, join as join4, extname as extname2 } from "path";
10015
10094
  function filenameFromUrl(url) {
10016
10095
  try {
10017
10096
  const pathname = new URL(url).pathname;
@@ -10022,7 +10101,7 @@ function filenameFromUrl(url) {
10022
10101
  }
10023
10102
  }
10024
10103
  function resolveUniqueFilename(name, claimed) {
10025
- const isFree = (candidate) => !claimed.has(candidate) && !existsSync(join3(UPLOADS_DIR, candidate));
10104
+ const isFree = (candidate) => !claimed.has(candidate) && !existsSync(join4(UPLOADS_DIR, candidate));
10026
10105
  if (isFree(name)) {
10027
10106
  return name;
10028
10107
  }
@@ -10056,7 +10135,7 @@ async function persistAttachments(attachments) {
10056
10135
  const results = await Promise.allSettled(
10057
10136
  nonVoice.map(async (att, i) => {
10058
10137
  const name = names[i];
10059
- const localPath = join3(UPLOADS_DIR, name);
10138
+ const localPath = join4(UPLOADS_DIR, name);
10060
10139
  const res = await fetch(att.url, {
10061
10140
  signal: AbortSignal.timeout(3e4)
10062
10141
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/remy",
3
- "version": "0.1.300",
3
+ "version": "0.1.301",
4
4
  "description": "Remy coding agent",
5
5
  "repository": {
6
6
  "type": "git",