@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.
@@ -2153,6 +2153,16 @@ function isAskUserToolName(name) {
2153
2153
  const base = name.replace(/^mcp__sideboard__/i, "");
2154
2154
  return ASK_USER_TOOL_RE.test(name) || /^ask_user$/i.test(base);
2155
2155
  }
2156
+ function lastTopLevelToolIsAskUser(parts) {
2157
+ if (!parts?.length) return false;
2158
+ for (let i = parts.length - 1; i >= 0; i--) {
2159
+ const part = parts[i];
2160
+ if (part.type === "tool" && !part.parentId) {
2161
+ return isAskUserToolName(part.name);
2162
+ }
2163
+ }
2164
+ return false;
2165
+ }
2156
2166
  function questionsFromToolPart(part) {
2157
2167
  const fromInput = parsePlanQuestionsInput(part.input);
2158
2168
  if (fromInput.length) return fromInput;
@@ -2180,6 +2190,18 @@ function extractPendingPlanQuestions(parts) {
2180
2190
  }
2181
2191
  return null;
2182
2192
  }
2193
+ function askBlockStillWaiting(block, messages) {
2194
+ if (!messages?.length) return Boolean(block?.at);
2195
+ const askedAt = block?.at;
2196
+ if (!askedAt) return false;
2197
+ for (let i = messages.length - 1; i >= 0; i--) {
2198
+ const message = messages[i];
2199
+ if (message.role !== "user" || message.origin === "continue") continue;
2200
+ if (!message.ts) return true;
2201
+ return askedAt > message.ts;
2202
+ }
2203
+ return true;
2204
+ }
2183
2205
  var ASK_USER_TOOL_RE;
