replicas-engine 0.1.96 → 0.1.98

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.
Files changed (2) hide show
  1. package/dist/src/index.js +56 -16
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -1091,7 +1091,7 @@ function parseReplicasConfigString(content, filename) {
1091
1091
  }
1092
1092
 
1093
1093
  // ../shared/src/engine/environment.ts
1094
- var DAYTONA_SNAPSHOT_ID = "12-04-2026-royal-york";
1094
+ var DAYTONA_SNAPSHOT_ID = "13-04-2026-phantom-subagent-fix";
1095
1095
 
1096
1096
  // ../shared/src/engine/types.ts
1097
1097
  var DEFAULT_CHAT_TITLES = {
@@ -2998,7 +2998,7 @@ var CodexManager = class extends CodingAgentManager {
2998
2998
  import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
2999
2999
  import { z } from "zod";
3000
3000
  var POLL_INTERVAL_MS = 2e3;
3001
- var SUBAGENT_TIMEOUT_MS = 6e5;
3001
+ var DEFAULT_SUBAGENT_TIMEOUT_MS = 6e5;
3002
3002
  async function engineFetch(path4, options) {
3003
3003
  const baseUrl = `http://localhost:${ENGINE_ENV.REPLICAS_ENGINE_PORT}`;
3004
3004
  return fetch(`${baseUrl}${path4}`, {
@@ -3010,28 +3010,38 @@ async function engineFetch(path4, options) {
3010
3010
  }
3011
3011
  });
3012
3012
  }
3013
- async function waitForChatCompletion(chatId) {
3013
+ async function waitForChatCompletion(chatId, timeoutMs = DEFAULT_SUBAGENT_TIMEOUT_MS) {
3014
3014
  const start = Date.now();
3015
3015
  await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
3016
- while (Date.now() - start < SUBAGENT_TIMEOUT_MS) {
3016
+ while (Date.now() - start < timeoutMs) {
3017
3017
  const res = await engineFetch(`/chats/${chatId}`);
3018
3018
  if (!res.ok) {
3019
3019
  throw new Error(`Failed to poll chat ${chatId}: ${res.status}`);
3020
3020
  }
3021
- const data = await res.json();
3021
+ let data;
3022
+ try {
3023
+ data = await res.json();
3024
+ } catch {
3025
+ throw new Error(`Failed to parse poll response for chat ${chatId} (chat may have been interrupted)`);
3026
+ }
3022
3027
  if (!data.chat.processing) {
3023
3028
  return;
3024
3029
  }
3025
3030
  await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
3026
3031
  }
3027
- throw new Error(`Subagent chat ${chatId} timed out after ${SUBAGENT_TIMEOUT_MS}ms`);
3032
+ throw new Error(`Subagent chat ${chatId} timed out after ${timeoutMs}ms`);
3028
3033
  }
3029
3034
  async function getChatFinalResponse(chatId) {
3030
3035
  const res = await engineFetch(`/chats/${chatId}/history`);
3031
3036
  if (!res.ok) {
3032
3037
  return "[Failed to retrieve subagent history]";
3033
3038
  }
3034
- const history = await res.json();
3039
+ let history;
3040
+ try {
3041
+ history = await res.json();
3042
+ } catch {
3043
+ return "[Failed to parse subagent history (response may have been interrupted)]";
3044
+ }
3035
3045
  const events = history.events || [];
3036
3046
  for (let i = events.length - 1; i >= 0; i--) {
3037
3047
  const event = events[i];
@@ -3083,7 +3093,8 @@ You will also receive the chatId so you can send follow-up messages or clean up
3083
3093
  provider: z.enum(["claude", "codex", "relay"]).describe("Which agent to use. Prefer codex for code writing, claude for exploration/analysis, relay for complex multi-step orchestration."),
3084
3094
  prompt: z.string().describe("The full prompt/instructions for the subagent. Be detailed - it has no context from your conversation."),
3085
3095
  model: z.string().optional().describe("Model override. Claude: opus, sonnet, haiku. Codex: gpt-5.4, gpt-5.3-codex, etc."),
3086
- title: z.string().optional().describe("Optional title for the subagent chat (for identification).")
3096
+ title: z.string().optional().describe("Optional title for the subagent chat (for identification)."),
3097
+ timeout_minutes: z.number().positive().optional().describe("Timeout in minutes for the subagent to complete (default: 10). Set higher for large tasks to avoid losing work.")
3087
3098
  },
3088
3099
  async (args) => {
3089
3100
  try {
@@ -3116,7 +3127,8 @@ You will also receive the chatId so you can send follow-up messages or clean up
3116
3127
  const err = await sendRes.text();
3117
3128
  return { content: [{ type: "text", text: `Failed to send message to subagent: ${err}` }], isError: true };
3118
3129
  }
3119
- await waitForChatCompletion(chatId);
3130
+ const timeoutMs = args.timeout_minutes ? args.timeout_minutes * 6e4 : DEFAULT_SUBAGENT_TIMEOUT_MS;
3131
+ await waitForChatCompletion(chatId, timeoutMs);
3120
3132
  const response = await getChatFinalResponse(chatId);
3121
3133
  return {
3122
3134
  content: [{
@@ -3142,7 +3154,8 @@ The tool blocks until the subagent completes and returns its response.`,
3142
3154
  {
3143
3155
  chatId: z.string().describe("The chat ID of the subagent (returned by spawn_agent)."),
3144
3156
  message: z.string().describe("The follow-up message to send."),
3145
- model: z.string().optional().describe("Optional model override for this message.")
3157
+ model: z.string().optional().describe("Optional model override for this message."),
3158
+ timeout_minutes: z.number().positive().optional().describe("Timeout in minutes for the subagent to complete (default: 10). Set higher for large tasks to avoid losing work.")
3146
3159
  },
3147
3160
  async (args) => {
3148
3161
  try {
@@ -3158,7 +3171,8 @@ The tool blocks until the subagent completes and returns its response.`,
3158
3171
  const err = await sendRes.text();
3159
3172
  return { content: [{ type: "text", text: `Failed to send message: ${err}` }], isError: true };
3160
3173
  }
3161
- await waitForChatCompletion(args.chatId);
3174
+ const timeoutMs = args.timeout_minutes ? args.timeout_minutes * 6e4 : DEFAULT_SUBAGENT_TIMEOUT_MS;
3175
+ await waitForChatCompletion(args.chatId, timeoutMs);
3162
3176
  const response = await getChatFinalResponse(args.chatId);
3163
3177
  return {
3164
3178
  content: [{
@@ -3305,15 +3319,41 @@ function getDelegationSection() {
3305
3319
 
3306
3320
  You have three subagent tools for spawning and managing agents that run in separate context windows:
3307
3321
 
3308
- - **spawn_agent**: Create a new subagent with a specific provider (claude or codex), send it a prompt, and wait for its response. Returns the chatId and the agent's final response.
3322
+ - **spawn_agent**: Create a new subagent with a specific provider (claude or codex), send it a prompt, and wait for its response. Returns the chatId and the agent's final response. You can set a custom timeout via the timeout_minutes parameter (default: 10 minutes).
3309
3323
  - **message_agent**: Send a follow-up message to an existing subagent by chatId. Use for iteration, corrections, or additional requests in the same context.
3310
3324
  - **delete_agent**: Delete a subagent chat when you no longer need it.
3311
3325
 
3312
- ## When to delegate vs work directly
3326
+ ## When to delegate vs work directly \u2014 the context efficiency rule
3327
+
3328
+ Your primary resource is your context window. Every tool call result, every code snippet, every debug log you read consumes context that you cannot reclaim. The key question when deciding whether to delegate is: **will doing this work myself cost more context than delegating it?**
3329
+
3330
+ **Do it directly** when the task is simple and context-light: a small edit, a quick search, running a single command, reading one file, answering a question from brief investigation. These cost little context and the overhead of explaining the task to a subagent would be greater.
3331
+
3332
+ **Delegate to a subagent** when the work would pollute your context with large amounts of intermediate output. Examples:
3333
+ - Testing and debugging: Running a server, curling endpoints, reading error output, iterating on fixes \u2014 this cycle consumes enormous context. Delegate the entire test-and-fix loop to a subagent.
3334
+ - Large code writing across multiple files.
3335
+ - Exploring a large codebase where you'd need to read many files.
3336
+ - Any iterative work (trial-and-error debugging, running tests repeatedly, build-fix cycles).
3337
+
3338
+ **The tradeoff**: Subagents start with zero context, so they may produce lower quality work if the task requires deep understanding of the project. Weigh this: is it more context-efficient for you to do the work directly (because explaining the full context to a subagent would be lengthy and the task itself is small), or is it more efficient to spend tokens on a thorough subagent prompt and keep your own context clean? When in doubt, if the work involves more than a few tool calls of iterative output (e.g., running commands and reacting to results), delegate it.
3339
+
3340
+ ## Parallel execution
3341
+
3342
+ You can spawn multiple subagents in parallel by making multiple spawn_agent tool calls in a single response. Do this whenever you have independent tasks \u2014 do NOT call them sequentially if they don't depend on each other. For example, if you need to test three endpoints, spawn three subagents in one response, not one after another.
3343
+
3344
+ ## Testing your work
3345
+
3346
+ After implementing changes, verify they work. Scale testing to the complexity of the task:
3347
+
3348
+ - **Small changes** (updating one endpoint, fixing a bug): A quick direct verification is sufficient.
3349
+ - **Medium changes** (new feature, multiple files): Spawn a subagent to run tests or verify the feature works end-to-end.
3350
+ - **Large/complex changes** (new system, many features, full application): Spawn multiple parallel subagents to test different aspects thoroughly. For example, test each endpoint or feature independently via separate subagents. Do not skip testing on complex work \u2014 missing features and broken functionality are worse than the token cost of verification.
3351
+
3352
+ Do not burn tokens for the sake of it. Match testing depth to the risk and complexity of the change.
3313
3353
 
3314
- Do it directly when the task is simple: a small edit, a quick search, running a single command, answering a question that needs brief investigation.
3354
+ ## Subagent timeouts
3315
3355
 
3316
- Spawn subagents when the task involves writing substantial code across multiple files, exploring a large codebase, running extensive tests, or any work that would consume significant context. Use subagents to parallelize independent pieces of work.
3356
+ The default subagent timeout is 10 minutes. For tasks you expect to take longer (large implementations, extensive test suites, complex debugging), set a generous timeout using the timeout_minutes parameter on spawn_agent. For example, a subagent building a full feature might need 20-30 minutes. If a subagent times out, you lose all of its work, so err on the side of generous timeouts for substantial tasks.
3317
3357
 
3318
3358
  ## Agent selection
3319
3359
 
@@ -3326,7 +3366,7 @@ Use provider 'claude' for codebase exploration, code review, planning, complex d
3326
3366
  - Subagents start with a blank context. Include all relevant file paths, code snippets, requirements, and constraints in the prompt.
3327
3367
  - Give each subagent a focused, specific task. Don't ask one agent to do everything.
3328
3368
  - After a subagent completes, review its output. Send follow-up messages for corrections, or verify changes with your direct tools.
3329
- - Delete subagent chats with delete_agent when done to free resources.
3369
+ - Do NOT proactively delete subagent chats. Only use delete_agent when the user explicitly asks you to clean up or delete agents. Keeping subagents around allows the user to inspect their work and allows you to send follow-up messages if needed.
3330
3370
  - Use provider 'relay' for complex multi-step tasks that themselves benefit from orchestration and delegation.
3331
3371
  - Do not over-delegate. Spawning an agent for a trivial edit wastes time.`;
3332
3372
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.96",
3
+ "version": "0.1.98",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",