@sideboard-ai/core 0.1.299 → 0.1.301

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 (38) hide show
  1. package/dist/agents/claude-runner.cjs +36 -0
  2. package/dist/agents/claude-runner.js +17 -0
  3. package/dist/{agents-JCCJJMA7.js → agents-ETD2LWP4.js} +4 -4
  4. package/dist/{agents-IVKPCK56.js → agents-V2SRC2LH.js} +5 -4
  5. package/dist/{chunk-EB7B4D2Z.js → chunk-2KJE53ZJ.js} +15 -88
  6. package/dist/{chunk-3IXGI2FP.js → chunk-3XDU7RIE.js} +1 -1
  7. package/dist/{chunk-QB4UQA5L.js → chunk-7SEWZXPT.js} +2 -2
  8. package/dist/{chunk-F3JYJ4UB.js → chunk-EP4IIKP4.js} +4 -4
  9. package/dist/{chunk-HTL4FBTP.js → chunk-FBZODCO4.js} +65 -0
  10. package/dist/chunk-FO3NKZVJ.js +104 -0
  11. package/dist/{chunk-YAILSKW3.js → chunk-FPJFK4LB.js} +11 -11
  12. package/dist/{chunk-EB3F5AWV.js → chunk-FUXOZHHF.js} +15 -54
  13. package/dist/{chunk-NF3WDI7F.js → chunk-JTW4CZEQ.js} +41 -48
  14. package/dist/{chunk-G52HLBQR.js → chunk-KIWICTLW.js} +1 -1
  15. package/dist/{chunk-UMALFEDS.js → chunk-LZ744H2I.js} +2 -2
  16. package/dist/{chunk-MMIDAD6J.js → chunk-OJKSDRZU.js} +4 -4
  17. package/dist/{chunk-K5EACGG2.js → chunk-WV33I2GC.js} +13 -0
  18. package/dist/{conductor-UQDDDW35.js → conductor-57KZLWTB.js} +5 -4
  19. package/dist/{conductor-JLHXMMKR.js → conductor-57UKACQA.js} +4 -4
  20. package/dist/{coordinator-prompt-SSUYZSJO.js → coordinator-prompt-2TBRFJZ5.js} +3 -3
  21. package/dist/{coordinator-prompt-HKLVS26O.js → coordinator-prompt-VOWUJRVJ.js} +4 -3
  22. package/dist/{global-workspace-MHE6FCUZ.js → global-workspace-T5C7EMVV.js} +3 -3
  23. package/dist/{global-workspace-I32EWKX3.js → global-workspace-V6SOEETA.js} +4 -3
  24. package/dist/index.cjs +164 -120
  25. package/dist/index.d.cts +29 -18
  26. package/dist/index.d.ts +29 -18
  27. package/dist/index.js +43 -19
  28. package/dist/mcp/run-stdio.cjs +72 -57
  29. package/dist/mcp/run-stdio.js +6 -6
  30. package/dist/{orchestrator-SX4SGZHF.js → orchestrator-C43M3JDK.js} +6 -6
  31. package/dist/{orchestrator-ESLNXVOX.js → orchestrator-JEF7JC3S.js} +7 -6
  32. package/dist/{thread-store-5XXHSWMN.js → thread-store-BMNJEJUH.js} +1 -1
  33. package/dist/{thread-store-WXLXKDVN.js → thread-store-POAYB2ID.js} +2 -1
  34. package/dist/{workspaces-O3L32CDO.js → workspaces-7ZYTTB4A.js} +3 -3
  35. package/dist/{workspaces-ATMIKXLK.js → workspaces-EWVIEJ4O.js} +4 -3
  36. package/dist/{worktree-RMYXBTJG.js → worktree-2Q6J2VWZ.js} +3 -2
  37. package/dist/{worktree-6G5X4ABY.js → worktree-ZAJGYKCB.js} +2 -2
  38. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -43,6 +43,103 @@ var init_thread = __esm({
43
43
  }
44
44
  });
