replicas-mcp 0.2.12 → 0.2.14

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 +647 -133
  2. package/package.json +1 -1
  3. package/src/index.ts +1 -1
package/dist/index.js CHANGED
@@ -3,13 +3,16 @@
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",
15
+ KIMI: "kimi",
13
16
  OPENCODE: "opencode",
14
17
  PI: "pi",
15
18
  RELAY: "relay"
@@ -28,6 +31,9 @@ var THINKING_LEVELS_BY_AGENT = {
28
31
  claude: [...STANDARD_THINKING_LEVELS, "ultracode"],
29
32
  codex: [...STANDARD_THINKING_LEVELS, "ultra"],
30
33
  cursor: STANDARD_THINKING_LEVELS,
34
+ deepseek: STANDARD_THINKING_LEVELS,
35
+ fx: STANDARD_THINKING_LEVELS,
36
+ kimi: STANDARD_THINKING_LEVELS,
31
37
  opencode: STANDARD_THINKING_LEVELS,
32
38
  pi: STANDARD_THINKING_LEVELS,
33
39
  relay: [...STANDARD_THINKING_LEVELS, "ultracode"]
@@ -41,6 +47,12 @@ function getProviderDisplayName(provider, variant = "short") {
41
47
  return "Codex";
42
48
  case "cursor":
43
49
  return "Cursor";
50
+ case "deepseek":
51
+ return variant === "long" ? "DeepSeek Harness" : "DeepSeek";
52
+ case "fx":
53
+ return "fx";
54
+ case "kimi":
55
+ return variant === "long" ? "Kimi Code" : "Kimi";
44
56
  case "opencode":
45
57
  return "Opencode";
46
58
  case "pi":
@@ -55,6 +67,9 @@ function getCodingAgentDisplayNames() {
55
67
  return VALID_CODING_AGENT_PROVIDERS.map((provider) => getProviderDisplayName(provider)).join(", ");
56
68
  }
57
69
 
70
+ // ../shared/src/analytics/types.ts
71
+ import { z } from "zod";
72
+
58
73
  // ../shared/src/credentials/types.ts
59
74
  var CREDENTIAL_SCOPE = {
60
75
  USER: "user",
@@ -76,7 +91,8 @@ var CREDENTIAL_METHOD = {
76
91
  BEDROCK: "bedrock",
77
92
  OPENCODE_GO: "opencode-go",
78
93
  ASTER: "aster",
79
- OPENROUTER: "openrouter"
94
+ OPENROUTER: "openrouter",
95
+ AI_GATEWAY: "ai-gateway"
80
96
  };
81
97
  var CREDENTIAL_TYPE = {
82
98
  CLAUDE_OAUTH: "claude",
@@ -89,7 +105,8 @@ var CREDENTIAL_TYPE = {
89
105
  CURSOR_API_KEY: "cursor_api_key",
90
106
  OPENCODE_GO_API_KEY: "opencode_go_api_key",
91
107
  ASTER_API_KEY: "aster_api_key",
92
- OPENROUTER_API_KEY: "openrouter_api_key"
108
+ OPENROUTER_API_KEY: "openrouter_api_key",
109
+ AI_GATEWAY_API_KEY: "ai_gateway_api_key"
93
110
  };
94
111
  var AGENT_CREDENTIAL_METHODS_IN_ORDER = {
95
112
  [AGENT.CLAUDE]: [
@@ -106,6 +123,15 @@ var AGENT_CREDENTIAL_METHODS_IN_ORDER = {
106
123
  [AGENT.CURSOR]: [
107
124
  { method: CREDENTIAL_METHOD.API_KEY, credentialType: CREDENTIAL_TYPE.CURSOR_API_KEY }
108
125
  ],
126
+ [AGENT.DEEPSEEK]: [
127
+ { method: CREDENTIAL_METHOD.OPENROUTER, credentialType: CREDENTIAL_TYPE.OPENROUTER_API_KEY }
128
+ ],
129
+ [AGENT.FX]: [
130
+ { method: CREDENTIAL_METHOD.AI_GATEWAY, credentialType: CREDENTIAL_TYPE.AI_GATEWAY_API_KEY }
131
+ ],
132
+ [AGENT.KIMI]: [
133
+ { method: CREDENTIAL_METHOD.AI_GATEWAY, credentialType: CREDENTIAL_TYPE.AI_GATEWAY_API_KEY }
134
+ ],
109
135
  [AGENT.OPENCODE]: [
110
136
  { method: CREDENTIAL_METHOD.OPENCODE_GO, credentialType: CREDENTIAL_TYPE.OPENCODE_GO_API_KEY },
111
137
  { method: CREDENTIAL_METHOD.ASTER, credentialType: CREDENTIAL_TYPE.ASTER_API_KEY },
@@ -117,6 +143,13 @@ var AGENT_CREDENTIAL_METHODS_IN_ORDER = {
117
143
  ]
118
144
  };
119
145
 
146
+ // ../shared/src/analytics/types.ts
147
+ var agentCredentialSnapshotSchema = z.object({
148
+ method: z.enum(CREDENTIAL_METHOD),
149
+ scope: z.enum(CREDENTIAL_SCOPE),
150
+ revision: z.string().optional()
151
+ });
152
+
120
153
  // ../shared/src/event.ts
121
154
  var CLAUDE_PARTIAL_MESSAGE_EVENT_TYPE = "claude-partial-message";
122
155
  function coerceClaudePartialMessagePayload(payload) {
@@ -138,6 +171,37 @@ var USER_MESSAGE_ID_PAYLOAD_KEY = "replicasMessageId";
138
171
  var CODEX_ASP_ITEM_ID_PAYLOAD_KEY = "codexAspItemId";
139
172
  var CODEX_QUOTA_STATUS_EVENT_TYPE = "codex-quota-status";
140
173
 
174
+ // ../shared/src/urls.ts
175
+ var HOME_PATH = "/home";
176
+ var DASHBOARD_PATH = "/dashboard";
177
+ var WEB_APP_PATHS = {
178
+ home: HOME_PATH,
179
+ onboarding: `${HOME_PATH}/onboarding`,
180
+ workspaces: `${HOME_PATH}/workspace`,
181
+ automations: `${HOME_PATH}/automations`,
182
+ environment: `${HOME_PATH}/environment`,
183
+ analytics: `${HOME_PATH}/analytics`,
184
+ dashboard: DASHBOARD_PATH,
185
+ agents: `${DASHBOARD_PATH}/agents`,
186
+ integrations: `${DASHBOARD_PATH}/integrations`,
187
+ integrationsGithub: `${DASHBOARD_PATH}/integrations/github`,
188
+ integrationsGitlab: `${DASHBOARD_PATH}/integrations/gitlab`,
189
+ settings: `${DASHBOARD_PATH}/settings`,
190
+ billing: `${DASHBOARD_PATH}/billing`,
191
+ members: `${DASHBOARD_PATH}/members`,
192
+ "audit-logs": `${DASHBOARD_PATH}/audit-logs`,
193
+ accountAgents: `${DASHBOARD_PATH}/account/agents`,
194
+ accountProfile: `${DASHBOARD_PATH}/account/profile`,
195
+ accountIntegrations: `${DASHBOARD_PATH}/account/integrations`,
196
+ accountApiKeys: `${DASHBOARD_PATH}/account/api-keys`,
197
+ accountPreferences: `${DASHBOARD_PATH}/account/preferences`,
198
+ fullscreenWorkspaces: "/workspaces"
199
+ };
200
+ var LEGACY_WEB_APP_PATHS = {
201
+ app: "/app",
202
+ dashboardWorkspaces: `${DASHBOARD_PATH}/workspaces`
203
+ };
204
+
141
205
  // ../shared/src/default-skills/replicas-agent/abilities/computer.ts
142
206
  var SECTION = `### Computer use (Linux desktop control)
143
207
  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 +679,8 @@ var GITLAB_ABILITY = {
615
679
  referenceFile: { name: "GITLAB.md", content: REFERENCE4 }
616
680
  };
617
681
 
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
682
  // ../shared/src/default-skills/replicas-agent/abilities/linear.ts
646
- var SECTION6 = `### Linear
683
+ var SECTION5 = `### Linear
647
684
  Fetch issues, update state, add comments, and search through the workspace-scoped TypeScript SDK.
648
685
 
649
686
  **Reference:** \`references/LINEAR.md\`
@@ -652,7 +689,7 @@ Use this when:
652
689
  - You encounter a Linear issue link and need to understand the task
653
690
  - You need to update an issue's state or add a comment
654
691
  - You need to search Linear issues`;
655
- var REFERENCE6 = `# Linear Integration
692
+ var REFERENCE5 = `# Linear Integration
656
693
 
657
694
  Use \`@replicas/sdk\`; never request or handle a Linear OAuth token. Replicas refreshes the organization credential behind the gateway.
658
695
 
@@ -702,12 +739,12 @@ var LINEAR_ABILITY = {
702
739
  label: "Linear",
703
740
  description: "Fetch issues, comment, and update state through @replicas/sdk.",
704
741
  bullet: "- Interacting with Linear (fetching issues, updating state, commenting, etc.)",
705
- section: SECTION6,
706
- referenceFile: { name: "LINEAR.md", content: REFERENCE6 }
742
+ section: SECTION5,
743
+ referenceFile: { name: "LINEAR.md", content: REFERENCE5 }
707
744
  };
708
745
 
709
746
  // ../shared/src/default-skills/replicas-agent/abilities/media.ts
710
- var SECTION7 = `### Media
747
+ var SECTION6 = `### Media
711
748
  Share screenshots, screen recordings, generated diagrams, audio clips, and self-contained HTML pages through Replicas and external messages.
712
749
 
713
750
  **Reference:** \`references/MEDIA.md\`
@@ -717,7 +754,7 @@ Use this when:
717
754
  - You produce a self-contained HTML report that should be linked from a pull request
718
755
  - You record video output (browser automation, screen capture) \u2014 including the recommended aspect ratio and FPS
719
756
  - 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)
757
+ var REFERENCE6 = `# Media (Screenshots, Recordings, Audio, HTML)
721
758
 
722
759
  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
760
 
@@ -1023,12 +1060,12 @@ var MEDIA_ABILITY = {
1023
1060
  label: "Media",
1024
1061
  description: "Share screenshots, recordings, generated images, audio clips, and HTML pages.",
1025
1062
  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 }
1063
+ section: SECTION6,
1064
+ referenceFile: { name: "MEDIA.md", content: REFERENCE6 }
1028
1065
  };
1029
1066
 
1030
1067
  // ../shared/src/default-skills/replicas-agent/abilities/previews.ts
1031
- var SECTION8 = `### Previews
1068
+ var SECTION7 = `### Previews
1032
1069
  Expose locally running services (web apps, APIs, databases) as public preview URLs so humans can interact with them directly.
1033
1070
 
1034
1071
  **Reference:** \`references/PREVIEWS.md\`
@@ -1037,7 +1074,7 @@ Use this when:
1037
1074
  - You need to start a service that a human should view or interact with
1038
1075
  - The task involves UI work that benefits from human review
1039
1076
  - You are verifying frontend/backend integrations visually`;
1040
- var REFERENCE8 = `# Preview URLs
1077
+ var REFERENCE7 = `# Preview URLs
1041
1078
 
1042
1079
  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
1080
 
@@ -1113,12 +1150,12 @@ var PREVIEWS_ABILITY = {
1113
1150
  label: "Previews",
1114
1151
  description: "Expose locally running services on public preview URLs for humans.",
1115
1152
  bullet: "- Creating preview URLs for locally running services",
1116
- section: SECTION8,
1117
- referenceFile: { name: "PREVIEWS.md", content: REFERENCE8 }
1153
+ section: SECTION7,
1154
+ referenceFile: { name: "PREVIEWS.md", content: REFERENCE7 }
1118
1155
  };
1119
1156
 
1120
1157
  // ../shared/src/default-skills/replicas-agent/abilities/plugins.ts
1121
- var SECTION9 = `### Plugins
1158
+ var SECTION8 = `### Plugins
1122
1159
  Discover and execute installed marketplace tools through the workspace-scoped TypeScript SDK.
1123
1160
 
1124
1161
  **Reference:** \`references/PLUGINS.md\`
@@ -1128,10 +1165,15 @@ Use this when:
1128
1165
  - The user asks to manage Calendly events, invitees, or scheduling links
1129
1166
  - The user asks to read or change Google Ads, Docs, Drive, Search Console, or Sheets data
1130
1167
  - The user asks for ClickHouse analytics or PostHog product data
1168
+ - The user asks to create or control an E2B sandbox
1169
+ - The user asks to invoke a connected Modal function
1170
+ - The user asks to query or update a connected turbopuffer namespace
1171
+ - The user asks for PlanetScale organizations, databases, branches, or Insights data
1172
+ - The user asks to analyze text with Pangram
1131
1173
  - The user asks for Stripe customers, payments, invoices, subscriptions, or balances`;
1132
- var REFERENCE9 = `# Plugins
1174
+ var REFERENCE8 = `# Plugins
1133
1175
 
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.
1176
+ 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
1177
 
1136
1178
  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
1179
 
@@ -1181,17 +1223,80 @@ const results = await Promise.all(customerIds.map((customer) =>
1181
1223
  \`\`\`
1182
1224
 
1183
1225
  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.
1226
+
1227
+ ## PlanetScale
1228
+
1229
+ PlanetScale is a native OAuth integration. Pass a relative path from the PlanetScale v1 API; Replicas refreshes access tokens server-side:
1230
+
1231
+ \`\`\`ts
1232
+ const organizations = await replicas.planetscale.request('/organizations');
1233
+ const databases = await replicas.planetscale.request('/organizations/acme/databases');
1234
+ \`\`\`
1235
+
1236
+ OAuth scopes control permitted operations. Confirm mutations before using POST, PUT, PATCH, or DELETE.
1237
+
1238
+ ## Modal
1239
+
1240
+ Modal is a native integration and uses its official JavaScript SDK behind the Replicas gateway. Invoke a deployed function by name:
1241
+
1242
+ \`\`\`ts
1243
+ const result = await replicas.modal.call({
1244
+ app: 'my-app',
1245
+ function: 'my-function',
1246
+ args: ['input'],
1247
+ environment: 'main',
1248
+ });
1249
+ \`\`\`
1250
+
1251
+ Function calls execute with the connected Modal token and may mutate external state. Confirm the requested action before invoking a function with side effects.
1252
+
1253
+ ## turbopuffer
1254
+
1255
+ turbopuffer is a native integration backed by its official TypeScript SDK. Use the provider-shaped helpers:
1256
+
1257
+ \`\`\`ts
1258
+ const namespaces = await replicas.turbopuffer.namespaces({ prefix: 'products' });
1259
+ const results = await replicas.turbopuffer.query('products', {
1260
+ rank_by: ['text', 'BM25', 'wireless headphones'],
1261
+ top_k: 10,
1262
+ });
1263
+ \`\`\`
1264
+
1265
+ \`write\` mutates namespace data. Confirm the requested change before calling it.
1266
+
1267
+ ## Pangram
1268
+
1269
+ Pangram is a native integration. Text sent to either method is processed by Pangram and consumes the connected account's credits:
1270
+
1271
+ \`\`\`ts
1272
+ const detection = await replicas.pangram.detect({ text });
1273
+ const plagiarism = await replicas.pangram.plagiarism(text);
1274
+ \`\`\`
1275
+
1276
+ Do not send private or sensitive text without the user's authorization.
1277
+
1278
+ ## E2B
1279
+
1280
+ E2B is a native integration backed by its official JavaScript SDK. Create a sandbox, run commands, and remove it when finished:
1281
+
1282
+ \`\`\`ts
1283
+ const { sandboxId } = await replicas.e2b.create({ timeoutMs: 300_000 });
1284
+ const result = await replicas.e2b.run(sandboxId, 'python analysis.py', { cwd: '/home/user' });
1285
+ await replicas.e2b.kill(sandboxId);
1286
+ \`\`\`
1287
+
1288
+ Sandbox operations consume the connected account's E2B credits. Kill sandboxes after use unless the user asked to keep one running.
1184
1289
  `;
1185
1290
  var PLUGINS_ABILITY = {
1186
1291
  label: "Plugins",
1187
1292
  description: "Use installed marketplace tools through @replicas/sdk.",
1188
1293
  bullet: "- Interacting with installed marketplace plugins through TypeScript",
1189
- section: SECTION9,
1190
- referenceFile: { name: "PLUGINS.md", content: REFERENCE9 }
1294
+ section: SECTION8,
1295
+ referenceFile: { name: "PLUGINS.md", content: REFERENCE8 }
1191
1296
  };
1192
1297
 
1193
1298
  // ../shared/src/default-skills/replicas-agent/abilities/replicas.ts
1194
- var SECTION10 = `### Replicas (in-workspace CLI)
1299
+ var SECTION9 = `### Replicas (in-workspace CLI)
1195
1300
  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
1301
 
1197
1302
  **Reference:** \`references/REPLICAS.md\`
@@ -1202,7 +1307,7 @@ Use this when:
1202
1307
  - The user asks "what envs / repos / automations do I have?"
1203
1308
  - The user asks you to scaffold a \`replicas.json\` / \`replicas.yaml\` in a repo
1204
1309
  - 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)
1310
+ var REFERENCE9 = `# Replicas (in-workspace CLI)
1206
1311
 
1207
1312
  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
1313
 
@@ -1429,13 +1534,13 @@ var REPLICAS_ABILITY = {
1429
1534
  description: "Teach the agent about Replicas itself \u2014 automations, environments, the in-workspace CLI.",
1430
1535
  // No bullet — help_instructions covers the `replicas` CLI surface in detail.
1431
1536
  bullet: "",
1432
- section: SECTION10,
1433
- referenceFile: { name: "REPLICAS.md", content: REFERENCE10 },
1537
+ section: SECTION9,
1538
+ referenceFile: { name: "REPLICAS.md", content: REFERENCE9 },
1434
1539
  locked: true
1435
1540
  };
1436
1541
 
1437
1542
  // ../shared/src/default-skills/replicas-agent/abilities/sentry.ts
1438
- var SECTION11 = `### Sentry
1543
+ var SECTION10 = `### Sentry
1439
1544
  Read issues and project data through the workspace-scoped TypeScript SDK.
1440
1545
 
1441
1546
  **Reference:** \`references/SENTRY.md\`
@@ -1443,7 +1548,7 @@ Read issues and project data through the workspace-scoped TypeScript SDK.
1443
1548
  Use this when:
1444
1549
  - You need to inspect Sentry projects or issues
1445
1550
  - A user asks for error-monitoring context outside a webhook-triggered task`;
1446
- var REFERENCE11 = `# Sentry Integration
1551
+ var REFERENCE10 = `# Sentry Integration
1447
1552
 
1448
1553
  Use \`@replicas/sdk\`; never request or read a Sentry token. Sentry access is organization-scoped and read-only.
1449
1554
 
@@ -1462,12 +1567,12 @@ var SENTRY_ABILITY = {
1462
1567
  label: "Sentry",
1463
1568
  description: "Read Sentry projects and issues through @replicas/sdk.",
1464
1569
  bullet: "- Interacting with Sentry (reading projects, issues, and error context)",
1465
- section: SECTION11,
1466
- referenceFile: { name: "SENTRY.md", content: REFERENCE11 }
1570
+ section: SECTION10,
1571
+ referenceFile: { name: "SENTRY.md", content: REFERENCE10 }
1467
1572
  };
1468
1573
 
1469
1574
  // ../shared/src/default-skills/replicas-agent/abilities/slack.ts
1470
- var SECTION12 = `### Slack
1575
+ var SECTION11 = `### Slack
1471
1576
  Send messages, read threads, search conversations, and upload files through the workspace-scoped TypeScript SDK.
1472
1577
 
1473
1578
  **Reference:** \`references/SLACK.md\`
@@ -1476,7 +1581,7 @@ Use this when:
1476
1581
  - You need to send a message to a Slack channel or thread
1477
1582
  - You need to read or search Slack conversations
1478
1583
  - You encounter a Slack message link and need its full thread`;
1479
- var REFERENCE12 = `# Slack Integration
1584
+ var REFERENCE11 = `# Slack Integration
1480
1585
 
1481
1586
  Use \`@replicas/sdk\`; never read or send a Slack token directly. The SDK calls Replicas, which selects only this workspace's organization connection.
1482
1587
 
@@ -1511,7 +1616,7 @@ const message = await replicas.slack.call<{ channel: string; ts: string }>('chat
1511
1616
  await replicas.slack.attachThread({ channel: message.channel, threadTs: message.ts });
1512
1617
  \`\`\`
1513
1618
 
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.
1619
+ 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
1620
 
1516
1621
  \`\`\`ts
1517
1622
  await replicas.slack.call('chat.postMessage', {
@@ -1523,14 +1628,14 @@ await replicas.slack.call('chat.postMessage', {
1523
1628
  {
1524
1629
  type: 'actions',
1525
1630
  elements: [
1526
- { type: 'button', text: { type: 'plain_text', text: 'View PR' }, url: prUrl, action_id: 'view_pr' },
1631
+ { type: 'button', style: 'primary', text: { type: 'plain_text', text: 'View PR' }, url: prUrl, action_id: 'view_pr' },
1527
1632
  ],
1528
1633
  },
1529
1634
  ],
1530
1635
  });
1531
1636
  \`\`\`
1532
1637
 
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").
1638
+ 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
1639
 
1535
1640
  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
1641
 
@@ -1549,8 +1654,8 @@ var SLACK_ABILITY = {
1549
1654
  label: "Slack",
1550
1655
  description: "Send messages, read threads, and search through @replicas/sdk.",
1551
1656
  bullet: "- Interacting with Slack (sending messages, reading threads, etc.)",
1552
- section: SECTION12,
1553
- referenceFile: { name: "SLACK.md", content: REFERENCE12 }
1657
+ section: SECTION11,
1658
+ referenceFile: { name: "SLACK.md", content: REFERENCE11 }
1554
1659
  };
1555
1660
 
1556
1661
  // ../shared/src/default-skills/replicas-agent/registry.ts
@@ -1560,7 +1665,6 @@ var REPLICAS_AGENT_ABILITY_REGISTRY = {
1560
1665
  docker: DOCKER_ABILITY,
1561
1666
  github: GITHUB_ABILITY,
1562
1667
  gitlab: GITLAB_ABILITY,
1563
- learnings: LEARNINGS_ABILITY,
1564
1668
  linear: LINEAR_ABILITY,
1565
1669
  media: MEDIA_ABILITY,
1566
1670
  plugins: PLUGINS_ABILITY,
@@ -1654,12 +1758,24 @@ var GPT_5_6_TERRA_MODEL = "gpt-5.6-terra";
1654
1758
  var GPT_5_6_LUNA_MODEL = "gpt-5.6-luna";
1655
1759
  var DEFAULT_CODEX_MODEL = GPT_5_6_SOL_MODEL;
1656
1760
  var DEFAULT_OPENCODE_MODEL = "z-ai/glm-5.2";
1761
+ var DEEPSEEK_V4_PRO_MODEL = "deepseek/deepseek-v4-pro-0813";
1762
+ var DEEPSEEK_V4_FLASH_MODEL = "deepseek/deepseek-v4-flash-0731";
1763
+ var DEFAULT_FX_MODEL = "zai/glm-5.2";
1764
+ var DEFAULT_KIMI_MODEL = "moonshotai/kimi-k2.6";
1657
1765
  var OPENROUTER_MODELS = [
1658
1766
  DEFAULT_OPENCODE_MODEL,
1767
+ DEEPSEEK_V4_PRO_MODEL,
1768
+ DEEPSEEK_V4_FLASH_MODEL,
1659
1769
  "minimax/minimax-m3",
1660
1770
  "xiaomi/mimo-v2.5-pro",
1661
1771
  "moonshotai/kimi-k2.6"
1662
1772
  ];
1773
+ function prioritizeDeepseekModels(models) {
1774
+ return [
1775
+ ...models.filter((model) => model.startsWith("deepseek/")),
1776
+ ...models.filter((model) => !model.startsWith("deepseek/"))
1777
+ ];
1778
+ }
1663
1779
  var CLAUDE_MODEL_CONTEXT_WINDOWS = {
1664
1780
  [CLAUDE_FABLE_5_MODEL]: 1e6,
1665
1781
  [CLAUDE_OPUS_5_MODEL]: 1e6,
@@ -1709,11 +1825,15 @@ var AGENT_MODELS = {
1709
1825
  "gpt-5-mini",
1710
1826
  "gemini-3.1-pro",
1711
1827
  "gemini-3-flash",
1828
+ "grok-4.6",
1712
1829
  "grok-4.5",
1713
1830
  "grok-4.3",
1714
1831
  CLAUDE_HAIKU_4_5_MODEL,
1715
1832
  "kimi-k2.5"
1716
1833
  ],
1834
+ deepseek: prioritizeDeepseekModels(OPENROUTER_MODELS),
1835
+ fx: [DEFAULT_FX_MODEL],
1836
+ kimi: [DEFAULT_KIMI_MODEL],
1717
1837
  opencode: [...OPENROUTER_MODELS, ...ASTER_MODELS],
1718
1838
  pi: [...OPENROUTER_MODELS, ...ASTER_MODELS],
1719
1839
  relay: [CLAUDE_FABLE_5_MODEL, CLAUDE_OPUS_5_MODEL, CLAUDE_OPUS_4_8_MODEL, CLAUDE_SONNET_5_MODEL]
@@ -1733,6 +1853,8 @@ var MODEL_LABELS = {
1733
1853
  [GPT_5_6_LUNA_MODEL]: "GPT-5.6 Luna",
1734
1854
  "gpt-5.5": "GPT-5.5",
1735
1855
  [DEFAULT_OPENCODE_MODEL]: "GLM 5.2 via OpenRouter",
1856
+ [DEEPSEEK_V4_PRO_MODEL]: "DeepSeek V4 Pro via OpenRouter",
1857
+ [DEEPSEEK_V4_FLASH_MODEL]: "DeepSeek V4 Flash via OpenRouter",
1736
1858
  "minimax/minimax-m3": "MiniMax M3 via OpenRouter",
1737
1859
  "xiaomi/mimo-v2.5-pro": "MiMo V2.5 Pro via OpenRouter",
1738
1860
  "moonshotai/kimi-k2.6": "Kimi K2.6 via OpenRouter",
@@ -1753,6 +1875,7 @@ var MODEL_LABELS = {
1753
1875
  [CLAUDE_HAIKU_4_5_MODEL]: "Haiku 4.5",
1754
1876
  "gemini-3.1-pro": "Gemini 3.1 Pro",
1755
1877
  "gemini-3-flash": "Gemini 3 Flash",
1878
+ "grok-4.6": "Grok 4.6",
1756
1879
  "grok-4.5": "Grok 4.5",
1757
1880
  "grok-4.3": "Grok 4.3",
1758
1881
  "kimi-k2.5": "Kimi K2.5"
@@ -1762,51 +1885,51 @@ var MAX_CANVAS_FILE_BYTES = 25 * 1024 * 1024;
1762
1885
  var MAX_CANVAS_PREVIEW_BYTES = 5 * 1024 * 1024;
1763
1886
 
1764
1887
  // ../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()
1888
+ import { z as z2 } from "zod";
1889
+ var createChatRequestSchema = z2.object({
1890
+ id: z2.string().uuid().optional(),
1891
+ createdAt: z2.string().datetime().optional(),
1892
+ provider: z2.enum(VALID_AGENT_PROVIDERS),
1893
+ title: z2.string().min(1).optional(),
1894
+ parentChatId: z2.string().uuid().optional(),
1895
+ clientRequestId: z2.string().min(1).max(128).optional()
1773
1896
  });
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() })
1897
+ var sendChatMessageRequestSchema = z2.object({
1898
+ messageId: z2.string().optional(),
1899
+ submittedAt: z2.string().optional(),
1900
+ message: z2.string(),
1901
+ model: z2.string().optional(),
1902
+ customInstructions: z2.string().optional(),
1903
+ planMode: z2.boolean().optional(),
1904
+ images: z2.array(z2.object({
1905
+ type: z2.literal("image"),
1906
+ source: z2.discriminatedUnion("type", [
1907
+ z2.object({ type: z2.literal("base64"), media_type: z2.enum(IMAGE_MEDIA_TYPES), data: z2.string() }),
1908
+ z2.object({ type: z2.literal("url"), url: z2.string() })
1786
1909
  ])
1787
1910
  })).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()
1911
+ thinkingLevel: z2.enum(VALID_THINKING_LEVELS).optional(),
1912
+ goalMode: z2.boolean().optional(),
1913
+ fastMode: z2.boolean().optional(),
1914
+ enableInteractiveTools: z2.boolean().optional(),
1915
+ type: z2.string().optional(),
1916
+ merge: z2.boolean().optional(),
1917
+ idempotencyKey: z2.string().optional(),
1918
+ senderUserId: z2.string().optional(),
1919
+ senderEmail: z2.string().optional(),
1920
+ senderDisplayName: z2.string().optional(),
1921
+ senderAvatarUrl: z2.string().optional(),
1922
+ errorNotificationTarget: z2.discriminatedUnion("type", [
1923
+ z2.object({ type: z2.literal("slack") }),
1924
+ z2.object({ type: z2.literal("linear"), sessionId: z2.string() }),
1925
+ z2.object({
1926
+ type: z2.literal("code_host"),
1927
+ provider: z2.enum(["github", "gitlab"]),
1928
+ resource: z2.enum(["issue", "pull_request"]),
1929
+ repositoryId: z2.string(),
1930
+ resourceNumber: z2.number()
1808
1931
  }),
1809
- z.object({ type: z.literal("automation"), executionId: z.string() })
1932
+ z2.object({ type: z2.literal("automation"), executionId: z2.string() })
1810
1933
  ]).optional()
1811
1934
  }).passthrough();
1812
1935
 
@@ -2156,6 +2279,15 @@ function areUserMessagesWithinMatchWindow(a, b) {
2156
2279
  return a.content === b.content && Math.abs(parseTimestampMs(a.timestamp) - parseTimestampMs(b.timestamp)) <= USER_MESSAGE_MATCH_GRACE_PERIOD_MS;
2157
2280
  }
2158
2281
 
2282
+ // ../shared/src/agent-event-utils.ts
2283
+ function getUserMessage(event) {
2284
+ return event.type === "event_msg" && event.payload.type === "user_message" && typeof event.payload.message === "string" ? event.payload.message : null;
2285
+ }
2286
+ function getUserMessageId(event) {
2287
+ const messageId = event.payload[USER_MESSAGE_ID_PAYLOAD_KEY];
2288
+ return typeof messageId === "string" ? messageId : null;
2289
+ }
2290
+
2159
2291
  // ../shared/src/display-message/parsers/codex-parser.ts
2160
2292
  function getStatusFromExitCode(exitCode) {
2161
2293
  return exitCode === 0 ? "completed" : "failed";
@@ -2581,6 +2713,305 @@ function parseCursorEvents(events) {
2581
2713
  return messages;
2582
2714
  }
2583
2715
 
2716
+ // ../shared/src/display-message/parsers/deepseek-parser.ts
2717
+ function parseArguments(value) {
2718
+ if (typeof value !== "string") return void 0;
2719
+ const parsed = safeJsonParse(value, value);
2720
+ return isRecord(parsed) ? parsed : stringifyDisplayValue(parsed);
2721
+ }
2722
+ function getDeepseekAssistantMessageText(event, blockType) {
2723
+ if (!isRecord(event) || event.type !== "assistant/message" || !isRecord(event.data) || !isRecord(event.data.message) || !Array.isArray(event.data.message.content)) return "";
2724
+ return event.data.message.content.flatMap((block) => isRecord(block) && block.type === blockType && typeof block.text === "string" ? [block.text] : []).join("");
2725
+ }
2726
+ function parseDeepseekEvents(events) {
2727
+ const messages = [];
2728
+ for (const source of events) {
2729
+ if (source.type === "event_msg" && source.payload.type === "user_message") {
2730
+ if (typeof source.payload.message === "string" && source.payload.message.trim()) {
2731
+ const messageId = source.payload[USER_MESSAGE_ID_PAYLOAD_KEY];
2732
+ messages.push({
2733
+ id: typeof messageId === "string" ? messageId : `deepseek-user-${source.timestamp}`,
2734
+ type: "user",
2735
+ content: source.payload.message,
2736
+ timestamp: source.timestamp
2737
+ });
2738
+ }
2739
+ continue;
2740
+ }
2741
+ if (source.type === "deepseek-error") {
2742
+ messages.push({
2743
+ id: `deepseek-error-${source.timestamp}`,
2744
+ type: "error",
2745
+ message: typeof source.payload.message === "string" ? source.payload.message : "DeepSeek Harness failed",
2746
+ timestamp: source.timestamp
2747
+ });
2748
+ continue;
2749
+ }
2750
+ if (source.type === "deepseek-input-request") {
2751
+ const requestId = typeof source.payload.requestId === "string" ? source.payload.requestId : `deepseek-input-${source.timestamp}`;
2752
+ if (source.payload.kind === "approval") {
2753
+ upsertDisplayMessage(messages, createCallDisplayMessage({
2754
+ id: requestId,
2755
+ server: "deepseek",
2756
+ tool: typeof source.payload.toolName === "string" ? source.payload.toolName : "approval",
2757
+ input: typeof source.payload.reason === "string" ? source.payload.reason : void 0,
2758
+ status: "in_progress",
2759
+ timestamp: source.timestamp,
2760
+ inputRequest: {
2761
+ requestId,
2762
+ status: "pending",
2763
+ options: [
2764
+ { id: "deny", label: "Deny", variant: "secondary" },
2765
+ { id: "allow", label: "Allow once", variant: "primary" }
2766
+ ]
2767
+ }
2768
+ }));
2769
+ } else {
2770
+ const questions = Array.isArray(source.payload.questions) ? source.payload.questions : [];
2771
+ upsertDisplayMessage(messages, createCallDisplayMessage({
2772
+ id: requestId,
2773
+ server: "deepseek",
2774
+ tool: "ask_user",
2775
+ status: "in_progress",
2776
+ timestamp: source.timestamp,
2777
+ inputRequest: {
2778
+ requestId,
2779
+ status: "pending",
2780
+ questions: questions.flatMap((question) => {
2781
+ if (!isRecord(question) || typeof question.id !== "string" || typeof question.question !== "string") return [];
2782
+ const options = Array.isArray(question.options) ? question.options : [];
2783
+ return [{
2784
+ id: question.id,
2785
+ prompt: question.question,
2786
+ multiSelect: question.multiSelect === true,
2787
+ 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 } : {} }] : [])
2788
+ }];
2789
+ })
2790
+ }
2791
+ }));
2792
+ }
2793
+ continue;
2794
+ }
2795
+ if (source.type === "deepseek-input-resolved") {
2796
+ const requestId = typeof source.payload.requestId === "string" ? source.payload.requestId : "";
2797
+ const existing = messages.find((message) => message.type === "tool_call" && message.inputRequest?.requestId === requestId);
2798
+ if (existing?.type === "tool_call" && existing.inputRequest) {
2799
+ existing.inputRequest.status = source.payload.status === "aborted" ? "aborted" : "resolved";
2800
+ existing.status = source.payload.status === "aborted" ? "failed" : "completed";
2801
+ }
2802
+ continue;
2803
+ }
2804
+ if (source.type === "deepseek-jobs" && Array.isArray(source.payload.jobs)) {
2805
+ for (const job of source.payload.jobs) {
2806
+ if (!isRecord(job) || typeof job.id !== "string") continue;
2807
+ const existing = messages.find((message) => message.type === "background_task" && message.taskId === job.id);
2808
+ const status = normalizeBackgroundTaskStatus(job.status);
2809
+ const next = {
2810
+ id: existing?.id ?? `deepseek-job-${job.id}`,
2811
+ type: "background_task",
2812
+ taskId: job.id,
2813
+ status,
2814
+ description: typeof job.label === "string" ? job.label : void 0,
2815
+ taskType: typeof job.kind === "string" ? job.kind : void 0,
2816
+ error: status === "failed" && typeof job.detail === "string" ? job.detail : void 0,
2817
+ timestamp: existing?.timestamp ?? source.timestamp
2818
+ };
2819
+ if (existing?.type === "background_task") Object.assign(existing, next);
2820
+ else messages.push(next);
2821
+ }
2822
+ continue;
2823
+ }
2824
+ if (source.type !== "deepseek-session-event" || !isRecord(source.payload.event)) continue;
2825
+ const event = source.payload.event;
2826
+ const data = isRecord(event.data) ? event.data : {};
2827
+ const seq = typeof event.seq === "number" ? event.seq : source.timestamp;
2828
+ if (event.type === "assistant/message") {
2829
+ const content = getDeepseekAssistantMessageText(event, "text");
2830
+ const reasoning = getDeepseekAssistantMessageText(event, "reasoning");
2831
+ if (reasoning.trim()) messages.push({ id: `deepseek-reasoning-${seq}`, type: "reasoning", content: reasoning, status: "completed", timestamp: source.timestamp });
2832
+ if (content.trim()) messages.push({ id: `deepseek-assistant-${seq}`, type: "agent", content, timestamp: source.timestamp });
2833
+ continue;
2834
+ }
2835
+ if (event.type === "tool/call") {
2836
+ const callId = typeof data.callId === "string" ? data.callId : String(seq);
2837
+ upsertDisplayMessage(messages, createCallDisplayMessage({
2838
+ id: `deepseek-tool-${callId}`,
2839
+ server: "deepseek",
2840
+ tool: typeof data.name === "string" ? data.name : "tool",
2841
+ input: parseArguments(data.arguments),
2842
+ status: "in_progress",
2843
+ timestamp: source.timestamp
2844
+ }));
2845
+ continue;
2846
+ }
2847
+ if (event.type === "tool/result") {
2848
+ const message = isRecord(data.message) ? data.message : {};
2849
+ const messageSource = isRecord(message.source) ? message.source : {};
2850
+ const result2 = Array.isArray(message.content) && isRecord(message.content[0]) ? message.content[0] : {};
2851
+ const callId = typeof messageSource.callId === "string" ? messageSource.callId : typeof data.callId === "string" ? data.callId : String(seq);
2852
+ const existing = messages.find((message2) => message2.id === `deepseek-tool-${callId}` && message2.type === "tool_call");
2853
+ if (existing?.type === "tool_call") {
2854
+ existing.output = stringifyDisplayValue(result2.content ?? data.result);
2855
+ existing.status = result2.isError === true || data.isError === true ? "failed" : "completed";
2856
+ }
2857
+ continue;
2858
+ }
2859
+ if (event.type === "todo/write" && Array.isArray(data.items)) {
2860
+ messages.push({
2861
+ id: `deepseek-todo-${seq}`,
2862
+ type: "todo_list",
2863
+ items: data.items.flatMap((item) => {
2864
+ if (!isRecord(item) || typeof item.content !== "string") return [];
2865
+ const itemStatus = item.status === "in_progress" || item.status === "completed" ? item.status : "pending";
2866
+ return [{ text: item.content, completed: itemStatus === "completed", itemStatus }];
2867
+ }),
2868
+ timestamp: source.timestamp
2869
+ });
2870
+ }
2871
+ }
2872
+ return messages;
2873
+ }
2874
+
2875
+ // ../shared/src/display-message/parsers/acp-parser.ts
2876
+ var FX_CONTEXT_DIAGNOSTIC = /^\[context\] skill catalog omitted .*?(?:\r?\n|$)/;
2877
+ function getAcpTextChunk(update, kind) {
2878
+ if (!isRecord(update) || update.sessionUpdate !== kind || !isRecord(update.content) || update.content.type !== "text") return null;
2879
+ return typeof update.content.text === "string" && update.content.text.length > 0 ? update.content.text : null;
2880
+ }
2881
+ function isProviderEvent(event, provider, suffix) {
2882
+ return event.type === `acp-${suffix}` && (event.payload.provider === void 0 || event.payload.provider === provider) || event.type === `${provider}-${suffix}`;
2883
+ }
2884
+ function parseAcpEvents(events, provider) {
2885
+ const messages = [];
2886
+ const activeChunks = {};
2887
+ const resolvedInputs = /* @__PURE__ */ new Map();
2888
+ let chunkSequence = 0;
2889
+ for (const event of events) {
2890
+ const userContent = getUserMessage(event);
2891
+ if (userContent !== null) {
2892
+ activeChunks.agent_message_chunk = void 0;
2893
+ activeChunks.agent_thought_chunk = void 0;
2894
+ if (userContent.trim()) {
2895
+ messages.push({
2896
+ id: getUserMessageId(event) ?? `${provider}-user-${event.timestamp}`,
2897
+ type: "user",
2898
+ content: userContent,
2899
+ timestamp: event.timestamp
2900
+ });
2901
+ }
2902
+ continue;
2903
+ }
2904
+ if (isProviderEvent(event, provider, "error")) {
2905
+ messages.push({
2906
+ id: `${provider}-error-${event.timestamp}`,
2907
+ type: "error",
2908
+ message: typeof event.payload.message === "string" ? event.payload.message : `${provider} failed`,
2909
+ timestamp: event.timestamp
2910
+ });
2911
+ continue;
2912
+ }
2913
+ if (isProviderEvent(event, provider, "turn-complete")) {
2914
+ for (const message of messages) {
2915
+ if (message.type === "reasoning" && message.status === "in_progress") message.status = "completed";
2916
+ }
2917
+ activeChunks.agent_message_chunk = void 0;
2918
+ activeChunks.agent_thought_chunk = void 0;
2919
+ continue;
2920
+ }
2921
+ if (event.type === "replicas-tool-input-request" && typeof event.payload.toolUseId === "string" && typeof event.payload.requestId === "string") {
2922
+ const message = messages.find((candidate) => candidate.type === "tool_call" && candidate.id === `${provider}-tool-${event.payload.toolUseId}`);
2923
+ if (message && (Array.isArray(event.payload.options) || Array.isArray(event.payload.questions))) {
2924
+ const resolved = resolvedInputs.get(event.payload.toolUseId);
2925
+ message.inputRequest = {
2926
+ requestId: event.payload.requestId,
2927
+ status: resolved?.selectionId === "aborted" ? "aborted" : resolved ? "resolved" : "pending",
2928
+ ...Array.isArray(event.payload.options) ? { options: event.payload.options } : {},
2929
+ ...Array.isArray(event.payload.questions) ? { questions: event.payload.questions } : {},
2930
+ ...resolved ? { selectionId: resolved.selectionId } : {},
2931
+ ...resolved?.selectionSummary ? { selectionSummary: resolved.selectionSummary } : {}
2932
+ };
2933
+ }
2934
+ continue;
2935
+ }
2936
+ if (event.type === "replicas-tool-input-resolved" && typeof event.payload.toolUseId === "string" && typeof event.payload.selectionId === "string") {
2937
+ const resolved = {
2938
+ selectionId: event.payload.selectionId,
2939
+ ...typeof event.payload.selectionSummary === "string" ? { selectionSummary: event.payload.selectionSummary } : {}
2940
+ };
2941
+ resolvedInputs.set(event.payload.toolUseId, resolved);
2942
+ const message = messages.find((candidate) => candidate.type === "tool_call" && candidate.id === `${provider}-tool-${event.payload.toolUseId}`);
2943
+ if (message?.inputRequest) {
2944
+ message.inputRequest = {
2945
+ ...message.inputRequest,
2946
+ status: resolved.selectionId === "aborted" ? "aborted" : "resolved",
2947
+ ...resolved
2948
+ };
2949
+ }
2950
+ continue;
2951
+ }
2952
+ if (!isProviderEvent(event, provider, "session-update") || !isRecord(event.payload.update)) continue;
2953
+ const update = event.payload.update;
2954
+ const kind = update.sessionUpdate;
2955
+ if (kind === "agent_message_chunk" || kind === "agent_thought_chunk") {
2956
+ const content = getAcpTextChunk(update, kind);
2957
+ if (content === null) continue;
2958
+ const explicitId = typeof update.messageId === "string" && update.messageId.length > 0 ? `${provider}-${kind}-${update.messageId}` : null;
2959
+ const id = explicitId ?? activeChunks[kind] ?? `${provider}-${kind}-${chunkSequence++}`;
2960
+ activeChunks[kind] = id;
2961
+ const otherKind = kind === "agent_message_chunk" ? "agent_thought_chunk" : "agent_message_chunk";
2962
+ if (!explicitId) activeChunks[otherKind] = void 0;
2963
+ const existing = messages.find((message) => message.id === id);
2964
+ const text = `${existing && "content" in existing && typeof existing.content === "string" ? existing.content : ""}${content}`.replace(provider === "fx" ? FX_CONTEXT_DIAGNOSTIC : /$^/, "");
2965
+ if (!text) continue;
2966
+ 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 });
2967
+ continue;
2968
+ }
2969
+ activeChunks.agent_message_chunk = void 0;
2970
+ activeChunks.agent_thought_chunk = void 0;
2971
+ if ((kind === "tool_call" || kind === "tool_call_update") && typeof update.toolCallId === "string") {
2972
+ const id = `${provider}-tool-${update.toolCallId}`;
2973
+ const existing = messages.find((message) => message.id === id && message.type === "tool_call");
2974
+ const status = update.status === "completed" || update.status === "failed" ? update.status === "failed" ? "failed" : "completed" : "in_progress";
2975
+ const contentOutput = Array.isArray(update.content) && update.content.length > 0 ? stringifyDisplayValue(update.content) : void 0;
2976
+ upsertDisplayMessage(messages, createCallDisplayMessage({
2977
+ id,
2978
+ server: provider,
2979
+ tool: typeof update.name === "string" && update.name ? update.name : typeof update.title === "string" && update.title ? update.title : existing?.tool ?? (typeof update.kind === "string" ? update.kind : "tool"),
2980
+ input: update.rawInput === void 0 ? existing?.input : isRecord(update.rawInput) ? update.rawInput : stringifyDisplayValue(update.rawInput),
2981
+ output: update.rawOutput === void 0 ? contentOutput ?? existing?.output : stringifyDisplayValue(update.rawOutput),
2982
+ status,
2983
+ timestamp: existing?.timestamp ?? event.timestamp
2984
+ }));
2985
+ continue;
2986
+ }
2987
+ const plan = kind === "plan" ? update : isRecord(update.plan) ? update.plan : void 0;
2988
+ if ((kind === "plan" || kind === "plan_update") && plan && Array.isArray(plan.entries)) {
2989
+ const items = plan.entries.flatMap((entry) => {
2990
+ if (!isRecord(entry) || typeof entry.content !== "string") return [];
2991
+ const itemStatus = entry.status === "completed" || entry.status === "in_progress" ? entry.status : "pending";
2992
+ return [{
2993
+ text: entry.content,
2994
+ completed: entry.status === "completed",
2995
+ itemStatus
2996
+ }];
2997
+ });
2998
+ upsertDisplayMessage(messages, {
2999
+ id: `${provider}-plan-${typeof plan.planId === "string" ? plan.planId : "current"}`,
3000
+ type: "todo_list",
3001
+ items,
3002
+ status: items.every((item) => item.completed) ? "completed" : "in_progress",
3003
+ timestamp: event.timestamp
3004
+ });
3005
+ }
3006
+ }
3007
+ return messages;
3008
+ }
3009
+
3010
+ // ../shared/src/display-message/parsers/fx-parser.ts
3011
+ function parseFxEvents(events) {
3012
+ return parseAcpEvents(events, "fx");
3013
+ }
3014
+
2584
3015
  // ../shared/src/display-message/parsers/opencode-parser.ts
2585
3016
  function timestampFromMs(value, fallback) {
2586
3017
  return typeof value === "number" && Number.isFinite(value) ? new Date(value).toISOString() : fallback;
@@ -3327,6 +3758,15 @@ function parseAgentEvents(events, agentType) {
3327
3758
  if (agentType === "cursor") {
3328
3759
  return parseCursorEvents(events);
3329
3760
  }
3761
+ if (agentType === "deepseek") {
3762
+ return parseDeepseekEvents(events);
3763
+ }
3764
+ if (agentType === "fx") {
3765
+ return parseFxEvents(events);
3766
+ }
3767
+ if (agentType === "kimi") {
3768
+ return parseAcpEvents(events, "kimi");
3769
+ }
3330
3770
  if (agentType === "opencode") {
3331
3771
  return parseOpencodeEvents(events);
3332
3772
  }
@@ -3336,6 +3776,80 @@ function parseAgentEvents(events, agentType) {
3336
3776
  return parseClaudeEvents(events);
3337
3777
  }
3338
3778
 
3779
+ // ../shared/src/pricing.ts
3780
+ var AUTOMATED_SOURCES = ["api", "automation"];
3781
+ var AUTOMATED_SOURCES_SQL = AUTOMATED_SOURCES.map((s) => `'${s}'`).join(", ");
3782
+ var PLANS = {
3783
+ trial: {
3784
+ id: "trial",
3785
+ name: "Free Trial",
3786
+ monthlyPrice: 0,
3787
+ seatPriceCents: 0,
3788
+ features: [
3789
+ "14 days free, no credit card required",
3790
+ "Unlimited manual workspace minutes",
3791
+ "15,000 automated workspace minutes",
3792
+ "Unlimited repositories and environments",
3793
+ "Unlimited automations",
3794
+ "Warm pools and warm hooks",
3795
+ "API access"
3796
+ ]
3797
+ },
3798
+ developer: {
3799
+ id: "developer",
3800
+ name: "Developer",
3801
+ monthlyPrice: 120,
3802
+ seatPriceCents: 12e3,
3803
+ features: [
3804
+ "Unlimited manual workspace minutes",
3805
+ "5,000 automated workspace minutes per month",
3806
+ "Up to 10 repositories",
3807
+ "Up to 15 environments",
3808
+ "Up to 5 automations",
3809
+ "Warm pools and warm hooks",
3810
+ "API access"
3811
+ ]
3812
+ },
3813
+ team: {
3814
+ id: "team",
3815
+ name: "Team",
3816
+ monthlyPrice: 300,
3817
+ seatPriceCents: 3e4,
3818
+ features: [
3819
+ "Unlimited manual workspace minutes",
3820
+ "15,000 automated workspace minutes per month",
3821
+ "Unlimited repositories",
3822
+ "Unlimited automations",
3823
+ "Warm pools and warm hooks",
3824
+ "Higher API rate limits",
3825
+ "Access to a static egress IP",
3826
+ "Auto-upgraded sandbox resources (4 vCPU, 32 GB disk, 16 GB memory)",
3827
+ "Shared Slack support channel"
3828
+ ]
3829
+ },
3830
+ enterprise: {
3831
+ id: "enterprise",
3832
+ name: "Enterprise",
3833
+ monthlyPrice: 0,
3834
+ seatPriceCents: 0,
3835
+ features: [
3836
+ "Unlimited manual workspace minutes",
3837
+ "Unlimited automated workspace minutes",
3838
+ "Unlimited automations",
3839
+ "Custom warm hooks and pools",
3840
+ "Custom API rates",
3841
+ "Custom rate limits",
3842
+ "Shared Slack support channel",
3843
+ "SOC 2",
3844
+ "Data Processing Agreement (DPA)",
3845
+ "SCIM user provisioning",
3846
+ "Custom security and privacy settings"
3847
+ ]
3848
+ }
3849
+ };
3850
+ var TEAM_PLAN = PLANS.team;
3851
+ var ENTERPRISE_PLAN = PLANS.enterprise;
3852
+
3339
3853
  // ../shared/src/routes/workspaces.ts
3340
3854
  var WORKSPACE_STATUSES = ["active", "sleeping", "archived", "preparing", "error"];
3341
3855
  var VALID_LIFECYCLE_POLICIES = ["default", "archive_when_done", "sleep_when_done", "delete_after_inactivity"];
@@ -3396,24 +3910,24 @@ function result(data, isError = false) {
3396
3910
  }
3397
3911
  var server = new McpServer({
3398
3912
  name: "replicas-mcp",
3399
- version: "0.2.12"
3913
+ version: "0.2.14"
3400
3914
  });
3401
3915
  var registerTool = server.registerTool.bind(server);
3402
- var codingAgentSchema = z2.string().refine(isValidCodingAgentProvider, {
3916
+ var codingAgentSchema = z3.string().refine(isValidCodingAgentProvider, {
3403
3917
  message: `Coding agent must be ${getCodingAgentDisplayNames()}`
3404
3918
  });
3405
- var lifecyclePolicySchema = z2.enum(VALID_LIFECYCLE_POLICIES);
3919
+ var lifecyclePolicySchema = z3.enum(VALID_LIFECYCLE_POLICIES);
3406
3920
  registerTool(
3407
3921
  "create_replica",
3408
3922
  {
3409
3923
  description: "Create a new replica workspace and send initial message",
3410
3924
  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."),
3925
+ name: z3.string().regex(/^\S+$/, "Name cannot contain spaces"),
3926
+ message: z3.string(),
3927
+ repository_ids: z3.array(z3.string()).min(1).optional().describe("Repository IDs to include. Mutually exclusive with repository_set_id."),
3928
+ repository_set_id: z3.string().optional().describe("Repository set ID to use. Mutually exclusive with repository_ids."),
3415
3929
  coding_agent: codingAgentSchema.optional().describe("Coding agent to use (default: claude)"),
3416
- model: z2.string().optional().describe("Model to use for the coding agent"),
3930
+ model: z3.string().optional().describe("Model to use for the coding agent"),
3417
3931
  lifecycle_policy: lifecyclePolicySchema.optional().describe("Lifecycle policy for the workspace")
3418
3932
  }
3419
3933
  },
@@ -3439,11 +3953,11 @@ registerTool(
3439
3953
  {
3440
3954
  description: "List all replicas with pagination and optional filters",
3441
3955
  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")
3956
+ page: z3.number().optional(),
3957
+ limit: z3.number().optional(),
3958
+ user_id: z3.string().optional().describe("Filter by creator user ID"),
3959
+ source: z3.enum(["api", "dashboard", "slack", "linear", "github", "gitlab", "automation"]).optional().describe("Filter by creation source"),
3960
+ status: z3.enum(["active", "sleeping", "preparing"]).optional().describe("Filter by workspace status")
3447
3961
  }
3448
3962
  },
3449
3963
  async (args) => {
@@ -3466,8 +3980,8 @@ registerTool(
3466
3980
  {
3467
3981
  description: "Get replica details by ID, including current status, chats, and repository statuses",
3468
3982
  inputSchema: {
3469
- id: z2.string(),
3470
- include: z2.array(z2.enum(["environment", "diffs"])).optional().describe("Extra data to include in response")
3983
+ id: z3.string(),
3984
+ include: z3.array(z3.enum(["environment", "diffs"])).optional().describe("Extra data to include in response")
3471
3985
  }
3472
3986
  },
3473
3987
  async (args) => {
@@ -3486,12 +4000,12 @@ registerTool(
3486
4000
  {
3487
4001
  description: "Send a message to an existing replica. Automatically handles waking sleeping replicas.",
3488
4002
  inputSchema: {
3489
- id: z2.string(),
3490
- message: z2.string(),
4003
+ id: z3.string(),
4004
+ message: z3.string(),
3491
4005
  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")
4006
+ chat_id: z3.string().optional().describe("Chat ID to send the message to (uses default chat if omitted)"),
4007
+ model: z3.string().optional().describe("Model to use"),
4008
+ plan_mode: z3.boolean().optional().describe("Whether to use plan mode")
3495
4009
  }
3496
4010
  },
3497
4011
  async (args) => {
@@ -3512,7 +4026,7 @@ registerTool(
3512
4026
  {
3513
4027
  description: "Delete a replica. This terminates the workspace and cannot be undone.",
3514
4028
  inputSchema: {
3515
- id: z2.string()
4029
+ id: z3.string()
3516
4030
  }
3517
4031
  },
3518
4032
  async (args) => {
@@ -3554,7 +4068,7 @@ registerTool(
3554
4068
  {
3555
4069
  description: "List chats in a replica workspace",
3556
4070
  inputSchema: {
3557
- id: z2.string().describe("Replica ID")
4071
+ id: z3.string().describe("Replica ID")
3558
4072
  }
3559
4073
  },
3560
4074
  async (args) => {
@@ -3570,9 +4084,9 @@ registerTool(
3570
4084
  {
3571
4085
  description: "Create a new chat in a replica workspace",
3572
4086
  inputSchema: {
3573
- id: z2.string().describe("Replica ID"),
4087
+ id: z3.string().describe("Replica ID"),
3574
4088
  provider: codingAgentSchema.describe("Coding agent provider for the chat"),
3575
- title: z2.string().optional().describe("Optional title for the chat")
4089
+ title: z3.string().optional().describe("Optional title for the chat")
3576
4090
  }
3577
4091
  },
3578
4092
  async (args) => {
@@ -3590,8 +4104,8 @@ registerTool(
3590
4104
  {
3591
4105
  description: "Delete a chat from a replica workspace",
3592
4106
  inputSchema: {
3593
- id: z2.string().describe("Replica ID"),
3594
- chat_id: z2.string().describe("Chat ID to delete")
4107
+ id: z3.string().describe("Replica ID"),
4108
+ chat_id: z3.string().describe("Chat ID to delete")
3595
4109
  }
3596
4110
  },
3597
4111
  async (args) => {
@@ -3607,8 +4121,8 @@ registerTool(
3607
4121
  {
3608
4122
  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
4123
  inputSchema: {
3610
- id: z2.string(),
3611
- chat_id: z2.string().optional().describe("Read history for a specific chat (uses default chat if omitted)")
4124
+ id: z3.string(),
4125
+ chat_id: z3.string().optional().describe("Read history for a specific chat (uses default chat if omitted)")
3612
4126
  }
3613
4127
  },
3614
4128
  async (args) => {
@@ -3642,7 +4156,7 @@ registerTool(
3642
4156
  {
3643
4157
  description: "Get warm and start hook logs for a replica workspace",
3644
4158
  inputSchema: {
3645
- id: z2.string().describe("Replica ID")
4159
+ id: z3.string().describe("Replica ID")
3646
4160
  }
3647
4161
  },
3648
4162
  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.14",
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.14',
75
75
  });
76
76
 
77
77
  const registerTool = server.registerTool.bind(server) as RegisterToolFn;