@mindstudio-ai/remy 0.1.137 → 0.1.139

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.
@@ -29,4 +29,4 @@ The initial build prioritizes getting everything connected and functional, but t
29
29
 
30
30
  Then, ask the `visualDesignExpert` to take a screenshot and verity that the visual design looks correct. Fix any issues it flags - we want the user's first time seeing the finished product to truly wow them.
31
31
 
32
- When everything is working, use `productVision` to mark the MVP roadmap item as done, then call `setProjectOnboardingState({ state: "onboardingFinished" })`.
32
+ When everything is working, use `productVision` to mark the MVP roadmap item as done, then call `setProjectOnboardingState({ state: "onboardingFinished" })`. Finally, call `compactConversation` to summarize the build session and free up context for the next phase of work.
@@ -12,4 +12,4 @@ Then, put together a plan to build out the feature. Present the plan to the user
12
12
 
13
13
  When they've approved the plan, be sure to update the spec first - remember, the spec is the source of truth about the product. Then, build everything in one turn, using the spec as the master plan.
14
14
 
15
- When you're finished, verify your work, then tell`productVision` what was done so it can update the roadmap to reflect the progress. Give the user a summary of what was done.
15
+ When you're finished, verify your work, then tell `productVision` what was done so it can update the roadmap to reflect the progress. Give the user a summary of what was done, then call `compactConversation` to summarize the build session and free up context.
package/dist/headless.js CHANGED
@@ -1739,6 +1739,29 @@ var setProjectMetadataTool = {
1739
1739
  }
1740
1740
  };
1741
1741
 
1742
+ // src/tools/common/compactConversation.ts
1743
+ var compactConversationTool = {
1744
+ clearable: false,
1745
+ definition: {
1746
+ name: "compactConversation",
1747
+ description: "Compact the conversation history by summarizing older messages into a checkpoint. The summary preserves key decisions, what was built, and the current state of the project, but drops the verbose tool results, diffs, and intermediate steps that are no longer useful. Use this when you have just finished a large block of mechanical work (building, refactoring, debugging) and are about to shift back into conversational mode with the user. Runs in the background. Do not use after small changes like fixing a bug or editing copy.",
1748
+ inputSchema: {
1749
+ type: "object",
1750
+ properties: {}
1751
+ }
1752
+ },
1753
+ async execute(_input, context) {
1754
+ if (!context?.conversationMessages || !context.apiConfig) {
1755
+ return "Error: compaction requires execution context.";
1756
+ }
1757
+ triggerCompaction(
1758
+ { messages: context.conversationMessages },
1759
+ context.apiConfig
1760
+ );
1761
+ return "Compaction started in the background.";
1762
+ }
1763
+ };
1764
+
1742
1765
  // src/tools/code/readFile.ts
1743
1766
  import fs9 from "fs/promises";
1744
1767
  var DEFAULT_MAX_LINES2 = 500;
@@ -2842,8 +2865,10 @@ async function runSubAgent(config) {
2842
2865
  requestId,
2843
2866
  history,
2844
2867
  background,
2845
- onBackgroundComplete
2868
+ onBackgroundComplete,
2869
+ captureArtifacts
2846
2870
  } = config;
2871
+ const artifacts = {};
2847
2872
  const bgAbort = background ? new AbortController() : null;
2848
2873
  const signal = background ? bgAbort.signal : parentSignal;
2849
2874
  const agentName = subAgentId || "sub-agent";
@@ -3015,7 +3040,12 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
3015
3040
  if (stopReason !== "tool_use" || toolCalls.length === 0) {
3016
3041
  statusWatcher.stop();
3017
3042
  const text = getPartialText(contentBlocks);
3018
- return { text, messages: thisInvocation() };
3043
+ const hasArtifacts = Object.keys(artifacts).length > 0;
3044
+ return {
3045
+ text,
3046
+ messages: thisInvocation(),
3047
+ ...hasArtifacts ? { artifacts } : {}
3048
+ };
3019
3049
  }