45
45
 
46
+ // src/agents/claude-mcp.ts
47
+ function parseMcpList(text7) {
48
+ const servers = [];
49
+ const seen = /* @__PURE__ */ new Set();
50
+ for (const raw of text7.split("\n")) {
51
+ const line = raw.trim();
52
+ if (!line || /^Checking MCP/i.test(line)) continue;
53
+ const m = line.match(/^(.+?):\s+\S+/);
54
+ if (!m) continue;
55
+ const name = m[1].trim();
56
+ if (!name || seen.has(name)) continue;
57
+ const needsAuth = /Needs authentication/i.test(line);
58
+ const connected = !needsAuth && /Connected/i.test(line);
59
+ if (!needsAuth && !connected) continue;
60
+ seen.add(name);
61
+ servers.push({ name, connected, needsAuth });
62
+ }
63
+ return servers;
64
+ }
65
+ function sanitizeMcpServerName(name) {
66
+ return name.replace(/[^A-Za-z0-9_-]/g, "_");
67
+ }
68
+ function mcpServerIdForPermission(toolName, serverName) {
69
+ if (toolName.startsWith("mcp__")) {
70
+ const rest = toolName.slice("mcp__".length);
71
+ const split = rest.indexOf("__");
72
+ const id = (split === -1 ? rest : rest.slice(0, split)).trim();
73
+ if (id && id !== "*") return id;
74
+ }
75
+ const sanitized = sanitizeMcpServerName((serverName ?? "").trim());
76
+ return sanitized || null;
77
+ }
78
+ function mcpServerPermissionUpdate(serverId) {
79
+ return {
80
+ type: "addRules",
81
+ behavior: "allow",
82
+ destination: "session",
83
+ rules: [{ toolName: `mcp__${serverId}` }, { toolName: `mcp__${serverId}__*` }]
84
+ };
85
+ }
86
+ function mcpAllowTools(servers) {
87
+ const out = [];
88
+ for (const s of servers) {
89
+ if (!s.connected) continue;
90
+ const id = sanitizeMcpServerName(s.name);
91
+ if (!id) continue;
92
+ out.push(`mcp__${id}`);
93
+ out.push(`mcp__${id}__*`);
94
+ }
95
+ return out;
96
+ }
97
+ function mcpAllowToolsFromNames(names) {
98
+ return mcpAllowTools(
99
+ [...names].map((name) => name.trim()).filter(Boolean).map((name) => ({ name, connected: true, needsAuth: false }))
100
+ );
101
+ }
102
+ function mcpAuthWarnings(servers) {
103
+ const needing = servers.filter((s) => s.needsAuth).map((s) => s.name);
104
+ if (needing.length === 0) return [];
105
+ return [
106
+ `MCP needs login: ${needing.join(", ")}. Run: claude mcp login "<name>"`
107
+ ];
108
+ }
109
+ var BASE_ALLOWED_TOOLS, CLAUDE_PRINT_BG_WAIT_CEILING_MS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_POSTHOG_CONNECTOR_ALLOWED_TOOLS;
110
+ var init_claude_mcp = __esm({
111
+ "src/agents/claude-mcp.ts"() {
112
+ "use strict";
113
+ BASE_ALLOWED_TOOLS = [
114
+ "Edit",
115
+ "Write",
116
+ "Bash",
117
+ "Read",
118
+ "Glob",
119
+ "Grep",
120
+ "WebFetch",
121
+ "WebSearch",
122
+ "Task",
123
+ "Agent",
124
+ "TaskOutput",
125
+ "TaskStop",
126
+ "EnterWorktree",
127
+ "ExitWorktree",
128
+ "Skill"
129
+ ];
130
+ CLAUDE_PRINT_BG_WAIT_CEILING_MS = 72e5;
131
+ CLAUDE_CHROME_ALLOWED_TOOLS = [
132
+ "mcp__claude-in-chrome",
133
+ "mcp__claude-in-chrome__*",
134
+ "Skill(claude-in-chrome)"
135
+ ];
136
+ CLAUDE_POSTHOG_CONNECTOR_ALLOWED_TOOLS = [
137
+ "mcp__claude_ai_PostHog",
138
+ "mcp__claude_ai_PostHog__*"
139
+ ];
140
+ }
141
+ });
142
+
46
143
  // src/agents/tool-permission.ts
