@mindstudio-ai/remy 0.1.268 → 0.1.269
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/headless.js +67 -159
- package/dist/index.js +27 -119
- package/dist/prompt/skills/voiceInterfaces.md +15 -1
- package/package.json +1 -1
package/dist/headless.js
CHANGED
|
@@ -618,7 +618,6 @@ function readJsonAsset(fallback, ...segments) {
|
|
|
618
618
|
|
|
619
619
|
// src/prompt/static/projectContext.ts
|
|
620
620
|
import fs4 from "fs";
|
|
621
|
-
import path3 from "path";
|
|
622
621
|
|
|
623
622
|
// src/projectRoot.ts
|
|
624
623
|
var PROJECT_ROOT = process.cwd();
|
|
@@ -631,78 +630,23 @@ function loadProjectRoot() {
|
|
|
631
630
|
|
|
632
631
|
File paths are relative to this directory. Every tool operates here and bash commands run it it.`;
|
|
633
632
|
}
|
|
634
|
-
function
|
|
635
|
-
try {
|
|
636
|
-
const manifest = fs4.readFileSync("mindstudio.json", "utf-8");
|
|
637
|
-
return `
|
|
638
|
-
## Project Manifest (mindstudio.json)
|
|
639
|
-
\`\`\`json
|
|
640
|
-
${manifest}
|
|
641
|
-
\`\`\``;
|
|
642
|
-
} catch {
|
|
643
|
-
return "";
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
function loadSpecFileMetadata() {
|
|
633
|
+
function loadAppIdentity() {
|
|
647
634
|
try {
|
|
648
|
-
const
|
|
649
|
-
|
|
635
|
+
const manifest = JSON.parse(fs4.readFileSync("mindstudio.json", "utf-8"));
|
|
636
|
+
const name = typeof manifest.name === "string" ? manifest.name.trim() : "";
|
|
637
|
+
if (!name) {
|
|
650
638
|
return "";
|
|
651
639
|
}
|
|
652
|
-
const
|
|
653
|
-
for (const filePath of files) {
|
|
654
|
-
const { name, description, type } = parseFrontmatter(filePath);
|
|
655
|
-
let line = `- ${filePath}`;
|
|
656
|
-
if (name) {
|
|
657
|
-
line += ` \u2014 "${name}"`;
|
|
658
|
-
}
|
|
659
|
-
if (type) {
|
|
660
|
-
line += ` (${type})`;
|
|
661
|
-
}
|
|
662
|
-
if (description) {
|
|
663
|
-
line += ` \u2014 ${description}`;
|
|
664
|
-
}
|
|
665
|
-
entries.push(line);
|
|
666
|
-
}
|
|
640
|
+
const description = typeof manifest.description === "string" ? manifest.description.trim() : "";
|
|
667
641
|
return `
|
|
668
|
-
##
|
|
669
|
-
${
|
|
642
|
+
## App
|
|
643
|
+
"${name}"${description ? ` \u2014 ${description}` : ""}
|
|
644
|
+
|
|
645
|
+
Full manifest: \`mindstudio.json\` (read it when you need the app's structure, tables, methods, roles, auth settings, interfaces, and everything else).`;
|
|
670
646
|
} catch {
|
|
671
647
|
return "";
|
|
672
648
|
}
|
|
673
649
|
}
|
|
674
|
-
function walkMdFiles(dir) {
|
|
675
|
-
const results = [];
|
|
676
|
-
try {
|
|
677
|
-
const entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
678
|
-
for (const entry of entries) {
|
|
679
|
-
const full = path3.join(dir, entry.name);
|
|
680
|
-
if (entry.isDirectory()) {
|
|
681
|
-
results.push(...walkMdFiles(full));
|
|
682
|
-
} else if (entry.name.endsWith(".md")) {
|
|
683
|
-
results.push(full);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
} catch {
|
|
687
|
-
}
|
|
688
|
-
return results.sort();
|
|
689
|
-
}
|
|
690
|
-
function parseFrontmatter(filePath) {
|
|
691
|
-
try {
|
|
692
|
-
const content = fs4.readFileSync(filePath, "utf-8");
|
|
693
|
-
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
694
|
-
if (!match) {
|
|
695
|
-
return { name: "", description: "", type: "" };
|
|
696
|
-
}
|
|
697
|
-
const fm = match[1];
|
|
698
|
-
const name = fm.match(/^name:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
699
|
-
const description = fm.match(/^description:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
700
|
-
const type = fm.match(/^type:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
701
|
-
return { name, description, type };
|
|
702
|
-
} catch {
|
|
703
|
-
return { name: "", description: "", type: "" };
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
650
|
function loadPlanStatus(onboardingState) {
|
|
707
651
|
try {
|
|
708
652
|
const content = fs4.readFileSync(".remy-plan.md", "utf-8");
|
|
@@ -731,33 +675,12 @@ The user has approved your implementation plan in .remy-plan.md. You may referen
|
|
|
731
675
|
return "";
|
|
732
676
|
}
|
|
733
677
|
}
|
|
734
|
-
function loadProjectFileListing() {
|
|
735
|
-
try {
|
|
736
|
-
const entries = fs4.readdirSync(".", { withFileTypes: true });
|
|
737
|
-
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
738
|
-
if (a.isDirectory() && !b.isDirectory()) {
|
|
739
|
-
return -1;
|
|
740
|
-
}
|
|
741
|
-
if (!a.isDirectory() && b.isDirectory()) {
|
|
742
|
-
return 1;
|
|
743
|
-
}
|
|
744
|
-
return a.name.localeCompare(b.name);
|
|
745
|
-
}).map((e) => e.isDirectory() ? `${e.name}/` : e.name).join("\n");
|
|
746
|
-
return `
|
|
747
|
-
## Project Files
|
|
748
|
-
\`\`\`
|
|
749
|
-
${listing}
|
|
750
|
-
\`\`\``;
|
|
751
|
-
} catch {
|
|
752
|
-
return "";
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
678
|
|
|
756
679
|
// src/prompt/skills/catalog.ts
|
|
757
680
|
import fs5 from "fs";
|
|
758
|
-
import
|
|
681
|
+
import path3 from "path";
|
|
759
682
|
var SKILLS_DIR = assetPath("prompt", "skills");
|
|
760
|
-
function
|
|
683
|
+
function parseFrontmatter(content) {
|
|
761
684
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
762
685
|
if (!match) {
|
|
763
686
|
return {};
|
|
@@ -780,9 +703,9 @@ function loadCatalog() {
|
|
|
780
703
|
}
|
|
781
704
|
const skills = [];
|
|
782
705
|
for (const file of files.sort()) {
|
|
783
|
-
const full =
|
|
706
|
+
const full = path3.join(SKILLS_DIR, file);
|
|
784
707
|
const id = file.replace(/\.md$/, "");
|
|
785
|
-
const fields =
|
|
708
|
+
const fields = parseFrontmatter(fs5.readFileSync(full, "utf-8"));
|
|
786
709
|
if (!fields.name || !fields.what || !fields.when) {
|
|
787
710
|
continue;
|
|
788
711
|
}
|
|
@@ -836,13 +759,7 @@ function resolveIncludes(template) {
|
|
|
836
759
|
);
|
|
837
760
|
return result.replace(/\n{3,}/g, "\n\n").trim();
|
|
838
761
|
}
|
|
839
|
-
function buildSystemPrompt(onboardingState
|
|
840
|
-
const projectContext = [
|
|
841
|
-
loadProjectRoot(),
|
|
842
|
-
loadProjectManifest(),
|
|
843
|
-
loadSpecFileMetadata(),
|
|
844
|
-
loadProjectFileListing()
|
|
845
|
-
].filter(Boolean).join("\n");
|
|
762
|
+
function buildSystemPrompt(onboardingState) {
|
|
846
763
|
const now = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
|
|
847
764
|
month: "long",
|
|
848
765
|
day: "numeric",
|
|
@@ -851,8 +768,6 @@ function buildSystemPrompt(onboardingState, viewContext) {
|
|
|
851
768
|
const template = `
|
|
852
769
|
{{static/identity.md}}
|
|
853
770
|
|
|
854
|
-
Current date: ${now}
|
|
855
|
-
|
|
856
771
|
<platform_docs>
|
|
857
772
|
<platform>
|
|
858
773
|
{{compiled/platform.md}}
|
|
@@ -932,7 +847,7 @@ ${loadSkillsCatalog()}
|
|
|
932
847
|
<conversation_summaries>
|
|
933
848
|
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.
|
|
934
849
|
|
|
935
|
-
|
|
850
|
+
Tool results generally persist in the conversation until a compaction summarizes them. In rare cases very old tool results may be trimmed when the conversation nears the context limit. Either way, if you need something from an earlier tool call and can't see it anymore, just re-read the file or re-run the query.
|
|
936
851
|
</conversation_summaries>
|
|
937
852
|
|
|
938
853
|
<project_onboarding>
|
|
@@ -944,24 +859,21 @@ New projects progress through three onboarding states. The user might skip this
|
|
|
944
859
|
- **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.
|
|
945
860
|
</project_onboarding>
|
|
946
861
|
|
|
862
|
+
${loadProjectRoot()}
|
|
863
|
+
|
|
864
|
+
${renderOrgContextBlock()}
|
|
865
|
+
|
|
947
866
|
{{static/instructions.md}}
|
|
948
867
|
|
|
949
868
|
<!-- cache_breakpoint -->
|
|
950
869
|
|
|
870
|
+
Current date: ${now}
|
|
871
|
+
|
|
951
872
|
<current_project_onboarding_state>
|
|
952
873
|
${onboardingState ?? "onboardingFinished"}
|
|
953
874
|
</current_project_onboarding_state>
|
|
954
875
|
|
|
955
|
-
|
|
956
|
-
${projectContext}
|
|
957
|
-
</project_context>
|
|
958
|
-
|
|
959
|
-
${renderOrgContextBlock()}
|
|
960
|
-
|
|
961
|
-
<view_context>
|
|
962
|
-
The user is currently in ${viewContext?.mode ?? "code"} mode.
|
|
963
|
-
${viewContext?.activeFile ? `Active file: ${viewContext.activeFile}` : ""}
|
|
964
|
-
</view_context>
|
|
876
|
+
${loadAppIdentity()}
|
|
965
877
|
|
|
966
878
|
${loadPlanStatus(onboardingState)}
|
|
967
879
|
`;
|
|
@@ -1100,7 +1012,7 @@ var readSpecTool = {
|
|
|
1100
1012
|
|
|
1101
1013
|
// src/tools/spec/writeSpec.ts
|
|
1102
1014
|
import fs7 from "fs/promises";
|
|
1103
|
-
import
|
|
1015
|
+
import path4 from "path";
|
|
1104
1016
|
|
|
1105
1017
|
// src/tools/_helpers/diff.ts
|
|
1106
1018
|
var CONTEXT_LINES = 3;
|
|
@@ -1189,7 +1101,7 @@ ${unifiedDiff(partial.path, oldContent, partial.content)}`;
|
|
|
1189
1101
|
}
|
|
1190
1102
|
const release = await acquireFileLock(input.path);
|
|
1191
1103
|
try {
|
|
1192
|
-
await fs7.mkdir(
|
|
1104
|
+
await fs7.mkdir(path4.dirname(input.path), { recursive: true });
|
|
1193
1105
|
let oldContent = null;
|
|
1194
1106
|
try {
|
|
1195
1107
|
oldContent = await fs7.readFile(input.path, "utf-8");
|
|
@@ -1394,7 +1306,7 @@ var editSpecTool = {
|
|
|
1394
1306
|
|
|
1395
1307
|
// src/tools/spec/listSpecFiles.ts
|
|
1396
1308
|
import fs9 from "fs/promises";
|
|
1397
|
-
import
|
|
1309
|
+
import path5 from "path";
|
|
1398
1310
|
var listSpecFilesTool = {
|
|
1399
1311
|
definition: {
|
|
1400
1312
|
name: "listSpecFiles",
|
|
@@ -1433,7 +1345,7 @@ async function listRecursive(dir) {
|
|
|
1433
1345
|
return a.name.localeCompare(b.name);
|
|
1434
1346
|
});
|
|
1435
1347
|
for (const entry of entries) {
|
|
1436
|
-
const fullPath =
|
|
1348
|
+
const fullPath = path5.join(dir, entry.name);
|
|
1437
1349
|
if (entry.isDirectory()) {
|
|
1438
1350
|
results.push(`${fullPath}/`);
|
|
1439
1351
|
results.push(...await listRecursive(fullPath));
|
|
@@ -2271,7 +2183,7 @@ var readFileTool = {
|
|
|
2271
2183
|
|
|
2272
2184
|
// src/tools/code/writeFile.ts
|
|
2273
2185
|
import fs14 from "fs/promises";
|
|
2274
|
-
import
|
|
2186
|
+
import path6 from "path";
|
|
2275
2187
|
var writeFileTool = {
|
|
2276
2188
|
definition: {
|
|
2277
2189
|
name: "writeFile",
|
|
@@ -2316,7 +2228,7 @@ ${unifiedDiff(partial.path, oldContent, completeContent)}`;
|
|
|
2316
2228
|
async execute(input) {
|
|
2317
2229
|
const release = await acquireFileLock(input.path);
|
|
2318
2230
|
try {
|
|
2319
|
-
await fs14.mkdir(
|
|
2231
|
+
await fs14.mkdir(path6.dirname(input.path), { recursive: true });
|
|
2320
2232
|
let oldContent = null;
|
|
2321
2233
|
try {
|
|
2322
2234
|
oldContent = await fs14.readFile(input.path, "utf-8");
|
|
@@ -2425,7 +2337,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2425
2337
|
|
|
2426
2338
|
// src/tools/code/bash.ts
|
|
2427
2339
|
import { spawn as spawn2 } from "child_process";
|
|
2428
|
-
import
|
|
2340
|
+
import path7 from "path";
|
|
2429
2341
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
2430
2342
|
var DEFAULT_MAX_LINES2 = 500;
|
|
2431
2343
|
var MAX_OUTPUT_BYTES = 3e4;
|
|
@@ -2463,7 +2375,7 @@ var bashTool = {
|
|
|
2463
2375
|
const child = spawn2("sh", ["-c", input.command], {
|
|
2464
2376
|
// Pinned rather than inherited. `undefined` here means "wherever the
|
|
2465
2377
|
// process happens to be", which is the project root only by luck.
|
|
2466
|
-
cwd: input.cwd ?
|
|
2378
|
+
cwd: input.cwd ? path7.resolve(PROJECT_ROOT, input.cwd) : PROJECT_ROOT,
|
|
2467
2379
|
env: { ...process.env, FORCE_COLOR: "1" }
|
|
2468
2380
|
});
|
|
2469
2381
|
let output = "";
|
|
@@ -2707,7 +2619,7 @@ var globTool = {
|
|
|
2707
2619
|
|
|
2708
2620
|
// src/tools/code/listDir.ts
|
|
2709
2621
|
import fs16 from "fs/promises";
|
|
2710
|
-
import
|
|
2622
|
+
import path8 from "path";
|
|
2711
2623
|
var EXCLUDE = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
2712
2624
|
var MAX_CHILDREN = 15;
|
|
2713
2625
|
async function readAndSort(dirPath) {
|
|
@@ -2724,7 +2636,7 @@ async function readAndSort(dirPath) {
|
|
|
2724
2636
|
}
|
|
2725
2637
|
async function collapsePath(basePath, name) {
|
|
2726
2638
|
let display = name;
|
|
2727
|
-
let current =
|
|
2639
|
+
let current = path8.join(basePath, name);
|
|
2728
2640
|
for (; ; ) {
|
|
2729
2641
|
let children;
|
|
2730
2642
|
try {
|
|
@@ -2734,7 +2646,7 @@ async function collapsePath(basePath, name) {
|
|
|
2734
2646
|
}
|
|
2735
2647
|
if (children.length === 1 && children[0].isDirectory()) {
|
|
2736
2648
|
display += "/" + children[0].name;
|
|
2737
|
-
current =
|
|
2649
|
+
current = path8.join(current, children[0].name);
|
|
2738
2650
|
} else {
|
|
2739
2651
|
break;
|
|
2740
2652
|
}
|
|
@@ -2752,7 +2664,7 @@ function formatSize(bytes) {
|
|
|
2752
2664
|
}
|
|
2753
2665
|
async function formatFile(dirPath, name, indent) {
|
|
2754
2666
|
try {
|
|
2755
|
-
const stat2 = await fs16.stat(
|
|
2667
|
+
const stat2 = await fs16.stat(path8.join(dirPath, name));
|
|
2756
2668
|
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat2.size)}`;
|
|
2757
2669
|
} catch {
|
|
2758
2670
|
return `${indent}${name}`;
|
|
@@ -3215,7 +3127,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
3215
3127
|
let onLog;
|
|
3216
3128
|
let model;
|
|
3217
3129
|
let apiConfig;
|
|
3218
|
-
let
|
|
3130
|
+
let path13;
|
|
3219
3131
|
let fullPage = true;
|
|
3220
3132
|
let width;
|
|
3221
3133
|
let height;
|
|
@@ -3223,7 +3135,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
3223
3135
|
if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
|
|
3224
3136
|
prompt = promptOrOptions.prompt;
|
|
3225
3137
|
existingImage = promptOrOptions.image;
|
|
3226
|
-
|
|
3138
|
+
path13 = promptOrOptions.path;
|
|
3227
3139
|
if (promptOrOptions.fullPage !== void 0) {
|
|
3228
3140
|
fullPage = promptOrOptions.fullPage;
|
|
3229
3141
|
}
|
|
@@ -3247,7 +3159,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
3247
3159
|
const ssResult = await sidecarRequest(
|
|
3248
3160
|
fullPage ? "/screenshot-full-page" : "/screenshot-viewport",
|
|
3249
3161
|
{
|
|
3250
|
-
...
|
|
3162
|
+
...path13 ? { path: path13 } : {},
|
|
3251
3163
|
...width != null ? { width } : {},
|
|
3252
3164
|
...height != null ? { height } : {},
|
|
3253
3165
|
...format ? { format } : {}
|
|
@@ -4250,15 +4162,15 @@ var BROWSER_EXTERNAL_TOOLS = /* @__PURE__ */ new Set(["browserCommand"]);
|
|
|
4250
4162
|
|
|
4251
4163
|
// src/subagents/common/context.ts
|
|
4252
4164
|
import fs17 from "fs";
|
|
4253
|
-
import
|
|
4254
|
-
function
|
|
4165
|
+
import path9 from "path";
|
|
4166
|
+
function walkMdFiles(dir, skip) {
|
|
4255
4167
|
const files = [];
|
|
4256
4168
|
try {
|
|
4257
4169
|
for (const entry of fs17.readdirSync(dir, { withFileTypes: true })) {
|
|
4258
|
-
const full =
|
|
4170
|
+
const full = path9.join(dir, entry.name);
|
|
4259
4171
|
if (entry.isDirectory()) {
|
|
4260
4172
|
if (!skip?.has(entry.name)) {
|
|
4261
|
-
files.push(...
|
|
4173
|
+
files.push(...walkMdFiles(full, skip));
|
|
4262
4174
|
}
|
|
4263
4175
|
} else if (entry.name.endsWith(".md")) {
|
|
4264
4176
|
files.push(full);
|
|
@@ -4268,7 +4180,7 @@ function walkMdFiles2(dir, skip) {
|
|
|
4268
4180
|
}
|
|
4269
4181
|
return files.sort();
|
|
4270
4182
|
}
|
|
4271
|
-
function
|
|
4183
|
+
function parseFrontmatter2(filePath) {
|
|
4272
4184
|
try {
|
|
4273
4185
|
const content = fs17.readFileSync(filePath, "utf-8");
|
|
4274
4186
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -4290,12 +4202,12 @@ function parseFrontmatter3(filePath) {
|
|
|
4290
4202
|
}
|
|
4291
4203
|
}
|
|
4292
4204
|
function loadSpecIndex() {
|
|
4293
|
-
const files =
|
|
4205
|
+
const files = walkMdFiles("src", /* @__PURE__ */ new Set(["roadmap"]));
|
|
4294
4206
|
if (files.length === 0) {
|
|
4295
4207
|
return "";
|
|
4296
4208
|
}
|
|
4297
4209
|
const lines = files.map((f) => {
|
|
4298
|
-
const fm =
|
|
4210
|
+
const fm = parseFrontmatter2(f);
|
|
4299
4211
|
let line = `- ${f}`;
|
|
4300
4212
|
if (fm.name) {
|
|
4301
4213
|
line += ` \u2014 "${fm.name}"`;
|
|
@@ -4333,10 +4245,10 @@ ${indexJson.standalone.map((s) => `- ${s}`).join("\n")}`
|
|
|
4333
4245
|
}
|
|
4334
4246
|
} catch {
|
|
4335
4247
|
}
|
|
4336
|
-
const files =
|
|
4248
|
+
const files = walkMdFiles("src/roadmap");
|
|
4337
4249
|
if (files.length > 0) {
|
|
4338
4250
|
const lines = files.map((f) => {
|
|
4339
|
-
const fm =
|
|
4251
|
+
const fm = parseFrontmatter2(f);
|
|
4340
4252
|
let line = `- ${f}`;
|
|
4341
4253
|
if (fm.name) {
|
|
4342
4254
|
line += ` \u2014 "${fm.name}"`;
|
|
@@ -5705,14 +5617,14 @@ var VISION_TOOLS = [
|
|
|
5705
5617
|
|
|
5706
5618
|
// src/subagents/productVision/executor.ts
|
|
5707
5619
|
import fs19 from "fs";
|
|
5708
|
-
import
|
|
5620
|
+
import path10 from "path";
|
|
5709
5621
|
var ROADMAP_DIR = "src/roadmap";
|
|
5710
5622
|
var PITCH_DECK_SHELL = readAsset(
|
|
5711
5623
|
"subagents/productVision",
|
|
5712
5624
|
"pitch-deck-shell.html"
|
|
5713
5625
|
);
|
|
5714
5626
|
function resolve2(filePath) {
|
|
5715
|
-
return
|
|
5627
|
+
return path10.join(ROADMAP_DIR, filePath);
|
|
5716
5628
|
}
|
|
5717
5629
|
async function executeVisionTool(name, input, context) {
|
|
5718
5630
|
switch (name) {
|
|
@@ -6732,7 +6644,7 @@ Write the summary of the conversation above, following your instructions.`;
|
|
|
6732
6644
|
|
|
6733
6645
|
// src/session.ts
|
|
6734
6646
|
import fs21 from "fs";
|
|
6735
|
-
import
|
|
6647
|
+
import path11 from "path";
|
|
6736
6648
|
var log10 = createLogger("session");
|
|
6737
6649
|
var SESSION_FILE = ".remy-session.json";
|
|
6738
6650
|
var ARCHIVE_DIR = ".logs/sessions";
|
|
@@ -6831,17 +6743,17 @@ function archiveMessages(messages, label, models) {
|
|
|
6831
6743
|
fs21.mkdirSync(ARCHIVE_DIR, { recursive: true });
|
|
6832
6744
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
6833
6745
|
const count = messages.length;
|
|
6834
|
-
let dest =
|
|
6746
|
+
let dest = path11.join(ARCHIVE_DIR, `${label}-${ts}.c${count}.json`);
|
|
6835
6747
|
let n = 1;
|
|
6836
6748
|
while (fs21.existsSync(dest)) {
|
|
6837
|
-
dest =
|
|
6749
|
+
dest = path11.join(ARCHIVE_DIR, `${label}-${ts}-${n++}.c${count}.json`);
|
|
6838
6750
|
}
|
|
6839
6751
|
const payload = { messages };
|
|
6840
6752
|
if (models && Object.keys(models).length > 0) {
|
|
6841
6753
|
payload.models = models;
|
|
6842
6754
|
}
|
|
6843
6755
|
writeFileAtomicSync(dest, JSON.stringify(payload));
|
|
6844
|
-
archiveCountCache.set(
|
|
6756
|
+
archiveCountCache.set(path11.basename(dest), count);
|
|
6845
6757
|
log10.info("Session archived", { label, dest, messageCount: count });
|
|
6846
6758
|
pruneArchives();
|
|
6847
6759
|
return dest;
|
|
@@ -6854,7 +6766,7 @@ function pruneArchives() {
|
|
|
6854
6766
|
}
|
|
6855
6767
|
const archives = entries.map((name) => ({
|
|
6856
6768
|
name,
|
|
6857
|
-
size: fs21.statSync(
|
|
6769
|
+
size: fs21.statSync(path11.join(ARCHIVE_DIR, name)).size
|
|
6858
6770
|
})).sort(
|
|
6859
6771
|
(a, b) => archiveSortKey(b.name).localeCompare(archiveSortKey(a.name))
|
|
6860
6772
|
);
|
|
@@ -6872,7 +6784,7 @@ function pruneArchives() {
|
|
|
6872
6784
|
let freed = 0;
|
|
6873
6785
|
for (let i = cut; i < archives.length; i++) {
|
|
6874
6786
|
try {
|
|
6875
|
-
fs21.unlinkSync(
|
|
6787
|
+
fs21.unlinkSync(path11.join(ARCHIVE_DIR, archives[i].name));
|
|
6876
6788
|
freed += archives[i].size;
|
|
6877
6789
|
removed++;
|
|
6878
6790
|
} catch {
|
|
@@ -6896,7 +6808,7 @@ function parseArchive(name) {
|
|
|
6896
6808
|
return cached3;
|
|
6897
6809
|
}
|
|
6898
6810
|
try {
|
|
6899
|
-
const raw = fs21.readFileSync(
|
|
6811
|
+
const raw = fs21.readFileSync(path11.join(ARCHIVE_DIR, name), "utf-8");
|
|
6900
6812
|
const data = JSON.parse(raw);
|
|
6901
6813
|
const messages = Array.isArray(data?.messages) ? data.messages : [];
|
|
6902
6814
|
archiveCountCache.set(name, messages.length);
|
|
@@ -7177,7 +7089,7 @@ function triggerCompaction(state, apiConfig, opts = {}) {
|
|
|
7177
7089
|
|
|
7178
7090
|
// src/brandExtraction/index.ts
|
|
7179
7091
|
import fs22 from "fs";
|
|
7180
|
-
import
|
|
7092
|
+
import path12 from "path";
|
|
7181
7093
|
import { createHash } from "crypto";
|
|
7182
7094
|
var log12 = createLogger("brandExtraction");
|
|
7183
7095
|
var EXTRACT_PROMPT = readAsset("brandExtraction", "extract.md");
|
|
@@ -7201,18 +7113,18 @@ async function runExtraction(apiConfig, model) {
|
|
|
7201
7113
|
return brand;
|
|
7202
7114
|
}
|
|
7203
7115
|
function isDedicatedBrandFile(filePath) {
|
|
7204
|
-
if (filePath.split(
|
|
7116
|
+
if (filePath.split(path12.sep).includes("@brand")) {
|
|
7205
7117
|
return true;
|
|
7206
7118
|
}
|
|
7207
|
-
const { type } =
|
|
7119
|
+
const { type } = parseFrontmatter3(filePath);
|
|
7208
7120
|
return type.startsWith("design/color") || type.startsWith("design/typography");
|
|
7209
7121
|
}
|
|
7210
7122
|
function isBrandRelevant(filePath) {
|
|
7211
|
-
return filePath ===
|
|
7123
|
+
return filePath === path12.join("src", "app.md") || isDedicatedBrandFile(filePath);
|
|
7212
7124
|
}
|
|
7213
7125
|
function computeInputHash() {
|
|
7214
7126
|
const entries = [];
|
|
7215
|
-
for (const filePath of
|
|
7127
|
+
for (const filePath of walkMdFiles2("src")) {
|
|
7216
7128
|
if (isBrandRelevant(filePath)) {
|
|
7217
7129
|
entries.push({ path: filePath, content: readSafe(filePath) });
|
|
7218
7130
|
}
|
|
@@ -7253,14 +7165,14 @@ function readBrandManifest() {
|
|
|
7253
7165
|
return "";
|
|
7254
7166
|
}
|
|
7255
7167
|
}
|
|
7256
|
-
function
|
|
7168
|
+
function walkMdFiles2(dir) {
|
|
7257
7169
|
const results = [];
|
|
7258
7170
|
try {
|
|
7259
7171
|
const entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
7260
7172
|
for (const entry of entries) {
|
|
7261
|
-
const full =
|
|
7173
|
+
const full = path12.join(dir, entry.name);
|
|
7262
7174
|
if (entry.isDirectory()) {
|
|
7263
|
-
results.push(...
|
|
7175
|
+
results.push(...walkMdFiles2(full));
|
|
7264
7176
|
} else if (entry.name.endsWith(".md")) {
|
|
7265
7177
|
results.push(full);
|
|
7266
7178
|
}
|
|
@@ -7269,7 +7181,7 @@ function walkMdFiles3(dir) {
|
|
|
7269
7181
|
}
|
|
7270
7182
|
return results.sort();
|
|
7271
7183
|
}
|
|
7272
|
-
function
|
|
7184
|
+
function parseFrontmatter3(filePath) {
|
|
7273
7185
|
try {
|
|
7274
7186
|
const content = fs22.readFileSync(filePath, "utf-8");
|
|
7275
7187
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -7337,7 +7249,7 @@ async function extractBrand(apiConfig, model) {
|
|
|
7337
7249
|
var HEAD_SLICE_CHARS = 2e3;
|
|
7338
7250
|
var BRAND_CORPUS_CHAR_LIMIT = 24e5;
|
|
7339
7251
|
function buildCorpus() {
|
|
7340
|
-
const all =
|
|
7252
|
+
const all = walkMdFiles2("src");
|
|
7341
7253
|
const ordered = [
|
|
7342
7254
|
...all.filter(isBrandRelevant),
|
|
7343
7255
|
...all.filter((f) => !isBrandRelevant(f))
|
|
@@ -9350,10 +9262,7 @@ var HeadlessSession = class {
|
|
|
9350
9262
|
const buildModel = hasSentinel(rawText, "approvePlan") ? parsed.buildModel : void 0;
|
|
9351
9263
|
const onboardingState = parsed.onboardingState ?? "onboardingFinished";
|
|
9352
9264
|
this.currentOnboardingState = onboardingState;
|
|
9353
|
-
const system = buildSystemPrompt(
|
|
9354
|
-
onboardingState,
|
|
9355
|
-
parsed.viewContext
|
|
9356
|
-
);
|
|
9265
|
+
const system = buildSystemPrompt(onboardingState);
|
|
9357
9266
|
if (resolved?.next && !fromChain) {
|
|
9358
9267
|
for (const step of getActionChain(resolved.next)) {
|
|
9359
9268
|
this.queue.push({
|
|
@@ -9430,13 +9339,12 @@ var HeadlessSession = class {
|
|
|
9430
9339
|
}
|
|
9431
9340
|
const onboardingState = batch.find((b) => b.command.onboardingState !== void 0)?.command.onboardingState ?? this.currentOnboardingState ?? "onboardingFinished";
|
|
9432
9341
|
this.currentOnboardingState = onboardingState;
|
|
9433
|
-
const viewContext = [...batch].reverse().find((b) => b.command.viewContext !== void 0)?.command.viewContext;
|
|
9434
9342
|
await this.executeTurn({
|
|
9435
9343
|
entries,
|
|
9436
9344
|
requestId: primaryRid,
|
|
9437
9345
|
absorbedRids,
|
|
9438
9346
|
onboardingState,
|
|
9439
|
-
system: buildSystemPrompt(onboardingState
|
|
9347
|
+
system: buildSystemPrompt(onboardingState)
|
|
9440
9348
|
});
|
|
9441
9349
|
}
|
|
9442
9350
|
/**
|
package/dist/index.js
CHANGED
|
@@ -4215,7 +4215,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
4215
4215
|
let onLog;
|
|
4216
4216
|
let model;
|
|
4217
4217
|
let apiConfig;
|
|
4218
|
-
let
|
|
4218
|
+
let path14;
|
|
4219
4219
|
let fullPage = true;
|
|
4220
4220
|
let width;
|
|
4221
4221
|
let height;
|
|
@@ -4223,7 +4223,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
4223
4223
|
if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
|
|
4224
4224
|
prompt = promptOrOptions.prompt;
|
|
4225
4225
|
existingImage = promptOrOptions.image;
|
|
4226
|
-
|
|
4226
|
+
path14 = promptOrOptions.path;
|
|
4227
4227
|
if (promptOrOptions.fullPage !== void 0) {
|
|
4228
4228
|
fullPage = promptOrOptions.fullPage;
|
|
4229
4229
|
}
|
|
@@ -4247,7 +4247,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
4247
4247
|
const ssResult = await sidecarRequest(
|
|
4248
4248
|
fullPage ? "/screenshot-full-page" : "/screenshot-viewport",
|
|
4249
4249
|
{
|
|
4250
|
-
...
|
|
4250
|
+
...path14 ? { path: path14 } : {},
|
|
4251
4251
|
...width != null ? { width } : {},
|
|
4252
4252
|
...height != null ? { height } : {},
|
|
4253
4253
|
...format ? { format } : {}
|
|
@@ -8977,7 +8977,6 @@ var init_agent = __esm({
|
|
|
8977
8977
|
|
|
8978
8978
|
// src/prompt/static/projectContext.ts
|
|
8979
8979
|
import fs21 from "fs";
|
|
8980
|
-
import path12 from "path";
|
|
8981
8980
|
function loadProjectRoot() {
|
|
8982
8981
|
return `
|
|
8983
8982
|
## Project Root
|
|
@@ -8985,78 +8984,23 @@ function loadProjectRoot() {
|
|
|
8985
8984
|
|
|
8986
8985
|
File paths are relative to this directory. Every tool operates here and bash commands run it it.`;
|
|
8987
8986
|
}
|
|
8988
|
-
function
|
|
8989
|
-
try {
|
|
8990
|
-
const manifest = fs21.readFileSync("mindstudio.json", "utf-8");
|
|
8991
|
-
return `
|
|
8992
|
-
## Project Manifest (mindstudio.json)
|
|
8993
|
-
\`\`\`json
|
|
8994
|
-
${manifest}
|
|
8995
|
-
\`\`\``;
|
|
8996
|
-
} catch {
|
|
8997
|
-
return "";
|
|
8998
|
-
}
|
|
8999
|
-
}
|
|
9000
|
-
function loadSpecFileMetadata() {
|
|
8987
|
+
function loadAppIdentity() {
|
|
9001
8988
|
try {
|
|
9002
|
-
const
|
|
9003
|
-
|
|
8989
|
+
const manifest = JSON.parse(fs21.readFileSync("mindstudio.json", "utf-8"));
|
|
8990
|
+
const name = typeof manifest.name === "string" ? manifest.name.trim() : "";
|
|
8991
|
+
if (!name) {
|
|
9004
8992
|
return "";
|
|
9005
8993
|
}
|
|
9006
|
-
const
|
|
9007
|
-
for (const filePath of files) {
|
|
9008
|
-
const { name, description, type } = parseFrontmatter4(filePath);
|
|
9009
|
-
let line = `- ${filePath}`;
|
|
9010
|
-
if (name) {
|
|
9011
|
-
line += ` \u2014 "${name}"`;
|
|
9012
|
-
}
|
|
9013
|
-
if (type) {
|
|
9014
|
-
line += ` (${type})`;
|
|
9015
|
-
}
|
|
9016
|
-
if (description) {
|
|
9017
|
-
line += ` \u2014 ${description}`;
|
|
9018
|
-
}
|
|
9019
|
-
entries.push(line);
|
|
9020
|
-
}
|
|
8994
|
+
const description = typeof manifest.description === "string" ? manifest.description.trim() : "";
|
|
9021
8995
|
return `
|
|
9022
|
-
##
|
|
9023
|
-
${
|
|
8996
|
+
## App
|
|
8997
|
+
"${name}"${description ? ` \u2014 ${description}` : ""}
|
|
8998
|
+
|
|
8999
|
+
Full manifest: \`mindstudio.json\` (read it when you need the app's structure, tables, methods, roles, auth settings, interfaces, and everything else).`;
|
|
9024
9000
|
} catch {
|
|
9025
9001
|
return "";
|
|
9026
9002
|
}
|
|
9027
9003
|
}
|
|
9028
|
-
function walkMdFiles3(dir) {
|
|
9029
|
-
const results = [];
|
|
9030
|
-
try {
|
|
9031
|
-
const entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
9032
|
-
for (const entry of entries) {
|
|
9033
|
-
const full = path12.join(dir, entry.name);
|
|
9034
|
-
if (entry.isDirectory()) {
|
|
9035
|
-
results.push(...walkMdFiles3(full));
|
|
9036
|
-
} else if (entry.name.endsWith(".md")) {
|
|
9037
|
-
results.push(full);
|
|
9038
|
-
}
|
|
9039
|
-
}
|
|
9040
|
-
} catch {
|
|
9041
|
-
}
|
|
9042
|
-
return results.sort();
|
|
9043
|
-
}
|
|
9044
|
-
function parseFrontmatter4(filePath) {
|
|
9045
|
-
try {
|
|
9046
|
-
const content = fs21.readFileSync(filePath, "utf-8");
|
|
9047
|
-
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
9048
|
-
if (!match) {
|
|
9049
|
-
return { name: "", description: "", type: "" };
|
|
9050
|
-
}
|
|
9051
|
-
const fm = match[1];
|
|
9052
|
-
const name = fm.match(/^name:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
9053
|
-
const description = fm.match(/^description:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
9054
|
-
const type = fm.match(/^type:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
9055
|
-
return { name, description, type };
|
|
9056
|
-
} catch {
|
|
9057
|
-
return { name: "", description: "", type: "" };
|
|
9058
|
-
}
|
|
9059
|
-
}
|
|
9060
9004
|
function loadPlanStatus(onboardingState) {
|
|
9061
9005
|
try {
|
|
9062
9006
|
const content = fs21.readFileSync(".remy-plan.md", "utf-8");
|
|
@@ -9085,27 +9029,6 @@ The user has approved your implementation plan in .remy-plan.md. You may referen
|
|
|
9085
9029
|
return "";
|
|
9086
9030
|
}
|
|
9087
9031
|
}
|
|
9088
|
-
function loadProjectFileListing() {
|
|
9089
|
-
try {
|
|
9090
|
-
const entries = fs21.readdirSync(".", { withFileTypes: true });
|
|
9091
|
-
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
9092
|
-
if (a.isDirectory() && !b.isDirectory()) {
|
|
9093
|
-
return -1;
|
|
9094
|
-
}
|
|
9095
|
-
if (!a.isDirectory() && b.isDirectory()) {
|
|
9096
|
-
return 1;
|
|
9097
|
-
}
|
|
9098
|
-
return a.name.localeCompare(b.name);
|
|
9099
|
-
}).map((e) => e.isDirectory() ? `${e.name}/` : e.name).join("\n");
|
|
9100
|
-
return `
|
|
9101
|
-
## Project Files
|
|
9102
|
-
\`\`\`
|
|
9103
|
-
${listing}
|
|
9104
|
-
\`\`\``;
|
|
9105
|
-
} catch {
|
|
9106
|
-
return "";
|
|
9107
|
-
}
|
|
9108
|
-
}
|
|
9109
9032
|
var init_projectContext = __esm({
|
|
9110
9033
|
"src/prompt/static/projectContext.ts"() {
|
|
9111
9034
|
"use strict";
|
|
@@ -9121,13 +9044,7 @@ function resolveIncludes(template) {
|
|
|
9121
9044
|
);
|
|
9122
9045
|
return result.replace(/\n{3,}/g, "\n\n").trim();
|
|
9123
9046
|
}
|
|
9124
|
-
function buildSystemPrompt(onboardingState
|
|
9125
|
-
const projectContext = [
|
|
9126
|
-
loadProjectRoot(),
|
|
9127
|
-
loadProjectManifest(),
|
|
9128
|
-
loadSpecFileMetadata(),
|
|
9129
|
-
loadProjectFileListing()
|
|
9130
|
-
].filter(Boolean).join("\n");
|
|
9047
|
+
function buildSystemPrompt(onboardingState) {
|
|
9131
9048
|
const now = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
|
|
9132
9049
|
month: "long",
|
|
9133
9050
|
day: "numeric",
|
|
@@ -9136,8 +9053,6 @@ function buildSystemPrompt(onboardingState, viewContext) {
|
|
|
9136
9053
|
const template = `
|
|
9137
9054
|
{{static/identity.md}}
|
|
9138
9055
|
|
|
9139
|
-
Current date: ${now}
|
|
9140
|
-
|
|
9141
9056
|
<platform_docs>
|
|
9142
9057
|
<platform>
|
|
9143
9058
|
{{compiled/platform.md}}
|
|
@@ -9217,7 +9132,7 @@ ${loadSkillsCatalog()}
|
|
|
9217
9132
|
<conversation_summaries>
|
|
9218
9133
|
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.
|
|
9219
9134
|
|
|
9220
|
-
|
|
9135
|
+
Tool results generally persist in the conversation until a compaction summarizes them. In rare cases very old tool results may be trimmed when the conversation nears the context limit. Either way, if you need something from an earlier tool call and can't see it anymore, just re-read the file or re-run the query.
|
|
9221
9136
|
</conversation_summaries>
|
|
9222
9137
|
|
|
9223
9138
|
<project_onboarding>
|
|
@@ -9229,24 +9144,21 @@ New projects progress through three onboarding states. The user might skip this
|
|
|
9229
9144
|
- **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.
|
|
9230
9145
|
</project_onboarding>
|
|
9231
9146
|
|
|
9147
|
+
${loadProjectRoot()}
|
|
9148
|
+
|
|
9149
|
+
${renderOrgContextBlock()}
|
|
9150
|
+
|
|
9232
9151
|
{{static/instructions.md}}
|
|
9233
9152
|
|
|
9234
9153
|
<!-- cache_breakpoint -->
|
|
9235
9154
|
|
|
9155
|
+
Current date: ${now}
|
|
9156
|
+
|
|
9236
9157
|
<current_project_onboarding_state>
|
|
9237
9158
|
${onboardingState ?? "onboardingFinished"}
|
|
9238
9159
|
</current_project_onboarding_state>
|
|
9239
9160
|
|
|
9240
|
-
|
|
9241
|
-
${projectContext}
|
|
9242
|
-
</project_context>
|
|
9243
|
-
|
|
9244
|
-
${renderOrgContextBlock()}
|
|
9245
|
-
|
|
9246
|
-
<view_context>
|
|
9247
|
-
The user is currently in ${viewContext?.mode ?? "code"} mode.
|
|
9248
|
-
${viewContext?.activeFile ? `Active file: ${viewContext.activeFile}` : ""}
|
|
9249
|
-
</view_context>
|
|
9161
|
+
${loadAppIdentity()}
|
|
9250
9162
|
|
|
9251
9163
|
${loadPlanStatus(onboardingState)}
|
|
9252
9164
|
`;
|
|
@@ -9264,7 +9176,7 @@ var init_prompt4 = __esm({
|
|
|
9264
9176
|
|
|
9265
9177
|
// src/config.ts
|
|
9266
9178
|
import fs22 from "fs";
|
|
9267
|
-
import
|
|
9179
|
+
import path12 from "path";
|
|
9268
9180
|
import os from "os";
|
|
9269
9181
|
function loadConfigFile() {
|
|
9270
9182
|
try {
|
|
@@ -9307,7 +9219,7 @@ var init_config = __esm({
|
|
|
9307
9219
|
"use strict";
|
|
9308
9220
|
init_logger();
|
|
9309
9221
|
log14 = createLogger("config");
|
|
9310
|
-
CONFIG_PATH =
|
|
9222
|
+
CONFIG_PATH = path12.join(
|
|
9311
9223
|
os.homedir(),
|
|
9312
9224
|
".mindstudio-local-tunnel",
|
|
9313
9225
|
"config.json"
|
|
@@ -10269,10 +10181,7 @@ var init_headless = __esm({
|
|
|
10269
10181
|
const buildModel = hasSentinel(rawText, "approvePlan") ? parsed.buildModel : void 0;
|
|
10270
10182
|
const onboardingState = parsed.onboardingState ?? "onboardingFinished";
|
|
10271
10183
|
this.currentOnboardingState = onboardingState;
|
|
10272
|
-
const system = buildSystemPrompt(
|
|
10273
|
-
onboardingState,
|
|
10274
|
-
parsed.viewContext
|
|
10275
|
-
);
|
|
10184
|
+
const system = buildSystemPrompt(onboardingState);
|
|
10276
10185
|
if (resolved?.next && !fromChain) {
|
|
10277
10186
|
for (const step of getActionChain(resolved.next)) {
|
|
10278
10187
|
this.queue.push({
|
|
@@ -10349,13 +10258,12 @@ var init_headless = __esm({
|
|
|
10349
10258
|
}
|
|
10350
10259
|
const onboardingState = batch.find((b) => b.command.onboardingState !== void 0)?.command.onboardingState ?? this.currentOnboardingState ?? "onboardingFinished";
|
|
10351
10260
|
this.currentOnboardingState = onboardingState;
|
|
10352
|
-
const viewContext = [...batch].reverse().find((b) => b.command.viewContext !== void 0)?.command.viewContext;
|
|
10353
10261
|
await this.executeTurn({
|
|
10354
10262
|
entries,
|
|
10355
10263
|
requestId: primaryRid,
|
|
10356
10264
|
absorbedRids,
|
|
10357
10265
|
onboardingState,
|
|
10358
|
-
system: buildSystemPrompt(onboardingState
|
|
10266
|
+
system: buildSystemPrompt(onboardingState)
|
|
10359
10267
|
});
|
|
10360
10268
|
}
|
|
10361
10269
|
/**
|
|
@@ -10896,7 +10804,7 @@ var init_headless = __esm({
|
|
|
10896
10804
|
import { render } from "ink";
|
|
10897
10805
|
import os2 from "os";
|
|
10898
10806
|
import fs23 from "fs";
|
|
10899
|
-
import
|
|
10807
|
+
import path13 from "path";
|
|
10900
10808
|
|
|
10901
10809
|
// src/tui/App.tsx
|
|
10902
10810
|
import { useState as useState2, useCallback, useRef } from "react";
|
|
@@ -11222,7 +11130,7 @@ var startupLog = createLogger("startup");
|
|
|
11222
11130
|
function printDebugInfo(config) {
|
|
11223
11131
|
const pkg = JSON.parse(
|
|
11224
11132
|
fs23.readFileSync(
|
|
11225
|
-
|
|
11133
|
+
path13.join(import.meta.dirname, "..", "package.json"),
|
|
11226
11134
|
"utf-8"
|
|
11227
11135
|
)
|
|
11228
11136
|
);
|
|
@@ -99,6 +99,12 @@ method itself can read `session.voiceSessionId` / `session.visitorId` from the a
|
|
|
99
99
|
(`import { session } from '@mindstudio-ai/agent'`) — the same id the browser holds as
|
|
100
100
|
`session.sessionId`, guaranteed by the platform rather than echoed by the model.
|
|
101
101
|
|
|
102
|
+
Voice sessions also carry `session.medium` (`'web' | 'phone-in' | 'phone-out'`) and, on phone
|
|
103
|
+
calls, `session.sip` (`{ to, fromNumber }`) — use `medium` in tool methods and the session-context
|
|
104
|
+
method to branch web vs phone behavior (what to prefetch, how to phrase context). Treat
|
|
105
|
+
`session.sip.fromNumber` as context only, never identity: caller ID is spoofable, so don't gate
|
|
106
|
+
data or roles on it — in-call verification is the auth rail.
|
|
107
|
+
|
|
102
108
|
### Client tools: actions that happen on screen (`target: "client"`)
|
|
103
109
|
|
|
104
110
|
A tool whose effect belongs in the browser — open the verification sheet, navigate to a page,
|
|
@@ -136,6 +142,11 @@ session.registerClientTool('showVerification', async ({ reason }) => {
|
|
|
136
142
|
becomes an error/ack the agent can speak around.
|
|
137
143
|
- The progressive-auth pattern above is the canonical use: make the verification sheet a client
|
|
138
144
|
tool and the agent opens it deliberately instead of the frontend inferring it from tool events.
|
|
145
|
+
- Phone sessions never see client tools — there is no browser on a call, so the platform drops
|
|
146
|
+
them from the toolset and tells the agent it's on a phone call with no screen. Verification
|
|
147
|
+
branches by itself: on a phone the agent gets the platform's in-call verify tools instead of
|
|
148
|
+
the app's sheet. Nothing to author; just don't make a client tool the only path to something
|
|
149
|
+
phone callers need.
|
|
139
150
|
|
|
140
151
|
### Tool descriptions say results out loud
|
|
141
152
|
|
|
@@ -338,7 +349,10 @@ export async function callMeAboutMyOrder(input: { phone: string }) {
|
|
|
338
349
|
who it's talking to (Current User block) and every tool call carries their roles — regardless
|
|
339
350
|
of which number was dialed (the user types any number into a field; identity comes from their
|
|
340
351
|
session, not the phone). Omitted/false → anonymous call; role-gated tools decline.
|
|
341
|
-
System/cron invocations have no human identity and always run anonymously.
|
|
352
|
+
System/cron invocations have no human identity and always run anonymously. Anonymous outbound
|
|
353
|
+
calls (deployed) get the same in-call verification flow as inbound — the callee proves
|
|
354
|
+
possession of the number that was dialed, or verifies by email — so an anonymous call can
|
|
355
|
+
still upgrade to a known user mid-conversation.
|
|
342
356
|
- **Production needs a dedicated phone number.** The app owner attaches one ($1/month) via the
|
|
343
357
|
dashboard or `mindstudio-prod voice numbers` (see "The voice CLI"
|
|
344
358
|
below) — it becomes the caller ID for every call, in dev sessions too, so users always see
|