@mindstudio-ai/remy 0.1.235 → 0.1.237
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
|
@@ -421,6 +421,13 @@ var MODEL_SURFACES = {
|
|
|
421
421
|
modelType: "text",
|
|
422
422
|
userPickable: true
|
|
423
423
|
},
|
|
424
|
+
specSync: {
|
|
425
|
+
default: "claude-5-sonnet",
|
|
426
|
+
label: "Spec Sync Agent",
|
|
427
|
+
description: "Keeps your spec in sync with the code as you build, updating the affected sections in the background after changes.",
|
|
428
|
+
modelType: "text",
|
|
429
|
+
userPickable: true
|
|
430
|
+
},
|
|
424
431
|
imageGeneration: {
|
|
425
432
|
default: "seedream-4.5",
|
|
426
433
|
label: "Image Generation",
|
|
@@ -3231,6 +3238,7 @@ async function runSubAgent(config) {
|
|
|
3231
3238
|
requestId,
|
|
3232
3239
|
history,
|
|
3233
3240
|
background,
|
|
3241
|
+
acquireLock,
|
|
3234
3242
|
onBackgroundComplete,
|
|
3235
3243
|
captureArtifacts
|
|
3236
3244
|
} = config;
|
|
@@ -3623,7 +3631,15 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
3623
3631
|
agentName: subAgentId || "agent",
|
|
3624
3632
|
task
|
|
3625
3633
|
});
|
|
3626
|
-
|
|
3634
|
+
const runDetached = async () => {
|
|
3635
|
+
const release = acquireLock ? await acquireLock() : null;
|
|
3636
|
+
try {
|
|
3637
|
+
return await wrapRun();
|
|
3638
|
+
} finally {
|
|
3639
|
+
release?.();
|
|
3640
|
+
}
|
|
3641
|
+
};
|
|
3642
|
+
runDetached().then((finalResult) => {
|
|
3627
3643
|
toolRegistry?.unregister(parentToolId);
|
|
3628
3644
|
onBackgroundComplete?.(finalResult);
|
|
3629
3645
|
}).catch((err) => {
|
|
@@ -5571,6 +5587,102 @@ var codeSanityCheckTool = {
|
|
|
5571
5587
|
}
|
|
5572
5588
|
};
|
|
5573
5589
|
|
|
5590
|
+
// src/subagents/specSync/tools.ts
|
|
5591
|
+
var SPEC_SYNC_TOOLS = [
|
|
5592
|
+
...COMMON_READ_TOOLS,
|
|
5593
|
+
readSpecTool.definition,
|
|
5594
|
+
editSpecTool.definition,
|
|
5595
|
+
writeSpecTool.definition
|
|
5596
|
+
];
|
|
5597
|
+
|
|
5598
|
+
// src/subagents/specSync/lock.ts
|
|
5599
|
+
var lockQueue2 = Promise.resolve();
|
|
5600
|
+
function acquireSpecSyncLock() {
|
|
5601
|
+
let release;
|
|
5602
|
+
const next = new Promise((res) => {
|
|
5603
|
+
release = res;
|
|
5604
|
+
});
|
|
5605
|
+
const wait = lockQueue2;
|
|
5606
|
+
lockQueue2 = next;
|
|
5607
|
+
return wait.then(() => release);
|
|
5608
|
+
}
|
|
5609
|
+
|
|
5610
|
+
// src/subagents/specSync/index.ts
|
|
5611
|
+
var BASE_PROMPT5 = readAsset("subagents/specSync", "prompt.md");
|
|
5612
|
+
var MSFM_DOCS = `<mindstudio_flavored_markdown_spec_docs>
|
|
5613
|
+
${readAsset(
|
|
5614
|
+
"prompt",
|
|
5615
|
+
"compiled/msfm.md"
|
|
5616
|
+
)}
|
|
5617
|
+
</mindstudio_flavored_markdown_spec_docs>`;
|
|
5618
|
+
var specSyncTool = {
|
|
5619
|
+
clearable: false,
|
|
5620
|
+
backgroundOnly: true,
|
|
5621
|
+
definition: {
|
|
5622
|
+
name: "specSync",
|
|
5623
|
+
description: "Reconcile the spec to bring it in line with code changes you have made. Provide a brief, bulleted list of what changed and why; it finds the affected spec sections and updates them to match. Always runs in the background \u2014 it returns immediately and reports back when done.",
|
|
5624
|
+
inputSchema: {
|
|
5625
|
+
type: "object",
|
|
5626
|
+
properties: {
|
|
5627
|
+
task: {
|
|
5628
|
+
type: "string",
|
|
5629
|
+
description: "What you changed in the code and why, in plain language \u2014 the way you would explain it to a teammate. The specialist reads the spec itself and decides which sections to update; you do not need to name files or spec locations."
|
|
5630
|
+
}
|
|
5631
|
+
},
|
|
5632
|
+
required: ["task"]
|
|
5633
|
+
}
|
|
5634
|
+
},
|
|
5635
|
+
async execute(input, context) {
|
|
5636
|
+
if (!context) {
|
|
5637
|
+
return "Error: spec sync requires execution context";
|
|
5638
|
+
}
|
|
5639
|
+
if (context.onboardingState !== "onboardingFinished") {
|
|
5640
|
+
return "Spec sync runs only after the build is finished (onboardingFinished). During intake and the initial build you author the spec directly, so there is nothing to reconcile yet.";
|
|
5641
|
+
}
|
|
5642
|
+
const specIndex = loadSpecIndex();
|
|
5643
|
+
const parts = [
|
|
5644
|
+
BASE_PROMPT5,
|
|
5645
|
+
loadPlatformBrief(),
|
|
5646
|
+
MSFM_DOCS,
|
|
5647
|
+
"<!-- cache_breakpoint -->"
|
|
5648
|
+
];
|
|
5649
|
+
if (specIndex) {
|
|
5650
|
+
parts.push(specIndex);
|
|
5651
|
+
}
|
|
5652
|
+
const system = parts.join("\n\n");
|
|
5653
|
+
const result = await runSubAgent({
|
|
5654
|
+
system,
|
|
5655
|
+
task: input.task,
|
|
5656
|
+
tools: SPEC_SYNC_TOOLS,
|
|
5657
|
+
externalTools: /* @__PURE__ */ new Set(),
|
|
5658
|
+
executeTool: (name, toolInput) => executeTool(name, toolInput, context),
|
|
5659
|
+
apiConfig: context.apiConfig,
|
|
5660
|
+
model: resolveModel("specSync", context.models, context.model),
|
|
5661
|
+
subAgentId: "specSync",
|
|
5662
|
+
signal: context.signal,
|
|
5663
|
+
parentToolId: context.toolCallId,
|
|
5664
|
+
requestId: context.requestId,
|
|
5665
|
+
onEvent: context.onEvent,
|
|
5666
|
+
resolveExternalTool: context.resolveExternalTool,
|
|
5667
|
+
toolRegistry: context.toolRegistry,
|
|
5668
|
+
// Always background + serialized: never blocks Remy's turn, and two
|
|
5669
|
+
// reconciliations queue (FIFO) instead of running at once.
|
|
5670
|
+
background: true,
|
|
5671
|
+
acquireLock: acquireSpecSyncLock,
|
|
5672
|
+
onBackgroundComplete: (bgResult) => {
|
|
5673
|
+
context.onBackgroundComplete?.(
|
|
5674
|
+
context.toolCallId,
|
|
5675
|
+
"specSync",
|
|
5676
|
+
bgResult.text,
|
|
5677
|
+
bgResult.messages
|
|
5678
|
+
);
|
|
5679
|
+
}
|
|
5680
|
+
});
|
|
5681
|
+
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
5682
|
+
return result.text;
|
|
5683
|
+
}
|
|
5684
|
+
};
|
|
5685
|
+
|
|
5574
5686
|
// src/tools/common/scrapeWebUrl.ts
|
|
5575
5687
|
var scrapeWebUrlTool = {
|
|
5576
5688
|
clearable: false,
|
|
@@ -5739,6 +5851,8 @@ var ALL_TOOLS = [
|
|
|
5739
5851
|
productVisionTool,
|
|
5740
5852
|
codeSanityCheckTool,
|
|
5741
5853
|
copyEditorTool,
|
|
5854
|
+
specSyncTool,
|
|
5855
|
+
// no-ops until onboardingFinished; kept here so the tool list stays cache-stable
|
|
5742
5856
|
buildOverviewTool,
|
|
5743
5857
|
compactConversationTool,
|
|
5744
5858
|
// Post-onboarding
|
|
@@ -5776,6 +5890,7 @@ var SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
5776
5890
|
"productVision",
|
|
5777
5891
|
"codeSanityCheck",
|
|
5778
5892
|
"copyEditor",
|
|
5893
|
+
"specSync",
|
|
5779
5894
|
"runAutomatedBrowserTest",
|
|
5780
5895
|
"askMindStudioSdk"
|
|
5781
5896
|
]);
|
|
@@ -6860,6 +6975,11 @@ var USER_BLOCKING_EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
|
6860
6975
|
"presentPublishPlan",
|
|
6861
6976
|
"confirmDestructiveAction"
|
|
6862
6977
|
]);
|
|
6978
|
+
function isBackgroundCall(tc) {
|
|
6979
|
+
return Boolean(
|
|
6980
|
+
tc.input?.background || getToolByName(tc.name)?.backgroundOnly
|
|
6981
|
+
);
|
|
6982
|
+
}
|
|
6863
6983
|
function createAgentState() {
|
|
6864
6984
|
return { messages: [] };
|
|
6865
6985
|
}
|
|
@@ -6912,7 +7032,10 @@ async function runTurn(params) {
|
|
|
6912
7032
|
onEvent({
|
|
6913
7033
|
type: "user_message",
|
|
6914
7034
|
text: userMessage,
|
|
6915
|
-
hidden: hidden || void 0
|
|
7035
|
+
hidden: hidden || void 0,
|
|
7036
|
+
// Include attachments so the live event can render a queued voice/image/file
|
|
7037
|
+
// bubble; a voice message has empty text and the transcript lives here.
|
|
7038
|
+
...hasAttachments && { attachments }
|
|
6916
7039
|
});
|
|
6917
7040
|
const isFirstMessage = state.messages.filter((m) => m.role === "user").length === 1;
|
|
6918
7041
|
const STATUS_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -7124,16 +7247,18 @@ async function runTurn(params) {
|
|
|
7124
7247
|
break;
|
|
7125
7248
|
}
|
|
7126
7249
|
case "tool_use": {
|
|
7250
|
+
const tool = getToolByName(event.name);
|
|
7127
7251
|
contentBlocks.push({
|
|
7128
7252
|
type: "tool",
|
|
7129
7253
|
id: event.id,
|
|
7130
7254
|
name: event.name,
|
|
7131
7255
|
input: event.input,
|
|
7132
7256
|
startedAt: event.ts,
|
|
7133
|
-
...event.input.background && {
|
|
7257
|
+
...(event.input.background || tool?.backgroundOnly) && {
|
|
7258
|
+
background: true
|
|
7259
|
+
}
|
|
7134
7260
|
});
|
|
7135
7261
|
const acc = toolInputAccumulators.get(event.id);
|
|
7136
|
-
const tool = getToolByName(event.name);
|
|
7137
7262
|
const wasStreamed = acc?.started ?? false;
|
|
7138
7263
|
const isInputStreaming = !!tool?.streaming?.partialInput;
|
|
7139
7264
|
log13.info("Tool received", {
|
|
@@ -7366,7 +7491,7 @@ async function runTurn(params) {
|
|
|
7366
7491
|
toolRegistry?.register(entry);
|
|
7367
7492
|
run(tc.input);
|
|
7368
7493
|
const r = await resultPromise;
|
|
7369
|
-
if (!tc
|
|
7494
|
+
if (!isBackgroundCall(tc)) {
|
|
7370
7495
|
toolRegistry?.unregister(tc.id);
|
|
7371
7496
|
}
|
|
7372
7497
|
log13.info("Tool completed", {
|
|
@@ -8030,7 +8155,16 @@ var HeadlessSession = class {
|
|
|
8030
8155
|
this.emit("turn_started", {}, rid);
|
|
8031
8156
|
return;
|
|
8032
8157
|
case "user_message":
|
|
8033
|
-
this.emit(
|
|
8158
|
+
this.emit(
|
|
8159
|
+
"user_message",
|
|
8160
|
+
{
|
|
8161
|
+
text: e.text,
|
|
8162
|
+
// Forward attachments so queued voice/image/file sends render live;
|
|
8163
|
+
// otherwise the bubble is blank until a get_history refresh.
|
|
8164
|
+
...e.attachments && { attachments: e.attachments }
|
|
8165
|
+
},
|
|
8166
|
+
rid
|
|
8167
|
+
);
|
|
8034
8168
|
return;
|
|
8035
8169
|
// Terminal events — translate to `completed`.
|
|
8036
8170
|
// Post-turn queue drain happens in handleMessage AFTER runTurn returns,
|
package/dist/index.js
CHANGED
|
@@ -1810,7 +1810,7 @@ var init_compaction = __esm({
|
|
|
1810
1810
|
"use strict";
|
|
1811
1811
|
init_api();
|
|
1812
1812
|
init_assets();
|
|
1813
|
-
|
|
1813
|
+
init_tools8();
|
|
1814
1814
|
init_logger();
|
|
1815
1815
|
init_usageLedger();
|
|
1816
1816
|
log2 = createLogger("compaction");
|
|
@@ -2037,6 +2037,13 @@ var init_surfaces = __esm({
|
|
|
2037
2037
|
modelType: "text",
|
|
2038
2038
|
userPickable: true
|
|
2039
2039
|
},
|
|
2040
|
+
specSync: {
|
|
2041
|
+
default: "claude-5-sonnet",
|
|
2042
|
+
label: "Spec Sync Agent",
|
|
2043
|
+
description: "Keeps your spec in sync with the code as you build, updating the affected sections in the background after changes.",
|
|
2044
|
+
modelType: "text",
|
|
2045
|
+
userPickable: true
|
|
2046
|
+
},
|
|
2040
2047
|
imageGeneration: {
|
|
2041
2048
|
default: "seedream-4.5",
|
|
2042
2049
|
label: "Image Generation",
|
|
@@ -2356,7 +2363,7 @@ var init_trigger = __esm({
|
|
|
2356
2363
|
"use strict";
|
|
2357
2364
|
init_compaction();
|
|
2358
2365
|
init_prompt();
|
|
2359
|
-
|
|
2366
|
+
init_tools8();
|
|
2360
2367
|
init_logger();
|
|
2361
2368
|
init_surfaces();
|
|
2362
2369
|
log4 = createLogger("compaction:trigger");
|
|
@@ -3883,6 +3890,7 @@ async function runSubAgent(config) {
|
|
|
3883
3890
|
requestId,
|
|
3884
3891
|
history,
|
|
3885
3892
|
background,
|
|
3893
|
+
acquireLock,
|
|
3886
3894
|
onBackgroundComplete,
|
|
3887
3895
|
captureArtifacts
|
|
3888
3896
|
} = config;
|
|
@@ -4275,7 +4283,15 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
4275
4283
|
agentName: subAgentId || "agent",
|
|
4276
4284
|
task
|
|
4277
4285
|
});
|
|
4278
|
-
|
|
4286
|
+
const runDetached = async () => {
|
|
4287
|
+
const release = acquireLock ? await acquireLock() : null;
|
|
4288
|
+
try {
|
|
4289
|
+
return await wrapRun();
|
|
4290
|
+
} finally {
|
|
4291
|
+
release?.();
|
|
4292
|
+
}
|
|
4293
|
+
};
|
|
4294
|
+
runDetached().then((finalResult) => {
|
|
4279
4295
|
toolRegistry?.unregister(parentToolId);
|
|
4280
4296
|
onBackgroundComplete?.(finalResult);
|
|
4281
4297
|
}).catch((err) => {
|
|
@@ -5615,7 +5631,7 @@ var init_copyEditor = __esm({
|
|
|
5615
5631
|
init_assets();
|
|
5616
5632
|
init_runner();
|
|
5617
5633
|
init_context();
|
|
5618
|
-
|
|
5634
|
+
init_tools8();
|
|
5619
5635
|
init_tools3();
|
|
5620
5636
|
init_surfaces();
|
|
5621
5637
|
BASE_PROMPT2 = readAsset("subagents/copyEditor", "prompt.md");
|
|
@@ -5714,7 +5730,7 @@ var tools, DESIGN_EXPERT_TOOLS;
|
|
|
5714
5730
|
var init_tools4 = __esm({
|
|
5715
5731
|
"src/subagents/designExpert/tools/index.ts"() {
|
|
5716
5732
|
"use strict";
|
|
5717
|
-
|
|
5733
|
+
init_tools8();
|
|
5718
5734
|
init_tools2();
|
|
5719
5735
|
init_searchGoogle2();
|
|
5720
5736
|
init_scrapeWebUrl();
|
|
@@ -6080,7 +6096,7 @@ var DESCRIPTION, RENDER_WRITE_TOOL_NAMES, DESIGN_EXPERT_RENDER_TOOLS, designExpe
|
|
|
6080
6096
|
var init_designExpert = __esm({
|
|
6081
6097
|
"src/subagents/designExpert/index.ts"() {
|
|
6082
6098
|
"use strict";
|
|
6083
|
-
|
|
6099
|
+
init_tools8();
|
|
6084
6100
|
init_runner();
|
|
6085
6101
|
init_tools4();
|
|
6086
6102
|
init_tools2();
|
|
@@ -6335,7 +6351,7 @@ var productVisionTool;
|
|
|
6335
6351
|
var init_productVision = __esm({
|
|
6336
6352
|
"src/subagents/productVision/index.ts"() {
|
|
6337
6353
|
"use strict";
|
|
6338
|
-
|
|
6354
|
+
init_tools8();
|
|
6339
6355
|
init_runner();
|
|
6340
6356
|
init_tools5();
|
|
6341
6357
|
init_tools2();
|
|
@@ -6471,7 +6487,7 @@ var init_codeSanityCheck = __esm({
|
|
|
6471
6487
|
init_assets();
|
|
6472
6488
|
init_runner();
|
|
6473
6489
|
init_context();
|
|
6474
|
-
|
|
6490
|
+
init_tools8();
|
|
6475
6491
|
init_tools6();
|
|
6476
6492
|
init_surfaces();
|
|
6477
6493
|
BASE_PROMPT4 = readAsset("subagents/codeSanityCheck", "prompt.md");
|
|
@@ -6525,6 +6541,131 @@ var init_codeSanityCheck = __esm({
|
|
|
6525
6541
|
}
|
|
6526
6542
|
});
|
|
6527
6543
|
|
|
6544
|
+
// src/subagents/specSync/tools.ts
|
|
6545
|
+
var SPEC_SYNC_TOOLS;
|
|
6546
|
+
var init_tools7 = __esm({
|
|
6547
|
+
"src/subagents/specSync/tools.ts"() {
|
|
6548
|
+
"use strict";
|
|
6549
|
+
init_tools2();
|
|
6550
|
+
init_readSpec();
|
|
6551
|
+
init_editSpec();
|
|
6552
|
+
init_writeSpec();
|
|
6553
|
+
SPEC_SYNC_TOOLS = [
|
|
6554
|
+
...COMMON_READ_TOOLS,
|
|
6555
|
+
readSpecTool.definition,
|
|
6556
|
+
editSpecTool.definition,
|
|
6557
|
+
writeSpecTool.definition
|
|
6558
|
+
];
|
|
6559
|
+
}
|
|
6560
|
+
});
|
|
6561
|
+
|
|
6562
|
+
// src/subagents/specSync/lock.ts
|
|
6563
|
+
function acquireSpecSyncLock() {
|
|
6564
|
+
let release;
|
|
6565
|
+
const next = new Promise((res) => {
|
|
6566
|
+
release = res;
|
|
6567
|
+
});
|
|
6568
|
+
const wait = lockQueue2;
|
|
6569
|
+
lockQueue2 = next;
|
|
6570
|
+
return wait.then(() => release);
|
|
6571
|
+
}
|
|
6572
|
+
var lockQueue2;
|
|
6573
|
+
var init_lock = __esm({
|
|
6574
|
+
"src/subagents/specSync/lock.ts"() {
|
|
6575
|
+
"use strict";
|
|
6576
|
+
lockQueue2 = Promise.resolve();
|
|
6577
|
+
}
|
|
6578
|
+
});
|
|
6579
|
+
|
|
6580
|
+
// src/subagents/specSync/index.ts
|
|
6581
|
+
var BASE_PROMPT5, MSFM_DOCS, specSyncTool;
|
|
6582
|
+
var init_specSync = __esm({
|
|
6583
|
+
"src/subagents/specSync/index.ts"() {
|
|
6584
|
+
"use strict";
|
|
6585
|
+
init_assets();
|
|
6586
|
+
init_runner();
|
|
6587
|
+
init_context();
|
|
6588
|
+
init_tools8();
|
|
6589
|
+
init_tools7();
|
|
6590
|
+
init_lock();
|
|
6591
|
+
init_surfaces();
|
|
6592
|
+
BASE_PROMPT5 = readAsset("subagents/specSync", "prompt.md");
|
|
6593
|
+
MSFM_DOCS = `<mindstudio_flavored_markdown_spec_docs>
|
|
6594
|
+
${readAsset(
|
|
6595
|
+
"prompt",
|
|
6596
|
+
"compiled/msfm.md"
|
|
6597
|
+
)}
|
|
6598
|
+
</mindstudio_flavored_markdown_spec_docs>`;
|
|
6599
|
+
specSyncTool = {
|
|
6600
|
+
clearable: false,
|
|
6601
|
+
backgroundOnly: true,
|
|
6602
|
+
definition: {
|
|
6603
|
+
name: "specSync",
|
|
6604
|
+
description: "Reconcile the spec to bring it in line with code changes you have made. Provide a brief, bulleted list of what changed and why; it finds the affected spec sections and updates them to match. Always runs in the background \u2014 it returns immediately and reports back when done.",
|
|
6605
|
+
inputSchema: {
|
|
6606
|
+
type: "object",
|
|
6607
|
+
properties: {
|
|
6608
|
+
task: {
|
|
6609
|
+
type: "string",
|
|
6610
|
+
description: "What you changed in the code and why, in plain language \u2014 the way you would explain it to a teammate. The specialist reads the spec itself and decides which sections to update; you do not need to name files or spec locations."
|
|
6611
|
+
}
|
|
6612
|
+
},
|
|
6613
|
+
required: ["task"]
|
|
6614
|
+
}
|
|
6615
|
+
},
|
|
6616
|
+
async execute(input, context) {
|
|
6617
|
+
if (!context) {
|
|
6618
|
+
return "Error: spec sync requires execution context";
|
|
6619
|
+
}
|
|
6620
|
+
if (context.onboardingState !== "onboardingFinished") {
|
|
6621
|
+
return "Spec sync runs only after the build is finished (onboardingFinished). During intake and the initial build you author the spec directly, so there is nothing to reconcile yet.";
|
|
6622
|
+
}
|
|
6623
|
+
const specIndex = loadSpecIndex();
|
|
6624
|
+
const parts = [
|
|
6625
|
+
BASE_PROMPT5,
|
|
6626
|
+
loadPlatformBrief(),
|
|
6627
|
+
MSFM_DOCS,
|
|
6628
|
+
"<!-- cache_breakpoint -->"
|
|
6629
|
+
];
|
|
6630
|
+
if (specIndex) {
|
|
6631
|
+
parts.push(specIndex);
|
|
6632
|
+
}
|
|
6633
|
+
const system = parts.join("\n\n");
|
|
6634
|
+
const result = await runSubAgent({
|
|
6635
|
+
system,
|
|
6636
|
+
task: input.task,
|
|
6637
|
+
tools: SPEC_SYNC_TOOLS,
|
|
6638
|
+
externalTools: /* @__PURE__ */ new Set(),
|
|
6639
|
+
executeTool: (name, toolInput) => executeTool(name, toolInput, context),
|
|
6640
|
+
apiConfig: context.apiConfig,
|
|
6641
|
+
model: resolveModel("specSync", context.models, context.model),
|
|
6642
|
+
subAgentId: "specSync",
|
|
6643
|
+
signal: context.signal,
|
|
6644
|
+
parentToolId: context.toolCallId,
|
|
6645
|
+
requestId: context.requestId,
|
|
6646
|
+
onEvent: context.onEvent,
|
|
6647
|
+
resolveExternalTool: context.resolveExternalTool,
|
|
6648
|
+
toolRegistry: context.toolRegistry,
|
|
6649
|
+
// Always background + serialized: never blocks Remy's turn, and two
|
|
6650
|
+
// reconciliations queue (FIFO) instead of running at once.
|
|
6651
|
+
background: true,
|
|
6652
|
+
acquireLock: acquireSpecSyncLock,
|
|
6653
|
+
onBackgroundComplete: (bgResult) => {
|
|
6654
|
+
context.onBackgroundComplete?.(
|
|
6655
|
+
context.toolCallId,
|
|
6656
|
+
"specSync",
|
|
6657
|
+
bgResult.text,
|
|
6658
|
+
bgResult.messages
|
|
6659
|
+
);
|
|
6660
|
+
}
|
|
6661
|
+
});
|
|
6662
|
+
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
6663
|
+
return result.text;
|
|
6664
|
+
}
|
|
6665
|
+
};
|
|
6666
|
+
}
|
|
6667
|
+
});
|
|
6668
|
+
|
|
6528
6669
|
// src/tools/common/scrapeWebUrl.ts
|
|
6529
6670
|
var scrapeWebUrlTool;
|
|
6530
6671
|
var init_scrapeWebUrl2 = __esm({
|
|
@@ -6708,7 +6849,7 @@ function executeTool(name, input, context) {
|
|
|
6708
6849
|
return tool.execute(input, context);
|
|
6709
6850
|
}
|
|
6710
6851
|
var ALL_TOOLS, CLEARABLE_TOOLS, SUBAGENT_TOOL_NAMES;
|
|
6711
|
-
var
|
|
6852
|
+
var init_tools8 = __esm({
|
|
6712
6853
|
"src/tools/index.ts"() {
|
|
6713
6854
|
"use strict";
|
|
6714
6855
|
init_readSpec();
|
|
@@ -6744,6 +6885,7 @@ var init_tools7 = __esm({
|
|
|
6744
6885
|
init_productVision();
|
|
6745
6886
|
init_codeSanityCheck();
|
|
6746
6887
|
init_copyEditor();
|
|
6888
|
+
init_specSync();
|
|
6747
6889
|
init_scrapeWebUrl2();
|
|
6748
6890
|
init_writeBuildOverview();
|
|
6749
6891
|
ALL_TOOLS = [
|
|
@@ -6759,6 +6901,8 @@ var init_tools7 = __esm({
|
|
|
6759
6901
|
productVisionTool,
|
|
6760
6902
|
codeSanityCheckTool,
|
|
6761
6903
|
copyEditorTool,
|
|
6904
|
+
specSyncTool,
|
|
6905
|
+
// no-ops until onboardingFinished; kept here so the tool list stays cache-stable
|
|
6762
6906
|
buildOverviewTool,
|
|
6763
6907
|
compactConversationTool,
|
|
6764
6908
|
// Post-onboarding
|
|
@@ -6796,6 +6940,7 @@ var init_tools7 = __esm({
|
|
|
6796
6940
|
"productVision",
|
|
6797
6941
|
"codeSanityCheck",
|
|
6798
6942
|
"copyEditor",
|
|
6943
|
+
"specSync",
|
|
6799
6944
|
"runAutomatedBrowserTest",
|
|
6800
6945
|
"askMindStudioSdk"
|
|
6801
6946
|
]);
|
|
@@ -7562,6 +7707,11 @@ function getToolCalls(blocks) {
|
|
|
7562
7707
|
(b) => b.type === "tool"
|
|
7563
7708
|
);
|
|
7564
7709
|
}
|
|
7710
|
+
function isBackgroundCall(tc) {
|
|
7711
|
+
return Boolean(
|
|
7712
|
+
tc.input?.background || getToolByName(tc.name)?.backgroundOnly
|
|
7713
|
+
);
|
|
7714
|
+
}
|
|
7565
7715
|
function createAgentState() {
|
|
7566
7716
|
return { messages: [] };
|
|
7567
7717
|
}
|
|
@@ -7614,7 +7764,10 @@ async function runTurn(params) {
|
|
|
7614
7764
|
onEvent({
|
|
7615
7765
|
type: "user_message",
|
|
7616
7766
|
text: userMessage,
|
|
7617
|
-
hidden: hidden || void 0
|
|
7767
|
+
hidden: hidden || void 0,
|
|
7768
|
+
// Include attachments so the live event can render a queued voice/image/file
|
|
7769
|
+
// bubble; a voice message has empty text and the transcript lives here.
|
|
7770
|
+
...hasAttachments && { attachments }
|
|
7618
7771
|
});
|
|
7619
7772
|
const isFirstMessage = state.messages.filter((m) => m.role === "user").length === 1;
|
|
7620
7773
|
const STATUS_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -7826,16 +7979,18 @@ async function runTurn(params) {
|
|
|
7826
7979
|
break;
|
|
7827
7980
|
}
|
|
7828
7981
|
case "tool_use": {
|
|
7982
|
+
const tool = getToolByName(event.name);
|
|
7829
7983
|
contentBlocks.push({
|
|
7830
7984
|
type: "tool",
|
|
7831
7985
|
id: event.id,
|
|
7832
7986
|
name: event.name,
|
|
7833
7987
|
input: event.input,
|
|
7834
7988
|
startedAt: event.ts,
|
|
7835
|
-
...event.input.background && {
|
|
7989
|
+
...(event.input.background || tool?.backgroundOnly) && {
|
|
7990
|
+
background: true
|
|
7991
|
+
}
|
|
7836
7992
|
});
|
|
7837
7993
|
const acc = toolInputAccumulators.get(event.id);
|
|
7838
|
-
const tool = getToolByName(event.name);
|
|
7839
7994
|
const wasStreamed = acc?.started ?? false;
|
|
7840
7995
|
const isInputStreaming = !!tool?.streaming?.partialInput;
|
|
7841
7996
|
log12.info("Tool received", {
|
|
@@ -8068,7 +8223,7 @@ async function runTurn(params) {
|
|
|
8068
8223
|
toolRegistry?.register(entry);
|
|
8069
8224
|
run(tc.input);
|
|
8070
8225
|
const r = await resultPromise;
|
|
8071
|
-
if (!tc
|
|
8226
|
+
if (!isBackgroundCall(tc)) {
|
|
8072
8227
|
toolRegistry?.unregister(tc.id);
|
|
8073
8228
|
}
|
|
8074
8229
|
log12.info("Tool completed", {
|
|
@@ -8135,7 +8290,7 @@ var init_agent = __esm({
|
|
|
8135
8290
|
"src/agent.ts"() {
|
|
8136
8291
|
"use strict";
|
|
8137
8292
|
init_api();
|
|
8138
|
-
|
|
8293
|
+
init_tools8();
|
|
8139
8294
|
init_session();
|
|
8140
8295
|
init_logger();
|
|
8141
8296
|
init_usageLedger();
|
|
@@ -8143,7 +8298,7 @@ var init_agent = __esm({
|
|
|
8143
8298
|
init_statusWatcher();
|
|
8144
8299
|
init_errors();
|
|
8145
8300
|
init_cleanMessages();
|
|
8146
|
-
|
|
8301
|
+
init_tools8();
|
|
8147
8302
|
init_sentinel();
|
|
8148
8303
|
init_trigger2();
|
|
8149
8304
|
init_surfaces();
|
|
@@ -8885,7 +9040,16 @@ var init_headless = __esm({
|
|
|
8885
9040
|
this.emit("turn_started", {}, rid);
|
|
8886
9041
|
return;
|
|
8887
9042
|
case "user_message":
|
|
8888
|
-
this.emit(
|
|
9043
|
+
this.emit(
|
|
9044
|
+
"user_message",
|
|
9045
|
+
{
|
|
9046
|
+
text: e.text,
|
|
9047
|
+
// Forward attachments so queued voice/image/file sends render live;
|
|
9048
|
+
// otherwise the bubble is blank until a get_history refresh.
|
|
9049
|
+
...e.attachments && { attachments: e.attachments }
|
|
9050
|
+
},
|
|
9051
|
+
rid
|
|
9052
|
+
);
|
|
8889
9053
|
return;
|
|
8890
9054
|
// Terminal events — translate to `completed`.
|
|
8891
9055
|
// Post-turn queue drain happens in handleMessage AFTER runTurn returns,
|
|
@@ -89,6 +89,6 @@ When generated code exists in `dist/`, you have both spec tools and code tools.
|
|
|
89
89
|
|
|
90
90
|
**Key principle: spec and code stay in sync.**
|
|
91
91
|
- When editing the spec, also update the affected code in the same turn.
|
|
92
|
-
- When
|
|
92
|
+
- When you make a code change that alters what the app does, the spec has to catch up too - but this must happen infrequently and in the background so it doesn't block your work and iteration with the user. Use the specSync tool to periodically update the spec after big/meaningful chunks of work, avoid editing the spec files directly. Remember, it is completely fine and normal for there to be some drift between the code and spec - you do not need to sync after every conversation turn. You can batch many changes together - you can trust that the specSync tool will investigate the changes and reconcile on its own.
|
|
93
93
|
- Spec tools work on `src/` files: `readSpec` to read (do this before editing), `editSpec` for targeted find/replace edits (same `old_string`/`new_string` model as `editFile`), `writeSpec` for a full-file write, `listSpecFiles` to see what exists.
|
|
94
94
|
- Code tools (`readFile`, `writeFile`, `editFile`, etc.) work on `dist/` and other project files.
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
4. **Iterate.** If something fails, read the error, diagnose the root cause, and try a different approach.
|
|
6
6
|
|
|
7
7
|
## Principles
|
|
8
|
-
- The spec in `src/` is the source of truth
|
|
9
|
-
- Always keep the spec up to date after making changes to the code, especially when adding features or building things from the roadmap.
|
|
8
|
+
- The spec in `src/` is the source of truth for what the app does: consult it before making behavioral code changes, and keep it in sync as the app evolves by dispatching periodic requests to the specSync tool after meaningful changes. Some amount of drift is fine and inevitable - your priority is working efficiently with the user, let the specSync tool handle things in the background.
|
|
10
9
|
- Keep `src/overview.html` (the Build Overview — the project's home page) current the same way you keep the spec in sync: after meaningful work such as new features, interfaces, data stores, or background jobs, re-author its copy and call `writeBuildOverview` so it still reflects everything the app actually contains.
|
|
11
10
|
- Change only what the task requires. Match existing styles. Keep solutions simple.
|
|
12
11
|
- Read files before editing them. Understand the context before making changes.
|
|
@@ -40,6 +40,12 @@ Always consult the code sanity check before writing code in initialCodegen with
|
|
|
40
40
|
|
|
41
41
|
Your editor — a design expert for words. Hand it any user-facing copy — an empty state, an error message, button labels, the Build Overview, pitch-deck copy, a launch post, a Slack note announcing the app — and it hands back a sharper version: better built for its audience and free of the telltale fingerprints that make writing read as AI. You're good at deciding *what* to say; it's great at making it land. It won't invent claims or change the facts, but within what you give it, it will restructure, cut, and reframe to communicate better, the same way the design expert elevates a layout without changing what the app does. Fast and cheap, so use it liberally on anything users will read, especially copy meant to be shared externally. Give it the text plus what it's for (the medium, the audience).
|
|
42
42
|
|
|
43
|
+
### Spec Sync Agent (`specSync`)
|
|
44
|
+
|
|
45
|
+
Your spec keeper. Once the app is built and you're iterating on it, whenever you make code changes that alter what the app does, hand it a brief, plain-language description of what you changed and why (prefer bullet points, batch multiple changes into one invocation). It finds the affected sections of the spec in `src/` and updates them to match what has been built. It reads the spec itself and decides what to touch, so you don't need to name files or locations.
|
|
46
|
+
|
|
47
|
+
It always runs in the background: it returns immediately and you keep working while it reconciles, and you'll get notified when the updated spec lands later. So don't hunt through spec files to sync them yourself and don't wait on it — hand off and move on. You decide when the spec has drifted enough to be worth a hand-off (after a meaningful change, or a batch of them - you do not need to invoke this after every change or conversation turn - some amount of drift between code and spec is completely normal and acceptable).
|
|
48
|
+
|
|
43
49
|
### QA (`runAutomatedBrowserTest`)
|
|
44
50
|
|
|
45
51
|
For verifying complex stateful interactions: multi-step form submissions, auth flows, real-time updates, flows that require specific data/role setup. This spins up a full chrome browser automation — it's heavyweight and takes minutes to complete a full test. Do not use it for basic rendering or navigation checks. If you can verify something with a screenshot or by reading the code, do that instead. Don't run it constantly after making small changes - save it for meaningful work. Run a scenario first to seed test data and set user roles. The user is able to watch QA work on their screen via a live browser preview - the cursor will move, type, etc - so you can also use this to demo functionality to the user and help them understand how to use their app.
|
|
@@ -72,4 +78,4 @@ You can only background the following two tasks, unless the user specifically as
|
|
|
72
78
|
- `productVision` seeding the intiial roadmap after writing the spec for the first time or updating the roadmap after large work sessions. This task takes a while and we can allow the user to continue building while it happens in the background.
|
|
73
79
|
- After writing the spec, once you have finalized the shape of the app, ask `visualDesignExpert` to create an icon and an open graph shring image for the app, then set them with `setProjectMetadata`, alongside the app's name and short description.
|
|
74
80
|
|
|
75
|
-
Do not background any other tasks.
|
|
81
|
+
Do not background any other tasks. Be aware that sometimes tools like specSync will background on their own - this is not something that is within your control.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
You keep the app's spec in `src/` aligned with the code. The developer has just made code changes and is handing you a description of what changed and why. Your one job is to find the spec sections that describe that behavior and update them so the spec matches the code again.
|
|
2
|
+
|
|
3
|
+
Work fast. This is reconciliation, not review.
|
|
4
|
+
|
|
5
|
+
## How to work
|
|
6
|
+
|
|
7
|
+
1. **Trust the developer's brief.** The developer knows what it changed and why. Take the brief as ground truth for the change. Do not re-derive it, second-guess whether the change was correct, or audit the rest of the app.
|
|
8
|
+
2. **Locate the affected spec.** The project's spec files are already listed for you below, each with a one-line description. Use `grep`/`readSpec` to find where the changed behavior is documented, and read only the sections you are about to touch. If the brief mentions something not yet in any spec (a new feature, table, role, interface, background job), add it to the spec file where it belongs.
|
|
9
|
+
3. **Update it.** Use `editSpec` for targeted changes (read the section first so your `old_string` matches), or `writeSpec` for a full rewrite of a file. Read a spec file with `readSpec` before editing it.
|
|
10
|
+
4. **Finish.** Once the spec reflects the change, you are done. Do not keep looking for more to fix, do not polish unrelated sections, do not verify the code beyond a quick read if and only if the brief is genuinely ambiguous.
|
|
11
|
+
|
|
12
|
+
## What to write
|
|
13
|
+
|
|
14
|
+
The spec is written in MindStudio-Flavored Markdown (MSFM); the format reference is included below. Follow it, and match the structure and voice already in the spec files. Write prose in plain, human language, the way you would describe the app to a colleague. Keep exact technical values (field names, routes, roles, limits, config) in annotations, not in the prose. Do not invent detail the brief and code do not support: describe what actually exists now, nothing more.
|
|
15
|
+
|
|
16
|
+
You edit the spec only. You never edit code.
|