@sideboard-ai/core 0.1.310 → 0.1.312

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.
package/dist/index.js CHANGED
@@ -188,6 +188,7 @@ import {
188
188
  stampNotifyBlock,
189
189
  stampPlanBlock,
190
190
  startOrchestration,
191
+ stashMcpAppModelContext,
191
192
  statWorktreePath,
192
193
  syncBoardPins,
193
194
  takePendingTurnAttachments,
@@ -201,7 +202,7 @@ import {
201
202
  worktreeCleanupSettings,
202
203
  writeAgentBlock,
203
204
  writeWorktreeFile
204
- } from "./chunk-IT6A4FZF.js";
205
+ } from "./chunk-VIVXKN5N.js";
205
206
  import {
206
207
  LEGACY_PLAN_FILE_REL,
207
208
  PLAN_FILE_NAME,
@@ -237,9 +238,9 @@ import {
237
238
  buildClaudeStreamJsonUserMessage,
238
239
  claudeAdapter,
239
240
  claudeUsageOverLimitWindows,
241
+ closeMcpUiHost,
240
242
  codexAdapter,
241
243
  countCacheControlBlocks,
242
- cspFromMeta,
243
244
  cursorAdapter,
244
245
  decodeBrightsyTarget,
245
246
  dropCachedPrefixOnResume,
@@ -256,8 +257,8 @@ import {
256
257
  formatClaudeUsageTooltip,
257
258
  getAdapter,
258
259
  getClaudePlanUsage,
260
+ getMcpUiHost,
259
261
  hottestClaudeWindow,
260
- htmlFromResourceContent,
261
262
  isBrightsyConnected,
262
263
  isCursorApiKeyConfigured,
263
264
  isCursorAutoModel,
@@ -268,15 +269,12 @@ import {
268
269
  listCursorModels,
269
270
  listModelsForAgent,
270
271
  listOpencodeModels,
271
- listUserClaudeMcpServerEntries,
272
- matchClaudeMcpTool,
273
272
  mcpStatusThinkingFromClaudeInit,
274
273
  mcpUiContentSecurityPolicy,
275
274
  mergeSideboardIntoMcpServersJson,
276
275
  normalizeTurnInput,
277
276
  opencodeAdapter,
278
277
  parseClaudeUsagePayload,
279
- parseMcpServerLaunch,
280
278
  parseSessionQuotaResetAt,
281
279
  permissionMode,
282
280
  planModeTurnInstruction,
@@ -289,13 +287,12 @@ import {
289
287
  sideboardMcpProfile,
290
288
  splitTurnInputForSystemPrompt,
291
289
  threadRequestsBrightsyMcp,
292
- uiResourceUriFromMeta,
293
290
  userClaudeMcpConfigPath,
294
291
  userCursorMcpConfigPath,
295
292
  userMcpNamesToDisable,
296
293
  worktreeMcpToolNames,
297
294
  writeInjectedMcpConfig
298
- } from "./chunk-4YBQBVYL.js";
295
+ } from "./chunk-4FZUUH6W.js";
299
296
  import {
300
297
  brightsyMcpServerName,
301
298
  connectBrightsyTeam,
@@ -335,6 +332,7 @@ import {
335
332
  appendCompactedMessages,
336
333
  applyCompaction,
337
334
  applyForwardOccupancy,
335
+ askBlockStillWaiting,
338
336
  buildBrightsySessionSeed,
339
337
  buildForkTranscriptAttachment,
340
338
  buildReviewRequestAttachment,
@@ -380,6 +378,7 @@ import {
380
378
  killStaleWorktreeListeners,
381
379
  killableListenerPids,
382
380
  lastRequestOccupancy,
381
+ lastTopLevelToolIsAskUser,
383
382
  latestPendingPlanQuestions,
384
383
  linkSharedDirectories,
385
384
  listRunScripts,
@@ -424,7 +423,7 @@ import {
424
423
  workspaceAgentChatSummaries,
425
424
  worktreeRunPortsPath,
426
425
  wrapReviewSkillMarkdown
427
- } from "./chunk-VBVSJUM2.js";
426
+ } from "./chunk-QC2TQMPJ.js";
428
427
  import {
429
428
  STDERR_EVENT_MAX_CHARS,
430
429
  STDOUT_EVENT_MAX_CHARS,
@@ -1660,208 +1659,6 @@ function buildPathRefAttachment(input) {
1660
1659
  };
1661
1660
  }
1662
1661
 
1663
- // src/mcp-ui/host.ts
1664
- import { homedir } from "os";
1665
-
1666
- // src/mcp-ui/session.ts
1667
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
1668
- import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
1669
- import { StdioClientTransport, getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
1670
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
1671
- var REQUEST_TIMEOUT_MS = 2e4;
1672
- var UI_CAPABILITIES = {
1673
- extensions: {
1674
- "io.modelcontextprotocol/ui": {
1675
- mimeTypes: ["text/html;profile=mcp-app"]
1676
- }
1677
- }
1678
- };
1679
- async function openMcpUiSession(launch) {
1680
- return openMcpUiSessionFromTransport(transportForLaunch(launch));
1681
- }
1682
- async function openMcpUiSessionFromTransport(transport) {
1683
- const client = new Client(
1684
- { name: "sideboard", version: "1.0.0" },
1685
- { capabilities: UI_CAPABILITIES }
1686
- );
1687
- await client.connect(transport, { timeout: REQUEST_TIMEOUT_MS });
1688
- return {
1689
- async listTools() {
1690
- const listed = await client.listTools(void 0, { timeout: REQUEST_TIMEOUT_MS });
1691
- return listed.tools.map((tool) => {
1692
- const meta = tool._meta;
1693
- return {
1694
- name: tool.name,
1695
- ...tool.description ? { description: tool.description } : {},
1696
- ...tool.inputSchema ? { inputSchema: tool.inputSchema } : {},
1697
- resourceUri: uiResourceUriFromMeta(meta),
1698
- ...cspFromMeta(meta) ? { csp: cspFromMeta(meta) } : {}
1699
- };
1700
- });
1701
- },
1702
- async readResource(uri) {
1703
- const result = await client.readResource({ uri }, { timeout: REQUEST_TIMEOUT_MS });
1704
- const content = result.contents?.[0];
1705
- if (!content) return null;
1706
- const text5 = "text" in content && typeof content.text === "string" ? content.text : void 0;
1707
- const blob = "blob" in content && typeof content.blob === "string" ? content.blob : void 0;
1708
- return {
1709
- uri: content.uri,
1710
- ...content.mimeType ? { mimeType: content.mimeType } : {},
1711
- ...text5 != null ? { text: text5 } : {},
1712
- ...blob != null ? { blob } : {},
1713
- ...cspFromMeta(content._meta) ? { csp: cspFromMeta(content._meta) } : {}
1714
- };
1715
- },
1716
- async callTool(name, args) {
1717
- return client.callTool({ name, arguments: args }, void 0, { timeout: REQUEST_TIMEOUT_MS });
1718
- },
1719
- async close() {
1720
- await client.close();
1721
- }
1722
- };
1723
- }
1724
- function transportForLaunch(launch) {
1725
- if (launch.kind === "stdio") {
1726
- return new StdioClientTransport({
1727
- command: launch.command,
1728
- args: launch.args,
1729
- env: { ...getDefaultEnvironment(), ...launch.env },
1730
- stderr: "ignore",
1731
- ...launch.cwd ? { cwd: launch.cwd } : {}
1732
- });
1733
- }
1734
- const url = new URL(launch.url);
1735
- const requestInit = launch.headers ? { headers: launch.headers } : void 0;
1736
- if (launch.kind === "sse") {
1737
- return new SSEClientTransport(url, requestInit ? { requestInit } : void 0);
1738
- }
1739
- return new StreamableHTTPClientTransport(url, requestInit ? { requestInit } : void 0);
1740
- }
1741
-
1742
- // src/mcp-ui/host.ts
1743
- var TOOL_TTL_MS = 5 * 60 * 1e3;
1744
- var McpUiHost = class {
1745
- constructor(opts) {
1746
- this.opts = opts;
1747
- }
1748
- opts;
1749
- sessions = /* @__PURE__ */ new Map();
1750
- toolLists = /* @__PURE__ */ new Map();
1751
- async viewForClaudeTool(toolName, projectDirs) {
1752
- const entries = this.userEntries(projectDirs);
1753
- const match = matchClaudeMcpTool(toolName, Object.keys(entries));
1754
- if (!match) return null;
1755
- const launch = parseMcpServerLaunch(entries[match.server] ?? {});
1756
- if (!launch) return null;
1757
- try {
1758
- const session = await this.sessionFor(match.server, launch);
1759
- const tools = await this.toolsFor(match.server, launch, session);
1760
- const tool = tools.find((item) => item.name === match.tool);
1761
- if (!tool?.resourceUri) return null;
1762
- const resource = await session.readResource(tool.resourceUri);
1763
- if (!resource) return null;
1764
- const html = htmlFromResourceContent(resource);
1765
- if (!html) return null;
1766
- const csp = resource.csp ?? tool.csp;
1767
- return {
1768
- server: match.server,
1769
- tool: match.tool,
1770
- uri: html.uri,
1771
- mimeType: html.mimeType,
1772
- html: html.html,
1773
- csp: mcpUiContentSecurityPolicy(csp)
1774
- };
1775
- } catch (err) {
1776
- console.error(
1777
- `[mcp-ui] ${match.server}/${match.tool}:`,
1778
- err instanceof Error ? err.message : err
1779
- );
1780
- return null;
1781
- }
1782
- }
1783
- async callTool(server, tool, args, projectDirs) {
1784
- const session = await this.sessionForName(server, projectDirs);
1785
- if (!session) throw new Error(`MCP server ${server} is not available`);
1786
- return session.callTool(tool, args);
1787
- }
1788
- async readResource(server, uri, projectDirs) {
1789
- const session = await this.sessionForName(server, projectDirs);
1790
- if (!session) throw new Error(`MCP server ${server} is not available`);
1791
- const resource = await session.readResource(uri);
1792
- if (!resource) throw new Error(`Resource ${uri} was empty`);
1793
- return {
1794
- contents: [
1795
- {
1796
- uri: resource.uri,
1797
- ...resource.mimeType ? { mimeType: resource.mimeType } : {},
1798
- ...resource.text != null ? { text: resource.text } : {},
1799
- ...resource.blob != null ? { blob: resource.blob } : {}
1800
- }
1801
- ]
1802
- };
1803
- }
1804
- async closeAll() {
1805
- const pending = [...this.sessions.values()];
1806
- this.sessions.clear();
1807
- this.toolLists.clear();
1808
- await Promise.all(
1809
- pending.map((session) => session.then((open) => open.close()).catch(() => void 0))
1810
- );
1811
- }
1812
- userEntries(projectDirs) {
1813
- const out = {};
1814
- for (const [name, entry] of Object.entries(this.opts.entries(projectDirs))) {
1815
- if (!name.trim() || isInjectedOrchMcpName(name) || !entry) continue;
1816
- out[name] = entry;
1817
- }
1818
- return out;
1819
- }
1820
- async sessionForName(server, projectDirs) {
1821
- const entry = this.userEntries(projectDirs)[server];
1822
- if (!entry) return null;
1823
- const launch = parseMcpServerLaunch(entry);
1824
- if (!launch) return null;
1825
- return this.sessionFor(server, launch);
1826
- }
1827
- sessionFor(server, launch) {
1828
- const key = `${server}
1829
- ${JSON.stringify(launch)}`;
1830
- const existing = this.sessions.get(key);
1831
- if (existing) return existing;
1832
- const open = this.opts.openSession ?? openMcpUiSession;
1833
- const pending = open(launch).catch((err) => {
1834
- this.sessions.delete(key);
1835
- throw err;
1836
- });
1837
- this.sessions.set(key, pending);
1838
- return pending;
1839
- }
1840
- async toolsFor(server, launch, session) {
1841
- const key = `${server}
1842
- ${JSON.stringify(launch)}`;
1843
- const cached2 = this.toolLists.get(key);
1844
- if (cached2 && cached2.key === key && Date.now() - cached2.at < TOOL_TTL_MS) return cached2.tools;
1845
- const tools = await session.listTools();
1846
- this.toolLists.set(key, { at: Date.now(), key, tools });
1847
- return tools;
1848
- }
1849
- };
1850
- var singleton;
1851
- function getMcpUiHost() {
1852
- if (!singleton) {
1853
- singleton = new McpUiHost({
1854
- entries: (projectDirs) => listUserClaudeMcpServerEntries(homedir(), projectDirs)
1855
- });
1856
- }
1857
- return singleton;
1858
- }
1859
- function closeMcpUiHost() {
1860
- const host = singleton;
1861
- singleton = void 0;
1862
- if (host) void host.closeAll();
1863
- }
1864
-
1865
1662
  // src/remote/phone-chats.ts
1866
1663
  import { randomUUID as randomUUID2 } from "crypto";
1867
1664
 
@@ -2452,13 +2249,21 @@ function parsePhoneMcpUi(value, op, chatId) {
2452
2249
  }
2453
2250
  return { op: "mcp-ui", chatId, tool: tool2, callId, reqId: reqId2 };
2454
2251
  }
2252
+ if (op === "mcp-ui-context") {
2253
+ const reqId2 = controlText(value, "reqId");
2254
+ const text5 = controlText(value, "text");
2255
+ if (!reqId2 || reqId2.length > 40 || !text5 || text5.length > 8e3) return "invalid";
2256
+ return { op, chatId, reqId: reqId2, text: text5 };
2257
+ }
2455
2258
  if (op !== "mcp-ui-call" && op !== "mcp-ui-read") return "invalid";
2456
2259
  const reqId = controlText(value, "reqId");
2457
2260
  const server = controlText(value, "server");
2458
2261
  if (!reqId || reqId.length > 40 || !server || server.length > 120) return "invalid";
2459
2262
  if (op === "mcp-ui-read") {
2460
2263
  const uri = controlText(value, "uri");
2461
- if (!uri.startsWith("ui://") || uri.length > 500) return "invalid";
2264
+ if (!uri || uri.length > 500 || /\s/.test(uri) || /^(javascript|data):/i.test(uri)) {
2265
+ return "invalid";
2266
+ }
2462
2267
  return { op, chatId, reqId, server, uri };
2463
2268
  }
2464
2269
  const tool = controlText(value, "tool");
@@ -2676,7 +2481,7 @@ function parsePhoneControl(value) {
2676
2481
  }
2677
2482
  if (op === "pane") return parsePhonePane(value, chatId);
2678
2483
  if (op === "open-artifact") return parseOpenArtifact(value, chatId);
2679
- if (op === "mcp-ui" || op === "mcp-ui-call" || op === "mcp-ui-read") return parsePhoneMcpUi(value, op, chatId);
2484
+ if (op === "mcp-ui" || op === "mcp-ui-call" || op === "mcp-ui-read" || op === "mcp-ui-context") return parsePhoneMcpUi(value, op, chatId);
2680
2485
  if (op === "open-file" || op === "media") {
2681
2486
  const path2 = textField(value, "path");
2682
2487
  if (!path2 || path2.length > 2e3) return "invalid";
@@ -2848,6 +2653,7 @@ function parsePhoneCreate(value) {
2848
2653
  }
2849
2654
  function summarize(thread) {
2850
2655
  const pr = phonePrPill(thread);
2656
+ const block = visibleAgentBlock(thread);
2851
2657
  return {
2852
2658
  id: thread.id,
2853
2659
  title: thread.title?.trim() || "Untitled",
@@ -2858,14 +2664,12 @@ function summarize(thread) {
2858
2664
  ...pr?.url ? { prUrl: pr.url } : {},
2859
2665
  ...pr?.number ? { prNumber: pr.number } : {},
2860
2666
  ...pr?.status ? { prStatus: pr.status } : {},
2861
- ...thread.agentBlock ? { needsYou: true, blockedReason: thread.agentBlock.reason } : {}
2667
+ ...block ? { needsYou: true, blockedReason: block.reason } : {}
2862
2668
  };
2863
2669
  }
2864
2670
  function previewOf2(thread) {
2865
- const messages = listPhoneMessages(thread);
2866
- const last = messages[messages.length - 1];
2867
- if (!last) return "";
2868
- return clip(last.text.replace(/\s+/g, " ").trim(), PREVIEW_LIMIT2);
2671
+ const last = listPhoneMessages(thread).at(-1);
2672
+ return last ? clip(last.text.replace(/\s+/g, " ").trim(), PREVIEW_LIMIT2) : "";
2869
2673
  }
2870
2674
  function repoName2(repoPath) {
2871
2675
  return repoPath.replace(/\/$/, "").split("/").pop() || repoPath;
@@ -3215,7 +3019,8 @@ function emitChatTurn(opts, chatId, threadId) {
3215
3019
  const pending = latestPendingPlanQuestions({
3216
3020
  messages: thread.messages,
3217
3021
  agentBlock: thread.agentBlock,
3218
- dismissedQuestionSignature: thread.dismissedQuestionSignature
3022
+ dismissedQuestionSignature: thread.dismissedQuestionSignature,
3023
+ status: thread.status
3219
3024
  });
3220
3025
  if (pending && result.taskState !== "failed" && result.taskState !== "canceled" && (result.taskState === "input-required" || thread.agentBlock?.source === "ask_user")) {
3221
3026
  emitControl(opts, {
@@ -3279,7 +3084,9 @@ function deliverPhoneTurn(opts, chatId) {
3279
3084
  // src/remote/phone-mcp-ui-listen.ts
3280
3085
  var PHONE_HTML_CAP = 7e5;
3281
3086
  function dispatchPhoneMcpUi(cmd, opts) {
3282
- if (cmd.op !== "mcp-ui" && cmd.op !== "mcp-ui-call" && cmd.op !== "mcp-ui-read") return false;
3087
+ if (cmd.op !== "mcp-ui" && cmd.op !== "mcp-ui-call" && cmd.op !== "mcp-ui-read" && cmd.op !== "mcp-ui-context") {
3088
+ return false;
3089
+ }
3283
3090
  const thread = readPhoneThread(cmd.chatId);
3284
3091
  const reqId = cmd.reqId;
3285
3092
  const chatId = cmd.chatId;
@@ -3289,6 +3096,11 @@ function dispatchPhoneMcpUi(cmd, opts) {
3289
3096
  return true;
3290
3097
  }
3291
3098
  const projectDirs = [thread.repoPath, thread.worktreePath];
3099
+ if (cmd.op === "mcp-ui-context") {
3100
+ stashMcpAppModelContext(thread.id, cmd.text);
3101
+ emitControl(opts, { op: "mcp-ui-context", chatId, reqId, result: {} });
3102
+ return true;
3103
+ }
3292
3104
  if (cmd.op === "mcp-ui-call") {
3293
3105
  void getMcpUiHost().callTool(cmd.server, cmd.tool, cmd.args, projectDirs).then((result) => emitControl(opts, { op: "mcp-ui-call", chatId, reqId, result })).catch(
3294
3106
  (err) => emitControl(opts, { op: "mcp-ui-call", chatId, reqId, error: phoneError(err) })
@@ -3575,7 +3387,8 @@ function republishPendingAsk(opts, chatId) {
3575
3387
  const pending = latestPendingPlanQuestions({
3576
3388
  messages: thread.messages,
3577
3389
  agentBlock: thread.agentBlock,
3578
- dismissedQuestionSignature: thread.dismissedQuestionSignature
3390
+ dismissedQuestionSignature: thread.dismissedQuestionSignature,
3391
+ status: thread.status
3579
3392
  });
3580
3393
  if (!pending) return;
3581
3394
  emitControl(opts, {
@@ -5800,7 +5613,8 @@ function runCoordinatorPrompt(opts, body, generation) {
5800
5613
  const pending = latestPendingPlanQuestions({
5801
5614
  messages: thread.messages,
5802
5615
  agentBlock: thread.agentBlock,
5803
- dismissedQuestionSignature: thread.dismissedQuestionSignature
5616
+ dismissedQuestionSignature: thread.dismissedQuestionSignature,
5617
+ status: thread.status
5804
5618
  });
5805
5619
  if (pending && result.taskState !== "failed" && result.taskState !== "canceled" && (result.taskState === "input-required" || thread.agentBlock?.source === "ask_user")) {
5806
5620
  opts.onOutbound({
@@ -6246,7 +6060,15 @@ import { basename as basename2 } from "path";
6246
6060
  import { watch } from "chokidar";
6247
6061
  var PHONE_BOARD_SYNC_MS = 300;
6248
6062
  function chatKey(row2) {
6249
- return [row2.id, row2.title, row2.status, row2.agent, row2.preview].join(" ");
6063
+ return [
6064
+ row2.id,
6065
+ row2.title,
6066
+ row2.status,
6067
+ row2.agent,
6068
+ row2.preview,
6069
+ row2.needsYou ? "1" : "0",
6070
+ row2.blockedReason ?? ""
6071
+ ].join(" ");
6250
6072
  }
6251
6073
  function phoneBoardKey(sidebar) {
6252
6074
  const orchestration = sidebar.orchestration.map(chatKey).join("\n");
@@ -8905,7 +8727,7 @@ async function startHeadlessHost(opts) {
8905
8727
  };
8906
8728
  const refreshRunning = async () => {
8907
8729
  try {
8908
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-XFE3U3RP.js");
8730
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-BYTSILLH.js");
8909
8731
  agentsRunning = getOrchestrator2().getRuntime().running;
8910
8732
  } catch {
8911
8733
  agentsRunning = 0;
@@ -10837,7 +10659,7 @@ async function handleJobStop(params, _ctx) {
10837
10659
  return stopDetachedJob(jobCwd(rec), id, reason ? { reason } : void 0);
10838
10660
  }
10839
10661
  async function defaultTurnWait(ref, timeoutMs) {
10840
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-XFE3U3RP.js");
10662
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-BYTSILLH.js");
10841
10663
  const orch = getOrchestrator2();
10842
10664
  const thread = await orch.waitForTurn(ref, timeoutMs, {
10843
10665
  resolveIfStillRunning: true
@@ -14213,7 +14035,7 @@ async function continueSourceThread(threadId, prompt) {
14213
14035
  await continueOnReply(threadId, prompt);
14214
14036
  return;
14215
14037
  }
14216
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-XFE3U3RP.js");
14038
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-BYTSILLH.js");
14217
14039
  await getOrchestrator2().send(threadId, prompt);
14218
14040
  } catch {
14219
14041
  }
@@ -14677,6 +14499,7 @@ export {
14677
14499
  archiveRemovedWorkspaceChats,
14678
14500
  archivesWithRemovedWorkspace,
14679
14501
  armSchedules,
14502
+ askBlockStillWaiting,
14680
14503
  assertAbleTimeCredential,
14681
14504
  assertAbleTimeMcpCredential,
14682
14505
  assertOrchestratorCapableAgent,
@@ -15117,6 +14940,7 @@ export {
15117
14940
  lastAgentReply,
15118
14941
  lastAssistantMessageText,
15119
14942
  lastRequestOccupancy,
14943
+ lastTopLevelToolIsAskUser,
15120
14944
  latestPendingPlanQuestions,
15121
14945
  linearAuthorizationHeader,
15122
14946
  linearCycleIsActive,
@@ -15463,6 +15287,7 @@ export {
15463
15287
  startRelayServer,
15464
15288
  startRemoteAccountLogin,
15465
15289
  startSlackOAuth,
15290
+ stashMcpAppModelContext,
15466
15291
  statWorktreePath,
15467
15292
  stopAgentRpcServer,
15468
15293
  stripBrightsyNdjsonNoise,