3020
3050
  log5.info("Tools executing", {
3021
3051
  requestId,
@@ -3126,6 +3156,12 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
3126
3156
  if (innerMsgs) {
3127
3157
  block.subAgentMessages = innerMsgs;
3128
3158
  }
3159
+ if (captureArtifacts?.includes(block.name) && !r.isError) {
3160
+ try {
3161
+ artifacts[block.name] = JSON.parse(r.result);
3162
+ } catch {
3163
+ }
3164
+ }
3129
3165
  }
3130
3166
  messages.push({
3131
3167
  role: "user",
@@ -3457,13 +3493,22 @@ var browserAutomationTool = {
3457
3493
  }
3458
3494
  return result2;
3459
3495
  },
3460
- toolRegistry: context.toolRegistry
3496
+ toolRegistry: context.toolRegistry,
3497
+ captureArtifacts: ["screenshotFullPage"]
3461
3498
  });
3462
3499
  try {
3463
3500
  await sidecarRequest("/reset-browser", {}, { timeout: 5e3 });
3464
3501
  } catch {
3465
3502
  }
3466
3503
  context.subAgentMessages?.set(context.toolCallId, result.messages);
3504
+ const ss = result.artifacts?.screenshotFullPage;
3505
+ if (ss?.url) {
3506
+ return JSON.stringify({
3507
+ text: result.text,
3508
+ screenshotUrl: ss.url,
3509
+ ...ss.styleMap ? { styleMap: ss.styleMap } : {}
3510
+ });
3511
+ }
3467
3512
  return result.text;
