@mindstudio-ai/remy 0.1.299 → 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 +119 -23
- package/dist/index.js +127 -23
- package/dist/subagents/codeSanityCheck/prompt.md +2 -1
- package/package.json +1 -1
package/dist/headless.js
CHANGED
|
@@ -942,9 +942,9 @@ function buildBackgroundResultsMessage(results) {
|
|
|
942
942
|
${r.result}
|
|
943
943
|
</tool_result>`
|
|
944
944
|
).join("\n\n");
|
|
945
|
-
const
|
|
946
|
-
|
|
947
|
-
|
|
945
|
+
const body = `<background_results>
|
|
946
|
+
Automated delivery of results from background tool calls that completed while other work was in progress. This block is not from the user \u2014 anything outside it is.
|
|
947
|
+
|
|
948
948
|
${xml}
|
|
949
949
|
</background_results>`;
|
|
950
950
|
return automatedMessage("background_results", body);
|
|
@@ -1848,7 +1848,7 @@ var askMindStudioSdkTool = {
|
|
|
1848
1848
|
async execute(input, context) {
|
|
1849
1849
|
const query = input.query;
|
|
1850
1850
|
const result = await runCli("mindstudio", ["ask", query], {
|
|
1851
|
-
timeout:
|
|
1851
|
+
timeout: 48e4,
|
|
1852
1852
|
maxBuffer: 512 * 1024,
|
|
1853
1853
|
onLog: context?.onLog
|
|
1854
1854
|
});
|
|
@@ -2757,8 +2757,8 @@ function formatSize(bytes) {
|
|
|
2757
2757
|
}
|
|
2758
2758
|
async function formatFile(dirPath, name, indent) {
|
|
2759
2759
|
try {
|
|
2760
|
-
const
|
|
2761
|
-
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(
|
|
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,
|
|
@@ -4570,7 +4605,7 @@ async function runBrowserAutomation(task, context, opts) {
|
|
|
4570
4605
|
task,
|
|
4571
4606
|
tools: BROWSER_TOOLS,
|
|
4572
4607
|
externalTools: BROWSER_EXTERNAL_TOOLS,
|
|
4573
|
-
executeTool: async (name, _input,
|
|
4608
|
+
executeTool: async (name, _input, toolCallId, onLog) => {
|
|
4574
4609
|
if (name === "setupBrowser") {
|
|
4575
4610
|
try {
|
|
4576
4611
|
const result2 = await sidecarRequest(
|
|
@@ -4587,7 +4622,11 @@ async function runBrowserAutomation(task, context, opts) {
|
|
|
4587
4622
|
}
|
|
4588
4623
|
}
|
|
4589
4624
|
if (COMMON_READ_TOOL_NAMES.has(name) || name === readSpecTool.definition.name) {
|
|
4590
|
-
return executeTool(
|
|
4625
|
+
return executeTool(
|
|
4626
|
+
name,
|
|
4627
|
+
_input,
|
|
4628
|
+
toolCallId ? deriveContext(context, toolCallId, onLog) : context
|
|
4629
|
+
);
|
|
4591
4630
|
}
|
|
4592
4631
|
return `Error: unknown local tool "${name}"`;
|
|
4593
4632
|
},
|
|
@@ -5556,7 +5595,13 @@ var copyEditorTool = {
|
|
|
5556
5595
|
task: input.task,
|
|
5557
5596
|
tools: COPY_EDITOR_TOOLS,
|
|
5558
5597
|
externalTools: /* @__PURE__ */ new Set(),
|
|
5559
|
-
executeTool: (name, toolInput
|
|
5598
|
+
executeTool: (name, toolInput, toolCallId, onLog, sams) => {
|
|
5599
|
+
const childCtx = toolCallId ? {
|
|
5600
|
+
...deriveContext(context, toolCallId, onLog),
|
|
5601
|
+
subAgentMessages: sams
|
|
5602
|
+
} : context;
|
|
5603
|
+
return executeTool(name, toolInput, childCtx);
|
|
5604
|
+
},
|
|
5560
5605
|
apiConfig: context.apiConfig,
|
|
5561
5606
|
model: resolveModel("copyEditor", context.models, context.model),
|
|
5562
5607
|
subAgentId: "copyEditor",
|
|
@@ -5824,7 +5869,7 @@ async function executeDesignExpertTool(name, input, context, toolCallId, onLog)
|
|
|
5824
5869
|
if (!tool) {
|
|
5825
5870
|
return `Error: unknown tool "${name}"`;
|
|
5826
5871
|
}
|
|
5827
|
-
const childContext = context && toolCallId ? deriveContext(context, toolCallId) : context;
|
|
5872
|
+
const childContext = context && toolCallId ? deriveContext(context, toolCallId, onLog) : context;
|
|
5828
5873
|
return tool.execute(input, onLog, childContext);
|
|
5829
5874
|
}
|
|
5830
5875
|
|
|
@@ -6045,6 +6090,38 @@ ${RENDER_TASK_BLOCK}`;
|
|
|
6045
6090
|
return prompt;
|
|
6046
6091
|
}
|
|
6047
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
|
+
|
|
6048
6125
|
// src/subagents/common/history.ts
|
|
6049
6126
|
function getSubAgentHistory(messages, subAgentName) {
|
|
6050
6127
|
let checkpointIdx = -1;
|
|
@@ -6112,7 +6189,10 @@ async function runDesignExpert(opts, context) {
|
|
|
6112
6189
|
tools: opts.render ? DESIGN_EXPERT_RENDER_TOOLS : DESIGN_EXPERT_TOOLS,
|
|
6113
6190
|
externalTools: /* @__PURE__ */ new Set(),
|
|
6114
6191
|
executeTool: (name, input, toolCallId, onLog, sams) => {
|
|
6115
|
-
const childCtx = toolCallId ? {
|
|
6192
|
+
const childCtx = toolCallId ? {
|
|
6193
|
+
...deriveContext(context, toolCallId, onLog),
|
|
6194
|
+
subAgentMessages: sams
|
|
6195
|
+
} : { ...context, subAgentMessages: sams };
|
|
6116
6196
|
if (COMMON_READ_TOOL_NAMES.has(name)) {
|
|
6117
6197
|
return executeTool(name, input, childCtx);
|
|
6118
6198
|
}
|
|
@@ -6130,6 +6210,10 @@ async function runDesignExpert(opts, context) {
|
|
|
6130
6210
|
onEvent: context.onEvent,
|
|
6131
6211
|
resolveExternalTool: context.resolveExternalTool,
|
|
6132
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),
|
|
6133
6217
|
background: opts.background,
|
|
6134
6218
|
onBackgroundComplete: opts.background ? (bgResult) => {
|
|
6135
6219
|
context.onBackgroundComplete?.(
|
|
@@ -6380,8 +6464,11 @@ var productVisionTool = {
|
|
|
6380
6464
|
cachePolicy: "conversation",
|
|
6381
6465
|
tools: VISION_TOOLS,
|
|
6382
6466
|
externalTools: /* @__PURE__ */ new Set(),
|
|
6383
|
-
executeTool: (name, input2, toolCallId,
|
|
6384
|
-
const childCtx = toolCallId ? {
|
|
6467
|
+
executeTool: (name, input2, toolCallId, onLog, sams) => {
|
|
6468
|
+
const childCtx = toolCallId ? {
|
|
6469
|
+
...deriveContext(context, toolCallId, onLog),
|
|
6470
|
+
subAgentMessages: sams
|
|
6471
|
+
} : { ...context, subAgentMessages: sams };
|
|
6385
6472
|
if (COMMON_READ_TOOL_NAMES.has(name)) {
|
|
6386
6473
|
return executeTool(name, input2, childCtx);
|
|
6387
6474
|
}
|
|
@@ -6493,7 +6580,13 @@ var codeSanityCheckTool = {
|
|
|
6493
6580
|
task: input.task,
|
|
6494
6581
|
tools: SANITY_CHECK_TOOLS,
|
|
6495
6582
|
externalTools: /* @__PURE__ */ new Set(),
|
|
6496
|
-
executeTool: (name, toolInput
|
|
6583
|
+
executeTool: (name, toolInput, toolCallId, onLog, sams) => {
|
|
6584
|
+
const childCtx = toolCallId ? {
|
|
6585
|
+
...deriveContext(context, toolCallId, onLog),
|
|
6586
|
+
subAgentMessages: sams
|
|
6587
|
+
} : context;
|
|
6588
|
+
return executeTool(name, toolInput, childCtx);
|
|
6589
|
+
},
|
|
6497
6590
|
apiConfig: context.apiConfig,
|
|
6498
6591
|
model: resolveModel("codeSanityCheck", context.models, context.model),
|
|
6499
6592
|
subAgentId: "codeSanityCheck",
|
|
@@ -6703,16 +6796,19 @@ After reconciling the spec, refresh the Build Overview: author the complete upda
|
|
|
6703
6796
|
task,
|
|
6704
6797
|
tools: tools2,
|
|
6705
6798
|
externalTools: /* @__PURE__ */ new Set(),
|
|
6706
|
-
executeTool: (name, toolInput, toolCallId,
|
|
6799
|
+
executeTool: (name, toolInput, toolCallId, onLog, sams) => {
|
|
6800
|
+
const childCtx = toolCallId ? {
|
|
6801
|
+
...deriveContext(context, toolCallId, onLog),
|
|
6802
|
+
subAgentMessages: sams
|
|
6803
|
+
} : { ...context, subAgentMessages: sams };
|
|
6707
6804
|
if (name === "writeBuildOverview") {
|
|
6708
|
-
const childCtx = toolCallId ? { ...deriveContext(context, toolCallId), subAgentMessages: sams } : { ...context, subAgentMessages: sams };
|
|
6709
6805
|
return renderBuildOverview(
|
|
6710
6806
|
String(toolInput.content ?? "").trim(),
|
|
6711
6807
|
childCtx,
|
|
6712
6808
|
{ background: false }
|
|
6713
6809
|
);
|
|
6714
6810
|
}
|
|
6715
|
-
return executeTool(name, toolInput,
|
|
6811
|
+
return executeTool(name, toolInput, childCtx);
|
|
6716
6812
|
},
|
|
6717
6813
|
apiConfig: context.apiConfig,
|
|
6718
6814
|
model: resolveModel("specSync", context.models, context.model),
|
|
@@ -6786,8 +6882,8 @@ var scrapeWebUrlTool = {
|
|
|
6786
6882
|
};
|
|
6787
6883
|
|
|
6788
6884
|
// src/tools/index.ts
|
|
6789
|
-
function deriveContext(parent, toolCallId) {
|
|
6790
|
-
return { ...parent, toolCallId };
|
|
6885
|
+
function deriveContext(parent, toolCallId, onLog) {
|
|
6886
|
+
return { ...parent, toolCallId, onLog };
|
|
6791
6887
|
}
|
|
6792
6888
|
var ALL_TOOLS = [
|
|
6793
6889
|
// Common
|
|
@@ -9058,7 +9154,7 @@ async function runTurn(params) {
|
|
|
9058
9154
|
// src/headless/attachments.ts
|
|
9059
9155
|
import { mkdirSync, existsSync } from "fs";
|
|
9060
9156
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
9061
|
-
import { basename as basename2, join as
|
|
9157
|
+
import { basename as basename2, join as join4, extname as extname2 } from "path";
|
|
9062
9158
|
var log16 = createLogger("headless:attachments");
|
|
9063
9159
|
var UPLOADS_DIR = "src/.user-uploads";
|
|
9064
9160
|
function filenameFromUrl(url) {
|
|
@@ -9071,7 +9167,7 @@ function filenameFromUrl(url) {
|
|
|
9071
9167
|
}
|
|
9072
9168
|
}
|
|
9073
9169
|
function resolveUniqueFilename(name, claimed) {
|
|
9074
|
-
const isFree = (candidate) => !claimed.has(candidate) && !existsSync(
|
|
9170
|
+
const isFree = (candidate) => !claimed.has(candidate) && !existsSync(join4(UPLOADS_DIR, candidate));
|
|
9075
9171
|
if (isFree(name)) {
|
|
9076
9172
|
return name;
|
|
9077
9173
|
}
|
|
@@ -9106,7 +9202,7 @@ async function persistAttachments(attachments) {
|
|
|
9106
9202
|
const results = await Promise.allSettled(
|
|
9107
9203
|
nonVoice.map(async (att, i) => {
|
|
9108
9204
|
const name = names[i];
|
|
9109
|
-
const localPath =
|
|
9205
|
+
const localPath = join4(UPLOADS_DIR, name);
|
|
9110
9206
|
const res = await fetch(att.url, {
|
|
9111
9207
|
signal: AbortSignal.timeout(3e4)
|
|
9112
9208
|
});
|
package/dist/index.js
CHANGED
|
@@ -1379,7 +1379,7 @@ var init_sdkConsultant = __esm({
|
|
|
1379
1379
|
async execute(input, context) {
|
|
1380
1380
|
const query = input.query;
|
|
1381
1381
|
const result = await runCli("mindstudio", ["ask", query], {
|
|
1382
|
-
timeout:
|
|
1382
|
+
timeout: 48e4,
|
|
1383
1383
|
maxBuffer: 512 * 1024,
|
|
1384
1384
|
onLog: context?.onLog
|
|
1385
1385
|
});
|
|
@@ -1660,9 +1660,9 @@ function buildBackgroundResultsMessage(results) {
|
|
|
1660
1660
|
${r.result}
|
|
1661
1661
|
</tool_result>`
|
|
1662
1662
|
).join("\n\n");
|
|
1663
|
-
const
|
|
1664
|
-
|
|
1665
|
-
|
|
1663
|
+
const body = `<background_results>
|
|
1664
|
+
Automated delivery of results from background tool calls that completed while other work was in progress. This block is not from the user \u2014 anything outside it is.
|
|
1665
|
+
|
|
1666
1666
|
${xml}
|
|
1667
1667
|
</background_results>`;
|
|
1668
1668
|
return automatedMessage("background_results", body);
|
|
@@ -3905,8 +3905,8 @@ function formatSize(bytes) {
|
|
|
3905
3905
|
}
|
|
3906
3906
|
async function formatFile(dirPath, name, indent) {
|
|
3907
3907
|
try {
|
|
3908
|
-
const
|
|
3909
|
-
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(
|
|
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,
|
|
@@ -5648,7 +5683,7 @@ async function runBrowserAutomation(task, context, opts) {
|
|
|
5648
5683
|
task,
|
|
5649
5684
|
tools: BROWSER_TOOLS,
|
|
5650
5685
|
externalTools: BROWSER_EXTERNAL_TOOLS,
|
|
5651
|
-
executeTool: async (name, _input,
|
|
5686
|
+
executeTool: async (name, _input, toolCallId, onLog) => {
|
|
5652
5687
|
if (name === "setupBrowser") {
|
|
5653
5688
|
try {
|
|
5654
5689
|
const result2 = await sidecarRequest(
|
|
@@ -5665,7 +5700,11 @@ async function runBrowserAutomation(task, context, opts) {
|
|
|
5665
5700
|
}
|
|
5666
5701
|
}
|
|
5667
5702
|
if (COMMON_READ_TOOL_NAMES.has(name) || name === readSpecTool.definition.name) {
|
|
5668
|
-
return executeTool(
|
|
5703
|
+
return executeTool(
|
|
5704
|
+
name,
|
|
5705
|
+
_input,
|
|
5706
|
+
toolCallId ? deriveContext(context, toolCallId, onLog) : context
|
|
5707
|
+
);
|
|
5669
5708
|
}
|
|
5670
5709
|
return `Error: unknown local tool "${name}"`;
|
|
5671
5710
|
},
|
|
@@ -6752,7 +6791,13 @@ var init_copyEditor = __esm({
|
|
|
6752
6791
|
task: input.task,
|
|
6753
6792
|
tools: COPY_EDITOR_TOOLS,
|
|
6754
6793
|
externalTools: /* @__PURE__ */ new Set(),
|
|
6755
|
-
executeTool: (name, toolInput
|
|
6794
|
+
executeTool: (name, toolInput, toolCallId, onLog, sams) => {
|
|
6795
|
+
const childCtx = toolCallId ? {
|
|
6796
|
+
...deriveContext(context, toolCallId, onLog),
|
|
6797
|
+
subAgentMessages: sams
|
|
6798
|
+
} : context;
|
|
6799
|
+
return executeTool(name, toolInput, childCtx);
|
|
6800
|
+
},
|
|
6756
6801
|
apiConfig: context.apiConfig,
|
|
6757
6802
|
model: resolveModel("copyEditor", context.models, context.model),
|
|
6758
6803
|
subAgentId: "copyEditor",
|
|
@@ -7034,7 +7079,7 @@ async function executeDesignExpertTool(name, input, context, toolCallId, onLog)
|
|
|
7034
7079
|
if (!tool) {
|
|
7035
7080
|
return `Error: unknown tool "${name}"`;
|
|
7036
7081
|
}
|
|
7037
|
-
const childContext = context && toolCallId ? deriveContext(context, toolCallId) : context;
|
|
7082
|
+
const childContext = context && toolCallId ? deriveContext(context, toolCallId, onLog) : context;
|
|
7038
7083
|
return tool.execute(input, onLog, childContext);
|
|
7039
7084
|
}
|
|
7040
7085
|
var tools, DESIGN_EXPERT_TOOLS;
|
|
@@ -7403,6 +7448,45 @@ The guidance about specifying layouts in prose, writing implementation notes, an
|
|
|
7403
7448
|
}
|
|
7404
7449
|
});
|
|
7405
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
|
+
|
|
7406
7490
|
// src/subagents/common/history.ts
|
|
7407
7491
|
function getSubAgentHistory(messages, subAgentName) {
|
|
7408
7492
|
let checkpointIdx = -1;
|
|
@@ -7466,7 +7550,10 @@ async function runDesignExpert(opts, context) {
|
|
|
7466
7550
|
tools: opts.render ? DESIGN_EXPERT_RENDER_TOOLS : DESIGN_EXPERT_TOOLS,
|
|
7467
7551
|
externalTools: /* @__PURE__ */ new Set(),
|
|
7468
7552
|
executeTool: (name, input, toolCallId, onLog, sams) => {
|
|
7469
|
-
const childCtx = toolCallId ? {
|
|
7553
|
+
const childCtx = toolCallId ? {
|
|
7554
|
+
...deriveContext(context, toolCallId, onLog),
|
|
7555
|
+
subAgentMessages: sams
|
|
7556
|
+
} : { ...context, subAgentMessages: sams };
|
|
7470
7557
|
if (COMMON_READ_TOOL_NAMES.has(name)) {
|
|
7471
7558
|
return executeTool(name, input, childCtx);
|
|
7472
7559
|
}
|
|
@@ -7484,6 +7571,10 @@ async function runDesignExpert(opts, context) {
|
|
|
7484
7571
|
onEvent: context.onEvent,
|
|
7485
7572
|
resolveExternalTool: context.resolveExternalTool,
|
|
7486
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),
|
|
7487
7578
|
background: opts.background,
|
|
7488
7579
|
onBackgroundComplete: opts.background ? (bgResult) => {
|
|
7489
7580
|
context.onBackgroundComplete?.(
|
|
@@ -7507,6 +7598,7 @@ var init_designExpert = __esm({
|
|
|
7507
7598
|
init_tools4();
|
|
7508
7599
|
init_tools();
|
|
7509
7600
|
init_prompt2();
|
|
7601
|
+
init_validateWireframeRefs();
|
|
7510
7602
|
init_history();
|
|
7511
7603
|
init_surfaces();
|
|
7512
7604
|
init_writeFile();
|
|
@@ -7794,8 +7886,11 @@ var init_productVision = __esm({
|
|
|
7794
7886
|
cachePolicy: "conversation",
|
|
7795
7887
|
tools: VISION_TOOLS,
|
|
7796
7888
|
externalTools: /* @__PURE__ */ new Set(),
|
|
7797
|
-
executeTool: (name, input2, toolCallId,
|
|
7798
|
-
const childCtx = toolCallId ? {
|
|
7889
|
+
executeTool: (name, input2, toolCallId, onLog, sams) => {
|
|
7890
|
+
const childCtx = toolCallId ? {
|
|
7891
|
+
...deriveContext(context, toolCallId, onLog),
|
|
7892
|
+
subAgentMessages: sams
|
|
7893
|
+
} : { ...context, subAgentMessages: sams };
|
|
7799
7894
|
if (COMMON_READ_TOOL_NAMES.has(name)) {
|
|
7800
7895
|
return executeTool(name, input2, childCtx);
|
|
7801
7896
|
}
|
|
@@ -7926,7 +8021,13 @@ var init_codeSanityCheck = __esm({
|
|
|
7926
8021
|
task: input.task,
|
|
7927
8022
|
tools: SANITY_CHECK_TOOLS,
|
|
7928
8023
|
externalTools: /* @__PURE__ */ new Set(),
|
|
7929
|
-
executeTool: (name, toolInput
|
|
8024
|
+
executeTool: (name, toolInput, toolCallId, onLog, sams) => {
|
|
8025
|
+
const childCtx = toolCallId ? {
|
|
8026
|
+
...deriveContext(context, toolCallId, onLog),
|
|
8027
|
+
subAgentMessages: sams
|
|
8028
|
+
} : context;
|
|
8029
|
+
return executeTool(name, toolInput, childCtx);
|
|
8030
|
+
},
|
|
7930
8031
|
apiConfig: context.apiConfig,
|
|
7931
8032
|
model: resolveModel("codeSanityCheck", context.models, context.model),
|
|
7932
8033
|
subAgentId: "codeSanityCheck",
|
|
@@ -8173,16 +8274,19 @@ After reconciling the spec, refresh the Build Overview: author the complete upda
|
|
|
8173
8274
|
task,
|
|
8174
8275
|
tools: tools2,
|
|
8175
8276
|
externalTools: /* @__PURE__ */ new Set(),
|
|
8176
|
-
executeTool: (name, toolInput, toolCallId,
|
|
8277
|
+
executeTool: (name, toolInput, toolCallId, onLog, sams) => {
|
|
8278
|
+
const childCtx = toolCallId ? {
|
|
8279
|
+
...deriveContext(context, toolCallId, onLog),
|
|
8280
|
+
subAgentMessages: sams
|
|
8281
|
+
} : { ...context, subAgentMessages: sams };
|
|
8177
8282
|
if (name === "writeBuildOverview") {
|
|
8178
|
-
const childCtx = toolCallId ? { ...deriveContext(context, toolCallId), subAgentMessages: sams } : { ...context, subAgentMessages: sams };
|
|
8179
8283
|
return renderBuildOverview(
|
|
8180
8284
|
String(toolInput.content ?? "").trim(),
|
|
8181
8285
|
childCtx,
|
|
8182
8286
|
{ background: false }
|
|
8183
8287
|
);
|
|
8184
8288
|
}
|
|
8185
|
-
return executeTool(name, toolInput,
|
|
8289
|
+
return executeTool(name, toolInput, childCtx);
|
|
8186
8290
|
},
|
|
8187
8291
|
apiConfig: context.apiConfig,
|
|
8188
8292
|
model: resolveModel("specSync", context.models, context.model),
|
|
@@ -8266,8 +8370,8 @@ var init_scrapeWebUrl2 = __esm({
|
|
|
8266
8370
|
});
|
|
8267
8371
|
|
|
8268
8372
|
// src/tools/index.ts
|
|
8269
|
-
function deriveContext(parent, toolCallId) {
|
|
8270
|
-
return { ...parent, toolCallId };
|
|
8373
|
+
function deriveContext(parent, toolCallId, onLog) {
|
|
8374
|
+
return { ...parent, toolCallId, onLog };
|
|
8271
8375
|
}
|
|
8272
8376
|
function getToolDefinitions() {
|
|
8273
8377
|
return ALL_TOOLS.map((t) => t.definition);
|
|
@@ -9986,7 +10090,7 @@ var init_config = __esm({
|
|
|
9986
10090
|
// src/headless/attachments.ts
|
|
9987
10091
|
import { mkdirSync, existsSync } from "fs";
|
|
9988
10092
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
9989
|
-
import { basename as basename2, join as
|
|
10093
|
+
import { basename as basename2, join as join4, extname as extname2 } from "path";
|
|
9990
10094
|
function filenameFromUrl(url) {
|
|
9991
10095
|
try {
|
|
9992
10096
|
const pathname = new URL(url).pathname;
|
|
@@ -9997,7 +10101,7 @@ function filenameFromUrl(url) {
|
|
|
9997
10101
|
}
|
|
9998
10102
|
}
|
|
9999
10103
|
function resolveUniqueFilename(name, claimed) {
|
|
10000
|
-
const isFree = (candidate) => !claimed.has(candidate) && !existsSync(
|
|
10104
|
+
const isFree = (candidate) => !claimed.has(candidate) && !existsSync(join4(UPLOADS_DIR, candidate));
|
|
10001
10105
|
if (isFree(name)) {
|
|
10002
10106
|
return name;
|
|
10003
10107
|
}
|
|
@@ -10031,7 +10135,7 @@ async function persistAttachments(attachments) {
|
|
|
10031
10135
|
const results = await Promise.allSettled(
|
|
10032
10136
|
nonVoice.map(async (att, i) => {
|
|
10033
10137
|
const name = names[i];
|
|
10034
|
-
const localPath =
|
|
10138
|
+
const localPath = join4(UPLOADS_DIR, name);
|
|
10035
10139
|
const res = await fetch(att.url, {
|
|
10036
10140
|
signal: AbortSignal.timeout(3e4)
|
|
10037
10141
|
});
|
|
@@ -38,7 +38,8 @@ These are things we already know about and have decided to accept:
|
|
|
38
38
|
- Preferences:
|
|
39
39
|
- use [wouter](https://github.com/molefrog/wouter) for React routing instead of reaching for react-router
|
|
40
40
|
- uploading user files should always happen via `platform.uploadFile()` from `@mindstudio-ai/interface` — not custom S3 code, not FormData to a method endpoint
|
|
41
|
-
- for
|
|
41
|
+
- for build-time prerendering of purely static sites (marketing pages with no dynamic content — distinct from the platform's crawler prerendering, below), roll your own with a post-build `renderToString` script — do not use `vite-prerender-plugin` (it bundles the prerender script as a client chunk, adding ~800KB to the user-facing bundle with no way to prevent it)
|
|
42
|
+
- **Prerendering for crawlers/unfurlers is a platform feature — don't design around it.** For SEO / link-unfurl / AI-crawler visibility on a non-static SPA, the platform already handles it: routes opt in via `web.json` (`{ "web": { "prerender": { "paths": ["/blog/*"] } } }`), the SPA signals readiness by setting `data-prerender-ready` on the html element, deploys invalidate the snapshot cache automatically, and content that changes outside deploys is invalidated at runtime with `await prerender.invalidate([...])` from `@mindstudio-ai/agent` (called from the mutating method). It serves cached headless snapshots of the live SPA to bots — it is NOT build-time rendering. Do not recommend post-build render scripts, rebuild-on-content-change, or third-party prerender services for this. The developer's main context carries the full interfaces reference with exact semantics — tell them to consult it rather than improvising.
|
|
42
43
|
|
|
43
44
|
### Common pitfalls (always flag these)
|
|
44
45
|
|