2184
2206
  var init_ask_user = __esm({
2185
2207
  "src/plan/ask-user.ts"() {
@@ -2196,14 +2218,7 @@ function needsCoordinatorAction(state) {
2196
2218
  return isIncompleteTaskState(state) || state === "input-required";
2197
2219
  }
2198
2220
  function endedOnAskUser(parts) {
2199
- if (!parts?.length) return false;
2200
- for (let i = parts.length - 1; i >= 0; i--) {
2201
- const p = parts[i];
2202
- if (p.type === "tool" && !p.parentId) {
2203
- return isAskUserToolName(p.name);
2204
- }
2205
- }
2206
- return false;
2221
+ return lastTopLevelToolIsAskUser(parts);
2207
2222
  }
2208
2223
  function deriveTaskState(input) {
2209
2224
  if (input.stillRunning) {
@@ -11317,6 +11332,19 @@ var init_brightsy = __esm({
11317
11332
  });
11318
11333
 
11319
11334
  // src/mcp-ui/meta.ts
11335
+ function matchClaudeMcpTool(toolName, serverNames) {
11336
+ const name = toolName.trim();
11337
+ if (!name.startsWith("mcp__")) return null;
11338
+ const ranked = [...serverNames].filter((s) => s.trim()).sort((a, b) => b.length - a.length);
11339
+ for (const server of ranked) {
11340
+ const prefix = `mcp__${server}__`;
11341
+ if (!name.startsWith(prefix)) continue;
11342
+ const tool = name.slice(prefix.length).trim();
11343
+ if (!tool) continue;
11344
+ return { server, tool };
11345
+ }
11346
+ return null;
11347
+ }
11320
11348
  function parseMcpServerLaunch(entry) {
11321
11349
  const command = typeof entry.command === "string" ? entry.command.trim() : "";
11322
11350
  if (command) {
@@ -11348,6 +11376,105 @@ function parseMcpServerLaunch(entry) {
11348
11376
  ...headers ? { headers } : {}
11349
11377
  };
11350
11378
  }
11379
+ function uiResourceUriFromMeta(meta) {
11380
+ const rec = asRecord3(meta);
11381
+ const nested = asRecord3(rec.ui);
11382
+ const fromNested = typeof nested.resourceUri === "string" ? nested.resourceUri.trim() : "";
11383
+ const legacy = typeof rec["ui/resourceUri"] === "string" ? rec["ui/resourceUri"].trim() : "";
11384
+ const uri = fromNested || legacy;
11385
+ return uri.startsWith("ui://") ? uri : null;
11386
+ }
11387
+ function cspFromMeta(meta) {
11388
+ const ui = asRecord3(asRecord3(meta).ui);
11389
+ const csp = asRecord3(ui.csp);
11390
+ const connectDomains = originList(csp.connectDomains);
11391
+ const resourceDomains = originList(csp.resourceDomains);
11392
+ const frameDomains = originList(csp.frameDomains);
11393
+ const baseUriDomains = originList(csp.baseUriDomains);
11394
+ if (!connectDomains?.length && !resourceDomains?.length && !frameDomains?.length && !baseUriDomains?.length) {
11395
+ return void 0;
11396
+ }
11397
+ return {
11398
+ ...connectDomains?.length ? { connectDomains } : {},
11399
+ ...resourceDomains?.length ? { resourceDomains } : {},
11400
+ ...frameDomains?.length ? { frameDomains } : {},
11401
+ ...baseUriDomains?.length ? { baseUriDomains } : {}
11402
+ };
11403
+ }
11404
+ function htmlFromResourceContent(content) {
11405
+ const uri = content.uri?.trim() ?? "";
11406
+ if (!uri.startsWith("ui://")) return null;
11407
+ const mimeType = content.mimeType?.trim() || "text/html";
11408
+ if (!isHtmlMime(mimeType)) return null;
11409
+ const html = htmlText(content.text, content.blob);
11410
+ if (!html) return null;
11411
+ return { uri, mimeType, html };
11412
+ }
11413
+ function mcpUiContentSecurityPolicy(csp) {
11414
+ const resource = (csp?.resourceDomains ?? []).join(" ");
11415
+ const connect = (csp?.connectDomains ?? []).join(" ");
11416
+ const frames = csp?.frameDomains?.length ? csp.frameDomains.join(" ") : "'none'";
11417
+ const base = csp?.baseUriDomains?.length ? csp.baseUriDomains.join(" ") : "'none'";
11418
+ const connectSrc = connect ? `'self' ${connect}`.trim() : "'none'";
11419
+ return [
11420
+ "default-src 'none'",
11421
+ `script-src 'self' 'unsafe-inline' ${resource}`.trim(),
11422
+ `style-src 'self' 'unsafe-inline' ${resource}`.trim(),
11423
+ `img-src 'self' data: ${resource}`.trim(),
11424
+ `font-src 'self' ${resource}`.trim(),
11425
+ `connect-src ${connectSrc}`,
11426
+ `media-src 'self' data: ${resource}`.trim(),
11427
+ `frame-src ${frames}`,
11428
+ `base-uri ${base}`,
11429
+ "object-src 'none'"
11430
+ ].join("; ");
11431
+ }
11432
+ function htmlText(text7, blob) {
11433
+ if (typeof text7 === "string" && text7.trim()) {
11434
+ return text7.length <= HTML_MAX ? text7 : null;
11435
+ }
11436
+ if (typeof blob !== "string" || !blob.trim()) return null;
11437
+ try {
11438
+ const decoded = decodeBase64(blob.trim());
11439
+ if (!decoded.trim() || decoded.length > HTML_MAX) return null;
11440
+ return decoded;
11441
+ } catch {
11442
+ return null;
11443
+ }
11444
+ }
11445
+ function decodeBase64(blob) {
11446
+ if (typeof Buffer !== "undefined") return Buffer.from(blob, "base64").toString("utf8");
11447
+ const bin = atob(blob);
11448
+ const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));
11449
+ return new TextDecoder().decode(bytes);
11450
+ }
11451
+ function isHtmlMime(mime) {
11452
+ const base = mime.split(";")[0]?.trim().toLowerCase() ?? "";
11453
+ return base === "text/html";
11454
+ }
11455
+ function originList(value) {
11456
+ if (!Array.isArray(value)) return void 0;
11457
+ const out = [];
11458
+ for (const item of value) {
11459
+ if (typeof item !== "string") continue;
11460
+ const origin = originToken(item);
11461
+ if (origin && !out.includes(origin)) out.push(origin);
11462
+ }
11463
+ return out;
11464
+ }
11465
+ function originToken(value) {
11466
+ const trimmed = value.trim();
11467
+ if (/^(https?|wss?):\/\/\*\.[a-z0-9.-]+(?::\d+)?$/i.test(trimmed)) return trimmed;
11468
+ try {
11469
+ const url = new URL(trimmed);
11470
+ if (url.protocol !== "https:" && url.protocol !== "http:" && url.protocol !== "wss:" && url.protocol !== "ws:") {
11471
+ return null;
11472
+ }
11473
+ return url.origin;
11474
+ } catch {
11475
+ return null;
11476
+ }
11477
+ }
11351
11478
  function stringRecord(value) {
11352
11479
  const rec = asRecord3(value);
11353
11480
  const out = {};
@@ -11360,9 +11487,11 @@ function asRecord3(value) {
11360
11487
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
11361
11488
  return value;
11362
11489
  }
11490
+ var HTML_MAX;
11363
11491
  var init_meta = __esm({
11364
11492
  "src/mcp-ui/meta.ts"() {
11365
11493
  "use strict";
11494
+ HTML_MAX = 15e5;
11366
11495
  }
11367
11496
  });
11368
11497
 
@@ -12401,6 +12530,519 @@ var init_orch_mcp_isolation = __esm({
12401
12530
  }
12402
12531
  });
12403
12532
 
12533
+ // src/mcp-ui/visibility.ts
12534
+ function visibilityFromMeta(meta) {
12535
+ const rec = asRecord4(meta);
12536
+ const ui = asRecord4(rec.ui);
12537
+ const raw = ui.visibility;
12538
+ if (!Array.isArray(raw)) return ["model", "app"];
12539
+ const out = [];
12540
+ for (const item of raw) {
12541
+ if (item !== "model" && item !== "app") continue;
12542
+ if (!out.includes(item)) out.push(item);
12543
+ }
12544
+ return out.length ? out : ["model", "app"];
12545
+ }
12546
+ function toolVisibleTo(visibility, audience) {
12547
+ const vis = visibility?.length ? visibility : ["model", "app"];
12548
+ return vis.includes(audience);
12549
+ }
12550
+ function asRecord4(value) {
12551
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
12552
+ return value;
12553
+ }
12554
+ var init_visibility = __esm({
12555
+ "src/mcp-ui/visibility.ts"() {
12556
+ "use strict";
12557
+ }
12558
+ });
12559
+
12560
+ // src/mcp-ui/proxy-server.ts
12561
+ function attachMcpUiProxyHandlers(server, session) {
12562
+ server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
12563
+ const tools = await session.listTools();
12564
+ return { tools: tools.filter((tool) => toolVisibleTo(tool.visibility, "model")).map(listedTool) };
12565
+ });
12566
+ server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
12567
+ const name = request.params.name;
12568
+ const tools = await session.listTools();
12569
+ const tool = tools.find((item) => item.name === name);
12570
+ if (!tool || !toolVisibleTo(tool.visibility, "model")) {
12571
+ throw new Error(`Tool ${name} is not available to the model`);
12572
+ }
12573
+ return await session.callTool(name, request.params.arguments ?? {});
12574
+ });
12575
+ server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
12576
+ const resource = await session.readResource(request.params.uri);
12577
+ if (!resource) throw new Error(`Resource ${request.params.uri} was empty`);
12578
+ return {
12579
+ contents: [
12580
+ {
12581
+ uri: resource.uri,
12582
+ ...resource.mimeType ? { mimeType: resource.mimeType } : {},
12583
+ ...resource.text != null ? { text: resource.text } : {},
12584
+ ...resource.blob != null ? { blob: resource.blob } : {}
12585
+ }
12586
+ ]
12587
+ };
12588
+ });
12589
+ server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => session.listResources());
12590
+ server.setRequestHandler(
12591
+ import_types.ListResourceTemplatesRequestSchema,
12592
+ async () => session.listResourceTemplates()
12593
+ );
12594
+ server.setRequestHandler(import_types.ListPromptsRequestSchema, async () => session.listPrompts());
12595
+ server.setRequestHandler(import_types.GetPromptRequestSchema, async (request) => {
12596
+ return await session.getPrompt(request.params.name, request.params.arguments);
12597
+ });
12598
+ server.fallbackRequestHandler = async (request) => {
12599
+ return await session.forward(request.method, request.params);
12600
+ };
12601
+ }
12602
+ function listedTool(tool) {
12603
+ return {
12604
+ name: tool.name,
12605
+ ...tool.description ? { description: tool.description } : {},
12606
+ inputSchema: tool.inputSchema ?? { type: "object", properties: {} },
12607
+ _meta: {
12608
+ ui: {
12609
+ ...tool.resourceUri ? { resourceUri: tool.resourceUri } : {},
12610
+ visibility: tool.visibility
12611
+ }
12612
+ }
12613
+ };
12614
+ }
12615
+ var import_server, import_types;
12616
+ var init_proxy_server = __esm({
12617
+ "src/mcp-ui/proxy-server.ts"() {
12618
+ "use strict";
12619
+ import_server = require("@modelcontextprotocol/sdk/server/index.js");
12620
+ import_types = require("@modelcontextprotocol/sdk/types.js");
12621
+ init_visibility();
12622
+ }
12623
+ });
12624
+
12625
+ // src/mcp-ui/proxy-http.ts
12626
+ async function listenMcpUiProxy(opts) {
12627
+ const token = (0, import_node_crypto6.randomBytes)(24).toString("hex");
12628
+ const transports = /* @__PURE__ */ new Map();
12629
+ const httpServer = (0, import_node_http.createServer)((req, res) => {
12630
+ void handle(req, res);
12631
+ });
12632
+ await new Promise((resolve3, reject) => {
12633
+ httpServer.once("error", reject);
12634
+ httpServer.listen(0, "127.0.0.1", () => resolve3());
12635
+ });
12636
+ const addr = httpServer.address();
12637
+ if (!addr || typeof addr === "string") throw new Error("mcp-ui proxy failed to bind");
12638
+ const origin = `http://127.0.0.1:${addr.port}`;
12639
+ async function handle(req, res) {
12640
+ if (req.headers.authorization !== `Bearer ${token}`) {
12641
+ res.writeHead(401).end();
12642
+ return;
12643
+ }
12644
+ const target = parseMcpUiProxyPath(req.url);
12645
+ if (!target) {
12646
+ res.writeHead(404).end();
12647
+ return;
12648
+ }
12649
+ try {
12650
+ const session = await opts.sessionFor(target.server, target.dirsKey);
12651
+ if (!session) {
12652
+ res.writeHead(404).end();
12653
+ return;
12654
+ }
12655
+ const header = req.headers["mcp-session-id"];
12656
+ const sid = typeof header === "string" ? header : "";
12657
+ const sessionKey = `${target.server}:${target.dirsKey}`;
12658
+ let transport = sid ? transports.get(`${sessionKey}:${sid}`) : void 0;
12659
+ if (!transport) {
12660
+ if (req.method !== "POST") {
12661
+ res.writeHead(400).end();
12662
+ return;
12663
+ }
12664
+ const created = new import_streamableHttp.StreamableHTTPServerTransport({
12665
+ sessionIdGenerator: () => (0, import_node_crypto6.randomUUID)(),
12666
+ allowedHosts: ["127.0.0.1", "localhost"],
12667
+ onsessioninitialized: (id) => {
12668
+ transports.set(`${sessionKey}:${id}`, created);
12669
+ },
12670
+ onsessionclosed: (id) => {
12671
+ transports.delete(`${sessionKey}:${id}`);
12672
+ }
12673
+ });
12674
+ transport = created;
12675
+ const mcp = new import_server2.Server(
12676
+ { name: `sideboard-ui-proxy-${target.server}`, version: "1.0.0" },
12677
+ { capabilities: session.capabilities() }
12678
+ );
12679
+ attachMcpUiProxyHandlers(mcp, session);
12680
+ await mcp.connect(created);
12681
+ }
12682
+ await transport.handleRequest(req, res);
12683
+ } catch {
12684
+ if (!res.headersSent) res.writeHead(500).end();
12685
+ }
12686
+ }
12687
+ return {
12688
+ urlFor(server, dirsKey) {
12689
+ const base = `${origin}/s/${encodeURIComponent(server)}`;
12690
+ return dirsKey ? `${base}/d/${dirsKey}` : base;
12691
+ },
12692
+ headers: { Authorization: `Bearer ${token}` },
12693
+ async close() {
12694
+ await Promise.all([...transports.values()].map((item) => item.close().catch(() => void 0)));
12695
+ transports.clear();
12696
+ await new Promise((resolve3) => httpServer.close(() => resolve3()));
12697
+ }
12698
+ };
12699
+ }
12700
+ function parseMcpUiProxyPath(url) {
12701
+ const path = (url ?? "").split("?")[0] ?? "";
12702
+ if (!path.startsWith("/s/")) return null;
12703
+ const rest = path.slice(3).replace(/\/$/, "");
12704
+ const split = rest.indexOf("/d/");
12705
+ try {
12706
+ if (split < 0) {
12707
+ const server2 = decodeURIComponent(rest).trim();
12708
+ return server2 ? { server: server2, dirsKey: "" } : null;
12709
+ }
12710
+ const server = decodeURIComponent(rest.slice(0, split)).trim();
12711
+ const dirsKey = rest.slice(split + 3);
12712
+ if (!server || !/^[a-f0-9]{16}$/.test(dirsKey)) return null;
12713
+ return { server, dirsKey };
12714
+ } catch {
12715
+ return null;
12716
+ }
12717
+ }
12718
+ var import_node_crypto6, import_node_http, import_server2, import_streamableHttp;
12719
+ var init_proxy_http = __esm({
12720
+ "src/mcp-ui/proxy-http.ts"() {
12721
+ "use strict";
12722
+ import_node_crypto6 = require("crypto");
12723
+ import_node_http = require("http");
12724
+ import_server2 = require("@modelcontextprotocol/sdk/server/index.js");
12725
+ import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
12726
+ init_proxy_server();
12727
+ }
12728
+ });
12729
+
12730
+ // src/mcp-ui/session.ts
12731
+ async function orEmpty(run2, fallback) {
12732
+ try {
12733
+ return await run2();
12734
+ } catch {
12735
+ return fallback;
12736
+ }
12737
+ }
12738
+ async function openMcpUiSession(launch) {
12739
+ return openMcpUiSessionFromTransport(transportForLaunch(launch));
12740
+ }
12741
+ async function openMcpUiSessionFromTransport(transport) {
12742
+ const client = new import_client.Client(
12743
+ { name: "sideboard", version: "1.0.0" },
12744
+ { capabilities: UI_CAPABILITIES }
12745
+ );
12746
+ await client.connect(transport, { timeout: REQUEST_TIMEOUT_MS });
12747
+ return {
12748
+ async listTools() {
12749
+ const listed = await client.listTools(void 0, { timeout: REQUEST_TIMEOUT_MS });
12750
+ return listed.tools.map((tool) => {
12751
+ const meta = tool._meta;
12752
+ return {
12753
+ name: tool.name,
12754
+ ...tool.description ? { description: tool.description } : {},
12755
+ ...tool.inputSchema ? { inputSchema: tool.inputSchema } : {},
12756
+ resourceUri: uiResourceUriFromMeta(meta),
12757
+ visibility: visibilityFromMeta(meta),
12758
+ ...cspFromMeta(meta) ? { csp: cspFromMeta(meta) } : {}
12759
+ };
12760
+ });
12761
+ },
12762
+ listResources: () => orEmpty(
12763
+ () => client.listResources(void 0, { timeout: REQUEST_TIMEOUT_MS }),
12764
+ { resources: [] }
12765
+ ),
12766
+ listResourceTemplates: () => orEmpty(
12767
+ () => client.listResourceTemplates(void 0, { timeout: REQUEST_TIMEOUT_MS }),
12768
+ { resourceTemplates: [] }
12769
+ ),
12770
+ listPrompts: () => orEmpty(
12771
+ () => client.listPrompts(void 0, { timeout: REQUEST_TIMEOUT_MS }),
12772
+ { prompts: [] }
12773
+ ),
12774
+ getPrompt(name, args) {
12775
+ return client.getPrompt({ name, arguments: args }, { timeout: REQUEST_TIMEOUT_MS });
12776
+ },
12777
+ async readResource(uri) {
12778
+ const result = await client.readResource({ uri }, { timeout: REQUEST_TIMEOUT_MS });
12779
+ const content = result.contents?.[0];
12780
+ if (!content) return null;
12781
+ const text7 = "text" in content && typeof content.text === "string" ? content.text : void 0;
12782
+ const blob = "blob" in content && typeof content.blob === "string" ? content.blob : void 0;
12783
+ return {
12784
+ uri: content.uri,
12785
+ ...content.mimeType ? { mimeType: content.mimeType } : {},
12786
+ ...text7 != null ? { text: text7 } : {},
12787
+ ...blob != null ? { blob } : {},
12788
+ ...cspFromMeta(content._meta) ? { csp: cspFromMeta(content._meta) } : {}
12789
+ };
12790
+ },
12791
+ async callTool(name, args) {
12792
+ return client.callTool({ name, arguments: args }, void 0, { timeout: REQUEST_TIMEOUT_MS });
12793
+ },
12794
+ capabilities() {
12795
+ const caps = client.getServerCapabilities() ?? {};
12796
+ return {
12797
+ tools: caps.tools ?? {},
12798
+ resources: caps.resources ?? {},
12799
+ prompts: caps.prompts ?? {},
12800
+ completions: caps.completions ?? {},
12801
+ ...caps.logging ? { logging: caps.logging } : {}
12802
+ };
12803
+ },
12804
+ forward(method, params) {
12805
+ return client.request(
12806
+ { method, params },
12807
+ import_types2.ResultSchema,
12808
+ { timeout: REQUEST_TIMEOUT_MS }
12809
+ );
12810
+ },
12811
+ async close() {
12812
+ await client.close();
12813
+ }
12814
+ };
12815
+ }
12816
+ function transportForLaunch(launch) {
12817
+ if (launch.kind === "stdio") {
12818
+ return new import_stdio.StdioClientTransport({
12819
+ command: launch.command,
12820
+ args: launch.args,
12821
+ env: { ...(0, import_stdio.getDefaultEnvironment)(), ...launch.env },
12822
+ stderr: "ignore",
12823
+ ...launch.cwd ? { cwd: launch.cwd } : {}
12824
+ });
12825
+ }
12826
+ const url = new URL(launch.url);
12827
+ const requestInit = launch.headers ? { headers: launch.headers } : void 0;
12828
+ if (launch.kind === "sse") {
12829
+ return new import_sse.SSEClientTransport(url, requestInit ? { requestInit } : void 0);
12830
+ }
12831
+ return new import_streamableHttp2.StreamableHTTPClientTransport(url, requestInit ? { requestInit } : void 0);
12832
+ }
12833
+ var import_client, import_sse, import_stdio, import_streamableHttp2, import_types2, REQUEST_TIMEOUT_MS, UI_CAPABILITIES;
12834
+ var init_session = __esm({
12835
+ "src/mcp-ui/session.ts"() {
12836
+ "use strict";
12837
+ import_client = require("@modelcontextprotocol/sdk/client/index.js");
12838
+ import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
12839
+ import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
12840
+ import_streamableHttp2 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
12841
+ import_types2 = require("@modelcontextprotocol/sdk/types.js");
12842
+ init_meta();
12843
+ init_visibility();
12844
+ REQUEST_TIMEOUT_MS = 2e4;
12845
+ UI_CAPABILITIES = {
12846
+ extensions: {
12847
+ "io.modelcontextprotocol/ui": {
12848
+ mimeTypes: ["text/html;profile=mcp-app"]
12849
+ }
12850
+ }
12851
+ };
12852
+ }
12853
+ });
12854
+
12855
+ // src/mcp-ui/host.ts
12856
+ function getMcpUiHost() {
12857
+ if (!singleton) {
12858
+ singleton = new McpUiHost({
12859
+ entries: (projectDirs) => listUserClaudeMcpServerEntries((0, import_node_os15.homedir)(), projectDirs)
12860
+ });
12861
+ }
12862
+ return singleton;
12863
+ }
12864
+ async function proxyClaudeUserMcp(orchestrator, projectDirs) {
12865
+ if (orchestrator) return {};
12866
+ const dirs = projectDirs.filter((d) => typeof d === "string" && d.length > 0);
12867
+ return getMcpUiHost().proxyEntries(listUserClaudeMcpServerEntries((0, import_node_os15.homedir)(), dirs), dirs);
12868
+ }
12869
+ function dirsKeyFor(projectDirs) {
12870
+ if (!projectDirs?.length) return "";
12871
+ return (0, import_node_crypto7.createHash)("sha256").update(projectDirs.join("\0")).digest("hex").slice(0, 16);
12872
+ }
12873
+ var import_node_crypto7, import_node_os15, TOOL_TTL_MS, McpUiHost, singleton;
12874
+ var init_host = __esm({
12875
+ "src/mcp-ui/host.ts"() {
12876
+ "use strict";
12877
+ import_node_crypto7 = require("crypto");
12878
+ import_node_os15 = require("os");
12879
+ init_orch_mcp_isolation();
12880
+ init_meta();
12881
+ init_proxy_http();
12882
+ init_session();
12883
+ init_visibility();
12884
+ TOOL_TTL_MS = 5 * 60 * 1e3;
12885
+ McpUiHost = class {
12886
+ constructor(opts) {
12887
+ this.opts = opts;
12888
+ }
12889
+ opts;
12890
+ sessions = /* @__PURE__ */ new Map();
12891
+ toolLists = /* @__PURE__ */ new Map();
12892
+ proxy;
12893
+ proxyDirs = /* @__PURE__ */ new Map();
12894
+ async viewForClaudeTool(toolName, projectDirs) {
12895
+ const entries = this.userEntries(projectDirs);
12896
+ const match = matchClaudeMcpTool(toolName, Object.keys(entries));
12897
+ if (!match) return null;
12898
+ const launch = parseMcpServerLaunch(entries[match.server] ?? {});
12899
+ if (!launch) return null;
12900
+ try {
12901
+ const session = await this.sessionFor(match.server, launch);
12902
+ const tools = await this.toolsFor(match.server, launch, session);
12903
+ const tool = tools.find((item) => item.name === match.tool);
12904
+ if (!tool?.resourceUri) return null;
12905
+ const resource = await session.readResource(tool.resourceUri);
12906
+ if (!resource) return null;
12907
+ const html = htmlFromResourceContent(resource);
12908
+ if (!html) return null;
12909
+ const csp = resource.csp ?? tool.csp;
12910
+ return {
12911
+ server: match.server,
12912
+ tool: match.tool,
12913
+ uri: html.uri,
12914
+ mimeType: html.mimeType,
12915
+ html: html.html,
12916
+ csp: mcpUiContentSecurityPolicy(csp)
12917
+ };
12918
+ } catch (err) {
12919
+ console.error(
12920
+ `[mcp-ui] ${match.server}/${match.tool}:`,
12921
+ err instanceof Error ? err.message : err
12922
+ );
12923
+ return null;
12924
+ }
12925
+ }
12926
+ /**
12927
+ * Point Claude at loopback HTTP so it shares this host's upstream session
12928
+ * and never sees `visibility: ["app"]` tools.
12929
+ */
12930
+ async proxyEntries(entries, projectDirs) {
12931
+ const names = Object.keys(entries).filter((name) => name.trim());
12932
+ if (names.length === 0) return {};
12933
+ const dirsKey = dirsKeyFor(projectDirs);
12934
+ this.proxyDirs.set(dirsKey, projectDirs);
12935
+ try {
12936
+ const listen = await this.ensureProxy();
12937
+ const out = {};
12938
+ for (const name of names) {
12939
+ out[name] = {
12940
+ type: "http",
12941
+ url: listen.urlFor(name, dirsKey),
12942
+ headers: listen.headers
12943
+ };
12944
+ }
12945
+ return out;
12946
+ } catch (err) {
12947
+ console.error("[mcp-ui] proxy:", err instanceof Error ? err.message : err);
12948
+ return { ...entries };
12949
+ }
12950
+ }
12951
+ async callTool(server, tool, args, projectDirs) {
12952
+ const entry = this.userEntries(projectDirs)[server];
12953
+ if (!entry) throw new Error(`MCP server ${server} is not available`);
12954
+ const launch = parseMcpServerLaunch(entry);
12955
+ if (!launch) throw new Error(`MCP server ${server} is not available`);
12956
+ const session = await this.sessionFor(server, launch);
12957
+ const tools = await this.toolsFor(server, launch, session);
12958
+ const info = tools.find((item) => item.name === tool);
12959
+ if (!info || !toolVisibleTo(info.visibility, "app")) {
12960
+ throw new Error(`Tool ${tool} is not available to the app`);
12961
+ }
12962
+ return session.callTool(tool, args);
12963
+ }
12964
+ async readResource(server, uri, projectDirs) {
12965
+ const session = await this.sessionForName(server, projectDirs);
12966
+ if (!session) throw new Error(`MCP server ${server} is not available`);
12967
+ const resource = await session.readResource(uri);
12968
+ if (!resource) throw new Error(`Resource ${uri} was empty`);
12969
+ return {
12970
+ contents: [
12971
+ {
12972
+ uri: resource.uri,
12973
+ ...resource.mimeType ? { mimeType: resource.mimeType } : {},
12974
+ ...resource.text != null ? { text: resource.text } : {},
12975
+ ...resource.blob != null ? { blob: resource.blob } : {}
12976
+ }
12977
+ ]
12978
+ };
12979
+ }
12980
+ async closeAll() {
12981
+ const pending = [...this.sessions.values()];
12982
+ const proxy = this.proxy;
12983
+ this.sessions.clear();
12984
+ this.toolLists.clear();
12985
+ this.proxyDirs.clear();
12986
+ this.proxy = void 0;
12987
+ await Promise.all([
12988
+ ...pending.map((session) => session.then((open) => open.close()).catch(() => void 0)),
12989
+ proxy ? proxy.then((listen) => listen.close()).catch(() => void 0) : Promise.resolve()
12990
+ ]);
12991
+ }
12992
+ ensureProxy() {
12993
+ if (this.proxy) return this.proxy;
12994
+ this.proxy = listenMcpUiProxy({
12995
+ sessionFor: (server, dirsKey) => {
12996
+ if (dirsKey && !this.proxyDirs.has(dirsKey)) return Promise.resolve(null);
12997
+ return this.sessionForName(server, this.proxyDirs.get(dirsKey));
12998
+ }
12999
+ }).catch((err) => {
13000
+ this.proxy = void 0;
13001
+ throw err;
13002
+ });
13003
+ return this.proxy;
13004
+ }
13005
+ userEntries(projectDirs) {
13006
+ const out = {};
13007
+ for (const [name, entry] of Object.entries(this.opts.entries(projectDirs))) {
13008
+ if (!name.trim() || isInjectedOrchMcpName(name) || !entry) continue;
13009
+ out[name] = entry;
13010
+ }
13011
+ return out;
13012
+ }
13013
+ async sessionForName(server, projectDirs) {
13014
+ const entry = this.userEntries(projectDirs)[server];
13015
+ if (!entry) return null;
13016
+ const launch = parseMcpServerLaunch(entry);
13017
+ if (!launch) return null;
13018
+ return this.sessionFor(server, launch);
13019
+ }
13020
+ sessionFor(server, launch) {
13021
+ const key = `${server}
13022
+ ${JSON.stringify(launch)}`;
13023
+ const existing = this.sessions.get(key);
13024
+ if (existing) return existing;
13025
+ const open = this.opts.openSession ?? openMcpUiSession;
13026
+ const pending = open(launch).catch((err) => {
13027
+ this.sessions.delete(key);
13028
+ throw err;
13029
+ });
13030
+ this.sessions.set(key, pending);
13031
+ return pending;
13032
+ }
13033
+ async toolsFor(server, launch, session) {
13034
+ const key = `${server}
13035
+ ${JSON.stringify(launch)}`;
13036
+ const cached = this.toolLists.get(key);
13037
+ if (cached && cached.key === key && Date.now() - cached.at < TOOL_TTL_MS) return cached.tools;
13038
+ const tools = await session.listTools();
13039
+ this.toolLists.set(key, { at: Date.now(), key, tools });
13040
+ return tools;
13041
+ }
13042
+ };
13043
+ }
13044
+ });
13045
+
12404
13046
  // src/agents/types.ts
