@mindstudio-ai/remy 0.1.269 → 0.1.270
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/README.md +10 -18
- package/dist/headless.js +99 -61
- package/dist/index.js +113 -62
- package/dist/prompt/compiled/auth.md +20 -415
- package/dist/prompt/compiled/design.md +4 -2
- package/dist/prompt/compiled/dev-and-deploy.md +1 -8
- package/dist/prompt/compiled/interfaces.md +9 -27
- package/dist/prompt/compiled/methods.md +3 -37
- package/dist/prompt/compiled/msfm.md +1 -16
- package/dist/prompt/compiled/platform.md +7 -17
- package/dist/prompt/skills/auth.md +443 -0
- package/dist/prompt/{compiled → skills}/files.md +6 -0
- package/dist/prompt/{compiled → skills}/scenarios.md +6 -0
- package/dist/prompt/{compiled → skills}/secrets.md +6 -0
- package/dist/prompt/skills/voiceInterfaces.md +15 -9
- package/dist/prompt/static/authoring.md +1 -1
- package/dist/prompt/static/coding.md +4 -2
- package/dist/prompt/static/intake.md +1 -4
- package/dist/prompt/static/spec-maintenance.md +41 -0
- package/dist/prompt/static/team.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,13 +43,13 @@ Remy saves conversation history to `.remy-session.json` in the working directory
|
|
|
43
43
|
|
|
44
44
|
## Tools
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
The full tool set is always available — it is deliberately invariant (no onboarding-state gating) so the provider's tools-tier cache prefix stays identical across turns and sessions. The sections below are thematic groupings only.
|
|
47
47
|
|
|
48
|
-
### Common Tools
|
|
48
|
+
### Common Tools
|
|
49
49
|
|
|
50
50
|
| Tool | Description |
|
|
51
51
|
|------|-------------|
|
|
52
|
-
| `setProjectOnboardingState` | Advance the onboarding flow (intake →
|
|
52
|
+
| `setProjectOnboardingState` | Advance the onboarding flow (intake → building → buildComplete → onboardingFinished) |
|
|
53
53
|
| `setProjectName` | Set the project name |
|
|
54
54
|
| `promptUser` | Ask the user structured questions (form or inline display) |
|
|
55
55
|
| `confirmDestructiveAction` | Confirm a destructive or irreversible action with the user |
|
|
@@ -74,8 +74,6 @@ Available in all onboarding states. Used for authoring and editing MSFM specs in
|
|
|
74
74
|
|
|
75
75
|
### Code Tools
|
|
76
76
|
|
|
77
|
-
Available from `initialCodegen` onward.
|
|
78
|
-
|
|
79
77
|
| Tool | Description |
|
|
80
78
|
|------|-------------|
|
|
81
79
|
| `readFile` | Read a file with line numbers |
|
|
@@ -100,9 +98,7 @@ Available when `--lsp-url` is passed.
|
|
|
100
98
|
| `lspDiagnostics` | Type errors and warnings for a file, with suggested quick fixes |
|
|
101
99
|
| `restartProcess` | Restart a managed sandbox process (e.g., dev server after npm install) |
|
|
102
100
|
|
|
103
|
-
###
|
|
104
|
-
|
|
105
|
-
Available only when `onboardingState` is `onboardingFinished`.
|
|
101
|
+
### Development & Publishing Tools
|
|
106
102
|
|
|
107
103
|
| Tool | Description |
|
|
108
104
|
|------|-------------|
|
|
@@ -174,11 +170,7 @@ src/
|
|
|
174
170
|
logger.ts Structured logging
|
|
175
171
|
|
|
176
172
|
prompt/
|
|
177
|
-
index.ts System prompt builder (
|
|
178
|
-
actions/ Built-in prompts for runCommand actions
|
|
179
|
-
sync.md
|
|
180
|
-
publish.md
|
|
181
|
-
buildFromInitialSpec.md
|
|
173
|
+
index.ts System prompt builder (static prefix + small dynamic tail)
|
|
182
174
|
static/ Behavioral instruction fragments
|
|
183
175
|
identity.md
|
|
184
176
|
intake.md
|
|
@@ -187,7 +179,7 @@ src/
|
|
|
187
179
|
instructions.md
|
|
188
180
|
team.md
|
|
189
181
|
lsp.md
|
|
190
|
-
projectContext.ts Reads
|
|
182
|
+
projectContext.ts Reads project root, app identity, plan status at runtime
|
|
191
183
|
compiled/ Platform docs distilled for agent consumption
|
|
192
184
|
sources/ Prompt source material (hand-maintained)
|
|
193
185
|
|
|
@@ -267,7 +259,7 @@ The headless IPC protocol uses request correlation and a unified response patter
|
|
|
267
259
|
- Messages sent while a turn is running are queued. When the turn ends, all contiguous queued user messages and background results are delivered together as **one merged turn**: the first queued message's `requestId` becomes the turn's primary id (stamped on `turn_started` and all streaming events), each absorbed message echoes its own `user_message` with its original `requestId` and `queued: true`, and at turn end the primary `completed` is emitted first, followed immediately by one `completed {…same outcome, absorbed: true}` per other absorbed `requestId`. Automated-action (`@@automated::…@@`) messages and chain steps never merge — they always run one turn each.
|
|
268
260
|
- A queued **user** message can be promoted to ASAP delivery via `setQueuedDelivery`. ASAP items are pulled into the **running** turn at its next tool boundary (injected as plain user messages — no abort, no restart), echo `user_message` with `queued: true` and their own `requestId`, and get a `{…, absorbed: true}` completed with the turn's outcome at turn end. Promotion deliberately jumps ahead of anything else in the queue, including chain steps. If the turn ends before injection, the tag is ignored and the item drains in normal FIFO order.
|
|
269
261
|
- Background tool completions have three delivery classes (per-tool `backgroundNotify` on the tool definition). `wake` (default): the result is queued as a `background_results` message and may start a turn when the agent is idle. `passive` (e.g. `specSync`): the result never enters the queue and never wakes the agent — it parks in a persisted holding pen and rides the next real turn as a hidden `background_results` entry (so it never appears in `queuedMessages`, never affects queue-derived busy state, and never triggers resume-on-restart). `silent` (e.g. `compactConversation`): the tool block is updated for the UI and the model is never told — its outcome reaches the model by another mechanism. All classes emit `tool_background_complete` immediately. `specSync` also takes a `refreshBuildOverview` flag (set post-deploy / post-milestone, prompted via the publish flow): the run gains the `writeBuildOverview` tool and re-authors the Build Overview from the freshly-reconciled spec — the design-expert render runs foreground within specSync's already-detached run, never nested-background.
|
|
270
|
-
- Compaction lifecycle rides `compaction_started {blocking}` / `compaction_complete {error?}` system events. Every compaction also renders as a normal `compactConversation` tool call
|
|
262
|
+
- Compaction lifecycle rides `compaction_started {blocking}` / `compaction_complete {error?}` system events. Every compaction also renders as a normal `compactConversation` tool call. The **model-invoked** tool is a real background tool (its block completes via `tool_background_complete`; the summary lands in `backgroundResult`). User (`/compact`) and gate-initiated compactions instead get a **synthesized UI-only foreground block** in history — the user is actively waiting on these, so they follow the normal foreground lifecycle: `tool_start` with no result, then a (late) `tool_done {result, isError}` carrying the summary or the error when the compaction finishes. Either way the block is excluded from every API payload — the model receives the summary via the checkpoint prefix instead. Every turn passes a **compaction gate**: it waits for any in-flight compaction and applies the finished checkpoint before running, so no turn ever bills at the uncompacted context. Messages received while a compaction is in flight are queued exactly like mid-turn messages (they appear in `queuedMessages` and drain — with merged-turn semantics — when the compaction completes, on success and failure alike). A `compact` command received **mid-turn** queues too: it's acked immediately with `completed {success: true, queued: true}` (duplicate clicks coalesce onto the queued item), appears in `queuedMessages` as a removable `@@automated::compact@@` user item, and runs as its own drain step when the turn ends — everything queued behind it runs against the compacted history.
|
|
271
263
|
- The caller distinguishes command responses from system events with a single check: `if (msg.requestId)`
|
|
272
264
|
|
|
273
265
|
This enables a simple promise-based RPC layer: send a command with a unique ID, store a pending promise keyed by that ID, resolve it when you see `completed` with the matching ID.
|
|
@@ -287,8 +279,7 @@ Send a user message to the agent.
|
|
|
287
279
|
Fields:
|
|
288
280
|
- `requestId` — caller-provided correlation ID (echoed on all response events)
|
|
289
281
|
- `text` — the user message (required unless `runCommand` is set)
|
|
290
|
-
- `onboardingState` —
|
|
291
|
-
- `viewContext` — `{ mode, openFiles?, activeFile? }` for prompt context
|
|
282
|
+
- `onboardingState` — the project's onboarding phase, reflected in the system prompt's dynamic tail and plan-status behavior. One of: `intake`, `building`, `buildComplete`, `onboardingFinished` (default: `onboardingFinished`)
|
|
292
283
|
- `attachments` — array of `{ url, extractedTextUrl? }` for file attachments
|
|
293
284
|
- `runCommand` — triggers a built-in action prompt (`"sync"`, `"publish"`, `"buildFromInitialSpec"`)
|
|
294
285
|
|
|
@@ -379,7 +370,8 @@ All command responses include the `requestId` from the originating command.
|
|
|
379
370
|
| `user_message` | `text`, `attachments?`, `queued?`, `hidden?` | Echo of a user message entering the turn. Queue-delivered messages (including ASAP items injected mid-turn) carry `queued: true` and their own original `requestId` (a merged turn emits one per absorbed message); idle sends echo with the turn's requestId and no `queued` flag. `hidden: true` marks internal entries (e.g. passive background results) that should not render. |
|
|
380
371
|
| `tool_start` | `id`, `name`, `input`, `partial?`, `parentToolId?` | Tool execution started. `partial: true` means more `tool_start` events will follow for this id (progressive input streaming). |
|
|
381
372
|
| `tool_input_delta` | `id`, `name`, `result`, `parentToolId?` | Progressive tool content (streaming tools only) |
|
|
382
|
-
| `tool_done` | `id`, `name`, `result`, `isError`, `parentToolId?` | Tool execution completed |
|
|
373
|
+
| `tool_done` | `id`, `name`, `result`, `isError`, `parentToolId?` | Tool execution completed. For a background tool this carries the synchronous ack; for a synthesized user/gate compaction block it arrives late, when the compaction finishes, carrying the summary (or error). |
|
|
374
|
+
| `tool_background_complete` | `id`, `name`, `result`, `parentToolId?` | A background tool's detached work finished; `result` belongs in the block's `backgroundResult`. Emitted for all `backgroundNotify` classes. |
|
|
383
375
|
| `status` | `message` | Contextual status label (e.g., "Writing files...") |
|
|
384
376
|
| `error` | `error` | Error message (may precede `completed`) |
|
|
385
377
|
| `history` | `messages` | Response to `get_history` |
|
package/dist/headless.js
CHANGED
|
@@ -628,7 +628,7 @@ function loadProjectRoot() {
|
|
|
628
628
|
## Project Root
|
|
629
629
|
\`${PROJECT_ROOT}\`
|
|
630
630
|
|
|
631
|
-
File paths are relative to this directory. Every tool operates here and bash commands run
|
|
631
|
+
File paths are relative to this directory. Every tool operates here and bash commands run in it.`;
|
|
632
632
|
}
|
|
633
633
|
function loadAppIdentity() {
|
|
634
634
|
try {
|
|
@@ -797,21 +797,9 @@ function buildSystemPrompt(onboardingState) {
|
|
|
797
797
|
{{compiled/design.md}}
|
|
798
798
|
</design>
|
|
799
799
|
|
|
800
|
-
<app_files>
|
|
801
|
-
{{compiled/files.md}}
|
|
802
|
-
</app_files>
|
|
803
|
-
|
|
804
800
|
<interfaces>
|
|
805
801
|
{{compiled/interfaces.md}}
|
|
806
802
|
</interfaces>
|
|
807
|
-
|
|
808
|
-
<scenarios>
|
|
809
|
-
{{compiled/scenarios.md}}
|
|
810
|
-
</scenarios>
|
|
811
|
-
|
|
812
|
-
<secrets>
|
|
813
|
-
{{compiled/secrets.md}}
|
|
814
|
-
</secrets>
|
|
815
803
|
</platform_docs>
|
|
816
804
|
|
|
817
805
|
${loadSkillsCatalog()}
|
|
@@ -824,12 +812,12 @@ ${loadSkillsCatalog()}
|
|
|
824
812
|
{{compiled/msfm.md}}
|
|
825
813
|
</mindstudio_flavored_markdown_spec_docs>
|
|
826
814
|
|
|
827
|
-
|
|
815
|
+
${onboardingState === "intake" ? `<intake_mode_instructions>
|
|
828
816
|
{{static/intake.md}}
|
|
829
|
-
</intake_mode_instructions
|
|
817
|
+
</intake_mode_instructions>` : ""}
|
|
830
818
|
|
|
831
819
|
<spec_authoring_instructions>
|
|
832
|
-
{{static/authoring.md}}
|
|
820
|
+
${onboardingState !== void 0 && onboardingState !== "onboardingFinished" ? "{{static/authoring.md}}" : "{{static/spec-maintenance.md}}"}
|
|
833
821
|
</spec_authoring_instructions>
|
|
834
822
|
|
|
835
823
|
<team>
|
|
@@ -851,7 +839,7 @@ Tool results generally persist in the conversation until a compaction summarizes
|
|
|
851
839
|
</conversation_summaries>
|
|
852
840
|
|
|
853
841
|
<project_onboarding>
|
|
854
|
-
New projects progress through
|
|
842
|
+
New projects progress through four 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:
|
|
855
843
|
|
|
856
844
|
- **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.
|
|
857
845
|
- **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).
|
|
@@ -1378,8 +1366,33 @@ var presentPublishPlanTool = {
|
|
|
1378
1366
|
}
|
|
1379
1367
|
};
|
|
1380
1368
|
|
|
1369
|
+
// src/atomicWrite.ts
|
|
1370
|
+
import fs10 from "fs";
|
|
1371
|
+
import fsp from "fs/promises";
|
|
1372
|
+
var writeFileAtomicSync = (file, data) => {
|
|
1373
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
1374
|
+
const fd2 = fs10.openSync(tmp, "w");
|
|
1375
|
+
try {
|
|
1376
|
+
fs10.writeSync(fd2, data);
|
|
1377
|
+
fs10.fsyncSync(fd2);
|
|
1378
|
+
} finally {
|
|
1379
|
+
fs10.closeSync(fd2);
|
|
1380
|
+
}
|
|
1381
|
+
fs10.renameSync(tmp, file);
|
|
1382
|
+
};
|
|
1383
|
+
var writeFileAtomic = async (file, data) => {
|
|
1384
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
1385
|
+
const handle = await fsp.open(tmp, "w");
|
|
1386
|
+
try {
|
|
1387
|
+
await handle.writeFile(data);
|
|
1388
|
+
await handle.sync();
|
|
1389
|
+
} finally {
|
|
1390
|
+
await handle.close();
|
|
1391
|
+
}
|
|
1392
|
+
await fsp.rename(tmp, file);
|
|
1393
|
+
};
|
|
1394
|
+
|
|
1381
1395
|
// src/tools/spec/writePlan.ts
|
|
1382
|
-
import fs10 from "fs/promises";
|
|
1383
1396
|
var PLAN_FILE = ".remy-plan.md";
|
|
1384
1397
|
var writePlanTool = {
|
|
1385
1398
|
definition: {
|
|
@@ -1403,7 +1416,7 @@ status: pending
|
|
|
1403
1416
|
---
|
|
1404
1417
|
|
|
1405
1418
|
${content}`;
|
|
1406
|
-
await
|
|
1419
|
+
await writeFileAtomic(PLAN_FILE, file);
|
|
1407
1420
|
return "Plan written to .remy-plan.md. Waiting for user approval.";
|
|
1408
1421
|
}
|
|
1409
1422
|
};
|
|
@@ -1439,13 +1452,13 @@ var updatePlanStatusTool = {
|
|
|
1439
1452
|
return "No plan file found.";
|
|
1440
1453
|
}
|
|
1441
1454
|
if (status === "rejected") {
|
|
1442
|
-
await fs11.unlink(PLAN_FILE2)
|
|
1455
|
+
await fs11.unlink(PLAN_FILE2).catch(() => {
|
|
1456
|
+
});
|
|
1443
1457
|
return "Plan rejected and removed.";
|
|
1444
1458
|
}
|
|
1445
|
-
await
|
|
1459
|
+
await writeFileAtomic(
|
|
1446
1460
|
PLAN_FILE2,
|
|
1447
|
-
content.replace(/^status:\s*\w+/m, `status: ${status}`)
|
|
1448
|
-
"utf-8"
|
|
1461
|
+
content.replace(/^status:\s*\w+/m, `status: ${status}`)
|
|
1449
1462
|
);
|
|
1450
1463
|
return "Plan approved. Proceeding with implementation.";
|
|
1451
1464
|
}
|
|
@@ -2975,7 +2988,8 @@ var CONTENT_TYPES = {
|
|
|
2975
2988
|
".jpg": "image/jpeg",
|
|
2976
2989
|
".jpeg": "image/jpeg",
|
|
2977
2990
|
".gif": "image/gif",
|
|
2978
|
-
".webp": "image/webp"
|
|
2991
|
+
".webp": "image/webp",
|
|
2992
|
+
".svg": "image/svg+xml"
|
|
2979
2993
|
};
|
|
2980
2994
|
var hosted = /* @__PURE__ */ new Map();
|
|
2981
2995
|
function isFetchableUrl(ref) {
|
|
@@ -3582,7 +3596,8 @@ async function runSubAgent(config) {
|
|
|
3582
3596
|
background,
|
|
3583
3597
|
acquireLock,
|
|
3584
3598
|
onBackgroundComplete,
|
|
3585
|
-
captureArtifacts
|
|
3599
|
+
captureArtifacts,
|
|
3600
|
+
cachePolicy = "run"
|
|
3586
3601
|
} = config;
|
|
3587
3602
|
const artifacts = {};
|
|
3588
3603
|
const bgAbort = background ? new AbortController() : null;
|
|
@@ -3677,6 +3692,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
3677
3692
|
system: fullSystem,
|
|
3678
3693
|
messages: cleanMessagesForApi(messages),
|
|
3679
3694
|
tools: tools2,
|
|
3695
|
+
cachePolicy,
|
|
3680
3696
|
signal
|
|
3681
3697
|
},
|
|
3682
3698
|
{
|
|
@@ -5243,7 +5259,7 @@ function load() {
|
|
|
5243
5259
|
}
|
|
5244
5260
|
function save(indices) {
|
|
5245
5261
|
try {
|
|
5246
|
-
|
|
5262
|
+
writeFileAtomicSync(SAMPLE_FILE, JSON.stringify(indices));
|
|
5247
5263
|
} catch {
|
|
5248
5264
|
}
|
|
5249
5265
|
}
|
|
@@ -5494,6 +5510,8 @@ async function runDesignExpert(opts, context) {
|
|
|
5494
5510
|
}),
|
|
5495
5511
|
task: opts.task,
|
|
5496
5512
|
history: history.length > 0 ? history : void 0,
|
|
5513
|
+
// History-carrying thread re-sent across parent turns — 1h-TTL profile.
|
|
5514
|
+
cachePolicy: "conversation",
|
|
5497
5515
|
tools: opts.render ? DESIGN_EXPERT_RENDER_TOOLS : DESIGN_EXPERT_TOOLS,
|
|
5498
5516
|
externalTools: /* @__PURE__ */ new Set(),
|
|
5499
5517
|
executeTool: (name, input, toolCallId, onLog, sams) => {
|
|
@@ -5761,6 +5779,8 @@ var productVisionTool = {
|
|
|
5761
5779
|
system: getProductVisionPrompt(),
|
|
5762
5780
|
task: input.task,
|
|
5763
5781
|
history: history.length > 0 ? history : void 0,
|
|
5782
|
+
// History-carrying thread re-sent across parent turns — 1h-TTL profile.
|
|
5783
|
+
cachePolicy: "conversation",
|
|
5764
5784
|
tools: VISION_TOOLS,
|
|
5765
5785
|
externalTools: /* @__PURE__ */ new Set(),
|
|
5766
5786
|
executeTool: (name, input2, toolCallId, _onLog, sams) => {
|
|
@@ -6228,7 +6248,7 @@ var SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
6228
6248
|
"runAutomatedBrowserTest",
|
|
6229
6249
|
"askMindStudioSdk"
|
|
6230
6250
|
]);
|
|
6231
|
-
function getToolDefinitions(
|
|
6251
|
+
function getToolDefinitions() {
|
|
6232
6252
|
return ALL_TOOLS.map((t) => t.definition);
|
|
6233
6253
|
}
|
|
6234
6254
|
function getToolByName(name) {
|
|
@@ -6612,7 +6632,11 @@ Write the summary of the conversation above, following your instructions.`;
|
|
|
6612
6632
|
messages: [{ role: "user", content: userContent }],
|
|
6613
6633
|
// Always empty. With a toolset available the model picks `tool_use` over
|
|
6614
6634
|
// producing a summary, leaving summaryText empty.
|
|
6615
|
-
tools: []
|
|
6635
|
+
tools: [],
|
|
6636
|
+
// Each summary call carries a unique 100-200KB chunk that is never
|
|
6637
|
+
// re-read (parallel siblings can't read each other's in-flight writes
|
|
6638
|
+
// either) — a cache write here is pure waste.
|
|
6639
|
+
cachePolicy: "oneshot"
|
|
6616
6640
|
})) {
|
|
6617
6641
|
if (event.type === "text") {
|
|
6618
6642
|
summaryText += event.text;
|
|
@@ -6648,11 +6672,6 @@ import path11 from "path";
|
|
|
6648
6672
|
var log10 = createLogger("session");
|
|
6649
6673
|
var SESSION_FILE = ".remy-session.json";
|
|
6650
6674
|
var ARCHIVE_DIR = ".logs/sessions";
|
|
6651
|
-
function writeFileAtomicSync(file, data) {
|
|
6652
|
-
const tmp = `${file}.tmp`;
|
|
6653
|
-
fs21.writeFileSync(tmp, data, "utf-8");
|
|
6654
|
-
fs21.renameSync(tmp, file);
|
|
6655
|
-
}
|
|
6656
6675
|
var ROTATE_THRESHOLD_BYTES = 32 * 1024 * 1024;
|
|
6657
6676
|
var RETAIN_TAIL_BYTES = 16 * 1024 * 1024;
|
|
6658
6677
|
var ARCHIVE_RETENTION_BYTES = 64 * 1024 * 1024;
|
|
@@ -6681,6 +6700,12 @@ function loadSession(state) {
|
|
|
6681
6700
|
return true;
|
|
6682
6701
|
}
|
|
6683
6702
|
} catch {
|
|
6703
|
+
try {
|
|
6704
|
+
const quarantine = `${SESSION_FILE}.corrupt-${Date.now()}`;
|
|
6705
|
+
fs21.renameSync(SESSION_FILE, quarantine);
|
|
6706
|
+
log10.warn(`Session file unreadable \u2014 quarantined to ${quarantine}`);
|
|
6707
|
+
} catch {
|
|
6708
|
+
}
|
|
6684
6709
|
}
|
|
6685
6710
|
return false;
|
|
6686
6711
|
}
|
|
@@ -7210,7 +7235,10 @@ async function extractBrand(apiConfig, model) {
|
|
|
7210
7235
|
subAgentId: "brandExtractor",
|
|
7211
7236
|
system: EXTRACT_PROMPT,
|
|
7212
7237
|
messages: [{ role: "user", content: corpus }],
|
|
7213
|
-
tools: []
|
|
7238
|
+
tools: [],
|
|
7239
|
+
// One call per extraction over a corpus that changes with every spec
|
|
7240
|
+
// edit — never re-read, so a cache write is pure waste.
|
|
7241
|
+
cachePolicy: "oneshot"
|
|
7214
7242
|
})) {
|
|
7215
7243
|
if (event.type === "text") {
|
|
7216
7244
|
responseText += event.text;
|
|
@@ -7392,11 +7420,9 @@ function pickFont(raw) {
|
|
|
7392
7420
|
return out;
|
|
7393
7421
|
}
|
|
7394
7422
|
function persistBrand(brand, inputHash) {
|
|
7395
|
-
|
|
7396
|
-
fs22.writeFileSync(tmp, JSON.stringify(brand, null, 2), "utf-8");
|
|
7397
|
-
fs22.renameSync(tmp, BRAND_FILE);
|
|
7423
|
+
writeFileAtomicSync(BRAND_FILE, JSON.stringify(brand, null, 2));
|
|
7398
7424
|
const cache = { inputHash, generatedAt: Date.now() };
|
|
7399
|
-
|
|
7425
|
+
writeFileAtomicSync(CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
7400
7426
|
}
|
|
7401
7427
|
function readCache() {
|
|
7402
7428
|
try {
|
|
@@ -7737,7 +7763,7 @@ async function runTurn(params) {
|
|
|
7737
7763
|
toolRegistry,
|
|
7738
7764
|
onBackgroundComplete
|
|
7739
7765
|
} = params;
|
|
7740
|
-
const tools2 = getToolDefinitions(
|
|
7766
|
+
const tools2 = getToolDefinitions();
|
|
7741
7767
|
const buildModelOverride = buildModel ? filterModelPicks({ parent: buildModel }).parent : void 0;
|
|
7742
7768
|
const baseline = resolveModel("parent", state.models, model);
|
|
7743
7769
|
const parentModel = buildModelOverride ?? baseline;
|
|
@@ -7945,6 +7971,9 @@ async function runTurn(params) {
|
|
|
7945
7971
|
system,
|
|
7946
7972
|
messages: cleanMessagesForApi(state.messages),
|
|
7947
7973
|
tools: tools2,
|
|
7974
|
+
// The parent is one long-lived conversation re-read across turns
|
|
7975
|
+
// with multi-minute gaps — the 1h-TTL profile.
|
|
7976
|
+
cachePolicy: "conversation",
|
|
7948
7977
|
signal
|
|
7949
7978
|
},
|
|
7950
7979
|
{
|
|
@@ -8481,16 +8510,15 @@ ${lines.join("\n")}`;
|
|
|
8481
8510
|
}
|
|
8482
8511
|
|
|
8483
8512
|
// src/headless/planFile.ts
|
|
8484
|
-
import { readFileSync,
|
|
8513
|
+
import { readFileSync, unlinkSync } from "fs";
|
|
8485
8514
|
var PLAN_FILE3 = ".remy-plan.md";
|
|
8486
8515
|
function applyPlanFileSideEffect(rawText) {
|
|
8487
8516
|
if (hasSentinel(rawText, "approvePlan") || hasSentinel(rawText, "approveInitialPlan")) {
|
|
8488
8517
|
try {
|
|
8489
8518
|
const plan = readFileSync(PLAN_FILE3, "utf-8");
|
|
8490
|
-
|
|
8519
|
+
writeFileAtomicSync(
|
|
8491
8520
|
PLAN_FILE3,
|
|
8492
|
-
plan.replace(/^status:\s*pending/m, "status: approved")
|
|
8493
|
-
"utf-8"
|
|
8521
|
+
plan.replace(/^status:\s*pending/m, "status: approved")
|
|
8494
8522
|
);
|
|
8495
8523
|
} catch {
|
|
8496
8524
|
}
|
|
@@ -8503,7 +8531,7 @@ function applyPlanFileSideEffect(rawText) {
|
|
|
8503
8531
|
}
|
|
8504
8532
|
|
|
8505
8533
|
// src/headless/stats.ts
|
|
8506
|
-
import { readFileSync as readFileSync2
|
|
8534
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
8507
8535
|
var STATS_FILE = ".remy-stats.json";
|
|
8508
8536
|
function createSessionStats() {
|
|
8509
8537
|
return {
|
|
@@ -8540,7 +8568,7 @@ function loadPassiveResults() {
|
|
|
8540
8568
|
}
|
|
8541
8569
|
function writeStats(stats, queue, passiveResults) {
|
|
8542
8570
|
try {
|
|
8543
|
-
|
|
8571
|
+
writeFileAtomicSync(
|
|
8544
8572
|
STATS_FILE,
|
|
8545
8573
|
JSON.stringify({
|
|
8546
8574
|
...stats,
|
|
@@ -8769,9 +8797,7 @@ var HeadlessSession = class {
|
|
|
8769
8797
|
name: "compactConversation",
|
|
8770
8798
|
input: {},
|
|
8771
8799
|
startedAt: Date.now(),
|
|
8772
|
-
|
|
8773
|
-
uiOnly: true,
|
|
8774
|
-
result: "Compaction started in the background."
|
|
8800
|
+
uiOnly: true
|
|
8775
8801
|
}
|
|
8776
8802
|
]
|
|
8777
8803
|
});
|
|
@@ -8780,15 +8806,7 @@ var HeadlessSession = class {
|
|
|
8780
8806
|
type: "tool_start",
|
|
8781
8807
|
id,
|
|
8782
8808
|
name: "compactConversation",
|
|
8783
|
-
input: {}
|
|
8784
|
-
background: true
|
|
8785
|
-
});
|
|
8786
|
-
this.onEvent({
|
|
8787
|
-
type: "tool_done",
|
|
8788
|
-
id,
|
|
8789
|
-
name: "compactConversation",
|
|
8790
|
-
result: "Compaction started in the background.",
|
|
8791
|
-
isError: false
|
|
8809
|
+
input: {}
|
|
8792
8810
|
});
|
|
8793
8811
|
}
|
|
8794
8812
|
} else {
|
|
@@ -8797,11 +8815,31 @@ var HeadlessSession = class {
|
|
|
8797
8815
|
if (this.syntheticCompactionId) {
|
|
8798
8816
|
const id = this.syntheticCompactionId;
|
|
8799
8817
|
this.syntheticCompactionId = null;
|
|
8800
|
-
|
|
8818
|
+
const result = event.error ? `Error: ${event.error}` : formatSummariesResult(event.summaries ?? []);
|
|
8819
|
+
const isError = !!event.error;
|
|
8820
|
+
for (let i = this.state.messages.length - 1; i >= 0; i--) {
|
|
8821
|
+
const msg = this.state.messages[i];
|
|
8822
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
|
|
8823
|
+
continue;
|
|
8824
|
+
}
|
|
8825
|
+
const block = msg.content.find(
|
|
8826
|
+
(b) => b.type === "tool" && b.id === id
|
|
8827
|
+
);
|
|
8828
|
+
if (block && block.type === "tool") {
|
|
8829
|
+
block.result = result;
|
|
8830
|
+
block.isError = isError;
|
|
8831
|
+
block.completedAt = Date.now();
|
|
8832
|
+
saveSession(this.state);
|
|
8833
|
+
break;
|
|
8834
|
+
}
|
|
8835
|
+
}
|
|
8836
|
+
this.onEvent({
|
|
8837
|
+
type: "tool_done",
|
|
8801
8838
|
id,
|
|
8802
|
-
"compactConversation",
|
|
8803
|
-
|
|
8804
|
-
|
|
8839
|
+
name: "compactConversation",
|
|
8840
|
+
result,
|
|
8841
|
+
isError
|
|
8842
|
+
});
|
|
8805
8843
|
}
|
|
8806
8844
|
this.sessionStats.compactionInProgress = false;
|
|
8807
8845
|
if (!event.error) {
|