replicas-mcp 0.2.20 → 0.2.21

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 (3) hide show
  1. package/dist/index.js +359 -320
  2. package/package.json +1 -1
  3. package/src/index.ts +1 -8
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/index.ts
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
- import { z as z10 } from "zod";
6
+ import { z as z9 } from "zod";
7
7
 
8
8
  // ../shared/src/agent.ts
9
9
  var AGENT = {
@@ -19,7 +19,6 @@ var SUBAGENT_MCP_HARNESSES = [AGENT.CLAUDE, AGENT.CODEX];
19
19
  function isValidAgentProvider(value) {
20
20
  return VALID_AGENT_PROVIDERS.some((p) => p === value);
21
21
  }
22
- var LEGACY_RELAY_BASE_PROVIDERS = [AGENT.CLAUDE, AGENT.CODEX, AGENT.OPENCODE, AGENT.PI];
23
22
  var VALID_THINKING_LEVELS = ["low", "medium", "high", "xhigh", "max", "ultra", "ultracode"];
24
23
  var STANDARD_THINKING_LEVELS = ["low", "medium", "high", "xhigh", "max"];
25
24
  var THINKING_LEVELS_BY_AGENT = {
@@ -30,9 +29,6 @@ var THINKING_LEVELS_BY_AGENT = {
30
29
  opencode: STANDARD_THINKING_LEVELS,
31
30
  pi: STANDARD_THINKING_LEVELS
32
31
  };
33
- function isValidThinkingLevel(value) {
34
- return VALID_THINKING_LEVELS.some((l) => l === value);
35
- }
36
32
  function getProviderDisplayName(provider, variant = "short") {
37
33
  switch (provider) {
38
34
  case "codex":
@@ -99,6 +95,13 @@ var CREDENTIAL_TYPE = {
99
95
  MUSE_OAUTH: "muse_oauth",
100
96
  MUSE_API_KEY: "muse_api_key"
101
97
  };
98
+ var ENVIRONMENT_AUTH_REQUIRED_KEYS = {
99
+ [AGENT.CLAUDE]: [
100
+ [CREDENTIAL_METHOD.BEDROCK, ["CLAUDE_CODE_USE_BEDROCK", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]],
101
+ [CREDENTIAL_METHOD.API_KEY, ["ANTHROPIC_API_KEY"]]
102
+ ],
103
+ [AGENT.CODEX]: [[CREDENTIAL_METHOD.API_KEY, ["OPENAI_API_KEY"]]]
104
+ };
102
105
  var INFERENCE_PROVIDER_LABELS = {
103
106
  [CREDENTIAL_METHOD.OLLAMA]: "Ollama",
104
107
  [CREDENTIAL_METHOD.OPENCODE_GO]: "OpenCode Go",
@@ -230,13 +233,17 @@ var ENTERPRISE_PLAN = PLANS.enterprise;
230
233
  // ../shared/src/analytics/types.ts
231
234
  var credentialAccountSchema = z.object({
232
235
  ownerName: z.string().optional(),
233
- email: z.string().optional()
236
+ email: z.string().optional(),
237
+ /** 1-based place in the owner's account pool, set only when the pool holds several accounts. */
238
+ ordinal: z.number().int().positive().optional()
234
239
  });
235
240
  var agentCredentialSnapshotSchema = z.object({
236
241
  method: z.enum(CREDENTIAL_METHOD),
237
242
  scope: z.enum(CREDENTIAL_SCOPE),
238
243
  revision: z.string().optional(),
239
- account: credentialAccountSchema.optional()
244
+ account: credentialAccountSchema.optional(),
245
+ /** The `credentials` row, which tells apart accounts pooled under one method and scope. */
246
+ credentialId: z.string().optional()
240
247
  });
241
248
 
242
249
  // ../shared/src/event.ts
@@ -267,6 +274,7 @@ var CODEX_ITEM_EVENT_TYPE = "codex-item";
267
274
  var CODEX_ITEM_PARTIAL_EVENT_TYPE = "codex-item-partial";
268
275
  var CODEX_TURN_EVENT_TYPE = "codex-turn";
269
276
  var CODEX_QUOTA_STATUS_EVENT_TYPE = "codex-quota-status";
277
+ var CHAT_INTERRUPTED_EVENT_TYPE = "replicas-interrupted";
270
278
 
271
279
  // ../shared/src/urls.ts
272
280
  var DASHBOARD_PATH = "/dashboard";
@@ -286,6 +294,7 @@ var WEB_APP_PATHS = {
286
294
  settings: `${DASHBOARD_PATH}/settings`,
287
295
  billing: `${DASHBOARD_PATH}/billing`,
288
296
  members: `${DASHBOARD_PATH}/members`,
297
+ learnings: `${DASHBOARD_PATH}/learnings`,
289
298
  "audit-logs": `${DASHBOARD_PATH}/audit-logs`,
290
299
  accountAgents: `${DASHBOARD_PATH}/account/agents`,
291
300
  accountProfile: `${DASHBOARD_PATH}/account/profile`,
@@ -330,14 +339,21 @@ var automationTriggerSchema = z2.discriminatedUnion("type", [
330
339
  ]);
331
340
 
332
341
  // ../shared/src/replicas-mcp.ts
333
- var REPLICAS_MCP_SERVER_NAME = "replicas-mcp";
334
342
  var REPLICAS_MCP_TOOLS = {
335
343
  listEnvironments: "list_environments",
336
344
  createAutomation: "create_automation",
337
345
  spawn: "spawn_workspace",
338
346
  relay: "relay_to_workspace",
339
347
  linearStart: "start_linear_session",
340
- linearSessionIntent: "register_linear_session_intent"
348
+ linearSessionIntent: "register_linear_session_intent",
349
+ listChannelSettings: "list_channel_settings",
350
+ saveChannelPreset: "save_channel_preset",
351
+ updateChannelSettings: "update_channel_settings",
352
+ spawnSubagent: "spawn_subagent",
353
+ messageSubagent: "message_subagent",
354
+ listSubagents: "list_subagents",
355
+ readSubagent: "read_subagent",
356
+ archiveSubagent: "archive_subagent"
341
357
  };
342
358
  function environmentToolSummary({ id, name, description, is_global, repository_id, repository_set_id }) {
343
359
  return { id, name, description, is_global, repository_id, repository_set_id };
@@ -804,10 +820,11 @@ Explore a codebase, get it running in this workspace, verify it end to end, and
804
820
  Use this when:
805
821
  - This workspace was started as an environment setup run (\`replicas environment-setup status\` succeeds)
806
822
  - The user asks you to set up, configure, audit, or improve a Replicas environment for a repository
807
- - The user asks what should go in a start hook, warm hook, or environment system prompt`;
823
+ - The user asks what should go in a start hook, warm hook, or environment system prompt
824
+ - A warm pool, warm hook, or start hook is failing, or the fix you found belongs in the environment rather than the repository`;
808
825
  var REFERENCE3 = `# Environment setup (agent-led)
809
826
 
810
- Use this workflow to make a repository fully usable by Replicas background agents: agents booted from the environment should be able to install, run, test, verify, and demo changes without a human babysitting them. The result of the workflow is a **proposal**, never a direct change: the user reviews, edits, and saves it in the Environment panel next to the chat.
827
+ Use this workflow to make a repository fully usable by Replicas background agents: agents booted from the environment should be able to install, run, test, verify, and demo changes without a human babysitting them. In a setup run, the result is a **proposal**, never a direct change: the user reviews, edits, and saves it in the Environment panel next to the chat. Outside a setup run, see below.
811
828
 
812
829
  ## Mental model
813
830
 
@@ -839,7 +856,14 @@ replicas environment-setup wait-variables [-t 5m]
839
856
 
840
857
  \`status\` prints the environment, repositories, current steps, and variable requests. \`request-variables\` returns immediately; \`wait-variables\` blocks until the required values arrive, are skipped, or the time runs out. Step statuses are \`pending\`, \`in_progress\`, \`completed\`, and \`skipped\`. Step keys, in order: \`understand\`, \`install\`, \`verify\`, \`configure\`, \`propose\`. Mark the first step \`in_progress\` before your first tool call, keep exactly one step \`in_progress\`, and mark a step \`completed\` only after its work actually succeeded. Use \`skipped\` when something genuinely cannot run (for example no browser UI to record) and say so in the proposal.
841
858
 
842
- If \`replicas environment-setup status\` reports that there is no active setup run, the user asked for this in an ordinary workspace. Follow the same discovery and verification steps, then present the configuration in chat; apply it with \`replicas environment ...\` and \`replicas automation ...\` only when the user explicitly asks you to.
859
+ If \`replicas environment-setup status\` reports that there is no active setup run, you are in an ordinary workspace, either because the user asked for this or because the fix you found while debugging belongs in the environment. Follow the same discovery and verification steps, show the exact change in chat, and offer to apply and test it yourself. Ask the user before changing the environment, since it affects every workspace built from it. Once they approve:
860
+
861
+ - Warm hook: \`replicas environment warm-hooks save-test <env> -f <file>\` runs it in an isolated sandbox and saves it only if it passes.
862
+ - Start hook: \`replicas environment start-hooks test <env> -f <file>\`, then \`replicas environment start-hooks save <env> -f <file>\` with the same file once it passes.
863
+ - Variables and files: apply with \`replicas environment vars set\` or \`files set\`, then confirm with \`vars list\` or \`files list\`.
864
+ - Automations: apply with \`replicas automation ...\`, then read back with \`replicas automation get <id>\`.
865
+
866
+ When a test fails, fix the earliest failure and test again instead of saving. If the fix materially changes what the user approved, show the revised change and ask again before saving. Report what you changed and the test result.
843
867
 
844
868
  ## 1. Understand the codebase
845
869
 
@@ -913,7 +937,7 @@ replicas computer record stop
913
937
  replicas media upload /tmp/walkthrough.mp4 --name "<Project> walkthrough"
914
938
  \`\`\`
915
939
 
916
- Keep the recording short (one to three minutes) and focused on the flow that proves the environment works. \`replicas media upload\` prints a chat embed line and a \`View in Replicas\` link ending in \`?media=<media-id>\`; keep both. The media id goes into the proposal, and the embed line goes into your final message. Follow \`MEDIA.md\` for anything shared outside the dashboard.
940
+ Keep the recording short (one to three minutes) and focused on the flow that proves the environment works. \`replicas media upload\` prints a chat embed line and a \`View in Replicas\` link ending in \`?mode=media&media=<media-id>\`; keep both. The media id goes into the proposal, and the embed line goes into your final message. Follow \`MEDIA.md\` for anything shared outside the dashboard.
917
941
 
918
942
  Add screenshots of important states (a passing test run, the running app) with \`replicas computer screenshot\` and upload them the same way when they help the user trust the result.
919
943
 
@@ -1031,7 +1055,7 @@ After \`propose\` succeeds, mark the \`propose\` step completed and send one mes
1031
1055
  Review the start hook, warm hook, system prompt, variables, and automations in the Environment panel on the right. Edit anything inline, then select **Save configuration**. Ask me here if you want changes and I will revise the proposal.
1032
1056
  \`\`\`
1033
1057
 
1034
- Keep it under 250 words plus the lists. Do not paste the hooks or system prompt into chat; the panel shows them. Never imply the configuration was saved: saving is the user's action.
1058
+ Keep it under 250 words plus the lists. Do not paste the hooks or system prompt into chat; the panel shows them. Never imply the configuration was saved: in a setup run, saving is the user's action.
1035
1059
 
1036
1060
  ## Safety
1037
1061
 
@@ -1043,7 +1067,7 @@ Keep it under 250 words plus the lists. Do not paste the hooks or system prompt
1043
1067
  var ENVIRONMENT_SETUP_ABILITY = {
1044
1068
  label: "Environment setup",
1045
1069
  description: "Explore a codebase, verify it runs, and propose environment hooks, a system prompt, variables, and automations for review.",
1046
- bullet: "- Setting up, auditing, or improving a Replicas environment for a repository (hooks, system prompt, variables, automations)",
1070
+ bullet: "- Setting up, auditing, fixing, or improving a Replicas environment for a repository (hooks, system prompt, variables, automations), including a failing warm pool or hook",
1047
1071
  section: SECTION3,
1048
1072
  referenceFile: { name: "ENVIRONMENT-SETUP.md", content: REFERENCE3 }
1049
1073
  };
@@ -1287,7 +1311,9 @@ if (result.errors?.length) throw new Error(JSON.stringify(result.errors));
1287
1311
 
1288
1312
  The gateway refreshes expired credentials and retries authentication failures once. Check the response's \`errors\` field before using \`data\`.
1289
1313
 
1290
- Personal \u2192 Connections links a person's Linear identity for agent defaults and attribution. Its disconnected state does not mean the organization's Linear integration is disconnected, and connecting it does not refresh the organization's token. Only recommend reconnecting under Organization \u2192 Integrations when the gateway reports missing credentials or an unrecoverable OAuth failure. Report transient failures without asking the user to disconnect.
1314
+ Initiatives need the \`initiative:read\` scope, which organizations connected before it was requested do not have. If Linear answers \`Invalid scope\`, ask an admin to reconnect Linear under Organization \u2192 Integrations; retrying will not help.
1315
+
1316
+ Personal \u2192 Connections links a person's Linear identity for agent defaults and attribution. Its disconnected state does not mean the organization's Linear integration is disconnected, and connecting it does not refresh the organization's token. Only recommend reconnecting under Organization \u2192 Integrations when the gateway reports missing credentials or an unrecoverable OAuth failure, or when Linear answers \`Invalid scope\`. Report transient failures without asking the user to disconnect.
1291
1317
 
1292
1318
  ## Create a ticket
1293
1319
 
@@ -1437,7 +1463,7 @@ For each media file, the CLI prints an embed line and a dashboard link:
1437
1463
 
1438
1464
  1. Images always print \`![filename](<public-url>)\`. This public, stable link works in Replicas chat, GitHub, and anywhere else Markdown images render. No flag is needed.
1439
1465
  2. Video and audio print \`Replicas chat embed: ![filename](<api-url>)\` by default; this authenticated URL renders inline in Replicas chat **only** \u2014 see below. \`--access public\` prints a public embed instead.
1440
- 3. \`View in Replicas: <deep-link>\` is a per-file dashboard URL of the form \`https://app.replicas.dev/workspaces/<workspace-id>?media=<media-id>\` that opens directly to that specific file.
1466
+ 3. \`View in Replicas: <deep-link>\` is a per-file dashboard URL of the form \`https://app.replicas.dev/workspaces/<workspace-id>?mode=media&media=<media-id>\` that opens directly to that specific file.
1441
1467
 
1442
1468
  Match each "View in Replicas" line to the embed line directly above it \u2014 that's the deep link for that file.
1443
1469
 
@@ -1468,7 +1494,7 @@ Permanently delete an upload with \`replicas media delete <media-id>\`. The back
1468
1494
  | \`https://api.replicas.dev/v1/media/<id>\` (inside \`![\u2026](\u2026)\`) | Authenticated API redirect printed for private video, audio, and HTML \u2014 requires the Replicas chat session | **Only** in the Replicas chat reply. Nowhere else. |
1469
1495
  | \`https://replicas.dev/organizations/<organization-id>/media/<media-id>\` | Organization-only page printed by \`--access organization\` | Pull requests and other places where every viewer belongs to the Replicas organization. |
1470
1496
  | \`https://api.replicas.dev/v1/media/public/<id>/<token>\` | Public bearer URL printed for every image, and for video or audio with \`--access public\` | The Replicas chat reply and GitHub embeds. |
1471
- | \`https://app.replicas.dev/workspaces/<workspace-id>?media=<media-id>\` | Authenticated dashboard deep link that opens the media file in the workspace | Everywhere external: Slack, GitHub, GitLab, Linear, PR bodies, comments, emails. |
1497
+ | \`https://app.replicas.dev/workspaces/<workspace-id>?mode=media&media=<media-id>\` | Authenticated dashboard deep link that opens the media file in the workspace | Everywhere external: Slack, GitHub, GitLab, Linear, PR bodies, comments, emails. |
1472
1498
 
1473
1499
  ### NEVER link the API redirect URL outside the Replicas chat reply
1474
1500
 
@@ -1487,10 +1513,10 @@ This rule has no exceptions:
1487
1513
  For an organization-only HTML page, use the \`Forge link\` printed by \`--access organization\`. For other authenticated media references, use the dashboard form:
1488
1514
 
1489
1515
  \`\`\`
1490
- https://app.replicas.dev/workspaces/<workspace-id>?media=<media-id>
1516
+ https://app.replicas.dev/workspaces/<workspace-id>?mode=media&media=<media-id>
1491
1517
  \`\`\`
1492
1518
 
1493
- The \`media=<media-id>\` query param opens that specific file in the workspace.
1519
+ The \`mode=media&media=<media-id>\` query params open that specific file in the workspace.
1494
1520
 
1495
1521
  When you have a specific file in mind, never link the bare workspace URL without the \`media=<id>\` param.
1496
1522
 
@@ -1500,6 +1526,8 @@ Always render as a markdown / mrkdwn hyperlink labeled **View in Replicas** \u20
1500
1526
 
1501
1527
  Include each markdown embed line **verbatim** where you want that file to render inline. The chat substitutes each one with an embedded image, video, or audio player. For HTML, use the printed \`HTML page\` link. Multiple embeds can appear in a single reply.
1502
1528
 
1529
+ Never reply with only dashboard links (for example a "Links:" list of \`?mode=media&media=\` URLs) in place of the embeds: a dashboard link renders as plain text, so the user sees nothing inline. This applies even when the task came from Slack, Linear, GitHub, or GitLab; the chat reply still gets the embeds.
1530
+
1503
1531
  After (or alongside) the embeds, include a \`[View in Canvas](<deep-link>)\` hyperlink for each file using the per-file dashboard URL printed by the CLI as **View in Replicas**. Keep that URL unchanged so it opens the specific media item in the workspace's Canvas. External messages still label this dashboard link **View in Replicas**.
1504
1532
 
1505
1533
  ## On external platforms: embed the file AND link the dashboard
@@ -1507,7 +1535,7 @@ After (or alongside) the embeds, include a \`[View in Canvas](<deep-link>)\` hyp
1507
1535
  After \`replicas media upload\` succeeds, external delivery has two required follow-up steps for every file:
1508
1536
 
1509
1537
  1. **Use the destination's inline representation:** upload the raw bytes to Slack, Linear, or GitLab; embed the image's printed public link on GitHub.
1510
- 2. **Link the Replicas dashboard deep link** (\`?media=<id>\` form) so the user can jump straight to the file in the workspace.
1538
+ 2. **Link the Replicas dashboard deep link** (\`?mode=media&media=<id>\` form) so the user can jump straight to the file in the workspace.
1511
1539
 
1512
1540
  A dashboard link alone is not enough. An embed without the dashboard link is not enough. Do both.
1513
1541
 
@@ -1536,7 +1564,7 @@ await replicas.slack.call('files.completeUploadExternal', {
1536
1564
  files: [{ id: reservation.file_id, title: 'Screenshot' }],
1537
1565
  channel_id: 'CHANNEL_ID',
1538
1566
  thread_ts: 'THREAD_TS',
1539
- initial_comment: '<https://app.replicas.dev/workspaces/<workspace-id>?media=<media-id>|View in Replicas>',
1567
+ initial_comment: '<https://app.replicas.dev/workspaces/<workspace-id>?mode=media&media=<media-id>|View in Replicas>',
1540
1568
  });
1541
1569
  \`\`\`
1542
1570
 
@@ -1559,7 +1587,7 @@ Use \`jq\` to build the JSON bodies \u2014 the GraphQL variables (\`size\`, \`fi
1559
1587
  FILE=/abs/path/to/screenshot.png
1560
1588
  FILENAME=$(basename "$FILE")
1561
1589
  SIZE=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE")
1562
- CONTENT_TYPE=image/png
1590
+ CONTENT_TYPE=image/png # the file's real MIME type, e.g. video/mp4 for a recording
1563
1591
  ISSUE_UUID=... # the Linear issue's UUID
1564
1592
  MEDIA_ID=... # the media ID printed by \`replicas media upload\`
1565
1593
 
@@ -1596,7 +1624,7 @@ curl -s -X PUT "$UPLOAD_URL" \\
1596
1624
  --data-binary @"$FILE"
1597
1625
 
1598
1626
  # 3. Reference assetUrl inline in the comment body, alongside the dashboard link.
1599
- BODY=$(printf '![screenshot](%s)\\n\\n[View in Replicas](https://app.replicas.dev/workspaces/%s?media=%s)' \\
1627
+ BODY=$(printf '![screenshot](%s)\\n\\n[View in Replicas](https://app.replicas.dev/workspaces/%s?mode=media&media=%s)' \\
1600
1628
  "$ASSET_URL" "$REPLICAS_WORKSPACE_ID" "$MEDIA_ID")
1601
1629
 
1602
1630
  jq -n \\
@@ -1608,6 +1636,8 @@ jq -n \\
1608
1636
  }' | linear_graphql
1609
1637
  \`\`\`
1610
1638
 
1639
+ For a recording, upload the MP4 with its own MIME type, then upload a poster frame (\`ffmpeg -i /tmp/demo.mp4 -frames:v 1 /tmp/demo-poster.png\`) the same way as \`image/png\` and link it: \`[![Demo recording](<poster-asset-url>)](<recording-asset-url>)\`.
1640
+
1611
1641
  The same flow works for issue bodies (use \`issueCreate\` / \`issueUpdate\` with the same markdown body).
1612
1642
 
1613
1643
  Never paste the \`api.replicas.dev/v1/media/<id>\` URL into a Linear body or comment \u2014 it will render as a broken image to anyone outside your Replicas chat.
@@ -1647,7 +1677,7 @@ Do **not** commit screenshots to the repo, use placeholder URLs, expose the auth
1647
1677
 
1648
1678
  GitLab supports native project Markdown uploads. For a GitLab destination, always upload the media to Replicas first, then POST the same original file to the repository's \`/projects/:id/uploads\` API using the workspace GitLab credential. Insert the returned \`markdown\` value into the merge request body or comment and include the Replicas dashboard link. Image uploads render inline; MP4, MOV, and WebM uploads render as inline video players.
1649
1679
 
1650
- Do not use a Replicas public link for GitLab when the native project upload succeeds; native uploads preserve project access controls.
1680
+ Do not use a Replicas public link for GitLab when the native project upload succeeds; native uploads preserve project access controls. \`replicas.gitlab.request\` only sends JSON and cannot POST multipart file bytes, so without another supported upload path use the GitHub flow above instead: the image's printed public embed, and a poster linked to the recording's **View in Replicas** URL.
1651
1681
 
1652
1682
  ## Recording defaults
1653
1683
 
@@ -1695,7 +1725,7 @@ replicas media upload chart-a.svg chart-b.svg --name "Latency chart" --name "Err
1695
1725
  var MEDIA_ABILITY = {
1696
1726
  label: "Media",
1697
1727
  description: "Deliver media to Replicas and destination workflows with inline uploads.",
1698
- bullet: "- Producing or showing the user any media \u2014 screenshots, screen recordings, generated images or diagrams, audio clips, or HTML recap pages \u2014 including in your Replicas chat reply, PR descriptions/comments, and other external platforms",
1728
+ bullet: "- Producing or showing the user any media \u2014 screenshots, screen recordings, generated images or diagrams, audio clips, or HTML recap pages \u2014 including in your Replicas chat reply, PR descriptions/comments, and other external platforms. Paste the printed `![\u2026](\u2026)` embeds in your chat reply, never only dashboard links, and upload the file itself to Slack, Linear, or GitLab",
1699
1729
  section: SECTION7,
1700
1730
  referenceFile: { name: "MEDIA.md", content: REFERENCE7 }
1701
1731
  };
@@ -1802,6 +1832,7 @@ Use this when:
1802
1832
  - The user asks to create or control an E2B sandbox
1803
1833
  - The user asks to invoke a connected Modal function
1804
1834
  - The user asks to access Vercel projects, deployments, domains, logs, or infrastructure
1835
+ - The user asks to read or update Lightfield CRM accounts, contacts, opportunities, meetings, notes, or tasks
1805
1836
  - The user asks to search or read Greptile's codebase knowledge bases
1806
1837
  - The user asks to inspect Vanta controls, tests, evidence, vendors, risks, or compliance data
1807
1838
  - The user asks to query or update a connected turbopuffer namespace
@@ -1913,6 +1944,18 @@ const deployments = await replicas.vercel.request('/v6/deployments?teamId=team_1
1913
1944
 
1914
1945
  Confirm mutations before using POST, PUT, PATCH, or DELETE.
1915
1946
 
1947
+ ## Lightfield
1948
+
1949
+ Lightfield is a native API-key integration. Pass a path relative to the Lightfield \`/v1\` API; Replicas sends the key and \`Lightfield-Version\` header server-side:
1950
+
1951
+ \`\`\`ts
1952
+ const accounts = await replicas.lightfield.request('/accounts?limit=25');
1953
+ const fields = await replicas.lightfield.request('/accounts/definitions');
1954
+ const note = await replicas.lightfield.request('/notes', { method: 'POST', body: { fields: { $title: 'Call recap' } } });
1955
+ \`\`\`
1956
+
1957
+ See https://docs.lightfield.app/llms.txt for endpoints. Paginate with \`limit\` and \`offset\`. The key's scopes control permitted operations. Confirm mutations before using POST, PUT, PATCH, or DELETE.
1958
+
1916
1959
  ## Vanta
1917
1960
 
1918
1961
  Vanta is a native Manage API integration. Pass a relative path beginning with /v1/; Replicas exchanges the stored client credentials and caches the short-lived token server-side:
@@ -1990,6 +2033,7 @@ Take action *with* Replicas itself \u2014 manage automations, environments (vari
1990
2033
 
1991
2034
  Use this when:
1992
2035
  - The user asks you to create, edit, run, or delete an automation
2036
+ - The user asks you to come back to something later ("check back on this in a week", "remind me tomorrow", "tell me when CI is green") \u2014 schedule a follow-up (see *Follow-ups and reminders*)
1993
2037
  - The user asks you to manage environments, environment variables, or environment files
1994
2038
  - The user asks "what envs / repos / automations do I have?"
1995
2039
  - The user asks you to scaffold a \`replicas.json\` / \`replicas.yaml\` in a repo
@@ -2036,6 +2080,7 @@ In agent mode the CLI hides commands that don't make sense for in-workspace agen
2036
2080
  | \`replicas preview ...\` | Register / list preview URLs (covered in \`PREVIEWS.md\`) |
2037
2081
  | \`replicas service ...\` | Run long-lived services detached so they survive workspace sleep/wake (see below) |
2038
2082
  | \`replicas media ...\` | Upload, list, share, revoke, or delete workspace media; follow \`MEDIA.md\` for destination embedding |
2083
+ | \`replicas skills suggest <SKILL.md> --title <text>\` | Suggest saving a lasting user instruction as a learning (an organization skill); the user accepts or dismisses it |
2039
2084
  | \`replicas slack thread ...\` | Attach or switch Slack thread routing (covered in \`SLACK.md\`) |
2040
2085
 
2041
2086
  \`replicas <command> --help\` is always the source of truth for flags.
@@ -2050,6 +2095,7 @@ In agent mode the CLI hides commands that don't make sense for in-workspace agen
2050
2095
  | "Create a new env for my \`acme/web\` repo" | \`replicas environment create <name> -r acme/web\` |
2051
2096
  | "What automations do I have?" | \`replicas automation list\` |
2052
2097
  | "Run my nightly automation now" | \`replicas automation run <id>\` |
2098
+ | "Check back on this in a week" | \`replicas automation create ... --in 1w --background --personal\` |
2053
2099
  | "Make an automation that runs every weekday at 9am" | \`replicas automation create\` (interactive) or \`--trigger-cron "0 9 * * 1-5"\` |
2054
2100
  | "Make an automation that runs when a PR opens on \`acme/web\`" | \`replicas automation create ... --trigger-github pull_request.opened --trigger-github-repos acme/web\` |
2055
2101
  | "Make an automation that runs when an MR opens on \`acme/web\`" | \`replicas automation create ... --trigger-gitlab merge_request.opened --trigger-gitlab-repos acme/web\` |
@@ -2161,6 +2207,26 @@ replicas automation create ... --lifecycle default --auto-stop-minutes 30
2161
2207
 
2162
2208
  When the user says "make me an automation that...", default to **picking the environment for them by listing the available envs** and asking only when the user hasn't already pinned one. Same for repos in GitHub triggers. Don't pepper the user with questions you can answer by reading the existing config.
2163
2209
 
2210
+ ### Follow-ups and reminders
2211
+
2212
+ You cannot wait days yourself. When the user asks you to come back to something later, schedule a one-time automation that delivers a prompt back into **this workspace's conversation** at that time, then deletes itself:
2213
+
2214
+ \`\`\`bash
2215
+ replicas automation create "Re-check PR #142 CI" \\
2216
+ --in 1w \\
2217
+ --background --personal \\
2218
+ --prompt "A week ago the user asked me to re-check CI on https://github.com/acme/web/pull/142 (flaky e2e job 'checkout'). Check the latest run, fix it if it is still failing, and summarize the result for the user."
2219
+ \`\`\`
2220
+
2221
+ - \`--in <duration>\` (\`30m\`, \`2h\`, \`3d\`, \`1w\`, \`1d12h\`) or \`--at <ISO-8601 with offset>\` (e.g. "tomorrow at 9am" \u2192 \`--at 2026-10-08T09:00:00-07:00\`, using the user's timezone if you know it; otherwise say which timezone you assumed).
2222
+ - Inside a workspace, one-time follow-ups default to \`--workspace current\`, so you resume with this conversation's context. Inside a workspace, \`--environment\` defaults to this workspace's environment (same repositories), including for \`--workspace new\` and \`reuse\`.
2223
+ - Write \`--prompt\` as instructions to your future self: the links, what to check, what "done" looks like, and how to report back (e.g. reply in the original Slack thread). It runs with no memory of why it was scheduled beyond what you write.
2224
+ - \`--background\` keeps follow-ups out of the user's main automations list (they are still visible under **Background**). Use it for every one-time follow-up that reports back into this conversation, even one that leads to a big decision. Omit it only for recurring automations the user wants to see and manage, such as ongoing monitoring.
2225
+ - \`--personal\` makes the automation the user's own. Omit it only when the follow-up belongs to the organization.
2226
+ - Recurring with an end: \`--trigger-cron "0 9 * * *" --max-runs 5 --workspace current\` runs daily five times, then deletes itself. \`--workspace reuse\` creates one new workspace on the first run and sends every later run to it; \`--workspace new\` (the default for non-one-time automations) creates a fresh workspace per run.
2227
+ - Only schedule when the user asked for a follow-up or clearly agreed to one; not for "maybe I'll look at this later".
2228
+ - Confirm using the **Next Run** time the CLI prints, not your own arithmetic. Cancel with \`replicas automation delete <id> --force\`; find them with \`replicas automation list --background true\`.
2229
+
2164
2230
  ### Edit
2165
2231
 
2166
2232
  \`\`\`bash
@@ -2262,9 +2328,6 @@ var SENTRY_ABILITY = {
2262
2328
  referenceFile: { name: "SENTRY.md", content: REFERENCE11 }
2263
2329
  };
2264
2330
 
2265
- // ../shared/src/memory.ts
2266
- import { z as z5 } from "zod";
2267
-
2268
2331
  // ../shared/src/sandbox.ts
2269
2332
  var SANDBOX_LIFECYCLE = {
2270
2333
  AUTO_STOP_MINUTES: 60,
@@ -2285,11 +2348,6 @@ var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZ
2285
2348
  // ../shared/src/memory.ts
2286
2349
  var HISTORY_ROOT = `${SANDBOX_PATHS.REPLICAS_DIR}/history`;
2287
2350
  var HISTORY_INDEX_FILENAME = "INDEX.md";
2288
- var memoryExtractionResultSchema = z5.object({
2289
- summary: z5.string().nullable(),
2290
- indexEntry: z5.string().nullable()
2291
- });
2292
- var { $schema: _, ...MEMORY_EXTRACTION_SCHEMA } = z5.toJSONSchema(memoryExtractionResultSchema);
2293
2351
 
2294
2352
  // ../shared/src/default-skills/replicas-agent/abilities/session-history.ts
2295
2353
  var SECTION12 = `### Session history
@@ -2300,20 +2358,20 @@ var REFERENCE12 = `# Session history
2300
2358
 
2301
2359
  Past sessions are installed under \`${HISTORY_ROOT}\`:
2302
2360
 
2303
- - \`${HISTORY_INDEX_FILENAME}\` lists every session, grouped under \`## YYYY-MM-DD\` headings by start day (UTC). Each line is \`- <date> <time> | <who> | <workspace> | <description> | active: <days> | <summary>\`; \`active:\` appears only for sessions spanning several days.
2304
- - \`summaries/<YYYY-MM-DD>-<id>.md\` holds the summary of a session you can read. Teammates' private sessions show \`\u2014\` instead of a summary path. Sessions without a shareable description show "Private session" unless they are yours.
2361
+ - \`${HISTORY_INDEX_FILENAME}\` lists every session, grouped under \`## YYYY-MM-DD\` headings by start day (UTC). Each line is \`- <date> <time> | <who> | <workspace> | <description>; <PRs with current state> | active: <days> | <condensed file>\`; \`active:\` appears only for sessions spanning several days.
2362
+ - \`condensed/<YYYY-MM-DD>-<id>.md\` holds the condensed transcript of a session you can read: the user's messages, the agent's replies (the final one in full), commands with their last output lines, errors, links, small diffs, and the files touched. Teammates' private sessions show "Private session" and \`\u2014\` instead of a file.
2305
2363
 
2306
- Search the index, then open the referenced summaries:
2364
+ Search the index, then open or search the condensed transcripts:
2307
2365
 
2308
2366
  \`\`\`bash
2309
2367
  cd ${HISTORY_ROOT}
2310
2368
  rg '^- 2026-09-30' ${HISTORY_INDEX_FILENAME} # sessions that started that day
2311
2369
  rg 2026-09-30 ${HISTORY_INDEX_FILENAME} # also sessions active that day
2312
2370
  rg -i 'launch|video' ${HISTORY_INDEX_FILENAME} # by topic, person, or workspace
2313
- rg -il 'launch|video' summaries/ # full-text search of summaries
2371
+ rg -il 'launch|video' condensed/ # full-text search of condensed transcripts
2314
2372
  \`\`\`
2315
2373
 
2316
- When a summary isn't enough, fetch the full transcript of any session you can read. It is saved as JSONL, one JSON string per message or tool call:
2374
+ Condensed transcripts drop most tool output. When you need an exact value, id, number, error, or a command's full output, fetch the full transcript of any session you can read. It is saved as JSONL, one JSON string per message or tool call:
2317
2375
 
2318
2376
  \`\`\`bash
2319
2377
  replicas history transcript 2026-09-30-<id>
@@ -2479,7 +2537,6 @@ var DEFAULT_USER_PREFERENCES = {
2479
2537
  auto_draft_prs: false,
2480
2538
  open_prs_in_graphite: false,
2481
2539
  open_links_in_desktop_app: false,
2482
- mothership_in_dms: false,
2483
2540
  memory_enabled: null,
2484
2541
  subagent_mcp_enabled: null,
2485
2542
  default_fast_mode: false,
@@ -2504,15 +2561,6 @@ var DEFAULT_DEFAULT_SKILLS = {
2504
2561
  }
2505
2562
  };
2506
2563
 
2507
- // ../shared/src/prompts.ts
2508
- var mothershipMcpActions = `These are tools of the \`${REPLICAS_MCP_SERVER_NAME}\` MCP server; their argument names match the envelope fields. Call them directly as tools, never through a shell or the Replicas CLI.
2509
- - **Spawn** a dedicated workspace for the thread and hand off: \`${REPLICAS_MCP_TOOLS.spawn}\`, with \`environment_id\` when you chose one.
2510
- - **Relay** a follow-up that is clearly meant for you into the workspace already bound to the thread (pass the envelope's \`user_id\` so it's attributed to its author): \`${REPLICAS_MCP_TOOLS.relay}\`, with \`queue: true\` to queue it.
2511
- - **List environments**: \`${REPLICAS_MCP_TOOLS.listEnvironments}\`.
2512
- - **Create an automation**: \`${REPLICAS_MCP_TOOLS.createAutomation}\`, filling its \`confirmation_*\` arguments from the accepting message's envelope.
2513
- - **Register a Linear session intent**: \`${REPLICAS_MCP_TOOLS.linearSessionIntent}\` with \`comment_id\` and \`action\`.
2514
- - **Start a Linear session**: \`${REPLICAS_MCP_TOOLS.linearStart}\` with \`issue_id\`, \`agent_session_id\`, and \`prompt\`.`;
2515
-
2516
2564
  // ../shared/src/display-message/types.ts
2517
2565
  var BACKGROUND_TASK_SUBTYPES = /* @__PURE__ */ new Set([
2518
2566
  "task_started",
@@ -2596,6 +2644,8 @@ function normalizeBackgroundTaskStatus(status) {
2596
2644
 
2597
2645
  // ../shared/src/display-message/constants.ts
2598
2646
  var USER_MESSAGE_MATCH_GRACE_PERIOD_MS = 3e4;
2647
+ var COMPACTION_CONTENT = "Context compacted";
2648
+ var GOAL_TOOL_NAME = "goal";
2599
2649
 
2600
2650
  // ../shared/src/shell-commands.ts
2601
2651
  var SHELL_OPAQUE = "\0";
@@ -3057,6 +3107,15 @@ function skillNamesFromCommand(command) {
3057
3107
  })));
3058
3108
  }
3059
3109
 
3110
+ // ../shared/src/user-message-matching.ts
3111
+ function parseTimestampMs(timestamp) {
3112
+ const value = typeof timestamp === "number" ? timestamp : Date.parse(timestamp);
3113
+ return Number.isFinite(value) ? value : 0;
3114
+ }
3115
+ function areUserMessagesWithinMatchWindow(a, b) {
3116
+ return a.content === b.content && Math.abs(parseTimestampMs(a.timestamp) - parseTimestampMs(b.timestamp)) <= USER_MESSAGE_MATCH_GRACE_PERIOD_MS;
3117
+ }
3118
+
3060
3119
  // ../shared/src/display-message/task-accumulator.ts
3061
3120
  function mapTaskStatus(status) {
3062
3121
  if (status === "in_progress" || status === "completed") return status;
@@ -3184,15 +3243,6 @@ var TaskAccumulator = class {
3184
3243
  }
3185
3244
  };
3186
3245
 
3187
- // ../shared/src/user-message-matching.ts
3188
- function parseTimestampMs(timestamp) {
3189
- const value = typeof timestamp === "number" ? timestamp : Date.parse(timestamp);
3190
- return Number.isFinite(value) ? value : 0;
3191
- }
3192
- function areUserMessagesWithinMatchWindow(a, b) {
3193
- return a.content === b.content && Math.abs(parseTimestampMs(a.timestamp) - parseTimestampMs(b.timestamp)) <= USER_MESSAGE_MATCH_GRACE_PERIOD_MS;
3194
- }
3195
-
3196
3246
  // ../shared/src/agent-event-utils.ts
3197
3247
  function getUserMessage(event) {
3198
3248
  return event.type === "event_msg" && event.payload.type === "user_message" && typeof event.payload.message === "string" ? event.payload.message : null;
@@ -3232,20 +3282,20 @@ var ASTER_MODEL_LABELS = {
3232
3282
  };
3233
3283
 
3234
3284
  // ../shared/src/models-dev.ts
3235
- import { z as z6 } from "zod";
3285
+ import { z as z5 } from "zod";
3236
3286
  var MODELS_DEV_CACHE_MS = 5 * 6e4;
3237
- var modelsDevCatalogSchema = z6.record(z6.string(), z6.object({
3238
- models: z6.record(z6.string(), z6.object({
3239
- id: z6.string().optional(),
3240
- name: z6.string(),
3241
- description: z6.string().optional(),
3242
- status: z6.enum(["alpha", "beta", "deprecated"]).optional(),
3243
- provider: z6.object({ npm: z6.string().optional() }).optional(),
3244
- cost: z6.object({
3245
- input: z6.number().finite().nonnegative().optional(),
3246
- output: z6.number().finite().nonnegative().optional(),
3247
- cache_read: z6.number().finite().nonnegative().optional(),
3248
- cache_write: z6.number().finite().nonnegative().optional()
3287
+ var modelsDevCatalogSchema = z5.record(z5.string(), z5.object({
3288
+ models: z5.record(z5.string(), z5.object({
3289
+ id: z5.string().optional(),
3290
+ name: z5.string(),
3291
+ description: z5.string().optional(),
3292
+ status: z5.enum(["alpha", "beta", "deprecated"]).optional(),
3293
+ provider: z5.object({ npm: z5.string().optional() }).optional(),
3294
+ cost: z5.object({
3295
+ input: z5.number().finite().nonnegative().optional(),
3296
+ output: z5.number().finite().nonnegative().optional(),
3297
+ cache_read: z5.number().finite().nonnegative().optional(),
3298
+ cache_write: z5.number().finite().nonnegative().optional()
3249
3299
  }).optional()
3250
3300
  }))
3251
3301
  }));
@@ -3260,6 +3310,7 @@ var CLAUDE_OPUS_5_MODEL = "claude-opus-5";
3260
3310
  var CLAUDE_OPUS_4_8_MODEL = "claude-opus-4-8";
3261
3311
  var CLAUDE_SONNET_5_5_MODEL = "claude-sonnet-5-5";
3262
3312
  var CLAUDE_SONNET_5_MODEL = "claude-sonnet-5";
3313
+ var CLAUDE_HAIKU_5_5_MODEL = "claude-haiku-5-5";
3263
3314
  var CLAUDE_HAIKU_4_5_MODEL = "claude-haiku-4-5";
3264
3315
  var CLAUDE_FABLE_5_1_BEDROCK_MODEL = "anthropic.claude-fable-5-1";
3265
3316
  var CLAUDE_FABLE_5_BEDROCK_MODEL = "anthropic.claude-fable-5";
@@ -3268,6 +3319,7 @@ var CLAUDE_OPUS_5_BEDROCK_MODEL = "anthropic.claude-opus-5";
3268
3319
  var CLAUDE_OPUS_4_8_BEDROCK_MODEL = "us.anthropic.claude-opus-4-8";
3269
3320
  var CLAUDE_SONNET_5_5_BEDROCK_MODEL = "anthropic.claude-sonnet-5-5";
3270
3321
  var CLAUDE_SONNET_5_BEDROCK_MODEL = "anthropic.claude-sonnet-5";
3322
+ var CLAUDE_HAIKU_5_5_BEDROCK_MODEL = "anthropic.claude-haiku-5-5";
3271
3323
  var CLAUDE_HAIKU_4_5_BEDROCK_MODEL = "us.anthropic.claude-haiku-4-5-20251001-v1:0";
3272
3324
  var GPT_6_ASTRA_MODEL = "gpt-6-astra";
3273
3325
  var GPT_6_1_SOL_MODEL = "gpt-6.1-sol";
@@ -3303,6 +3355,7 @@ var CLAUDE_MODEL_CONTEXT_WINDOWS = {
3303
3355
  [CLAUDE_OPUS_4_8_MODEL]: 1e6,
3304
3356
  [CLAUDE_SONNET_5_5_MODEL]: 1e6,
3305
3357
  [CLAUDE_SONNET_5_MODEL]: 1e6,
3358
+ [CLAUDE_HAIKU_5_5_MODEL]: 1e6,
3306
3359
  [CLAUDE_HAIKU_4_5_MODEL]: 2e5,
3307
3360
  [CLAUDE_FABLE_5_1_BEDROCK_MODEL]: 1e6,
3308
3361
  [CLAUDE_FABLE_5_BEDROCK_MODEL]: 1e6,
@@ -3311,6 +3364,7 @@ var CLAUDE_MODEL_CONTEXT_WINDOWS = {
3311
3364
  [CLAUDE_OPUS_4_8_BEDROCK_MODEL]: 1e6,
3312
3365
  [CLAUDE_SONNET_5_5_BEDROCK_MODEL]: 1e6,
3313
3366
  [CLAUDE_SONNET_5_BEDROCK_MODEL]: 1e6,
3367
+ [CLAUDE_HAIKU_5_5_BEDROCK_MODEL]: 1e6,
3314
3368
  [CLAUDE_HAIKU_4_5_BEDROCK_MODEL]: 2e5,
3315
3369
  "anthropic.claude-opus-4-8": 1e6,
3316
3370
  "us.anthropic.claude-sonnet-5": 1e6,
@@ -3318,18 +3372,33 @@ var CLAUDE_MODEL_CONTEXT_WINDOWS = {
3318
3372
  "claude-haiku-4-5-20251001": 2e5,
3319
3373
  "claude-haiku-4-5@20251001": 2e5
3320
3374
  };
3375
+ var CLAUDE_MODEL_MIN_SANDBOX_IMAGE = {
3376
+ [CLAUDE_FABLE_5_1_MODEL]: "replicas-sandbox-2026-09-01-v6",
3377
+ [CLAUDE_FABLE_5_MODEL]: null,
3378
+ [CLAUDE_OPUS_5_5_MODEL]: "replicas-sandbox-2026-09-22-v4",
3379
+ [CLAUDE_OPUS_5_MODEL]: null,
3380
+ [CLAUDE_OPUS_4_8_MODEL]: null,
3381
+ [CLAUDE_SONNET_5_5_MODEL]: "replicas-sandbox-2026-09-28-v7",
3382
+ [CLAUDE_SONNET_5_MODEL]: null,
3383
+ [CLAUDE_HAIKU_5_5_MODEL]: "replicas-sandbox-2026-10-08-v4",
3384
+ [CLAUDE_HAIKU_4_5_MODEL]: null
3385
+ };
3386
+ var CODEX_MODEL_MIN_SANDBOX_IMAGE = {
3387
+ [DEFAULT_CODEX_MODEL]: "replicas-sandbox-2026-09-30-v11",
3388
+ [GPT_6_SOL_MODEL]: "replicas-sandbox-2026-09-24-v1",
3389
+ [GPT_6_ASTRA_MODEL]: "replicas-sandbox-2026-09-04-v7",
3390
+ [GPT_6_LUNA_MODEL]: "replicas-sandbox-2026-09-24-v1",
3391
+ [GPT_5_6_SOL_MODEL]: null,
3392
+ [GPT_5_6_TERRA_MODEL]: null,
3393
+ [GPT_5_6_LUNA_MODEL]: null,
3394
+ "gpt-5.5": null,
3395
+ "gpt-5.4": null,
3396
+ "gpt-5.4-mini": null,
3397
+ "gpt-5.2": null
3398
+ };
3321
3399
  var AGENT_MODELS = {
3322
- claude: [
3323
- CLAUDE_FABLE_5_1_MODEL,
3324
- CLAUDE_FABLE_5_MODEL,
3325
- CLAUDE_OPUS_5_5_MODEL,
3326
- CLAUDE_OPUS_5_MODEL,
3327
- CLAUDE_OPUS_4_8_MODEL,
3328
- CLAUDE_SONNET_5_5_MODEL,
3329
- CLAUDE_SONNET_5_MODEL,
3330
- CLAUDE_HAIKU_4_5_MODEL
3331
- ],
3332
- codex: [DEFAULT_CODEX_MODEL, GPT_6_SOL_MODEL, GPT_6_ASTRA_MODEL, GPT_6_LUNA_MODEL, GPT_5_6_SOL_MODEL, GPT_5_6_TERRA_MODEL, GPT_5_6_LUNA_MODEL, "gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.2"],
3400
+ claude: Object.keys(CLAUDE_MODEL_MIN_SANDBOX_IMAGE),
3401
+ codex: Object.keys(CODEX_MODEL_MIN_SANDBOX_IMAGE),
3333
3402
  cursor: [
3334
3403
  CLAUDE_FABLE_5_1_MODEL,
3335
3404
  CLAUDE_FABLE_5_MODEL,
@@ -3401,20 +3470,10 @@ var DEFAULT_STATIC_AGENT_MODELS = {
3401
3470
  codex: withoutHiddenByDefault(AGENT_MODELS.codex)
3402
3471
  };
3403
3472
  var DEFAULT_CURSOR_MODELS = withoutHiddenByDefault(AGENT_MODELS.cursor);
3404
- var CLAUDE_MODEL_MIN_SANDBOX_IMAGE = {
3405
- [CLAUDE_FABLE_5_1_MODEL]: "replicas-sandbox-2026-09-01-v6",
3406
- [CLAUDE_OPUS_5_5_MODEL]: "replicas-sandbox-2026-09-22-v4",
3407
- [CLAUDE_SONNET_5_5_MODEL]: "replicas-sandbox-2026-09-28-v7"
3408
- };
3409
- var CODEX_MODEL_MIN_SANDBOX_IMAGE = {
3410
- [GPT_6_ASTRA_MODEL]: "replicas-sandbox-2026-09-04-v7",
3411
- [GPT_6_1_SOL_MODEL]: "replicas-sandbox-2026-09-30-v11",
3412
- [GPT_6_SOL_MODEL]: "replicas-sandbox-2026-09-24-v1",
3413
- [GPT_6_LUNA_MODEL]: "replicas-sandbox-2026-09-24-v1"
3414
- };
3415
3473
  var SANDBOX_IMAGE_MODEL_FALLBACKS = {
3416
3474
  [CLAUDE_OPUS_5_5_MODEL]: CLAUDE_OPUS_5_MODEL,
3417
3475
  [CLAUDE_SONNET_5_5_MODEL]: CLAUDE_SONNET_5_MODEL,
3476
+ [CLAUDE_HAIKU_5_5_MODEL]: CLAUDE_HAIKU_4_5_MODEL,
3418
3477
  [GPT_6_1_SOL_MODEL]: GPT_6_SOL_MODEL,
3419
3478
  [GPT_6_SOL_MODEL]: GPT_5_6_SOL_MODEL,
3420
3479
  [GPT_6_ASTRA_MODEL]: GPT_5_6_TERRA_MODEL,
@@ -3468,6 +3527,7 @@ var MODEL_LABELS = {
3468
3527
  "composer-2.5": "Composer 2.5",
3469
3528
  [CLAUDE_OPUS_4_8_MODEL]: "Opus 4.8",
3470
3529
  "claude-opus-4-7": "Opus 4.7",
3530
+ [CLAUDE_HAIKU_5_5_MODEL]: "Haiku 5.5",
3471
3531
  [CLAUDE_HAIKU_4_5_MODEL]: "Haiku 4.5",
3472
3532
  "gemini-3.8-flash": "Gemini 3.8 Flash",
3473
3533
  "gemini-3.1-pro": "Gemini 3.1 Pro",
@@ -3483,97 +3543,97 @@ var MAX_CANVAS_FILE_BYTES = 25 * 1024 * 1024;
3483
3543
  var MAX_CANVAS_PREVIEW_BYTES = 5 * 1024 * 1024;
3484
3544
 
3485
3545
  // ../shared/src/engine/v1.ts
3486
- import { z as z7 } from "zod";
3487
- var updateChatDisplayNameRequestSchema = z7.object({
3488
- displayName: z7.string().trim().min(1).max(100)
3546
+ import { z as z6 } from "zod";
3547
+ var updateChatDisplayNameRequestSchema = z6.object({
3548
+ displayName: z6.string().trim().min(1).max(100)
3489
3549
  });
3490
- var createChatRequestSchema = z7.object({
3491
- id: z7.string().uuid().optional(),
3492
- createdAt: z7.string().datetime().optional(),
3493
- provider: z7.enum(VALID_AGENT_PROVIDERS),
3494
- title: z7.string().min(1).optional(),
3495
- parentChatId: z7.string().uuid().optional(),
3496
- forkSourceChatId: z7.string().uuid().optional(),
3497
- sideChat: z7.boolean().optional(),
3498
- clientRequestId: z7.string().min(1).max(128).optional()
3550
+ var createChatRequestSchema = z6.object({
3551
+ id: z6.string().uuid().optional(),
3552
+ createdAt: z6.string().datetime().optional(),
3553
+ provider: z6.enum(VALID_AGENT_PROVIDERS),
3554
+ title: z6.string().min(1).optional(),
3555
+ parentChatId: z6.string().uuid().optional(),
3556
+ forkSourceChatId: z6.string().uuid().optional(),
3557
+ sideChat: z6.boolean().optional(),
3558
+ clientRequestId: z6.string().min(1).max(128).optional()
3499
3559
  }).refine((request) => !request.sideChat || request.forkSourceChatId !== void 0 && request.parentChatId === request.forkSourceChatId, {
3500
3560
  message: "A side chat must fork its parent chat"
3501
3561
  });
3502
- var spawnSubagentRequestSchema = z7.object({
3503
- provider: z7.enum(VALID_AGENT_PROVIDERS),
3504
- prompt: z7.string().min(1),
3505
- forkTurns: z7.string().regex(/^(all|none|[1-9]\d*)$/).refine(
3562
+ var spawnSubagentRequestSchema = z6.object({
3563
+ provider: z6.enum(VALID_AGENT_PROVIDERS),
3564
+ prompt: z6.string().min(1),
3565
+ forkTurns: z6.string().regex(/^(all|none|[1-9]\d*)$/).refine(
3506
3566
  (value) => value === "all" || value === "none" || Number.isSafeInteger(Number(value)),
3507
3567
  "Expected all, none, or a positive safe integer string"
3508
3568
  ).default("all"),
3509
- model: z7.string().min(1).optional(),
3510
- thinkingLevel: z7.enum(VALID_THINKING_LEVELS).optional(),
3511
- title: z7.string().min(1).optional()
3569
+ model: z6.string().min(1).optional(),
3570
+ thinkingLevel: z6.enum(VALID_THINKING_LEVELS).optional(),
3571
+ title: z6.string().min(1).optional()
3512
3572
  });
3513
- var messageSubagentRequestSchema = z7.object({
3514
- message: z7.string().min(1),
3515
- model: z7.string().min(1).optional(),
3516
- thinkingLevel: z7.enum(VALID_THINKING_LEVELS).optional()
3573
+ var messageSubagentRequestSchema = z6.object({
3574
+ message: z6.string().min(1),
3575
+ model: z6.string().min(1).optional(),
3576
+ thinkingLevel: z6.enum(VALID_THINKING_LEVELS).optional()
3517
3577
  });
3518
- var waitSubagentRequestSchema = z7.object({
3519
- timeoutMinutes: z7.coerce.number().positive().max(1440).optional()
3578
+ var waitSubagentRequestSchema = z6.object({
3579
+ timeoutMinutes: z6.coerce.number().positive().max(1440).optional()
3520
3580
  });
3521
- var subagentResponseSchema = z7.object({
3522
- chatId: z7.string().uuid(),
3523
- status: z7.enum(["working", "completed"]),
3524
- response: z7.string().optional(),
3525
- timedOut: z7.literal(true).optional()
3581
+ var subagentResponseSchema = z6.object({
3582
+ chatId: z6.string().uuid(),
3583
+ status: z6.enum(["working", "completed"]),
3584
+ response: z6.string().optional(),
3585
+ timedOut: z6.literal(true).optional(),
3586
+ interruptedByUserMessage: z6.literal(true).optional()
3526
3587
  });
3527
- var integrationErrorNotificationTargetSchema = z7.discriminatedUnion("type", [
3528
- z7.object({
3529
- type: z7.literal("slack"),
3530
- conversation: z7.object({
3531
- channelId: z7.string().min(1),
3532
- threadTs: z7.string().min(1)
3588
+ var integrationErrorNotificationTargetSchema = z6.discriminatedUnion("type", [
3589
+ z6.object({
3590
+ type: z6.literal("slack"),
3591
+ conversation: z6.object({
3592
+ channelId: z6.string().min(1),
3593
+ threadTs: z6.string().min(1)
3533
3594
  }).optional()
3534
3595
  }),
3535
- z7.object({ type: z7.literal("linear"), sessionId: z7.string().min(1) }),
3536
- z7.object({
3537
- type: z7.literal("code_host"),
3538
- provider: z7.enum(["github", "gitlab"]),
3539
- resource: z7.enum(["issue", "pull_request"]),
3540
- repositoryId: z7.string().min(1),
3541
- resourceNumber: z7.number().int().positive()
3596
+ z6.object({ type: z6.literal("linear"), sessionId: z6.string().min(1) }),
3597
+ z6.object({
3598
+ type: z6.literal("code_host"),
3599
+ provider: z6.enum(["github", "gitlab"]),
3600
+ resource: z6.enum(["issue", "pull_request"]),
3601
+ repositoryId: z6.string().min(1),
3602
+ resourceNumber: z6.number().int().positive()
3542
3603
  }),
3543
- z7.object({ type: z7.literal("automation"), executionId: z7.string().min(1) })
3604
+ z6.object({ type: z6.literal("automation"), executionId: z6.string().min(1) })
3544
3605
  ]);
3545
- var sendChatMessageRequestSchema = z7.object({
3546
- messageId: z7.string().min(1).optional(),
3547
- submittedAt: z7.string().datetime().optional(),
3548
- message: z7.string().min(1),
3549
- model: z7.string().optional(),
3550
- customInstructions: z7.string().optional(),
3551
- planMode: z7.boolean().optional(),
3552
- images: z7.array(z7.object({
3553
- type: z7.literal("image"),
3554
- source: z7.discriminatedUnion("type", [
3555
- z7.object({ type: z7.literal("base64"), media_type: z7.enum(IMAGE_MEDIA_TYPES), data: z7.string().min(1) }),
3556
- z7.object({ type: z7.literal("url"), url: z7.string().url() })
3606
+ var sendChatMessageRequestSchema = z6.object({
3607
+ messageId: z6.string().min(1).optional(),
3608
+ submittedAt: z6.string().datetime().optional(),
3609
+ message: z6.string().min(1),
3610
+ model: z6.string().optional(),
3611
+ customInstructions: z6.string().optional(),
3612
+ planMode: z6.boolean().optional(),
3613
+ images: z6.array(z6.object({
3614
+ type: z6.literal("image"),
3615
+ source: z6.discriminatedUnion("type", [
3616
+ z6.object({ type: z6.literal("base64"), media_type: z6.enum(IMAGE_MEDIA_TYPES), data: z6.string().min(1) }),
3617
+ z6.object({ type: z6.literal("url"), url: z6.string().url() })
3557
3618
  ])
3558
3619
  })).optional(),
3559
- canvasUploadIds: z7.array(z7.string().min(1)).optional(),
3560
- thinkingLevel: z7.enum(VALID_THINKING_LEVELS).optional(),
3561
- goalMode: z7.boolean().optional(),
3562
- fastMode: z7.boolean().optional(),
3563
- source: z7.enum(MESSAGE_SOURCES).optional(),
3564
- enableInteractiveTools: z7.boolean().optional(),
3565
- type: z7.string().min(1).optional(),
3566
- merge: z7.boolean().optional(),
3567
- idempotencyKey: z7.string().min(1).max(128).optional(),
3568
- automationId: z7.string().optional(),
3569
- environmentId: z7.string().min(1).optional(),
3570
- chatCredentials: z7.boolean().optional(),
3571
- senderUserId: z7.string().optional(),
3572
- senderEmail: z7.string().optional(),
3573
- senderDisplayName: z7.string().optional(),
3574
- senderAvatarUrl: z7.string().optional(),
3620
+ canvasUploadIds: z6.array(z6.string().min(1)).optional(),
3621
+ thinkingLevel: z6.enum(VALID_THINKING_LEVELS).optional(),
3622
+ goalMode: z6.boolean().optional(),
3623
+ fastMode: z6.boolean().optional(),
3624
+ source: z6.enum(MESSAGE_SOURCES).optional(),
3625
+ enableInteractiveTools: z6.boolean().optional(),
3626
+ type: z6.string().min(1).optional(),
3627
+ merge: z6.boolean().optional(),
3628
+ idempotencyKey: z6.string().min(1).max(128).optional(),
3629
+ automationId: z6.string().optional(),
3630
+ environmentId: z6.string().min(1).optional(),
3631
+ senderUserId: z6.string().optional(),
3632
+ senderEmail: z6.string().optional(),
3633
+ senderDisplayName: z6.string().optional(),
3634
+ senderAvatarUrl: z6.string().optional(),
3575
3635
  errorNotificationTarget: integrationErrorNotificationTargetSchema.optional(),
3576
- errorNotificationTargets: z7.array(integrationErrorNotificationTargetSchema).optional()
3636
+ errorNotificationTargets: z6.array(integrationErrorNotificationTargetSchema).optional()
3577
3637
  }).passthrough();
3578
3638
  function normalizeCodexAspTranscriptStatus(status, failed = false) {
3579
3639
  if (failed || status === "failed" || status === "declined") return "failed";
@@ -3605,6 +3665,7 @@ var CODEX_ITEM_FIELD_CHECKS = {
3605
3665
  function isCodexAspTranscriptItem(value) {
3606
3666
  if (!isRecord(value) || typeof value.id !== "string" || typeof value.timestamp !== "string") return false;
3607
3667
  if (value.sequence !== void 0 && typeof value.sequence !== "number") return false;
3668
+ if (value.completedAt !== void 0 && typeof value.completedAt !== "string") return false;
3608
3669
  if (typeof value.type !== "string" || !Object.hasOwn(CODEX_ITEM_FIELD_CHECKS, value.type)) return false;
3609
3670
  return CODEX_ITEM_FIELD_CHECKS[value.type](value);
3610
3671
  }
@@ -3791,9 +3852,10 @@ function messageForItem(item) {
3791
3852
  return {
3792
3853
  id: `reasoning-${item.id}`,
3793
3854
  type: "reasoning",
3794
- content: "Context compacted",
3855
+ content: COMPACTION_CONTENT,
3795
3856
  status: normalizeCodexAspTranscriptStatus(item.status),
3796
- timestamp: item.timestamp
3857
+ timestamp: item.timestamp,
3858
+ completedAt: item.completedAt
3797
3859
  };
3798
3860
  }
3799
3861
  return {
@@ -4587,6 +4649,15 @@ function parseMuseEvents(events) {
4587
4649
  status: terminal ? "completed" : "in_progress",
4588
4650
  timestamp: typeof item.recordedAt === "string" ? item.recordedAt : event.timestamp
4589
4651
  });
4652
+ } else if (item.kind === "compaction") {
4653
+ upsertDisplayMessage(messages, {
4654
+ id: `muse-compaction-${item.itemId}`,
4655
+ type: "reasoning",
4656
+ content: COMPACTION_CONTENT,
4657
+ status: item.status === "failed" ? "failed" : terminal ? "completed" : "in_progress",
4658
+ timestamp: typeof item.recordedAt === "string" ? item.recordedAt : event.timestamp,
4659
+ completedAt: terminal ? event.timestamp : void 0
4660
+ });
4590
4661
  } else if (item.kind === "toolCall") {
4591
4662
  let input = item.args;
4592
4663
  if (typeof item.args === "string") {
@@ -4693,19 +4764,6 @@ function parsePiEvents(events) {
4693
4764
  return messages;
4694
4765
  }
4695
4766
 
4696
- // ../shared/src/display-message/parsers/mcp.ts
4697
- function parseMcpToolName(name) {
4698
- const prefix = "mcp__";
4699
- if (!name.startsWith(prefix)) return null;
4700
- const rest = name.slice(prefix.length);
4701
- const sep = rest.indexOf("__");
4702
- if (sep <= 0) return null;
4703
- const server2 = rest.slice(0, sep);
4704
- const tool = rest.slice(sep + 2);
4705
- if (!server2 || !tool) return null;
4706
- return { server: server2, tool };
4707
- }
4708
-
4709
4767
  // ../shared/src/display-message/parsers/claude-parser.ts
4710
4768
  var ABORTED_TERMINAL_REASONS = /* @__PURE__ */ new Set(["aborted_streaming", "aborted_tools"]);
4711
4769
  var COMMAND_TOOL_NAMES = /* @__PURE__ */ new Set(["Bash", "bash", "shell"]);
@@ -4760,6 +4818,17 @@ function createSubagentMessage(messageIndex, timestamp, fields) {
4760
4818
  function collectNestedEvents(events, toolUseId) {
4761
4819
  return events.filter((e) => e.payload.parent_tool_use_id === toolUseId).map(({ timestamp, type, payload }) => ({ timestamp, type, payload }));
4762
4820
  }
4821
+ function readToolResult(event) {
4822
+ if (event.type !== "claude-user") return void 0;
4823
+ const toolResult = normalizeContentBlocks(event.payload.message?.content).find((c) => c.type === "tool_result");
4824
+ if (!toolResult?.tool_use_id) return void 0;
4825
+ return {
4826
+ toolResult,
4827
+ toolUseId: toolResult.tool_use_id,
4828
+ output: extractToolResultText(toolResult.content),
4829
+ status: toolResult.is_error ? "failed" : "completed"
4830
+ };
4831
+ }
4763
4832
  function coerceClaudeResultPayload(payload) {
4764
4833
  return {
4765
4834
  is_error: typeof payload.is_error === "boolean" ? payload.is_error : void 0,
@@ -4793,6 +4862,7 @@ function parseClaudeEvents(events, parentToolUseId) {
4793
4862
  const completedStreamIds = /* @__PURE__ */ new Set();
4794
4863
  const supersededStreamIds = /* @__PURE__ */ new Set();
4795
4864
  let liveStreamId = null;
4865
+ let compaction;
4796
4866
  const assistantThinking = /* @__PURE__ */ new Map();
4797
4867
  const assistantTextCounts = /* @__PURE__ */ new Map();
4798
4868
  const acceptedUserMessageIndexes = /* @__PURE__ */ new Set();
@@ -4926,6 +4996,20 @@ function parseClaudeEvents(events, parentToolUseId) {
4926
4996
  const messageId = event.payload.message?.id;
4927
4997
  const contentBlocks = normalizeContentBlocks(event.payload.message?.content);
4928
4998
  const messageKey = messageId || event.timestamp;
4999
+ const localCommand = event.payload.local_command_run;
5000
+ const goalArgs = isRecord(localCommand) && localCommand.command === GOAL_TOOL_NAME && typeof localCommand.args === "string" ? localCommand.args.trim() : "";
5001
+ if (goalArgs) {
5002
+ upsertDisplayMessage(messages, {
5003
+ id: `goal-${messageKey}`,
5004
+ type: "tool_call",
5005
+ server: "claude",
5006
+ tool: GOAL_TOOL_NAME,
5007
+ input: goalArgs === "clear" ? void 0 : { objective: goalArgs },
5008
+ status: "completed",
5009
+ timestamp: event.timestamp
5010
+ });
5011
+ return;
5012
+ }
4929
5013
  const streamRefs = messageId ? partialIndexes.get(messageId) : void 0;
4930
5014
  if (messageId) {
4931
5015
  if (liveStreamId !== null && liveStreamId !== messageId) {
@@ -5029,48 +5113,6 @@ function parseClaudeEvents(events, parentToolUseId) {
5029
5113
  status: "in_progress",
5030
5114
  timestamp: event.timestamp
5031
5115
  });
5032
- } else if (toolName === "mcp__relay-subagent-tools__spawn_agent") {
5033
- const inputObj = typeof toolInput === "string" ? safeJsonParse(toolInput, {}) : toolInput;
5034
- const provider = typeof inputObj.provider === "string" ? inputObj.provider : "unknown";
5035
- const prompt = typeof inputObj.prompt === "string" ? inputObj.prompt : "";
5036
- const model = typeof inputObj.model === "string" ? inputObj.model : void 0;
5037
- const title = typeof inputObj.title === "string" ? inputObj.title : void 0;
5038
- messages.push(createSubagentMessage(messages.length, event.timestamp, {
5039
- toolUseId,
5040
- description: title || `Relay subagent (${provider})`,
5041
- prompt,
5042
- subagentType: provider,
5043
- model,
5044
- thinkingLevel: isValidThinkingLevel(inputObj.thinking_level) ? inputObj.thinking_level : void 0,
5045
- status: "in_progress",
5046
- nestedEvents: [],
5047
- isChatBased: true
5048
- }));
5049
- } else if (toolName === "mcp__relay-subagent-tools__message_agent") {
5050
- const inputObj = typeof toolInput === "string" ? safeJsonParse(toolInput, {}) : toolInput;
5051
- messages.push({
5052
- id: `subagent-followup-${event.timestamp}-${messages.length}`,
5053
- type: "subagent_followup",
5054
- toolUseId,
5055
- chatId: typeof inputObj.chatId === "string" ? inputObj.chatId : "",
5056
- message: typeof inputObj.message === "string" ? inputObj.message : "",
5057
- model: typeof inputObj.model === "string" ? inputObj.model : void 0,
5058
- thinkingLevel: isValidThinkingLevel(inputObj.thinking_level) ? inputObj.thinking_level : void 0,
5059
- status: "in_progress",
5060
- timestamp: event.timestamp
5061
- });
5062
- } else if (toolName === "mcp__relay-subagent-tools__delete_agent") {
5063
- const inputObj = typeof toolInput === "string" ? safeJsonParse(toolInput, {}) : toolInput;
5064
- const mcp = parseMcpToolName(toolName);
5065
- messages.push({
5066
- id: `toolcall-${event.timestamp}-${messages.length}`,
5067
- type: "tool_call",
5068
- server: mcp?.server ?? "relay-subagent-tools",
5069
- tool: mcp?.tool ?? "delete_agent",
5070
- input: inputObj,
5071
- status: "in_progress",
5072
- timestamp: event.timestamp
5073
- });
5074
5116
  } else {
5075
5117
  messages.push({
5076
5118
  id: `toolcall-${event.timestamp}-${messages.length}`,
@@ -5085,6 +5127,11 @@ function parseClaudeEvents(events, parentToolUseId) {
5085
5127
  }
5086
5128
  });
5087
5129
  }
5130
+ if (compaction && (event.type === "claude-result" || event.type === CHAT_INTERRUPTED_EVENT_TYPE)) {
5131
+ compaction.status = "failed";
5132
+ compaction.completedAt = event.timestamp;
5133
+ compaction = void 0;
5134
+ }
5088
5135
  if (event.type === "claude-result") {
5089
5136
  const payload = coerceClaudeResultPayload(event.payload);
5090
5137
  if (isClaudeResultError(payload)) {
@@ -5098,6 +5145,19 @@ function parseClaudeEvents(events, parentToolUseId) {
5098
5145
  }
5099
5146
  }
5100
5147
  if (event.type === "claude-system") {
5148
+ const { subtype, status, compact_result: compactResult } = event.payload;
5149
+ const compactionEnded = subtype === "compact_boundary" || compactResult === "failed";
5150
+ if (compactionEnded || subtype === "status" && status === "compacting") {
5151
+ if (!compaction) {
5152
+ compaction = { id: `compaction-${event.timestamp}`, type: "reasoning", content: COMPACTION_CONTENT, timestamp: event.timestamp };
5153
+ messages.push(compaction);
5154
+ }
5155
+ compaction.status = !compactionEnded ? "in_progress" : compactResult === "failed" ? "failed" : "completed";
5156
+ if (compactionEnded) {
5157
+ compaction.completedAt = event.timestamp;
5158
+ compaction = void 0;
5159
+ }
5160
+ }
5101
5161
  const payload = coerceBackgroundTaskPayload(event.payload);
5102
5162
  if (payload?.ambient) ambientTaskIds.add(payload.taskId);
5103
5163
  if (payload && !ambientTaskIds.has(payload.taskId)) {
@@ -5182,52 +5242,38 @@ function parseClaudeEvents(events, parentToolUseId) {
5182
5242
  messages.push(fileOrCommandMessage(tool.name, tool.id, "filePath" in tool ? { file_path: tool.filePath } : { command: tool.command }, timestamp));
5183
5243
  }
5184
5244
  filteredEvents.forEach((event) => {
5185
- if (event.type === "claude-user") {
5186
- const content = normalizeContentBlocks(event.payload.message?.content);
5187
- const toolResult = content.find((c) => c.type === "tool_result");
5188
- if (toolResult && toolResult.tool_use_id) {
5189
- const toolInfo = toolCallMap.get(toolResult.tool_use_id);
5190
- if (!toolInfo) return;
5191
- const resultContent = extractToolResultText(toolResult.content);
5192
- const isError = toolResult.is_error || false;
5193
- const status = isError ? "failed" : "completed";
5194
- if (toolInfo.toolName === "TaskCreate" && !isError) {
5195
- taskAccumulator.processContentBlock(toolResult);
5196
- }
5197
- const message = messages[toolInfo.messageIndex];
5198
- if (!message) return;
5199
- if (message.type === "command") {
5200
- message.output = resultContent;
5201
- message.status = status;
5202
- const exitCodeMatch = resultContent.match(/exit code:?\s*(\d+)/i);
5203
- if (exitCodeMatch) {
5204
- message.exitCode = parseInt(exitCodeMatch[1], 10);
5205
- } else {
5206
- message.exitCode = isError ? 1 : 0;
5207
- }
5208
- } else if (message.type === "patch") {
5209
- message.status = status;
5210
- } else if (message.type === "web_search") {
5211
- message.status = status;
5212
- } else if (message.type === "tool_call") {
5213
- message.output = resultContent;
5214
- message.status = status;
5215
- } else if (message.type === "skill") {
5216
- message.status = status;
5217
- } else if (message.type === "subagent") {
5218
- message.output = resultContent;
5219
- message.status = status;
5220
- if (!message.chatId && resultContent) {
5221
- const parsed = safeJsonParse(resultContent, {});
5222
- if (parsed.chatId) {
5223
- message.chatId = parsed.chatId;
5224
- }
5225
- }
5226
- } else if (message.type === "subagent_followup") {
5227
- message.output = resultContent;
5228
- message.status = status;
5229
- }
5245
+ const result2 = readToolResult(event);
5246
+ if (!result2) return;
5247
+ const { toolResult, output: resultContent, status } = result2;
5248
+ const toolInfo = toolCallMap.get(result2.toolUseId);
5249
+ if (!toolInfo) return;
5250
+ const isError = status === "failed";
5251
+ if (toolInfo.toolName === "TaskCreate" && !isError) {
5252
+ taskAccumulator.processContentBlock(toolResult);
5253
+ }
5254
+ const message = messages[toolInfo.messageIndex];
5255
+ if (!message) return;
5256
+ if (message.type === "command") {
5257
+ message.output = resultContent;
5258
+ message.status = status;
5259
+ const exitCodeMatch = resultContent.match(/exit code:?\s*(\d+)/i);
5260
+ if (exitCodeMatch) {
5261
+ message.exitCode = parseInt(exitCodeMatch[1], 10);
5262
+ } else {
5263
+ message.exitCode = isError ? 1 : 0;
5230
5264
  }
5265
+ } else if (message.type === "patch") {
5266
+ message.status = status;
5267
+ } else if (message.type === "web_search") {
5268
+ message.status = status;
5269
+ } else if (message.type === "tool_call") {
5270
+ message.output = resultContent;
5271
+ message.status = status;
5272
+ } else if (message.type === "skill") {
5273
+ message.status = status;
5274
+ } else if (message.type === "subagent") {
5275
+ message.output = resultContent;
5276
+ message.status = status;
5231
5277
  }
5232
5278
  });
5233
5279
  for (const message of messages) {
@@ -5240,12 +5286,6 @@ function parseClaudeEvents(events, parentToolUseId) {
5240
5286
  message.subagentType ??= toolMessage.subagentType;
5241
5287
  }
5242
5288
  }
5243
- if (message.type !== "subagent_followup") continue;
5244
- const target = messages.find((candidate) => candidate.type === "subagent" && candidate.chatId === message.chatId);
5245
- if (target?.type === "subagent") {
5246
- message.targetTitle = target.description;
5247
- message.targetProvider = target.subagentType;
5248
- }
5249
5289
  }
5250
5290
  const staleIndexes = new Set(supersededUserMessageIndexes);
5251
5291
  for (const streamId of [...completedStreamIds, ...supersededStreamIds]) {
@@ -5254,7 +5294,7 @@ function parseClaudeEvents(events, parentToolUseId) {
5254
5294
  if (refs?.agent !== void 0) staleIndexes.add(refs.agent);
5255
5295
  }
5256
5296
  if (staleIndexes.size === 0) return messages;
5257
- return messages.filter((_2, index) => !staleIndexes.has(index));
5297
+ return messages.filter((_, index) => !staleIndexes.has(index));
5258
5298
  }
5259
5299
 
5260
5300
  // ../shared/src/display-message/parsers/index.ts
@@ -5279,15 +5319,15 @@ function parseAgentEvents(events, agentType) {
5279
5319
  }
5280
5320
 
5281
5321
  // ../shared/src/routes/workspaces.ts
5282
- import { z as z9 } from "zod";
5322
+ import { z as z8 } from "zod";
5283
5323
 
5284
5324
  // ../shared/src/workspace-credentials.ts
5285
- import { z as z8 } from "zod";
5325
+ import { z as z7 } from "zod";
5286
5326
  var WORKSPACE_CREDENTIAL_PROVIDERS = ["claude", "codex"];
5287
- var workspaceCredentialProviderSchema = z8.enum(WORKSPACE_CREDENTIAL_PROVIDERS);
5288
- var workspaceCredentialSelectionSchema = agentCredentialSnapshotSchema.omit({ revision: true, account: true });
5327
+ var workspaceCredentialProviderSchema = z7.enum(WORKSPACE_CREDENTIAL_PROVIDERS);
5328
+ var workspaceCredentialSelectionSchema = agentCredentialSnapshotSchema.omit({ revision: true, account: true, credentialId: true });
5289
5329
  var createWorkspaceCredentialSchema = workspaceCredentialSelectionSchema.extend({
5290
- provider: z8.enum(VALID_AGENT_PROVIDERS)
5330
+ provider: z7.enum(VALID_AGENT_PROVIDERS)
5291
5331
  }).refine(({ provider, method }) => agentCredentialMethods(provider).includes(method), {
5292
5332
  message: "This inference provider is not supported by this agent"
5293
5333
  });
@@ -5299,6 +5339,7 @@ var switchWorkspaceCredentialSchema = workspaceCredentialSelectionSchema.extend(
5299
5339
 
5300
5340
  // ../shared/src/routes/workspaces.ts
5301
5341
  var WORKSPACE_STATUSES = ["active", "sleeping", "archived", "preparing", "error"];
5342
+ var STALE_PREPARING_MS = 10 * 6e4;
5302
5343
  var VALID_LIFECYCLE_POLICIES = ["default", "archive_when_done", "sleep_when_done", "delete_after_inactivity"];
5303
5344
  var WORKSPACE_STATUS_FILTERS = WORKSPACE_STATUSES.map((status) => status === "error" ? "failed" : status);
5304
5345
  var WORKSPACE_SORT_CHOICES = [
@@ -5322,12 +5363,16 @@ var DEFAULT_WORKSPACE_FILTERS = {
5322
5363
  activity: "any"
5323
5364
  };
5324
5365
  var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
5325
- var terminalRtcCandidateSchema = z9.object({ type: z9.literal("rtc-candidate"), candidate: z9.string(), mid: z9.string() });
5326
- var terminalSocketServerMessageSchema = z9.discriminatedUnion("type", [
5327
- z9.object({ type: z9.literal("output"), data: z9.string() }),
5328
- z9.object({ type: z9.literal("rtc-answer"), sdp: z9.string() }),
5366
+ var workspaceExecRequestSchema = z8.object({
5367
+ command: z8.string().regex(/\S/, "command is required"),
5368
+ timeout_seconds: z8.number().int().min(1).max(50).optional().describe("Default: 30")
5369
+ });
5370
+ var terminalRtcCandidateSchema = z8.object({ type: z8.literal("rtc-candidate"), candidate: z8.string(), mid: z8.string() });
5371
+ var terminalSocketServerMessageSchema = z8.discriminatedUnion("type", [
5372
+ z8.object({ type: z8.literal("output"), data: z8.string() }),
5373
+ z8.object({ type: z8.literal("rtc-answer"), sdp: z8.string() }),
5329
5374
  terminalRtcCandidateSchema,
5330
- z9.object({ type: z9.literal("rtc-switch") })
5375
+ z8.object({ type: z8.literal("rtc-switch") })
5331
5376
  ]);
5332
5377
 
5333
5378
  // src/index.ts
@@ -5363,7 +5408,7 @@ function result(data, isError = false) {
5363
5408
  }
5364
5409
  var server = new McpServer({
5365
5410
  name: "replicas-mcp",
5366
- version: "0.2.20"
5411
+ version: "0.2.21"
5367
5412
  });
5368
5413
  function registerTool(name, config, handler) {
5369
5414
  server.registerTool.bind(server)(name, config, async (args) => {
@@ -5374,21 +5419,21 @@ function registerTool(name, config, handler) {
5374
5419
  }
5375
5420
  });
5376
5421
  }
5377
- var codingAgentSchema = z10.string().refine(isValidAgentProvider, {
5422
+ var codingAgentSchema = z9.string().refine(isValidAgentProvider, {
5378
5423
  message: `Coding agent must be ${getCodingAgentDisplayNames()}`
5379
5424
  });
5380
- var lifecyclePolicySchema = z10.enum(VALID_LIFECYCLE_POLICIES);
5425
+ var lifecyclePolicySchema = z9.enum(VALID_LIFECYCLE_POLICIES);
5381
5426
  registerTool(
5382
5427
  "create_replica",
5383
5428
  {
5384
5429
  description: "Create a new replica workspace and send initial message",
5385
5430
  inputSchema: {
5386
- name: z10.string().regex(/^\S+$/, "Name cannot contain spaces"),
5387
- message: z10.string(),
5388
- repository_ids: z10.array(z10.string()).min(1).optional().describe("Repository IDs to include. Mutually exclusive with repository_set_id."),
5389
- repository_set_id: z10.string().optional().describe("Repository set ID to use. Mutually exclusive with repository_ids."),
5431
+ name: z9.string().regex(/^\S+$/, "Name cannot contain spaces"),
5432
+ message: z9.string(),
5433
+ repository_ids: z9.array(z9.string()).min(1).optional().describe("Repository IDs to include. Mutually exclusive with repository_set_id."),
5434
+ repository_set_id: z9.string().optional().describe("Repository set ID to use. Mutually exclusive with repository_ids."),
5390
5435
  coding_agent: codingAgentSchema.optional().describe("Coding agent to use (default: claude)"),
5391
- model: z10.string().optional().describe("Model to use for the coding agent"),
5436
+ model: z9.string().optional().describe("Model to use for the coding agent"),
5392
5437
  lifecycle_policy: lifecyclePolicySchema.optional().describe("Lifecycle policy for the workspace")
5393
5438
  }
5394
5439
  },
@@ -5410,11 +5455,11 @@ registerTool(
5410
5455
  {
5411
5456
  description: "List all replicas with pagination and optional filters",
5412
5457
  inputSchema: {
5413
- page: z10.number().optional(),
5414
- limit: z10.number().optional(),
5415
- user_id: z10.string().optional().describe("Filter by creator user ID"),
5416
- source: z10.enum(["api", "dashboard", "slack", "linear", "github", "gitlab", "automation"]).optional().describe("Filter by creation source"),
5417
- status: z10.enum(["active", "sleeping", "preparing"]).optional().describe("Filter by workspace status")
5458
+ page: z9.number().optional(),
5459
+ limit: z9.number().optional(),
5460
+ user_id: z9.string().optional().describe("Filter by creator user ID"),
5461
+ source: z9.enum(["api", "dashboard", "slack", "linear", "github", "gitlab", "automation"]).optional().describe("Filter by creation source"),
5462
+ status: z9.enum(["active", "sleeping", "preparing"]).optional().describe("Filter by workspace status")
5418
5463
  }
5419
5464
  },
5420
5465
  async (args) => {
@@ -5433,8 +5478,8 @@ registerTool(
5433
5478
  {
5434
5479
  description: "Get replica details by ID, including current status, chats, and repository statuses",
5435
5480
  inputSchema: {
5436
- id: z10.string(),
5437
- include: z10.array(z10.enum(["environment", "diffs"])).optional().describe("Extra data to include in response")
5481
+ id: z9.string(),
5482
+ include: z9.array(z9.enum(["environment", "diffs"])).optional().describe("Extra data to include in response")
5438
5483
  }
5439
5484
  },
5440
5485
  async (args) => {
@@ -5449,12 +5494,12 @@ registerTool(
5449
5494
  {
5450
5495
  description: "Send a message to an existing replica. Automatically handles waking sleeping replicas.",
5451
5496
  inputSchema: {
5452
- id: z10.string(),
5453
- message: z10.string(),
5497
+ id: z9.string(),
5498
+ message: z9.string(),
5454
5499
  coding_agent: codingAgentSchema.optional().describe("Coding agent to use"),
5455
- chat_id: z10.string().optional().describe("Chat ID to send the message to (uses default chat if omitted)"),
5456
- model: z10.string().optional().describe("Model to use"),
5457
- plan_mode: z10.boolean().optional().describe("Whether to use plan mode")
5500
+ chat_id: z9.string().optional().describe("Chat ID to send the message to (uses default chat if omitted)"),
5501
+ model: z9.string().optional().describe("Model to use"),
5502
+ plan_mode: z9.boolean().optional().describe("Whether to use plan mode")
5458
5503
  }
5459
5504
  },
5460
5505
  async (args) => {
@@ -5471,7 +5516,7 @@ registerTool(
5471
5516
  {
5472
5517
  description: "Delete a replica. This terminates the workspace and cannot be undone.",
5473
5518
  inputSchema: {
5474
- id: z10.string()
5519
+ id: z9.string()
5475
5520
  }
5476
5521
  },
5477
5522
  (args) => api("DELETE", `/v1/replica/${args.id}`)
@@ -5495,7 +5540,7 @@ registerTool(
5495
5540
  {
5496
5541
  description: "List chats in a replica workspace",
5497
5542
  inputSchema: {
5498
- id: z10.string().describe("Replica ID")
5543
+ id: z9.string().describe("Replica ID")
5499
5544
  }
5500
5545
  },
5501
5546
  (args) => api("GET", `/v1/replica/${args.id}/chats`)
@@ -5505,9 +5550,9 @@ registerTool(
5505
5550
  {
5506
5551
  description: "Create a new chat in a replica workspace",
5507
5552
  inputSchema: {
5508
- id: z10.string().describe("Replica ID"),
5553
+ id: z9.string().describe("Replica ID"),
5509
5554
  provider: codingAgentSchema.describe("Coding agent provider for the chat"),
5510
- title: z10.string().optional().describe("Optional title for the chat")
5555
+ title: z9.string().optional().describe("Optional title for the chat")
5511
5556
  }
5512
5557
  },
5513
5558
  async (args) => {
@@ -5521,8 +5566,8 @@ registerTool(
5521
5566
  {
5522
5567
  description: "Delete a chat from a replica workspace",
5523
5568
  inputSchema: {
5524
- id: z10.string().describe("Replica ID"),
5525
- chat_id: z10.string().describe("Chat ID to delete")
5569
+ id: z9.string().describe("Replica ID"),
5570
+ chat_id: z9.string().describe("Chat ID to delete")
5526
5571
  }
5527
5572
  },
5528
5573
  (args) => api("DELETE", `/v1/replica/${args.id}/chats/${args.chat_id}`)
@@ -5532,8 +5577,8 @@ registerTool(
5532
5577
  {
5533
5578
  description: "Read conversation history of a replica. Returns parsed DisplayMessage objects (user messages, agent responses, commands, file changes, tool calls, etc). If the workspace is sleeping, it will be woken and you should retry in 30-90 seconds.",
5534
5579
  inputSchema: {
5535
- id: z10.string(),
5536
- chat_id: z10.string().optional().describe("Read history for a specific chat (uses default chat if omitted)")
5580
+ id: z9.string(),
5581
+ chat_id: z9.string().optional().describe("Read history for a specific chat (uses default chat if omitted)")
5537
5582
  }
5538
5583
  },
5539
5584
  async (args) => {
@@ -5541,12 +5586,6 @@ registerTool(
5541
5586
  if (args.chat_id) params.set("chat_id", args.chat_id);
5542
5587
  const q = params.toString();
5543
5588
  const response = await api("GET", `/v1/replica/${args.id}/history${q ? "?" + q : ""}`);
5544
- if (response.waking) {
5545
- return {
5546
- status: "waking",
5547
- message: "Workspace is waking from sleep. Retry in 30-90 seconds."
5548
- };
5549
- }
5550
5589
  const agentType = response.coding_agent || "claude";
5551
5590
  const messages = parseAgentEvents(response.events, agentType);
5552
5591
  return {
@@ -5563,7 +5602,7 @@ registerTool(
5563
5602
  {
5564
5603
  description: "Get warm and start hook logs for a replica workspace",
5565
5604
  inputSchema: {
5566
- id: z10.string().describe("Replica ID")
5605
+ id: z9.string().describe("Replica ID")
5567
5606
  }
5568
5607
  },
5569
5608
  (args) => api("GET", `/v1/replica/${args.id}/hooks`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-mcp",
3
- "version": "0.2.20",
3
+ "version": "0.2.21",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -78,7 +78,7 @@ type RegisterToolFn = (
78
78
 
79
79
  const server = new McpServer({
80
80
  name: 'replicas-mcp',
81
- version: '0.2.20',
81
+ version: '0.2.21',
82
82
  });
83
83
 
84
84
  function registerTool(
@@ -290,13 +290,6 @@ registerTool(
290
290
  const q = params.toString();
291
291
  const response = await api<ReadReplicaHistoryResponse>('GET', `/v1/replica/${args.id}/history${q ? '?' + q : ''}`);
292
292
 
293
- if (response.waking) {
294
- return {
295
- status: 'waking',
296
- message: 'Workspace is waking from sleep. Retry in 30-90 seconds.',
297
- };
298
- }
299
-
300
293
  const agentType = (response.coding_agent || 'claude') as AgentProvider;
301
294
  const messages = parseAgentEvents(response.events, agentType);
302
295