12405
13047
  function planModeTurnInstruction(planMode) {
12406
13048
  return planMode ? PLAN_MODE_INSTRUCTION : PLAN_MODE_OFF_INSTRUCTION;
@@ -12581,7 +13223,7 @@ function entryDir() {
12581
13223
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
12582
13224
  if (cjsDir) return cjsDir;
12583
13225
  try {
12584
- return (0, import_node_path35.dirname)((0, import_node_url2.fileURLToPath)(import_meta3.url));
13226
+ return (0, import_node_path35.dirname)((0, import_node_url2.fileURLToPath)(import_meta5.url));
12585
13227
  } catch {
12586
13228
  try {
12587
13229
  const req = (0, import_node_module2.createRequire)(process.cwd() + "/");
@@ -12631,7 +13273,7 @@ async function buildClaudeRpcTurnCommand(req) {
12631
13273
  }
12632
13274
  };
12633
13275
  }
12634
- var import_node_fs33, import_node_module2, import_node_path35, import_node_url2, import_meta3;
13276
+ var import_node_fs33, import_node_module2, import_node_path35, import_node_url2, import_meta5;
12635
13277
  var init_claude_host = __esm({
12636
13278
  "src/agents/claude-host.ts"() {
12637
13279
  "use strict";
@@ -12643,7 +13285,7 @@ var init_claude_host = __esm({
12643
13285
  init_pilot_tools();
12644
13286
  init_node_launch();
12645
13287
  init_packaged_runtime();
12646
- import_meta3 = {};
13288
+ import_meta5 = {};
12647
13289
  }
12648
13290
  });
12649
13291
 
@@ -13046,16 +13688,15 @@ function parseIssuesJson(raw) {
13046
13688
  }
13047
13689
  return [];
13048
13690
  }
13049
- var import_node_fs34, import_node_os15, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
13691
+ var import_node_fs34, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
13050
13692
  var init_claude = __esm({
13051
13693
  "src/agents/claude.ts"() {
13052
13694
  "use strict";
13053
13695
  import_node_fs34 = require("fs");
13054
- import_node_os15 = require("os");
13055
13696
  init_run();
13056
13697
  init_app_settings();
13057
13698
  init_claude_mcp();
13058
- init_orch_mcp_isolation();
13699
+ init_host();
13059
13700
  init_error_detail();
13060
13701
  init_message_parts();
13061
13702
  init_injected_mcp();
@@ -13132,7 +13773,7 @@ var init_claude = __esm({
13132
13773
  });
13133
13774
  const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
13134
13775
  const chromeOn = claudeChromeEnabled();
13135
- const userMcpEntries = isOrchestrator ? {} : listUserClaudeMcpServerEntries((0, import_node_os15.homedir)(), [thread.repoPath, thread.worktreePath]);
13776
+ const userMcpEntries = await proxyClaudeUserMcp(isOrchestrator, [thread.repoPath, thread.worktreePath]);
13136
13777
  let allowedTools;
13137
13778
  if (isOrchestrator) {
13138
13779
  allowedTools = [
@@ -13434,7 +14075,7 @@ function unwrapCodexMcpResult(result) {
13434
14075
  return String(result);
13435
14076
  }
13436
14077
  }
13437
- function asRecord4(value) {
14078
+ function asRecord5(value) {
13438
14079
  if (value && typeof value === "object" && !Array.isArray(value)) {
13439
14080
  return value;
13440
14081
  }
@@ -13614,7 +14255,7 @@ var init_codex = __esm({
13614
14255
  }
13615
14256
  if (itemType === "mcp_tool_call" && item.id && item.server && item.tool) {
13616
14257
  const name = `mcp__${item.server}__${item.tool}`;
13617
- const input = asRecord4(item.arguments);
14258
+ const input = asRecord5(item.arguments);
13618
14259
  const events = [
13619
14260
  { type: "tool_use", id: item.id, name, input }
13620
14261
  ];
@@ -13761,7 +14402,7 @@ function thisModuleFile() {
13761
14402
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
13762
14403
  if (cjsDir) return (0, import_node_path37.join)(cjsDir, "cursor-sdk-resolve.js");
13763
14404
  try {
13764
- return (0, import_node_url3.fileURLToPath)(import_meta4.url);
14405
+ return (0, import_node_url3.fileURLToPath)(import_meta6.url);
13765
14406
  } catch {
13766
14407
  return (0, import_node_path37.join)(process.cwd(), "package.json");
13767
14408
  }
@@ -13860,7 +14501,7 @@ function cursorSdkRipgrepCandidate(opts) {
13860
14501
  const nested = (0, import_node_path37.join)(root, "node_modules", pkg, "bin", bin);
13861
14502
  return (0, import_node_fs36.existsSync)(nested) ? nested : null;
13862
14503
  }
13863
- var import_node_fs36, import_node_module3, import_node_path37, import_node_url3, import_meta4, CURSOR_SDK_INSTALL_HINT;
14504
+ var import_node_fs36, import_node_module3, import_node_path37, import_node_url3, import_meta6, CURSOR_SDK_INSTALL_HINT;
13864
14505
  var init_cursor_sdk_resolve = __esm({
13865
14506
  "src/agents/cursor-sdk-resolve.ts"() {
13866
14507
  "use strict";
@@ -13869,7 +14510,7 @@ var init_cursor_sdk_resolve = __esm({
13869
14510
  import_node_path37 = require("path");
13870
14511
  import_node_url3 = require("url");
13871
14512
  init_path();
13872
- import_meta4 = {};
14513
+ import_meta6 = {};
13873
14514
  CURSOR_SDK_INSTALL_HINT = "Cursor SDK is not installed. Settings \u2192 Agents \u2192 Cursor \u2192 Install (`npm i -g @cursor/sdk`). Sideboard does not ship the SDK \u2014 update it anytime without a new Sideboard build.";
13874
14515
  }
13875
14516
  });
@@ -14004,7 +14645,7 @@ function entryDir2() {
14004
14645
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
14005
14646
  if (cjsDir) return cjsDir;
14006
14647
  try {
14007
- return (0, import_node_path39.dirname)((0, import_node_url4.fileURLToPath)(import_meta5.url));
14648
+ return (0, import_node_path39.dirname)((0, import_node_url4.fileURLToPath)(import_meta7.url));
14008
14649
  } catch {
14009
14650
  try {
14010
14651
  const req = (0, import_node_module5.createRequire)(process.cwd() + "/");
@@ -14033,7 +14674,7 @@ function cursorRunnerPath() {
14033
14674
  }
14034
14675
  return candidates[0];
14035
14676
  }
14036
- var import_node_fs38, import_node_module5, import_node_path39, import_node_url4, import_meta5, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
14677
+ var import_node_fs38, import_node_module5, import_node_path39, import_node_url4, import_meta7, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
14037
14678
  var init_cursor = __esm({
14038
14679
  "src/agents/cursor.ts"() {
14039
14680
  "use strict";
@@ -14053,7 +14694,7 @@ var init_cursor = __esm({
14053
14694
  init_packaged_runtime();
14054
14695
  init_turn_input();
14055
14696
  init_cursor_events();
14056
- import_meta5 = {};
14697
+ import_meta7 = {};
14057
14698
  FALLBACK_CURSOR_MODELS = [
14058
14699
  { id: "default", displayName: "Auto" },
14059
14700
  { id: "composer-2.5", displayName: "Composer 2.5" },
@@ -14327,7 +14968,7 @@ var init_opencode_host = __esm({
14327
14968
  });
14328
14969
 
14329
14970
  // src/agents/opencode.ts
14330
- function asRecord5(value) {
14971
+ function asRecord6(value) {
14331
14972
  if (value && typeof value === "object" && !Array.isArray(value)) {
14332
14973
  return value;
14333
14974
  }
@@ -14523,7 +15164,7 @@ var init_opencode = __esm({
14523
15164
  const name = part?.tool ?? part?.name ?? obj.tool ?? obj.name ?? "tool";
14524
15165
  const state = part?.state;
14525
15166
  const input = (state?.input && typeof state.input === "object" ? state.input : void 0) ?? part?.input ?? obj.input;
14526
- const metadata = asRecord5(state?.metadata);
15167
+ const metadata = asRecord6(state?.metadata);
14527
15168
  const id = opencodeToolId(
14528
15169
  name,
14529
15170
  part?.callID ?? part?.id ?? obj.id,
@@ -14558,7 +15199,7 @@ var init_opencode = __esm({
14558
15199
  }
14559
15200
  if (obj.type === "subtask_event") {
14560
15201
  const parentId = str3(obj.childSessionID) ?? str3(obj.childSessionId);
14561
- const part = asRecord5(obj.part);
15202
+ const part = asRecord6(obj.part);
14562
15203
  if (!parentId || !part) return null;
14563
15204
  if (part.type === "text" && str3(part.text)) {
14564
15205
  return withTaskParent(
@@ -14569,8 +15210,8 @@ var init_opencode = __esm({
14569
15210
  if (part.type === "tool" || str3(part.tool)) {
14570
15211
  const name = str3(part.tool) ?? str3(part.name) ?? "tool";
14571
15212
  const id = str3(part.callID) ?? str3(part.id) ?? `${parentId}-${name}`;
14572
- const state = asRecord5(part.state);
14573
- const input = asRecord5(state?.input) ?? asRecord5(part.input);
15213
+ const state = asRecord6(part.state);
15214
+ const input = asRecord6(state?.input) ?? asRecord6(part.input);
14574
15215
  const nested = [
14575
15216
  withEventParentId({ type: "tool_use", id, name, input }, parentId)
14576
15217
  ];
@@ -16346,7 +16987,7 @@ function forkMessageSlice(from, throughIndex) {
16346
16987
  function buildForkTranscriptAttachment(baseTitle, messages) {
16347
16988
  const title = baseTitle || "Chat";
16348
16989
  return {
16349
- id: (0, import_node_crypto6.randomUUID)(),
16990
+ id: (0, import_node_crypto8.randomUUID)(),
16350
16991
  name: `Transcript of ${title}.md`,
16351
16992
  kind: "transcript",
16352
16993
  content: formatTranscriptMarkdown(title, messages)
@@ -16405,11 +17046,11 @@ function forkChatTab(input) {
16405
17046
  }
16406
17047
  return tab;
16407
17048
  }
16408
- var import_node_crypto6;
17049
+ var import_node_crypto8;
16409
17050
  var init_chat_tabs = __esm({
16410
17051
  "src/threads/chat-tabs.ts"() {
16411
17052
  "use strict";
16412
- import_node_crypto6 = require("crypto");
17053
+ import_node_crypto8 = require("crypto");
16413
17054
  init_context_compact();
16414
17055
  init_teams();
16415
17056
  init_worktree_labels();
@@ -16683,7 +17324,7 @@ function buildReviewRequestAttachment(content, opts) {
16683
17324
  const path = opts?.path ?? REVIEW_SKILL_PATH;
16684
17325
  const name = opts?.name ?? (path === REVIEW_SKILL_PATH ? REVIEW_SKILL_NAME : path === REPO_REVIEW_PATH || path === CONTEXT_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
16685
17326
  return {
16686
- id: (0, import_node_crypto7.randomUUID)(),
17327
+ id: (0, import_node_crypto9.randomUUID)(),
16687
17328
  name,
16688
17329
  kind: "file",
16689
17330
  path,
@@ -16736,11 +17377,11 @@ async function requestReview(threadRef, send) {
16736
17377
  const started = await send(tab.id, REVIEW_REQUEST_PREFILL);
16737
17378
  return { tab: started, from };
16738
17379
  }
16739
- var import_node_crypto7, import_node_fs44, import_node_path44, REPO_REVIEW_PATH, REPO_REVIEW_NAME, CONTEXT_REVIEW_PATH, REVIEW_REQUEST_PATH, LEGACY_REVIEW_REQUEST_PATH, REVIEW_REQUEST_NAME, REVIEW_REQUEST_PREFILL, LEGACY_REVIEW_TEMPLATE_MARKERS;
17380
+ var import_node_crypto9, import_node_fs44, import_node_path44, REPO_REVIEW_PATH, REPO_REVIEW_NAME, CONTEXT_REVIEW_PATH, REVIEW_REQUEST_PATH, LEGACY_REVIEW_REQUEST_PATH, REVIEW_REQUEST_NAME, REVIEW_REQUEST_PREFILL, LEGACY_REVIEW_TEMPLATE_MARKERS;
16740
17381
  var init_request_review = __esm({
16741
17382
  "src/review/request-review.ts"() {
16742
17383
  "use strict";
16743
- import_node_crypto7 = require("crypto");
17384
+ import_node_crypto9 = require("crypto");
16744
17385
  import_node_fs44 = require("fs");
16745
17386
  import_node_path44 = require("path");
16746
17387
  init_global_workspace();
@@ -17998,7 +18639,7 @@ function setupLogPath(threadId) {
17998
18639
  }
17999
18640
  function setupLogKeyForWorktree(worktreePath) {
18000
18641
  const norm = normalizeWorktreePath(worktreePath);
18001
- return `wt-${(0, import_node_crypto8.createHash)("sha1").update(norm).digest("hex").slice(0, 16)}`;
18642
+ return `wt-${(0, import_node_crypto10.createHash)("sha1").update(norm).digest("hex").slice(0, 16)}`;
18002
18643
  }
18003
18644
  function emptySetupLog() {
18004
18645
  return { ...EMPTY };
@@ -18091,11 +18732,11 @@ function finishSetupLog(threadId, exitCode, source) {
18091
18732
  persistNow(threadId);
18092
18733
  return materialize(mem);
18093
18734
  }
18094
- var import_node_crypto8, import_node_fs48, import_node_path48, EMPTY, MAX_SETUP_LOG_CHARS, memory, persistTimers, PERSIST_DEBOUNCE_MS;
18735
+ var import_node_crypto10, import_node_fs48, import_node_path48, EMPTY, MAX_SETUP_LOG_CHARS, memory, persistTimers, PERSIST_DEBOUNCE_MS;
18095
18736
  var init_setup_log = __esm({
18096
18737
  "src/store/setup-log.ts"() {
18097
18738
  "use strict";
18098
- import_node_crypto8 = require("crypto");
18739
+ import_node_crypto10 = require("crypto");
18099
18740
  import_node_fs48 = require("fs");
18100
18741
  import_node_path48 = require("path");
18101
18742
  init_worktree_labels();
@@ -18146,7 +18787,7 @@ function runLogPath(key) {
18146
18787
  }
18147
18788
  function runLogKeyForWorktree(worktreePath, scriptName) {
18148
18789
  const norm = normalizeWorktreePath(worktreePath);
18149
- const hash = (0, import_node_crypto9.createHash)("sha1").update(norm).digest("hex").slice(0, 16);
18790
+ const hash = (0, import_node_crypto11.createHash)("sha1").update(norm).digest("hex").slice(0, 16);
18150
18791
  return `wt-${hash}-run-${safeScriptToken(scriptName)}`;
18151
18792
  }
18152
18793
  function emptyRunLog() {
@@ -18247,11 +18888,11 @@ function finishRunLog(key, exitCode) {
18247
18888
  function flushRunLog(key) {
18248
18889
  persistNow2(key);
18249
18890
  }
18250
- var import_node_crypto9, import_node_fs49, import_node_path49, EMPTY2, MAX_RUN_LOG_CHARS, DEFAULT_RUN_LOG_TAIL_CHARS, memory2, persistTimers2, PERSIST_DEBOUNCE_MS2;
18891
+ var import_node_crypto11, import_node_fs49, import_node_path49, EMPTY2, MAX_RUN_LOG_CHARS, DEFAULT_RUN_LOG_TAIL_CHARS, memory2, persistTimers2, PERSIST_DEBOUNCE_MS2;
18251
18892
  var init_run_log = __esm({
18252
18893
  "src/store/run-log.ts"() {
18253
18894
  "use strict";
18254
- import_node_crypto9 = require("crypto");
18895
+ import_node_crypto11 = require("crypto");
18255
18896
  import_node_fs49 = require("fs");
18256
18897
  import_node_path49 = require("path");
18257
18898
  init_worktree_labels();
@@ -18642,6 +19283,21 @@ var init_desktop_host = __esm({
18642
19283
  }
18643
19284
  });
18644
19285
 
19286
+ // src/mcp-ui/model-context.ts
19287
+ function takeMcpAppModelContext(threadId) {
19288
+ const thread = readThread(threadId);
19289
+ const text7 = thread?.mcpAppModelContext?.trim();
19290
+ if (!text7) return void 0;
19291
+ updateThread(threadId, { mcpAppModelContext: null });
19292
+ return text7;
19293
+ }
19294
+ var init_model_context = __esm({
19295
+ "src/mcp-ui/model-context.ts"() {
19296
+ "use strict";
19297
+ init_thread_store();
19298
+ }
19299
+ });
19300
+
18645
19301
  // src/agents/tool-permission.ts
18646
19302
  function setToolPermissionPublisher(next) {
18647
19303
  publisher = next;
@@ -18662,11 +19318,11 @@ function denyPendingToolPermissions(threadId) {
18662
19318
  const last = pending[pending.length - 1];
18663
19319
  if (last) clearPrompt(threadId, last.id);
18664
19320
  }
18665
- var import_node_crypto10, import_node_path53, queues, publisher;
19321
+ var import_node_crypto12, import_node_path53, queues, publisher;
18666
19322
  var init_tool_permission = __esm({
18667
19323
  "src/agents/tool-permission.ts"() {
18668
19324
  "use strict";
18669
- import_node_crypto10 = require("crypto");
19325
+ import_node_crypto12 = require("crypto");
18670
19326
  import_node_path53 = require("path");
18671
19327
  init_claude_mcp();
18672
19328
  queues = /* @__PURE__ */ new Map();
@@ -18696,6 +19352,11 @@ function visibleAgentBlock(thread) {
18696
19352
  const block = thread.agentBlock;
18697
19353
  if (!block?.reason?.trim()) return null;
18698
19354
  if (isDismissedAskUserBlock(block, thread.dismissedQuestionSignature)) return null;
19355
+ if (block.source === "ask_user") {
19356
+ const inFlight = thread.status === "running" || thread.status === "queued";
19357
+ if (inFlight && !block.onScreen) return null;
19358
+ if (!askBlockStillWaiting(block, thread.messages)) return null;
19359
+ }
18699
19360
  return block;
18700
19361
  }
18701
19362
  function worktreeAgentBlock(group) {
@@ -18714,11 +19375,12 @@ function threadCardBlockFields(group) {
18714
19375
  var init_agent_block_view = __esm({
18715
19376
  "src/board/agent-block-view.ts"() {
18716
19377
  "use strict";
19378
+ init_ask_user();
18717
19379
  }
18718
19380
  });
18719
19381
 
18720
19382
  // src/plan/plan-present.ts
18721
- function asRecord6(v) {
19383
+ function asRecord7(v) {
18722
19384
  return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
18723
19385
  }
18724
19386
  function isPresentPlanToolName(name) {
@@ -18737,7 +19399,7 @@ function extractPresentedPlan(parts) {
18737
19399
  for (let i = parts.length - 1; i >= 0; i--) {
18738
19400
  const p = parts[i];
18739
19401
  if (p.type !== "tool" || !isPresentPlanToolName(p.name)) continue;
18740
- const input = asRecord6(p.input) ?? {};
19402
+ const input = asRecord7(p.input) ?? {};
18741
19403
  const content = typeof input.content === "string" ? input.content : typeof input.plan === "string" ? input.plan : typeof input.markdown === "string" ? input.markdown : "";
18742
19404
  if (!content.trim()) continue;
18743
19405
  const title = typeof input.title === "string" && input.title.trim() ? input.title.trim() : "Plan";
@@ -18939,6 +19601,7 @@ function decisionBlockFromParts(parts, planMode, at = (/* @__PURE__ */ new Date(
18939
19601
  const reason = pending ? formatAskUserNotifyMessage(pending.questions) : "Waiting for an answer in that chat.";
18940
19602
  return {
18941
19603
  ...makeAgentBlock("ask_user", reason, at),
19604
+ onScreen: true,
18942
19605
  ...pending ? { questionSignature: pending.signature, questions: pending.questions } : {}
18943
19606
  };
18944
19607
  }
@@ -18996,6 +19659,38 @@ function dismissAskUserQuestion(threadId, signature) {
18996
19659
  ...clearBlock ? { agentBlock: null } : {}
18997
19660
  });
18998
19661
  }
19662
+ function askBlocksEqual(a, b) {
19663
+ if (a === b) return true;
19664
+ if (!a || !b) return false;
19665
+ return a.source === b.source && a.reason === b.reason && a.at === b.at && a.onScreen === b.onScreen && a.questionSignature === b.questionSignature;
19666
+ }
19667
+ function askBlockAfterLiveParts(existing, parts) {
19668
+ const last = lastTopLevelTool(parts);
19669
+ if (last?.type === "tool" && isAskUserToolName(last.name)) {
19670
+ if (existing?.source === "ask_user" && existing.onScreen) return existing;
19671
+ const pending = extractPendingPlanQuestions(parts);
19672
+ const questions = pending?.questions ?? existing?.questions;
19673
+ if (!questions?.length) {
19674
+ return existing?.source === "ask_user" ? { ...existing, onScreen: true } : existing ?? null;
19675
+ }
19676
+ const at = existing?.source === "ask_user" ? existing.at : void 0;
19677
+ return {
19678
+ ...makeAgentBlock("ask_user", formatAskUserNotifyMessage(questions), at),
19679
+ onScreen: true,
19680
+ questionSignature: pending?.signature ?? existing?.questionSignature,
19681
+ questions
19682
+ };
19683
+ }
19684
+ if (last && existing?.source === "ask_user") return null;
19685
+ return existing ?? null;
19686
+ }
19687
+ function syncAskUserBlockFromLive(threadId, parts) {
19688
+ const thread = readThread(threadId);
19689
+ if (!thread || thread.status === "archived") return;
19690
+ const next = askBlockAfterLiveParts(thread.agentBlock, parts);
19691
+ if (askBlocksEqual(thread.agentBlock, next)) return;
19692
+ updateThread(threadId, { agentBlock: next });
19693
+ }
18999
19694
  function writeAgentBlock(threadId, block) {
19000
19695
  const thread = readThread(threadId);
19001
19696
  if (!thread || thread.status === "archived") return;
@@ -19159,7 +19854,7 @@ function isUnclaimedSteerRequest(req) {
19159
19854
  }
19160
19855
  function nextSteerRequest(existing) {
19161
19856
  if (existing?.requestId && !existing.claimedAt) return existing;
19162
- return { requestId: (0, import_node_crypto11.randomUUID)(), requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
19857
+ return { requestId: (0, import_node_crypto13.randomUUID)(), requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
19163
19858
  }
19164
19859
  function claimSteerRequest(threadId) {
19165
19860
  const pending = readThread(threadId)?.steerRequest;
@@ -19168,11 +19863,11 @@ function claimSteerRequest(threadId) {
19168
19863
  steerRequest: { ...pending, claimedAt: (/* @__PURE__ */ new Date()).toISOString() }
19169
19864
  });
19170
19865
  }
19171
- var import_node_crypto11;
19866
+ var import_node_crypto13;
19172
19867
  var init_steer_request = __esm({
19173
19868
  "src/orchestrator/steer-request.ts"() {
19174
19869
  "use strict";
19175
- import_node_crypto11 = require("crypto");
19870
+ import_node_crypto13 = require("crypto");
19176
19871
  init_thread_store();
19177
19872
  }
19178
19873
  });
@@ -19944,8 +20639,8 @@ function resolveAbleTimeOAuthHost(requested, saved) {
19944
20639
  return fromSaved || void 0;
19945
20640
  }
19946
20641
  function createAbleTimePkce() {
19947
- const verifier = (0, import_node_crypto12.randomBytes)(32).toString("base64url");
19948
- const challenge = (0, import_node_crypto12.createHash)("sha256").update(verifier).digest("base64url");
20642
+ const verifier = (0, import_node_crypto14.randomBytes)(32).toString("base64url");
20643
+ const challenge = (0, import_node_crypto14.createHash)("sha256").update(verifier).digest("base64url");
19949
20644
  return { verifier, challenge };
19950
20645
  }
19951
20646
  function ableTimeOAuthAuthorizeUrl(clientId, state, codeChallenge, host) {
@@ -20050,7 +20745,7 @@ async function startAbleTimeOAuth(opts) {
20050
20745
  opts?.host,
20051
20746
  loadAppSettings().integrations.abletimeHost
20052
20747
  );
20053
- const state = (0, import_node_crypto12.randomBytes)(16).toString("hex");
20748
+ const state = (0, import_node_crypto14.randomBytes)(16).toString("hex");
20054
20749
  const pkce = createAbleTimePkce();
20055
20750
  const authorizeUrl = ableTimeOAuthAuthorizeUrl(clientId, state, pkce.challenge, host);
20056
20751
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
@@ -20063,7 +20758,7 @@ async function startAbleTimeOAuth(opts) {
20063
20758
  if (err) reject(err);
20064
20759
  else resolve3(value);
20065
20760
  };
20066
- const server = (0, import_node_http.createServer)((req, res) => {
20761
+ const server = (0, import_node_http2.createServer)((req, res) => {
20067
20762
  try {
20068
20763
  const url = new URL(req.url || "/", `http://127.0.0.1:${ABLETIME_OAUTH_PORT}`);
20069
20764
  if (url.pathname !== "/callback") {
@@ -20150,12 +20845,12 @@ async function startAbleTimeOAuth(opts) {
20150
20845
  return loadAppSettings();
20151
20846
  }
20152
20847
  }
20153
- var import_node_crypto12, import_node_http, ABLETIME_OAUTH_CALLBACK_PATH, ABLETIME_OAUTH_PORT, ABLETIME_OAUTH_LOCAL_CALLBACK, ABLETIME_OAUTH_REDIRECT, BOUNCE_QUERY_KEYS, BAKED_ABLETIME_OAUTH_CLIENT_ID, ABLETIME_OAUTH_RESOURCE, REFRESH_SKEW_MS2, refreshInFlight2, ABLETIME_OAUTH_CANCELLED, AbleTimeOAuthCancelledError;
20848
+ var import_node_crypto14, import_node_http2, ABLETIME_OAUTH_CALLBACK_PATH, ABLETIME_OAUTH_PORT, ABLETIME_OAUTH_LOCAL_CALLBACK, ABLETIME_OAUTH_REDIRECT, BOUNCE_QUERY_KEYS, BAKED_ABLETIME_OAUTH_CLIENT_ID, ABLETIME_OAUTH_RESOURCE, REFRESH_SKEW_MS2, refreshInFlight2, ABLETIME_OAUTH_CANCELLED, AbleTimeOAuthCancelledError;
20154
20849
  var init_abletime_oauth = __esm({
20155
20850
  "src/integrations/abletime-oauth.ts"() {
20156
20851
  "use strict";
20157
- import_node_crypto12 = require("crypto");
20158
- import_node_http = require("http");
20852
+ import_node_crypto14 = require("crypto");
20853
+ import_node_http2 = require("http");
20159
20854
  init_fetch();
20160
20855
  init_app_settings();
20161
20856
  init_abletime_mcp();
@@ -20188,7 +20883,7 @@ __export(abletime_rest_exports, {
20188
20883
  looksLikeAbleTimeUlid: () => looksLikeAbleTimeUlid,
20189
20884
  textFromAbleTimeDoc: () => textFromAbleTimeDoc
20190
20885
  });
20191
- function asRecord7(value) {
20886
+ function asRecord8(value) {
20192
20887
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
20193
20888
  }
20194
20889
  function asString(value) {
@@ -20196,7 +20891,7 @@ function asString(value) {
20196
20891
  }
20197
20892
  function asList(value) {
20198
20893
  if (Array.isArray(value)) return value;
20199
- const record = asRecord7(value);
20894
+ const record = asRecord8(value);
20200
20895
  if (!record) return [];
20201
20896
  for (const key of ["data", "tasks", "projects", "users", "items", "results"]) {
20202
20897
  if (Array.isArray(record[key])) return record[key];
@@ -20213,12 +20908,12 @@ function ableTimeDocFromText(text7) {
20213
20908
  }
20214
20909
  function textFromAbleTimeDoc(value) {
20215
20910
  if (typeof value === "string") return value.trim();
20216
- const record = asRecord7(value);
20911
+ const record = asRecord8(value);
20217
20912
  if (!record) return "";
20218
20913
  if (typeof record.text === "string" && record.text.trim()) return record.text.trim();
20219
20914
  const chunks = [];
20220
20915
  const walk = (node) => {
20221
- const rec = asRecord7(node);
20916
+ const rec = asRecord8(node);
20222
20917
  if (!rec) return;
20223
20918
  if (typeof rec.text === "string" && rec.text) chunks.push(rec.text);
20224
20919
  if (Array.isArray(rec.content)) rec.content.forEach(walk);
@@ -20280,7 +20975,7 @@ async function restPages(path, opts, maxPages = 20) {
20280
20975
  query: { ...opts.query, ...cursor ? { cursor } : {} }
20281
20976
  });
20282
20977
  items.push(...asList(raw));
20283
- const page = asRecord7(asRecord7(raw)?.page);
20978
+ const page = asRecord8(asRecord8(raw)?.page);
20284
20979
  const next = page ? asString(page.nextCursor) : "";
20285
20980
  if (!next) break;
20286
20981
  cursor = next;
@@ -20288,7 +20983,7 @@ async function restPages(path, opts, maxPages = 20) {
20288
20983
  return items;
20289
20984
  }
20290
20985
  function userDisplayName(raw) {
20291
- const rec = asRecord7(raw);
20986
+ const rec = asRecord8(raw);
20292
20987
  if (!rec) return null;
20293
20988
  const id = asString(rec.userId) || asString(rec.id);
20294
20989
  const name = [asString(rec.firstName), asString(rec.lastName)].filter(Boolean).join(" ") || asString(rec.username) || asString(rec.email) || asString(rec.name);
@@ -20296,7 +20991,7 @@ function userDisplayName(raw) {
20296
20991
  return { id: id || void 0, name };
20297
20992
  }
20298
20993
  function restTaskToRaw(raw, users) {
20299
- const rec = asRecord7(raw);
20994
+ const rec = asRecord8(raw);
20300
20995
  if (!rec) return null;
20301
20996
  const id = asString(rec.timeflowTaskId) || asString(rec.taskId) || asString(rec.id);
20302
20997
  const identifier = asString(rec.taskRef) || asString(rec.reference) || id;
@@ -20304,7 +20999,7 @@ function restTaskToRaw(raw, users) {
20304
20999
  if (!id && !identifier && !title) return null;
20305
21000
  const assignee = users.get(asString(rec.assignedUserId));
20306
21001
  const comments = asList(rec.comments).map((item) => {
20307
- const comment = asRecord7(item) ?? {};
21002
+ const comment = asRecord8(item) ?? {};
20308
21003
  const user = userDisplayName(comment);
20309
21004
  return {
20310
21005
  id: asString(comment.commentId) || asString(comment.id),
@@ -20334,13 +21029,13 @@ function restTaskToRaw(raw, users) {
20334
21029
  };
20335
21030
  }
20336
21031
  function restProjectToRaw(raw) {
20337
- const rec = asRecord7(raw);
21032
+ const rec = asRecord8(raw);
20338
21033
  if (!rec) return null;
20339
21034
  const id = asString(rec.projectId) || asString(rec.id);
20340
21035
  const name = asString(rec.projectName) || asString(rec.name);
20341
21036
  if (!id && !name) return null;
20342
21037
  const categories = asList(rec.categories).map((item) => {
20343
- const cat = asRecord7(item) ?? {};
21038
+ const cat = asRecord8(item) ?? {};
20344
21039
  return {
20345
21040
  id: asString(cat.projectCategoryId) || asString(cat.categoryId) || asString(cat.id),
20346
21041
  name: asString(cat.categoryName) || asString(cat.name)
@@ -20352,7 +21047,7 @@ async function listUsers(opts) {
20352
21047
  const users = /* @__PURE__ */ new Map();
20353
21048
  for (const item of await restPages("/users", opts).catch(() => [])) {
20354
21049
  const user = userDisplayName(item);
20355
- const rec = asRecord7(item);
21050
+ const rec = asRecord8(item);
20356
21051
  const id = user?.id || (rec ? asString(rec.userId) : "");
20357
21052
  if (user && id) users.set(id, user);
20358
21053
  }
@@ -20478,10 +21173,10 @@ async function callAbleTimeRestTool(name, args = {}, opts) {
20478
21173
  if (!project) throw new Error("AbleTime has no projects to create a task in");
20479
21174
  const categories = asList(project.categories);
20480
21175
  const wantedCategory = firstStringArg(args, ["category_id", "categoryId", "category"]);
20481
- const category = categories.map((item) => asRecord7(item)).find((item) => {
21176
+ const category = categories.map((item) => asRecord8(item)).find((item) => {
20482
21177
  if (!item || !wantedCategory) return false;
20483
21178
  return asString(item.id).toLowerCase() === wantedCategory.toLowerCase() || asString(item.name).toLowerCase() === wantedCategory.toLowerCase();
20484
- }) ?? asRecord7(categories[0]);
21179
+ }) ?? asRecord8(categories[0]);
20485
21180
  const parentArg = firstStringArg(args, ["parent", "parent_id", "parentId", "related_task_id"]);
20486
21181
  const parentTaskId = parentArg ? await resolveRestParentTaskId(parentArg, requestOpts) : void 0;
20487
21182
  const created = await abletimeRestRequest("/tasks", {
@@ -20495,7 +21190,7 @@ async function callAbleTimeRestTool(name, args = {}, opts) {
20495
21190
  }
20496
21191
  });
20497
21192
  const state = firstStringArg(args, ["state"]);
20498
- const createdId = asString(asRecord7(created)?.timeflowTaskId);
21193
+ const createdId = asString(asRecord8(created)?.timeflowTaskId);
20499
21194
  const description = asString(args.description);
20500
21195
  if (createdId && description) {
20501
21196
  await abletimeRestRequest(`/tasks/${encodeURIComponent(createdId)}`, {
@@ -20568,7 +21263,7 @@ async function callAbleTimeRestTool(name, args = {}, opts) {
20568
21263
  method: "POST",
20569
21264
  body: { content: ableTimeDocFromText(body) }
20570
21265
  });
20571
- const rec = asRecord7(created) ?? {};
21266
+ const rec = asRecord8(created) ?? {};
20572
21267
  return {
20573
21268
  id: asString(rec.commentId) || void 0,
20574
21269
  body: textFromAbleTimeDoc(rec.content) || body
@@ -20663,7 +21358,7 @@ function rewriteAbleTimeError(message) {
20663
21358
  }
20664
21359
  return message;
20665
21360
  }
20666
- function asRecord8(value) {
21361
+ function asRecord9(value) {
20667
21362
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
20668
21363
  }
20669
21364
  function parseSseJson(body) {
@@ -20685,12 +21380,12 @@ function parseSseJson(body) {
20685
21380
  }
20686
21381
  }
20687
21382
  function unwrapToolResult(result) {
20688
- const record = asRecord8(result);
21383
+ const record = asRecord9(result);
20689
21384
  if (!record) return result;
20690
21385
  const content = record.content;
20691
21386
  if (Array.isArray(content)) {
20692
21387
  const texts = content.map((part) => {
20693
- const item = asRecord8(part);
21388
+ const item = asRecord9(part);
20694
21389
  if (!item) return "";
20695
21390
  return typeof item.text === "string" ? item.text : "";
20696
21391
  }).filter(Boolean);
@@ -20712,7 +21407,7 @@ function unwrapToolResult(result) {
20712
21407
  return result;
20713
21408
  }
20714
21409
  function textsFromUnknown(value) {
20715
- const record = asRecord8(value);
21410
+ const record = asRecord9(value);
20716
21411
  if (!record) return "";
20717
21412
  if (typeof record.message === "string") return record.message;
20718
21413
  if (typeof record.error === "string") return record.error;
@@ -20759,7 +21454,7 @@ async function abletimeMcpRequest(method, params, opts) {
20759
21454
  return parseSseJson(body);
20760
21455
  }
20761
21456
  })();
20762
- const rpc = asRecord8(parsed);
21457
+ const rpc = asRecord9(parsed);
20763
21458
  if (rpc?.error) {
20764
21459
  const detail = typeof rpc.error.message === "string" && rpc.error.message.trim() ? rpc.error.message : `AbleTime MCP error ${rpc.error.code ?? ""}`.trim();
20765
21460
  throw new Error(rewriteAbleTimeError(detail));
@@ -20914,7 +21609,7 @@ function uniqueAttachmentFileName(dir, originalName) {
20914
21609
  const candidate = `${stem}-${i}${ext}`;
20915
21610
  if (!(0, import_node_fs53.existsSync)((0, import_node_path54.join)(dir, candidate))) return candidate;
20916
21611
  }
20917
- return `${stem}-${(0, import_node_crypto13.randomUUID)()}${ext}`;
21612
+ return `${stem}-${(0, import_node_crypto15.randomUUID)()}${ext}`;
20918
21613
  }
20919
21614
  function extractHttpUrls(markdown) {
20920
21615
  const text7 = markdown ?? "";
@@ -21020,13 +21715,13 @@ async function downloadAuthenticatedFile(opts) {
21020
21715
  ...contentType ? { contentType } : {}
21021
21716
  };
21022
21717
  }
21023
- var import_node_fs53, import_node_path54, import_node_crypto13, ISSUE_ATTACHMENT_MAX_BYTES, MARKDOWN_IMAGE_RE, MARKDOWN_LINK_RE, HTML_SRC_RE, BARE_URL_RE, EXT_BY_MIME;
21718
+ var import_node_fs53, import_node_path54, import_node_crypto15, ISSUE_ATTACHMENT_MAX_BYTES, MARKDOWN_IMAGE_RE, MARKDOWN_LINK_RE, HTML_SRC_RE, BARE_URL_RE, EXT_BY_MIME;
21024
21719
  var init_issue_attachments = __esm({
21025
21720
  "src/integrations/issue-attachments.ts"() {
21026
21721
  "use strict";
21027
21722
  import_node_fs53 = require("fs");
21028
21723
  import_node_path54 = require("path");
21029
- import_node_crypto13 = require("crypto");
21724
+ import_node_crypto15 = require("crypto");
21030
21725
  init_fetch();
21031
21726
  init_workspace_scratch();
21032
21727
  ISSUE_ATTACHMENT_MAX_BYTES = 25 * 1024 * 1024;
@@ -21051,7 +21746,7 @@ var init_issue_attachments = __esm({
21051
21746
  });
21052
21747
 
21053
21748
  // src/integrations/abletime-attachments.ts
21054
- function asRecord9(value) {
21749
+ function asRecord10(value) {
21055
21750
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
21056
21751
  }
21057
21752
  function asString2(value) {
@@ -21059,7 +21754,7 @@ function asString2(value) {
21059
21754
  }
21060
21755
  function asList2(value) {
21061
21756
  if (Array.isArray(value)) return value;
21062
- const record = asRecord9(value);
21757
+ const record = asRecord10(value);
21063
21758
  if (!record) return [];
21064
21759
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
21065
21760
  if (Array.isArray(record[key])) return record[key];
@@ -21078,9 +21773,9 @@ function filesFromAbleTimeDoc(value) {
21078
21773
  const out = [];
21079
21774
  const seen = /* @__PURE__ */ new Set();
21080
21775
  const walk = (node) => {
21081
- const rec = asRecord9(node);
21776
+ const rec = asRecord10(node);
21082
21777
  if (!rec) return;
21083
- const attrs = asRecord9(rec.attrs) ?? rec;
21778
+ const attrs = asRecord10(rec.attrs) ?? rec;
21084
21779
  const url = asString2(attrs.src) || asString2(attrs.href) || asString2(attrs.url) || asString2(attrs.fileUrl) || asString2(attrs.downloadUrl);
21085
21780
  if (url && !seen.has(url)) {
21086
21781
  seen.add(url);
@@ -21098,7 +21793,7 @@ function filesFromAbleTimeDoc(value) {
21098
21793
  function listedAbleTimeFiles(raw) {
21099
21794
  const out = [];
21100
21795
  for (const item of asList2(raw)) {
21101
- const rec = asRecord9(item);
21796
+ const rec = asRecord10(item);
21102
21797
  if (!rec) continue;
21103
21798
  const url = firstString(rec, ["url", "href", "src", "fileUrl", "downloadUrl", "permalink"]);
21104
21799
  if (!url) continue;
@@ -21118,7 +21813,7 @@ function mapAbleTimeAttachments(record) {
21118
21813
  ...filesFromAbleTimeDoc(record.description),
21119
21814
  ...filesFromAbleTimeDoc(record.content),
21120
21815
  ...asList2(record.comments).flatMap((item) => {
21121
- const rec = asRecord9(item);
21816
+ const rec = asRecord10(item);
21122
21817
  return rec ? filesFromAbleTimeDoc(rec.content ?? rec.body) : [];
21123
21818
  })
21124
21819
  ].map((file) => ({
@@ -21160,7 +21855,7 @@ async function resolveAbleTimeAttachmentUrl(id, opts) {
21160
21855
  const json = await res.json().catch(() => null);
21161
21856
  const rec = json && typeof json === "object" && !Array.isArray(json) ? json.data && typeof json.data === "object" ? json.data : json : null;
21162
21857
  if (!rec) continue;
21163
- const url = firstString(rec, ["url", "href", "src", "fileUrl", "downloadUrl"]) || firstString(asRecord9(rec.file), ["url", "href"]);
21858
+ const url = firstString(rec, ["url", "href", "src", "fileUrl", "downloadUrl"]) || firstString(asRecord10(rec.file), ["url", "href"]);
21164
21859
  if (url) {
21165
21860
  return {
21166
21861
  url,
@@ -21202,7 +21897,7 @@ var init_abletime_attachments = __esm({
21202
21897
  });
21203
21898
 
21204
21899
  // src/integrations/abletime-projects.ts
21205
- function asRecord10(value) {
21900
+ function asRecord11(value) {
21206
21901
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
21207
21902
  }
21208
21903
  function firstString2(record, keys) {
@@ -21215,7 +21910,7 @@ function firstString2(record, keys) {
21215
21910
  }
21216
21911
  function asList3(value) {
21217
21912
  if (Array.isArray(value)) return value;
21218
- const record = asRecord10(value);
21913
+ const record = asRecord11(value);
21219
21914
  if (!record) return [];
21220
21915
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
21221
21916
  if (Array.isArray(record[key])) return record[key];
@@ -21223,7 +21918,7 @@ function asList3(value) {
21223
21918
  return [];
21224
21919
  }
21225
21920
  function ableTimeProjectFields(nested) {
21226
- const projectRecord = asRecord10(nested.project);
21921
+ const projectRecord = asRecord11(nested.project);
21227
21922
  const projectId = firstString2(nested, ["project_id", "projectId"]) || firstString2(projectRecord, ["id"]) || void 0;
21228
21923
  const projectName = firstString2(nested, ["project_name", "projectName"]) || firstString2(projectRecord, ["name", "title"]) || void 0;
21229
21924
  if (!projectId && !projectName) return {};
@@ -21236,13 +21931,13 @@ function ableTimeProjectFields(nested) {
21236
21931
  };
21237
21932
  }
21238
21933
  function mapAbleTimeProject(raw) {
21239
- const record = asRecord10(raw);
21934
+ const record = asRecord11(raw);
21240
21935
  if (!record) return null;
21241
21936
  const id = firstString2(record, ["id", "project_id", "projectId"]);
21242
21937
  const name = firstString2(record, ["name", "title", "projectName"]);
21243
21938
  if (!id && !name) return null;
21244
21939
  const categories = asList3(record.categories ?? record.category).map((item) => {
21245
- const rec = asRecord10(item);
21940
+ const rec = asRecord11(item);
21246
21941
  if (!rec) return null;
21247
21942
  const categoryId = firstString2(rec, ["id", "category_id", "projectCategoryId", "categoryId"]);
21248
21943
  const categoryName = firstString2(rec, ["name", "title", "categoryName"]);
@@ -21278,7 +21973,7 @@ __export(abletime_exports, {
21278
21973
  updateAbleTimeTask: () => updateAbleTimeTask,
21279
21974
  verifyAbleTimeConnection: () => verifyAbleTimeConnection
21280
21975
  });
21281
- function asRecord11(value) {
21976
+ function asRecord12(value) {
21282
21977
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
21283
21978
  }
21284
21979
  function asString3(value) {
@@ -21295,14 +21990,14 @@ function firstString3(record, keys) {
21295
21990
  function firstRecord(record, keys) {
21296
21991
  if (!record) return null;
21297
21992
  for (const key of keys) {
21298
- const value = asRecord11(record[key]);
21993
+ const value = asRecord12(record[key]);
21299
21994
  if (value) return value;
21300
21995
  }
21301
21996
  return null;
21302
21997
  }
21303
21998
  function asList4(value) {
21304
21999
  if (Array.isArray(value)) return value;
21305
- const record = asRecord11(value);
22000
+ const record = asRecord12(value);
21306
22001
  if (!record) return [];
21307
22002
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
21308
22003
  if (Array.isArray(record[key])) return record[key];
@@ -21314,18 +22009,18 @@ function labelsOf(record) {
21314
22009
  if (!Array.isArray(raw)) return [];
21315
22010
  return raw.map((item) => {
21316
22011
  if (typeof item === "string") return item.trim();
21317
- const rec = asRecord11(item);
22012
+ const rec = asRecord12(item);
21318
22013
  return rec ? firstString3(rec, ["name", "title", "label"]) : "";
21319
22014
  }).filter(Boolean);
21320
22015
  }
21321
22016
  function commentsOf(record) {
21322
22017
  const out = [];
21323
22018
  for (const item of asList4(record.comments ?? record.notes ?? record.discussion)) {
21324
- const rec = asRecord11(item);
22019
+ const rec = asRecord12(item);
21325
22020
  if (!rec) continue;
21326
22021
  const body = firstString3(rec, ["body", "text", "comment", "message", "contentSanitized"]) || textFromAbleTimeDoc(rec.content);
21327
22022
  if (!body) continue;
21328
- const user = firstString3(asRecord11(rec.user) ?? asRecord11(rec.author), ["name", "display_name", "username"]) || firstString3(rec, ["user_name", "author", "username"]);
22023
+ const user = firstString3(asRecord12(rec.user) ?? asRecord12(rec.author), ["name", "display_name", "username"]) || firstString3(rec, ["user_name", "author", "username"]);
21329
22024
  const id = firstString3(rec, ["id", "comment_id", "commentId"]);
21330
22025
  const url = firstString3(rec, ["url", "permalink"]);
21331
22026
  const createdAt = firstString3(rec, ["created_at", "createdAt", "created"]);
@@ -21352,7 +22047,7 @@ function taskUrl(task, host) {
21352
22047
  return `${base}/tasks/${slug}`;
21353
22048
  }
21354
22049
  function mapAbleTimeTask(raw, host) {
21355
- const record = asRecord11(raw);
22050
+ const record = asRecord12(raw);
21356
22051
  if (!record) return null;
21357
22052
  const nested = firstRecord(record, ["task", "data"]) ?? record;
21358
22053
  const id = firstString3(nested, ["id", "task_id", "taskId", "timeflowTaskId"]);
@@ -21370,7 +22065,7 @@ function mapAbleTimeTask(raw, host) {
21370
22065
  description: firstString3(nested, ["description", "body", "details", "descriptionSanitized"]) || textFromAbleTimeDoc(nested.description) || void 0,
21371
22066
  state: firstString3(nested, ["state", "board_state", "boardState", "status", "taskState"]) || void 0,
21372
22067
  ...ableTimeProjectFields(nested),
21373
- categoryId: firstString3(nested, ["category_id", "categoryId", "projectCategoryId"]) || firstString3(asRecord11(nested.category), ["id"]) || void 0,
22068
+ categoryId: firstString3(nested, ["category_id", "categoryId", "projectCategoryId"]) || firstString3(asRecord12(nested.category), ["id"]) || void 0,
21374
22069
  assignee: assigneeOf(nested),
21375
22070
  labels: labelsOf(nested),
21376
22071
  comments: commentsOf(nested),
@@ -21420,7 +22115,7 @@ function isOpenTask(task) {
21420
22115
  }
21421
22116
  async function getAbleTimeOrientation(opts) {
21422
22117
  const raw = await callAbleTimeTool("orientation", {}, opts);
21423
- const record = asRecord11(raw);
22118
+ const record = asRecord12(raw);
21424
22119
  const viewerRec = firstRecord(record, ["viewer", "principal", "user", "actor", "me", "person"]) ?? record;
21425
22120
  const viewer = {
21426
22121
  id: firstString3(viewerRec, ["id", "user_id"]) || void 0,
@@ -21499,7 +22194,7 @@ async function commentAbleTimeTask(input, opts) {
21499
22194
  { id, task_id: id, task: id, body, comment: body, text: body },
21500
22195
  opts
21501
22196
  );
21502
- const rec = asRecord11(raw);
22197
+ const rec = asRecord12(raw);
21503
22198
  return {
21504
22199
  id: rec ? firstString3(rec, ["id", "comment_id"]) || void 0 : void 0,
21505
22200
  body: rec ? firstString3(rec, ["body", "text", "comment"]) || body : body
@@ -22366,6 +23061,7 @@ function noteTurnLiveEvent(threadId, event) {
22366
23061
  const prev = buffers.get(threadId) ?? { parts: [], timer: null };
22367
23062
  prev.parts = trimParts(applyAgentEvent(prev.parts, event));
22368
23063
  buffers.set(threadId, prev);
23064
+ syncAskUserBlockFromLive(threadId, prev.parts);
22369
23065
  if (event.type === "exit") {
22370
23066
  flush(threadId);
22371
23067
  const cur = buffers.get(threadId);
@@ -22419,6 +23115,7 @@ var init_turn_live = __esm({
22419
23115
  import_node_fs55 = require("fs");
22420
23116
  init_message_parts();
22421
23117
  init_paths();
23118
+ init_agent_block();
22422
23119
  buffers = /* @__PURE__ */ new Map();
22423
23120
  FLUSH_MS = 800;
22424
23121
  MAX_PARTS = 80;
@@ -22635,7 +23332,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
22635
23332
  ...instructions
22636
23333
  ].join("\n");
22637
23334
  return {
22638
- id: (0, import_node_crypto14.randomUUID)(),
23335
+ id: (0, import_node_crypto16.randomUUID)(),
22639
23336
  name: orch ? "Orchestration quota handoff.md" : "Quota handoff.md",
22640
23337
  kind: "transcript",
22641
23338
  content: body
@@ -22667,11 +23364,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText, opts) {
22667
23364
  }
22668
23365
  return updateThread(tab.id, patch);
22669
23366
  }
22670
- var import_node_crypto14, QUOTA_CONTINUE_PROMPT, QUOTA_CONTINUE_ACCOUNT_PROMPT, QUOTA_RESUME_PROMPT;
23367
+ var import_node_crypto16, QUOTA_CONTINUE_PROMPT, QUOTA_CONTINUE_ACCOUNT_PROMPT, QUOTA_RESUME_PROMPT;
22671
23368
  var init_quota_failover = __esm({
22672
23369
  "src/orchestrator/quota-failover.ts"() {
22673
23370
  "use strict";
22674
- import_node_crypto14 = require("crypto");
23371
+ import_node_crypto16 = require("crypto");
22675
23372
  init_session_quota();
22676
23373
  init_app_settings();
22677
23374
  init_managed_accounts();
@@ -24734,7 +25431,7 @@ function compiledModuleDir() {
24734
25431
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
24735
25432
  if (cjsDir) return cjsDir;
24736
25433
  try {
24737
- const url = import_meta6.url;
25434
+ const url = import_meta8.url;
24738
25435
  if (typeof url === "string" && url.length > 0) {
24739
25436
  return (0, import_node_path60.dirname)((0, import_node_url5.fileURLToPath)(url));
24740
25437
  }
@@ -24773,7 +25470,7 @@ function formatDetachedJobInvoke(scriptPath) {
24773
25470
  if (resolved) return `node ${JSON.stringify(resolved)}`;
24774
25471
  return "node scripts/detached-job.cjs";
24775
25472
  }
24776
- var import_node_fs61, import_node_path60, import_node_url5, import_meta6, DETACHED_JOB_FILENAMES;
25473
+ var import_node_fs61, import_node_path60, import_node_url5, import_meta8, DETACHED_JOB_FILENAMES;
24777
25474
  var init_detached_job_path = __esm({
24778
25475
  "src/skills/detached-job-path.ts"() {
24779
25476
  "use strict";
@@ -24781,7 +25478,7 @@ var init_detached_job_path = __esm({
24781
25478
  import_node_path60 = require("path");
24782
25479
  import_node_url5 = require("url");
24783
25480
  init_packaged_runtime();
24784
- import_meta6 = {};
25481
+ import_meta8 = {};
24785
25482
  DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
24786
25483
  }
24787
25484
  });
@@ -26149,7 +26846,7 @@ function buildTask(input, now) {
26149
26846
  const ts = nowIso2(now);
26150
26847
  const agent = input.agent ? coerceOrchestratorAgent(input.agent) : null;
26151
26848
  return {
26152
- id: (0, import_node_crypto15.randomUUID)(),
26849
+ id: (0, import_node_crypto17.randomUUID)(),
26153
26850
  name: input.name?.trim() || defaultScheduleName(prompt),
26154
26851
  prompt,
26155
26852
  enabled: input.enabled !== false,
@@ -26249,11 +26946,11 @@ function recordScheduleRun(id, result) {
26249
26946
  writeAll2(rows);
26250
26947
  return next;
26251
26948
  }
26252
- var import_node_crypto15, import_node_fs65, import_node_path64, import_croner, DURATION_RE, UNIT_MS;
26949
+ var import_node_crypto17, import_node_fs65, import_node_path64, import_croner, DURATION_RE, UNIT_MS;
26253
26950
  var init_schedules = __esm({
26254
26951
  "src/store/schedules.ts"() {
26255
26952
  "use strict";
26256
- import_node_crypto15 = require("crypto");
26953
+ import_node_crypto17 = require("crypto");
26257
26954
  import_node_fs65 = require("fs");
26258
26955
  import_node_path64 = require("path");
26259
26956
  import_croner = require("croner");
@@ -26496,10 +27193,10 @@ function collectActiveRunPorts(runs) {
26496
27193
  return [...ports];
26497
27194
  }
26498
27195
  function getOrchestrator() {
26499
- if (!singleton) {
26500
- singleton = new Orchestrator();
27196
+ if (!singleton2) {
27197
+ singleton2 = new Orchestrator();
26501
27198
  }
26502
- return singleton;
27199
+ return singleton2;
26503
27200
  }
26504
27201
  async function startOrchestration(opts) {
26505
27202
  const repoPath = opts.repoPath?.trim();
@@ -26559,7 +27256,7 @@ async function startOrchestration(opts) {
26559
27256
  }
26560
27257
  return updated;
26561
27258
  }
26562
- var import_node_events, import_node_crypto16, import_node_fs67, LIVE_TURN_SPAWN_GRACE_MS, RUN_SCRIPT_DESKTOP_ADOPT_MS, STALE_AGENT_PID_WAIT_MS, LIGHT_RECONCILE_THROTTLE_MS, Orchestrator, singleton;
27259
+ var import_node_events, import_node_crypto18, import_node_fs67, LIVE_TURN_SPAWN_GRACE_MS, RUN_SCRIPT_DESKTOP_ADOPT_MS, STALE_AGENT_PID_WAIT_MS, LIGHT_RECONCILE_THROTTLE_MS, Orchestrator, singleton2;
26563
27260
  var init_orchestrator = __esm({
26564
27261
  "src/orchestrator/orchestrator.ts"() {
26565
27262
  "use strict";
@@ -26567,7 +27264,7 @@ var init_orchestrator = __esm({
26567
27264
  init_injected_notices();
26568
27265
  init_task_state();
26569
27266
  init_wait_for_turn();
26570
- import_node_crypto16 = require("crypto");
27267
+ import_node_crypto18 = require("crypto");
26571
27268
  import_node_fs67 = require("fs");
26572
27269
  init_error_detail();
26573
27270
  init_run();
@@ -26593,6 +27290,7 @@ var init_orchestrator = __esm({
26593
27290
  init_usage();
26594
27291
  init_thread_store();
26595
27292
  init_desktop_host();
27293
+ init_model_context();
26596
27294
  init_agent_block();
26597
27295
  init_child_halt();
26598
27296
  init_stop_reason();
@@ -27591,6 +28289,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
27591
28289
  orchestrationReminder,
27592
28290
  prGateDirective,
27593
28291
  injectedNoticeContext,
28292
+ takeMcpAppModelContext(threadId),
27594
28293
  expandedPrompt
27595
28294
  ].filter(Boolean).join("\n\n");
27596
28295
  if (!autoContinue && sentAttachments) {
@@ -28219,7 +28918,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
28219
28918
  const request = sameIntent ? pending : {
28220
28919
  op,
28221
28920
  scriptName: scriptName ?? null,
28222
- requestId: (0, import_node_crypto16.randomUUID)(),
28921
+ requestId: (0, import_node_crypto18.randomUUID)(),
28223
28922
  requestedAt: (/* @__PURE__ */ new Date()).toISOString()
28224
28923
  };
28225
28924
  if (!sameIntent) {
@@ -29340,7 +30039,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
29340
30039
  return thread;
29341
30040
  }
29342
30041
  };
29343
- singleton = null;
30042
+ singleton2 = null;
29344
30043
  }
29345
30044
  });
29346
30045
 
@@ -29349,7 +30048,7 @@ init_nested_electron_env();
29349
30048
  init_system_ca();
29350
30049
 
29351
30050
  // src/mcp/server.ts
29352
- var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
30051
+ var import_stdio2 = require("@modelcontextprotocol/sdk/server/stdio.js");
29353
30052
 
29354
30053
  // src/mcp/wrap-mcp-tools.ts
29355
30054
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
@@ -29460,7 +30159,7 @@ function createClippedMcpServer() {
29460
30159
 
29461
30160
  // src/mcp/server.ts
29462
30161
  var import_zod11 = require("zod");
29463
- var import_node_crypto20 = require("crypto");
30162
+ var import_node_crypto22 = require("crypto");
29464
30163
  var import_node_path68 = require("path");
29465
30164
  init_orchestrator();
29466
30165
  init_task_state();
@@ -29972,8 +30671,8 @@ init_fetch();
29972
30671
  init_issue_attachments();
29973
30672
 
29974
30673
  // src/integrations/linear-oauth.ts
29975
- var import_node_crypto17 = require("crypto");
29976
- var import_node_http2 = require("http");
30674
+ var import_node_crypto19 = require("crypto");
30675
+ var import_node_http3 = require("http");
29977
30676
  init_app_settings();
29978
30677
  init_fetch();
29979
30678
 
@@ -32958,7 +33657,7 @@ function registerScheduleTools(server) {
32958
33657
  }
32959
33658
 
32960
33659
  // src/agent-rpc/handlers.ts
32961
- var import_node_crypto18 = require("crypto");
33660
+ var import_node_crypto20 = require("crypto");
32962
33661
  var import_node_path65 = require("path");
32963
33662
  init_wait_for_job();
32964
33663
  init_wait_for_turn();
@@ -33005,7 +33704,7 @@ setToolPermissionPublisher({
33005
33704
  // src/agent-rpc/handlers.ts
33006
33705
  init_protocol2();
33007
33706
  function presentArtifactResult(input) {
33008
- const id = input.artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${(0, import_node_crypto18.randomUUID)().slice(0, 8)}`;
33707
+ const id = input.artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${(0, import_node_crypto20.randomUUID)().slice(0, 8)}`;
33009
33708
  return {
33010
33709
  ok: true,
33011
33710
  artifact_id: id,
@@ -33030,7 +33729,7 @@ init_worktree();
33030
33729
  init_paths();
33031
33730
 
33032
33731
  // src/board/board-pins.ts
33033
- var import_node_crypto19 = require("crypto");
33732
+ var import_node_crypto21 = require("crypto");
33034
33733
  var import_node_fs68 = require("fs");
33035
33734
  var import_node_path66 = require("path");
33036
33735
  init_paths();
@@ -33980,7 +34679,7 @@ async function startMcpServer() {
33980
34679
  const ident = issue?.identifier ?? ref.trim();
33981
34680
  const attachments = issue ? [
33982
34681
  {
33983
- id: (0, import_node_crypto20.randomUUID)(),
34682
+ id: (0, import_node_crypto22.randomUUID)(),
33984
34683
  name: issue.identifier,
33985
34684
  kind: "issue",
33986
34685
  content: [
@@ -34533,7 +35232,7 @@ async function startMcpServer() {
34533
35232
  }
34534
35233
  );
34535
35234
  }
34536
- const transport = new import_stdio.StdioServerTransport();
35235
+ const transport = new import_stdio2.StdioServerTransport();
34537
35236
  await server.connect(transport);
34538
35237
  }
34539
35238