@sideboard-ai/core 0.1.311 → 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.
@@ -11332,6 +11332,19 @@ var init_brightsy = __esm({
11332
11332
  });
11333
11333
 
11334
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
+ }
11335
11348
  function parseMcpServerLaunch(entry) {
11336
11349
  const command = typeof entry.command === "string" ? entry.command.trim() : "";
11337
11350
  if (command) {
@@ -11363,6 +11376,105 @@ function parseMcpServerLaunch(entry) {
11363
11376
  ...headers ? { headers } : {}
11364
11377
  };
11365
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
+ }
11366
11478
  function stringRecord(value) {
11367
11479
  const rec = asRecord3(value);
11368
11480
  const out = {};
@@ -11375,9 +11487,11 @@ function asRecord3(value) {
11375
11487
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
11376
11488
  return value;
11377
11489
  }
11490
+ var HTML_MAX;
11378
11491
  var init_meta = __esm({
11379
11492
  "src/mcp-ui/meta.ts"() {
11380
11493
  "use strict";
11494
+ HTML_MAX = 15e5;
11381
11495
  }
11382
11496
  });
11383
11497
 
@@ -12416,6 +12530,519 @@ var init_orch_mcp_isolation = __esm({
12416
12530
  }
12417
12531
  });
12418
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
+
12419
13046
  // src/agents/types.ts
12420
13047
  function planModeTurnInstruction(planMode) {
12421
13048
  return planMode ? PLAN_MODE_INSTRUCTION : PLAN_MODE_OFF_INSTRUCTION;
@@ -12596,7 +13223,7 @@ function entryDir() {
12596
13223
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
12597
13224
  if (cjsDir) return cjsDir;
12598
13225
  try {
12599
- 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));
12600
13227
  } catch {
12601
13228
  try {
12602
13229
  const req = (0, import_node_module2.createRequire)(process.cwd() + "/");
@@ -12646,7 +13273,7 @@ async function buildClaudeRpcTurnCommand(req) {
12646
13273
  }
12647
13274
  };
12648
13275
  }
12649
- 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;
12650
13277
  var init_claude_host = __esm({
12651
13278
  "src/agents/claude-host.ts"() {
12652
13279
  "use strict";
@@ -12658,7 +13285,7 @@ var init_claude_host = __esm({
12658
13285
  init_pilot_tools();
12659
13286
  init_node_launch();
12660
13287
  init_packaged_runtime();
12661
- import_meta3 = {};
13288
+ import_meta5 = {};
12662
13289
  }
12663
13290
  });
12664
13291
 
@@ -13061,16 +13688,15 @@ function parseIssuesJson(raw) {
13061
13688
  }
13062
13689
  return [];
13063
13690
  }
13064
- var import_node_fs34, import_node_os15, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
13691
+ var import_node_fs34, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
13065
13692
  var init_claude = __esm({
13066
13693
  "src/agents/claude.ts"() {
13067
13694
  "use strict";
13068
13695
  import_node_fs34 = require("fs");
13069
- import_node_os15 = require("os");
13070
13696
  init_run();
13071
13697
  init_app_settings();
13072
13698
  init_claude_mcp();
13073
- init_orch_mcp_isolation();
13699
+ init_host();
13074
13700
  init_error_detail();
13075
13701
  init_message_parts();
13076
13702
  init_injected_mcp();
@@ -13147,7 +13773,7 @@ var init_claude = __esm({
13147
13773
  });
13148
13774
  const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
13149
13775
  const chromeOn = claudeChromeEnabled();
13150
- const userMcpEntries = isOrchestrator ? {} : listUserClaudeMcpServerEntries((0, import_node_os15.homedir)(), [thread.repoPath, thread.worktreePath]);
13776
+ const userMcpEntries = await proxyClaudeUserMcp(isOrchestrator, [thread.repoPath, thread.worktreePath]);
13151
13777
  let allowedTools;
13152
13778
  if (isOrchestrator) {
13153
13779
  allowedTools = [
@@ -13449,7 +14075,7 @@ function unwrapCodexMcpResult(result) {
13449
14075
  return String(result);
13450
14076
  }
13451
14077
  }
13452
- function asRecord4(value) {
14078
+ function asRecord5(value) {
13453
14079
  if (value && typeof value === "object" && !Array.isArray(value)) {
13454
14080
  return value;
13455
14081
  }
@@ -13629,7 +14255,7 @@ var init_codex = __esm({
13629
14255
  }
13630
14256
  if (itemType === "mcp_tool_call" && item.id && item.server && item.tool) {
13631
14257
  const name = `mcp__${item.server}__${item.tool}`;
13632
- const input = asRecord4(item.arguments);
14258
+ const input = asRecord5(item.arguments);
13633
14259
  const events = [
13634
14260
  { type: "tool_use", id: item.id, name, input }
13635
14261
  ];
@@ -13776,7 +14402,7 @@ function thisModuleFile() {
13776
14402
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
13777
14403
  if (cjsDir) return (0, import_node_path37.join)(cjsDir, "cursor-sdk-resolve.js");
13778
14404
  try {
13779
- return (0, import_node_url3.fileURLToPath)(import_meta4.url);
14405
+ return (0, import_node_url3.fileURLToPath)(import_meta6.url);
13780
14406
  } catch {
13781
14407
  return (0, import_node_path37.join)(process.cwd(), "package.json");
13782
14408
  }
@@ -13875,7 +14501,7 @@ function cursorSdkRipgrepCandidate(opts) {
13875
14501
  const nested = (0, import_node_path37.join)(root, "node_modules", pkg, "bin", bin);
13876
14502
  return (0, import_node_fs36.existsSync)(nested) ? nested : null;
13877
14503
  }
13878
- 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;
13879
14505
  var init_cursor_sdk_resolve = __esm({
13880
14506
  "src/agents/cursor-sdk-resolve.ts"() {
13881
14507
  "use strict";
@@ -13884,7 +14510,7 @@ var init_cursor_sdk_resolve = __esm({
13884
14510
  import_node_path37 = require("path");
13885
14511
  import_node_url3 = require("url");
13886
14512
  init_path();
13887
- import_meta4 = {};
14513
+ import_meta6 = {};
13888
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.";
13889
14515
  }
13890
14516
  });
@@ -14019,7 +14645,7 @@ function entryDir2() {
14019
14645
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
14020
14646
  if (cjsDir) return cjsDir;
14021
14647
  try {
14022
- 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));
14023
14649
  } catch {
14024
14650
  try {
14025
14651
  const req = (0, import_node_module5.createRequire)(process.cwd() + "/");
@@ -14048,7 +14674,7 @@ function cursorRunnerPath() {
14048
14674
  }
14049
14675
  return candidates[0];
14050
14676
  }
14051
- 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;
14052
14678
  var init_cursor = __esm({
14053
14679
  "src/agents/cursor.ts"() {
14054
14680
  "use strict";
@@ -14068,7 +14694,7 @@ var init_cursor = __esm({
14068
14694
  init_packaged_runtime();
14069
14695
  init_turn_input();
14070
14696
  init_cursor_events();
14071
- import_meta5 = {};
14697
+ import_meta7 = {};
14072
14698
  FALLBACK_CURSOR_MODELS = [
14073
14699
  { id: "default", displayName: "Auto" },
14074
14700
  { id: "composer-2.5", displayName: "Composer 2.5" },
@@ -14342,7 +14968,7 @@ var init_opencode_host = __esm({
14342
14968
  });
14343
14969
 
14344
14970
  // src/agents/opencode.ts
14345
- function asRecord5(value) {
14971
+ function asRecord6(value) {
14346
14972
  if (value && typeof value === "object" && !Array.isArray(value)) {
14347
14973
  return value;
14348
14974
  }
@@ -14538,7 +15164,7 @@ var init_opencode = __esm({
14538
15164
  const name = part?.tool ?? part?.name ?? obj.tool ?? obj.name ?? "tool";
14539
15165
  const state = part?.state;
14540
15166
  const input = (state?.input && typeof state.input === "object" ? state.input : void 0) ?? part?.input ?? obj.input;
14541
- const metadata = asRecord5(state?.metadata);
15167
+ const metadata = asRecord6(state?.metadata);
14542
15168
  const id = opencodeToolId(
14543
15169
  name,
14544
15170
  part?.callID ?? part?.id ?? obj.id,
@@ -14573,7 +15199,7 @@ var init_opencode = __esm({
14573
15199
  }
14574
15200
  if (obj.type === "subtask_event") {
14575
15201
  const parentId = str3(obj.childSessionID) ?? str3(obj.childSessionId);
14576
- const part = asRecord5(obj.part);
15202
+ const part = asRecord6(obj.part);
14577
15203
  if (!parentId || !part) return null;
14578
15204
  if (part.type === "text" && str3(part.text)) {
14579
15205
  return withTaskParent(
@@ -14584,8 +15210,8 @@ var init_opencode = __esm({
14584
15210
  if (part.type === "tool" || str3(part.tool)) {
14585
15211
  const name = str3(part.tool) ?? str3(part.name) ?? "tool";
14586
15212
  const id = str3(part.callID) ?? str3(part.id) ?? `${parentId}-${name}`;
14587
- const state = asRecord5(part.state);
14588
- const input = asRecord5(state?.input) ?? asRecord5(part.input);
15213
+ const state = asRecord6(part.state);
15214
+ const input = asRecord6(state?.input) ?? asRecord6(part.input);
14589
15215
  const nested = [
14590
15216
  withEventParentId({ type: "tool_use", id, name, input }, parentId)
14591
15217
  ];
@@ -16361,7 +16987,7 @@ function forkMessageSlice(from, throughIndex) {
16361
16987
  function buildForkTranscriptAttachment(baseTitle, messages) {
16362
16988
  const title = baseTitle || "Chat";
16363
16989
  return {
16364
- id: (0, import_node_crypto6.randomUUID)(),
16990
+ id: (0, import_node_crypto8.randomUUID)(),
16365
16991
  name: `Transcript of ${title}.md`,
16366
16992
  kind: "transcript",
16367
16993
  content: formatTranscriptMarkdown(title, messages)
@@ -16420,11 +17046,11 @@ function forkChatTab(input) {
16420
17046
  }
16421
17047
  return tab;
16422
17048
  }
16423
- var import_node_crypto6;
17049
+ var import_node_crypto8;
16424
17050
  var init_chat_tabs = __esm({
16425
17051
  "src/threads/chat-tabs.ts"() {
16426
17052
  "use strict";
16427
- import_node_crypto6 = require("crypto");
17053
+ import_node_crypto8 = require("crypto");
16428
17054
  init_context_compact();
16429
17055
  init_teams();
16430
17056
  init_worktree_labels();
@@ -16698,7 +17324,7 @@ function buildReviewRequestAttachment(content, opts) {
16698
17324
  const path = opts?.path ?? REVIEW_SKILL_PATH;
16699
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);
16700
17326
  return {
16701
- id: (0, import_node_crypto7.randomUUID)(),
17327
+ id: (0, import_node_crypto9.randomUUID)(),
16702
17328
  name,
16703
17329
  kind: "file",
16704
17330
  path,
@@ -16751,11 +17377,11 @@ async function requestReview(threadRef, send) {
16751
17377
  const started = await send(tab.id, REVIEW_REQUEST_PREFILL);
16752
17378
  return { tab: started, from };
16753
17379
  }
16754
- 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;
16755
17381
  var init_request_review = __esm({
16756
17382
  "src/review/request-review.ts"() {
16757
17383
  "use strict";
16758
- import_node_crypto7 = require("crypto");
17384
+ import_node_crypto9 = require("crypto");
16759
17385
  import_node_fs44 = require("fs");
16760
17386
  import_node_path44 = require("path");
16761
17387
  init_global_workspace();
@@ -18013,7 +18639,7 @@ function setupLogPath(threadId) {
18013
18639
  }
18014
18640
  function setupLogKeyForWorktree(worktreePath) {
18015
18641
  const norm = normalizeWorktreePath(worktreePath);
18016
- 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)}`;
18017
18643
  }
18018
18644
  function emptySetupLog() {
18019
18645
  return { ...EMPTY };
@@ -18106,11 +18732,11 @@ function finishSetupLog(threadId, exitCode, source) {
18106
18732
  persistNow(threadId);
18107
18733
  return materialize(mem);
18108
18734
  }
18109
- 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;
18110
18736
  var init_setup_log = __esm({
18111
18737
  "src/store/setup-log.ts"() {
18112
18738
  "use strict";
18113
- import_node_crypto8 = require("crypto");
18739
+ import_node_crypto10 = require("crypto");
18114
18740
  import_node_fs48 = require("fs");
18115
18741
  import_node_path48 = require("path");
18116
18742
  init_worktree_labels();
@@ -18161,7 +18787,7 @@ function runLogPath(key) {
18161
18787
  }
18162
18788
  function runLogKeyForWorktree(worktreePath, scriptName) {
18163
18789
  const norm = normalizeWorktreePath(worktreePath);
18164
- 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);
18165
18791
  return `wt-${hash}-run-${safeScriptToken(scriptName)}`;
18166
18792
  }
18167
18793
  function emptyRunLog() {
@@ -18262,11 +18888,11 @@ function finishRunLog(key, exitCode) {
18262
18888
  function flushRunLog(key) {
18263
18889
  persistNow2(key);
18264
18890
  }
18265
- 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;
18266
18892
  var init_run_log = __esm({
18267
18893
  "src/store/run-log.ts"() {
18268
18894
  "use strict";
18269
- import_node_crypto9 = require("crypto");
18895
+ import_node_crypto11 = require("crypto");
18270
18896
  import_node_fs49 = require("fs");
18271
18897
  import_node_path49 = require("path");
18272
18898
  init_worktree_labels();
@@ -18657,6 +19283,21 @@ var init_desktop_host = __esm({
18657
19283
  }
18658
19284
  });
18659
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
+
18660
19301
  // src/agents/tool-permission.ts
18661
19302
  function setToolPermissionPublisher(next) {
18662
19303
  publisher = next;
@@ -18677,11 +19318,11 @@ function denyPendingToolPermissions(threadId) {
18677
19318
  const last = pending[pending.length - 1];
18678
19319
  if (last) clearPrompt(threadId, last.id);
18679
19320
  }
18680
- var import_node_crypto10, import_node_path53, queues, publisher;
19321
+ var import_node_crypto12, import_node_path53, queues, publisher;
18681
19322
  var init_tool_permission = __esm({
18682
19323
  "src/agents/tool-permission.ts"() {
18683
19324
  "use strict";
18684
- import_node_crypto10 = require("crypto");
19325
+ import_node_crypto12 = require("crypto");
18685
19326
  import_node_path53 = require("path");
18686
19327
  init_claude_mcp();
18687
19328
  queues = /* @__PURE__ */ new Map();
@@ -18739,7 +19380,7 @@ var init_agent_block_view = __esm({
18739
19380
  });
18740
19381
 
18741
19382
  // src/plan/plan-present.ts
18742
- function asRecord6(v) {
19383
+ function asRecord7(v) {
18743
19384
  return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
18744
19385
  }
18745
19386
  function isPresentPlanToolName(name) {
@@ -18758,7 +19399,7 @@ function extractPresentedPlan(parts) {
18758
19399
  for (let i = parts.length - 1; i >= 0; i--) {
18759
19400
  const p = parts[i];
18760
19401
  if (p.type !== "tool" || !isPresentPlanToolName(p.name)) continue;
18761
- const input = asRecord6(p.input) ?? {};
19402
+ const input = asRecord7(p.input) ?? {};
18762
19403
  const content = typeof input.content === "string" ? input.content : typeof input.plan === "string" ? input.plan : typeof input.markdown === "string" ? input.markdown : "";
18763
19404
  if (!content.trim()) continue;
18764
19405
  const title = typeof input.title === "string" && input.title.trim() ? input.title.trim() : "Plan";
@@ -19213,7 +19854,7 @@ function isUnclaimedSteerRequest(req) {
19213
19854
  }
19214
19855
  function nextSteerRequest(existing) {
19215
19856
  if (existing?.requestId && !existing.claimedAt) return existing;
19216
- 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() };
19217
19858
  }
19218
19859
  function claimSteerRequest(threadId) {
19219
19860
  const pending = readThread(threadId)?.steerRequest;
@@ -19222,11 +19863,11 @@ function claimSteerRequest(threadId) {
19222
19863
  steerRequest: { ...pending, claimedAt: (/* @__PURE__ */ new Date()).toISOString() }
19223
19864
  });
19224
19865
  }
19225
- var import_node_crypto11;
19866
+ var import_node_crypto13;
19226
19867
  var init_steer_request = __esm({
19227
19868
  "src/orchestrator/steer-request.ts"() {
19228
19869
  "use strict";
19229
- import_node_crypto11 = require("crypto");
19870
+ import_node_crypto13 = require("crypto");
19230
19871
  init_thread_store();
19231
19872
  }
19232
19873
  });
@@ -19998,8 +20639,8 @@ function resolveAbleTimeOAuthHost(requested, saved) {
19998
20639
  return fromSaved || void 0;
19999
20640
  }
20000
20641
  function createAbleTimePkce() {
20001
- const verifier = (0, import_node_crypto12.randomBytes)(32).toString("base64url");
20002
- 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");
20003
20644
  return { verifier, challenge };
20004
20645
  }
20005
20646
  function ableTimeOAuthAuthorizeUrl(clientId, state, codeChallenge, host) {
@@ -20104,7 +20745,7 @@ async function startAbleTimeOAuth(opts) {
20104
20745
  opts?.host,
20105
20746
  loadAppSettings().integrations.abletimeHost
20106
20747
  );
20107
- const state = (0, import_node_crypto12.randomBytes)(16).toString("hex");
20748
+ const state = (0, import_node_crypto14.randomBytes)(16).toString("hex");
20108
20749
  const pkce = createAbleTimePkce();
20109
20750
  const authorizeUrl = ableTimeOAuthAuthorizeUrl(clientId, state, pkce.challenge, host);
20110
20751
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
@@ -20117,7 +20758,7 @@ async function startAbleTimeOAuth(opts) {
20117
20758
  if (err) reject(err);
20118
20759
  else resolve3(value);
20119
20760
  };
20120
- const server = (0, import_node_http.createServer)((req, res) => {
20761
+ const server = (0, import_node_http2.createServer)((req, res) => {
20121
20762
  try {
20122
20763
  const url = new URL(req.url || "/", `http://127.0.0.1:${ABLETIME_OAUTH_PORT}`);
20123
20764
  if (url.pathname !== "/callback") {
@@ -20204,12 +20845,12 @@ async function startAbleTimeOAuth(opts) {
20204
20845
  return loadAppSettings();
20205
20846
  }
20206
20847
  }
20207
- 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;
20208
20849
  var init_abletime_oauth = __esm({
20209
20850
  "src/integrations/abletime-oauth.ts"() {
20210
20851
  "use strict";
20211
- import_node_crypto12 = require("crypto");
20212
- import_node_http = require("http");
20852
+ import_node_crypto14 = require("crypto");
20853
+ import_node_http2 = require("http");
20213
20854
  init_fetch();
20214
20855
  init_app_settings();
20215
20856
  init_abletime_mcp();
@@ -20242,7 +20883,7 @@ __export(abletime_rest_exports, {
20242
20883
  looksLikeAbleTimeUlid: () => looksLikeAbleTimeUlid,
20243
20884
  textFromAbleTimeDoc: () => textFromAbleTimeDoc
20244
20885
  });
20245
- function asRecord7(value) {
20886
+ function asRecord8(value) {
20246
20887
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
20247
20888
  }
20248
20889
  function asString(value) {
@@ -20250,7 +20891,7 @@ function asString(value) {
20250
20891
  }
20251
20892
  function asList(value) {
20252
20893
  if (Array.isArray(value)) return value;
20253
- const record = asRecord7(value);
20894
+ const record = asRecord8(value);
20254
20895
  if (!record) return [];
20255
20896
  for (const key of ["data", "tasks", "projects", "users", "items", "results"]) {
20256
20897
  if (Array.isArray(record[key])) return record[key];
@@ -20267,12 +20908,12 @@ function ableTimeDocFromText(text7) {
20267
20908
  }
20268
20909
  function textFromAbleTimeDoc(value) {
20269
20910
  if (typeof value === "string") return value.trim();
20270
- const record = asRecord7(value);
20911
+ const record = asRecord8(value);
20271
20912
  if (!record) return "";
20272
20913
  if (typeof record.text === "string" && record.text.trim()) return record.text.trim();
20273
20914
  const chunks = [];
20274
20915
  const walk = (node) => {
20275
- const rec = asRecord7(node);
20916
+ const rec = asRecord8(node);
20276
20917
  if (!rec) return;
20277
20918
  if (typeof rec.text === "string" && rec.text) chunks.push(rec.text);
20278
20919
  if (Array.isArray(rec.content)) rec.content.forEach(walk);
@@ -20334,7 +20975,7 @@ async function restPages(path, opts, maxPages = 20) {
20334
20975
  query: { ...opts.query, ...cursor ? { cursor } : {} }
20335
20976
  });
20336
20977
  items.push(...asList(raw));
20337
- const page = asRecord7(asRecord7(raw)?.page);
20978
+ const page = asRecord8(asRecord8(raw)?.page);
20338
20979
  const next = page ? asString(page.nextCursor) : "";
20339
20980
  if (!next) break;
20340
20981
  cursor = next;
@@ -20342,7 +20983,7 @@ async function restPages(path, opts, maxPages = 20) {
20342
20983
  return items;
20343
20984
  }
20344
20985
  function userDisplayName(raw) {
20345
- const rec = asRecord7(raw);
20986
+ const rec = asRecord8(raw);
20346
20987
  if (!rec) return null;
20347
20988
  const id = asString(rec.userId) || asString(rec.id);
20348
20989
  const name = [asString(rec.firstName), asString(rec.lastName)].filter(Boolean).join(" ") || asString(rec.username) || asString(rec.email) || asString(rec.name);
@@ -20350,7 +20991,7 @@ function userDisplayName(raw) {
20350
20991
  return { id: id || void 0, name };
20351
20992
  }
20352
20993
  function restTaskToRaw(raw, users) {
20353
- const rec = asRecord7(raw);
20994
+ const rec = asRecord8(raw);
20354
20995
  if (!rec) return null;
20355
20996
  const id = asString(rec.timeflowTaskId) || asString(rec.taskId) || asString(rec.id);
20356
20997
  const identifier = asString(rec.taskRef) || asString(rec.reference) || id;
@@ -20358,7 +20999,7 @@ function restTaskToRaw(raw, users) {
20358
20999
  if (!id && !identifier && !title) return null;
20359
21000
  const assignee = users.get(asString(rec.assignedUserId));
20360
21001
  const comments = asList(rec.comments).map((item) => {
20361
- const comment = asRecord7(item) ?? {};
21002
+ const comment = asRecord8(item) ?? {};
20362
21003
  const user = userDisplayName(comment);
20363
21004
  return {
20364
21005
  id: asString(comment.commentId) || asString(comment.id),
@@ -20388,13 +21029,13 @@ function restTaskToRaw(raw, users) {
20388
21029
  };
20389
21030
  }
20390
21031
  function restProjectToRaw(raw) {
20391
- const rec = asRecord7(raw);
21032
+ const rec = asRecord8(raw);
20392
21033
  if (!rec) return null;
20393
21034
  const id = asString(rec.projectId) || asString(rec.id);
20394
21035
  const name = asString(rec.projectName) || asString(rec.name);
20395
21036
  if (!id && !name) return null;
20396
21037
  const categories = asList(rec.categories).map((item) => {
20397
- const cat = asRecord7(item) ?? {};
21038
+ const cat = asRecord8(item) ?? {};
20398
21039
  return {
20399
21040
  id: asString(cat.projectCategoryId) || asString(cat.categoryId) || asString(cat.id),
20400
21041
  name: asString(cat.categoryName) || asString(cat.name)
@@ -20406,7 +21047,7 @@ async function listUsers(opts) {
20406
21047
  const users = /* @__PURE__ */ new Map();
20407
21048
  for (const item of await restPages("/users", opts).catch(() => [])) {
20408
21049
  const user = userDisplayName(item);
20409
- const rec = asRecord7(item);
21050
+ const rec = asRecord8(item);
20410
21051
  const id = user?.id || (rec ? asString(rec.userId) : "");
20411
21052
  if (user && id) users.set(id, user);
20412
21053
  }
@@ -20532,10 +21173,10 @@ async function callAbleTimeRestTool(name, args = {}, opts) {
20532
21173
  if (!project) throw new Error("AbleTime has no projects to create a task in");
20533
21174
  const categories = asList(project.categories);
20534
21175
  const wantedCategory = firstStringArg(args, ["category_id", "categoryId", "category"]);
20535
- const category = categories.map((item) => asRecord7(item)).find((item) => {
21176
+ const category = categories.map((item) => asRecord8(item)).find((item) => {
20536
21177
  if (!item || !wantedCategory) return false;
20537
21178
  return asString(item.id).toLowerCase() === wantedCategory.toLowerCase() || asString(item.name).toLowerCase() === wantedCategory.toLowerCase();
20538
- }) ?? asRecord7(categories[0]);
21179
+ }) ?? asRecord8(categories[0]);
20539
21180
  const parentArg = firstStringArg(args, ["parent", "parent_id", "parentId", "related_task_id"]);
20540
21181
  const parentTaskId = parentArg ? await resolveRestParentTaskId(parentArg, requestOpts) : void 0;
20541
21182
  const created = await abletimeRestRequest("/tasks", {
@@ -20549,7 +21190,7 @@ async function callAbleTimeRestTool(name, args = {}, opts) {
20549
21190
  }
20550
21191
  });
20551
21192
  const state = firstStringArg(args, ["state"]);
20552
- const createdId = asString(asRecord7(created)?.timeflowTaskId);
21193
+ const createdId = asString(asRecord8(created)?.timeflowTaskId);
20553
21194
  const description = asString(args.description);
20554
21195
  if (createdId && description) {
20555
21196
  await abletimeRestRequest(`/tasks/${encodeURIComponent(createdId)}`, {
@@ -20622,7 +21263,7 @@ async function callAbleTimeRestTool(name, args = {}, opts) {
20622
21263
  method: "POST",
20623
21264
  body: { content: ableTimeDocFromText(body) }
20624
21265
  });
20625
- const rec = asRecord7(created) ?? {};
21266
+ const rec = asRecord8(created) ?? {};
20626
21267
  return {
20627
21268
  id: asString(rec.commentId) || void 0,
20628
21269
  body: textFromAbleTimeDoc(rec.content) || body
@@ -20717,7 +21358,7 @@ function rewriteAbleTimeError(message) {
20717
21358
  }
20718
21359
  return message;
20719
21360
  }
20720
- function asRecord8(value) {
21361
+ function asRecord9(value) {
20721
21362
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
20722
21363
  }
20723
21364
  function parseSseJson(body) {
@@ -20739,12 +21380,12 @@ function parseSseJson(body) {
20739
21380
  }
20740
21381
  }
20741
21382
  function unwrapToolResult(result) {
20742
- const record = asRecord8(result);
21383
+ const record = asRecord9(result);
20743
21384
  if (!record) return result;
20744
21385
  const content = record.content;
20745
21386
  if (Array.isArray(content)) {
20746
21387
  const texts = content.map((part) => {
20747
- const item = asRecord8(part);
21388
+ const item = asRecord9(part);
20748
21389
  if (!item) return "";
20749
21390
  return typeof item.text === "string" ? item.text : "";
20750
21391
  }).filter(Boolean);
@@ -20766,7 +21407,7 @@ function unwrapToolResult(result) {
20766
21407
  return result;
20767
21408
  }
20768
21409
  function textsFromUnknown(value) {
20769
- const record = asRecord8(value);
21410
+ const record = asRecord9(value);
20770
21411
  if (!record) return "";
20771
21412
  if (typeof record.message === "string") return record.message;
20772
21413
  if (typeof record.error === "string") return record.error;
@@ -20813,7 +21454,7 @@ async function abletimeMcpRequest(method, params, opts) {
20813
21454
  return parseSseJson(body);
20814
21455
  }
20815
21456
  })();
20816
- const rpc = asRecord8(parsed);
21457
+ const rpc = asRecord9(parsed);
20817
21458
  if (rpc?.error) {
20818
21459
  const detail = typeof rpc.error.message === "string" && rpc.error.message.trim() ? rpc.error.message : `AbleTime MCP error ${rpc.error.code ?? ""}`.trim();
20819
21460
  throw new Error(rewriteAbleTimeError(detail));
@@ -20968,7 +21609,7 @@ function uniqueAttachmentFileName(dir, originalName) {
20968
21609
  const candidate = `${stem}-${i}${ext}`;
20969
21610
  if (!(0, import_node_fs53.existsSync)((0, import_node_path54.join)(dir, candidate))) return candidate;
20970
21611
  }
20971
- return `${stem}-${(0, import_node_crypto13.randomUUID)()}${ext}`;
21612
+ return `${stem}-${(0, import_node_crypto15.randomUUID)()}${ext}`;
20972
21613
  }
20973
21614
  function extractHttpUrls(markdown) {
20974
21615
  const text7 = markdown ?? "";
@@ -21074,13 +21715,13 @@ async function downloadAuthenticatedFile(opts) {
21074
21715
  ...contentType ? { contentType } : {}
21075
21716
  };
21076
21717
  }
21077
- 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;
21078
21719
  var init_issue_attachments = __esm({
21079
21720
  "src/integrations/issue-attachments.ts"() {
21080
21721
  "use strict";
21081
21722
  import_node_fs53 = require("fs");
21082
21723
  import_node_path54 = require("path");
21083
- import_node_crypto13 = require("crypto");
21724
+ import_node_crypto15 = require("crypto");
21084
21725
  init_fetch();
21085
21726
  init_workspace_scratch();
21086
21727
  ISSUE_ATTACHMENT_MAX_BYTES = 25 * 1024 * 1024;
@@ -21105,7 +21746,7 @@ var init_issue_attachments = __esm({
21105
21746
  });
21106
21747
 
21107
21748
  // src/integrations/abletime-attachments.ts
21108
- function asRecord9(value) {
21749
+ function asRecord10(value) {
21109
21750
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
21110
21751
  }
21111
21752
  function asString2(value) {
@@ -21113,7 +21754,7 @@ function asString2(value) {
21113
21754
  }
21114
21755
  function asList2(value) {
21115
21756
  if (Array.isArray(value)) return value;
21116
- const record = asRecord9(value);
21757
+ const record = asRecord10(value);
21117
21758
  if (!record) return [];
21118
21759
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
21119
21760
  if (Array.isArray(record[key])) return record[key];
@@ -21132,9 +21773,9 @@ function filesFromAbleTimeDoc(value) {
21132
21773
  const out = [];
21133
21774
  const seen = /* @__PURE__ */ new Set();
21134
21775
  const walk = (node) => {
21135
- const rec = asRecord9(node);
21776
+ const rec = asRecord10(node);
21136
21777
  if (!rec) return;
21137
- const attrs = asRecord9(rec.attrs) ?? rec;
21778
+ const attrs = asRecord10(rec.attrs) ?? rec;
21138
21779
  const url = asString2(attrs.src) || asString2(attrs.href) || asString2(attrs.url) || asString2(attrs.fileUrl) || asString2(attrs.downloadUrl);
21139
21780
  if (url && !seen.has(url)) {
21140
21781
  seen.add(url);
@@ -21152,7 +21793,7 @@ function filesFromAbleTimeDoc(value) {
21152
21793
  function listedAbleTimeFiles(raw) {
21153
21794
  const out = [];
21154
21795
  for (const item of asList2(raw)) {
21155
- const rec = asRecord9(item);
21796
+ const rec = asRecord10(item);
21156
21797
  if (!rec) continue;
21157
21798
  const url = firstString(rec, ["url", "href", "src", "fileUrl", "downloadUrl", "permalink"]);
21158
21799
  if (!url) continue;
@@ -21172,7 +21813,7 @@ function mapAbleTimeAttachments(record) {
21172
21813
  ...filesFromAbleTimeDoc(record.description),
21173
21814
  ...filesFromAbleTimeDoc(record.content),
21174
21815
  ...asList2(record.comments).flatMap((item) => {
21175
- const rec = asRecord9(item);
21816
+ const rec = asRecord10(item);
21176
21817
  return rec ? filesFromAbleTimeDoc(rec.content ?? rec.body) : [];
21177
21818
  })
21178
21819
  ].map((file) => ({
@@ -21214,7 +21855,7 @@ async function resolveAbleTimeAttachmentUrl(id, opts) {
21214
21855
  const json = await res.json().catch(() => null);
21215
21856
  const rec = json && typeof json === "object" && !Array.isArray(json) ? json.data && typeof json.data === "object" ? json.data : json : null;
21216
21857
  if (!rec) continue;
21217
- 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"]);
21218
21859
  if (url) {
21219
21860
  return {
21220
21861
  url,
@@ -21256,7 +21897,7 @@ var init_abletime_attachments = __esm({
21256
21897
  });
21257
21898
 
21258
21899
  // src/integrations/abletime-projects.ts
21259
- function asRecord10(value) {
21900
+ function asRecord11(value) {
21260
21901
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
21261
21902
  }
21262
21903
  function firstString2(record, keys) {
@@ -21269,7 +21910,7 @@ function firstString2(record, keys) {
21269
21910
  }
21270
21911
  function asList3(value) {
21271
21912
  if (Array.isArray(value)) return value;
21272
- const record = asRecord10(value);
21913
+ const record = asRecord11(value);
21273
21914
  if (!record) return [];
21274
21915
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
21275
21916
  if (Array.isArray(record[key])) return record[key];
@@ -21277,7 +21918,7 @@ function asList3(value) {
21277
21918
  return [];
21278
21919
  }
21279
21920
  function ableTimeProjectFields(nested) {
21280
- const projectRecord = asRecord10(nested.project);
21921
+ const projectRecord = asRecord11(nested.project);
21281
21922
  const projectId = firstString2(nested, ["project_id", "projectId"]) || firstString2(projectRecord, ["id"]) || void 0;
21282
21923
  const projectName = firstString2(nested, ["project_name", "projectName"]) || firstString2(projectRecord, ["name", "title"]) || void 0;
21283
21924
  if (!projectId && !projectName) return {};
@@ -21290,13 +21931,13 @@ function ableTimeProjectFields(nested) {
21290
21931
  };
21291
21932
  }
21292
21933
  function mapAbleTimeProject(raw) {
21293
- const record = asRecord10(raw);
21934
+ const record = asRecord11(raw);
21294
21935
  if (!record) return null;
21295
21936
  const id = firstString2(record, ["id", "project_id", "projectId"]);
21296
21937
  const name = firstString2(record, ["name", "title", "projectName"]);
21297
21938
  if (!id && !name) return null;
21298
21939
  const categories = asList3(record.categories ?? record.category).map((item) => {
21299
- const rec = asRecord10(item);
21940
+ const rec = asRecord11(item);
21300
21941
  if (!rec) return null;
21301
21942
  const categoryId = firstString2(rec, ["id", "category_id", "projectCategoryId", "categoryId"]);
21302
21943
  const categoryName = firstString2(rec, ["name", "title", "categoryName"]);
@@ -21332,7 +21973,7 @@ __export(abletime_exports, {
21332
21973
  updateAbleTimeTask: () => updateAbleTimeTask,
21333
21974
  verifyAbleTimeConnection: () => verifyAbleTimeConnection
21334
21975
  });
21335
- function asRecord11(value) {
21976
+ function asRecord12(value) {
21336
21977
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
21337
21978
  }
21338
21979
  function asString3(value) {
@@ -21349,14 +21990,14 @@ function firstString3(record, keys) {
21349
21990
  function firstRecord(record, keys) {
21350
21991
  if (!record) return null;
21351
21992
  for (const key of keys) {
21352
- const value = asRecord11(record[key]);
21993
+ const value = asRecord12(record[key]);
21353
21994
  if (value) return value;
21354
21995
  }
21355
21996
  return null;
21356
21997
  }
21357
21998
  function asList4(value) {
21358
21999
  if (Array.isArray(value)) return value;
21359
- const record = asRecord11(value);
22000
+ const record = asRecord12(value);
21360
22001
  if (!record) return [];
21361
22002
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
21362
22003
  if (Array.isArray(record[key])) return record[key];
@@ -21368,18 +22009,18 @@ function labelsOf(record) {
21368
22009
  if (!Array.isArray(raw)) return [];
21369
22010
  return raw.map((item) => {
21370
22011
  if (typeof item === "string") return item.trim();
21371
- const rec = asRecord11(item);
22012
+ const rec = asRecord12(item);
21372
22013
  return rec ? firstString3(rec, ["name", "title", "label"]) : "";
21373
22014
  }).filter(Boolean);
21374
22015
  }
21375
22016
  function commentsOf(record) {
21376
22017
  const out = [];
21377
22018
  for (const item of asList4(record.comments ?? record.notes ?? record.discussion)) {
21378
- const rec = asRecord11(item);
22019
+ const rec = asRecord12(item);
21379
22020
  if (!rec) continue;
21380
22021
  const body = firstString3(rec, ["body", "text", "comment", "message", "contentSanitized"]) || textFromAbleTimeDoc(rec.content);
21381
22022
  if (!body) continue;
21382
- 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"]);
21383
22024
  const id = firstString3(rec, ["id", "comment_id", "commentId"]);
21384
22025
  const url = firstString3(rec, ["url", "permalink"]);
21385
22026
  const createdAt = firstString3(rec, ["created_at", "createdAt", "created"]);
@@ -21406,7 +22047,7 @@ function taskUrl(task, host) {
21406
22047
  return `${base}/tasks/${slug}`;
21407
22048
  }
21408
22049
  function mapAbleTimeTask(raw, host) {
21409
- const record = asRecord11(raw);
22050
+ const record = asRecord12(raw);
21410
22051
  if (!record) return null;
21411
22052
  const nested = firstRecord(record, ["task", "data"]) ?? record;
21412
22053
  const id = firstString3(nested, ["id", "task_id", "taskId", "timeflowTaskId"]);
@@ -21424,7 +22065,7 @@ function mapAbleTimeTask(raw, host) {
21424
22065
  description: firstString3(nested, ["description", "body", "details", "descriptionSanitized"]) || textFromAbleTimeDoc(nested.description) || void 0,
21425
22066
  state: firstString3(nested, ["state", "board_state", "boardState", "status", "taskState"]) || void 0,
21426
22067
  ...ableTimeProjectFields(nested),
21427
- 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,
21428
22069
  assignee: assigneeOf(nested),
21429
22070
  labels: labelsOf(nested),
21430
22071
  comments: commentsOf(nested),
@@ -21474,7 +22115,7 @@ function isOpenTask(task) {
21474
22115
  }
21475
22116
  async function getAbleTimeOrientation(opts) {
21476
22117
  const raw = await callAbleTimeTool("orientation", {}, opts);
21477
- const record = asRecord11(raw);
22118
+ const record = asRecord12(raw);
21478
22119
  const viewerRec = firstRecord(record, ["viewer", "principal", "user", "actor", "me", "person"]) ?? record;
21479
22120
  const viewer = {
21480
22121
  id: firstString3(viewerRec, ["id", "user_id"]) || void 0,
@@ -21553,7 +22194,7 @@ async function commentAbleTimeTask(input, opts) {
21553
22194
  { id, task_id: id, task: id, body, comment: body, text: body },
21554
22195
  opts
21555
22196
  );
21556
- const rec = asRecord11(raw);
22197
+ const rec = asRecord12(raw);
21557
22198
  return {
21558
22199
  id: rec ? firstString3(rec, ["id", "comment_id"]) || void 0 : void 0,
21559
22200
  body: rec ? firstString3(rec, ["body", "text", "comment"]) || body : body
@@ -22691,7 +23332,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
22691
23332
  ...instructions
22692
23333
  ].join("\n");
22693
23334
  return {
22694
- id: (0, import_node_crypto14.randomUUID)(),
23335
+ id: (0, import_node_crypto16.randomUUID)(),
22695
23336
  name: orch ? "Orchestration quota handoff.md" : "Quota handoff.md",
22696
23337
  kind: "transcript",
22697
23338
  content: body
@@ -22723,11 +23364,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText, opts) {
22723
23364
  }
22724
23365
  return updateThread(tab.id, patch);
22725
23366
  }
22726
- 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;
22727
23368
  var init_quota_failover = __esm({
22728
23369
  "src/orchestrator/quota-failover.ts"() {
22729
23370
  "use strict";
22730
- import_node_crypto14 = require("crypto");
23371
+ import_node_crypto16 = require("crypto");
22731
23372
  init_session_quota();
22732
23373
  init_app_settings();
22733
23374
  init_managed_accounts();
@@ -24790,7 +25431,7 @@ function compiledModuleDir() {
24790
25431
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
24791
25432
  if (cjsDir) return cjsDir;
24792
25433
  try {
24793
- const url = import_meta6.url;
25434
+ const url = import_meta8.url;
24794
25435
  if (typeof url === "string" && url.length > 0) {
24795
25436
  return (0, import_node_path60.dirname)((0, import_node_url5.fileURLToPath)(url));
24796
25437
  }
@@ -24829,7 +25470,7 @@ function formatDetachedJobInvoke(scriptPath) {
24829
25470
  if (resolved) return `node ${JSON.stringify(resolved)}`;
24830
25471
  return "node scripts/detached-job.cjs";
24831
25472
  }
24832
- 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;
24833
25474
  var init_detached_job_path = __esm({
24834
25475
  "src/skills/detached-job-path.ts"() {
24835
25476
  "use strict";
@@ -24837,7 +25478,7 @@ var init_detached_job_path = __esm({
24837
25478
  import_node_path60 = require("path");
24838
25479
  import_node_url5 = require("url");
24839
25480
  init_packaged_runtime();
24840
- import_meta6 = {};
25481
+ import_meta8 = {};
24841
25482
  DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
24842
25483
  }
24843
25484
  });
@@ -26205,7 +26846,7 @@ function buildTask(input, now) {
26205
26846
  const ts = nowIso2(now);
26206
26847
  const agent = input.agent ? coerceOrchestratorAgent(input.agent) : null;
26207
26848
  return {
26208
- id: (0, import_node_crypto15.randomUUID)(),
26849
+ id: (0, import_node_crypto17.randomUUID)(),
26209
26850
  name: input.name?.trim() || defaultScheduleName(prompt),
26210
26851
  prompt,
26211
26852
  enabled: input.enabled !== false,
@@ -26305,11 +26946,11 @@ function recordScheduleRun(id, result) {
26305
26946
  writeAll2(rows);
26306
26947
  return next;
26307
26948
  }
26308
- 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;
26309
26950
  var init_schedules = __esm({
26310
26951
  "src/store/schedules.ts"() {
26311
26952
  "use strict";
26312
- import_node_crypto15 = require("crypto");
26953
+ import_node_crypto17 = require("crypto");
26313
26954
  import_node_fs65 = require("fs");
26314
26955
  import_node_path64 = require("path");
26315
26956
  import_croner = require("croner");
@@ -26552,10 +27193,10 @@ function collectActiveRunPorts(runs) {
26552
27193
  return [...ports];
26553
27194
  }
26554
27195
  function getOrchestrator() {
26555
- if (!singleton) {
26556
- singleton = new Orchestrator();
27196
+ if (!singleton2) {
27197
+ singleton2 = new Orchestrator();
26557
27198
  }
26558
- return singleton;
27199
+ return singleton2;
26559
27200
  }
26560
27201
  async function startOrchestration(opts) {
26561
27202
  const repoPath = opts.repoPath?.trim();
@@ -26615,7 +27256,7 @@ async function startOrchestration(opts) {
26615
27256
  }
26616
27257
  return updated;
26617
27258
  }
26618
- 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;
26619
27260
  var init_orchestrator = __esm({
26620
27261
  "src/orchestrator/orchestrator.ts"() {
26621
27262
  "use strict";
@@ -26623,7 +27264,7 @@ var init_orchestrator = __esm({
26623
27264
  init_injected_notices();
26624
27265
  init_task_state();
26625
27266
  init_wait_for_turn();
26626
- import_node_crypto16 = require("crypto");
27267
+ import_node_crypto18 = require("crypto");
26627
27268
  import_node_fs67 = require("fs");
26628
27269
  init_error_detail();
26629
27270
  init_run();
@@ -26649,6 +27290,7 @@ var init_orchestrator = __esm({
26649
27290
  init_usage();
26650
27291
  init_thread_store();
26651
27292
  init_desktop_host();
27293
+ init_model_context();
26652
27294
  init_agent_block();
26653
27295
  init_child_halt();
26654
27296
  init_stop_reason();
@@ -27647,6 +28289,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
27647
28289
  orchestrationReminder,
27648
28290
  prGateDirective,
27649
28291
  injectedNoticeContext,
28292
+ takeMcpAppModelContext(threadId),
27650
28293
  expandedPrompt
27651
28294
  ].filter(Boolean).join("\n\n");
27652
28295
  if (!autoContinue && sentAttachments) {
@@ -28275,7 +28918,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
28275
28918
  const request = sameIntent ? pending : {
28276
28919
  op,
28277
28920
  scriptName: scriptName ?? null,
28278
- requestId: (0, import_node_crypto16.randomUUID)(),
28921
+ requestId: (0, import_node_crypto18.randomUUID)(),
28279
28922
  requestedAt: (/* @__PURE__ */ new Date()).toISOString()
28280
28923
  };
28281
28924
  if (!sameIntent) {
@@ -29396,7 +30039,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
29396
30039
  return thread;
29397
30040
  }
29398
30041
  };
29399
- singleton = null;
30042
+ singleton2 = null;
29400
30043
  }
29401
30044
  });
29402
30045
 
@@ -29405,7 +30048,7 @@ init_nested_electron_env();
29405
30048
  init_system_ca();
29406
30049
 
29407
30050
  // src/mcp/server.ts
29408
- var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
30051
+ var import_stdio2 = require("@modelcontextprotocol/sdk/server/stdio.js");
29409
30052
 
29410
30053
  // src/mcp/wrap-mcp-tools.ts
29411
30054
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
@@ -29516,7 +30159,7 @@ function createClippedMcpServer() {
29516
30159
 
29517
30160
  // src/mcp/server.ts
29518
30161
  var import_zod11 = require("zod");
29519
- var import_node_crypto20 = require("crypto");
30162
+ var import_node_crypto22 = require("crypto");
29520
30163
  var import_node_path68 = require("path");
29521
30164
  init_orchestrator();
29522
30165
  init_task_state();
@@ -30028,8 +30671,8 @@ init_fetch();
30028
30671
  init_issue_attachments();
30029
30672
 
30030
30673
  // src/integrations/linear-oauth.ts
30031
- var import_node_crypto17 = require("crypto");
30032
- var import_node_http2 = require("http");
30674
+ var import_node_crypto19 = require("crypto");
30675
+ var import_node_http3 = require("http");
30033
30676
  init_app_settings();
30034
30677
  init_fetch();
30035
30678
 
@@ -33014,7 +33657,7 @@ function registerScheduleTools(server) {
33014
33657
  }
33015
33658
 
33016
33659
  // src/agent-rpc/handlers.ts
33017
- var import_node_crypto18 = require("crypto");
33660
+ var import_node_crypto20 = require("crypto");
33018
33661
  var import_node_path65 = require("path");
33019
33662
  init_wait_for_job();
33020
33663
  init_wait_for_turn();
@@ -33061,7 +33704,7 @@ setToolPermissionPublisher({
33061
33704
  // src/agent-rpc/handlers.ts
33062
33705
  init_protocol2();
33063
33706
  function presentArtifactResult(input) {
33064
- 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)}`;
33065
33708
  return {
33066
33709
  ok: true,
33067
33710
  artifact_id: id,
@@ -33086,7 +33729,7 @@ init_worktree();
33086
33729
  init_paths();
33087
33730
 
33088
33731
  // src/board/board-pins.ts
33089
- var import_node_crypto19 = require("crypto");
33732
+ var import_node_crypto21 = require("crypto");
33090
33733
  var import_node_fs68 = require("fs");
33091
33734
  var import_node_path66 = require("path");
33092
33735
  init_paths();
@@ -34036,7 +34679,7 @@ async function startMcpServer() {
34036
34679
  const ident = issue?.identifier ?? ref.trim();
34037
34680
  const attachments = issue ? [
34038
34681
  {
34039
- id: (0, import_node_crypto20.randomUUID)(),
34682
+ id: (0, import_node_crypto22.randomUUID)(),
34040
34683
  name: issue.identifier,
34041
34684
  kind: "issue",
34042
34685
  content: [
@@ -34589,7 +35232,7 @@ async function startMcpServer() {
34589
35232
  }
34590
35233
  );
34591
35234
  }
34592
- const transport = new import_stdio.StdioServerTransport();
35235
+ const transport = new import_stdio2.StdioServerTransport();
34593
35236
  await server.connect(transport);
34594
35237
  }
34595
35238