replicas-mcp 0.2.12 → 0.2.13

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 +560 -133
  2. package/package.json +1 -1
  3. package/src/index.ts +1 -1
package/dist/index.js CHANGED
@@ -3,13 +3,15 @@
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 z2 } from "zod";
6
+ import { z as z3 } from "zod";
7
7
 
8
8
  // ../shared/src/agent.ts
9
9
  var AGENT = {
10
10
  CLAUDE: "claude",
11
11
  CODEX: "codex",
12
12
  CURSOR: "cursor",
13
+ DEEPSEEK: "deepseek",
14
+ FX: "fx",
13
15
  OPENCODE: "opencode",
14
16
  PI: "pi",
15
17
  RELAY: "relay"
@@ -28,6 +30,8 @@ var THINKING_LEVELS_BY_AGENT = {
28
30
  claude: [...STANDARD_THINKING_LEVELS, "ultracode"],
29
31
  codex: [...STANDARD_THINKING_LEVELS, "ultra"],
30
32
  cursor: STANDARD_THINKING_LEVELS,
33
+ deepseek: STANDARD_THINKING_LEVELS,
34
+ fx: STANDARD_THINKING_LEVELS,
31
35
  opencode: STANDARD_THINKING_LEVELS,
32
36
  pi: STANDARD_THINKING_LEVELS,
33
37
  relay: [...STANDARD_THINKING_LEVELS, "ultracode"]
@@ -41,6 +45,10 @@ function getProviderDisplayName(provider, variant = "short") {
41
45
  return "Codex";
42
46
  case "cursor":
43
47
  return "Cursor";
48
+ case "deepseek":
49
+ return variant === "long" ? "DeepSeek Harness" : "DeepSeek";
50
+ case "fx":
51
+ return "fx";
44
52
  case "opencode":
45
53
  return "Opencode";
46
54
  case "pi":
@@ -55,6 +63,9 @@ function getCodingAgentDisplayNames() {
55
63
  return VALID_CODING_AGENT_PROVIDERS.map((provider) => getProviderDisplayName(provider)).join(", ");
56
64
  }
57
65
 
66
+ // ../shared/src/analytics/types.ts
67
+ import { z } from "zod";
68
+
58
69
  // ../shared/src/credentials/types.ts
59
70
  var CREDENTIAL_SCOPE = {
60
71
  USER: "user",
@@ -76,7 +87,8 @@ var CREDENTIAL_METHOD = {
76
87
  BEDROCK: "bedrock",
77
88
  OPENCODE_GO: "opencode-go",
78
89
  ASTER: "aster",
79
- OPENROUTER: "openrouter"
90
+ OPENROUTER: "openrouter",
91
+ AI_GATEWAY: "ai-gateway"
80
92
  };
81
93
  var CREDENTIAL_TYPE = {
82
94
  CLAUDE_OAUTH: "claude",
@@ -89,7 +101,8 @@ var CREDENTIAL_TYPE = {
89
101
  CURSOR_API_KEY: "cursor_api_key",
90
102
  OPENCODE_GO_API_KEY: "opencode_go_api_key",
91
103
  ASTER_API_KEY: "aster_api_key",
92
- OPENROUTER_API_KEY: "openrouter_api_key"
104
+ OPENROUTER_API_KEY: "openrouter_api_key",
105
+ AI_GATEWAY_API_KEY: "ai_gateway_api_key"
93
106
  };
94
107
  var AGENT_CREDENTIAL_METHODS_IN_ORDER = {
95
108
  [AGENT.CLAUDE]: [
@@ -106,6 +119,12 @@ var AGENT_CREDENTIAL_METHODS_IN_ORDER = {
106
119
  [AGENT.CURSOR]: [
107
120
  { method: CREDENTIAL_METHOD.API_KEY, credentialType: CREDENTIAL_TYPE.CURSOR_API_KEY }
108
121
  ],
122
+ [AGENT.DEEPSEEK]: [
123
+ { method: CREDENTIAL_METHOD.OPENROUTER, credentialType: CREDENTIAL_TYPE.OPENROUTER_API_KEY }
124
+ ],
125
+ [AGENT.FX]: [
126
+ { method: CREDENTIAL_METHOD.AI_GATEWAY, credentialType: CREDENTIAL_TYPE.AI_GATEWAY_API_KEY }
127
+ ],
109
128
  [AGENT.OPENCODE]: [
110
129
  { method: CREDENTIAL_METHOD.OPENCODE_GO, credentialType: CREDENTIAL_TYPE.OPENCODE_GO_API_KEY },
111
130
  { method: CREDENTIAL_METHOD.ASTER, credentialType: CREDENTIAL_TYPE.ASTER_API_KEY },
@@ -117,6 +136,13 @@ var AGENT_CREDENTIAL_METHODS_IN_ORDER = {
117
136
  ]
118
137
  };
119
138
 
139
+ // ../shared/src/analytics/types.ts
140
+ var agentCredentialSnapshotSchema = z.object({
141
+ method: z.enum(CREDENTIAL_METHOD),
142
+ scope: z.enum(CREDENTIAL_SCOPE),
143
+ revision: z.string().optional()
144
+ });
145
+
120
146
  // ../shared/src/event.ts
121
147
  var CLAUDE_PARTIAL_MESSAGE_EVENT_TYPE = "claude-partial-message";
122
148
  function coerceClaudePartialMessagePayload(payload) {
@@ -138,6 +164,37 @@ var USER_MESSAGE_ID_PAYLOAD_KEY = "replicasMessageId";
138
164
  var CODEX_ASP_ITEM_ID_PAYLOAD_KEY = "codexAspItemId";
139
165
  var CODEX_QUOTA_STATUS_EVENT_TYPE = "codex-quota-status";
140
166
 
167
+ // ../shared/src/urls.ts
168
+ var HOME_PATH = "/home";
169
+ var DASHBOARD_PATH = "/dashboard";
170
+ var WEB_APP_PATHS = {
171
+ home: HOME_PATH,
172
+ onboarding: `${HOME_PATH}/onboarding`,
173
+ workspaces: `${HOME_PATH}/workspace`,
174
+ automations: `${HOME_PATH}/automations`,
175
+ environment: `${HOME_PATH}/environment`,
176
+ analytics: `${HOME_PATH}/analytics`,
177
+ dashboard: DASHBOARD_PATH,
178
+ agents: `${DASHBOARD_PATH}/agents`,
179
+ integrations: `${DASHBOARD_PATH}/integrations`,
180
+ integrationsGithub: `${DASHBOARD_PATH}/integrations/github`,
181
+ integrationsGitlab: `${DASHBOARD_PATH}/integrations/gitlab`,
182
+ settings: `${DASHBOARD_PATH}/settings`,
183
+ billing: `${DASHBOARD_PATH}/billing`,
184
+ members: `${DASHBOARD_PATH}/members`,
185
+ "audit-logs": `${DASHBOARD_PATH}/audit-logs`,
186
+ accountAgents: `${DASHBOARD_PATH}/account/agents`,
187
+ accountProfile: `${DASHBOARD_PATH}/account/profile`,
188
+ accountIntegrations: `${DASHBOARD_PATH}/account/integrations`,
189
+ accountApiKeys: `${DASHBOARD_PATH}/account/api-keys`,
190
+ accountPreferences: `${DASHBOARD_PATH}/account/preferences`,
191
+ fullscreenWorkspaces: "/workspaces"
192
+ };
193
+ var LEGACY_WEB_APP_PATHS = {
194
+ app: "/app",
195
+ dashboardWorkspaces: `${DASHBOARD_PATH}/workspaces`
196
+ };
197
+
141
198
  // ../shared/src/default-skills/replicas-agent/abilities/computer.ts
142
199
  var SECTION = `### Computer use (Linux desktop control)
143
200
  Drive the workspace's Linux desktop - open a browser, click, type, scroll, screenshot, record - via the \`replicas computer\` CLI. Every Replicas workspace boots with a live H.264 and Opus stream published as an authenticated preview, so a \`Desktop\` tab is always available in the dashboard. Use \`replicas computer info\` to get the viewer URL when you want to share it (e.g. point a user on Slack at the live stream).
@@ -615,35 +672,8 @@ var GITLAB_ABILITY = {
615
672
  referenceFile: { name: "GITLAB.md", content: REFERENCE4 }
616
673
  };
617
674
 
618
- // ../shared/src/default-skills/replicas-agent/abilities/learnings.ts
619
- var SECTION5 = `### Learnings
620
- Propose additions, updates, or deletions to curated organization knowledge.
621
-
622
- **Reference:** \`references/LEARNINGS.md\``;
623
- var REFERENCE5 = `# Learnings
624
-
625
- Learnings are curated, human-approved knowledge entries your organization maintains: conventions, gotchas, environment facts, and process rules.
626
-
627
- All writes are proposals: a human reviews them in the dashboard, and nothing takes effect until approved.
628
-
629
- Use \`replicas learnings --help\` and subcommand help such as \`replicas learnings add --help\`, \`replicas learnings update --help\`, or \`replicas learnings delete --help\` for current command syntax.
630
-
631
- A human approves every proposal before it takes effect, so don't agonize over borderline calls \u2014 but keep proposals genuinely useful rather than reflexive.
632
-
633
- - Propose \`add\` for anything repeatable and org-useful: user corrections and preferences, hard-won environment or process knowledge, conventions and decisions worth following, and gotchas that would waste time again. If you learned it the hard way this task, capture it.
634
- - Never store task-specific details that won't recur, facts trivially derivable by reading the repo, or secrets/credentials.
635
- - Propose \`update\` or \`delete\` when a learning is contradicted by the current code or docs, especially if the outdated guidance would affect future repeated work.
636
- - Write narrow triggers describing when the entry should surface (e.g. "UI/UX work on the admin panel"), not vague topics.`;
637
- var LEARNINGS_ABILITY = {
638
- label: "Learnings",
639
- description: "Propose changes to curated org knowledge for human review.",
640
- bullet: "- Proposing changes to organization Learnings (curated, human-approved knowledge)",
641
- section: SECTION5,
642
- referenceFile: { name: "LEARNINGS.md", content: REFERENCE5 }
643
- };
644
-
645
675
  // ../shared/src/default-skills/replicas-agent/abilities/linear.ts
646
- var SECTION6 = `### Linear
676
+ var SECTION5 = `### Linear
647
677
  Fetch issues, update state, add comments, and search through the workspace-scoped TypeScript SDK.
648
678
 
649
679
  **Reference:** \`references/LINEAR.md\`
@@ -652,7 +682,7 @@ Use this when:
652
682
  - You encounter a Linear issue link and need to understand the task
653
683
  - You need to update an issue's state or add a comment
654
684
  - You need to search Linear issues`;
655
- var REFERENCE6 = `# Linear Integration
685
+ var REFERENCE5 = `# Linear Integration
656
686
 
657
687
  Use \`@replicas/sdk\`; never request or handle a Linear OAuth token. Replicas refreshes the organization credential behind the gateway.
658
688
 
@@ -702,12 +732,12 @@ var LINEAR_ABILITY = {
702
732
  label: "Linear",
703
733
  description: "Fetch issues, comment, and update state through @replicas/sdk.",
704
734
  bullet: "- Interacting with Linear (fetching issues, updating state, commenting, etc.)",
705
- section: SECTION6,
706
- referenceFile: { name: "LINEAR.md", content: REFERENCE6 }
735
+ section: SECTION5,
736
+ referenceFile: { name: "LINEAR.md", content: REFERENCE5 }
707
737
  };
708
738
 
709
739
  // ../shared/src/default-skills/replicas-agent/abilities/media.ts
710
- var SECTION7 = `### Media
740
+ var SECTION6 = `### Media
711
741
  Share screenshots, screen recordings, generated diagrams, audio clips, and self-contained HTML pages through Replicas and external messages.
712
742
 
713
743
  **Reference:** \`references/MEDIA.md\`
@@ -717,7 +747,7 @@ Use this when:
717
747
  - You produce a self-contained HTML report that should be linked from a pull request
718
748
  - You record video output (browser automation, screen capture) \u2014 including the recommended aspect ratio and FPS
719
749
  - You need to embed media in a Slack/Linear/GitHub message AND keep a referenceable copy in the Replicas dashboard`;
720
- var REFERENCE7 = `# Media (Screenshots, Recordings, Audio, HTML)
750
+ var REFERENCE6 = `# Media (Screenshots, Recordings, Audio, HTML)
721
751
 
722
752
  This guide covers how to share screenshots, screen recordings, generated diagrams, audio clips, and self-contained HTML pages through Replicas and external surfaces (Slack, Linear, GitHub).
723
753
 
@@ -1023,12 +1053,12 @@ var MEDIA_ABILITY = {
1023
1053
  label: "Media",
1024
1054
  description: "Share screenshots, recordings, generated images, audio clips, and HTML pages.",
1025
1055
  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",
1026
- section: SECTION7,
1027
- referenceFile: { name: "MEDIA.md", content: REFERENCE7 }
1056
+ section: SECTION6,
1057
+ referenceFile: { name: "MEDIA.md", content: REFERENCE6 }
1028
1058
  };
1029
1059
 
1030
1060
  // ../shared/src/default-skills/replicas-agent/abilities/previews.ts
1031
- var SECTION8 = `### Previews
1061
+ var SECTION7 = `### Previews
1032
1062
  Expose locally running services (web apps, APIs, databases) as public preview URLs so humans can interact with them directly.
1033
1063
 
1034
1064
  **Reference:** \`references/PREVIEWS.md\`
@@ -1037,7 +1067,7 @@ Use this when:
1037
1067
  - You need to start a service that a human should view or interact with
1038
1068
  - The task involves UI work that benefits from human review
1039
1069
  - You are verifying frontend/backend integrations visually`;
1040
- var REFERENCE8 = `# Preview URLs
1070
+ var REFERENCE7 = `# Preview URLs
1041
1071
 
1042
1072
  When you run services on ports \u2014 such as a web app, API server, or database \u2014 humans may want to interact with them directly. You can expose your locally running services as public preview URLs.
1043
1073
 
@@ -1113,12 +1143,12 @@ var PREVIEWS_ABILITY = {
1113
1143
  label: "Previews",
1114
1144
  description: "Expose locally running services on public preview URLs for humans.",
1115
1145
  bullet: "- Creating preview URLs for locally running services",
1116
- section: SECTION8,
1117
- referenceFile: { name: "PREVIEWS.md", content: REFERENCE8 }
1146
+ section: SECTION7,
1147
+ referenceFile: { name: "PREVIEWS.md", content: REFERENCE7 }
1118
1148
  };
1119
1149
 
1120
1150
  // ../shared/src/default-skills/replicas-agent/abilities/plugins.ts
1121
- var SECTION9 = `### Plugins
1151
+ var SECTION8 = `### Plugins
1122
1152
  Discover and execute installed marketplace tools through the workspace-scoped TypeScript SDK.
1123
1153
 
1124
1154
  **Reference:** \`references/PLUGINS.md\`
@@ -1128,10 +1158,15 @@ Use this when:
1128
1158
  - The user asks to manage Calendly events, invitees, or scheduling links
1129
1159
  - The user asks to read or change Google Ads, Docs, Drive, Search Console, or Sheets data
1130
1160
  - The user asks for ClickHouse analytics or PostHog product data
1161
+ - The user asks to create or control an E2B sandbox
1162
+ - The user asks to invoke a connected Modal function
1163
+ - The user asks to query or update a connected turbopuffer namespace
1164
+ - The user asks for PlanetScale organizations, databases, branches, or Insights data
1165
+ - The user asks to analyze text with Pangram
1131
1166
  - The user asks for Stripe customers, payments, invoices, subscriptions, or balances`;
1132
- var REFERENCE9 = `# Plugins
1167
+ var REFERENCE8 = `# Plugins
1133
1168
 
1134
- Plugins are TypeScript libraries, not MCP servers. Import \`@replicas/sdk\`; Replicas authenticates the workspace and selects only the owner\u2019s personal connection or the organization fallback. Composio credentials, sessions, and project keys are never exposed.
1169
+ Plugins are TypeScript libraries, not MCP servers. Import \`@replicas/sdk\`; Replicas authenticates the workspace and selects only the owner\u2019s personal connection or the organization fallback. Provider credentials, sessions, and project keys are never exposed.
1135
1170
 
1136
1171
  Plugin tools may read or write provider data according to the connected account's permissions. Only execute write or destructive tools when they match the user's request.
1137
1172
 
@@ -1181,17 +1216,80 @@ const results = await Promise.all(customerIds.map((customer) =>
1181
1216
  \`\`\`
1182
1217
 
1183
1218
  Never attempt to import Composio, create a session, manage a connection, or call a provider with raw credentials. Those operations are intentionally unavailable inside the workspace.
1219
+
1220
+ ## PlanetScale
1221
+
1222
+ PlanetScale is a native OAuth integration. Pass a relative path from the PlanetScale v1 API; Replicas refreshes access tokens server-side:
1223
+
1224
+ \`\`\`ts
1225
+ const organizations = await replicas.planetscale.request('/organizations');
1226
+ const databases = await replicas.planetscale.request('/organizations/acme/databases');
1227
+ \`\`\`
1228
+
1229
+ OAuth scopes control permitted operations. Confirm mutations before using POST, PUT, PATCH, or DELETE.
1230
+
1231
+ ## Modal
1232
+
1233
+ Modal is a native integration and uses its official JavaScript SDK behind the Replicas gateway. Invoke a deployed function by name:
1234
+
1235
+ \`\`\`ts
1236
+ const result = await replicas.modal.call({
1237
+ app: 'my-app',
1238
+ function: 'my-function',
1239
+ args: ['input'],
1240
+ environment: 'main',
1241
+ });
1242
+ \`\`\`
1243
+
1244
+ Function calls execute with the connected Modal token and may mutate external state. Confirm the requested action before invoking a function with side effects.
1245
+
1246
+ ## turbopuffer
1247
+
1248
+ turbopuffer is a native integration backed by its official TypeScript SDK. Use the provider-shaped helpers:
1249
+
1250
+ \`\`\`ts
1251
+ const namespaces = await replicas.turbopuffer.namespaces({ prefix: 'products' });
1252
+ const results = await replicas.turbopuffer.query('products', {
1253
+ rank_by: ['text', 'BM25', 'wireless headphones'],
1254
+ top_k: 10,
1255
+ });
1256
+ \`\`\`
1257
+
1258
+ \`write\` mutates namespace data. Confirm the requested change before calling it.
1259
+
1260
+ ## Pangram
1261
+
1262
+ Pangram is a native integration. Text sent to either method is processed by Pangram and consumes the connected account's credits:
1263
+
1264
+ \`\`\`ts
1265
+ const detection = await replicas.pangram.detect({ text });
1266
+ const plagiarism = await replicas.pangram.plagiarism(text);
1267
+ \`\`\`
1268
+
1269
+ Do not send private or sensitive text without the user's authorization.
1270
+
1271
+ ## E2B
1272
+
1273
+ E2B is a native integration backed by its official JavaScript SDK. Create a sandbox, run commands, and remove it when finished:
1274
+
1275
+ \`\`\`ts
1276
+ const { sandboxId } = await replicas.e2b.create({ timeoutMs: 300_000 });
1277
+ const result = await replicas.e2b.run(sandboxId, 'python analysis.py', { cwd: '/home/user' });
1278
+ await replicas.e2b.kill(sandboxId);
1279
+ \`\`\`
1280
+
1281
+ Sandbox operations consume the connected account's E2B credits. Kill sandboxes after use unless the user asked to keep one running.
1184
1282
  `;
1185
1283
  var PLUGINS_ABILITY = {
1186
1284
  label: "Plugins",
1187
1285
  description: "Use installed marketplace tools through @replicas/sdk.",
1188
1286
  bullet: "- Interacting with installed marketplace plugins through TypeScript",
1189
- section: SECTION9,
1190
- referenceFile: { name: "PLUGINS.md", content: REFERENCE9 }
1287
+ section: SECTION8,
1288
+ referenceFile: { name: "PLUGINS.md", content: REFERENCE8 }
1191
1289
  };
1192
1290
 
1193
1291
  // ../shared/src/default-skills/replicas-agent/abilities/replicas.ts
1194
- var SECTION10 = `### Replicas (in-workspace CLI)
1292
+ var SECTION9 = `### Replicas (in-workspace CLI)
1195
1293
  Take action *with* Replicas itself \u2014 manage automations, environments (variables, files), repos, and \`replicas.json\` config \u2014 using the pre-installed, pre-authenticated \`replicas\` CLI.
1196
1294
 
1197
1295
  **Reference:** \`references/REPLICAS.md\`
@@ -1202,7 +1300,7 @@ Use this when:
1202
1300
  - The user asks "what envs / repos / automations do I have?"
1203
1301
  - The user asks you to scaffold a \`replicas.json\` / \`replicas.yaml\` in a repo
1204
1302
  - You need to run a long-lived service (dev server, daemon) \u2014 always use \`replicas service start\` so it survives workspace sleep/wake`;
1205
- var REFERENCE10 = `# Replicas (in-workspace CLI)
1303
+ var REFERENCE9 = `# Replicas (in-workspace CLI)
1206
1304
 
1207
1305
  This guide covers how to take action *with* Replicas itself from inside a Replicas workspace \u2014 managing automations, environments (and their variables/files), repos, previews, and the user's \`replicas.json\` config \u2014 using the pre-installed \`replicas\` CLI.
1208
1306
 
@@ -1429,13 +1527,13 @@ var REPLICAS_ABILITY = {
1429
1527
  description: "Teach the agent about Replicas itself \u2014 automations, environments, the in-workspace CLI.",
1430
1528
  // No bullet — help_instructions covers the `replicas` CLI surface in detail.
1431
1529
  bullet: "",
1432
- section: SECTION10,
1433
- referenceFile: { name: "REPLICAS.md", content: REFERENCE10 },
1530
+ section: SECTION9,
1531
+ referenceFile: { name: "REPLICAS.md", content: REFERENCE9 },
1434
1532
  locked: true
1435
1533
  };
1436
1534
 
1437
1535
  // ../shared/src/default-skills/replicas-agent/abilities/sentry.ts
1438
- var SECTION11 = `### Sentry
1536
+ var SECTION10 = `### Sentry
1439
1537
  Read issues and project data through the workspace-scoped TypeScript SDK.
1440
1538
 
1441
1539
  **Reference:** \`references/SENTRY.md\`
@@ -1443,7 +1541,7 @@ Read issues and project data through the workspace-scoped TypeScript SDK.
1443
1541
  Use this when:
1444
1542
  - You need to inspect Sentry projects or issues
1445
1543
  - A user asks for error-monitoring context outside a webhook-triggered task`;
1446
- var REFERENCE11 = `# Sentry Integration
1544
+ var REFERENCE10 = `# Sentry Integration
1447
1545
 
1448
1546
  Use \`@replicas/sdk\`; never request or read a Sentry token. Sentry access is organization-scoped and read-only.
1449
1547
 
@@ -1462,12 +1560,12 @@ var SENTRY_ABILITY = {
1462
1560
  label: "Sentry",
1463
1561
  description: "Read Sentry projects and issues through @replicas/sdk.",
1464
1562
  bullet: "- Interacting with Sentry (reading projects, issues, and error context)",
1465
- section: SECTION11,
1466
- referenceFile: { name: "SENTRY.md", content: REFERENCE11 }
1563
+ section: SECTION10,
1564
+ referenceFile: { name: "SENTRY.md", content: REFERENCE10 }
1467
1565
  };
1468
1566
 
1469
1567
  // ../shared/src/default-skills/replicas-agent/abilities/slack.ts
1470
- var SECTION12 = `### Slack
1568
+ var SECTION11 = `### Slack
1471
1569
  Send messages, read threads, search conversations, and upload files through the workspace-scoped TypeScript SDK.
1472
1570
 
1473
1571
  **Reference:** \`references/SLACK.md\`
@@ -1476,7 +1574,7 @@ Use this when:
1476
1574
  - You need to send a message to a Slack channel or thread
1477
1575
  - You need to read or search Slack conversations
1478
1576
  - You encounter a Slack message link and need its full thread`;
1479
- var REFERENCE12 = `# Slack Integration
1577
+ var REFERENCE11 = `# Slack Integration
1480
1578
 
1481
1579
  Use \`@replicas/sdk\`; never read or send a Slack token directly. The SDK calls Replicas, which selects only this workspace's organization connection.
1482
1580
 
@@ -1511,7 +1609,7 @@ const message = await replicas.slack.call<{ channel: string; ts: string }>('chat
1511
1609
  await replicas.slack.attachThread({ channel: message.channel, threadTs: message.ts });
1512
1610
  \`\`\`
1513
1611
 
1514
- A link in the message text stays plain text, so when the message carries a pull request or merge request link, pass \`blocks\` alongside \`text\` to render a button for it. Keep \`text\` set to the same message \u2014 Slack uses it for notifications and fallback.
1612
+ A link in the message text stays plain text, so the message that first shares a pull request or merge request link passes \`blocks\` alongside \`text\` to render a button for it. Keep \`text\` set to the same message \u2014 Slack uses it for notifications and fallback.
1515
1613
 
1516
1614
  \`\`\`ts
1517
1615
  await replicas.slack.call('chat.postMessage', {
@@ -1523,14 +1621,14 @@ await replicas.slack.call('chat.postMessage', {
1523
1621
  {
1524
1622
  type: 'actions',
1525
1623
  elements: [
1526
- { type: 'button', text: { type: 'plain_text', text: 'View PR' }, url: prUrl, action_id: 'view_pr' },
1624
+ { type: 'button', style: 'primary', text: { type: 'plain_text', text: 'View PR' }, url: prUrl, action_id: 'view_pr' },
1527
1625
  ],
1528
1626
  },
1529
1627
  ],
1530
1628
  });
1531
1629
  \`\`\`
1532
1630
 
1533
- Use "View MR" for GitLab. With several links, give each button a unique \`action_id\` and name its repository in the label ("View PR \xB7 web").
1631
+ Only the announcing message gets the button \u2014 follow-ups about a pull request already linked in the thread keep the link in the text and drop the \`blocks\`. Use "View MR" for GitLab. With several links, give each button a unique \`action_id\` and name its repository in the label ("View PR \xB7 web").
1534
1632
 
1535
1633
  Attach every new top-level conversation so future replies route to this workspace. For a reply in another thread, attach its root \`thread_ts\`, not the reply timestamp. Do not attach the originating thread again.
1536
1634
 
@@ -1549,8 +1647,8 @@ var SLACK_ABILITY = {
1549
1647
  label: "Slack",
1550
1648
  description: "Send messages, read threads, and search through @replicas/sdk.",
1551
1649
  bullet: "- Interacting with Slack (sending messages, reading threads, etc.)",
1552
- section: SECTION12,
1553
- referenceFile: { name: "SLACK.md", content: REFERENCE12 }
1650
+ section: SECTION11,
1651
+ referenceFile: { name: "SLACK.md", content: REFERENCE11 }
1554
1652
  };
1555
1653
 
1556
1654
  // ../shared/src/default-skills/replicas-agent/registry.ts
@@ -1560,7 +1658,6 @@ var REPLICAS_AGENT_ABILITY_REGISTRY = {
1560
1658
  docker: DOCKER_ABILITY,
1561
1659
  github: GITHUB_ABILITY,
1562
1660
  gitlab: GITLAB_ABILITY,
1563
- learnings: LEARNINGS_ABILITY,
1564
1661
  linear: LINEAR_ABILITY,
1565
1662
  media: MEDIA_ABILITY,
1566
1663
  plugins: PLUGINS_ABILITY,
@@ -1654,12 +1751,23 @@ var GPT_5_6_TERRA_MODEL = "gpt-5.6-terra";
1654
1751
  var GPT_5_6_LUNA_MODEL = "gpt-5.6-luna";
1655
1752
  var DEFAULT_CODEX_MODEL = GPT_5_6_SOL_MODEL;
1656
1753
  var DEFAULT_OPENCODE_MODEL = "z-ai/glm-5.2";
1754
+ var DEEPSEEK_V4_PRO_MODEL = "deepseek/deepseek-v4-pro-0813";
1755
+ var DEEPSEEK_V4_FLASH_MODEL = "deepseek/deepseek-v4-flash-0731";
1756
+ var DEFAULT_FX_MODEL = "zai/glm-5.2";
1657
1757
  var OPENROUTER_MODELS = [
1658
1758
  DEFAULT_OPENCODE_MODEL,
1759
+ DEEPSEEK_V4_PRO_MODEL,
1760
+ DEEPSEEK_V4_FLASH_MODEL,
1659
1761
  "minimax/minimax-m3",
1660
1762
  "xiaomi/mimo-v2.5-pro",
1661
1763
  "moonshotai/kimi-k2.6"
1662
1764
  ];
1765
+ function prioritizeDeepseekModels(models) {
1766
+ return [
1767
+ ...models.filter((model) => model.startsWith("deepseek/")),
1768
+ ...models.filter((model) => !model.startsWith("deepseek/"))
1769
+ ];
1770
+ }
1663
1771
  var CLAUDE_MODEL_CONTEXT_WINDOWS = {
1664
1772
  [CLAUDE_FABLE_5_MODEL]: 1e6,
1665
1773
  [CLAUDE_OPUS_5_MODEL]: 1e6,
@@ -1709,11 +1817,14 @@ var AGENT_MODELS = {
1709
1817
  "gpt-5-mini",
1710
1818
  "gemini-3.1-pro",
1711
1819
  "gemini-3-flash",
1820
+ "grok-4.6",
1712
1821
  "grok-4.5",
1713
1822
  "grok-4.3",
1714
1823
  CLAUDE_HAIKU_4_5_MODEL,
1715
1824
  "kimi-k2.5"
1716
1825
  ],
1826
+ deepseek: prioritizeDeepseekModels(OPENROUTER_MODELS),
1827
+ fx: [DEFAULT_FX_MODEL],
1717
1828
  opencode: [...OPENROUTER_MODELS, ...ASTER_MODELS],
1718
1829
  pi: [...OPENROUTER_MODELS, ...ASTER_MODELS],
1719
1830
  relay: [CLAUDE_FABLE_5_MODEL, CLAUDE_OPUS_5_MODEL, CLAUDE_OPUS_4_8_MODEL, CLAUDE_SONNET_5_MODEL]
@@ -1733,6 +1844,8 @@ var MODEL_LABELS = {
1733
1844
  [GPT_5_6_LUNA_MODEL]: "GPT-5.6 Luna",
1734
1845
  "gpt-5.5": "GPT-5.5",
1735
1846
  [DEFAULT_OPENCODE_MODEL]: "GLM 5.2 via OpenRouter",
1847
+ [DEEPSEEK_V4_PRO_MODEL]: "DeepSeek V4 Pro via OpenRouter",
1848
+ [DEEPSEEK_V4_FLASH_MODEL]: "DeepSeek V4 Flash via OpenRouter",
1736
1849
  "minimax/minimax-m3": "MiniMax M3 via OpenRouter",
1737
1850
  "xiaomi/mimo-v2.5-pro": "MiMo V2.5 Pro via OpenRouter",
1738
1851
  "moonshotai/kimi-k2.6": "Kimi K2.6 via OpenRouter",
@@ -1753,6 +1866,7 @@ var MODEL_LABELS = {
1753
1866
  [CLAUDE_HAIKU_4_5_MODEL]: "Haiku 4.5",
1754
1867
  "gemini-3.1-pro": "Gemini 3.1 Pro",
1755
1868
  "gemini-3-flash": "Gemini 3 Flash",
1869
+ "grok-4.6": "Grok 4.6",
1756
1870
  "grok-4.5": "Grok 4.5",
1757
1871
  "grok-4.3": "Grok 4.3",
1758
1872
  "kimi-k2.5": "Kimi K2.5"
@@ -1762,51 +1876,51 @@ var MAX_CANVAS_FILE_BYTES = 25 * 1024 * 1024;
1762
1876
  var MAX_CANVAS_PREVIEW_BYTES = 5 * 1024 * 1024;
1763
1877
 
1764
1878
  // ../shared/src/engine/v1.ts
1765
- import { z } from "zod";
1766
- var createChatRequestSchema = z.object({
1767
- id: z.string().uuid().optional(),
1768
- createdAt: z.string().datetime().optional(),
1769
- provider: z.enum(VALID_AGENT_PROVIDERS),
1770
- title: z.string().min(1).optional(),
1771
- parentChatId: z.string().uuid().optional(),
1772
- clientRequestId: z.string().min(1).max(128).optional()
1879
+ import { z as z2 } from "zod";
1880
+ var createChatRequestSchema = z2.object({
1881
+ id: z2.string().uuid().optional(),
1882
+ createdAt: z2.string().datetime().optional(),
1883
+ provider: z2.enum(VALID_AGENT_PROVIDERS),
1884
+ title: z2.string().min(1).optional(),
1885
+ parentChatId: z2.string().uuid().optional(),
1886
+ clientRequestId: z2.string().min(1).max(128).optional()
1773
1887
  });
1774
- var sendChatMessageRequestSchema = z.object({
1775
- messageId: z.string().optional(),
1776
- submittedAt: z.string().optional(),
1777
- message: z.string(),
1778
- model: z.string().optional(),
1779
- customInstructions: z.string().optional(),
1780
- planMode: z.boolean().optional(),
1781
- images: z.array(z.object({
1782
- type: z.literal("image"),
1783
- source: z.discriminatedUnion("type", [
1784
- z.object({ type: z.literal("base64"), media_type: z.enum(IMAGE_MEDIA_TYPES), data: z.string() }),
1785
- z.object({ type: z.literal("url"), url: z.string() })
1888
+ var sendChatMessageRequestSchema = z2.object({
1889
+ messageId: z2.string().optional(),
1890
+ submittedAt: z2.string().optional(),
1891
+ message: z2.string(),
1892
+ model: z2.string().optional(),
1893
+ customInstructions: z2.string().optional(),
1894
+ planMode: z2.boolean().optional(),
1895
+ images: z2.array(z2.object({
1896
+ type: z2.literal("image"),
1897
+ source: z2.discriminatedUnion("type", [
1898
+ z2.object({ type: z2.literal("base64"), media_type: z2.enum(IMAGE_MEDIA_TYPES), data: z2.string() }),
1899
+ z2.object({ type: z2.literal("url"), url: z2.string() })
1786
1900
  ])
1787
1901
  })).optional(),
1788
- thinkingLevel: z.enum(VALID_THINKING_LEVELS).optional(),
1789
- goalMode: z.boolean().optional(),
1790
- fastMode: z.boolean().optional(),
1791
- enableInteractiveTools: z.boolean().optional(),
1792
- type: z.string().optional(),
1793
- merge: z.boolean().optional(),
1794
- idempotencyKey: z.string().optional(),
1795
- senderUserId: z.string().optional(),
1796
- senderEmail: z.string().optional(),
1797
- senderDisplayName: z.string().optional(),
1798
- senderAvatarUrl: z.string().optional(),
1799
- errorNotificationTarget: z.discriminatedUnion("type", [
1800
- z.object({ type: z.literal("slack") }),
1801
- z.object({ type: z.literal("linear"), sessionId: z.string() }),
1802
- z.object({
1803
- type: z.literal("code_host"),
1804
- provider: z.enum(["github", "gitlab"]),
1805
- resource: z.enum(["issue", "pull_request"]),
1806
- repositoryId: z.string(),
1807
- resourceNumber: z.number()
1902
+ thinkingLevel: z2.enum(VALID_THINKING_LEVELS).optional(),
1903
+ goalMode: z2.boolean().optional(),
1904
+ fastMode: z2.boolean().optional(),
1905
+ enableInteractiveTools: z2.boolean().optional(),
1906
+ type: z2.string().optional(),
1907
+ merge: z2.boolean().optional(),
1908
+ idempotencyKey: z2.string().optional(),
1909
+ senderUserId: z2.string().optional(),
1910
+ senderEmail: z2.string().optional(),
1911
+ senderDisplayName: z2.string().optional(),
1912
+ senderAvatarUrl: z2.string().optional(),
1913
+ errorNotificationTarget: z2.discriminatedUnion("type", [
1914
+ z2.object({ type: z2.literal("slack") }),
1915
+ z2.object({ type: z2.literal("linear"), sessionId: z2.string() }),
1916
+ z2.object({
1917
+ type: z2.literal("code_host"),
1918
+ provider: z2.enum(["github", "gitlab"]),
1919
+ resource: z2.enum(["issue", "pull_request"]),
1920
+ repositoryId: z2.string(),
1921
+ resourceNumber: z2.number()
1808
1922
  }),
1809
- z.object({ type: z.literal("automation"), executionId: z.string() })
1923
+ z2.object({ type: z2.literal("automation"), executionId: z2.string() })
1810
1924
  ]).optional()
1811
1925
  }).passthrough();
1812
1926
 
@@ -2156,6 +2270,15 @@ function areUserMessagesWithinMatchWindow(a, b) {
2156
2270
  return a.content === b.content && Math.abs(parseTimestampMs(a.timestamp) - parseTimestampMs(b.timestamp)) <= USER_MESSAGE_MATCH_GRACE_PERIOD_MS;
2157
2271
  }
2158
2272
 
2273
+ // ../shared/src/agent-event-utils.ts
2274
+ function getUserMessage(event) {
2275
+ return event.type === "event_msg" && event.payload.type === "user_message" && typeof event.payload.message === "string" ? event.payload.message : null;
2276
+ }
2277
+ function getUserMessageId(event) {
2278
+ const messageId = event.payload[USER_MESSAGE_ID_PAYLOAD_KEY];
2279
+ return typeof messageId === "string" ? messageId : null;
2280
+ }
2281
+
2159
2282
  // ../shared/src/display-message/parsers/codex-parser.ts
2160
2283
  function getStatusFromExitCode(exitCode) {
2161
2284
  return exitCode === 0 ? "completed" : "failed";
@@ -2581,6 +2704,230 @@ function parseCursorEvents(events) {
2581
2704
  return messages;
2582
2705
  }
2583
2706
 
2707
+ // ../shared/src/display-message/parsers/deepseek-parser.ts
2708
+ function parseArguments(value) {
2709
+ if (typeof value !== "string") return void 0;
2710
+ const parsed = safeJsonParse(value, value);
2711
+ return isRecord(parsed) ? parsed : stringifyDisplayValue(parsed);
2712
+ }
2713
+ function getDeepseekAssistantMessageText(event, blockType) {
2714
+ if (!isRecord(event) || event.type !== "assistant/message" || !isRecord(event.data) || !isRecord(event.data.message) || !Array.isArray(event.data.message.content)) return "";
2715
+ return event.data.message.content.flatMap((block) => isRecord(block) && block.type === blockType && typeof block.text === "string" ? [block.text] : []).join("");
2716
+ }
2717
+ function parseDeepseekEvents(events) {
2718
+ const messages = [];
2719
+ for (const source of events) {
2720
+ if (source.type === "event_msg" && source.payload.type === "user_message") {
2721
+ if (typeof source.payload.message === "string" && source.payload.message.trim()) {
2722
+ const messageId = source.payload[USER_MESSAGE_ID_PAYLOAD_KEY];
2723
+ messages.push({
2724
+ id: typeof messageId === "string" ? messageId : `deepseek-user-${source.timestamp}`,
2725
+ type: "user",
2726
+ content: source.payload.message,
2727
+ timestamp: source.timestamp
2728
+ });
2729
+ }
2730
+ continue;
2731
+ }
2732
+ if (source.type === "deepseek-error") {
2733
+ messages.push({
2734
+ id: `deepseek-error-${source.timestamp}`,
2735
+ type: "error",
2736
+ message: typeof source.payload.message === "string" ? source.payload.message : "DeepSeek Harness failed",
2737
+ timestamp: source.timestamp
2738
+ });
2739
+ continue;
2740
+ }
2741
+ if (source.type === "deepseek-input-request") {
2742
+ const requestId = typeof source.payload.requestId === "string" ? source.payload.requestId : `deepseek-input-${source.timestamp}`;
2743
+ if (source.payload.kind === "approval") {
2744
+ upsertDisplayMessage(messages, createCallDisplayMessage({
2745
+ id: requestId,
2746
+ server: "deepseek",
2747
+ tool: typeof source.payload.toolName === "string" ? source.payload.toolName : "approval",
2748
+ input: typeof source.payload.reason === "string" ? source.payload.reason : void 0,
2749
+ status: "in_progress",
2750
+ timestamp: source.timestamp,
2751
+ inputRequest: {
2752
+ requestId,
2753
+ status: "pending",
2754
+ options: [
2755
+ { id: "deny", label: "Deny", variant: "secondary" },
2756
+ { id: "allow", label: "Allow once", variant: "primary" }
2757
+ ]
2758
+ }
2759
+ }));
2760
+ } else {
2761
+ const questions = Array.isArray(source.payload.questions) ? source.payload.questions : [];
2762
+ upsertDisplayMessage(messages, createCallDisplayMessage({
2763
+ id: requestId,
2764
+ server: "deepseek",
2765
+ tool: "ask_user",
2766
+ status: "in_progress",
2767
+ timestamp: source.timestamp,
2768
+ inputRequest: {
2769
+ requestId,
2770
+ status: "pending",
2771
+ questions: questions.flatMap((question) => {
2772
+ if (!isRecord(question) || typeof question.id !== "string" || typeof question.question !== "string") return [];
2773
+ const options = Array.isArray(question.options) ? question.options : [];
2774
+ return [{
2775
+ id: question.id,
2776
+ prompt: question.question,
2777
+ multiSelect: question.multiSelect === true,
2778
+ options: options.flatMap((option) => isRecord(option) && typeof option.label === "string" ? [{ id: `${question.id}:${option.label}`, label: option.label, ...typeof option.description === "string" ? { description: option.description } : {} }] : [])
2779
+ }];
2780
+ })
2781
+ }
2782
+ }));
2783
+ }
2784
+ continue;
2785
+ }
2786
+ if (source.type === "deepseek-input-resolved") {
2787
+ const requestId = typeof source.payload.requestId === "string" ? source.payload.requestId : "";
2788
+ const existing = messages.find((message) => message.type === "tool_call" && message.inputRequest?.requestId === requestId);
2789
+ if (existing?.type === "tool_call" && existing.inputRequest) {
2790
+ existing.inputRequest.status = source.payload.status === "aborted" ? "aborted" : "resolved";
2791
+ existing.status = source.payload.status === "aborted" ? "failed" : "completed";
2792
+ }
2793
+ continue;
2794
+ }
2795
+ if (source.type === "deepseek-jobs" && Array.isArray(source.payload.jobs)) {
2796
+ for (const job of source.payload.jobs) {
2797
+ if (!isRecord(job) || typeof job.id !== "string") continue;
2798
+ const existing = messages.find((message) => message.type === "background_task" && message.taskId === job.id);
2799
+ const status = normalizeBackgroundTaskStatus(job.status);
2800
+ const next = {
2801
+ id: existing?.id ?? `deepseek-job-${job.id}`,
2802
+ type: "background_task",
2803
+ taskId: job.id,
2804
+ status,
2805
+ description: typeof job.label === "string" ? job.label : void 0,
2806
+ taskType: typeof job.kind === "string" ? job.kind : void 0,
2807
+ error: status === "failed" && typeof job.detail === "string" ? job.detail : void 0,
2808
+ timestamp: existing?.timestamp ?? source.timestamp
2809
+ };
2810
+ if (existing?.type === "background_task") Object.assign(existing, next);
2811
+ else messages.push(next);
2812
+ }
2813
+ continue;
2814
+ }
2815
+ if (source.type !== "deepseek-session-event" || !isRecord(source.payload.event)) continue;
2816
+ const event = source.payload.event;
2817
+ const data = isRecord(event.data) ? event.data : {};
2818
+ const seq = typeof event.seq === "number" ? event.seq : source.timestamp;
2819
+ if (event.type === "assistant/message") {
2820
+ const content = getDeepseekAssistantMessageText(event, "text");
2821
+ const reasoning = getDeepseekAssistantMessageText(event, "reasoning");
2822
+ if (reasoning.trim()) messages.push({ id: `deepseek-reasoning-${seq}`, type: "reasoning", content: reasoning, status: "completed", timestamp: source.timestamp });
2823
+ if (content.trim()) messages.push({ id: `deepseek-assistant-${seq}`, type: "agent", content, timestamp: source.timestamp });
2824
+ continue;
2825
+ }
2826
+ if (event.type === "tool/call") {
2827
+ const callId = typeof data.callId === "string" ? data.callId : String(seq);
2828
+ upsertDisplayMessage(messages, createCallDisplayMessage({
2829
+ id: `deepseek-tool-${callId}`,
2830
+ server: "deepseek",
2831
+ tool: typeof data.name === "string" ? data.name : "tool",
2832
+ input: parseArguments(data.arguments),
2833
+ status: "in_progress",
2834
+ timestamp: source.timestamp
2835
+ }));
2836
+ continue;
2837
+ }
2838
+ if (event.type === "tool/result") {
2839
+ const message = isRecord(data.message) ? data.message : {};
2840
+ const messageSource = isRecord(message.source) ? message.source : {};
2841
+ const result2 = Array.isArray(message.content) && isRecord(message.content[0]) ? message.content[0] : {};
2842
+ const callId = typeof messageSource.callId === "string" ? messageSource.callId : typeof data.callId === "string" ? data.callId : String(seq);
2843
+ const existing = messages.find((message2) => message2.id === `deepseek-tool-${callId}` && message2.type === "tool_call");
2844
+ if (existing?.type === "tool_call") {
2845
+ existing.output = stringifyDisplayValue(result2.content ?? data.result);
2846
+ existing.status = result2.isError === true || data.isError === true ? "failed" : "completed";
2847
+ }
2848
+ continue;
2849
+ }
2850
+ if (event.type === "todo/write" && Array.isArray(data.items)) {
2851
+ messages.push({
2852
+ id: `deepseek-todo-${seq}`,
2853
+ type: "todo_list",
2854
+ items: data.items.flatMap((item) => {
2855
+ if (!isRecord(item) || typeof item.content !== "string") return [];
2856
+ const itemStatus = item.status === "in_progress" || item.status === "completed" ? item.status : "pending";
2857
+ return [{ text: item.content, completed: itemStatus === "completed", itemStatus }];
2858
+ }),
2859
+ timestamp: source.timestamp
2860
+ });
2861
+ }
2862
+ }
2863
+ return messages;
2864
+ }
2865
+
2866
+ // ../shared/src/display-message/parsers/fx-parser.ts
2867
+ function getFxTextChunk(update, kind) {
2868
+ if (!isRecord(update) || update.sessionUpdate !== kind || !isRecord(update.content) || update.content.type !== "text") return null;
2869
+ return typeof update.content.text === "string" && update.content.text ? update.content.text : null;
2870
+ }
2871
+ function parseFxEvents(events) {
2872
+ const messages = [];
2873
+ for (const event of events) {
2874
+ const content = getUserMessage(event);
2875
+ if (content !== null) {
2876
+ if (content.trim()) {
2877
+ messages.push({
2878
+ id: getUserMessageId(event) ?? `fx-user-${event.timestamp}`,
2879
+ type: "user",
2880
+ content,
2881
+ timestamp: event.timestamp
2882
+ });
2883
+ }
2884
+ continue;
2885
+ }
2886
+ if (event.type === "fx-error") {
2887
+ messages.push({
2888
+ id: `fx-error-${event.timestamp}`,
2889
+ type: "error",
2890
+ message: typeof event.payload.message === "string" ? event.payload.message : "fx failed",
2891
+ timestamp: event.timestamp
2892
+ });
2893
+ continue;
2894
+ }
2895
+ if (event.type === "fx-turn-complete") {
2896
+ for (const message of messages) {
2897
+ if (message.type === "reasoning" && message.status === "in_progress") message.status = "completed";
2898
+ }
2899
+ continue;
2900
+ }
2901
+ if (event.type !== "fx-session-update" || !isRecord(event.payload.update)) continue;
2902
+ const update = event.payload.update;
2903
+ const kind = update.sessionUpdate;
2904
+ if (kind === "agent_message_chunk" || kind === "agent_thought_chunk") {
2905
+ const content2 = getFxTextChunk(update, kind);
2906
+ if (!content2) continue;
2907
+ const id = typeof update.messageId === "string" ? `fx-${update.messageId}` : `fx-${kind}-${event.timestamp}`;
2908
+ const existing = messages.find((message) => message.id === id);
2909
+ const text = `${existing && "content" in existing && typeof existing.content === "string" ? existing.content : ""}${content2}`;
2910
+ upsertDisplayMessage(messages, kind === "agent_thought_chunk" ? { id, type: "reasoning", content: text, status: "in_progress", timestamp: existing?.timestamp ?? event.timestamp } : { id, type: "agent", content: text, timestamp: existing?.timestamp ?? event.timestamp });
2911
+ continue;
2912
+ }
2913
+ if ((kind === "tool_call" || kind === "tool_call_update") && typeof update.toolCallId === "string") {
2914
+ const id = `fx-tool-${update.toolCallId}`;
2915
+ const existing = messages.find((message) => message.id === id && message.type === "tool_call");
2916
+ const status = update.status === "completed" || update.status === "failed" ? update.status === "failed" ? "failed" : "completed" : "in_progress";
2917
+ upsertDisplayMessage(messages, createCallDisplayMessage({
2918
+ id,
2919
+ server: "fx",
2920
+ tool: typeof update.name === "string" ? update.name : typeof update.title === "string" ? update.title : existing?.tool ?? "tool",
2921
+ input: update.rawInput === void 0 ? existing?.input : isRecord(update.rawInput) ? update.rawInput : stringifyDisplayValue(update.rawInput),
2922
+ output: update.rawOutput === void 0 ? existing?.output : stringifyDisplayValue(update.rawOutput),
2923
+ status,
2924
+ timestamp: event.timestamp
2925
+ }));
2926
+ }
2927
+ }
2928
+ return messages;
2929
+ }
2930
+
2584
2931
  // ../shared/src/display-message/parsers/opencode-parser.ts
2585
2932
  function timestampFromMs(value, fallback) {
2586
2933
  return typeof value === "number" && Number.isFinite(value) ? new Date(value).toISOString() : fallback;
@@ -3327,6 +3674,12 @@ function parseAgentEvents(events, agentType) {
3327
3674
  if (agentType === "cursor") {
3328
3675
  return parseCursorEvents(events);
3329
3676
  }
3677
+ if (agentType === "deepseek") {
3678
+ return parseDeepseekEvents(events);
3679
+ }
3680
+ if (agentType === "fx") {
3681
+ return parseFxEvents(events);
3682
+ }
3330
3683
  if (agentType === "opencode") {
3331
3684
  return parseOpencodeEvents(events);
3332
3685
  }
@@ -3336,6 +3689,80 @@ function parseAgentEvents(events, agentType) {
3336
3689
  return parseClaudeEvents(events);
3337
3690
  }
3338
3691
 
3692
+ // ../shared/src/pricing.ts
3693
+ var AUTOMATED_SOURCES = ["api", "automation"];
3694
+ var AUTOMATED_SOURCES_SQL = AUTOMATED_SOURCES.map((s) => `'${s}'`).join(", ");
3695
+ var PLANS = {
3696
+ trial: {
3697
+ id: "trial",
3698
+ name: "Free Trial",
3699
+ monthlyPrice: 0,
3700
+ seatPriceCents: 0,
3701
+ features: [
3702
+ "14 days free, no credit card required",
3703
+ "Unlimited manual workspace minutes",
3704
+ "15,000 automated workspace minutes",
3705
+ "Unlimited repositories and environments",
3706
+ "Unlimited automations",
3707
+ "Warm pools and warm hooks",
3708
+ "API access"
3709
+ ]
3710
+ },
3711
+ developer: {
3712
+ id: "developer",
3713
+ name: "Developer",
3714
+ monthlyPrice: 120,
3715
+ seatPriceCents: 12e3,
3716
+ features: [
3717
+ "Unlimited manual workspace minutes",
3718
+ "5,000 automated workspace minutes per month",
3719
+ "Up to 10 repositories",
3720
+ "Up to 15 environments",
3721
+ "Up to 5 automations",
3722
+ "Warm pools and warm hooks",
3723
+ "API access"
3724
+ ]
3725
+ },
3726
+ team: {
3727
+ id: "team",
3728
+ name: "Team",
3729
+ monthlyPrice: 300,
3730
+ seatPriceCents: 3e4,
3731
+ features: [
3732
+ "Unlimited manual workspace minutes",
3733
+ "15,000 automated workspace minutes per month",
3734
+ "Unlimited repositories",
3735
+ "Unlimited automations",
3736
+ "Warm pools and warm hooks",
3737
+ "Higher API rate limits",
3738
+ "Access to a static egress IP",
3739
+ "Auto-upgraded sandbox resources (4 vCPU, 32 GB disk, 16 GB memory)",
3740
+ "Shared Slack support channel"
3741
+ ]
3742
+ },
3743
+ enterprise: {
3744
+ id: "enterprise",
3745
+ name: "Enterprise",
3746
+ monthlyPrice: 0,
3747
+ seatPriceCents: 0,
3748
+ features: [
3749
+ "Unlimited manual workspace minutes",
3750
+ "Unlimited automated workspace minutes",
3751
+ "Unlimited automations",
3752
+ "Custom warm hooks and pools",
3753
+ "Custom API rates",
3754
+ "Custom rate limits",
3755
+ "Shared Slack support channel",
3756
+ "SOC 2",
3757
+ "Data Processing Agreement (DPA)",
3758
+ "SCIM user provisioning",
3759
+ "Custom security and privacy settings"
3760
+ ]
3761
+ }
3762
+ };
3763
+ var TEAM_PLAN = PLANS.team;
3764
+ var ENTERPRISE_PLAN = PLANS.enterprise;
3765
+
3339
3766
  // ../shared/src/routes/workspaces.ts
3340
3767
  var WORKSPACE_STATUSES = ["active", "sleeping", "archived", "preparing", "error"];
3341
3768
  var VALID_LIFECYCLE_POLICIES = ["default", "archive_when_done", "sleep_when_done", "delete_after_inactivity"];
@@ -3396,24 +3823,24 @@ function result(data, isError = false) {
3396
3823
  }
3397
3824
  var server = new McpServer({
3398
3825
  name: "replicas-mcp",
3399
- version: "0.2.12"
3826
+ version: "0.2.13"
3400
3827
  });
3401
3828
  var registerTool = server.registerTool.bind(server);
3402
- var codingAgentSchema = z2.string().refine(isValidCodingAgentProvider, {
3829
+ var codingAgentSchema = z3.string().refine(isValidCodingAgentProvider, {
3403
3830
  message: `Coding agent must be ${getCodingAgentDisplayNames()}`
3404
3831
  });
3405
- var lifecyclePolicySchema = z2.enum(VALID_LIFECYCLE_POLICIES);
3832
+ var lifecyclePolicySchema = z3.enum(VALID_LIFECYCLE_POLICIES);
3406
3833
  registerTool(
3407
3834
  "create_replica",
3408
3835
  {
3409
3836
  description: "Create a new replica workspace and send initial message",
3410
3837
  inputSchema: {
3411
- name: z2.string().regex(/^\S+$/, "Name cannot contain spaces"),
3412
- message: z2.string(),
3413
- repository_ids: z2.array(z2.string()).min(1).optional().describe("Repository IDs to include. Mutually exclusive with repository_set_id."),
3414
- repository_set_id: z2.string().optional().describe("Repository set ID to use. Mutually exclusive with repository_ids."),
3838
+ name: z3.string().regex(/^\S+$/, "Name cannot contain spaces"),
3839
+ message: z3.string(),
3840
+ repository_ids: z3.array(z3.string()).min(1).optional().describe("Repository IDs to include. Mutually exclusive with repository_set_id."),
3841
+ repository_set_id: z3.string().optional().describe("Repository set ID to use. Mutually exclusive with repository_ids."),
3415
3842
  coding_agent: codingAgentSchema.optional().describe("Coding agent to use (default: claude)"),
3416
- model: z2.string().optional().describe("Model to use for the coding agent"),
3843
+ model: z3.string().optional().describe("Model to use for the coding agent"),
3417
3844
  lifecycle_policy: lifecyclePolicySchema.optional().describe("Lifecycle policy for the workspace")
3418
3845
  }
3419
3846
  },
@@ -3439,11 +3866,11 @@ registerTool(
3439
3866
  {
3440
3867
  description: "List all replicas with pagination and optional filters",
3441
3868
  inputSchema: {
3442
- page: z2.number().optional(),
3443
- limit: z2.number().optional(),
3444
- user_id: z2.string().optional().describe("Filter by creator user ID"),
3445
- source: z2.enum(["api", "dashboard", "slack", "linear", "github", "gitlab", "automation"]).optional().describe("Filter by creation source"),
3446
- status: z2.enum(["active", "sleeping", "preparing"]).optional().describe("Filter by workspace status")
3869
+ page: z3.number().optional(),
3870
+ limit: z3.number().optional(),
3871
+ user_id: z3.string().optional().describe("Filter by creator user ID"),
3872
+ source: z3.enum(["api", "dashboard", "slack", "linear", "github", "gitlab", "automation"]).optional().describe("Filter by creation source"),
3873
+ status: z3.enum(["active", "sleeping", "preparing"]).optional().describe("Filter by workspace status")
3447
3874
  }
3448
3875
  },
3449
3876
  async (args) => {
@@ -3466,8 +3893,8 @@ registerTool(
3466
3893
  {
3467
3894
  description: "Get replica details by ID, including current status, chats, and repository statuses",
3468
3895
  inputSchema: {
3469
- id: z2.string(),
3470
- include: z2.array(z2.enum(["environment", "diffs"])).optional().describe("Extra data to include in response")
3896
+ id: z3.string(),
3897
+ include: z3.array(z3.enum(["environment", "diffs"])).optional().describe("Extra data to include in response")
3471
3898
  }
3472
3899
  },
3473
3900
  async (args) => {
@@ -3486,12 +3913,12 @@ registerTool(
3486
3913
  {
3487
3914
  description: "Send a message to an existing replica. Automatically handles waking sleeping replicas.",
3488
3915
  inputSchema: {
3489
- id: z2.string(),
3490
- message: z2.string(),
3916
+ id: z3.string(),
3917
+ message: z3.string(),
3491
3918
  coding_agent: codingAgentSchema.optional().describe("Coding agent to use"),
3492
- chat_id: z2.string().optional().describe("Chat ID to send the message to (uses default chat if omitted)"),
3493
- model: z2.string().optional().describe("Model to use"),
3494
- plan_mode: z2.boolean().optional().describe("Whether to use plan mode")
3919
+ chat_id: z3.string().optional().describe("Chat ID to send the message to (uses default chat if omitted)"),
3920
+ model: z3.string().optional().describe("Model to use"),
3921
+ plan_mode: z3.boolean().optional().describe("Whether to use plan mode")
3495
3922
  }
3496
3923
  },
3497
3924
  async (args) => {
@@ -3512,7 +3939,7 @@ registerTool(
3512
3939
  {
3513
3940
  description: "Delete a replica. This terminates the workspace and cannot be undone.",
3514
3941
  inputSchema: {
3515
- id: z2.string()
3942
+ id: z3.string()
3516
3943
  }
3517
3944
  },
3518
3945
  async (args) => {
@@ -3554,7 +3981,7 @@ registerTool(
3554
3981
  {
3555
3982
  description: "List chats in a replica workspace",
3556
3983
  inputSchema: {
3557
- id: z2.string().describe("Replica ID")
3984
+ id: z3.string().describe("Replica ID")
3558
3985
  }
3559
3986
  },
3560
3987
  async (args) => {
@@ -3570,9 +3997,9 @@ registerTool(
3570
3997
  {
3571
3998
  description: "Create a new chat in a replica workspace",
3572
3999
  inputSchema: {
3573
- id: z2.string().describe("Replica ID"),
4000
+ id: z3.string().describe("Replica ID"),
3574
4001
  provider: codingAgentSchema.describe("Coding agent provider for the chat"),
3575
- title: z2.string().optional().describe("Optional title for the chat")
4002
+ title: z3.string().optional().describe("Optional title for the chat")
3576
4003
  }
3577
4004
  },
3578
4005
  async (args) => {
@@ -3590,8 +4017,8 @@ registerTool(
3590
4017
  {
3591
4018
  description: "Delete a chat from a replica workspace",
3592
4019
  inputSchema: {
3593
- id: z2.string().describe("Replica ID"),
3594
- chat_id: z2.string().describe("Chat ID to delete")
4020
+ id: z3.string().describe("Replica ID"),
4021
+ chat_id: z3.string().describe("Chat ID to delete")
3595
4022
  }
3596
4023
  },
3597
4024
  async (args) => {
@@ -3607,8 +4034,8 @@ registerTool(
3607
4034
  {
3608
4035
  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.",
3609
4036
  inputSchema: {
3610
- id: z2.string(),
3611
- chat_id: z2.string().optional().describe("Read history for a specific chat (uses default chat if omitted)")
4037
+ id: z3.string(),
4038
+ chat_id: z3.string().optional().describe("Read history for a specific chat (uses default chat if omitted)")
3612
4039
  }
3613
4040
  },
3614
4041
  async (args) => {
@@ -3642,7 +4069,7 @@ registerTool(
3642
4069
  {
3643
4070
  description: "Get warm and start hook logs for a replica workspace",
3644
4071
  inputSchema: {
3645
- id: z2.string().describe("Replica ID")
4072
+ id: z3.string().describe("Replica ID")
3646
4073
  }
3647
4074
  },
3648
4075
  async (args) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-mcp",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -71,7 +71,7 @@ type RegisterToolFn = (
71
71
 
72
72
  const server = new McpServer({
73
73
  name: 'replicas-mcp',
74
- version: '0.2.12',
74
+ version: '0.2.13',
75
75
  });
76
76
 
77
77
  const registerTool = server.registerTool.bind(server) as RegisterToolFn;