@mindstudio-ai/remy 0.1.239 → 0.1.240
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/compaction/conversation.md +1 -1
- package/dist/headless.d.ts +0 -2
- package/dist/headless.js +332 -289
- package/dist/index.js +1239 -1196
- package/dist/prompt/compiled/dev-and-deploy.md +4 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1522,7 +1522,7 @@ var init_assets = __esm({
|
|
|
1522
1522
|
});
|
|
1523
1523
|
|
|
1524
1524
|
// src/compaction/index.ts
|
|
1525
|
-
async function compactConversation(messages, apiConfig,
|
|
1525
|
+
async function compactConversation(messages, apiConfig, model) {
|
|
1526
1526
|
const endIndex = findSafeInsertionPoint(messages);
|
|
1527
1527
|
const summaries = [];
|
|
1528
1528
|
const tasks = [];
|
|
@@ -1530,6 +1530,7 @@ async function compactConversation(messages, apiConfig, system, tools2, model) {
|
|
|
1530
1530
|
messages,
|
|
1531
1531
|
endIndex
|
|
1532
1532
|
);
|
|
1533
|
+
let conversationFailed = false;
|
|
1533
1534
|
if (conversationMessages.length > 0) {
|
|
1534
1535
|
tasks.push(
|
|
1535
1536
|
generateSummary(
|
|
@@ -1537,12 +1538,12 @@ async function compactConversation(messages, apiConfig, system, tools2, model) {
|
|
|
1537
1538
|
"conversation",
|
|
1538
1539
|
CONVERSATION_SUMMARY_PROMPT,
|
|
1539
1540
|
conversationMessages,
|
|
1540
|
-
system,
|
|
1541
|
-
tools2,
|
|
1542
1541
|
model
|
|
1543
1542
|
).then((text) => {
|
|
1544
1543
|
if (text) {
|
|
1545
1544
|
summaries.push({ name: "conversation", text });
|
|
1545
|
+
} else {
|
|
1546
|
+
conversationFailed = true;
|
|
1546
1547
|
}
|
|
1547
1548
|
})
|
|
1548
1549
|
);
|
|
@@ -1560,18 +1561,25 @@ async function compactConversation(messages, apiConfig, system, tools2, model) {
|
|
|
1560
1561
|
name,
|
|
1561
1562
|
SUBAGENT_SUMMARY_PROMPT,
|
|
1562
1563
|
subagentMessages,
|
|
1563
|
-
system,
|
|
1564
|
-
tools2,
|
|
1565
1564
|
model
|
|
1566
1565
|
).then((text) => {
|
|
1567
1566
|
if (text) {
|
|
1568
1567
|
summaries.push({ name, text });
|
|
1568
|
+
} else {
|
|
1569
|
+
log2.warn("Subagent summary unusable \u2014 leaving its history intact", {
|
|
1570
|
+
name
|
|
1571
|
+
});
|
|
1569
1572
|
}
|
|
1570
1573
|
})
|
|
1571
1574
|
);
|
|
1572
1575
|
}
|
|
1573
1576
|
}
|
|
1574
1577
|
await Promise.all(tasks);
|
|
1578
|
+
if (conversationFailed) {
|
|
1579
|
+
throw new Error(
|
|
1580
|
+
"Could not summarize the conversation \u2014 the model did not return a usable summary. History left intact."
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1575
1583
|
const checkpointMessages = summaries.map((s) => ({
|
|
1576
1584
|
role: "user",
|
|
1577
1585
|
hidden: true,
|
|
@@ -1716,65 +1724,106 @@ function serializeForSummary(messages) {
|
|
|
1716
1724
|
}
|
|
1717
1725
|
return lines.join("\n\n");
|
|
1718
1726
|
}
|
|
1719
|
-
async function generateSummary(apiConfig, name, compactionPrompt, messagesToSummarize,
|
|
1727
|
+
async function generateSummary(apiConfig, name, compactionPrompt, messagesToSummarize, model, opts = {}) {
|
|
1720
1728
|
const serialized = serializeForSummary(messagesToSummarize);
|
|
1721
1729
|
if (!serialized.trim()) {
|
|
1722
1730
|
return null;
|
|
1723
1731
|
}
|
|
1724
|
-
|
|
1732
|
+
const splittable = messagesToSummarize.length > 1;
|
|
1733
|
+
const allowRetry = opts.allowRetry ?? true;
|
|
1734
|
+
if (splittable && (opts.forceChunk || serialized.length > CHUNK_CHAR_LIMIT)) {
|
|
1725
1735
|
const mid = Math.floor(messagesToSummarize.length / 2);
|
|
1736
|
+
const halves = [
|
|
1737
|
+
messagesToSummarize.slice(0, mid),
|
|
1738
|
+
messagesToSummarize.slice(mid)
|
|
1739
|
+
];
|
|
1726
1740
|
log2.info("Chunking summary", {
|
|
1727
1741
|
name,
|
|
1728
1742
|
messageCount: messagesToSummarize.length,
|
|
1729
|
-
serializedLength: serialized.length
|
|
1743
|
+
serializedLength: serialized.length,
|
|
1744
|
+
forced: !!opts.forceChunk
|
|
1730
1745
|
});
|
|
1731
|
-
const
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
compactionPrompt,
|
|
1745
|
-
messagesToSummarize.slice(mid),
|
|
1746
|
-
mainSystem,
|
|
1747
|
-
mainTools,
|
|
1748
|
-
model
|
|
1746
|
+
const results = await Promise.all(
|
|
1747
|
+
halves.map(
|
|
1748
|
+
(half, i) => generateSummary(
|
|
1749
|
+
apiConfig,
|
|
1750
|
+
`${name} [pt${i + 1}]`,
|
|
1751
|
+
compactionPrompt,
|
|
1752
|
+
half,
|
|
1753
|
+
model,
|
|
1754
|
+
// A split driven by size gets its children a retry of their own. A
|
|
1755
|
+
// split that IS the retry does not, or a model that will not
|
|
1756
|
+
// summarize at any size fans out call after call before giving up.
|
|
1757
|
+
{ allowRetry: opts.forceChunk ? false : allowRetry }
|
|
1758
|
+
)
|
|
1749
1759
|
)
|
|
1750
|
-
|
|
1751
|
-
const
|
|
1760
|
+
);
|
|
1761
|
+
const lost = results.some(
|
|
1762
|
+
(r, i) => r === null && serializeForSummary(halves[i]).trim()
|
|
1763
|
+
);
|
|
1764
|
+
if (lost) {
|
|
1765
|
+
return null;
|
|
1766
|
+
}
|
|
1767
|
+
const parts = results.filter((p) => p !== null);
|
|
1752
1768
|
return parts.length > 0 ? parts.join("\n\n---\n\n") : null;
|
|
1753
1769
|
}
|
|
1754
1770
|
log2.info("Generating summary", {
|
|
1755
1771
|
name,
|
|
1756
1772
|
messageCount: messagesToSummarize.length,
|
|
1757
|
-
|
|
1773
|
+
serializedLength: serialized.length
|
|
1758
1774
|
});
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1775
|
+
const summaryText = await runSummaryCall(
|
|
1776
|
+
apiConfig,
|
|
1777
|
+
name,
|
|
1778
|
+
compactionPrompt,
|
|
1779
|
+
serialized,
|
|
1780
|
+
model
|
|
1781
|
+
);
|
|
1782
|
+
if (summaryText === null) {
|
|
1783
|
+
return null;
|
|
1784
|
+
}
|
|
1785
|
+
if (summaryText.length >= MIN_SUMMARY_CHARS) {
|
|
1786
|
+
log2.info("Summary generated", { name, summaryLength: summaryText.length });
|
|
1787
|
+
return summaryText;
|
|
1788
|
+
}
|
|
1789
|
+
log2.warn("Summary too short to be real", {
|
|
1790
|
+
name,
|
|
1791
|
+
summaryLength: summaryText.length,
|
|
1792
|
+
minimum: MIN_SUMMARY_CHARS,
|
|
1793
|
+
serializedLength: serialized.length,
|
|
1794
|
+
retrying: splittable && allowRetry
|
|
1795
|
+
});
|
|
1796
|
+
if (!splittable || !allowRetry) {
|
|
1797
|
+
return null;
|
|
1798
|
+
}
|
|
1799
|
+
return generateSummary(
|
|
1800
|
+
apiConfig,
|
|
1801
|
+
name,
|
|
1802
|
+
compactionPrompt,
|
|
1803
|
+
messagesToSummarize,
|
|
1804
|
+
model,
|
|
1805
|
+
{ forceChunk: true, allowRetry: false }
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
async function runSummaryCall(apiConfig, name, compactionPrompt, serialized, model) {
|
|
1809
|
+
const userContent = `Conversation to summarize:
|
|
1764
1810
|
|
|
1765
|
-
|
|
1811
|
+
${serialized}
|
|
1766
1812
|
|
|
1767
|
-
|
|
1813
|
+
---
|
|
1768
1814
|
|
|
1769
|
-
|
|
1815
|
+
Write the summary of the conversation above, following your instructions.`;
|
|
1816
|
+
let summaryText = "";
|
|
1770
1817
|
const iterStart = Date.now();
|
|
1771
|
-
for await (const event of
|
|
1818
|
+
for await (const event of streamChatWithRetry({
|
|
1772
1819
|
...apiConfig,
|
|
1773
1820
|
model,
|
|
1774
1821
|
subAgentId: "conversationSummarizer",
|
|
1775
|
-
system,
|
|
1822
|
+
system: compactionPrompt,
|
|
1776
1823
|
messages: [{ role: "user", content: userContent }],
|
|
1777
|
-
|
|
1824
|
+
// Always empty. With a toolset available the model picks `tool_use` over
|
|
1825
|
+
// producing a summary, leaving summaryText empty.
|
|
1826
|
+
tools: []
|
|
1778
1827
|
})) {
|
|
1779
1828
|
if (event.type === "text") {
|
|
1780
1829
|
summaryText += event.text;
|
|
@@ -1801,10 +1850,9 @@ ${serialized}` : serialized;
|
|
|
1801
1850
|
log2.warn("Empty summary generated", { name });
|
|
1802
1851
|
return null;
|
|
1803
1852
|
}
|
|
1804
|
-
log2.info("Summary generated", { name, summaryLength: summaryText.length });
|
|
1805
1853
|
return summaryText.trim();
|
|
1806
1854
|
}
|
|
1807
|
-
var log2, CONVERSATION_SUMMARY_PROMPT, SUBAGENT_SUMMARY_PROMPT, SUMMARIZABLE_SUBAGENTS, CHUNK_CHAR_LIMIT;
|
|
1855
|
+
var log2, CONVERSATION_SUMMARY_PROMPT, SUBAGENT_SUMMARY_PROMPT, SUMMARIZABLE_SUBAGENTS, CHUNK_CHAR_LIMIT, MIN_SUMMARY_CHARS;
|
|
1808
1856
|
var init_compaction = __esm({
|
|
1809
1857
|
"src/compaction/index.ts"() {
|
|
1810
1858
|
"use strict";
|
|
@@ -1817,137 +1865,8 @@ var init_compaction = __esm({
|
|
|
1817
1865
|
CONVERSATION_SUMMARY_PROMPT = readAsset("compaction", "conversation.md");
|
|
1818
1866
|
SUBAGENT_SUMMARY_PROMPT = readAsset("compaction", "subagent.md");
|
|
1819
1867
|
SUMMARIZABLE_SUBAGENTS = ["visualDesignExpert", "productVision"];
|
|
1820
|
-
CHUNK_CHAR_LIMIT =
|
|
1821
|
-
|
|
1822
|
-
});
|
|
1823
|
-
|
|
1824
|
-
// src/prompt/static/projectContext.ts
|
|
1825
|
-
import fs10 from "fs";
|
|
1826
|
-
import path4 from "path";
|
|
1827
|
-
function loadProjectManifest() {
|
|
1828
|
-
try {
|
|
1829
|
-
const manifest = fs10.readFileSync("mindstudio.json", "utf-8");
|
|
1830
|
-
return `
|
|
1831
|
-
## Project Manifest (mindstudio.json)
|
|
1832
|
-
\`\`\`json
|
|
1833
|
-
${manifest}
|
|
1834
|
-
\`\`\``;
|
|
1835
|
-
} catch {
|
|
1836
|
-
return "";
|
|
1837
|
-
}
|
|
1838
|
-
}
|
|
1839
|
-
function loadSpecFileMetadata() {
|
|
1840
|
-
try {
|
|
1841
|
-
const files = walkMdFiles("src");
|
|
1842
|
-
if (files.length === 0) {
|
|
1843
|
-
return "";
|
|
1844
|
-
}
|
|
1845
|
-
const entries = [];
|
|
1846
|
-
for (const filePath of files) {
|
|
1847
|
-
const { name, description, type } = parseFrontmatter(filePath);
|
|
1848
|
-
let line = `- ${filePath}`;
|
|
1849
|
-
if (name) {
|
|
1850
|
-
line += ` \u2014 "${name}"`;
|
|
1851
|
-
}
|
|
1852
|
-
if (type) {
|
|
1853
|
-
line += ` (${type})`;
|
|
1854
|
-
}
|
|
1855
|
-
if (description) {
|
|
1856
|
-
line += ` \u2014 ${description}`;
|
|
1857
|
-
}
|
|
1858
|
-
entries.push(line);
|
|
1859
|
-
}
|
|
1860
|
-
return `
|
|
1861
|
-
## Spec Files
|
|
1862
|
-
${entries.join("\n")}`;
|
|
1863
|
-
} catch {
|
|
1864
|
-
return "";
|
|
1865
|
-
}
|
|
1866
|
-
}
|
|
1867
|
-
function walkMdFiles(dir) {
|
|
1868
|
-
const results = [];
|
|
1869
|
-
try {
|
|
1870
|
-
const entries = fs10.readdirSync(dir, { withFileTypes: true });
|
|
1871
|
-
for (const entry of entries) {
|
|
1872
|
-
const full = path4.join(dir, entry.name);
|
|
1873
|
-
if (entry.isDirectory()) {
|
|
1874
|
-
results.push(...walkMdFiles(full));
|
|
1875
|
-
} else if (entry.name.endsWith(".md")) {
|
|
1876
|
-
results.push(full);
|
|
1877
|
-
}
|
|
1878
|
-
}
|
|
1879
|
-
} catch {
|
|
1880
|
-
}
|
|
1881
|
-
return results.sort();
|
|
1882
|
-
}
|
|
1883
|
-
function parseFrontmatter(filePath) {
|
|
1884
|
-
try {
|
|
1885
|
-
const content = fs10.readFileSync(filePath, "utf-8");
|
|
1886
|
-
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1887
|
-
if (!match) {
|
|
1888
|
-
return { name: "", description: "", type: "" };
|
|
1889
|
-
}
|
|
1890
|
-
const fm = match[1];
|
|
1891
|
-
const name = fm.match(/^name:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
1892
|
-
const description = fm.match(/^description:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
1893
|
-
const type = fm.match(/^type:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
1894
|
-
return { name, description, type };
|
|
1895
|
-
} catch {
|
|
1896
|
-
return { name: "", description: "", type: "" };
|
|
1897
|
-
}
|
|
1898
|
-
}
|
|
1899
|
-
function loadPlanStatus(onboardingState) {
|
|
1900
|
-
try {
|
|
1901
|
-
const content = fs10.readFileSync(".remy-plan.md", "utf-8");
|
|
1902
|
-
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1903
|
-
const status = match?.[1]?.match(/^status:\s*(.+)$/m)?.[1]?.trim();
|
|
1904
|
-
if (status === "pending" && onboardingState === "intake") {
|
|
1905
|
-
return `
|
|
1906
|
-
<pending_initial_plan>
|
|
1907
|
-
This is your initial plan, proposed via writePlan and awaiting the user's decision. The user approves it by pressing "Start Building" \u2014 you'll get an approveInitialPlan message when they do, and it will tell you to begin. Until then, stay in intake: keep discussing, and revise with writePlan if the user asks. Don't start building on your own, even if the user says "looks good" in chat.
|
|
1908
|
-
</pending_initial_plan>`;
|
|
1909
|
-
}
|
|
1910
|
-
if (status === "pending") {
|
|
1911
|
-
return `
|
|
1912
|
-
<pending_plan>
|
|
1913
|
-
You have a pending implementation plan in .remy-plan.md awaiting user approval. Do NOT begin implementing the plan until the user approves it. You may continue chatting, answering questions, and revising the plan if asked. To revise, call writePlan again with updated content. When the user approves the plan (via chat or any other signal), call updatePlanStatus with status "approved" before beginning any implementation work.
|
|
1914
|
-
</pending_plan>`;
|
|
1915
|
-
}
|
|
1916
|
-
if (status === "approved") {
|
|
1917
|
-
return `
|
|
1918
|
-
<approved_plan>
|
|
1919
|
-
The user has approved your implementation plan in .remy-plan.md. You may reference it during implementation. Delete the file when you have finished all planned work.
|
|
1920
|
-
</approved_plan>`;
|
|
1921
|
-
}
|
|
1922
|
-
return "";
|
|
1923
|
-
} catch {
|
|
1924
|
-
return "";
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
function loadProjectFileListing() {
|
|
1928
|
-
try {
|
|
1929
|
-
const entries = fs10.readdirSync(".", { withFileTypes: true });
|
|
1930
|
-
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
1931
|
-
if (a.isDirectory() && !b.isDirectory()) {
|
|
1932
|
-
return -1;
|
|
1933
|
-
}
|
|
1934
|
-
if (!a.isDirectory() && b.isDirectory()) {
|
|
1935
|
-
return 1;
|
|
1936
|
-
}
|
|
1937
|
-
return a.name.localeCompare(b.name);
|
|
1938
|
-
}).map((e) => e.isDirectory() ? `${e.name}/` : e.name).join("\n");
|
|
1939
|
-
return `
|
|
1940
|
-
## Project Files
|
|
1941
|
-
\`\`\`
|
|
1942
|
-
${listing}
|
|
1943
|
-
\`\`\``;
|
|
1944
|
-
} catch {
|
|
1945
|
-
return "";
|
|
1946
|
-
}
|
|
1947
|
-
}
|
|
1948
|
-
var init_projectContext = __esm({
|
|
1949
|
-
"src/prompt/static/projectContext.ts"() {
|
|
1950
|
-
"use strict";
|
|
1868
|
+
CHUNK_CHAR_LIMIT = 2e5;
|
|
1869
|
+
MIN_SUMMARY_CHARS = 400;
|
|
1951
1870
|
}
|
|
1952
1871
|
});
|
|
1953
1872
|
|
|
@@ -2111,220 +2030,430 @@ var init_surfaces = __esm({
|
|
|
2111
2030
|
}
|
|
2112
2031
|
});
|
|
2113
2032
|
|
|
2114
|
-
// src/
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
cached = await fetchRemyContext(config);
|
|
2118
|
-
const orgDefaultModels2 = filterModelPicks(cached?.defaultModels);
|
|
2119
|
-
setOrgDefaultModels(orgDefaultModels2);
|
|
2120
|
-
log3.debug("org context loaded", {
|
|
2121
|
-
delegatedAvailable: cached?.auth?.delegatedAvailable ?? false,
|
|
2122
|
-
requireDelegatedOnly: cached?.auth?.requireDelegatedOnly ?? false,
|
|
2123
|
-
hasOrgName: !!cached?.org?.name,
|
|
2124
|
-
orgDefaultModels: orgDefaultModels2
|
|
2125
|
-
});
|
|
2126
|
-
} catch (err) {
|
|
2127
|
-
cached = null;
|
|
2128
|
-
setOrgDefaultModels({});
|
|
2129
|
-
log3.debug("org context init failed", { error: err.message });
|
|
2130
|
-
}
|
|
2033
|
+
// src/automatedActions/sentinel.ts
|
|
2034
|
+
function sentinel(name) {
|
|
2035
|
+
return `@@automated::${name}@@`;
|
|
2131
2036
|
}
|
|
2132
|
-
function
|
|
2133
|
-
return
|
|
2037
|
+
function automatedMessage(name, body) {
|
|
2038
|
+
return body ? `${sentinel(name)}
|
|
2039
|
+
${body}` : sentinel(name);
|
|
2134
2040
|
}
|
|
2135
|
-
function
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
if (auth.delegatedAvailable) {
|
|
2146
|
-
lines.push(
|
|
2147
|
-
'"Sign in with Remy" (platform-delegated sign-in) is an available auth type for this app: organization members can sign in without a verification code.'
|
|
2148
|
-
);
|
|
2041
|
+
function hasSentinel(text, name) {
|
|
2042
|
+
return text.startsWith(sentinel(name));
|
|
2043
|
+
}
|
|
2044
|
+
function isAutomatedMessage(text) {
|
|
2045
|
+
return text.startsWith("@@automated::");
|
|
2046
|
+
}
|
|
2047
|
+
function parseSentinel(text) {
|
|
2048
|
+
const match = text.match(/^@@automated::(\w+)@@(.*)/s);
|
|
2049
|
+
if (!match) {
|
|
2050
|
+
return null;
|
|
2149
2051
|
}
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2052
|
+
return { name: match[1], remainder: match[2] };
|
|
2053
|
+
}
|
|
2054
|
+
function stripSentinelLine(text) {
|
|
2055
|
+
return text.replace(/^@@automated::[^@]*@@[^\n]*\n?/, "");
|
|
2056
|
+
}
|
|
2057
|
+
function buildBackgroundResultsMessage(results) {
|
|
2058
|
+
const xml = results.map(
|
|
2059
|
+
(r) => `<tool_result id="${r.toolCallId}" name="${r.name}">
|
|
2060
|
+
${r.result}
|
|
2061
|
+
</tool_result>`
|
|
2062
|
+
).join("\n\n");
|
|
2063
|
+
const plural = results.length > 1 ? "s" : "";
|
|
2064
|
+
const body = `This is an automated message containing the result${plural} of ${results.length > 1 ? "tool calls" : "a tool call"} that ${results.length > 1 ? "have" : "has"} been working in the background. This is not a direct message from the user.
|
|
2065
|
+
<background_results>
|
|
2066
|
+
${xml}
|
|
2067
|
+
</background_results>`;
|
|
2068
|
+
return automatedMessage("background_results", body);
|
|
2069
|
+
}
|
|
2070
|
+
function mergeBackgroundResultsMessages(messages) {
|
|
2071
|
+
const results = [];
|
|
2072
|
+
const toolRe = /<tool_result id="([^"]+)" name="([^"]+)">\n([\s\S]*?)\n<\/tool_result>/g;
|
|
2073
|
+
for (const msg of messages) {
|
|
2074
|
+
for (const m of msg.matchAll(toolRe)) {
|
|
2075
|
+
results.push({ toolCallId: m[1], name: m[2], result: m[3] });
|
|
2076
|
+
}
|
|
2154
2077
|
}
|
|
2155
|
-
|
|
2156
|
-
return lines.join("\n");
|
|
2078
|
+
return buildBackgroundResultsMessage(results);
|
|
2157
2079
|
}
|
|
2158
|
-
var
|
|
2159
|
-
|
|
2160
|
-
"src/orgContext.ts"() {
|
|
2080
|
+
var init_sentinel = __esm({
|
|
2081
|
+
"src/automatedActions/sentinel.ts"() {
|
|
2161
2082
|
"use strict";
|
|
2162
|
-
init_api();
|
|
2163
|
-
init_surfaces();
|
|
2164
|
-
init_logger();
|
|
2165
|
-
log3 = createLogger("orgContext");
|
|
2166
|
-
cached = null;
|
|
2167
2083
|
}
|
|
2168
2084
|
});
|
|
2169
2085
|
|
|
2170
|
-
// src/
|
|
2171
|
-
function
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2086
|
+
// src/subagents/common/cleanMessages.ts
|
|
2087
|
+
function findLastSummaryCheckpoint(messages, name) {
|
|
2088
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2089
|
+
const msg = messages[i];
|
|
2090
|
+
if (!Array.isArray(msg.content)) {
|
|
2091
|
+
continue;
|
|
2092
|
+
}
|
|
2093
|
+
for (const block of msg.content) {
|
|
2094
|
+
if (block.type === "summary" && block.name === name) {
|
|
2095
|
+
return i;
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
return -1;
|
|
2177
2100
|
}
|
|
2178
|
-
function
|
|
2179
|
-
const
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
{{static/intake.md}}
|
|
2260
|
-
</intake_mode_instructions>
|
|
2261
|
-
|
|
2262
|
-
<spec_authoring_instructions>
|
|
2263
|
-
{{static/authoring.md}}
|
|
2264
|
-
</spec_authoring_instructions>
|
|
2265
|
-
|
|
2266
|
-
<team>
|
|
2267
|
-
{{static/team.md}}
|
|
2268
|
-
</team>
|
|
2269
|
-
|
|
2270
|
-
<code_authoring_instructions>
|
|
2271
|
-
{{static/coding.md}}
|
|
2272
|
-
|
|
2273
|
-
<typescript_lsp>
|
|
2274
|
-
{{static/lsp.md}}
|
|
2275
|
-
</typescript_lsp>
|
|
2276
|
-
</code_authoring_instructions>
|
|
2277
|
-
|
|
2278
|
-
<conversation_summaries>
|
|
2279
|
-
Your conversation history may include <prior_conversation_summary> blocks in the user's messages. These are automated summaries of earlier messages that have been compacted to save context space. The user does not see this summary, they see the full conversation history in their UI. Treat the summary as ground truth for what happened before, but do not reference it directly to the user ("as mentioned in the summary..."). Just continue naturally as if you remember the prior work.
|
|
2280
|
-
|
|
2281
|
-
Old tool results are periodically cleared from the conversation to save context space. This is automatic and expected \u2014 you don't need to note down or preserve information from tool results. If you need to reference something from an earlier tool call, just re-read the file or re-run the query.
|
|
2282
|
-
</conversation_summaries>
|
|
2283
|
-
|
|
2284
|
-
<project_onboarding>
|
|
2285
|
-
New projects progress through three onboarding states. The user might skip this entirely and jump straight into working on the existing scaffold (which defaults to onboardingFinished), but ideally new projects move through each phase:
|
|
2286
|
-
|
|
2287
|
-
- **intake**: Gathering requirements. The project has scaffold code (a "hello world" starter) but it's not the user's app yet. Focus on understanding what they want to build, not on the existing code. Intake ends with a plan proposal via writePlan.
|
|
2288
|
-
- **building**: The user approved the initial plan. The agent is writing the spec and building the app. This can take a while and involves heavy tool use (spec authoring, design expert consultation, code generation, verification, polishing).
|
|
2289
|
-
- **buildComplete**: The build is done. The frontend is showing the user a reveal experience (pitch deck, app preview). The agent does not need to do anything in this state; the frontend advances to onboardingFinished when the user is ready.
|
|
2290
|
-
- **onboardingFinished**: The project is built and ready. Full development mode with all tools available. From here on, keep spec and code in sync as changes are made.
|
|
2291
|
-
</project_onboarding>
|
|
2292
|
-
|
|
2293
|
-
{{static/instructions.md}}
|
|
2294
|
-
|
|
2295
|
-
<!-- cache_breakpoint -->
|
|
2296
|
-
|
|
2297
|
-
<current_project_onboarding_state>
|
|
2298
|
-
${onboardingState ?? "onboardingFinished"}
|
|
2299
|
-
</current_project_onboarding_state>
|
|
2101
|
+
function fixOrphanedToolCalls(messages) {
|
|
2102
|
+
const toolResultIds = /* @__PURE__ */ new Set();
|
|
2103
|
+
for (const msg of messages) {
|
|
2104
|
+
if (msg.role === "user" && msg.toolCallId) {
|
|
2105
|
+
toolResultIds.add(msg.toolCallId);
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
const result = [];
|
|
2109
|
+
for (const msg of messages) {
|
|
2110
|
+
result.push(msg);
|
|
2111
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
|
|
2112
|
+
continue;
|
|
2113
|
+
}
|
|
2114
|
+
const toolBlocks = msg.content.filter(
|
|
2115
|
+
(b) => b.type === "tool"
|
|
2116
|
+
);
|
|
2117
|
+
const orphans = toolBlocks.filter((tc) => !toolResultIds.has(tc.id));
|
|
2118
|
+
for (const tc of orphans) {
|
|
2119
|
+
result.push({
|
|
2120
|
+
role: "user",
|
|
2121
|
+
content: "Error: tool result lost (session recovered)",
|
|
2122
|
+
toolCallId: tc.id,
|
|
2123
|
+
isToolError: true
|
|
2124
|
+
});
|
|
2125
|
+
toolResultIds.add(tc.id);
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
return result;
|
|
2129
|
+
}
|
|
2130
|
+
function cleanMessagesForApi(messages) {
|
|
2131
|
+
const checkpointIdx = findLastSummaryCheckpoint(messages, "conversation");
|
|
2132
|
+
let startIdx = 0;
|
|
2133
|
+
const prefix = [];
|
|
2134
|
+
if (checkpointIdx !== -1) {
|
|
2135
|
+
const checkpointMsg = messages[checkpointIdx];
|
|
2136
|
+
const blocks = checkpointMsg.content;
|
|
2137
|
+
const summaryBlock = blocks.find(
|
|
2138
|
+
(b) => b.type === "summary" && b.name === "conversation"
|
|
2139
|
+
);
|
|
2140
|
+
if (summaryBlock && summaryBlock.type === "summary") {
|
|
2141
|
+
prefix.push({
|
|
2142
|
+
role: "user",
|
|
2143
|
+
content: `<conversation_summary>
|
|
2144
|
+
${summaryBlock.text}
|
|
2145
|
+
</conversation_summary>`,
|
|
2146
|
+
hidden: true
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
startIdx = checkpointIdx + 1;
|
|
2150
|
+
}
|
|
2151
|
+
const messagesToProcess = fixOrphanedToolCalls(messages.slice(startIdx));
|
|
2152
|
+
const toolUseIds = /* @__PURE__ */ new Set();
|
|
2153
|
+
for (const msg of messagesToProcess) {
|
|
2154
|
+
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
2155
|
+
for (const block of msg.content) {
|
|
2156
|
+
if (block.type === "tool") {
|
|
2157
|
+
toolUseIds.add(block.id);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
const cleaned = messagesToProcess.filter((msg) => {
|
|
2163
|
+
if (Array.isArray(msg.content)) {
|
|
2164
|
+
const blocks = msg.content;
|
|
2165
|
+
if (blocks.some((b) => b.type === "summary")) {
|
|
2166
|
+
return false;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
if (msg.role === "assistant" && Array.isArray(msg.content) && msg.content.length === 0) {
|
|
2170
|
+
return false;
|
|
2171
|
+
}
|
|
2172
|
+
if (msg.role === "user" && msg.toolCallId && !toolUseIds.has(msg.toolCallId)) {
|
|
2173
|
+
return false;
|
|
2174
|
+
}
|
|
2175
|
+
return true;
|
|
2176
|
+
}).map((msg) => {
|
|
2177
|
+
if (msg.role === "user" && typeof msg.content === "string") {
|
|
2178
|
+
const { attachmentHeader, ...rest } = msg;
|
|
2179
|
+
let content = isAutomatedMessage(msg.content) ? stripSentinelLine(msg.content) : msg.content;
|
|
2180
|
+
if (attachmentHeader) {
|
|
2181
|
+
content = content ? `${attachmentHeader}
|
|
2300
2182
|
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2183
|
+
${content}` : attachmentHeader;
|
|
2184
|
+
}
|
|
2185
|
+
return { ...rest, content };
|
|
2186
|
+
}
|
|
2187
|
+
if (!Array.isArray(msg.content)) {
|
|
2188
|
+
return msg;
|
|
2189
|
+
}
|
|
2190
|
+
const blocks = msg.content;
|
|
2191
|
+
const text = blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
2192
|
+
const toolCalls = blocks.filter((b) => b.type === "tool").map((b) => ({ id: b.id, name: b.name, input: b.input }));
|
|
2193
|
+
const cleaned2 = {
|
|
2194
|
+
role: msg.role,
|
|
2195
|
+
content: text
|
|
2196
|
+
};
|
|
2197
|
+
if (toolCalls.length > 0) {
|
|
2198
|
+
cleaned2.toolCalls = toolCalls;
|
|
2199
|
+
}
|
|
2200
|
+
if (msg.providerMetadata) {
|
|
2201
|
+
cleaned2.providerMetadata = msg.providerMetadata;
|
|
2202
|
+
}
|
|
2203
|
+
if (msg.hidden) {
|
|
2204
|
+
cleaned2.hidden = true;
|
|
2205
|
+
}
|
|
2206
|
+
return cleaned2;
|
|
2207
|
+
});
|
|
2208
|
+
return [...prefix, ...cleaned];
|
|
2209
|
+
}
|
|
2210
|
+
var init_cleanMessages = __esm({
|
|
2211
|
+
"src/subagents/common/cleanMessages.ts"() {
|
|
2212
|
+
"use strict";
|
|
2213
|
+
init_sentinel();
|
|
2214
|
+
}
|
|
2215
|
+
});
|
|
2304
2216
|
|
|
2305
|
-
|
|
2217
|
+
// src/toolResultCap.ts
|
|
2218
|
+
function capToolResult(result) {
|
|
2219
|
+
const total = Buffer.byteLength(result, "utf-8");
|
|
2220
|
+
if (total <= MAX_TOOL_RESULT_BYTES) {
|
|
2221
|
+
return result;
|
|
2222
|
+
}
|
|
2223
|
+
const head = Buffer.from(result, "utf-8").subarray(0, MAX_TOOL_RESULT_BYTES).toString("utf-8");
|
|
2224
|
+
return head + `
|
|
2306
2225
|
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2226
|
+
(tool result truncated at ${(MAX_TOOL_RESULT_BYTES / 1024).toFixed(0)}KB of ${(total / 1024).toFixed(0)}KB \u2014 too large to keep in context. Narrow the call (select fewer fields, paginate, or query a subset) instead of fetching everything.)`;
|
|
2227
|
+
}
|
|
2228
|
+
var MAX_TOOL_RESULT_BYTES;
|
|
2229
|
+
var init_toolResultCap = __esm({
|
|
2230
|
+
"src/toolResultCap.ts"() {
|
|
2231
|
+
"use strict";
|
|
2232
|
+
MAX_TOOL_RESULT_BYTES = 256 * 1024;
|
|
2233
|
+
}
|
|
2234
|
+
});
|
|
2311
2235
|
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2236
|
+
// src/session.ts
|
|
2237
|
+
import fs10 from "fs";
|
|
2238
|
+
import path4 from "path";
|
|
2239
|
+
function loadSession(state) {
|
|
2240
|
+
pruneArchives();
|
|
2241
|
+
try {
|
|
2242
|
+
const raw = fs10.readFileSync(SESSION_FILE, "utf-8");
|
|
2243
|
+
const data = JSON.parse(raw);
|
|
2244
|
+
if (data.models && typeof data.models === "object") {
|
|
2245
|
+
state.models = data.models;
|
|
2246
|
+
}
|
|
2247
|
+
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
2248
|
+
state.messages = sanitizeMessages(data.messages);
|
|
2249
|
+
log3.info("Session loaded", {
|
|
2250
|
+
messageCount: state.messages.length,
|
|
2251
|
+
...state.models && { models: state.models }
|
|
2252
|
+
});
|
|
2253
|
+
return true;
|
|
2254
|
+
}
|
|
2255
|
+
} catch {
|
|
2256
|
+
}
|
|
2257
|
+
return false;
|
|
2258
|
+
}
|
|
2259
|
+
function capOversizedResults(msg) {
|
|
2260
|
+
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
2261
|
+
for (const block of msg.content) {
|
|
2262
|
+
if (block.type === "tool" && typeof block.result === "string") {
|
|
2263
|
+
block.result = capToolResult(block.result);
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
} else if (msg.role === "user" && msg.toolCallId && typeof msg.content === "string") {
|
|
2267
|
+
msg.content = capToolResult(msg.content);
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
function sanitizeMessages(messages) {
|
|
2271
|
+
const result = [];
|
|
2272
|
+
for (let i = 0; i < messages.length; i++) {
|
|
2273
|
+
const msg = messages[i];
|
|
2274
|
+
capOversizedResults(msg);
|
|
2275
|
+
result.push(msg);
|
|
2276
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
|
|
2277
|
+
continue;
|
|
2278
|
+
}
|
|
2279
|
+
const toolBlocks = msg.content.filter(
|
|
2280
|
+
(b) => b.type === "tool"
|
|
2281
|
+
);
|
|
2282
|
+
if (toolBlocks.length === 0) {
|
|
2283
|
+
continue;
|
|
2284
|
+
}
|
|
2285
|
+
const resultIds = /* @__PURE__ */ new Set();
|
|
2286
|
+
for (let j = i + 1; j < messages.length; j++) {
|
|
2287
|
+
const next = messages[j];
|
|
2288
|
+
if (next.role === "user" && next.toolCallId) {
|
|
2289
|
+
resultIds.add(next.toolCallId);
|
|
2290
|
+
} else {
|
|
2291
|
+
break;
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
for (const tc of toolBlocks) {
|
|
2295
|
+
if (!resultIds.has(tc.id)) {
|
|
2296
|
+
result.push({
|
|
2297
|
+
role: "user",
|
|
2298
|
+
content: "Error: tool result lost (session recovered)",
|
|
2299
|
+
toolCallId: tc.id,
|
|
2300
|
+
isToolError: true
|
|
2301
|
+
});
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
return result;
|
|
2306
|
+
}
|
|
2307
|
+
function buildPayload(state) {
|
|
2308
|
+
const payload = { messages: state.messages };
|
|
2309
|
+
if (state.models && Object.keys(state.models).length > 0) {
|
|
2310
|
+
payload.models = state.models;
|
|
2311
|
+
}
|
|
2312
|
+
return payload;
|
|
2313
|
+
}
|
|
2314
|
+
function archiveMessages(messages, label, models) {
|
|
2315
|
+
fs10.mkdirSync(ARCHIVE_DIR, { recursive: true });
|
|
2316
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2317
|
+
let dest = path4.join(ARCHIVE_DIR, `${label}-${ts}.json`);
|
|
2318
|
+
let n = 1;
|
|
2319
|
+
while (fs10.existsSync(dest)) {
|
|
2320
|
+
dest = path4.join(ARCHIVE_DIR, `${label}-${ts}-${n++}.json`);
|
|
2321
|
+
}
|
|
2322
|
+
const payload = { messages };
|
|
2323
|
+
if (models && Object.keys(models).length > 0) {
|
|
2324
|
+
payload.models = models;
|
|
2325
|
+
}
|
|
2326
|
+
fs10.writeFileSync(dest, JSON.stringify(payload), "utf-8");
|
|
2327
|
+
log3.info("Session archived", { label, dest, messageCount: messages.length });
|
|
2328
|
+
pruneArchives();
|
|
2329
|
+
return dest;
|
|
2330
|
+
}
|
|
2331
|
+
function pruneArchives() {
|
|
2332
|
+
try {
|
|
2333
|
+
const entries = fs10.readdirSync(ARCHIVE_DIR).filter((name) => /^(cleared|rotated)-.*\.json$/.test(name));
|
|
2334
|
+
if (entries.length <= 1) {
|
|
2335
|
+
return;
|
|
2336
|
+
}
|
|
2337
|
+
const sortKey = (name) => name.replace(/^(cleared|rotated)-/, "");
|
|
2338
|
+
const archives = entries.map((name) => ({
|
|
2339
|
+
name,
|
|
2340
|
+
size: fs10.statSync(path4.join(ARCHIVE_DIR, name)).size
|
|
2341
|
+
})).sort((a, b) => sortKey(b.name).localeCompare(sortKey(a.name)));
|
|
2342
|
+
let kept = 0;
|
|
2343
|
+
let cut = archives.length;
|
|
2344
|
+
for (let i = 0; i < archives.length; i++) {
|
|
2345
|
+
if (i === 0 || kept + archives[i].size <= ARCHIVE_RETENTION_BYTES) {
|
|
2346
|
+
kept += archives[i].size;
|
|
2347
|
+
} else {
|
|
2348
|
+
cut = i;
|
|
2349
|
+
break;
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
let removed = 0;
|
|
2353
|
+
let freed = 0;
|
|
2354
|
+
for (let i = cut; i < archives.length; i++) {
|
|
2355
|
+
try {
|
|
2356
|
+
fs10.unlinkSync(path4.join(ARCHIVE_DIR, archives[i].name));
|
|
2357
|
+
freed += archives[i].size;
|
|
2358
|
+
removed++;
|
|
2359
|
+
} catch {
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
if (removed > 0) {
|
|
2363
|
+
log3.info("Session archives pruned", {
|
|
2364
|
+
removed,
|
|
2365
|
+
freedBytes: freed,
|
|
2366
|
+
keptBytes: kept
|
|
2367
|
+
});
|
|
2368
|
+
}
|
|
2369
|
+
} catch {
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
function rotate(state) {
|
|
2373
|
+
const messages = state.messages;
|
|
2374
|
+
if (messages.length === 0) {
|
|
2375
|
+
return false;
|
|
2376
|
+
}
|
|
2377
|
+
let tailBytes = 0;
|
|
2378
|
+
let scrollbackStart = 0;
|
|
2379
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2380
|
+
tailBytes += Buffer.byteLength(JSON.stringify(messages[i]), "utf-8") + 1;
|
|
2381
|
+
if (tailBytes >= RETAIN_TAIL_BYTES) {
|
|
2382
|
+
scrollbackStart = i;
|
|
2383
|
+
break;
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
const checkpointIdx = findLastSummaryCheckpoint(messages, "conversation");
|
|
2387
|
+
let cut = checkpointIdx === -1 ? scrollbackStart : Math.min(scrollbackStart, checkpointIdx);
|
|
2388
|
+
cut = findSafeInsertionPoint(messages, cut);
|
|
2389
|
+
if (cut <= 0) {
|
|
2390
|
+
return false;
|
|
2391
|
+
}
|
|
2392
|
+
archiveMessages(messages.slice(0, cut), "rotated", state.models);
|
|
2393
|
+
state.messages = messages.slice(cut);
|
|
2394
|
+
log3.info("Session rotated", {
|
|
2395
|
+
archived: cut,
|
|
2396
|
+
retained: state.messages.length
|
|
2397
|
+
});
|
|
2398
|
+
return true;
|
|
2315
2399
|
}
|
|
2316
|
-
|
|
2317
|
-
|
|
2400
|
+
function saveSession(state) {
|
|
2401
|
+
try {
|
|
2402
|
+
let serialized = JSON.stringify(buildPayload(state));
|
|
2403
|
+
if (Buffer.byteLength(serialized, "utf-8") > ROTATE_THRESHOLD_BYTES && rotate(state)) {
|
|
2404
|
+
serialized = JSON.stringify(buildPayload(state));
|
|
2405
|
+
}
|
|
2406
|
+
fs10.writeFileSync(SESSION_FILE, serialized, "utf-8");
|
|
2407
|
+
log3.info("Session saved", { messageCount: state.messages.length });
|
|
2408
|
+
} catch (err) {
|
|
2409
|
+
log3.warn("Session save failed", { error: err.message });
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
function clearSession(state) {
|
|
2413
|
+
try {
|
|
2414
|
+
if (state.messages.length > 0) {
|
|
2415
|
+
archiveMessages(state.messages, "cleared", state.models);
|
|
2416
|
+
}
|
|
2417
|
+
} catch (err) {
|
|
2418
|
+
log3.warn("Session archive on clear failed", { error: err.message });
|
|
2419
|
+
}
|
|
2420
|
+
state.messages = [];
|
|
2421
|
+
try {
|
|
2422
|
+
if (fs10.existsSync(SESSION_FILE)) {
|
|
2423
|
+
fs10.unlinkSync(SESSION_FILE);
|
|
2424
|
+
}
|
|
2425
|
+
} catch (err) {
|
|
2426
|
+
log3.warn("Session clear: could not remove live file", {
|
|
2427
|
+
error: err.message
|
|
2428
|
+
});
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
var log3, SESSION_FILE, ARCHIVE_DIR, ROTATE_THRESHOLD_BYTES, RETAIN_TAIL_BYTES, ARCHIVE_RETENTION_BYTES;
|
|
2432
|
+
var init_session = __esm({
|
|
2433
|
+
"src/session.ts"() {
|
|
2318
2434
|
"use strict";
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2435
|
+
init_logger();
|
|
2436
|
+
init_compaction();
|
|
2437
|
+
init_cleanMessages();
|
|
2438
|
+
init_toolResultCap();
|
|
2439
|
+
log3 = createLogger("session");
|
|
2440
|
+
SESSION_FILE = ".remy-session.json";
|
|
2441
|
+
ARCHIVE_DIR = ".logs/sessions";
|
|
2442
|
+
ROTATE_THRESHOLD_BYTES = 32 * 1024 * 1024;
|
|
2443
|
+
RETAIN_TAIL_BYTES = 16 * 1024 * 1024;
|
|
2444
|
+
ARCHIVE_RETENTION_BYTES = 64 * 1024 * 1024;
|
|
2322
2445
|
}
|
|
2323
2446
|
});
|
|
2324
2447
|
|
|
2325
2448
|
// src/compaction/trigger.ts
|
|
2326
|
-
function
|
|
2327
|
-
|
|
2449
|
+
function applyPendingSummaries(state) {
|
|
2450
|
+
const summaries = pendingSummaries.splice(0);
|
|
2451
|
+
if (summaries.length === 0) {
|
|
2452
|
+
return;
|
|
2453
|
+
}
|
|
2454
|
+
const idx = findSafeInsertionPoint(state.messages);
|
|
2455
|
+
state.messages.splice(idx, 0, ...summaries);
|
|
2456
|
+
saveSession(state);
|
|
2328
2457
|
}
|
|
2329
2458
|
function setCompactionListener(l) {
|
|
2330
2459
|
listener = l;
|
|
@@ -2335,13 +2464,9 @@ function triggerCompaction(state, apiConfig, opts = {}) {
|
|
|
2335
2464
|
}
|
|
2336
2465
|
const { blocking = false, requestId, model } = opts;
|
|
2337
2466
|
listener?.({ type: "started", blocking, requestId });
|
|
2338
|
-
const system = buildSystemPrompt("onboardingFinished");
|
|
2339
|
-
const tools2 = getToolDefinitions("onboardingFinished");
|
|
2340
2467
|
inflightCompaction = compactConversation(
|
|
2341
2468
|
state.messages,
|
|
2342
2469
|
apiConfig,
|
|
2343
|
-
system,
|
|
2344
|
-
tools2,
|
|
2345
2470
|
resolveModel("conversationSummarizer", state.models, model)
|
|
2346
2471
|
).then((summaries) => {
|
|
2347
2472
|
pendingSummaries.push(...summaries);
|
|
@@ -2362,10 +2487,9 @@ var init_trigger = __esm({
|
|
|
2362
2487
|
"src/compaction/trigger.ts"() {
|
|
2363
2488
|
"use strict";
|
|
2364
2489
|
init_compaction();
|
|
2365
|
-
init_prompt();
|
|
2366
|
-
init_tools8();
|
|
2367
2490
|
init_logger();
|
|
2368
2491
|
init_surfaces();
|
|
2492
|
+
init_session();
|
|
2369
2493
|
log4 = createLogger("compaction:trigger");
|
|
2370
2494
|
pendingSummaries = [];
|
|
2371
2495
|
inflightCompaction = null;
|
|
@@ -3604,25 +3728,6 @@ ${partial}` : "[INTERRUPTED] Tool execution was stopped.";
|
|
|
3604
3728
|
}
|
|
3605
3729
|
});
|
|
3606
3730
|
|
|
3607
|
-
// src/toolResultCap.ts
|
|
3608
|
-
function capToolResult(result) {
|
|
3609
|
-
const total = Buffer.byteLength(result, "utf-8");
|
|
3610
|
-
if (total <= MAX_TOOL_RESULT_BYTES) {
|
|
3611
|
-
return result;
|
|
3612
|
-
}
|
|
3613
|
-
const head = Buffer.from(result, "utf-8").subarray(0, MAX_TOOL_RESULT_BYTES).toString("utf-8");
|
|
3614
|
-
return head + `
|
|
3615
|
-
|
|
3616
|
-
(tool result truncated at ${(MAX_TOOL_RESULT_BYTES / 1024).toFixed(0)}KB of ${(total / 1024).toFixed(0)}KB \u2014 too large to keep in context. Narrow the call (select fewer fields, paginate, or query a subset) instead of fetching everything.)`;
|
|
3617
|
-
}
|
|
3618
|
-
var MAX_TOOL_RESULT_BYTES;
|
|
3619
|
-
var init_toolResultCap = __esm({
|
|
3620
|
-
"src/toolResultCap.ts"() {
|
|
3621
|
-
"use strict";
|
|
3622
|
-
MAX_TOOL_RESULT_BYTES = 256 * 1024;
|
|
3623
|
-
}
|
|
3624
|
-
});
|
|
3625
|
-
|
|
3626
3731
|
// src/statusWatcher.ts
|
|
3627
3732
|
function startStatusWatcher(config) {
|
|
3628
3733
|
const { apiConfig, getContext, onStatus, interval = 5e3, signal } = config;
|
|
@@ -3679,195 +3784,11 @@ function startStatusWatcher(config) {
|
|
|
3679
3784
|
resume() {
|
|
3680
3785
|
pauseCount = Math.max(0, pauseCount - 1);
|
|
3681
3786
|
}
|
|
3682
|
-
};
|
|
3683
|
-
}
|
|
3684
|
-
var init_statusWatcher = __esm({
|
|
3685
|
-
"src/statusWatcher.ts"() {
|
|
3686
|
-
"use strict";
|
|
3687
|
-
}
|
|
3688
|
-
});
|
|
3689
|
-
|
|
3690
|
-
// src/automatedActions/sentinel.ts
|
|
3691
|
-
function sentinel(name) {
|
|
3692
|
-
return `@@automated::${name}@@`;
|
|
3693
|
-
}
|
|
3694
|
-
function automatedMessage(name, body) {
|
|
3695
|
-
return body ? `${sentinel(name)}
|
|
3696
|
-
${body}` : sentinel(name);
|
|
3697
|
-
}
|
|
3698
|
-
function hasSentinel(text, name) {
|
|
3699
|
-
return text.startsWith(sentinel(name));
|
|
3700
|
-
}
|
|
3701
|
-
function isAutomatedMessage(text) {
|
|
3702
|
-
return text.startsWith("@@automated::");
|
|
3703
|
-
}
|
|
3704
|
-
function parseSentinel(text) {
|
|
3705
|
-
const match = text.match(/^@@automated::(\w+)@@(.*)/s);
|
|
3706
|
-
if (!match) {
|
|
3707
|
-
return null;
|
|
3708
|
-
}
|
|
3709
|
-
return { name: match[1], remainder: match[2] };
|
|
3710
|
-
}
|
|
3711
|
-
function stripSentinelLine(text) {
|
|
3712
|
-
return text.replace(/^@@automated::[^@]*@@[^\n]*\n?/, "");
|
|
3713
|
-
}
|
|
3714
|
-
function buildBackgroundResultsMessage(results) {
|
|
3715
|
-
const xml = results.map(
|
|
3716
|
-
(r) => `<tool_result id="${r.toolCallId}" name="${r.name}">
|
|
3717
|
-
${r.result}
|
|
3718
|
-
</tool_result>`
|
|
3719
|
-
).join("\n\n");
|
|
3720
|
-
const plural = results.length > 1 ? "s" : "";
|
|
3721
|
-
const body = `This is an automated message containing the result${plural} of ${results.length > 1 ? "tool calls" : "a tool call"} that ${results.length > 1 ? "have" : "has"} been working in the background. This is not a direct message from the user.
|
|
3722
|
-
<background_results>
|
|
3723
|
-
${xml}
|
|
3724
|
-
</background_results>`;
|
|
3725
|
-
return automatedMessage("background_results", body);
|
|
3726
|
-
}
|
|
3727
|
-
function mergeBackgroundResultsMessages(messages) {
|
|
3728
|
-
const results = [];
|
|
3729
|
-
const toolRe = /<tool_result id="([^"]+)" name="([^"]+)">\n([\s\S]*?)\n<\/tool_result>/g;
|
|
3730
|
-
for (const msg of messages) {
|
|
3731
|
-
for (const m of msg.matchAll(toolRe)) {
|
|
3732
|
-
results.push({ toolCallId: m[1], name: m[2], result: m[3] });
|
|
3733
|
-
}
|
|
3734
|
-
}
|
|
3735
|
-
return buildBackgroundResultsMessage(results);
|
|
3736
|
-
}
|
|
3737
|
-
var init_sentinel = __esm({
|
|
3738
|
-
"src/automatedActions/sentinel.ts"() {
|
|
3739
|
-
"use strict";
|
|
3740
|
-
}
|
|
3741
|
-
});
|
|
3742
|
-
|
|
3743
|
-
// src/subagents/common/cleanMessages.ts
|
|
3744
|
-
function findLastSummaryCheckpoint(messages, name) {
|
|
3745
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
3746
|
-
const msg = messages[i];
|
|
3747
|
-
if (!Array.isArray(msg.content)) {
|
|
3748
|
-
continue;
|
|
3749
|
-
}
|
|
3750
|
-
for (const block of msg.content) {
|
|
3751
|
-
if (block.type === "summary" && block.name === name) {
|
|
3752
|
-
return i;
|
|
3753
|
-
}
|
|
3754
|
-
}
|
|
3755
|
-
}
|
|
3756
|
-
return -1;
|
|
3757
|
-
}
|
|
3758
|
-
function fixOrphanedToolCalls(messages) {
|
|
3759
|
-
const toolResultIds = /* @__PURE__ */ new Set();
|
|
3760
|
-
for (const msg of messages) {
|
|
3761
|
-
if (msg.role === "user" && msg.toolCallId) {
|
|
3762
|
-
toolResultIds.add(msg.toolCallId);
|
|
3763
|
-
}
|
|
3764
|
-
}
|
|
3765
|
-
const result = [];
|
|
3766
|
-
for (const msg of messages) {
|
|
3767
|
-
result.push(msg);
|
|
3768
|
-
if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
|
|
3769
|
-
continue;
|
|
3770
|
-
}
|
|
3771
|
-
const toolBlocks = msg.content.filter(
|
|
3772
|
-
(b) => b.type === "tool"
|
|
3773
|
-
);
|
|
3774
|
-
const orphans = toolBlocks.filter((tc) => !toolResultIds.has(tc.id));
|
|
3775
|
-
for (const tc of orphans) {
|
|
3776
|
-
result.push({
|
|
3777
|
-
role: "user",
|
|
3778
|
-
content: "Error: tool result lost (session recovered)",
|
|
3779
|
-
toolCallId: tc.id,
|
|
3780
|
-
isToolError: true
|
|
3781
|
-
});
|
|
3782
|
-
toolResultIds.add(tc.id);
|
|
3783
|
-
}
|
|
3784
|
-
}
|
|
3785
|
-
return result;
|
|
3786
|
-
}
|
|
3787
|
-
function cleanMessagesForApi(messages) {
|
|
3788
|
-
const checkpointIdx = findLastSummaryCheckpoint(messages, "conversation");
|
|
3789
|
-
let startIdx = 0;
|
|
3790
|
-
const prefix = [];
|
|
3791
|
-
if (checkpointIdx !== -1) {
|
|
3792
|
-
const checkpointMsg = messages[checkpointIdx];
|
|
3793
|
-
const blocks = checkpointMsg.content;
|
|
3794
|
-
const summaryBlock = blocks.find(
|
|
3795
|
-
(b) => b.type === "summary" && b.name === "conversation"
|
|
3796
|
-
);
|
|
3797
|
-
if (summaryBlock && summaryBlock.type === "summary") {
|
|
3798
|
-
prefix.push({
|
|
3799
|
-
role: "user",
|
|
3800
|
-
content: `<conversation_summary>
|
|
3801
|
-
${summaryBlock.text}
|
|
3802
|
-
</conversation_summary>`,
|
|
3803
|
-
hidden: true
|
|
3804
|
-
});
|
|
3805
|
-
}
|
|
3806
|
-
startIdx = checkpointIdx + 1;
|
|
3807
|
-
}
|
|
3808
|
-
const messagesToProcess = fixOrphanedToolCalls(messages.slice(startIdx));
|
|
3809
|
-
const toolUseIds = /* @__PURE__ */ new Set();
|
|
3810
|
-
for (const msg of messagesToProcess) {
|
|
3811
|
-
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
3812
|
-
for (const block of msg.content) {
|
|
3813
|
-
if (block.type === "tool") {
|
|
3814
|
-
toolUseIds.add(block.id);
|
|
3815
|
-
}
|
|
3816
|
-
}
|
|
3817
|
-
}
|
|
3818
|
-
}
|
|
3819
|
-
const cleaned = messagesToProcess.filter((msg) => {
|
|
3820
|
-
if (Array.isArray(msg.content)) {
|
|
3821
|
-
const blocks = msg.content;
|
|
3822
|
-
if (blocks.some((b) => b.type === "summary")) {
|
|
3823
|
-
return false;
|
|
3824
|
-
}
|
|
3825
|
-
}
|
|
3826
|
-
if (msg.role === "assistant" && Array.isArray(msg.content) && msg.content.length === 0) {
|
|
3827
|
-
return false;
|
|
3828
|
-
}
|
|
3829
|
-
if (msg.role === "user" && msg.toolCallId && !toolUseIds.has(msg.toolCallId)) {
|
|
3830
|
-
return false;
|
|
3831
|
-
}
|
|
3832
|
-
return true;
|
|
3833
|
-
}).map((msg) => {
|
|
3834
|
-
if (msg.role === "user" && typeof msg.content === "string") {
|
|
3835
|
-
const { attachmentHeader, ...rest } = msg;
|
|
3836
|
-
let content = isAutomatedMessage(msg.content) ? stripSentinelLine(msg.content) : msg.content;
|
|
3837
|
-
if (attachmentHeader) {
|
|
3838
|
-
content = content ? `${attachmentHeader}
|
|
3839
|
-
|
|
3840
|
-
${content}` : attachmentHeader;
|
|
3841
|
-
}
|
|
3842
|
-
return { ...rest, content };
|
|
3843
|
-
}
|
|
3844
|
-
if (!Array.isArray(msg.content)) {
|
|
3845
|
-
return msg;
|
|
3846
|
-
}
|
|
3847
|
-
const blocks = msg.content;
|
|
3848
|
-
const text = blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
3849
|
-
const toolCalls = blocks.filter((b) => b.type === "tool").map((b) => ({ id: b.id, name: b.name, input: b.input }));
|
|
3850
|
-
const cleaned2 = {
|
|
3851
|
-
role: msg.role,
|
|
3852
|
-
content: text
|
|
3853
|
-
};
|
|
3854
|
-
if (toolCalls.length > 0) {
|
|
3855
|
-
cleaned2.toolCalls = toolCalls;
|
|
3856
|
-
}
|
|
3857
|
-
if (msg.providerMetadata) {
|
|
3858
|
-
cleaned2.providerMetadata = msg.providerMetadata;
|
|
3859
|
-
}
|
|
3860
|
-
if (msg.hidden) {
|
|
3861
|
-
cleaned2.hidden = true;
|
|
3862
|
-
}
|
|
3863
|
-
return cleaned2;
|
|
3864
|
-
});
|
|
3865
|
-
return [...prefix, ...cleaned];
|
|
3787
|
+
};
|
|
3866
3788
|
}
|
|
3867
|
-
var
|
|
3868
|
-
"src/
|
|
3789
|
+
var init_statusWatcher = __esm({
|
|
3790
|
+
"src/statusWatcher.ts"() {
|
|
3869
3791
|
"use strict";
|
|
3870
|
-
init_sentinel();
|
|
3871
3792
|
}
|
|
3872
3793
|
});
|
|
3873
3794
|
|
|
@@ -4485,7 +4406,7 @@ ${appSpec}
|
|
|
4485
4406
|
}
|
|
4486
4407
|
}
|
|
4487
4408
|
var BASE_PROMPT;
|
|
4488
|
-
var
|
|
4409
|
+
var init_prompt = __esm({
|
|
4489
4410
|
"src/subagents/browserAutomation/prompt.ts"() {
|
|
4490
4411
|
"use strict";
|
|
4491
4412
|
init_assets();
|
|
@@ -4632,7 +4553,7 @@ var init_browserAutomation = __esm({
|
|
|
4632
4553
|
"use strict";
|
|
4633
4554
|
init_runner();
|
|
4634
4555
|
init_tools();
|
|
4635
|
-
|
|
4556
|
+
init_prompt();
|
|
4636
4557
|
init_sidecar();
|
|
4637
4558
|
init_browserLock();
|
|
4638
4559
|
init_screenshot();
|
|
@@ -5443,14 +5364,14 @@ var init_editImages = __esm({
|
|
|
5443
5364
|
// src/subagents/common/context.ts
|
|
5444
5365
|
import fs16 from "fs";
|
|
5445
5366
|
import path7 from "path";
|
|
5446
|
-
function
|
|
5367
|
+
function walkMdFiles(dir, skip) {
|
|
5447
5368
|
const files = [];
|
|
5448
5369
|
try {
|
|
5449
5370
|
for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
|
|
5450
5371
|
const full = path7.join(dir, entry.name);
|
|
5451
5372
|
if (entry.isDirectory()) {
|
|
5452
5373
|
if (!skip?.has(entry.name)) {
|
|
5453
|
-
files.push(...
|
|
5374
|
+
files.push(...walkMdFiles(full, skip));
|
|
5454
5375
|
}
|
|
5455
5376
|
} else if (entry.name.endsWith(".md")) {
|
|
5456
5377
|
files.push(full);
|
|
@@ -5460,7 +5381,7 @@ function walkMdFiles2(dir, skip) {
|
|
|
5460
5381
|
}
|
|
5461
5382
|
return files.sort();
|
|
5462
5383
|
}
|
|
5463
|
-
function
|
|
5384
|
+
function parseFrontmatter(filePath) {
|
|
5464
5385
|
try {
|
|
5465
5386
|
const content = fs16.readFileSync(filePath, "utf-8");
|
|
5466
5387
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -5482,12 +5403,12 @@ function parseFrontmatter2(filePath) {
|
|
|
5482
5403
|
}
|
|
5483
5404
|
}
|
|
5484
5405
|
function loadSpecIndex() {
|
|
5485
|
-
const files =
|
|
5406
|
+
const files = walkMdFiles("src", /* @__PURE__ */ new Set(["roadmap"]));
|
|
5486
5407
|
if (files.length === 0) {
|
|
5487
5408
|
return "";
|
|
5488
5409
|
}
|
|
5489
5410
|
const lines = files.map((f) => {
|
|
5490
|
-
const fm =
|
|
5411
|
+
const fm = parseFrontmatter(f);
|
|
5491
5412
|
let line = `- ${f}`;
|
|
5492
5413
|
if (fm.name) {
|
|
5493
5414
|
line += ` \u2014 "${fm.name}"`;
|
|
@@ -5525,10 +5446,10 @@ ${indexJson.standalone.map((s) => `- ${s}`).join("\n")}`
|
|
|
5525
5446
|
}
|
|
5526
5447
|
} catch {
|
|
5527
5448
|
}
|
|
5528
|
-
const files =
|
|
5449
|
+
const files = walkMdFiles("src/roadmap");
|
|
5529
5450
|
if (files.length > 0) {
|
|
5530
5451
|
const lines = files.map((f) => {
|
|
5531
|
-
const fm =
|
|
5452
|
+
const fm = parseFrontmatter(f);
|
|
5532
5453
|
let line = `- ${f}`;
|
|
5533
5454
|
if (fm.name) {
|
|
5534
5455
|
line += ` \u2014 "${fm.name}"`;
|
|
@@ -5757,6 +5678,62 @@ var init_tools4 = __esm({
|
|
|
5757
5678
|
}
|
|
5758
5679
|
});
|
|
5759
5680
|
|
|
5681
|
+
// src/orgContext.ts
|
|
5682
|
+
async function initOrgContext(config) {
|
|
5683
|
+
try {
|
|
5684
|
+
cached = await fetchRemyContext(config);
|
|
5685
|
+
const orgDefaultModels2 = filterModelPicks(cached?.defaultModels);
|
|
5686
|
+
setOrgDefaultModels(orgDefaultModels2);
|
|
5687
|
+
log9.debug("org context loaded", {
|
|
5688
|
+
delegatedAvailable: cached?.auth?.delegatedAvailable ?? false,
|
|
5689
|
+
requireDelegatedOnly: cached?.auth?.requireDelegatedOnly ?? false,
|
|
5690
|
+
hasOrgName: !!cached?.org?.name,
|
|
5691
|
+
orgDefaultModels: orgDefaultModels2
|
|
5692
|
+
});
|
|
5693
|
+
} catch (err) {
|
|
5694
|
+
cached = null;
|
|
5695
|
+
setOrgDefaultModels({});
|
|
5696
|
+
log9.debug("org context init failed", { error: err.message });
|
|
5697
|
+
}
|
|
5698
|
+
}
|
|
5699
|
+
function getOrgContext() {
|
|
5700
|
+
return cached;
|
|
5701
|
+
}
|
|
5702
|
+
function renderOrgContextBlock() {
|
|
5703
|
+
const ctx = cached;
|
|
5704
|
+
const auth = ctx?.auth;
|
|
5705
|
+
if (!auth || !auth.delegatedAvailable && !auth.requireDelegatedOnly) {
|
|
5706
|
+
return "";
|
|
5707
|
+
}
|
|
5708
|
+
const lines = ["<org_auth_context>"];
|
|
5709
|
+
if (ctx?.org?.name && ctx?.org?.name !== "Personal Workspace") {
|
|
5710
|
+
lines.push(`This app is owned by the organization "${ctx.org.name}".`);
|
|
5711
|
+
}
|
|
5712
|
+
if (auth.delegatedAvailable) {
|
|
5713
|
+
lines.push(
|
|
5714
|
+
'"Sign in with Remy" (platform-delegated sign-in) is an available auth type for this app: organization members can sign in without a verification code.'
|
|
5715
|
+
);
|
|
5716
|
+
}
|
|
5717
|
+
if (auth.requireDelegatedOnly) {
|
|
5718
|
+
lines.push(
|
|
5719
|
+
"This organization requires delegated sign-in: non-delegated human auth methods (email-code, sms-code) are blocked at the platform edge for its apps."
|
|
5720
|
+
);
|
|
5721
|
+
}
|
|
5722
|
+
lines.push("</org_auth_context>");
|
|
5723
|
+
return lines.join("\n");
|
|
5724
|
+
}
|
|
5725
|
+
var log9, cached;
|
|
5726
|
+
var init_orgContext = __esm({
|
|
5727
|
+
"src/orgContext.ts"() {
|
|
5728
|
+
"use strict";
|
|
5729
|
+
init_api();
|
|
5730
|
+
init_surfaces();
|
|
5731
|
+
init_logger();
|
|
5732
|
+
log9 = createLogger("orgContext");
|
|
5733
|
+
cached = null;
|
|
5734
|
+
}
|
|
5735
|
+
});
|
|
5736
|
+
|
|
5760
5737
|
// src/subagents/designExpert/data/sampleCache.ts
|
|
5761
5738
|
import fs17 from "fs";
|
|
5762
5739
|
function generateIndices(poolSize, sampleSize) {
|
|
@@ -5978,7 +5955,7 @@ This project is in the "${state}" phase. The codebase is a placeholder scaffold
|
|
|
5978
5955
|
return prompt;
|
|
5979
5956
|
}
|
|
5980
5957
|
var SUBAGENT, RUNTIME_PLACEHOLDERS, PROMPT_TEMPLATE;
|
|
5981
|
-
var
|
|
5958
|
+
var init_prompt2 = __esm({
|
|
5982
5959
|
"src/subagents/designExpert/prompt.ts"() {
|
|
5983
5960
|
"use strict";
|
|
5984
5961
|
init_assets();
|
|
@@ -6100,7 +6077,7 @@ var init_designExpert = __esm({
|
|
|
6100
6077
|
init_runner();
|
|
6101
6078
|
init_tools4();
|
|
6102
6079
|
init_tools2();
|
|
6103
|
-
|
|
6080
|
+
init_prompt2();
|
|
6104
6081
|
init_history();
|
|
6105
6082
|
init_surfaces();
|
|
6106
6083
|
init_writeFile();
|
|
@@ -6336,7 +6313,7 @@ function getProductVisionPrompt() {
|
|
|
6336
6313
|
return parts.join("\n\n");
|
|
6337
6314
|
}
|
|
6338
6315
|
var BASE_PROMPT3;
|
|
6339
|
-
var
|
|
6316
|
+
var init_prompt3 = __esm({
|
|
6340
6317
|
"src/subagents/productVision/prompt.ts"() {
|
|
6341
6318
|
"use strict";
|
|
6342
6319
|
init_assets();
|
|
@@ -6356,7 +6333,7 @@ var init_productVision = __esm({
|
|
|
6356
6333
|
init_tools5();
|
|
6357
6334
|
init_tools2();
|
|
6358
6335
|
init_executor();
|
|
6359
|
-
|
|
6336
|
+
init_prompt3();
|
|
6360
6337
|
init_history();
|
|
6361
6338
|
init_surfaces();
|
|
6362
6339
|
productVisionTool = {
|
|
@@ -6618,544 +6595,332 @@ ${readAsset(
|
|
|
6618
6595
|
return "Error: spec sync requires execution context";
|
|
6619
6596
|
}
|
|
6620
6597
|
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
|
-
|
|
6669
|
-
// src/tools/common/scrapeWebUrl.ts
|
|
6670
|
-
var scrapeWebUrlTool;
|
|
6671
|
-
var init_scrapeWebUrl2 = __esm({
|
|
6672
|
-
"src/tools/common/scrapeWebUrl.ts"() {
|
|
6673
|
-
"use strict";
|
|
6674
|
-
init_runMindstudioCli();
|
|
6675
|
-
scrapeWebUrlTool = {
|
|
6676
|
-
clearable: false,
|
|
6677
|
-
definition: {
|
|
6678
|
-
name: "scrapeWebUrl",
|
|
6679
|
-
description: "Scrape the content of a web page. Returns the HTML of the page as markdown text. Optionally capture a screenshot if you need see the visual design. Use this when you need to fetch or analyze content from a website",
|
|
6680
|
-
inputSchema: {
|
|
6681
|
-
type: "object",
|
|
6682
|
-
properties: {
|
|
6683
|
-
url: {
|
|
6684
|
-
type: "string",
|
|
6685
|
-
description: "The URL to fetch."
|
|
6686
|
-
},
|
|
6687
|
-
screenshot: {
|
|
6688
|
-
type: "boolean",
|
|
6689
|
-
description: "Capture a screenshot of the page in addition to the text content. Adds latency; only use when you need to see the visual design."
|
|
6690
|
-
}
|
|
6691
|
-
},
|
|
6692
|
-
required: ["url"]
|
|
6693
|
-
}
|
|
6694
|
-
},
|
|
6695
|
-
async execute(input, context) {
|
|
6696
|
-
const url = input.url;
|
|
6697
|
-
const screenshot = input.screenshot;
|
|
6698
|
-
const pageOptions = { onlyMainContent: true };
|
|
6699
|
-
if (screenshot) {
|
|
6700
|
-
pageOptions.screenshot = true;
|
|
6701
|
-
}
|
|
6702
|
-
return runMindstudioCli(
|
|
6703
|
-
[
|
|
6704
|
-
"scrape-url",
|
|
6705
|
-
"--url",
|
|
6706
|
-
url,
|
|
6707
|
-
"--page-options",
|
|
6708
|
-
JSON.stringify(pageOptions)
|
|
6709
|
-
],
|
|
6710
|
-
{ onLog: context?.onLog }
|
|
6711
|
-
);
|
|
6712
|
-
}
|
|
6713
|
-
};
|
|
6714
|
-
}
|
|
6715
|
-
});
|
|
6716
|
-
|
|
6717
|
-
// src/tools/spec/writeBuildOverview.ts
|
|
6718
|
-
import fs19 from "fs";
|
|
6719
|
-
function initialDelivery() {
|
|
6720
|
-
return `### Your deliverable
|
|
6721
|
-
Write the complete Build Overview to \`${OVERVIEW_FILE}\`. The file does not exist yet \u2014 start from this scaffold, which carries the technical hygiene for the iframed render context (keep the viewport meta and the reset). Layout, sections, type, and styling are yours to compose.
|
|
6722
|
-
|
|
6723
|
-
<overview_shell>
|
|
6724
|
-
${OVERVIEW_SHELL}
|
|
6725
|
-
</overview_shell>
|
|
6726
|
-
|
|
6727
|
-
Reply with a one-line summary of what you wrote \u2014 not the HTML.`;
|
|
6728
|
-
}
|
|
6729
|
-
function refreshDelivery() {
|
|
6730
|
-
return `### Your deliverable
|
|
6731
|
-
The Build Overview already exists at \`${OVERVIEW_FILE}\`. Read it, then update it to reflect <overview_copy>, preserving its established skin \u2014 change only what the copy changed.
|
|
6732
|
-
|
|
6733
|
-
Reply with a one-line summary of what you changed \u2014 not the HTML.`;
|
|
6734
|
-
}
|
|
6735
|
-
var OVERVIEW_FILE, DESIGN_BRIEF, OVERVIEW_SHELL, buildOverviewTool;
|
|
6736
|
-
var init_writeBuildOverview = __esm({
|
|
6737
|
-
"src/tools/spec/writeBuildOverview.ts"() {
|
|
6738
|
-
"use strict";
|
|
6739
|
-
init_designExpert();
|
|
6740
|
-
OVERVIEW_FILE = "src/overview.html";
|
|
6741
|
-
DESIGN_BRIEF = `We are building the Build Overview for this app \u2014 the home page of its Spec tab. It is a calm, dense, one-page reference of everything the app actually contains, including the parts the user can't see. It renders flush inside the Spec tab's content panel (the IDE supplies the surrounding nav).
|
|
6742
|
-
|
|
6743
|
-
Take the plain-language copy in <overview_copy> and lay it out and skin it into a single, beautiful, self-contained HTML document in the app's own brand.
|
|
6744
|
-
|
|
6745
|
-
### The single hard rule
|
|
6746
|
-
The copy in <overview_copy> is final \u2014 it was authored and edited before it reached you. Treat it as locked content to typeset, not a draft to improve. Reproduce the words exactly: do not rewrite, rephrase, shorten, expand, reorder, or "polish" them, and do not run them through any copy tool. This is the opposite of your usual role \u2014 here you own layout, typography, and visual design only, and the words (every number, name, label, claim, and sentence) are fixed. A single changed word or wrong number breaks this document's purpose.
|
|
6747
|
-
|
|
6748
|
-
### What it is (and is not)
|
|
6749
|
-
- A typeset reference dossier: composed, dense, a little cool \u2014 substantial at a glance, then readable. Density communicates substance; sparse and airy reads as "not much here."
|
|
6750
|
-
- Brand-skinned: pull the palette, type system, and a single accent from the app's spec. The accent leads labels and figures; it is a mark, not a fill.
|
|
6751
|
-
- NOT a slide deck \u2014 no theatrics, no building to a climax, no persuasion.
|
|
6752
|
-
- NOT a docs site \u2014 no raised or shadowed cards, no hover-lift, no clickable-feeling surfaces, no sticky table of contents.
|
|
6753
|
-
- NOT a sparse memo \u2014 a restrained single-column page under-sells the work.
|
|
6754
|
-
- No header chrome and no footer: open straight on the app's logo and name so it sits flush with the IDE.
|
|
6755
|
-
|
|
6756
|
-
### Constraints
|
|
6757
|
-
- A single self-contained HTML file. Fonts may load from a CDN; everything else (CSS, the logo SVG) is inline.
|
|
6758
|
-
- Responsive: fills the embedded panel width and collapses gracefully at narrow widths.`;
|
|
6759
|
-
OVERVIEW_SHELL = `<!DOCTYPE html>
|
|
6760
|
-
<html lang="en">
|
|
6761
|
-
<head>
|
|
6762
|
-
<meta charset="utf-8">
|
|
6763
|
-
<!-- This renders inside an iframe in the IDE. Keep it a simple, self-contained
|
|
6764
|
-
static document \u2014 no scroll/zoom/accessibility scaffolding beyond this head.
|
|
6765
|
-
The user-scalable=no viewport is intentional; leave it. -->
|
|
6766
|
-
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
|
6767
|
-
<title>Build Overview</title>
|
|
6768
|
-
<!-- SKIN: link the app's fonts here (CDN \u2014 e.g. Fontshare / Google Fonts). -->
|
|
6769
|
-
<style>
|
|
6770
|
-
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
6771
|
-
</style>
|
|
6772
|
-
</head>
|
|
6773
|
-
<body>
|
|
6774
|
-
<!-- Compose the Build Overview here. Layout, sections, type, and styling are
|
|
6775
|
-
yours \u2014 keep everything inline and self-contained, brand-skinned from the
|
|
6776
|
-
app's spec. -->
|
|
6777
|
-
</body>
|
|
6778
|
-
</html>`;
|
|
6779
|
-
buildOverviewTool = {
|
|
6780
|
-
clearable: false,
|
|
6781
|
-
definition: {
|
|
6782
|
-
name: "writeBuildOverview",
|
|
6783
|
-
description: "Generate or refresh the Build Overview \u2014 the project's home page in the Spec tab: a single-page, plain-language reference of everything the app actually contains, including the parts the user can't see (data stores, backend operations, access and roles, background jobs, seeded scenarios, the design system). You author the full copy: read the manifest and spec and state, plainly and exactly, what genuinely exists \u2014 real names and accurate counts \u2014 in calm, declarative, present-tense outcome language, with no persuasion or hype. Describe only what exists. Pass the complete copy as `content`; the design expert lays it out and skins it to the app's brand using your copy verbatim \u2014 it typesets your words, it does not rewrite them, so polish the copy before you pass it. Generate it at the end of a build and refresh it after meaningful work.",
|
|
6784
|
-
inputSchema: {
|
|
6785
|
-
type: "object",
|
|
6786
|
-
properties: {
|
|
6787
|
-
content: {
|
|
6788
|
-
type: "string",
|
|
6789
|
-
description: "The full Build Overview copy you authored: everything the app contains, in plain present-tense outcome language, with real names and exact counts. The design expert lays this out and skins it to the brand verbatim \u2014 it styles your words, it does not rewrite them \u2014 so this should be the final copy."
|
|
6790
|
-
}
|
|
6791
|
-
},
|
|
6792
|
-
required: ["content"]
|
|
6793
|
-
}
|
|
6794
|
-
},
|
|
6795
|
-
async execute(input, context) {
|
|
6796
|
-
if (!context) {
|
|
6797
|
-
return "Error: writeBuildOverview requires execution context for design expert delegation";
|
|
6798
|
-
}
|
|
6799
|
-
const content = (input.content ?? "").trim();
|
|
6800
|
-
if (!content) {
|
|
6801
|
-
return "Error: writeBuildOverview requires non-empty `content` (the overview copy).";
|
|
6598
|
+
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.";
|
|
6802
6599
|
}
|
|
6803
|
-
const
|
|
6804
|
-
const
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6600
|
+
const specIndex = loadSpecIndex();
|
|
6601
|
+
const parts = [
|
|
6602
|
+
BASE_PROMPT5,
|
|
6603
|
+
loadPlatformBrief(),
|
|
6604
|
+
MSFM_DOCS,
|
|
6605
|
+
"<!-- cache_breakpoint -->"
|
|
6606
|
+
];
|
|
6607
|
+
if (specIndex) {
|
|
6608
|
+
parts.push(specIndex);
|
|
6609
|
+
}
|
|
6610
|
+
const system = parts.join("\n\n");
|
|
6611
|
+
const result = await runSubAgent({
|
|
6612
|
+
system,
|
|
6613
|
+
task: input.task,
|
|
6614
|
+
tools: SPEC_SYNC_TOOLS,
|
|
6615
|
+
externalTools: /* @__PURE__ */ new Set(),
|
|
6616
|
+
executeTool: (name, toolInput) => executeTool(name, toolInput, context),
|
|
6617
|
+
apiConfig: context.apiConfig,
|
|
6618
|
+
model: resolveModel("specSync", context.models, context.model),
|
|
6619
|
+
subAgentId: "specSync",
|
|
6620
|
+
signal: context.signal,
|
|
6621
|
+
parentToolId: context.toolCallId,
|
|
6622
|
+
requestId: context.requestId,
|
|
6623
|
+
onEvent: context.onEvent,
|
|
6624
|
+
resolveExternalTool: context.resolveExternalTool,
|
|
6625
|
+
toolRegistry: context.toolRegistry,
|
|
6626
|
+
// Always background + serialized: never blocks Remy's turn, and two
|
|
6627
|
+
// reconciliations queue (FIFO) instead of running at once.
|
|
6628
|
+
background: true,
|
|
6629
|
+
acquireLock: acquireSpecSyncLock,
|
|
6630
|
+
onBackgroundComplete: (bgResult) => {
|
|
6631
|
+
context.onBackgroundComplete?.(
|
|
6632
|
+
context.toolCallId,
|
|
6633
|
+
"specSync",
|
|
6634
|
+
bgResult.text,
|
|
6635
|
+
bgResult.messages
|
|
6815
6636
|
);
|
|
6816
|
-
context.subAgentMessages?.set(context.toolCallId, result2.messages);
|
|
6817
|
-
return result2.text;
|
|
6818
|
-
}
|
|
6819
|
-
const result = await runDesignExpertRender({ task }, context);
|
|
6820
|
-
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
6821
|
-
if (!fs19.existsSync(OVERVIEW_FILE)) {
|
|
6822
|
-
return `Error: the design expert did not write ${OVERVIEW_FILE}. Its reply was:
|
|
6823
|
-
${result.text}`;
|
|
6824
6637
|
}
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
}
|
|
6638
|
+
});
|
|
6639
|
+
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
6640
|
+
return result.text;
|
|
6829
6641
|
}
|
|
6830
6642
|
};
|
|
6831
6643
|
}
|
|
6832
6644
|
});
|
|
6833
6645
|
|
|
6834
|
-
// src/tools/
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
function getToolDefinitions(_onboardingState) {
|
|
6839
|
-
return ALL_TOOLS.map((t) => t.definition);
|
|
6840
|
-
}
|
|
6841
|
-
function getToolByName(name) {
|
|
6842
|
-
return ALL_TOOLS.find((t) => t.definition.name === name);
|
|
6843
|
-
}
|
|
6844
|
-
function executeTool(name, input, context) {
|
|
6845
|
-
const tool = getToolByName(name);
|
|
6846
|
-
if (!tool) {
|
|
6847
|
-
return Promise.resolve(`Error: Unknown tool "${name}"`);
|
|
6848
|
-
}
|
|
6849
|
-
return tool.execute(input, context);
|
|
6850
|
-
}
|
|
6851
|
-
var ALL_TOOLS, CLEARABLE_TOOLS, SUBAGENT_TOOL_NAMES;
|
|
6852
|
-
var init_tools8 = __esm({
|
|
6853
|
-
"src/tools/index.ts"() {
|
|
6646
|
+
// src/tools/common/scrapeWebUrl.ts
|
|
6647
|
+
var scrapeWebUrlTool;
|
|
6648
|
+
var init_scrapeWebUrl2 = __esm({
|
|
6649
|
+
"src/tools/common/scrapeWebUrl.ts"() {
|
|
6854
6650
|
"use strict";
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
setProjectOnboardingStateTool,
|
|
6894
|
-
promptUserTool,
|
|
6895
|
-
confirmDestructiveActionTool,
|
|
6896
|
-
askMindStudioSdkTool,
|
|
6897
|
-
scrapeWebUrlTool,
|
|
6898
|
-
searchGoogleTool,
|
|
6899
|
-
setProjectMetadataTool,
|
|
6900
|
-
designExpertTool,
|
|
6901
|
-
productVisionTool,
|
|
6902
|
-
codeSanityCheckTool,
|
|
6903
|
-
copyEditorTool,
|
|
6904
|
-
specSyncTool,
|
|
6905
|
-
// no-ops until onboardingFinished; kept here so the tool list stays cache-stable
|
|
6906
|
-
buildOverviewTool,
|
|
6907
|
-
compactConversationTool,
|
|
6908
|
-
// Post-onboarding
|
|
6909
|
-
presentPublishPlanTool,
|
|
6910
|
-
writePlanTool,
|
|
6911
|
-
updatePlanStatusTool,
|
|
6912
|
-
// Spec
|
|
6913
|
-
readSpecTool,
|
|
6914
|
-
writeSpecTool,
|
|
6915
|
-
editSpecTool,
|
|
6916
|
-
listSpecFilesTool,
|
|
6917
|
-
// Code
|
|
6918
|
-
readFileTool,
|
|
6919
|
-
writeFileTool,
|
|
6920
|
-
editFileTool,
|
|
6921
|
-
bashTool,
|
|
6922
|
-
grepTool,
|
|
6923
|
-
globTool,
|
|
6924
|
-
listDirTool,
|
|
6925
|
-
editsFinishedTool,
|
|
6926
|
-
runScenarioTool,
|
|
6927
|
-
runMethodTool,
|
|
6928
|
-
queryDatabaseTool,
|
|
6929
|
-
screenshotTool,
|
|
6930
|
-
browserAutomationTool,
|
|
6931
|
-
// LSP
|
|
6932
|
-
lspDiagnosticsTool,
|
|
6933
|
-
restartProcessTool
|
|
6934
|
-
];
|
|
6935
|
-
CLEARABLE_TOOLS = new Set(
|
|
6936
|
-
ALL_TOOLS.filter((t) => t.clearable).map((t) => t.definition.name)
|
|
6937
|
-
);
|
|
6938
|
-
SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
6939
|
-
"visualDesignExpert",
|
|
6940
|
-
"productVision",
|
|
6941
|
-
"codeSanityCheck",
|
|
6942
|
-
"copyEditor",
|
|
6943
|
-
"specSync",
|
|
6944
|
-
"runAutomatedBrowserTest",
|
|
6945
|
-
"askMindStudioSdk"
|
|
6946
|
-
]);
|
|
6947
|
-
}
|
|
6948
|
-
});
|
|
6949
|
-
|
|
6950
|
-
// src/session.ts
|
|
6951
|
-
import fs20 from "fs";
|
|
6952
|
-
import path9 from "path";
|
|
6953
|
-
function loadSession(state) {
|
|
6954
|
-
pruneArchives();
|
|
6955
|
-
try {
|
|
6956
|
-
const raw = fs20.readFileSync(SESSION_FILE, "utf-8");
|
|
6957
|
-
const data = JSON.parse(raw);
|
|
6958
|
-
if (data.models && typeof data.models === "object") {
|
|
6959
|
-
state.models = data.models;
|
|
6960
|
-
}
|
|
6961
|
-
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
6962
|
-
state.messages = sanitizeMessages(data.messages);
|
|
6963
|
-
log9.info("Session loaded", {
|
|
6964
|
-
messageCount: state.messages.length,
|
|
6965
|
-
...state.models && { models: state.models }
|
|
6966
|
-
});
|
|
6967
|
-
return true;
|
|
6968
|
-
}
|
|
6969
|
-
} catch {
|
|
6970
|
-
}
|
|
6971
|
-
return false;
|
|
6972
|
-
}
|
|
6973
|
-
function capOversizedResults(msg) {
|
|
6974
|
-
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
6975
|
-
for (const block of msg.content) {
|
|
6976
|
-
if (block.type === "tool" && typeof block.result === "string") {
|
|
6977
|
-
block.result = capToolResult(block.result);
|
|
6978
|
-
}
|
|
6979
|
-
}
|
|
6980
|
-
} else if (msg.role === "user" && msg.toolCallId && typeof msg.content === "string") {
|
|
6981
|
-
msg.content = capToolResult(msg.content);
|
|
6982
|
-
}
|
|
6983
|
-
}
|
|
6984
|
-
function sanitizeMessages(messages) {
|
|
6985
|
-
const result = [];
|
|
6986
|
-
for (let i = 0; i < messages.length; i++) {
|
|
6987
|
-
const msg = messages[i];
|
|
6988
|
-
capOversizedResults(msg);
|
|
6989
|
-
result.push(msg);
|
|
6990
|
-
if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
|
|
6991
|
-
continue;
|
|
6992
|
-
}
|
|
6993
|
-
const toolBlocks = msg.content.filter(
|
|
6994
|
-
(b) => b.type === "tool"
|
|
6995
|
-
);
|
|
6996
|
-
if (toolBlocks.length === 0) {
|
|
6997
|
-
continue;
|
|
6998
|
-
}
|
|
6999
|
-
const resultIds = /* @__PURE__ */ new Set();
|
|
7000
|
-
for (let j = i + 1; j < messages.length; j++) {
|
|
7001
|
-
const next = messages[j];
|
|
7002
|
-
if (next.role === "user" && next.toolCallId) {
|
|
7003
|
-
resultIds.add(next.toolCallId);
|
|
7004
|
-
} else {
|
|
7005
|
-
break;
|
|
7006
|
-
}
|
|
7007
|
-
}
|
|
7008
|
-
for (const tc of toolBlocks) {
|
|
7009
|
-
if (!resultIds.has(tc.id)) {
|
|
7010
|
-
result.push({
|
|
7011
|
-
role: "user",
|
|
7012
|
-
content: "Error: tool result lost (session recovered)",
|
|
7013
|
-
toolCallId: tc.id,
|
|
7014
|
-
isToolError: true
|
|
7015
|
-
});
|
|
6651
|
+
init_runMindstudioCli();
|
|
6652
|
+
scrapeWebUrlTool = {
|
|
6653
|
+
clearable: false,
|
|
6654
|
+
definition: {
|
|
6655
|
+
name: "scrapeWebUrl",
|
|
6656
|
+
description: "Scrape the content of a web page. Returns the HTML of the page as markdown text. Optionally capture a screenshot if you need see the visual design. Use this when you need to fetch or analyze content from a website",
|
|
6657
|
+
inputSchema: {
|
|
6658
|
+
type: "object",
|
|
6659
|
+
properties: {
|
|
6660
|
+
url: {
|
|
6661
|
+
type: "string",
|
|
6662
|
+
description: "The URL to fetch."
|
|
6663
|
+
},
|
|
6664
|
+
screenshot: {
|
|
6665
|
+
type: "boolean",
|
|
6666
|
+
description: "Capture a screenshot of the page in addition to the text content. Adds latency; only use when you need to see the visual design."
|
|
6667
|
+
}
|
|
6668
|
+
},
|
|
6669
|
+
required: ["url"]
|
|
6670
|
+
}
|
|
6671
|
+
},
|
|
6672
|
+
async execute(input, context) {
|
|
6673
|
+
const url = input.url;
|
|
6674
|
+
const screenshot = input.screenshot;
|
|
6675
|
+
const pageOptions = { onlyMainContent: true };
|
|
6676
|
+
if (screenshot) {
|
|
6677
|
+
pageOptions.screenshot = true;
|
|
6678
|
+
}
|
|
6679
|
+
return runMindstudioCli(
|
|
6680
|
+
[
|
|
6681
|
+
"scrape-url",
|
|
6682
|
+
"--url",
|
|
6683
|
+
url,
|
|
6684
|
+
"--page-options",
|
|
6685
|
+
JSON.stringify(pageOptions)
|
|
6686
|
+
],
|
|
6687
|
+
{ onLog: context?.onLog }
|
|
6688
|
+
);
|
|
7016
6689
|
}
|
|
7017
|
-
}
|
|
7018
|
-
}
|
|
7019
|
-
return result;
|
|
7020
|
-
}
|
|
7021
|
-
function buildPayload(state) {
|
|
7022
|
-
const payload = { messages: state.messages };
|
|
7023
|
-
if (state.models && Object.keys(state.models).length > 0) {
|
|
7024
|
-
payload.models = state.models;
|
|
6690
|
+
};
|
|
7025
6691
|
}
|
|
7026
|
-
|
|
6692
|
+
});
|
|
6693
|
+
|
|
6694
|
+
// src/tools/spec/writeBuildOverview.ts
|
|
6695
|
+
import fs19 from "fs";
|
|
6696
|
+
function initialDelivery() {
|
|
6697
|
+
return `### Your deliverable
|
|
6698
|
+
Write the complete Build Overview to \`${OVERVIEW_FILE}\`. The file does not exist yet \u2014 start from this scaffold, which carries the technical hygiene for the iframed render context (keep the viewport meta and the reset). Layout, sections, type, and styling are yours to compose.
|
|
6699
|
+
|
|
6700
|
+
<overview_shell>
|
|
6701
|
+
${OVERVIEW_SHELL}
|
|
6702
|
+
</overview_shell>
|
|
6703
|
+
|
|
6704
|
+
Reply with a one-line summary of what you wrote \u2014 not the HTML.`;
|
|
7027
6705
|
}
|
|
7028
|
-
function
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
while (fs20.existsSync(dest)) {
|
|
7034
|
-
dest = path9.join(ARCHIVE_DIR, `${label}-${ts}-${n++}.json`);
|
|
7035
|
-
}
|
|
7036
|
-
const payload = { messages };
|
|
7037
|
-
if (models && Object.keys(models).length > 0) {
|
|
7038
|
-
payload.models = models;
|
|
7039
|
-
}
|
|
7040
|
-
fs20.writeFileSync(dest, JSON.stringify(payload), "utf-8");
|
|
7041
|
-
log9.info("Session archived", { label, dest, messageCount: messages.length });
|
|
7042
|
-
pruneArchives();
|
|
7043
|
-
return dest;
|
|
6706
|
+
function refreshDelivery() {
|
|
6707
|
+
return `### Your deliverable
|
|
6708
|
+
The Build Overview already exists at \`${OVERVIEW_FILE}\`. Read it, then update it to reflect <overview_copy>, preserving its established skin \u2014 change only what the copy changed.
|
|
6709
|
+
|
|
6710
|
+
Reply with a one-line summary of what you changed \u2014 not the HTML.`;
|
|
7044
6711
|
}
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
6712
|
+
var OVERVIEW_FILE, DESIGN_BRIEF, OVERVIEW_SHELL, buildOverviewTool;
|
|
6713
|
+
var init_writeBuildOverview = __esm({
|
|
6714
|
+
"src/tools/spec/writeBuildOverview.ts"() {
|
|
6715
|
+
"use strict";
|
|
6716
|
+
init_designExpert();
|
|
6717
|
+
OVERVIEW_FILE = "src/overview.html";
|
|
6718
|
+
DESIGN_BRIEF = `We are building the Build Overview for this app \u2014 the home page of its Spec tab. It is a calm, dense, one-page reference of everything the app actually contains, including the parts the user can't see. It renders flush inside the Spec tab's content panel (the IDE supplies the surrounding nav).
|
|
6719
|
+
|
|
6720
|
+
Take the plain-language copy in <overview_copy> and lay it out and skin it into a single, beautiful, self-contained HTML document in the app's own brand.
|
|
6721
|
+
|
|
6722
|
+
### The single hard rule
|
|
6723
|
+
The copy in <overview_copy> is final \u2014 it was authored and edited before it reached you. Treat it as locked content to typeset, not a draft to improve. Reproduce the words exactly: do not rewrite, rephrase, shorten, expand, reorder, or "polish" them, and do not run them through any copy tool. This is the opposite of your usual role \u2014 here you own layout, typography, and visual design only, and the words (every number, name, label, claim, and sentence) are fixed. A single changed word or wrong number breaks this document's purpose.
|
|
6724
|
+
|
|
6725
|
+
### What it is (and is not)
|
|
6726
|
+
- A typeset reference dossier: composed, dense, a little cool \u2014 substantial at a glance, then readable. Density communicates substance; sparse and airy reads as "not much here."
|
|
6727
|
+
- Brand-skinned: pull the palette, type system, and a single accent from the app's spec. The accent leads labels and figures; it is a mark, not a fill.
|
|
6728
|
+
- NOT a slide deck \u2014 no theatrics, no building to a climax, no persuasion.
|
|
6729
|
+
- NOT a docs site \u2014 no raised or shadowed cards, no hover-lift, no clickable-feeling surfaces, no sticky table of contents.
|
|
6730
|
+
- NOT a sparse memo \u2014 a restrained single-column page under-sells the work.
|
|
6731
|
+
- No header chrome and no footer: open straight on the app's logo and name so it sits flush with the IDE.
|
|
6732
|
+
|
|
6733
|
+
### Constraints
|
|
6734
|
+
- A single self-contained HTML file. Fonts may load from a CDN; everything else (CSS, the logo SVG) is inline.
|
|
6735
|
+
- Responsive: fills the embedded panel width and collapses gracefully at narrow widths.`;
|
|
6736
|
+
OVERVIEW_SHELL = `<!DOCTYPE html>
|
|
6737
|
+
<html lang="en">
|
|
6738
|
+
<head>
|
|
6739
|
+
<meta charset="utf-8">
|
|
6740
|
+
<!-- This renders inside an iframe in the IDE. Keep it a simple, self-contained
|
|
6741
|
+
static document \u2014 no scroll/zoom/accessibility scaffolding beyond this head.
|
|
6742
|
+
The user-scalable=no viewport is intentional; leave it. -->
|
|
6743
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
|
6744
|
+
<title>Build Overview</title>
|
|
6745
|
+
<!-- SKIN: link the app's fonts here (CDN \u2014 e.g. Fontshare / Google Fonts). -->
|
|
6746
|
+
<style>
|
|
6747
|
+
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
6748
|
+
</style>
|
|
6749
|
+
</head>
|
|
6750
|
+
<body>
|
|
6751
|
+
<!-- Compose the Build Overview here. Layout, sections, type, and styling are
|
|
6752
|
+
yours \u2014 keep everything inline and self-contained, brand-skinned from the
|
|
6753
|
+
app's spec. -->
|
|
6754
|
+
</body>
|
|
6755
|
+
</html>`;
|
|
6756
|
+
buildOverviewTool = {
|
|
6757
|
+
clearable: false,
|
|
6758
|
+
definition: {
|
|
6759
|
+
name: "writeBuildOverview",
|
|
6760
|
+
description: "Generate or refresh the Build Overview \u2014 the project's home page in the Spec tab: a single-page, plain-language reference of everything the app actually contains, including the parts the user can't see (data stores, backend operations, access and roles, background jobs, seeded scenarios, the design system). You author the full copy: read the manifest and spec and state, plainly and exactly, what genuinely exists \u2014 real names and accurate counts \u2014 in calm, declarative, present-tense outcome language, with no persuasion or hype. Describe only what exists. Pass the complete copy as `content`; the design expert lays it out and skins it to the app's brand using your copy verbatim \u2014 it typesets your words, it does not rewrite them, so polish the copy before you pass it. Generate it at the end of a build and refresh it after meaningful work.",
|
|
6761
|
+
inputSchema: {
|
|
6762
|
+
type: "object",
|
|
6763
|
+
properties: {
|
|
6764
|
+
content: {
|
|
6765
|
+
type: "string",
|
|
6766
|
+
description: "The full Build Overview copy you authored: everything the app contains, in plain present-tense outcome language, with real names and exact counts. The design expert lays this out and skins it to the brand verbatim \u2014 it styles your words, it does not rewrite them \u2014 so this should be the final copy."
|
|
6767
|
+
}
|
|
6768
|
+
},
|
|
6769
|
+
required: ["content"]
|
|
6770
|
+
}
|
|
6771
|
+
},
|
|
6772
|
+
async execute(input, context) {
|
|
6773
|
+
if (!context) {
|
|
6774
|
+
return "Error: writeBuildOverview requires execution context for design expert delegation";
|
|
6775
|
+
}
|
|
6776
|
+
const content = (input.content ?? "").trim();
|
|
6777
|
+
if (!content) {
|
|
6778
|
+
return "Error: writeBuildOverview requires non-empty `content` (the overview copy).";
|
|
6779
|
+
}
|
|
6780
|
+
const exists = fs19.existsSync(OVERVIEW_FILE);
|
|
6781
|
+
const task = `<overview_copy>${content}</overview_copy>
|
|
6782
|
+
|
|
6783
|
+
${DESIGN_BRIEF}
|
|
6784
|
+
|
|
6785
|
+
${exists ? refreshDelivery() : initialDelivery()}`;
|
|
6786
|
+
try {
|
|
6787
|
+
if (exists) {
|
|
6788
|
+
input.background = true;
|
|
6789
|
+
const result2 = await runDesignExpertRender(
|
|
6790
|
+
{ task, background: true, reportingName: "writeBuildOverview" },
|
|
6791
|
+
context
|
|
6792
|
+
);
|
|
6793
|
+
context.subAgentMessages?.set(context.toolCallId, result2.messages);
|
|
6794
|
+
return result2.text;
|
|
6795
|
+
}
|
|
6796
|
+
const result = await runDesignExpertRender({ task }, context);
|
|
6797
|
+
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
6798
|
+
if (!fs19.existsSync(OVERVIEW_FILE)) {
|
|
6799
|
+
return `Error: the design expert did not write ${OVERVIEW_FILE}. Its reply was:
|
|
6800
|
+
${result.text}`;
|
|
6801
|
+
}
|
|
6802
|
+
return `Build overview written to ${OVERVIEW_FILE}. ${result.text}`;
|
|
6803
|
+
} catch (err) {
|
|
6804
|
+
return `Error generating build overview: ${err.message}`;
|
|
6805
|
+
}
|
|
7074
6806
|
}
|
|
7075
|
-
}
|
|
7076
|
-
if (removed > 0) {
|
|
7077
|
-
log9.info("Session archives pruned", {
|
|
7078
|
-
removed,
|
|
7079
|
-
freedBytes: freed,
|
|
7080
|
-
keptBytes: kept
|
|
7081
|
-
});
|
|
7082
|
-
}
|
|
7083
|
-
} catch {
|
|
6807
|
+
};
|
|
7084
6808
|
}
|
|
6809
|
+
});
|
|
6810
|
+
|
|
6811
|
+
// src/tools/index.ts
|
|
6812
|
+
function deriveContext(parent, toolCallId) {
|
|
6813
|
+
return { ...parent, toolCallId };
|
|
7085
6814
|
}
|
|
7086
|
-
function
|
|
7087
|
-
|
|
7088
|
-
if (messages.length === 0) {
|
|
7089
|
-
return false;
|
|
7090
|
-
}
|
|
7091
|
-
let tailBytes = 0;
|
|
7092
|
-
let scrollbackStart = 0;
|
|
7093
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
7094
|
-
tailBytes += Buffer.byteLength(JSON.stringify(messages[i]), "utf-8") + 1;
|
|
7095
|
-
if (tailBytes >= RETAIN_TAIL_BYTES) {
|
|
7096
|
-
scrollbackStart = i;
|
|
7097
|
-
break;
|
|
7098
|
-
}
|
|
7099
|
-
}
|
|
7100
|
-
const checkpointIdx = findLastSummaryCheckpoint(messages, "conversation");
|
|
7101
|
-
let cut = checkpointIdx === -1 ? scrollbackStart : Math.min(scrollbackStart, checkpointIdx);
|
|
7102
|
-
cut = findSafeInsertionPoint(messages, cut);
|
|
7103
|
-
if (cut <= 0) {
|
|
7104
|
-
return false;
|
|
7105
|
-
}
|
|
7106
|
-
archiveMessages(messages.slice(0, cut), "rotated", state.models);
|
|
7107
|
-
state.messages = messages.slice(cut);
|
|
7108
|
-
log9.info("Session rotated", {
|
|
7109
|
-
archived: cut,
|
|
7110
|
-
retained: state.messages.length
|
|
7111
|
-
});
|
|
7112
|
-
return true;
|
|
6815
|
+
function getToolDefinitions(_onboardingState) {
|
|
6816
|
+
return ALL_TOOLS.map((t) => t.definition);
|
|
7113
6817
|
}
|
|
7114
|
-
function
|
|
7115
|
-
|
|
7116
|
-
let serialized = JSON.stringify(buildPayload(state));
|
|
7117
|
-
if (Buffer.byteLength(serialized, "utf-8") > ROTATE_THRESHOLD_BYTES && rotate(state)) {
|
|
7118
|
-
serialized = JSON.stringify(buildPayload(state));
|
|
7119
|
-
}
|
|
7120
|
-
fs20.writeFileSync(SESSION_FILE, serialized, "utf-8");
|
|
7121
|
-
log9.info("Session saved", { messageCount: state.messages.length });
|
|
7122
|
-
} catch (err) {
|
|
7123
|
-
log9.warn("Session save failed", { error: err.message });
|
|
7124
|
-
}
|
|
6818
|
+
function getToolByName(name) {
|
|
6819
|
+
return ALL_TOOLS.find((t) => t.definition.name === name);
|
|
7125
6820
|
}
|
|
7126
|
-
function
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
}
|
|
7131
|
-
} catch (err) {
|
|
7132
|
-
log9.warn("Session archive on clear failed", { error: err.message });
|
|
7133
|
-
}
|
|
7134
|
-
state.messages = [];
|
|
7135
|
-
try {
|
|
7136
|
-
if (fs20.existsSync(SESSION_FILE)) {
|
|
7137
|
-
fs20.unlinkSync(SESSION_FILE);
|
|
7138
|
-
}
|
|
7139
|
-
} catch (err) {
|
|
7140
|
-
log9.warn("Session clear: could not remove live file", {
|
|
7141
|
-
error: err.message
|
|
7142
|
-
});
|
|
6821
|
+
function executeTool(name, input, context) {
|
|
6822
|
+
const tool = getToolByName(name);
|
|
6823
|
+
if (!tool) {
|
|
6824
|
+
return Promise.resolve(`Error: Unknown tool "${name}"`);
|
|
7143
6825
|
}
|
|
6826
|
+
return tool.execute(input, context);
|
|
7144
6827
|
}
|
|
7145
|
-
var
|
|
7146
|
-
var
|
|
7147
|
-
"src/
|
|
6828
|
+
var ALL_TOOLS, CLEARABLE_TOOLS, SUBAGENT_TOOL_NAMES;
|
|
6829
|
+
var init_tools8 = __esm({
|
|
6830
|
+
"src/tools/index.ts"() {
|
|
7148
6831
|
"use strict";
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
6832
|
+
init_readSpec();
|
|
6833
|
+
init_writeSpec();
|
|
6834
|
+
init_editSpec();
|
|
6835
|
+
init_listSpecFiles();
|
|
6836
|
+
init_presentPublishPlan();
|
|
6837
|
+
init_writePlan();
|
|
6838
|
+
init_updatePlanStatus();
|
|
6839
|
+
init_setProjectOnboardingState();
|
|
6840
|
+
init_promptUser();
|
|
6841
|
+
init_confirmDestructiveAction();
|
|
6842
|
+
init_sdkConsultant();
|
|
6843
|
+
init_searchGoogle();
|
|
6844
|
+
init_setProjectMetadata();
|
|
6845
|
+
init_compactConversation();
|
|
6846
|
+
init_readFile();
|
|
6847
|
+
init_writeFile();
|
|
6848
|
+
init_editFile();
|
|
6849
|
+
init_bash();
|
|
6850
|
+
init_grep();
|
|
6851
|
+
init_glob();
|
|
6852
|
+
init_listDir();
|
|
6853
|
+
init_editsFinished();
|
|
6854
|
+
init_lspDiagnostics();
|
|
6855
|
+
init_restartProcess();
|
|
6856
|
+
init_runScenario();
|
|
6857
|
+
init_runMethod();
|
|
6858
|
+
init_queryDatabase();
|
|
6859
|
+
init_screenshot2();
|
|
6860
|
+
init_browserAutomation();
|
|
6861
|
+
init_designExpert();
|
|
6862
|
+
init_productVision();
|
|
6863
|
+
init_codeSanityCheck();
|
|
6864
|
+
init_copyEditor();
|
|
6865
|
+
init_specSync();
|
|
6866
|
+
init_scrapeWebUrl2();
|
|
6867
|
+
init_writeBuildOverview();
|
|
6868
|
+
ALL_TOOLS = [
|
|
6869
|
+
// Common
|
|
6870
|
+
setProjectOnboardingStateTool,
|
|
6871
|
+
promptUserTool,
|
|
6872
|
+
confirmDestructiveActionTool,
|
|
6873
|
+
askMindStudioSdkTool,
|
|
6874
|
+
scrapeWebUrlTool,
|
|
6875
|
+
searchGoogleTool,
|
|
6876
|
+
setProjectMetadataTool,
|
|
6877
|
+
designExpertTool,
|
|
6878
|
+
productVisionTool,
|
|
6879
|
+
codeSanityCheckTool,
|
|
6880
|
+
copyEditorTool,
|
|
6881
|
+
specSyncTool,
|
|
6882
|
+
// no-ops until onboardingFinished; kept here so the tool list stays cache-stable
|
|
6883
|
+
buildOverviewTool,
|
|
6884
|
+
compactConversationTool,
|
|
6885
|
+
// Post-onboarding
|
|
6886
|
+
presentPublishPlanTool,
|
|
6887
|
+
writePlanTool,
|
|
6888
|
+
updatePlanStatusTool,
|
|
6889
|
+
// Spec
|
|
6890
|
+
readSpecTool,
|
|
6891
|
+
writeSpecTool,
|
|
6892
|
+
editSpecTool,
|
|
6893
|
+
listSpecFilesTool,
|
|
6894
|
+
// Code
|
|
6895
|
+
readFileTool,
|
|
6896
|
+
writeFileTool,
|
|
6897
|
+
editFileTool,
|
|
6898
|
+
bashTool,
|
|
6899
|
+
grepTool,
|
|
6900
|
+
globTool,
|
|
6901
|
+
listDirTool,
|
|
6902
|
+
editsFinishedTool,
|
|
6903
|
+
runScenarioTool,
|
|
6904
|
+
runMethodTool,
|
|
6905
|
+
queryDatabaseTool,
|
|
6906
|
+
screenshotTool,
|
|
6907
|
+
browserAutomationTool,
|
|
6908
|
+
// LSP
|
|
6909
|
+
lspDiagnosticsTool,
|
|
6910
|
+
restartProcessTool
|
|
6911
|
+
];
|
|
6912
|
+
CLEARABLE_TOOLS = new Set(
|
|
6913
|
+
ALL_TOOLS.filter((t) => t.clearable).map((t) => t.definition.name)
|
|
6914
|
+
);
|
|
6915
|
+
SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
6916
|
+
"visualDesignExpert",
|
|
6917
|
+
"productVision",
|
|
6918
|
+
"codeSanityCheck",
|
|
6919
|
+
"copyEditor",
|
|
6920
|
+
"specSync",
|
|
6921
|
+
"runAutomatedBrowserTest",
|
|
6922
|
+
"askMindStudioSdk"
|
|
6923
|
+
]);
|
|
7159
6924
|
}
|
|
7160
6925
|
});
|
|
7161
6926
|
|
|
@@ -7369,8 +7134,8 @@ var init_errors = __esm({
|
|
|
7369
7134
|
});
|
|
7370
7135
|
|
|
7371
7136
|
// src/brandExtraction/index.ts
|
|
7372
|
-
import
|
|
7373
|
-
import
|
|
7137
|
+
import fs20 from "fs";
|
|
7138
|
+
import path9 from "path";
|
|
7374
7139
|
import { createHash } from "crypto";
|
|
7375
7140
|
async function runExtraction(apiConfig, model) {
|
|
7376
7141
|
const inputHash = computeInputHash();
|
|
@@ -7390,15 +7155,15 @@ async function runExtraction(apiConfig, model) {
|
|
|
7390
7155
|
return brand;
|
|
7391
7156
|
}
|
|
7392
7157
|
function isBrandRelevant(filePath) {
|
|
7393
|
-
if (filePath ===
|
|
7158
|
+
if (filePath === path9.join("src", "app.md")) {
|
|
7394
7159
|
return true;
|
|
7395
7160
|
}
|
|
7396
|
-
const { type } =
|
|
7161
|
+
const { type } = parseFrontmatter2(filePath);
|
|
7397
7162
|
return type.startsWith("design/color") || type.startsWith("design/typography");
|
|
7398
7163
|
}
|
|
7399
7164
|
function computeInputHash() {
|
|
7400
7165
|
const entries = [];
|
|
7401
|
-
for (const filePath of
|
|
7166
|
+
for (const filePath of walkMdFiles2("src")) {
|
|
7402
7167
|
if (isBrandRelevant(filePath)) {
|
|
7403
7168
|
entries.push({ path: filePath, content: readSafe(filePath) });
|
|
7404
7169
|
}
|
|
@@ -7416,19 +7181,19 @@ function sha256(input) {
|
|
|
7416
7181
|
}
|
|
7417
7182
|
function readSafe(filePath) {
|
|
7418
7183
|
try {
|
|
7419
|
-
return
|
|
7184
|
+
return fs20.readFileSync(filePath, "utf-8");
|
|
7420
7185
|
} catch {
|
|
7421
7186
|
return "";
|
|
7422
7187
|
}
|
|
7423
7188
|
}
|
|
7424
|
-
function
|
|
7189
|
+
function walkMdFiles2(dir) {
|
|
7425
7190
|
const results = [];
|
|
7426
7191
|
try {
|
|
7427
|
-
const entries =
|
|
7192
|
+
const entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
7428
7193
|
for (const entry of entries) {
|
|
7429
|
-
const full =
|
|
7194
|
+
const full = path9.join(dir, entry.name);
|
|
7430
7195
|
if (entry.isDirectory()) {
|
|
7431
|
-
results.push(...
|
|
7196
|
+
results.push(...walkMdFiles2(full));
|
|
7432
7197
|
} else if (entry.name.endsWith(".md")) {
|
|
7433
7198
|
results.push(full);
|
|
7434
7199
|
}
|
|
@@ -7437,9 +7202,9 @@ function walkMdFiles3(dir) {
|
|
|
7437
7202
|
}
|
|
7438
7203
|
return results.sort();
|
|
7439
7204
|
}
|
|
7440
|
-
function
|
|
7205
|
+
function parseFrontmatter2(filePath) {
|
|
7441
7206
|
try {
|
|
7442
|
-
const content =
|
|
7207
|
+
const content = fs20.readFileSync(filePath, "utf-8");
|
|
7443
7208
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
7444
7209
|
if (!match) {
|
|
7445
7210
|
return { type: "" };
|
|
@@ -7503,7 +7268,7 @@ async function extractBrand(apiConfig, model) {
|
|
|
7503
7268
|
return validateBrand(parsed);
|
|
7504
7269
|
}
|
|
7505
7270
|
function buildCorpus() {
|
|
7506
|
-
const all =
|
|
7271
|
+
const all = walkMdFiles2("src");
|
|
7507
7272
|
const ordered = [
|
|
7508
7273
|
...all.filter(isBrandRelevant),
|
|
7509
7274
|
...all.filter((f) => !isBrandRelevant(f))
|
|
@@ -7636,14 +7401,14 @@ function pickFont(raw) {
|
|
|
7636
7401
|
}
|
|
7637
7402
|
function persistBrand(brand, inputHash) {
|
|
7638
7403
|
const tmp = `${BRAND_FILE}.tmp`;
|
|
7639
|
-
|
|
7640
|
-
|
|
7404
|
+
fs20.writeFileSync(tmp, JSON.stringify(brand, null, 2), "utf-8");
|
|
7405
|
+
fs20.renameSync(tmp, BRAND_FILE);
|
|
7641
7406
|
const cache = { inputHash, generatedAt: Date.now() };
|
|
7642
|
-
|
|
7407
|
+
fs20.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
7643
7408
|
}
|
|
7644
7409
|
function readCache() {
|
|
7645
7410
|
try {
|
|
7646
|
-
const raw =
|
|
7411
|
+
const raw = fs20.readFileSync(CACHE_FILE, "utf-8");
|
|
7647
7412
|
const parsed = JSON.parse(raw);
|
|
7648
7413
|
if (parsed && typeof parsed.inputHash === "string" && typeof parsed.generatedAt === "number") {
|
|
7649
7414
|
return parsed;
|
|
@@ -8264,64 +8029,349 @@ async function runTurn(params) {
|
|
|
8264
8029
|
}
|
|
8265
8030
|
}
|
|
8266
8031
|
}
|
|
8267
|
-
const lastNonExcluded = toolCalls.filter(
|
|
8268
|
-
(tc) => !STATUS_EXCLUDED_TOOLS.has(tc.name)
|
|
8269
|
-
);
|
|
8270
|
-
lastCompletedTools = lastNonExcluded.map((tc) => tc.name).join(", ");
|
|
8271
|
-
lastCompletedInput = JSON.stringify(lastNonExcluded.at(-1)?.input ?? {});
|
|
8272
|
-
lastCompletedResult = results.at(-1)?.result ?? "";
|
|
8273
|
-
for (const r of results) {
|
|
8274
|
-
state.messages.push({
|
|
8275
|
-
role: "user",
|
|
8276
|
-
content: r.result,
|
|
8277
|
-
toolCallId: r.id,
|
|
8278
|
-
isToolError: r.isError
|
|
8279
|
-
});
|
|
8032
|
+
const lastNonExcluded = toolCalls.filter(
|
|
8033
|
+
(tc) => !STATUS_EXCLUDED_TOOLS.has(tc.name)
|
|
8034
|
+
);
|
|
8035
|
+
lastCompletedTools = lastNonExcluded.map((tc) => tc.name).join(", ");
|
|
8036
|
+
lastCompletedInput = JSON.stringify(lastNonExcluded.at(-1)?.input ?? {});
|
|
8037
|
+
lastCompletedResult = results.at(-1)?.result ?? "";
|
|
8038
|
+
for (const r of results) {
|
|
8039
|
+
state.messages.push({
|
|
8040
|
+
role: "user",
|
|
8041
|
+
content: r.result,
|
|
8042
|
+
toolCallId: r.id,
|
|
8043
|
+
isToolError: r.isError
|
|
8044
|
+
});
|
|
8045
|
+
}
|
|
8046
|
+
if (signal?.aborted) {
|
|
8047
|
+
onEvent({ type: "turn_cancelled" });
|
|
8048
|
+
saveSession(state);
|
|
8049
|
+
return;
|
|
8050
|
+
}
|
|
8051
|
+
}
|
|
8052
|
+
}
|
|
8053
|
+
var log12, BRAND_TRIGGERING_TOOLS, EXTERNAL_TOOLS, USER_BLOCKING_EXTERNAL_TOOLS;
|
|
8054
|
+
var init_agent = __esm({
|
|
8055
|
+
"src/agent.ts"() {
|
|
8056
|
+
"use strict";
|
|
8057
|
+
init_api();
|
|
8058
|
+
init_tools8();
|
|
8059
|
+
init_session();
|
|
8060
|
+
init_logger();
|
|
8061
|
+
init_usageLedger();
|
|
8062
|
+
init_parsePartialJson();
|
|
8063
|
+
init_statusWatcher();
|
|
8064
|
+
init_errors();
|
|
8065
|
+
init_cleanMessages();
|
|
8066
|
+
init_tools8();
|
|
8067
|
+
init_sentinel();
|
|
8068
|
+
init_trigger2();
|
|
8069
|
+
init_surfaces();
|
|
8070
|
+
init_toolRegistry();
|
|
8071
|
+
init_toolResultCap();
|
|
8072
|
+
log12 = createLogger("agent");
|
|
8073
|
+
BRAND_TRIGGERING_TOOLS = /* @__PURE__ */ new Set(["writeSpec", "editSpec"]);
|
|
8074
|
+
EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
8075
|
+
"promptUser",
|
|
8076
|
+
"setProjectOnboardingState",
|
|
8077
|
+
"presentPublishPlan",
|
|
8078
|
+
"confirmDestructiveAction",
|
|
8079
|
+
"runScenario",
|
|
8080
|
+
"runMethod",
|
|
8081
|
+
"queryDatabase",
|
|
8082
|
+
"browserCommand",
|
|
8083
|
+
"setProjectMetadata"
|
|
8084
|
+
]);
|
|
8085
|
+
USER_BLOCKING_EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
8086
|
+
"promptUser",
|
|
8087
|
+
"presentPublishPlan",
|
|
8088
|
+
"confirmDestructiveAction"
|
|
8089
|
+
]);
|
|
8090
|
+
}
|
|
8091
|
+
});
|
|
8092
|
+
|
|
8093
|
+
// src/prompt/static/projectContext.ts
|
|
8094
|
+
import fs21 from "fs";
|
|
8095
|
+
import path10 from "path";
|
|
8096
|
+
function loadProjectManifest() {
|
|
8097
|
+
try {
|
|
8098
|
+
const manifest = fs21.readFileSync("mindstudio.json", "utf-8");
|
|
8099
|
+
return `
|
|
8100
|
+
## Project Manifest (mindstudio.json)
|
|
8101
|
+
\`\`\`json
|
|
8102
|
+
${manifest}
|
|
8103
|
+
\`\`\``;
|
|
8104
|
+
} catch {
|
|
8105
|
+
return "";
|
|
8106
|
+
}
|
|
8107
|
+
}
|
|
8108
|
+
function loadSpecFileMetadata() {
|
|
8109
|
+
try {
|
|
8110
|
+
const files = walkMdFiles3("src");
|
|
8111
|
+
if (files.length === 0) {
|
|
8112
|
+
return "";
|
|
8113
|
+
}
|
|
8114
|
+
const entries = [];
|
|
8115
|
+
for (const filePath of files) {
|
|
8116
|
+
const { name, description, type } = parseFrontmatter3(filePath);
|
|
8117
|
+
let line = `- ${filePath}`;
|
|
8118
|
+
if (name) {
|
|
8119
|
+
line += ` \u2014 "${name}"`;
|
|
8120
|
+
}
|
|
8121
|
+
if (type) {
|
|
8122
|
+
line += ` (${type})`;
|
|
8123
|
+
}
|
|
8124
|
+
if (description) {
|
|
8125
|
+
line += ` \u2014 ${description}`;
|
|
8126
|
+
}
|
|
8127
|
+
entries.push(line);
|
|
8128
|
+
}
|
|
8129
|
+
return `
|
|
8130
|
+
## Spec Files
|
|
8131
|
+
${entries.join("\n")}`;
|
|
8132
|
+
} catch {
|
|
8133
|
+
return "";
|
|
8134
|
+
}
|
|
8135
|
+
}
|
|
8136
|
+
function walkMdFiles3(dir) {
|
|
8137
|
+
const results = [];
|
|
8138
|
+
try {
|
|
8139
|
+
const entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
8140
|
+
for (const entry of entries) {
|
|
8141
|
+
const full = path10.join(dir, entry.name);
|
|
8142
|
+
if (entry.isDirectory()) {
|
|
8143
|
+
results.push(...walkMdFiles3(full));
|
|
8144
|
+
} else if (entry.name.endsWith(".md")) {
|
|
8145
|
+
results.push(full);
|
|
8146
|
+
}
|
|
8147
|
+
}
|
|
8148
|
+
} catch {
|
|
8149
|
+
}
|
|
8150
|
+
return results.sort();
|
|
8151
|
+
}
|
|
8152
|
+
function parseFrontmatter3(filePath) {
|
|
8153
|
+
try {
|
|
8154
|
+
const content = fs21.readFileSync(filePath, "utf-8");
|
|
8155
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
8156
|
+
if (!match) {
|
|
8157
|
+
return { name: "", description: "", type: "" };
|
|
8158
|
+
}
|
|
8159
|
+
const fm = match[1];
|
|
8160
|
+
const name = fm.match(/^name:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
8161
|
+
const description = fm.match(/^description:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
8162
|
+
const type = fm.match(/^type:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
8163
|
+
return { name, description, type };
|
|
8164
|
+
} catch {
|
|
8165
|
+
return { name: "", description: "", type: "" };
|
|
8166
|
+
}
|
|
8167
|
+
}
|
|
8168
|
+
function loadPlanStatus(onboardingState) {
|
|
8169
|
+
try {
|
|
8170
|
+
const content = fs21.readFileSync(".remy-plan.md", "utf-8");
|
|
8171
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
8172
|
+
const status = match?.[1]?.match(/^status:\s*(.+)$/m)?.[1]?.trim();
|
|
8173
|
+
if (status === "pending" && onboardingState === "intake") {
|
|
8174
|
+
return `
|
|
8175
|
+
<pending_initial_plan>
|
|
8176
|
+
This is your initial plan, proposed via writePlan and awaiting the user's decision. The user approves it by pressing "Start Building" \u2014 you'll get an approveInitialPlan message when they do, and it will tell you to begin. Until then, stay in intake: keep discussing, and revise with writePlan if the user asks. Don't start building on your own, even if the user says "looks good" in chat.
|
|
8177
|
+
</pending_initial_plan>`;
|
|
8178
|
+
}
|
|
8179
|
+
if (status === "pending") {
|
|
8180
|
+
return `
|
|
8181
|
+
<pending_plan>
|
|
8182
|
+
You have a pending implementation plan in .remy-plan.md awaiting user approval. Do NOT begin implementing the plan until the user approves it. You may continue chatting, answering questions, and revising the plan if asked. To revise, call writePlan again with updated content. When the user approves the plan (via chat or any other signal), call updatePlanStatus with status "approved" before beginning any implementation work.
|
|
8183
|
+
</pending_plan>`;
|
|
8280
8184
|
}
|
|
8281
|
-
if (
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8185
|
+
if (status === "approved") {
|
|
8186
|
+
return `
|
|
8187
|
+
<approved_plan>
|
|
8188
|
+
The user has approved your implementation plan in .remy-plan.md. You may reference it during implementation. Delete the file when you have finished all planned work.
|
|
8189
|
+
</approved_plan>`;
|
|
8285
8190
|
}
|
|
8191
|
+
return "";
|
|
8192
|
+
} catch {
|
|
8193
|
+
return "";
|
|
8286
8194
|
}
|
|
8287
8195
|
}
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8196
|
+
function loadProjectFileListing() {
|
|
8197
|
+
try {
|
|
8198
|
+
const entries = fs21.readdirSync(".", { withFileTypes: true });
|
|
8199
|
+
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
8200
|
+
if (a.isDirectory() && !b.isDirectory()) {
|
|
8201
|
+
return -1;
|
|
8202
|
+
}
|
|
8203
|
+
if (!a.isDirectory() && b.isDirectory()) {
|
|
8204
|
+
return 1;
|
|
8205
|
+
}
|
|
8206
|
+
return a.name.localeCompare(b.name);
|
|
8207
|
+
}).map((e) => e.isDirectory() ? `${e.name}/` : e.name).join("\n");
|
|
8208
|
+
return `
|
|
8209
|
+
## Project Files
|
|
8210
|
+
\`\`\`
|
|
8211
|
+
${listing}
|
|
8212
|
+
\`\`\``;
|
|
8213
|
+
} catch {
|
|
8214
|
+
return "";
|
|
8215
|
+
}
|
|
8216
|
+
}
|
|
8217
|
+
var init_projectContext = __esm({
|
|
8218
|
+
"src/prompt/static/projectContext.ts"() {
|
|
8291
8219
|
"use strict";
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8220
|
+
}
|
|
8221
|
+
});
|
|
8222
|
+
|
|
8223
|
+
// src/prompt/index.ts
|
|
8224
|
+
function resolveIncludes(template) {
|
|
8225
|
+
const result = template.replace(
|
|
8226
|
+
/\{\{([^}]+)\}\}/g,
|
|
8227
|
+
(_, filePath) => readAsset("prompt", filePath.trim())
|
|
8228
|
+
);
|
|
8229
|
+
return result.replace(/\n{3,}/g, "\n\n").trim();
|
|
8230
|
+
}
|
|
8231
|
+
function buildSystemPrompt(onboardingState, viewContext) {
|
|
8232
|
+
const projectContext = [
|
|
8233
|
+
loadProjectManifest(),
|
|
8234
|
+
loadSpecFileMetadata(),
|
|
8235
|
+
loadProjectFileListing()
|
|
8236
|
+
].filter(Boolean).join("\n");
|
|
8237
|
+
const now = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
|
|
8238
|
+
month: "long",
|
|
8239
|
+
day: "numeric",
|
|
8240
|
+
year: "numeric"
|
|
8241
|
+
});
|
|
8242
|
+
const template = `
|
|
8243
|
+
{{static/identity.md}}
|
|
8244
|
+
|
|
8245
|
+
Current date: ${now}
|
|
8246
|
+
|
|
8247
|
+
<platform_docs>
|
|
8248
|
+
<platform>
|
|
8249
|
+
{{compiled/platform.md}}
|
|
8250
|
+
</platform>
|
|
8251
|
+
|
|
8252
|
+
<manifest>
|
|
8253
|
+
{{compiled/manifest.md}}
|
|
8254
|
+
</manifest>
|
|
8255
|
+
|
|
8256
|
+
<tables>
|
|
8257
|
+
{{compiled/tables.md}}
|
|
8258
|
+
</tables>
|
|
8259
|
+
|
|
8260
|
+
<methods>
|
|
8261
|
+
{{compiled/methods.md}}
|
|
8262
|
+
</methods>
|
|
8263
|
+
|
|
8264
|
+
<auth>
|
|
8265
|
+
{{compiled/auth.md}}
|
|
8266
|
+
</auth>
|
|
8267
|
+
|
|
8268
|
+
<dev_and_deploy>
|
|
8269
|
+
{{compiled/dev-and-deploy.md}}
|
|
8270
|
+
</dev_and_deploy>
|
|
8271
|
+
|
|
8272
|
+
<design>
|
|
8273
|
+
{{compiled/design.md}}
|
|
8274
|
+
</design>
|
|
8275
|
+
|
|
8276
|
+
<building_agent_interfaces>
|
|
8277
|
+
{{compiled/agent-interfaces.md}}
|
|
8278
|
+
</building_agent_interfaces>
|
|
8279
|
+
|
|
8280
|
+
<building_mcp_interfaces>
|
|
8281
|
+
{{compiled/mcp-interfaces.md}}
|
|
8282
|
+
</building_mcp_interfaces>
|
|
8283
|
+
|
|
8284
|
+
<media_cdn>
|
|
8285
|
+
{{compiled/media-cdn.md}}
|
|
8286
|
+
</media_cdn>
|
|
8287
|
+
|
|
8288
|
+
<interfaces>
|
|
8289
|
+
{{compiled/interfaces.md}}
|
|
8290
|
+
</interfaces>
|
|
8291
|
+
|
|
8292
|
+
<scenarios>
|
|
8293
|
+
{{compiled/scenarios.md}}
|
|
8294
|
+
</scenarios>
|
|
8295
|
+
|
|
8296
|
+
<secrets>
|
|
8297
|
+
{{compiled/secrets.md}}
|
|
8298
|
+
</secrets>
|
|
8299
|
+
</platform_docs>
|
|
8300
|
+
|
|
8301
|
+
<mindstudio_agent_sdk_docs>
|
|
8302
|
+
{{compiled/sdk-actions.md}}
|
|
8303
|
+
|
|
8304
|
+
{{compiled/task-agents.md}}
|
|
8305
|
+
</mindstudio_agent_sdk_docs>
|
|
8306
|
+
|
|
8307
|
+
<mindstudio_flavored_markdown_spec_docs>
|
|
8308
|
+
{{compiled/msfm.md}}
|
|
8309
|
+
</mindstudio_flavored_markdown_spec_docs>
|
|
8310
|
+
|
|
8311
|
+
<intake_mode_instructions>
|
|
8312
|
+
{{static/intake.md}}
|
|
8313
|
+
</intake_mode_instructions>
|
|
8314
|
+
|
|
8315
|
+
<spec_authoring_instructions>
|
|
8316
|
+
{{static/authoring.md}}
|
|
8317
|
+
</spec_authoring_instructions>
|
|
8318
|
+
|
|
8319
|
+
<team>
|
|
8320
|
+
{{static/team.md}}
|
|
8321
|
+
</team>
|
|
8322
|
+
|
|
8323
|
+
<code_authoring_instructions>
|
|
8324
|
+
{{static/coding.md}}
|
|
8325
|
+
|
|
8326
|
+
<typescript_lsp>
|
|
8327
|
+
{{static/lsp.md}}
|
|
8328
|
+
</typescript_lsp>
|
|
8329
|
+
</code_authoring_instructions>
|
|
8330
|
+
|
|
8331
|
+
<conversation_summaries>
|
|
8332
|
+
Your conversation history may include <prior_conversation_summary> blocks in the user's messages. These are automated summaries of earlier messages that have been compacted to save context space. The user does not see this summary, they see the full conversation history in their UI. Treat the summary as ground truth for what happened before, but do not reference it directly to the user ("as mentioned in the summary..."). Just continue naturally as if you remember the prior work.
|
|
8333
|
+
|
|
8334
|
+
Old tool results are periodically cleared from the conversation to save context space. This is automatic and expected \u2014 you don't need to note down or preserve information from tool results. If you need to reference something from an earlier tool call, just re-read the file or re-run the query.
|
|
8335
|
+
</conversation_summaries>
|
|
8336
|
+
|
|
8337
|
+
<project_onboarding>
|
|
8338
|
+
New projects progress through three onboarding states. The user might skip this entirely and jump straight into working on the existing scaffold (which defaults to onboardingFinished), but ideally new projects move through each phase:
|
|
8339
|
+
|
|
8340
|
+
- **intake**: Gathering requirements. The project has scaffold code (a "hello world" starter) but it's not the user's app yet. Focus on understanding what they want to build, not on the existing code. Intake ends with a plan proposal via writePlan.
|
|
8341
|
+
- **building**: The user approved the initial plan. The agent is writing the spec and building the app. This can take a while and involves heavy tool use (spec authoring, design expert consultation, code generation, verification, polishing).
|
|
8342
|
+
- **buildComplete**: The build is done. The frontend is showing the user a reveal experience (pitch deck, app preview). The agent does not need to do anything in this state; the frontend advances to onboardingFinished when the user is ready.
|
|
8343
|
+
- **onboardingFinished**: The project is built and ready. Full development mode with all tools available. From here on, keep spec and code in sync as changes are made.
|
|
8344
|
+
</project_onboarding>
|
|
8345
|
+
|
|
8346
|
+
{{static/instructions.md}}
|
|
8347
|
+
|
|
8348
|
+
<!-- cache_breakpoint -->
|
|
8349
|
+
|
|
8350
|
+
<current_project_onboarding_state>
|
|
8351
|
+
${onboardingState ?? "onboardingFinished"}
|
|
8352
|
+
</current_project_onboarding_state>
|
|
8353
|
+
|
|
8354
|
+
<project_context>
|
|
8355
|
+
${projectContext}
|
|
8356
|
+
</project_context>
|
|
8357
|
+
|
|
8358
|
+
${renderOrgContextBlock()}
|
|
8359
|
+
|
|
8360
|
+
<view_context>
|
|
8361
|
+
The user is currently in ${viewContext?.mode ?? "code"} mode.
|
|
8362
|
+
${viewContext?.activeFile ? `Active file: ${viewContext.activeFile}` : ""}
|
|
8363
|
+
</view_context>
|
|
8364
|
+
|
|
8365
|
+
${loadPlanStatus(onboardingState)}
|
|
8366
|
+
`;
|
|
8367
|
+
return resolveIncludes(template);
|
|
8368
|
+
}
|
|
8369
|
+
var init_prompt4 = __esm({
|
|
8370
|
+
"src/prompt/index.ts"() {
|
|
8371
|
+
"use strict";
|
|
8372
|
+
init_assets();
|
|
8373
|
+
init_projectContext();
|
|
8374
|
+
init_orgContext();
|
|
8325
8375
|
}
|
|
8326
8376
|
});
|
|
8327
8377
|
|
|
@@ -8721,9 +8771,8 @@ var init_headless = __esm({
|
|
|
8721
8771
|
init_logger();
|
|
8722
8772
|
init_config();
|
|
8723
8773
|
init_orgContext();
|
|
8724
|
-
|
|
8774
|
+
init_prompt4();
|
|
8725
8775
|
init_trigger();
|
|
8726
|
-
init_compaction();
|
|
8727
8776
|
init_trigger2();
|
|
8728
8777
|
init_lsp();
|
|
8729
8778
|
init_agent();
|
|
@@ -8835,7 +8884,9 @@ var init_headless = __esm({
|
|
|
8835
8884
|
const data = event.error ? { error: event.error } : {};
|
|
8836
8885
|
this.emit("compaction_complete", data, event.requestId);
|
|
8837
8886
|
this.sessionStats.compactionInProgress = false;
|
|
8838
|
-
|
|
8887
|
+
if (!event.error) {
|
|
8888
|
+
this.sessionStats.lastContextSize = 0;
|
|
8889
|
+
}
|
|
8839
8890
|
this.sessionStats.messageCount = this.state.messages.length;
|
|
8840
8891
|
this.persistStats();
|
|
8841
8892
|
}
|
|
@@ -8969,20 +9020,10 @@ var init_headless = __esm({
|
|
|
8969
9020
|
requestId,
|
|
8970
9021
|
model: this.opts.model
|
|
8971
9022
|
});
|
|
8972
|
-
this.
|
|
9023
|
+
applyPendingSummaries(this.state);
|
|
8973
9024
|
} catch {
|
|
8974
9025
|
}
|
|
8975
9026
|
}
|
|
8976
|
-
/** Drain pending compaction summaries and insert at a safe point. */
|
|
8977
|
-
applyPendingSummaries() {
|
|
8978
|
-
const summaries = getPendingSummaries();
|
|
8979
|
-
if (summaries.length === 0) {
|
|
8980
|
-
return;
|
|
8981
|
-
}
|
|
8982
|
-
const idx = findSafeInsertionPoint(this.state.messages);
|
|
8983
|
-
this.state.messages.splice(idx, 0, ...summaries);
|
|
8984
|
-
saveSession(this.state);
|
|
8985
|
-
}
|
|
8986
9027
|
onBackgroundComplete = (toolCallId, name, result, subAgentMessages) => {
|
|
8987
9028
|
this.pendingBlockUpdates.push({ toolCallId, result, subAgentMessages });
|
|
8988
9029
|
log15.info("Background complete", {
|
|
@@ -9311,7 +9352,7 @@ var init_headless = __esm({
|
|
|
9311
9352
|
error: err.message
|
|
9312
9353
|
});
|
|
9313
9354
|
}
|
|
9314
|
-
this.
|
|
9355
|
+
applyPendingSummaries(this.state);
|
|
9315
9356
|
this.applyPendingBlockUpdates();
|
|
9316
9357
|
}
|
|
9317
9358
|
async handleMessage(parsed, requestId) {
|
|
@@ -9615,7 +9656,7 @@ var init_headless = __esm({
|
|
|
9615
9656
|
model: this.opts.model
|
|
9616
9657
|
});
|
|
9617
9658
|
if (!this.running) {
|
|
9618
|
-
this.
|
|
9659
|
+
applyPendingSummaries(this.state);
|
|
9619
9660
|
}
|
|
9620
9661
|
this.emit("completed", { success: true }, requestId);
|
|
9621
9662
|
} catch (err) {
|
|
@@ -9795,7 +9836,8 @@ function MessageList({ turns }) {
|
|
|
9795
9836
|
|
|
9796
9837
|
// src/tui/App.tsx
|
|
9797
9838
|
init_agent();
|
|
9798
|
-
|
|
9839
|
+
init_trigger();
|
|
9840
|
+
init_prompt4();
|
|
9799
9841
|
init_session();
|
|
9800
9842
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
9801
9843
|
function App({ apiConfig, model }) {
|
|
@@ -9933,6 +9975,7 @@ Error: ${err.message}`,
|
|
|
9933
9975
|
done: true
|
|
9934
9976
|
}));
|
|
9935
9977
|
}
|
|
9978
|
+
applyPendingSummaries(agentState);
|
|
9936
9979
|
abortRef.current = null;
|
|
9937
9980
|
setIsRunning(false);
|
|
9938
9981
|
},
|