47
144
  function setToolPermissionPublisher(next) {
48
145
  publisher = next;
@@ -68,6 +165,7 @@ function denied(message) {
68
165
  return { behavior: "deny", message, decisionClassification: "user_reject" };
69
166
  }
70
167
  function sessionGrants(item) {
168
+ if (item.mcpServerId) return [mcpServerPermissionUpdate(item.mcpServerId)];
71
169
  if (Array.isArray(item.suggestions) && item.suggestions.length > 0) {
72
170
  return item.suggestions;
73
171
  }
@@ -126,12 +224,16 @@ function askToolPermission(ask) {
126
224
  const toolName = clean(ask.toolName);
127
225
  if (!threadId || !toolName) return Promise.resolve(denied("Missing chat or tool."));
128
226
  const described = describeToolPermission({ ...ask, threadId, toolName });
227
+ const mcpServerId = mcpServerIdForPermission(toolName, ask.mcpServer) ?? void 0;
129
228
  const prompt = {
130
229
  id: (0, import_node_crypto.randomUUID)(),
131
230
  title: described.title,
132
231
  detail: described.detail,
133
232
  path: described.path || void 0,
134
- allowSession: !ask.suppressAlwaysAllow,
233
+ // Claude hides "always allow" when the saved rule would be broader than
234
+ // this call. For MCP that broader rule is what we want: the whole server.
235
+ allowSession: mcpServerId ? true : !ask.suppressAlwaysAllow,
236
+ ...mcpServerId ? { sessionLabel: "Allow this MCP" } : {},
135
237
  defaultToNo: Boolean(ask.defaultToNo)
136
238
  };
137
239
  return new Promise((resolve2) => {
@@ -140,6 +242,7 @@ function askToolPermission(ask) {
140
242
  threadId,
141
243
  prompt,
142
244
  suggestions: ask.suggestions,
245
+ mcpServerId,
143
246
  resolve: resolve2
144
247
  };
145
248
  const q = queues.get(threadId) ?? [];
@@ -175,6 +278,7 @@ var init_tool_permission = __esm({
175
278
  "use strict";
176
279
  import_node_crypto = require("crypto");
177
280
  import_node_path = require("path");
281
+ init_claude_mcp();
178
282
  queues = /* @__PURE__ */ new Map();
179
283
  publisher = null;
180
284
  }
@@ -4702,6 +4806,15 @@ function resolveThreadEffort(raw) {
4702
4806
  if (raw.fast) return "low";
4703
4807
  return "high";
4704
4808
  }
4809
+ function normalizeApprovedMcpServers(raw) {
4810
+ if (!Array.isArray(raw)) return void 0;
4811
+ const ids = [
4812
+ ...new Set(
4813
+ raw.map((value) => typeof value === "string" ? sanitizeMcpServerName(value.trim()) : "").filter(Boolean)
4814
+ )
4815
+ ];
4816
+ return ids.length > 0 ? ids : void 0;
4817
+ }
4705
4818
  function normalizeRunScriptRequest(raw) {
4706
4819
  if (!raw || typeof raw !== "object") return void 0;
4707
4820
  const req = raw;
@@ -4727,6 +4840,7 @@ function normalizeThread(raw) {
4727
4840
  planMode: Boolean(raw.planMode),
4728
4841
  isolateCodexPlugins: raw.isolateCodexPlugins === true ? true : void 0,
4729
4842
  autonomy: raw.autonomy ?? "default",
4843
+ approvedMcpServers: normalizeApprovedMcpServers(raw.approvedMcpServers),
4730
4844
  lastError: raw.lastError ?? null,
4731
4845
  agentPid: raw.agentPid ?? null,
4732
4846
  attachments: Array.isArray(raw.attachments) ? raw.attachments : [],
@@ -4965,6 +5079,7 @@ var init_thread_store = __esm({
4965
5079
  import_node_path15 = require("path");
4966
5080
  import_proper_lockfile = __toESM(require("proper-lockfile"), 1);
4967
5081
  init_thinking_effort();
5082
+ init_claude_mcp();
4968
5083
  init_cursor_store();
4969
5084
  init_paths();
4970
5085
  listCache = null;
@@ -6613,13 +6728,13 @@ function isClosedPrState(prUrl, prState) {
6613
6728
  if (!prUrl?.trim()) return false;
6614
6729
  return (prState ?? "").trim().toUpperCase() === "CLOSED";
6615
6730
  }
6616
- function listBadge(id, title, conflict = false) {
6731
+ function listBadge(id, title, note) {
6617
6732
  return {
6618
6733
  id,
6619
6734
  label: id,
6620
- title: conflict ? `${title} \xB7 merge conflicts` : title,
6735
+ title: note ? `${title} \xB7 ${note}` : title,
6621
6736
  mod: `is-${id}`,
6622
- ...conflict ? { conflict: true } : {}
6737
+ ...note ? { conflict: true } : {}
6623
6738
  };
6624
6739
  }
6625
6740
  function hasMergeConflict(thread) {
@@ -6630,69 +6745,52 @@ function hasMergeConflict(thread) {
6630
6745
  isInMergeQueue: thread.prIsInMergeQueue
6631
6746
  }) === "conflicts";
6632
6747
  }
6633
- function groupPrError(group) {
6748
+ function blockerNote(group) {
6634
6749
  const open = group.filter(
6635
6750
  (thread) => isOpenPrState(thread.prUrl, thread.prState) && !thread.prIsInMergeQueue
6636
6751
  );
6752
+ const parts = [];
6637
6753
  if (open.some(
6638
6754
  (thread) => thread.prChecksFailed || mergeStateSuggestsCheckFailure({
6639
6755
  mergeStateStatus: thread.prMergeStateStatus ?? null,
6640
6756
  reviewDecision: thread.prReviewDecision ?? null
6641
6757
  })
6642
6758
  )) {
6643
- return { label: "failing", title: "Checks failing" };
6644
- }
6645
- if (open.some(
6759
+ parts.push("checks failing");
6760
+ } else if (open.some(
6646
6761
  (thread) => !thread.prIsDraft && (thread.prReviewDecision ?? "").trim().toUpperCase() === "CHANGES_REQUESTED"
6647
6762
  )) {
6648
- return { label: "changes", title: "Changes requested" };
6649
- }
6650
- return void 0;
6651
- }
6652
- function withPrError(badges, error, conflict) {
6653
- if (!error) return badges;
6654
- const [first] = badges;
6655
- if (!first) {
6656
- return [
6657
- {
6658
- id: "error",
6659
- label: error.label,
6660
- title: conflict ? `${error.title} \xB7 merge conflicts` : error.title,
6661
- mod: "is-error",
6662
- ...conflict ? { conflict: true } : {}
6663
- }
6664
- ];
6763
+ parts.push("changes requested");
6665
6764
  }
6666
- return [{ ...first, error }];
6765
+ if (open.some(hasMergeConflict)) parts.push("merge conflicts");
6766
+ return parts.length > 0 ? parts.join(" \xB7 ") : void 0;
6667
6767
  }
6668
6768
  function classifyWorktreeListBadges(group, viewerLogin = "", reviewLabel = "") {
6669
6769
  if (group.length === 0 || group.some((t) => t.cowboy)) return [];
6670
6770
  if (group.some((t) => isMergedPrState(t.prUrl, t.prState))) {
6671
6771
  return [listBadge("merged", "Merged")];
6672
6772
  }
6673
- const conflict = group.some(hasMergeConflict);
6674
- const error = groupPrError(group);
6675
- const finish2 = (badges) => withPrError(badges, error, conflict);
6773
+ const note = blockerNote(group);
6676
6774
  if (group.some((t) => isApprovedOpenPr(t))) {
6677
- return finish2([listBadge("approved", "Approved, not merged", conflict)]);
6775
+ return [listBadge("approved", "Approved, not merged", note)];
6678
6776
  }
6679
6777
  const open = group.filter((t) => isOpenPrState(t.prUrl, t.prState));
6680
6778
  if (open.length === 0) {
6681
6779
  if (group.some((t) => isClosedPrState(t.prUrl, t.prState))) return [];
6682
- return finish2([listBadge("new", "No pull request")]);
6780
+ return [listBadge("new", "No pull request")];
6683
6781
  }
6684
6782
  const ownership = classifyWorktreeOwnership(group, viewerLogin);
6685
6783
  if (ownership === "reviewing") {
6686
- return finish2([listBadge("review", "Someone else's PR", conflict)]);
6784
+ return [listBadge("review", "Someone else's PR", note)];
6687
6785
  }
6688
- if (!normalizeViewerLogin(viewerLogin)) return finish2([]);
6786
+ if (!normalizeViewerLogin(viewerLogin)) return [];
6689
6787
  if (!open.some((t) => !t.prIsDraft)) {
6690
- return finish2([listBadge("draft", "Draft pull request", conflict)]);
6788
+ return [listBadge("draft", "Draft pull request", note)];
6691
6789
  }
6692
6790
  const readyLabel = reviewLabel.trim();
6693
6791
  const inReview = readyLabel ? labelsInclude(groupPrLabels(group), readyLabel) : true;
6694
- if (!inReview) return finish2([]);
6695
- return finish2([listBadge("pending", "Waiting for review", conflict)]);
6792
+ if (!inReview) return [];
6793
+ return [listBadge("pending", "Waiting for review", note)];
6696
6794
  }
6697
6795
  var init_worktree_list_badges = __esm({
6698
6796
  "src/board/worktree-list-badges.ts"() {
@@ -13324,85 +13422,6 @@ var init_brightsy = __esm({
13324
13422
  }
13325
13423
  });
13326
13424
 
13327
- // src/agents/claude-mcp.ts
13328
- function parseMcpList(text7) {
13329
- const servers = [];
13330
- const seen = /* @__PURE__ */ new Set();
13331
- for (const raw of text7.split("\n")) {
13332
- const line = raw.trim();
13333
- if (!line || /^Checking MCP/i.test(line)) continue;
13334
- const m = line.match(/^(.+?):\s+\S+/);
13335
- if (!m) continue;
13336
- const name = m[1].trim();
13337
- if (!name || seen.has(name)) continue;
13338
- const needsAuth = /Needs authentication/i.test(line);
13339
- const connected = !needsAuth && /Connected/i.test(line);
13340
- if (!needsAuth && !connected) continue;
13341
- seen.add(name);
13342
- servers.push({ name, connected, needsAuth });
13343
- }
13344
- return servers;
13345
- }
13346
- function sanitizeMcpServerName(name) {
13347
- return name.replace(/[^A-Za-z0-9_-]/g, "_");
13348
- }
13349
- function mcpAllowTools(servers) {
13350
- const out = [];
13351
- for (const s of servers) {
13352
- if (!s.connected) continue;
13353
- const id = sanitizeMcpServerName(s.name);
13354
- if (!id) continue;
13355
- out.push(`mcp__${id}`);
13356
- out.push(`mcp__${id}__*`);
13357
- }
13358
- return out;
13359
- }
13360
- function mcpAllowToolsFromNames(names) {
13361
- return mcpAllowTools(
13362
- [...names].map((name) => name.trim()).filter(Boolean).map((name) => ({ name, connected: true, needsAuth: false }))
13363
- );
13364
- }
13365
- function mcpAuthWarnings(servers) {
13366
- const needing = servers.filter((s) => s.needsAuth).map((s) => s.name);
13367
- if (needing.length === 0) return [];
13368
- return [
13369
- `MCP needs login: ${needing.join(", ")}. Run: claude mcp login "<name>"`
13370
- ];
13371
- }
13372
- var BASE_ALLOWED_TOOLS, CLAUDE_PRINT_BG_WAIT_CEILING_MS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_POSTHOG_CONNECTOR_ALLOWED_TOOLS;
13373
- var init_claude_mcp = __esm({
13374
- "src/agents/claude-mcp.ts"() {
13375
- "use strict";
13376
- BASE_ALLOWED_TOOLS = [
13377
- "Edit",
13378
- "Write",
13379
- "Bash",
13380
- "Read",
13381
- "Glob",
13382
- "Grep",
13383
- "WebFetch",
13384
- "WebSearch",
13385
- "Task",
13386
- "Agent",
13387
- "TaskOutput",
13388
- "TaskStop",
13389
- "EnterWorktree",
13390
- "ExitWorktree",
13391
- "Skill"
13392
- ];
13393
- CLAUDE_PRINT_BG_WAIT_CEILING_MS = 72e5;
13394
- CLAUDE_CHROME_ALLOWED_TOOLS = [
13395
- "mcp__claude-in-chrome",
13396
- "mcp__claude-in-chrome__*",
13397
- "Skill(claude-in-chrome)"
13398
- ];
13399
- CLAUDE_POSTHOG_CONNECTOR_ALLOWED_TOOLS = [
13400
- "mcp__claude_ai_PostHog",
13401
- "mcp__claude_ai_PostHog__*"
13402
- ];
13403
- }
13404
- });
13405
-
13406
13425
  // src/agent-rpc/protocol.ts
13407
13426
  function isJsonRpcRequest(value) {
13408
13427
  if (!value || typeof value !== "object") return false;
@@ -15252,12 +15271,13 @@ var init_claude = __esm({
15252
15271
  allowedTools = [
15253
15272
  ...BASE_ALLOWED_TOOLS,
15254
15273
  ...SIDEBOARD_MCP_ALLOWED_TOOLS,
15255
- ...brightsyMcpAllowedTools(injectedBrightsyNames)
15274
+ ...brightsyMcpAllowedTools(injectedBrightsyNames),
15275
+ ...mcpAllowToolsFromNames(thread.approvedMcpServers ?? [])
15256
15276
  ];
15257
15277
  } else {
15258
15278
  allowedTools = [
15259
15279
  ...BASE_ALLOWED_TOOLS,
15260
- ...mcpAllowToolsFromNames(Object.keys(userMcpEntries)),
15280
+ ...mcpAllowToolsFromNames([...Object.keys(userMcpEntries), ...thread.approvedMcpServers ?? []]),
15261
15281
  ...sideboardWorktreeAllowedTools({
15262
15282
  github: true,
15263
15283
  linear: isLinearConnected(),
@@ -15297,7 +15317,8 @@ var init_claude = __esm({
15297
15317
  "--verbose",
15298
15318
  "--include-partial-messages",
15299
15319
  "--permission-mode",
15300
- mode.claude
15320
+ mode.claude,
15321
+ ...mode.claude === "bypassPermissions" ? ["--dangerously-skip-permissions"] : []
15301
15322
  ];
15302
15323
  if (systemPrompt) {
15303
15324
  args.push("--append-system-prompt", systemPrompt);
@@ -31751,6 +31772,8 @@ __export(index_exports, {
31751
31772
  mcpAllowTools: () => mcpAllowTools,
31752
31773
  mcpAllowToolsFromNames: () => mcpAllowToolsFromNames,
31753
31774
  mcpAuthWarnings: () => mcpAuthWarnings,
31775
+ mcpServerIdForPermission: () => mcpServerIdForPermission,
31776
+ mcpServerPermissionUpdate: () => mcpServerPermissionUpdate,
31754
31777
  mcpStatusThinkingFromClaudeInit: () => mcpStatusThinkingFromClaudeInit,
31755
31778
  mentionsPrGoal: () => mentionsPrGoal,
31756
31779
  mergeAgentGitAuthEnv: () => mergeAgentGitAuthEnv,
@@ -36492,6 +36515,7 @@ init_wait_for_turn();
36492
36515
  // src/agent-rpc/permission-handler.ts
36493
36516
  init_agent_block();
36494
36517
  init_thread_store();
36518
+ init_claude_mcp();
36495
36519
  init_tool_permission();
36496
36520
 
36497
36521
  // src/remote/phone-permission.ts
@@ -36518,6 +36542,7 @@ function permissionViewFromBlock(block) {
36518
36542
  detail: typeof prompt.detail === "string" ? prompt.detail : "",
36519
36543
  ...prompt.path ? { path: prompt.path } : {},
36520
36544
  allowSession: prompt.allowSession !== false,
36545
+ ...prompt.sessionLabel ? { sessionLabel: prompt.sessionLabel } : {},
36521
36546
  ...prompt.defaultToNo ? { defaultToNo: true } : {}
36522
36547
  };
36523
36548
  }
@@ -36606,10 +36631,27 @@ var handlePermissionAsk = async (params, _ctx) => {
36606
36631
  description: str4(rec.description).trim() || void 0,
36607
36632
  displayName: str4(rec.displayName).trim() || void 0,
36608
36633
  suggestions: rec.suggestions,
36634
+ mcpServer: str4(rec.mcpServer).trim() || void 0,
36609
36635
  suppressAlwaysAllow: rec.suppressAlwaysAllow === true,
36610
36636
  defaultToNo: rec.defaultToNo === true
36611
36637
  };
36612
- return askToolPermission(ask);
36638
+ const serverId = mcpServerIdForPermission(toolName, ask.mcpServer);
36639
+ if (serverId && readThread(threadId)?.approvedMcpServers?.includes(serverId)) {
36640
+ return {
36641
+ behavior: "allow",
36642
+ updatedPermissions: [mcpServerPermissionUpdate(serverId)],
36643
+ decisionClassification: "user_permanent"
36644
+ };
36645
+ }
36646
+ const answer = await askToolPermission(ask);
36647
+ if (serverId && answer.behavior === "allow" && answer.decisionClassification === "user_permanent") {
36648
+ const fresh = readThread(threadId);
36649
+ const prev = fresh?.approvedMcpServers ?? [];
36650
+ if (fresh && !prev.includes(serverId)) {
36651
+ updateThread(threadId, { approvedMcpServers: [...prev, serverId] });
36652
+ }
36653
+ }
36654
+ return answer;
36613
36655
  };
36614
36656
 
36615
36657
  // src/agent-rpc/handlers.ts
@@ -42013,7 +42055,7 @@ function listPhoneSidebar(viewerLogin = phoneViewerLogin()) {
42013
42055
  label: worktreeDisplayLabelForGroup(group),
42014
42056
  chats: group.map(summarize),
42015
42057
  ...tags.length > 0 ? { tags } : {},
42016
- ...badge ? { badge: { id: badge.id, label: badge.label, title: badge.title, ...badge.conflict ? { conflict: true } : {}, ...badge.error ? { error: badge.error } : {} } } : {}
42058
+ ...badge ? { badge: { id: badge.id, label: badge.label, title: badge.title, ...badge.conflict ? { conflict: true } : {} } } : {}
42017
42059
  };
42018
42060
  })
42019
42061
  }));
@@ -45934,6 +45976,8 @@ async function pollSlackOutboundWatches(opts) {
45934
45976
  mcpAllowTools,
45935
45977
  mcpAllowToolsFromNames,
45936
45978
  mcpAuthWarnings,
45979
+ mcpServerIdForPermission,
45980
+ mcpServerPermissionUpdate,
45937
45981
  mcpStatusThinkingFromClaudeInit,
45938
45982
  mentionsPrGoal,
45939
45983
  mergeAgentGitAuthEnv,