3468
3513
  } finally {
3469
3514
  release();
@@ -3511,19 +3556,18 @@ var screenshotTool = {
3511
3556
  if (input.instructions && context) {
3512
3557
  const task = input.path ? `Navigate to "${input.path}", then: ${input.instructions}. After completing these steps, take a full-page screenshot.` : `${input.instructions}. After completing these steps, take a full-page screenshot.`;
3513
3558
  const result = await browserAutomationTool.execute({ task }, context);
3514
- const urlMatch = result.match(
3515
- /https:\/\/[^\s"')]+\.(?:png|jpg|jpeg|webp)/i
3516
- );
3517
- if (!urlMatch) {
3518
- return `Error: browser navigation completed but no screenshot URL was returned. Agent output: ${result}`;
3519
- }
3520
- const url = urlMatch[0];
3559
+ const resultStr = result;
3560
+ let url;
3521
3561
  let styleMap;
3522
3562
  try {
3523
- const parsed = JSON.parse(result);
3524
- styleMap = parsed?.styleMap;
3563
+ const parsed = JSON.parse(resultStr);
3564
+ url = parsed.screenshotUrl;
3565
+ styleMap = parsed.styleMap;
3525
3566
  } catch {
3526
3567
  }
3568
+ if (!url) {
3569
+ return `Error: browser navigation completed but no screenshot URL was returned. Agent output: ${resultStr}`;
3570
+ }
3527
3571
  const analysisPrompt = buildScreenshotAnalysisPrompt({
3528
3572
  prompt: input.prompt,
3529
3573
  styleMap
@@ -3847,19 +3891,18 @@ async function execute5(input, onLog, context) {
3847
3891
  try {
3848
3892
  const task = input.path ? `Navigate to "${input.path}", then: ${input.instructions}. After completing these steps, take a full-page screenshot.` : `${input.instructions}. After completing these steps, take a full-page screenshot.`;
3849
3893
  const result = await browserAutomationTool.execute({ task }, context);
3850
- const urlMatch = result.match(
3851
- /https:\/\/[^\s"')]+\.(?:png|jpg|jpeg|webp)/i
3852
- );
3853
- if (!urlMatch) {
3854
- return `Error: browser navigation completed but no screenshot URL was returned. Agent output: ${result}`;
3855
- }
3856
- const url = urlMatch[0];
3894
+ const resultStr = result;
3895
+ let url;
3857
3896
  let styleMap;
3858
3897
  try {
3859
- const parsed = JSON.parse(result);
3860
- styleMap = parsed?.styleMap;
3898
+ const parsed = JSON.parse(resultStr);
3899
+ url = parsed.screenshotUrl;
3900
+ styleMap = parsed.styleMap;
3861
3901
  } catch {
3862
3902
  }
3903
+ if (!url) {
3904
+ return `Error: browser navigation completed but no screenshot URL was returned. Agent output: ${resultStr}`;
3905
+ }
3863
3906
  const analysisPrompt = buildScreenshotAnalysisPrompt({
3864
3907
  prompt: input.prompt,
3865
3908
  styleMap
@@ -4997,6 +5040,7 @@ var ALL_TOOLS = [
4997
5040
  designExpertTool,
4998
5041
  productVisionTool,
4999
5042
  codeSanityCheckTool,
5043
+ compactConversationTool,
5000
5044
  // Post-onboarding
5001
5045
  clearSyncStatusTool,
5002
5046
  presentSyncPlanTool,
@@ -5115,6 +5159,24 @@ function clearSession(state) {
5115
5159
  }
5116
5160
  }
5117
5161
 
5162
+ // src/compaction/trigger.ts
5163
+ var log8 = createLogger("compaction:trigger");
5164
+ function triggerCompaction(state, apiConfig, callbacks) {
5165
+ callbacks?.onStart?.();
5166
+ const system = buildSystemPrompt("onboardingFinished");
5167
+ const tools2 = getToolDefinitions("onboardingFinished");
5168
+ compactConversation(state, apiConfig, system, tools2).then(() => {
5169
+ saveSession(state);
5170
+ callbacks?.onComplete?.();
5171
+ log8.info("Compaction complete");
5172
+ }).catch((err) => {
5173
+ callbacks?.onError?.(err.message || "Compaction failed");
5174
+ log8.error("Compaction failed", { error: err.message });
5175
+ }).finally(() => {
5176
+ callbacks?.onFinally?.();
5177
+ });
5178
+ }
5179
+
5118
5180
  // src/parsePartialJson.ts
5119
5181
  var PartialJSON = class extends Error {
5120
5182
  };
@@ -5305,7 +5367,7 @@ function friendlyError(raw) {
5305
5367
  }
5306
5368
 
5307
5369
  // src/agent.ts
5308
- var log8 = createLogger("agent");
5370
+ var log9 = createLogger("agent");
5309
5371
  function getTextContent(blocks) {
5310
5372
  return blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
5311
5373
  }
@@ -5350,7 +5412,7 @@ async function runTurn(params) {
5350
5412
  } = params;
5351
5413
  const tools2 = getToolDefinitions(onboardingState);
5352
5414
  const excludeToolsFromClearing = tools2.filter((t) => !CLEARABLE_TOOLS.has(t.name)).map((t) => t.name);
5353
- log8.info("Turn started", {
5415
+ log9.info("Turn started", {
5354
5416
  requestId,
5355
5417
  model,
5356
5418
  toolCount: tools2.length,
@@ -5574,7 +5636,7 @@ async function runTurn(params) {
5574
5636
  const tool = getToolByName(event.name);
5575
5637
  const wasStreamed = acc?.started ?? false;
5576
5638
  const isInputStreaming = !!tool?.streaming?.partialInput;
5577
- log8.info("Tool received", {
5639
+ log9.info("Tool received", {
5578
5640
  requestId,
5579
5641
  toolCallId: event.id,
5580
5642
  name: event.name
@@ -5621,7 +5683,14 @@ async function runTurn(params) {
5621
5683
  });
5622
5684
  state.messages.push({
5623
5685
  role: "assistant",
5624
- content: [...contentBlocks].sort((a, b) => a.startedAt - b.startedAt)
5686
+ content: [...contentBlocks].sort((a, b) => a.startedAt - b.startedAt),
5687
+ usage: {
5688
+ inputTokens: turnInputTokens,
5689
+ outputTokens: turnOutputTokens,
5690
+ cacheCreationTokens: turnCacheCreation || void 0,
5691
+ cacheReadTokens: turnCacheRead || void 0,
5692
+ llmCalls: turnLlmCalls
5693
+ }
5625
5694
  });
5626
5695
  }
5627
5696
  onEvent({ type: "turn_cancelled" });
@@ -5631,7 +5700,14 @@ async function runTurn(params) {
5631
5700
  if (contentBlocks.length > 0) {
5632
5701
  state.messages.push({
5633
5702
  role: "assistant",
5634
- content: [...contentBlocks].sort((a, b) => a.startedAt - b.startedAt)
5703
+ content: [...contentBlocks].sort((a, b) => a.startedAt - b.startedAt),
5704
+ usage: {
5705
+ inputTokens: turnInputTokens,
5706
+ outputTokens: turnOutputTokens,
5707
+ cacheCreationTokens: turnCacheCreation || void 0,
5708
+ cacheReadTokens: turnCacheRead || void 0,
5709
+ llmCalls: turnLlmCalls
5710
+ }
5635
5711
  });
5636
5712
  }
5637
5713
  const toolCalls = getToolCalls(contentBlocks);
@@ -5653,7 +5729,7 @@ async function runTurn(params) {
5653
5729
  });
5654
5730
  return;
5655
5731
  }
5656
- log8.info("Tools executing", {
5732
+ log9.info("Tools executing", {
5657
5733
  requestId,
5658
5734
  count: toolCalls.length,
5659
5735
  tools: toolCalls.map((tc) => tc.name)
@@ -5700,7 +5776,7 @@ async function runTurn(params) {
5700
5776
  let result;
5701
5777
  if (EXTERNAL_TOOLS.has(tc.name) && resolveExternalTool) {
5702
5778
  saveSession(state);
5703
- log8.info("Waiting for external tool result", {
5779
+ log9.info("Waiting for external tool result", {
5704
5780
  requestId,
5705
5781
  toolCallId: tc.id,
5706
5782
  name: tc.name
@@ -5757,7 +5833,7 @@ async function runTurn(params) {
5757
5833
  if (!tc.input.background) {
5758
5834
  toolRegistry?.unregister(tc.id);
5759
5835
  }
5760
- log8.info("Tool completed", {
5836
+ log9.info("Tool completed", {
5761
5837
  requestId,
5762
5838
  toolCallId: tc.id,
5763
5839
  name: tc.name,
@@ -5812,7 +5888,7 @@ async function runTurn(params) {
5812
5888
  }
5813
5889
 
5814
5890
  // src/toolRegistry.ts
5815
- var log9 = createLogger("tool-registry");
5891
+ var log10 = createLogger("tool-registry");
5816
5892
  var ToolRegistry = class {
5817
5893
  entries = /* @__PURE__ */ new Map();
5818
5894
  onEvent;
@@ -5838,7 +5914,7 @@ var ToolRegistry = class {
5838
5914
  if (!entry) {
5839
5915
  return false;
5840
5916
  }
5841
- log9.info("Tool stopped", { toolCallId: id, name: entry.name, mode });
5917
+ log10.info("Tool stopped", { toolCallId: id, name: entry.name, mode });
5842
5918
  entry.abortController.abort(mode);
5843
5919
  if (mode === "graceful") {
5844
5920
  const partial = entry.getPartialResult?.() ?? "";
@@ -5871,7 +5947,7 @@ ${partial}` : "[INTERRUPTED] Tool execution was stopped.";
5871
5947
  if (!entry) {
5872
5948
  return false;
5873
5949
  }
5874
- log9.info("Tool restarted", { toolCallId: id, name: entry.name });
5950
+ log10.info("Tool restarted", { toolCallId: id, name: entry.name });
5875
5951
  entry.abortController.abort("restart");
5876
5952
  const newInput = patchedInput ? { ...entry.input, ...patchedInput } : entry.input;
5877
5953
  this.onEvent?.({
@@ -5916,7 +5992,7 @@ ${body}`;
5916
5992
  }
5917
5993
 
5918
5994
  // src/headless.ts
5919
- var log10 = createLogger("headless");
5995
+ var log11 = createLogger("headless");
5920
5996
  function emit(event, data, requestId) {
5921
5997
  const payload = { event, ...data };
5922
5998
  if (requestId) {
@@ -6032,7 +6108,7 @@ ${xmlParts}
6032
6108
  }
6033
6109
  function onBackgroundComplete(toolCallId, name, result, subAgentMessages) {
6034
6110
  pendingBlockUpdates.push({ toolCallId, result, subAgentMessages });
6035
- log10.info("Background complete", {
6111
+ log11.info("Background complete", {
6036
6112
  toolCallId,
6037
6113
  name,
6038
6114
  requestId: currentRequestId
@@ -6304,7 +6380,7 @@ ${xmlParts}
6304
6380
  requestId
6305
6381
  );
6306
6382
  }
6307
- log10.info("Turn complete", {
6383
+ log11.info("Turn complete", {
6308
6384
  requestId,
6309
6385
  durationMs: Date.now() - turnStart
6310
6386
  });
@@ -6313,7 +6389,7 @@ ${xmlParts}
6313
6389
  emit("error", { error: err.message }, requestId);
6314
6390
  emit("completed", { success: false, error: err.message }, requestId);
6315
6391
  }
6316
- log10.warn("Command failed", {
6392
+ log11.warn("Command failed", {
6317
6393
  action: "message",
6318
6394
  requestId,
6319
6395
  error: err.message
@@ -6333,7 +6409,7 @@ ${xmlParts}
6333
6409
  return;
6334
6410
  }
6335
6411
  const { action, requestId } = parsed;
6336
- log10.info("Command received", { action, requestId });
6412
+ log11.info("Command received", { action, requestId });
6337
6413
  if (action === "tool_result" && parsed.id) {
6338
6414
  const id = parsed.id;
6339
6415
  const result = parsed.result ?? "";
@@ -6342,7 +6418,7 @@ ${xmlParts}
6342
6418
  pendingTools.delete(id);
6343
6419
  pending.resolve(result);
6344
6420
  } else if (!running) {
6345
- log10.info("Late tool_result while idle, dismissing", { id });
6421
+ log11.info("Late tool_result while idle, dismissing", { id });
6346
6422
  emit("completed", { success: true }, requestId);
6347
6423
  } else {
6348
6424
  earlyResults.set(id, result);
@@ -6398,36 +6474,31 @@ ${xmlParts}
6398
6474
  return;
6399
6475
  }
6400
6476
  if (action === "compact") {
6401
- sessionStats.compactionInProgress = true;
6402
- sessionStats.updatedAt = Date.now();
6403
- try {
6404
- writeFileSync(".remy-stats.json", JSON.stringify(sessionStats));
6405
- } catch {
6406
- }
6407
- const compactSystem = buildSystemPrompt("onboardingFinished");
6408
- const compactTools = getToolDefinitions("onboardingFinished");
6409
- compactConversation(state, config, compactSystem, compactTools).then(() => {
6410
- saveSession(state);
6411
- emit("compaction_complete", {}, requestId);
6412
- emit("completed", { success: true }, requestId);
6413
- }).catch((err) => {
6414
- emit(
6415
- "compaction_complete",
6416
- { error: err.message || "Compaction failed" },
6417
- requestId
6418
- );
6419
- emit(
6420
- "completed",
6421
- { success: false, error: err.message || "Compaction failed" },
6422
- requestId
6423
- );
6424
- }).finally(() => {
6425
- sessionStats.compactionInProgress = false;
6426
- sessionStats.messageCount = state.messages.length;
6427
- sessionStats.updatedAt = Date.now();
6428
- try {
6429
- writeFileSync(".remy-stats.json", JSON.stringify(sessionStats));
6430
- } catch {
6477
+ triggerCompaction(state, config, {
6478
+ onStart: () => {
6479
+ sessionStats.compactionInProgress = true;
6480
+ sessionStats.updatedAt = Date.now();
6481
+ try {
6482
+ writeFileSync(".remy-stats.json", JSON.stringify(sessionStats));
6483
+ } catch {
6484
+ }
6485
+ },
6486
+ onComplete: () => {
6487
+ emit("compaction_complete", {}, requestId);
6488
+ emit("completed", { success: true }, requestId);
6489
+ },
6490
+ onError: (error) => {
6491
+ emit("compaction_complete", { error }, requestId);
6492
+ emit("completed", { success: false, error }, requestId);
6493
+ },
6494
+ onFinally: () => {
6495
+ sessionStats.compactionInProgress = false;
6496
+ sessionStats.messageCount = state.messages.length;
6497
+ sessionStats.updatedAt = Date.now();
6498
+ try {
6499
+ writeFileSync(".remy-stats.json", JSON.stringify(sessionStats));
6500
+ } catch {
6501
+ }
6431
6502
  }
6432
6503
  });
6433
6504
  return;