@mindstudio-ai/remy 0.1.239 → 0.1.241
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 +361 -302
- package/dist/index.js +1272 -1207
- 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;
|
|
@@ -2674,12 +2798,23 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2674
2798
|
}
|
|
2675
2799
|
});
|
|
2676
2800
|
|
|
2801
|
+
// src/projectRoot.ts
|
|
2802
|
+
var PROJECT_ROOT;
|
|
2803
|
+
var init_projectRoot = __esm({
|
|
2804
|
+
"src/projectRoot.ts"() {
|
|
2805
|
+
"use strict";
|
|
2806
|
+
PROJECT_ROOT = process.cwd();
|
|
2807
|
+
}
|
|
2808
|
+
});
|
|
2809
|
+
|
|
2677
2810
|
// src/tools/code/bash.ts
|
|
2678
2811
|
import { spawn as spawn2 } from "child_process";
|
|
2812
|
+
import path6 from "path";
|
|
2679
2813
|
var DEFAULT_TIMEOUT_MS, DEFAULT_MAX_LINES2, MAX_OUTPUT_BYTES, bashTool;
|
|
2680
2814
|
var init_bash = __esm({
|
|
2681
2815
|
"src/tools/code/bash.ts"() {
|
|
2682
2816
|
"use strict";
|
|
2817
|
+
init_projectRoot();
|
|
2683
2818
|
DEFAULT_TIMEOUT_MS = 12e4;
|
|
2684
2819
|
DEFAULT_MAX_LINES2 = 500;
|
|
2685
2820
|
MAX_OUTPUT_BYTES = 3e4;
|
|
@@ -2697,7 +2832,7 @@ var init_bash = __esm({
|
|
|
2697
2832
|
},
|
|
2698
2833
|
cwd: {
|
|
2699
2834
|
type: "string",
|
|
2700
|
-
description: "Working directory to
|
|
2835
|
+
description: "Working directory, relative to the project root. Defaults to the project root itself."
|
|
2701
2836
|
},
|
|
2702
2837
|
timeout: {
|
|
2703
2838
|
type: "number",
|
|
@@ -2716,7 +2851,9 @@ var init_bash = __esm({
|
|
|
2716
2851
|
const timeoutMs = input.timeout ? input.timeout * 1e3 : DEFAULT_TIMEOUT_MS;
|
|
2717
2852
|
return new Promise((resolve2) => {
|
|
2718
2853
|
const child = spawn2("sh", ["-c", input.command], {
|
|
2719
|
-
|
|
2854
|
+
// Pinned rather than inherited. `undefined` here means "wherever the
|
|
2855
|
+
// process happens to be", which is the project root only by luck.
|
|
2856
|
+
cwd: input.cwd ? path6.resolve(PROJECT_ROOT, input.cwd) : PROJECT_ROOT,
|
|
2720
2857
|
env: { ...process.env, FORCE_COLOR: "1" }
|
|
2721
2858
|
});
|
|
2722
2859
|
let output = "";
|
|
@@ -2976,7 +3113,7 @@ var init_glob = __esm({
|
|
|
2976
3113
|
|
|
2977
3114
|
// src/tools/code/listDir.ts
|
|
2978
3115
|
import fs14 from "fs/promises";
|
|
2979
|
-
import
|
|
3116
|
+
import path7 from "path";
|
|
2980
3117
|
async function readAndSort(dirPath) {
|
|
2981
3118
|
const entries = await fs14.readdir(dirPath, { withFileTypes: true });
|
|
2982
3119
|
return entries.filter((e) => !EXCLUDE.has(e.name)).sort((a, b) => {
|
|
@@ -2991,7 +3128,7 @@ async function readAndSort(dirPath) {
|
|
|
2991
3128
|
}
|
|
2992
3129
|
async function collapsePath(basePath, name) {
|
|
2993
3130
|
let display = name;
|
|
2994
|
-
let current =
|
|
3131
|
+
let current = path7.join(basePath, name);
|
|
2995
3132
|
for (; ; ) {
|
|
2996
3133
|
let children;
|
|
2997
3134
|
try {
|
|
@@ -3001,7 +3138,7 @@ async function collapsePath(basePath, name) {
|
|
|
3001
3138
|
}
|
|
3002
3139
|
if (children.length === 1 && children[0].isDirectory()) {
|
|
3003
3140
|
display += "/" + children[0].name;
|
|
3004
|
-
current =
|
|
3141
|
+
current = path7.join(current, children[0].name);
|
|
3005
3142
|
} else {
|
|
3006
3143
|
break;
|
|
3007
3144
|
}
|
|
@@ -3019,7 +3156,7 @@ function formatSize(bytes) {
|
|
|
3019
3156
|
}
|
|
3020
3157
|
async function formatFile(dirPath, name, indent) {
|
|
3021
3158
|
try {
|
|
3022
|
-
const stat = await fs14.stat(
|
|
3159
|
+
const stat = await fs14.stat(path7.join(dirPath, name));
|
|
3023
3160
|
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat.size)}`;
|
|
3024
3161
|
} catch {
|
|
3025
3162
|
return `${indent}${name}`;
|
|
@@ -3442,12 +3579,12 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
3442
3579
|
let existingUrl;
|
|
3443
3580
|
let onLog;
|
|
3444
3581
|
let model;
|
|
3445
|
-
let
|
|
3582
|
+
let path14;
|
|
3446
3583
|
let fullPage = true;
|
|
3447
3584
|
if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
|
|
3448
3585
|
prompt = promptOrOptions.prompt;
|
|
3449
3586
|
existingUrl = promptOrOptions.imageUrl;
|
|
3450
|
-
|
|
3587
|
+
path14 = promptOrOptions.path;
|
|
3451
3588
|
if (promptOrOptions.fullPage !== void 0) {
|
|
3452
3589
|
fullPage = promptOrOptions.fullPage;
|
|
3453
3590
|
}
|
|
@@ -3463,7 +3600,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
3463
3600
|
} else {
|
|
3464
3601
|
const ssResult = await sidecarRequest(
|
|
3465
3602
|
fullPage ? "/screenshot-full-page" : "/screenshot-viewport",
|
|
3466
|
-
|
|
3603
|
+
path14 ? { path: path14 } : void 0,
|
|
3467
3604
|
{ timeout: fullPage ? 12e4 : 3e4 }
|
|
3468
3605
|
);
|
|
3469
3606
|
url = ssResult?.url || ssResult?.screenshotUrl;
|
|
@@ -3604,25 +3741,6 @@ ${partial}` : "[INTERRUPTED] Tool execution was stopped.";
|
|
|
3604
3741
|
}
|
|
3605
3742
|
});
|
|
3606
3743
|
|
|
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
3744
|
// src/statusWatcher.ts
|
|
3627
3745
|
function startStatusWatcher(config) {
|
|
3628
3746
|
const { apiConfig, getContext, onStatus, interval = 5e3, signal } = config;
|
|
@@ -3679,195 +3797,11 @@ function startStatusWatcher(config) {
|
|
|
3679
3797
|
resume() {
|
|
3680
3798
|
pauseCount = Math.max(0, pauseCount - 1);
|
|
3681
3799
|
}
|
|
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];
|
|
3800
|
+
};
|
|
3866
3801
|
}
|
|
3867
|
-
var
|
|
3868
|
-
"src/
|
|
3802
|
+
var init_statusWatcher = __esm({
|
|
3803
|
+
"src/statusWatcher.ts"() {
|
|
3869
3804
|
"use strict";
|
|
3870
|
-
init_sentinel();
|
|
3871
3805
|
}
|
|
3872
3806
|
});
|
|
3873
3807
|
|
|
@@ -4485,7 +4419,7 @@ ${appSpec}
|
|
|
4485
4419
|
}
|
|
4486
4420
|
}
|
|
4487
4421
|
var BASE_PROMPT;
|
|
4488
|
-
var
|
|
4422
|
+
var init_prompt = __esm({
|
|
4489
4423
|
"src/subagents/browserAutomation/prompt.ts"() {
|
|
4490
4424
|
"use strict";
|
|
4491
4425
|
init_assets();
|
|
@@ -4632,7 +4566,7 @@ var init_browserAutomation = __esm({
|
|
|
4632
4566
|
"use strict";
|
|
4633
4567
|
init_runner();
|
|
4634
4568
|
init_tools();
|
|
4635
|
-
|
|
4569
|
+
init_prompt();
|
|
4636
4570
|
init_sidecar();
|
|
4637
4571
|
init_browserLock();
|
|
4638
4572
|
init_screenshot();
|
|
@@ -5442,15 +5376,15 @@ var init_editImages = __esm({
|
|
|
5442
5376
|
|
|
5443
5377
|
// src/subagents/common/context.ts
|
|
5444
5378
|
import fs16 from "fs";
|
|
5445
|
-
import
|
|
5446
|
-
function
|
|
5379
|
+
import path8 from "path";
|
|
5380
|
+
function walkMdFiles(dir, skip) {
|
|
5447
5381
|
const files = [];
|
|
5448
5382
|
try {
|
|
5449
5383
|
for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
|
|
5450
|
-
const full =
|
|
5384
|
+
const full = path8.join(dir, entry.name);
|
|
5451
5385
|
if (entry.isDirectory()) {
|
|
5452
5386
|
if (!skip?.has(entry.name)) {
|
|
5453
|
-
files.push(...
|
|
5387
|
+
files.push(...walkMdFiles(full, skip));
|
|
5454
5388
|
}
|
|
5455
5389
|
} else if (entry.name.endsWith(".md")) {
|
|
5456
5390
|
files.push(full);
|
|
@@ -5460,7 +5394,7 @@ function walkMdFiles2(dir, skip) {
|
|
|
5460
5394
|
}
|
|
5461
5395
|
return files.sort();
|
|
5462
5396
|
}
|
|
5463
|
-
function
|
|
5397
|
+
function parseFrontmatter(filePath) {
|
|
5464
5398
|
try {
|
|
5465
5399
|
const content = fs16.readFileSync(filePath, "utf-8");
|
|
5466
5400
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -5482,12 +5416,12 @@ function parseFrontmatter2(filePath) {
|
|
|
5482
5416
|
}
|
|
5483
5417
|
}
|
|
5484
5418
|
function loadSpecIndex() {
|
|
5485
|
-
const files =
|
|
5419
|
+
const files = walkMdFiles("src", /* @__PURE__ */ new Set(["roadmap"]));
|
|
5486
5420
|
if (files.length === 0) {
|
|
5487
5421
|
return "";
|
|
5488
5422
|
}
|
|
5489
5423
|
const lines = files.map((f) => {
|
|
5490
|
-
const fm =
|
|
5424
|
+
const fm = parseFrontmatter(f);
|
|
5491
5425
|
let line = `- ${f}`;
|
|
5492
5426
|
if (fm.name) {
|
|
5493
5427
|
line += ` \u2014 "${fm.name}"`;
|
|
@@ -5525,10 +5459,10 @@ ${indexJson.standalone.map((s) => `- ${s}`).join("\n")}`
|
|
|
5525
5459
|
}
|
|
5526
5460
|
} catch {
|
|
5527
5461
|
}
|
|
5528
|
-
const files =
|
|
5462
|
+
const files = walkMdFiles("src/roadmap");
|
|
5529
5463
|
if (files.length > 0) {
|
|
5530
5464
|
const lines = files.map((f) => {
|
|
5531
|
-
const fm =
|
|
5465
|
+
const fm = parseFrontmatter(f);
|
|
5532
5466
|
let line = `- ${f}`;
|
|
5533
5467
|
if (fm.name) {
|
|
5534
5468
|
line += ` \u2014 "${fm.name}"`;
|
|
@@ -5757,6 +5691,62 @@ var init_tools4 = __esm({
|
|
|
5757
5691
|
}
|
|
5758
5692
|
});
|
|
5759
5693
|
|
|
5694
|
+
// src/orgContext.ts
|
|
5695
|
+
async function initOrgContext(config) {
|
|
5696
|
+
try {
|
|
5697
|
+
cached = await fetchRemyContext(config);
|
|
5698
|
+
const orgDefaultModels2 = filterModelPicks(cached?.defaultModels);
|
|
5699
|
+
setOrgDefaultModels(orgDefaultModels2);
|
|
5700
|
+
log9.debug("org context loaded", {
|
|
5701
|
+
delegatedAvailable: cached?.auth?.delegatedAvailable ?? false,
|
|
5702
|
+
requireDelegatedOnly: cached?.auth?.requireDelegatedOnly ?? false,
|
|
5703
|
+
hasOrgName: !!cached?.org?.name,
|
|
5704
|
+
orgDefaultModels: orgDefaultModels2
|
|
5705
|
+
});
|
|
5706
|
+
} catch (err) {
|
|
5707
|
+
cached = null;
|
|
5708
|
+
setOrgDefaultModels({});
|
|
5709
|
+
log9.debug("org context init failed", { error: err.message });
|
|
5710
|
+
}
|
|
5711
|
+
}
|
|
5712
|
+
function getOrgContext() {
|
|
5713
|
+
return cached;
|
|
5714
|
+
}
|
|
5715
|
+
function renderOrgContextBlock() {
|
|
5716
|
+
const ctx = cached;
|
|
5717
|
+
const auth = ctx?.auth;
|
|
5718
|
+
if (!auth || !auth.delegatedAvailable && !auth.requireDelegatedOnly) {
|
|
5719
|
+
return "";
|
|
5720
|
+
}
|
|
5721
|
+
const lines = ["<org_auth_context>"];
|
|
5722
|
+
if (ctx?.org?.name && ctx?.org?.name !== "Personal Workspace") {
|
|
5723
|
+
lines.push(`This app is owned by the organization "${ctx.org.name}".`);
|
|
5724
|
+
}
|
|
5725
|
+
if (auth.delegatedAvailable) {
|
|
5726
|
+
lines.push(
|
|
5727
|
+
'"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.'
|
|
5728
|
+
);
|
|
5729
|
+
}
|
|
5730
|
+
if (auth.requireDelegatedOnly) {
|
|
5731
|
+
lines.push(
|
|
5732
|
+
"This organization requires delegated sign-in: non-delegated human auth methods (email-code, sms-code) are blocked at the platform edge for its apps."
|
|
5733
|
+
);
|
|
5734
|
+
}
|
|
5735
|
+
lines.push("</org_auth_context>");
|
|
5736
|
+
return lines.join("\n");
|
|
5737
|
+
}
|
|
5738
|
+
var log9, cached;
|
|
5739
|
+
var init_orgContext = __esm({
|
|
5740
|
+
"src/orgContext.ts"() {
|
|
5741
|
+
"use strict";
|
|
5742
|
+
init_api();
|
|
5743
|
+
init_surfaces();
|
|
5744
|
+
init_logger();
|
|
5745
|
+
log9 = createLogger("orgContext");
|
|
5746
|
+
cached = null;
|
|
5747
|
+
}
|
|
5748
|
+
});
|
|
5749
|
+
|
|
5760
5750
|
// src/subagents/designExpert/data/sampleCache.ts
|
|
5761
5751
|
import fs17 from "fs";
|
|
5762
5752
|
function generateIndices(poolSize, sampleSize) {
|
|
@@ -5978,7 +5968,7 @@ This project is in the "${state}" phase. The codebase is a placeholder scaffold
|
|
|
5978
5968
|
return prompt;
|
|
5979
5969
|
}
|
|
5980
5970
|
var SUBAGENT, RUNTIME_PLACEHOLDERS, PROMPT_TEMPLATE;
|
|
5981
|
-
var
|
|
5971
|
+
var init_prompt2 = __esm({
|
|
5982
5972
|
"src/subagents/designExpert/prompt.ts"() {
|
|
5983
5973
|
"use strict";
|
|
5984
5974
|
init_assets();
|
|
@@ -6100,7 +6090,7 @@ var init_designExpert = __esm({
|
|
|
6100
6090
|
init_runner();
|
|
6101
6091
|
init_tools4();
|
|
6102
6092
|
init_tools2();
|
|
6103
|
-
|
|
6093
|
+
init_prompt2();
|
|
6104
6094
|
init_history();
|
|
6105
6095
|
init_surfaces();
|
|
6106
6096
|
init_writeFile();
|
|
@@ -6212,9 +6202,9 @@ var init_tools5 = __esm({
|
|
|
6212
6202
|
|
|
6213
6203
|
// src/subagents/productVision/executor.ts
|
|
6214
6204
|
import fs18 from "fs";
|
|
6215
|
-
import
|
|
6205
|
+
import path9 from "path";
|
|
6216
6206
|
function resolve(filePath) {
|
|
6217
|
-
return
|
|
6207
|
+
return path9.join(ROADMAP_DIR, filePath);
|
|
6218
6208
|
}
|
|
6219
6209
|
async function executeVisionTool(name, input, context) {
|
|
6220
6210
|
switch (name) {
|
|
@@ -6336,7 +6326,7 @@ function getProductVisionPrompt() {
|
|
|
6336
6326
|
return parts.join("\n\n");
|
|
6337
6327
|
}
|
|
6338
6328
|
var BASE_PROMPT3;
|
|
6339
|
-
var
|
|
6329
|
+
var init_prompt3 = __esm({
|
|
6340
6330
|
"src/subagents/productVision/prompt.ts"() {
|
|
6341
6331
|
"use strict";
|
|
6342
6332
|
init_assets();
|
|
@@ -6356,7 +6346,7 @@ var init_productVision = __esm({
|
|
|
6356
6346
|
init_tools5();
|
|
6357
6347
|
init_tools2();
|
|
6358
6348
|
init_executor();
|
|
6359
|
-
|
|
6349
|
+
init_prompt3();
|
|
6360
6350
|
init_history();
|
|
6361
6351
|
init_surfaces();
|
|
6362
6352
|
productVisionTool = {
|
|
@@ -6622,540 +6612,328 @@ ${readAsset(
|
|
|
6622
6612
|
}
|
|
6623
6613
|
const specIndex = loadSpecIndex();
|
|
6624
6614
|
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).";
|
|
6615
|
+
BASE_PROMPT5,
|
|
6616
|
+
loadPlatformBrief(),
|
|
6617
|
+
MSFM_DOCS,
|
|
6618
|
+
"<!-- cache_breakpoint -->"
|
|
6619
|
+
];
|
|
6620
|
+
if (specIndex) {
|
|
6621
|
+
parts.push(specIndex);
|
|
6802
6622
|
}
|
|
6803
|
-
const
|
|
6804
|
-
const
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6623
|
+
const system = parts.join("\n\n");
|
|
6624
|
+
const result = await runSubAgent({
|
|
6625
|
+
system,
|
|
6626
|
+
task: input.task,
|
|
6627
|
+
tools: SPEC_SYNC_TOOLS,
|
|
6628
|
+
externalTools: /* @__PURE__ */ new Set(),
|
|
6629
|
+
executeTool: (name, toolInput) => executeTool(name, toolInput, context),
|
|
6630
|
+
apiConfig: context.apiConfig,
|
|
6631
|
+
model: resolveModel("specSync", context.models, context.model),
|
|
6632
|
+
subAgentId: "specSync",
|
|
6633
|
+
signal: context.signal,
|
|
6634
|
+
parentToolId: context.toolCallId,
|
|
6635
|
+
requestId: context.requestId,
|
|
6636
|
+
onEvent: context.onEvent,
|
|
6637
|
+
resolveExternalTool: context.resolveExternalTool,
|
|
6638
|
+
toolRegistry: context.toolRegistry,
|
|
6639
|
+
// Always background + serialized: never blocks Remy's turn, and two
|
|
6640
|
+
// reconciliations queue (FIFO) instead of running at once.
|
|
6641
|
+
background: true,
|
|
6642
|
+
acquireLock: acquireSpecSyncLock,
|
|
6643
|
+
onBackgroundComplete: (bgResult) => {
|
|
6644
|
+
context.onBackgroundComplete?.(
|
|
6645
|
+
context.toolCallId,
|
|
6646
|
+
"specSync",
|
|
6647
|
+
bgResult.text,
|
|
6648
|
+
bgResult.messages
|
|
6815
6649
|
);
|
|
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
6650
|
}
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
}
|
|
6651
|
+
});
|
|
6652
|
+
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
6653
|
+
return result.text;
|
|
6829
6654
|
}
|
|
6830
6655
|
};
|
|
6831
6656
|
}
|
|
6832
6657
|
});
|
|
6833
6658
|
|
|
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"() {
|
|
6659
|
+
// src/tools/common/scrapeWebUrl.ts
|
|
6660
|
+
var scrapeWebUrlTool;
|
|
6661
|
+
var init_scrapeWebUrl2 = __esm({
|
|
6662
|
+
"src/tools/common/scrapeWebUrl.ts"() {
|
|
6854
6663
|
"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
|
-
});
|
|
6664
|
+
init_runMindstudioCli();
|
|
6665
|
+
scrapeWebUrlTool = {
|
|
6666
|
+
clearable: false,
|
|
6667
|
+
definition: {
|
|
6668
|
+
name: "scrapeWebUrl",
|
|
6669
|
+
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",
|
|
6670
|
+
inputSchema: {
|
|
6671
|
+
type: "object",
|
|
6672
|
+
properties: {
|
|
6673
|
+
url: {
|
|
6674
|
+
type: "string",
|
|
6675
|
+
description: "The URL to fetch."
|
|
6676
|
+
},
|
|
6677
|
+
screenshot: {
|
|
6678
|
+
type: "boolean",
|
|
6679
|
+
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."
|
|
6680
|
+
}
|
|
6681
|
+
},
|
|
6682
|
+
required: ["url"]
|
|
6683
|
+
}
|
|
6684
|
+
},
|
|
6685
|
+
async execute(input, context) {
|
|
6686
|
+
const url = input.url;
|
|
6687
|
+
const screenshot = input.screenshot;
|
|
6688
|
+
const pageOptions = { onlyMainContent: true };
|
|
6689
|
+
if (screenshot) {
|
|
6690
|
+
pageOptions.screenshot = true;
|
|
6691
|
+
}
|
|
6692
|
+
return runMindstudioCli(
|
|
6693
|
+
[
|
|
6694
|
+
"scrape-url",
|
|
6695
|
+
"--url",
|
|
6696
|
+
url,
|
|
6697
|
+
"--page-options",
|
|
6698
|
+
JSON.stringify(pageOptions)
|
|
6699
|
+
],
|
|
6700
|
+
{ onLog: context?.onLog }
|
|
6701
|
+
);
|
|
7016
6702
|
}
|
|
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;
|
|
6703
|
+
};
|
|
7025
6704
|
}
|
|
7026
|
-
|
|
6705
|
+
});
|
|
6706
|
+
|
|
6707
|
+
// src/tools/spec/writeBuildOverview.ts
|
|
6708
|
+
import fs19 from "fs";
|
|
6709
|
+
function initialDelivery() {
|
|
6710
|
+
return `### Your deliverable
|
|
6711
|
+
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.
|
|
6712
|
+
|
|
6713
|
+
<overview_shell>
|
|
6714
|
+
${OVERVIEW_SHELL}
|
|
6715
|
+
</overview_shell>
|
|
6716
|
+
|
|
6717
|
+
Reply with a one-line summary of what you wrote \u2014 not the HTML.`;
|
|
7027
6718
|
}
|
|
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;
|
|
6719
|
+
function refreshDelivery() {
|
|
6720
|
+
return `### Your deliverable
|
|
6721
|
+
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.
|
|
6722
|
+
|
|
6723
|
+
Reply with a one-line summary of what you changed \u2014 not the HTML.`;
|
|
7044
6724
|
}
|
|
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
|
-
|
|
6725
|
+
var OVERVIEW_FILE, DESIGN_BRIEF, OVERVIEW_SHELL, buildOverviewTool;
|
|
6726
|
+
var init_writeBuildOverview = __esm({
|
|
6727
|
+
"src/tools/spec/writeBuildOverview.ts"() {
|
|
6728
|
+
"use strict";
|
|
6729
|
+
init_designExpert();
|
|
6730
|
+
OVERVIEW_FILE = "src/overview.html";
|
|
6731
|
+
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).
|
|
6732
|
+
|
|
6733
|
+
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.
|
|
6734
|
+
|
|
6735
|
+
### The single hard rule
|
|
6736
|
+
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.
|
|
6737
|
+
|
|
6738
|
+
### What it is (and is not)
|
|
6739
|
+
- 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."
|
|
6740
|
+
- 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.
|
|
6741
|
+
- NOT a slide deck \u2014 no theatrics, no building to a climax, no persuasion.
|
|
6742
|
+
- NOT a docs site \u2014 no raised or shadowed cards, no hover-lift, no clickable-feeling surfaces, no sticky table of contents.
|
|
6743
|
+
- NOT a sparse memo \u2014 a restrained single-column page under-sells the work.
|
|
6744
|
+
- No header chrome and no footer: open straight on the app's logo and name so it sits flush with the IDE.
|
|
6745
|
+
|
|
6746
|
+
### Constraints
|
|
6747
|
+
- A single self-contained HTML file. Fonts may load from a CDN; everything else (CSS, the logo SVG) is inline.
|
|
6748
|
+
- Responsive: fills the embedded panel width and collapses gracefully at narrow widths.`;
|
|
6749
|
+
OVERVIEW_SHELL = `<!DOCTYPE html>
|
|
6750
|
+
<html lang="en">
|
|
6751
|
+
<head>
|
|
6752
|
+
<meta charset="utf-8">
|
|
6753
|
+
<!-- This renders inside an iframe in the IDE. Keep it a simple, self-contained
|
|
6754
|
+
static document \u2014 no scroll/zoom/accessibility scaffolding beyond this head.
|
|
6755
|
+
The user-scalable=no viewport is intentional; leave it. -->
|
|
6756
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
|
6757
|
+
<title>Build Overview</title>
|
|
6758
|
+
<!-- SKIN: link the app's fonts here (CDN \u2014 e.g. Fontshare / Google Fonts). -->
|
|
6759
|
+
<style>
|
|
6760
|
+
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
6761
|
+
</style>
|
|
6762
|
+
</head>
|
|
6763
|
+
<body>
|
|
6764
|
+
<!-- Compose the Build Overview here. Layout, sections, type, and styling are
|
|
6765
|
+
yours \u2014 keep everything inline and self-contained, brand-skinned from the
|
|
6766
|
+
app's spec. -->
|
|
6767
|
+
</body>
|
|
6768
|
+
</html>`;
|
|
6769
|
+
buildOverviewTool = {
|
|
6770
|
+
clearable: false,
|
|
6771
|
+
definition: {
|
|
6772
|
+
name: "writeBuildOverview",
|
|
6773
|
+
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.",
|
|
6774
|
+
inputSchema: {
|
|
6775
|
+
type: "object",
|
|
6776
|
+
properties: {
|
|
6777
|
+
content: {
|
|
6778
|
+
type: "string",
|
|
6779
|
+
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."
|
|
6780
|
+
}
|
|
6781
|
+
},
|
|
6782
|
+
required: ["content"]
|
|
6783
|
+
}
|
|
6784
|
+
},
|
|
6785
|
+
async execute(input, context) {
|
|
6786
|
+
if (!context) {
|
|
6787
|
+
return "Error: writeBuildOverview requires execution context for design expert delegation";
|
|
6788
|
+
}
|
|
6789
|
+
const content = (input.content ?? "").trim();
|
|
6790
|
+
if (!content) {
|
|
6791
|
+
return "Error: writeBuildOverview requires non-empty `content` (the overview copy).";
|
|
6792
|
+
}
|
|
6793
|
+
const exists = fs19.existsSync(OVERVIEW_FILE);
|
|
6794
|
+
const task = `<overview_copy>${content}</overview_copy>
|
|
6795
|
+
|
|
6796
|
+
${DESIGN_BRIEF}
|
|
6797
|
+
|
|
6798
|
+
${exists ? refreshDelivery() : initialDelivery()}`;
|
|
6799
|
+
try {
|
|
6800
|
+
if (exists) {
|
|
6801
|
+
input.background = true;
|
|
6802
|
+
const result2 = await runDesignExpertRender(
|
|
6803
|
+
{ task, background: true, reportingName: "writeBuildOverview" },
|
|
6804
|
+
context
|
|
6805
|
+
);
|
|
6806
|
+
context.subAgentMessages?.set(context.toolCallId, result2.messages);
|
|
6807
|
+
return result2.text;
|
|
6808
|
+
}
|
|
6809
|
+
const result = await runDesignExpertRender({ task }, context);
|
|
6810
|
+
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
6811
|
+
if (!fs19.existsSync(OVERVIEW_FILE)) {
|
|
6812
|
+
return `Error: the design expert did not write ${OVERVIEW_FILE}. Its reply was:
|
|
6813
|
+
${result.text}`;
|
|
6814
|
+
}
|
|
6815
|
+
return `Build overview written to ${OVERVIEW_FILE}. ${result.text}`;
|
|
6816
|
+
} catch (err) {
|
|
6817
|
+
return `Error generating build overview: ${err.message}`;
|
|
6818
|
+
}
|
|
7074
6819
|
}
|
|
7075
|
-
}
|
|
7076
|
-
if (removed > 0) {
|
|
7077
|
-
log9.info("Session archives pruned", {
|
|
7078
|
-
removed,
|
|
7079
|
-
freedBytes: freed,
|
|
7080
|
-
keptBytes: kept
|
|
7081
|
-
});
|
|
7082
|
-
}
|
|
7083
|
-
} catch {
|
|
6820
|
+
};
|
|
7084
6821
|
}
|
|
6822
|
+
});
|
|
6823
|
+
|
|
6824
|
+
// src/tools/index.ts
|
|
6825
|
+
function deriveContext(parent, toolCallId) {
|
|
6826
|
+
return { ...parent, toolCallId };
|
|
7085
6827
|
}
|
|
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;
|
|
6828
|
+
function getToolDefinitions(_onboardingState) {
|
|
6829
|
+
return ALL_TOOLS.map((t) => t.definition);
|
|
7113
6830
|
}
|
|
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
|
-
}
|
|
6831
|
+
function getToolByName(name) {
|
|
6832
|
+
return ALL_TOOLS.find((t) => t.definition.name === name);
|
|
7125
6833
|
}
|
|
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
|
-
});
|
|
6834
|
+
function executeTool(name, input, context) {
|
|
6835
|
+
const tool = getToolByName(name);
|
|
6836
|
+
if (!tool) {
|
|
6837
|
+
return Promise.resolve(`Error: Unknown tool "${name}"`);
|
|
7143
6838
|
}
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
var
|
|
7147
|
-
|
|
6839
|
+
return tool.execute(input, context);
|
|
6840
|
+
}
|
|
6841
|
+
var ALL_TOOLS, CLEARABLE_TOOLS, SUBAGENT_TOOL_NAMES;
|
|
6842
|
+
var init_tools8 = __esm({
|
|
6843
|
+
"src/tools/index.ts"() {
|
|
7148
6844
|
"use strict";
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
6845
|
+
init_readSpec();
|
|
6846
|
+
init_writeSpec();
|
|
6847
|
+
init_editSpec();
|
|
6848
|
+
init_listSpecFiles();
|
|
6849
|
+
init_presentPublishPlan();
|
|
6850
|
+
init_writePlan();
|
|
6851
|
+
init_updatePlanStatus();
|
|
6852
|
+
init_setProjectOnboardingState();
|
|
6853
|
+
init_promptUser();
|
|
6854
|
+
init_confirmDestructiveAction();
|
|
6855
|
+
init_sdkConsultant();
|
|
6856
|
+
init_searchGoogle();
|
|
6857
|
+
init_setProjectMetadata();
|
|
6858
|
+
init_compactConversation();
|
|
6859
|
+
init_readFile();
|
|
6860
|
+
init_writeFile();
|
|
6861
|
+
init_editFile();
|
|
6862
|
+
init_bash();
|
|
6863
|
+
init_grep();
|
|
6864
|
+
init_glob();
|
|
6865
|
+
init_listDir();
|
|
6866
|
+
init_editsFinished();
|
|
6867
|
+
init_lspDiagnostics();
|
|
6868
|
+
init_restartProcess();
|
|
6869
|
+
init_runScenario();
|
|
6870
|
+
init_runMethod();
|
|
6871
|
+
init_queryDatabase();
|
|
6872
|
+
init_screenshot2();
|
|
6873
|
+
init_browserAutomation();
|
|
6874
|
+
init_designExpert();
|
|
6875
|
+
init_productVision();
|
|
6876
|
+
init_codeSanityCheck();
|
|
6877
|
+
init_copyEditor();
|
|
6878
|
+
init_specSync();
|
|
6879
|
+
init_scrapeWebUrl2();
|
|
6880
|
+
init_writeBuildOverview();
|
|
6881
|
+
ALL_TOOLS = [
|
|
6882
|
+
// Common
|
|
6883
|
+
setProjectOnboardingStateTool,
|
|
6884
|
+
promptUserTool,
|
|
6885
|
+
confirmDestructiveActionTool,
|
|
6886
|
+
askMindStudioSdkTool,
|
|
6887
|
+
scrapeWebUrlTool,
|
|
6888
|
+
searchGoogleTool,
|
|
6889
|
+
setProjectMetadataTool,
|
|
6890
|
+
designExpertTool,
|
|
6891
|
+
productVisionTool,
|
|
6892
|
+
codeSanityCheckTool,
|
|
6893
|
+
copyEditorTool,
|
|
6894
|
+
specSyncTool,
|
|
6895
|
+
// no-ops until onboardingFinished; kept here so the tool list stays cache-stable
|
|
6896
|
+
buildOverviewTool,
|
|
6897
|
+
compactConversationTool,
|
|
6898
|
+
// Post-onboarding
|
|
6899
|
+
presentPublishPlanTool,
|
|
6900
|
+
writePlanTool,
|
|
6901
|
+
updatePlanStatusTool,
|
|
6902
|
+
// Spec
|
|
6903
|
+
readSpecTool,
|
|
6904
|
+
writeSpecTool,
|
|
6905
|
+
editSpecTool,
|
|
6906
|
+
listSpecFilesTool,
|
|
6907
|
+
// Code
|
|
6908
|
+
readFileTool,
|
|
6909
|
+
writeFileTool,
|
|
6910
|
+
editFileTool,
|
|
6911
|
+
bashTool,
|
|
6912
|
+
grepTool,
|
|
6913
|
+
globTool,
|
|
6914
|
+
listDirTool,
|
|
6915
|
+
editsFinishedTool,
|
|
6916
|
+
runScenarioTool,
|
|
6917
|
+
runMethodTool,
|
|
6918
|
+
queryDatabaseTool,
|
|
6919
|
+
screenshotTool,
|
|
6920
|
+
browserAutomationTool,
|
|
6921
|
+
// LSP
|
|
6922
|
+
lspDiagnosticsTool,
|
|
6923
|
+
restartProcessTool
|
|
6924
|
+
];
|
|
6925
|
+
CLEARABLE_TOOLS = new Set(
|
|
6926
|
+
ALL_TOOLS.filter((t) => t.clearable).map((t) => t.definition.name)
|
|
6927
|
+
);
|
|
6928
|
+
SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
6929
|
+
"visualDesignExpert",
|
|
6930
|
+
"productVision",
|
|
6931
|
+
"codeSanityCheck",
|
|
6932
|
+
"copyEditor",
|
|
6933
|
+
"specSync",
|
|
6934
|
+
"runAutomatedBrowserTest",
|
|
6935
|
+
"askMindStudioSdk"
|
|
6936
|
+
]);
|
|
7159
6937
|
}
|
|
7160
6938
|
});
|
|
7161
6939
|
|
|
@@ -7369,7 +7147,7 @@ var init_errors = __esm({
|
|
|
7369
7147
|
});
|
|
7370
7148
|
|
|
7371
7149
|
// src/brandExtraction/index.ts
|
|
7372
|
-
import
|
|
7150
|
+
import fs20 from "fs";
|
|
7373
7151
|
import path10 from "path";
|
|
7374
7152
|
import { createHash } from "crypto";
|
|
7375
7153
|
async function runExtraction(apiConfig, model) {
|
|
@@ -7393,12 +7171,12 @@ function isBrandRelevant(filePath) {
|
|
|
7393
7171
|
if (filePath === path10.join("src", "app.md")) {
|
|
7394
7172
|
return true;
|
|
7395
7173
|
}
|
|
7396
|
-
const { type } =
|
|
7174
|
+
const { type } = parseFrontmatter2(filePath);
|
|
7397
7175
|
return type.startsWith("design/color") || type.startsWith("design/typography");
|
|
7398
7176
|
}
|
|
7399
7177
|
function computeInputHash() {
|
|
7400
7178
|
const entries = [];
|
|
7401
|
-
for (const filePath of
|
|
7179
|
+
for (const filePath of walkMdFiles2("src")) {
|
|
7402
7180
|
if (isBrandRelevant(filePath)) {
|
|
7403
7181
|
entries.push({ path: filePath, content: readSafe(filePath) });
|
|
7404
7182
|
}
|
|
@@ -7416,19 +7194,19 @@ function sha256(input) {
|
|
|
7416
7194
|
}
|
|
7417
7195
|
function readSafe(filePath) {
|
|
7418
7196
|
try {
|
|
7419
|
-
return
|
|
7197
|
+
return fs20.readFileSync(filePath, "utf-8");
|
|
7420
7198
|
} catch {
|
|
7421
7199
|
return "";
|
|
7422
7200
|
}
|
|
7423
7201
|
}
|
|
7424
|
-
function
|
|
7202
|
+
function walkMdFiles2(dir) {
|
|
7425
7203
|
const results = [];
|
|
7426
7204
|
try {
|
|
7427
|
-
const entries =
|
|
7205
|
+
const entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
7428
7206
|
for (const entry of entries) {
|
|
7429
7207
|
const full = path10.join(dir, entry.name);
|
|
7430
7208
|
if (entry.isDirectory()) {
|
|
7431
|
-
results.push(...
|
|
7209
|
+
results.push(...walkMdFiles2(full));
|
|
7432
7210
|
} else if (entry.name.endsWith(".md")) {
|
|
7433
7211
|
results.push(full);
|
|
7434
7212
|
}
|
|
@@ -7437,9 +7215,9 @@ function walkMdFiles3(dir) {
|
|
|
7437
7215
|
}
|
|
7438
7216
|
return results.sort();
|
|
7439
7217
|
}
|
|
7440
|
-
function
|
|
7218
|
+
function parseFrontmatter2(filePath) {
|
|
7441
7219
|
try {
|
|
7442
|
-
const content =
|
|
7220
|
+
const content = fs20.readFileSync(filePath, "utf-8");
|
|
7443
7221
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
7444
7222
|
if (!match) {
|
|
7445
7223
|
return { type: "" };
|
|
@@ -7503,7 +7281,7 @@ async function extractBrand(apiConfig, model) {
|
|
|
7503
7281
|
return validateBrand(parsed);
|
|
7504
7282
|
}
|
|
7505
7283
|
function buildCorpus() {
|
|
7506
|
-
const all =
|
|
7284
|
+
const all = walkMdFiles2("src");
|
|
7507
7285
|
const ordered = [
|
|
7508
7286
|
...all.filter(isBrandRelevant),
|
|
7509
7287
|
...all.filter((f) => !isBrandRelevant(f))
|
|
@@ -7636,14 +7414,14 @@ function pickFont(raw) {
|
|
|
7636
7414
|
}
|
|
7637
7415
|
function persistBrand(brand, inputHash) {
|
|
7638
7416
|
const tmp = `${BRAND_FILE}.tmp`;
|
|
7639
|
-
|
|
7640
|
-
|
|
7417
|
+
fs20.writeFileSync(tmp, JSON.stringify(brand, null, 2), "utf-8");
|
|
7418
|
+
fs20.renameSync(tmp, BRAND_FILE);
|
|
7641
7419
|
const cache = { inputHash, generatedAt: Date.now() };
|
|
7642
|
-
|
|
7420
|
+
fs20.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
7643
7421
|
}
|
|
7644
7422
|
function readCache() {
|
|
7645
7423
|
try {
|
|
7646
|
-
const raw =
|
|
7424
|
+
const raw = fs20.readFileSync(CACHE_FILE, "utf-8");
|
|
7647
7425
|
const parsed = JSON.parse(raw);
|
|
7648
7426
|
if (parsed && typeof parsed.inputHash === "string" && typeof parsed.generatedAt === "number") {
|
|
7649
7427
|
return parsed;
|
|
@@ -8264,70 +8042,364 @@ async function runTurn(params) {
|
|
|
8264
8042
|
}
|
|
8265
8043
|
}
|
|
8266
8044
|
}
|
|
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
|
-
});
|
|
8045
|
+
const lastNonExcluded = toolCalls.filter(
|
|
8046
|
+
(tc) => !STATUS_EXCLUDED_TOOLS.has(tc.name)
|
|
8047
|
+
);
|
|
8048
|
+
lastCompletedTools = lastNonExcluded.map((tc) => tc.name).join(", ");
|
|
8049
|
+
lastCompletedInput = JSON.stringify(lastNonExcluded.at(-1)?.input ?? {});
|
|
8050
|
+
lastCompletedResult = results.at(-1)?.result ?? "";
|
|
8051
|
+
for (const r of results) {
|
|
8052
|
+
state.messages.push({
|
|
8053
|
+
role: "user",
|
|
8054
|
+
content: r.result,
|
|
8055
|
+
toolCallId: r.id,
|
|
8056
|
+
isToolError: r.isError
|
|
8057
|
+
});
|
|
8058
|
+
}
|
|
8059
|
+
if (signal?.aborted) {
|
|
8060
|
+
onEvent({ type: "turn_cancelled" });
|
|
8061
|
+
saveSession(state);
|
|
8062
|
+
return;
|
|
8063
|
+
}
|
|
8064
|
+
}
|
|
8065
|
+
}
|
|
8066
|
+
var log12, BRAND_TRIGGERING_TOOLS, EXTERNAL_TOOLS, USER_BLOCKING_EXTERNAL_TOOLS;
|
|
8067
|
+
var init_agent = __esm({
|
|
8068
|
+
"src/agent.ts"() {
|
|
8069
|
+
"use strict";
|
|
8070
|
+
init_api();
|
|
8071
|
+
init_tools8();
|
|
8072
|
+
init_session();
|
|
8073
|
+
init_logger();
|
|
8074
|
+
init_usageLedger();
|
|
8075
|
+
init_parsePartialJson();
|
|
8076
|
+
init_statusWatcher();
|
|
8077
|
+
init_errors();
|
|
8078
|
+
init_cleanMessages();
|
|
8079
|
+
init_tools8();
|
|
8080
|
+
init_sentinel();
|
|
8081
|
+
init_trigger2();
|
|
8082
|
+
init_surfaces();
|
|
8083
|
+
init_toolRegistry();
|
|
8084
|
+
init_toolResultCap();
|
|
8085
|
+
log12 = createLogger("agent");
|
|
8086
|
+
BRAND_TRIGGERING_TOOLS = /* @__PURE__ */ new Set(["writeSpec", "editSpec"]);
|
|
8087
|
+
EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
8088
|
+
"promptUser",
|
|
8089
|
+
"setProjectOnboardingState",
|
|
8090
|
+
"presentPublishPlan",
|
|
8091
|
+
"confirmDestructiveAction",
|
|
8092
|
+
"runScenario",
|
|
8093
|
+
"runMethod",
|
|
8094
|
+
"queryDatabase",
|
|
8095
|
+
"browserCommand",
|
|
8096
|
+
"setProjectMetadata"
|
|
8097
|
+
]);
|
|
8098
|
+
USER_BLOCKING_EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
8099
|
+
"promptUser",
|
|
8100
|
+
"presentPublishPlan",
|
|
8101
|
+
"confirmDestructiveAction"
|
|
8102
|
+
]);
|
|
8103
|
+
}
|
|
8104
|
+
});
|
|
8105
|
+
|
|
8106
|
+
// src/prompt/static/projectContext.ts
|
|
8107
|
+
import fs21 from "fs";
|
|
8108
|
+
import path11 from "path";
|
|
8109
|
+
function loadProjectRoot() {
|
|
8110
|
+
return `
|
|
8111
|
+
## Project Root
|
|
8112
|
+
\`${PROJECT_ROOT}\`
|
|
8113
|
+
|
|
8114
|
+
File paths are relative to this directory. Every tool operates here and bash commands run it it.`;
|
|
8115
|
+
}
|
|
8116
|
+
function loadProjectManifest() {
|
|
8117
|
+
try {
|
|
8118
|
+
const manifest = fs21.readFileSync("mindstudio.json", "utf-8");
|
|
8119
|
+
return `
|
|
8120
|
+
## Project Manifest (mindstudio.json)
|
|
8121
|
+
\`\`\`json
|
|
8122
|
+
${manifest}
|
|
8123
|
+
\`\`\``;
|
|
8124
|
+
} catch {
|
|
8125
|
+
return "";
|
|
8126
|
+
}
|
|
8127
|
+
}
|
|
8128
|
+
function loadSpecFileMetadata() {
|
|
8129
|
+
try {
|
|
8130
|
+
const files = walkMdFiles3("src");
|
|
8131
|
+
if (files.length === 0) {
|
|
8132
|
+
return "";
|
|
8133
|
+
}
|
|
8134
|
+
const entries = [];
|
|
8135
|
+
for (const filePath of files) {
|
|
8136
|
+
const { name, description, type } = parseFrontmatter3(filePath);
|
|
8137
|
+
let line = `- ${filePath}`;
|
|
8138
|
+
if (name) {
|
|
8139
|
+
line += ` \u2014 "${name}"`;
|
|
8140
|
+
}
|
|
8141
|
+
if (type) {
|
|
8142
|
+
line += ` (${type})`;
|
|
8143
|
+
}
|
|
8144
|
+
if (description) {
|
|
8145
|
+
line += ` \u2014 ${description}`;
|
|
8146
|
+
}
|
|
8147
|
+
entries.push(line);
|
|
8148
|
+
}
|
|
8149
|
+
return `
|
|
8150
|
+
## Spec Files
|
|
8151
|
+
${entries.join("\n")}`;
|
|
8152
|
+
} catch {
|
|
8153
|
+
return "";
|
|
8154
|
+
}
|
|
8155
|
+
}
|
|
8156
|
+
function walkMdFiles3(dir) {
|
|
8157
|
+
const results = [];
|
|
8158
|
+
try {
|
|
8159
|
+
const entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
8160
|
+
for (const entry of entries) {
|
|
8161
|
+
const full = path11.join(dir, entry.name);
|
|
8162
|
+
if (entry.isDirectory()) {
|
|
8163
|
+
results.push(...walkMdFiles3(full));
|
|
8164
|
+
} else if (entry.name.endsWith(".md")) {
|
|
8165
|
+
results.push(full);
|
|
8166
|
+
}
|
|
8167
|
+
}
|
|
8168
|
+
} catch {
|
|
8169
|
+
}
|
|
8170
|
+
return results.sort();
|
|
8171
|
+
}
|
|
8172
|
+
function parseFrontmatter3(filePath) {
|
|
8173
|
+
try {
|
|
8174
|
+
const content = fs21.readFileSync(filePath, "utf-8");
|
|
8175
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
8176
|
+
if (!match) {
|
|
8177
|
+
return { name: "", description: "", type: "" };
|
|
8178
|
+
}
|
|
8179
|
+
const fm = match[1];
|
|
8180
|
+
const name = fm.match(/^name:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
8181
|
+
const description = fm.match(/^description:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
8182
|
+
const type = fm.match(/^type:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
8183
|
+
return { name, description, type };
|
|
8184
|
+
} catch {
|
|
8185
|
+
return { name: "", description: "", type: "" };
|
|
8186
|
+
}
|
|
8187
|
+
}
|
|
8188
|
+
function loadPlanStatus(onboardingState) {
|
|
8189
|
+
try {
|
|
8190
|
+
const content = fs21.readFileSync(".remy-plan.md", "utf-8");
|
|
8191
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
8192
|
+
const status = match?.[1]?.match(/^status:\s*(.+)$/m)?.[1]?.trim();
|
|
8193
|
+
if (status === "pending" && onboardingState === "intake") {
|
|
8194
|
+
return `
|
|
8195
|
+
<pending_initial_plan>
|
|
8196
|
+
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.
|
|
8197
|
+
</pending_initial_plan>`;
|
|
8198
|
+
}
|
|
8199
|
+
if (status === "pending") {
|
|
8200
|
+
return `
|
|
8201
|
+
<pending_plan>
|
|
8202
|
+
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.
|
|
8203
|
+
</pending_plan>`;
|
|
8280
8204
|
}
|
|
8281
|
-
if (
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8205
|
+
if (status === "approved") {
|
|
8206
|
+
return `
|
|
8207
|
+
<approved_plan>
|
|
8208
|
+
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.
|
|
8209
|
+
</approved_plan>`;
|
|
8285
8210
|
}
|
|
8211
|
+
return "";
|
|
8212
|
+
} catch {
|
|
8213
|
+
return "";
|
|
8286
8214
|
}
|
|
8287
8215
|
}
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8216
|
+
function loadProjectFileListing() {
|
|
8217
|
+
try {
|
|
8218
|
+
const entries = fs21.readdirSync(".", { withFileTypes: true });
|
|
8219
|
+
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
8220
|
+
if (a.isDirectory() && !b.isDirectory()) {
|
|
8221
|
+
return -1;
|
|
8222
|
+
}
|
|
8223
|
+
if (!a.isDirectory() && b.isDirectory()) {
|
|
8224
|
+
return 1;
|
|
8225
|
+
}
|
|
8226
|
+
return a.name.localeCompare(b.name);
|
|
8227
|
+
}).map((e) => e.isDirectory() ? `${e.name}/` : e.name).join("\n");
|
|
8228
|
+
return `
|
|
8229
|
+
## Project Files
|
|
8230
|
+
\`\`\`
|
|
8231
|
+
${listing}
|
|
8232
|
+
\`\`\``;
|
|
8233
|
+
} catch {
|
|
8234
|
+
return "";
|
|
8235
|
+
}
|
|
8236
|
+
}
|
|
8237
|
+
var init_projectContext = __esm({
|
|
8238
|
+
"src/prompt/static/projectContext.ts"() {
|
|
8291
8239
|
"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
|
-
|
|
8240
|
+
init_projectRoot();
|
|
8241
|
+
}
|
|
8242
|
+
});
|
|
8243
|
+
|
|
8244
|
+
// src/prompt/index.ts
|
|
8245
|
+
function resolveIncludes(template) {
|
|
8246
|
+
const result = template.replace(
|
|
8247
|
+
/\{\{([^}]+)\}\}/g,
|
|
8248
|
+
(_, filePath) => readAsset("prompt", filePath.trim())
|
|
8249
|
+
);
|
|
8250
|
+
return result.replace(/\n{3,}/g, "\n\n").trim();
|
|
8251
|
+
}
|
|
8252
|
+
function buildSystemPrompt(onboardingState, viewContext) {
|
|
8253
|
+
const projectContext = [
|
|
8254
|
+
loadProjectRoot(),
|
|
8255
|
+
loadProjectManifest(),
|
|
8256
|
+
loadSpecFileMetadata(),
|
|
8257
|
+
loadProjectFileListing()
|
|
8258
|
+
].filter(Boolean).join("\n");
|
|
8259
|
+
const now = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
|
|
8260
|
+
month: "long",
|
|
8261
|
+
day: "numeric",
|
|
8262
|
+
year: "numeric"
|
|
8263
|
+
});
|
|
8264
|
+
const template = `
|
|
8265
|
+
{{static/identity.md}}
|
|
8266
|
+
|
|
8267
|
+
Current date: ${now}
|
|
8268
|
+
|
|
8269
|
+
<platform_docs>
|
|
8270
|
+
<platform>
|
|
8271
|
+
{{compiled/platform.md}}
|
|
8272
|
+
</platform>
|
|
8273
|
+
|
|
8274
|
+
<manifest>
|
|
8275
|
+
{{compiled/manifest.md}}
|
|
8276
|
+
</manifest>
|
|
8277
|
+
|
|
8278
|
+
<tables>
|
|
8279
|
+
{{compiled/tables.md}}
|
|
8280
|
+
</tables>
|
|
8281
|
+
|
|
8282
|
+
<methods>
|
|
8283
|
+
{{compiled/methods.md}}
|
|
8284
|
+
</methods>
|
|
8285
|
+
|
|
8286
|
+
<auth>
|
|
8287
|
+
{{compiled/auth.md}}
|
|
8288
|
+
</auth>
|
|
8289
|
+
|
|
8290
|
+
<dev_and_deploy>
|
|
8291
|
+
{{compiled/dev-and-deploy.md}}
|
|
8292
|
+
</dev_and_deploy>
|
|
8293
|
+
|
|
8294
|
+
<design>
|
|
8295
|
+
{{compiled/design.md}}
|
|
8296
|
+
</design>
|
|
8297
|
+
|
|
8298
|
+
<building_agent_interfaces>
|
|
8299
|
+
{{compiled/agent-interfaces.md}}
|
|
8300
|
+
</building_agent_interfaces>
|
|
8301
|
+
|
|
8302
|
+
<building_mcp_interfaces>
|
|
8303
|
+
{{compiled/mcp-interfaces.md}}
|
|
8304
|
+
</building_mcp_interfaces>
|
|
8305
|
+
|
|
8306
|
+
<media_cdn>
|
|
8307
|
+
{{compiled/media-cdn.md}}
|
|
8308
|
+
</media_cdn>
|
|
8309
|
+
|
|
8310
|
+
<interfaces>
|
|
8311
|
+
{{compiled/interfaces.md}}
|
|
8312
|
+
</interfaces>
|
|
8313
|
+
|
|
8314
|
+
<scenarios>
|
|
8315
|
+
{{compiled/scenarios.md}}
|
|
8316
|
+
</scenarios>
|
|
8317
|
+
|
|
8318
|
+
<secrets>
|
|
8319
|
+
{{compiled/secrets.md}}
|
|
8320
|
+
</secrets>
|
|
8321
|
+
</platform_docs>
|
|
8322
|
+
|
|
8323
|
+
<mindstudio_agent_sdk_docs>
|
|
8324
|
+
{{compiled/sdk-actions.md}}
|
|
8325
|
+
|
|
8326
|
+
{{compiled/task-agents.md}}
|
|
8327
|
+
</mindstudio_agent_sdk_docs>
|
|
8328
|
+
|
|
8329
|
+
<mindstudio_flavored_markdown_spec_docs>
|
|
8330
|
+
{{compiled/msfm.md}}
|
|
8331
|
+
</mindstudio_flavored_markdown_spec_docs>
|
|
8332
|
+
|
|
8333
|
+
<intake_mode_instructions>
|
|
8334
|
+
{{static/intake.md}}
|
|
8335
|
+
</intake_mode_instructions>
|
|
8336
|
+
|
|
8337
|
+
<spec_authoring_instructions>
|
|
8338
|
+
{{static/authoring.md}}
|
|
8339
|
+
</spec_authoring_instructions>
|
|
8340
|
+
|
|
8341
|
+
<team>
|
|
8342
|
+
{{static/team.md}}
|
|
8343
|
+
</team>
|
|
8344
|
+
|
|
8345
|
+
<code_authoring_instructions>
|
|
8346
|
+
{{static/coding.md}}
|
|
8347
|
+
|
|
8348
|
+
<typescript_lsp>
|
|
8349
|
+
{{static/lsp.md}}
|
|
8350
|
+
</typescript_lsp>
|
|
8351
|
+
</code_authoring_instructions>
|
|
8352
|
+
|
|
8353
|
+
<conversation_summaries>
|
|
8354
|
+
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.
|
|
8355
|
+
|
|
8356
|
+
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.
|
|
8357
|
+
</conversation_summaries>
|
|
8358
|
+
|
|
8359
|
+
<project_onboarding>
|
|
8360
|
+
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:
|
|
8361
|
+
|
|
8362
|
+
- **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.
|
|
8363
|
+
- **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).
|
|
8364
|
+
- **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.
|
|
8365
|
+
- **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.
|
|
8366
|
+
</project_onboarding>
|
|
8367
|
+
|
|
8368
|
+
{{static/instructions.md}}
|
|
8369
|
+
|
|
8370
|
+
<!-- cache_breakpoint -->
|
|
8371
|
+
|
|
8372
|
+
<current_project_onboarding_state>
|
|
8373
|
+
${onboardingState ?? "onboardingFinished"}
|
|
8374
|
+
</current_project_onboarding_state>
|
|
8375
|
+
|
|
8376
|
+
<project_context>
|
|
8377
|
+
${projectContext}
|
|
8378
|
+
</project_context>
|
|
8379
|
+
|
|
8380
|
+
${renderOrgContextBlock()}
|
|
8381
|
+
|
|
8382
|
+
<view_context>
|
|
8383
|
+
The user is currently in ${viewContext?.mode ?? "code"} mode.
|
|
8384
|
+
${viewContext?.activeFile ? `Active file: ${viewContext.activeFile}` : ""}
|
|
8385
|
+
</view_context>
|
|
8386
|
+
|
|
8387
|
+
${loadPlanStatus(onboardingState)}
|
|
8388
|
+
`;
|
|
8389
|
+
return resolveIncludes(template);
|
|
8390
|
+
}
|
|
8391
|
+
var init_prompt4 = __esm({
|
|
8392
|
+
"src/prompt/index.ts"() {
|
|
8393
|
+
"use strict";
|
|
8394
|
+
init_assets();
|
|
8395
|
+
init_projectContext();
|
|
8396
|
+
init_orgContext();
|
|
8325
8397
|
}
|
|
8326
8398
|
});
|
|
8327
8399
|
|
|
8328
8400
|
// src/config.ts
|
|
8329
8401
|
import fs22 from "fs";
|
|
8330
|
-
import
|
|
8402
|
+
import path12 from "path";
|
|
8331
8403
|
import os from "os";
|
|
8332
8404
|
function loadConfigFile() {
|
|
8333
8405
|
try {
|
|
@@ -8370,7 +8442,7 @@ var init_config = __esm({
|
|
|
8370
8442
|
"use strict";
|
|
8371
8443
|
init_logger();
|
|
8372
8444
|
log13 = createLogger("config");
|
|
8373
|
-
CONFIG_PATH =
|
|
8445
|
+
CONFIG_PATH = path12.join(
|
|
8374
8446
|
os.homedir(),
|
|
8375
8447
|
".mindstudio-local-tunnel",
|
|
8376
8448
|
"config.json"
|
|
@@ -8721,9 +8793,8 @@ var init_headless = __esm({
|
|
|
8721
8793
|
init_logger();
|
|
8722
8794
|
init_config();
|
|
8723
8795
|
init_orgContext();
|
|
8724
|
-
|
|
8796
|
+
init_prompt4();
|
|
8725
8797
|
init_trigger();
|
|
8726
|
-
init_compaction();
|
|
8727
8798
|
init_trigger2();
|
|
8728
8799
|
init_lsp();
|
|
8729
8800
|
init_agent();
|
|
@@ -8835,7 +8906,9 @@ var init_headless = __esm({
|
|
|
8835
8906
|
const data = event.error ? { error: event.error } : {};
|
|
8836
8907
|
this.emit("compaction_complete", data, event.requestId);
|
|
8837
8908
|
this.sessionStats.compactionInProgress = false;
|
|
8838
|
-
|
|
8909
|
+
if (!event.error) {
|
|
8910
|
+
this.sessionStats.lastContextSize = 0;
|
|
8911
|
+
}
|
|
8839
8912
|
this.sessionStats.messageCount = this.state.messages.length;
|
|
8840
8913
|
this.persistStats();
|
|
8841
8914
|
}
|
|
@@ -8969,20 +9042,10 @@ var init_headless = __esm({
|
|
|
8969
9042
|
requestId,
|
|
8970
9043
|
model: this.opts.model
|
|
8971
9044
|
});
|
|
8972
|
-
this.
|
|
9045
|
+
applyPendingSummaries(this.state);
|
|
8973
9046
|
} catch {
|
|
8974
9047
|
}
|
|
8975
9048
|
}
|
|
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
9049
|
onBackgroundComplete = (toolCallId, name, result, subAgentMessages) => {
|
|
8987
9050
|
this.pendingBlockUpdates.push({ toolCallId, result, subAgentMessages });
|
|
8988
9051
|
log15.info("Background complete", {
|
|
@@ -9311,7 +9374,7 @@ var init_headless = __esm({
|
|
|
9311
9374
|
error: err.message
|
|
9312
9375
|
});
|
|
9313
9376
|
}
|
|
9314
|
-
this.
|
|
9377
|
+
applyPendingSummaries(this.state);
|
|
9315
9378
|
this.applyPendingBlockUpdates();
|
|
9316
9379
|
}
|
|
9317
9380
|
async handleMessage(parsed, requestId) {
|
|
@@ -9615,7 +9678,7 @@ var init_headless = __esm({
|
|
|
9615
9678
|
model: this.opts.model
|
|
9616
9679
|
});
|
|
9617
9680
|
if (!this.running) {
|
|
9618
|
-
this.
|
|
9681
|
+
applyPendingSummaries(this.state);
|
|
9619
9682
|
}
|
|
9620
9683
|
this.emit("completed", { success: true }, requestId);
|
|
9621
9684
|
} catch (err) {
|
|
@@ -9660,7 +9723,7 @@ var init_headless = __esm({
|
|
|
9660
9723
|
import { render } from "ink";
|
|
9661
9724
|
import os2 from "os";
|
|
9662
9725
|
import fs23 from "fs";
|
|
9663
|
-
import
|
|
9726
|
+
import path13 from "path";
|
|
9664
9727
|
|
|
9665
9728
|
// src/tui/App.tsx
|
|
9666
9729
|
import { useState as useState2, useCallback, useRef } from "react";
|
|
@@ -9795,7 +9858,8 @@ function MessageList({ turns }) {
|
|
|
9795
9858
|
|
|
9796
9859
|
// src/tui/App.tsx
|
|
9797
9860
|
init_agent();
|
|
9798
|
-
|
|
9861
|
+
init_trigger();
|
|
9862
|
+
init_prompt4();
|
|
9799
9863
|
init_session();
|
|
9800
9864
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
9801
9865
|
function App({ apiConfig, model }) {
|
|
@@ -9933,6 +9997,7 @@ Error: ${err.message}`,
|
|
|
9933
9997
|
done: true
|
|
9934
9998
|
}));
|
|
9935
9999
|
}
|
|
10000
|
+
applyPendingSummaries(agentState);
|
|
9936
10001
|
abortRef.current = null;
|
|
9937
10002
|
setIsRunning(false);
|
|
9938
10003
|
},
|
|
@@ -9980,7 +10045,7 @@ var startupLog = createLogger("startup");
|
|
|
9980
10045
|
function printDebugInfo(config) {
|
|
9981
10046
|
const pkg = JSON.parse(
|
|
9982
10047
|
fs23.readFileSync(
|
|
9983
|
-
|
|
10048
|
+
path13.join(import.meta.dirname, "..", "package.json"),
|
|
9984
10049
|
"utf-8"
|
|
9985
10050
|
)
|
|
9986
10051
|
);
|