@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.
package/dist/index.cjs CHANGED
@@ -13869,18 +13869,18 @@ function mcpUiContentSecurityPolicy(csp) {
13869
13869
  const connect = (csp?.connectDomains ?? []).join(" ");
13870
13870
  const frames = csp?.frameDomains?.length ? csp.frameDomains.join(" ") : "'none'";
13871
13871
  const base = csp?.baseUriDomains?.length ? csp.baseUriDomains.join(" ") : "'none'";
13872
+ const connectSrc = connect ? `'self' ${connect}`.trim() : "'none'";
13872
13873
  return [
13873
13874
  "default-src 'none'",
13874
- `script-src 'self' 'unsafe-inline' 'unsafe-eval' ${resource}`.trim(),
13875
+ `script-src 'self' 'unsafe-inline' ${resource}`.trim(),
13875
13876
  `style-src 'self' 'unsafe-inline' ${resource}`.trim(),
13876
- `img-src 'self' data: blob: ${resource}`.trim(),
13877
- `font-src 'self' data: ${resource}`.trim(),
13878
- `connect-src 'self' ${connect}`.trim(),
13879
- `media-src 'self' data: blob: ${resource}`.trim(),
13877
+ `img-src 'self' data: ${resource}`.trim(),
13878
+ `font-src 'self' ${resource}`.trim(),
13879
+ `connect-src ${connectSrc}`,
13880
+ `media-src 'self' data: ${resource}`.trim(),
13880
13881
  `frame-src ${frames}`,
13881
13882
  `base-uri ${base}`,
13882
- "object-src 'none'",
13883
- "form-action 'self'"
13883
+ "object-src 'none'"
13884
13884
  ].join("; ");
13885
13885
  }
13886
13886
  function htmlText(text7, blob) {
@@ -13917,8 +13917,10 @@ function originList(value) {
13917
13917
  return out;
13918
13918
  }
13919
13919
  function originToken(value) {
13920
+ const trimmed = value.trim();
13921
+ if (/^(https?|wss?):\/\/\*\.[a-z0-9.-]+(?::\d+)?$/i.test(trimmed)) return trimmed;
13920
13922
  try {
13921
- const url = new URL(value.trim());
13923
+ const url = new URL(trimmed);
13922
13924
  if (url.protocol !== "https:" && url.protocol !== "http:" && url.protocol !== "wss:" && url.protocol !== "ws:") {
13923
13925
  return null;
13924
13926
  }
@@ -15279,6 +15281,524 @@ var init_orch_mcp_isolation = __esm({
15279
15281
  }
15280
15282
  });
15281
15283
 
15284
+ // src/mcp-ui/visibility.ts
15285
+ function visibilityFromMeta(meta) {
15286
+ const rec = asRecord4(meta);
15287
+ const ui = asRecord4(rec.ui);
15288
+ const raw = ui.visibility;
15289
+ if (!Array.isArray(raw)) return ["model", "app"];
15290
+ const out = [];
15291
+ for (const item of raw) {
15292
+ if (item !== "model" && item !== "app") continue;
15293
+ if (!out.includes(item)) out.push(item);
15294
+ }
15295
+ return out.length ? out : ["model", "app"];
15296
+ }
15297
+ function toolVisibleTo(visibility, audience) {
15298
+ const vis = visibility?.length ? visibility : ["model", "app"];
15299
+ return vis.includes(audience);
15300
+ }
15301
+ function asRecord4(value) {
15302
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
15303
+ return value;
15304
+ }
15305
+ var init_visibility = __esm({
15306
+ "src/mcp-ui/visibility.ts"() {
15307
+ "use strict";
15308
+ }
15309
+ });
15310
+
15311
+ // src/mcp-ui/proxy-server.ts
15312
+ function attachMcpUiProxyHandlers(server, session) {
15313
+ server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
15314
+ const tools = await session.listTools();
15315
+ return { tools: tools.filter((tool) => toolVisibleTo(tool.visibility, "model")).map(listedTool) };
15316
+ });
15317
+ server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
15318
+ const name = request.params.name;
15319
+ const tools = await session.listTools();
15320
+ const tool = tools.find((item) => item.name === name);
15321
+ if (!tool || !toolVisibleTo(tool.visibility, "model")) {
15322
+ throw new Error(`Tool ${name} is not available to the model`);
15323
+ }
15324
+ return await session.callTool(name, request.params.arguments ?? {});
15325
+ });
15326
+ server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
15327
+ const resource = await session.readResource(request.params.uri);
15328
+ if (!resource) throw new Error(`Resource ${request.params.uri} was empty`);
15329
+ return {
15330
+ contents: [
15331
+ {
15332
+ uri: resource.uri,
15333
+ ...resource.mimeType ? { mimeType: resource.mimeType } : {},
15334
+ ...resource.text != null ? { text: resource.text } : {},
15335
+ ...resource.blob != null ? { blob: resource.blob } : {}
15336
+ }
15337
+ ]
15338
+ };
15339
+ });
15340
+ server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => session.listResources());
15341
+ server.setRequestHandler(
15342
+ import_types.ListResourceTemplatesRequestSchema,
15343
+ async () => session.listResourceTemplates()
15344
+ );
15345
+ server.setRequestHandler(import_types.ListPromptsRequestSchema, async () => session.listPrompts());
15346
+ server.setRequestHandler(import_types.GetPromptRequestSchema, async (request) => {
15347
+ return await session.getPrompt(request.params.name, request.params.arguments);
15348
+ });
15349
+ server.fallbackRequestHandler = async (request) => {
15350
+ return await session.forward(request.method, request.params);
15351
+ };
15352
+ }
15353
+ function listedTool(tool) {
15354
+ return {
15355
+ name: tool.name,
15356
+ ...tool.description ? { description: tool.description } : {},
15357
+ inputSchema: tool.inputSchema ?? { type: "object", properties: {} },
15358
+ _meta: {
15359
+ ui: {
15360
+ ...tool.resourceUri ? { resourceUri: tool.resourceUri } : {},
15361
+ visibility: tool.visibility
15362
+ }
15363
+ }
15364
+ };
15365
+ }
15366
+ var import_server, import_types;
15367
+ var init_proxy_server = __esm({
15368
+ "src/mcp-ui/proxy-server.ts"() {
15369
+ "use strict";
15370
+ import_server = require("@modelcontextprotocol/sdk/server/index.js");
15371
+ import_types = require("@modelcontextprotocol/sdk/types.js");
15372
+ init_visibility();
15373
+ }
15374
+ });
15375
+
15376
+ // src/mcp-ui/proxy-http.ts
15377
+ async function listenMcpUiProxy(opts) {
15378
+ const token = (0, import_node_crypto11.randomBytes)(24).toString("hex");
15379
+ const transports = /* @__PURE__ */ new Map();
15380
+ const httpServer = (0, import_node_http.createServer)((req, res) => {
15381
+ void handle(req, res);
15382
+ });
15383
+ await new Promise((resolve3, reject) => {
15384
+ httpServer.once("error", reject);
15385
+ httpServer.listen(0, "127.0.0.1", () => resolve3());
15386
+ });
15387
+ const addr = httpServer.address();
15388
+ if (!addr || typeof addr === "string") throw new Error("mcp-ui proxy failed to bind");
15389
+ const origin = `http://127.0.0.1:${addr.port}`;
15390
+ async function handle(req, res) {
15391
+ if (req.headers.authorization !== `Bearer ${token}`) {
15392
+ res.writeHead(401).end();
15393
+ return;
15394
+ }
15395
+ const target = parseMcpUiProxyPath(req.url);
15396
+ if (!target) {
15397
+ res.writeHead(404).end();
15398
+ return;
15399
+ }
15400
+ try {
15401
+ const session = await opts.sessionFor(target.server, target.dirsKey);
15402
+ if (!session) {
15403
+ res.writeHead(404).end();
15404
+ return;
15405
+ }
15406
+ const header = req.headers["mcp-session-id"];
15407
+ const sid = typeof header === "string" ? header : "";
15408
+ const sessionKey = `${target.server}:${target.dirsKey}`;
15409
+ let transport = sid ? transports.get(`${sessionKey}:${sid}`) : void 0;
15410
+ if (!transport) {
15411
+ if (req.method !== "POST") {
15412
+ res.writeHead(400).end();
15413
+ return;
15414
+ }
15415
+ const created = new import_streamableHttp.StreamableHTTPServerTransport({
15416
+ sessionIdGenerator: () => (0, import_node_crypto11.randomUUID)(),
15417
+ allowedHosts: ["127.0.0.1", "localhost"],
15418
+ onsessioninitialized: (id) => {
15419
+ transports.set(`${sessionKey}:${id}`, created);
15420
+ },
15421
+ onsessionclosed: (id) => {
15422
+ transports.delete(`${sessionKey}:${id}`);
15423
+ }
15424
+ });
15425
+ transport = created;
15426
+ const mcp = new import_server2.Server(
15427
+ { name: `sideboard-ui-proxy-${target.server}`, version: "1.0.0" },
15428
+ { capabilities: session.capabilities() }
15429
+ );
15430
+ attachMcpUiProxyHandlers(mcp, session);
15431
+ await mcp.connect(created);
15432
+ }
15433
+ await transport.handleRequest(req, res);
15434
+ } catch {
15435
+ if (!res.headersSent) res.writeHead(500).end();
15436
+ }
15437
+ }
15438
+ return {
15439
+ urlFor(server, dirsKey) {
15440
+ const base = `${origin}/s/${encodeURIComponent(server)}`;
15441
+ return dirsKey ? `${base}/d/${dirsKey}` : base;
15442
+ },
15443
+ headers: { Authorization: `Bearer ${token}` },
15444
+ async close() {
15445
+ await Promise.all([...transports.values()].map((item) => item.close().catch(() => void 0)));
15446
+ transports.clear();
15447
+ await new Promise((resolve3) => httpServer.close(() => resolve3()));
15448
+ }
15449
+ };
15450
+ }
15451
+ function parseMcpUiProxyPath(url) {
15452
+ const path2 = (url ?? "").split("?")[0] ?? "";
15453
+ if (!path2.startsWith("/s/")) return null;
15454
+ const rest = path2.slice(3).replace(/\/$/, "");
15455
+ const split = rest.indexOf("/d/");
15456
+ try {
15457
+ if (split < 0) {
15458
+ const server2 = decodeURIComponent(rest).trim();
15459
+ return server2 ? { server: server2, dirsKey: "" } : null;
15460
+ }
15461
+ const server = decodeURIComponent(rest.slice(0, split)).trim();
15462
+ const dirsKey = rest.slice(split + 3);
15463
+ if (!server || !/^[a-f0-9]{16}$/.test(dirsKey)) return null;
15464
+ return { server, dirsKey };
15465
+ } catch {
15466
+ return null;
15467
+ }
15468
+ }
15469
+ var import_node_crypto11, import_node_http, import_server2, import_streamableHttp;
15470
+ var init_proxy_http = __esm({
15471
+ "src/mcp-ui/proxy-http.ts"() {
15472
+ "use strict";
15473
+ import_node_crypto11 = require("crypto");
15474
+ import_node_http = require("http");
15475
+ import_server2 = require("@modelcontextprotocol/sdk/server/index.js");
15476
+ import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
15477
+ init_proxy_server();
15478
+ }
15479
+ });
15480
+
15481
+ // src/mcp-ui/session.ts
15482
+ async function orEmpty(run2, fallback) {
15483
+ try {
15484
+ return await run2();
15485
+ } catch {
15486
+ return fallback;
15487
+ }
15488
+ }
15489
+ async function openMcpUiSession(launch) {
15490
+ return openMcpUiSessionFromTransport(transportForLaunch(launch));
15491
+ }
15492
+ async function openMcpUiSessionFromTransport(transport) {
15493
+ const client = new import_client.Client(
15494
+ { name: "sideboard", version: "1.0.0" },
15495
+ { capabilities: UI_CAPABILITIES }
15496
+ );
15497
+ await client.connect(transport, { timeout: REQUEST_TIMEOUT_MS });
15498
+ return {
15499
+ async listTools() {
15500
+ const listed = await client.listTools(void 0, { timeout: REQUEST_TIMEOUT_MS });
15501
+ return listed.tools.map((tool) => {
15502
+ const meta = tool._meta;
15503
+ return {
15504
+ name: tool.name,
15505
+ ...tool.description ? { description: tool.description } : {},
15506
+ ...tool.inputSchema ? { inputSchema: tool.inputSchema } : {},
15507
+ resourceUri: uiResourceUriFromMeta(meta),
15508
+ visibility: visibilityFromMeta(meta),
15509
+ ...cspFromMeta(meta) ? { csp: cspFromMeta(meta) } : {}
15510
+ };
15511
+ });
15512
+ },
15513
+ listResources: () => orEmpty(
15514
+ () => client.listResources(void 0, { timeout: REQUEST_TIMEOUT_MS }),
15515
+ { resources: [] }
15516
+ ),
15517
+ listResourceTemplates: () => orEmpty(
15518
+ () => client.listResourceTemplates(void 0, { timeout: REQUEST_TIMEOUT_MS }),
15519
+ { resourceTemplates: [] }
15520
+ ),
15521
+ listPrompts: () => orEmpty(
15522
+ () => client.listPrompts(void 0, { timeout: REQUEST_TIMEOUT_MS }),
15523
+ { prompts: [] }
15524
+ ),
15525
+ getPrompt(name, args) {
15526
+ return client.getPrompt({ name, arguments: args }, { timeout: REQUEST_TIMEOUT_MS });
15527
+ },
15528
+ async readResource(uri) {
15529
+ const result = await client.readResource({ uri }, { timeout: REQUEST_TIMEOUT_MS });
15530
+ const content = result.contents?.[0];
15531
+ if (!content) return null;
15532
+ const text7 = "text" in content && typeof content.text === "string" ? content.text : void 0;
15533
+ const blob = "blob" in content && typeof content.blob === "string" ? content.blob : void 0;
15534
+ return {
15535
+ uri: content.uri,
15536
+ ...content.mimeType ? { mimeType: content.mimeType } : {},
15537
+ ...text7 != null ? { text: text7 } : {},
15538
+ ...blob != null ? { blob } : {},
15539
+ ...cspFromMeta(content._meta) ? { csp: cspFromMeta(content._meta) } : {}
15540
+ };
15541
+ },
15542
+ async callTool(name, args) {
15543
+ return client.callTool({ name, arguments: args }, void 0, { timeout: REQUEST_TIMEOUT_MS });
15544
+ },
15545
+ capabilities() {
15546
+ const caps = client.getServerCapabilities() ?? {};
15547
+ return {
15548
+ tools: caps.tools ?? {},
15549
+ resources: caps.resources ?? {},
15550
+ prompts: caps.prompts ?? {},
15551
+ completions: caps.completions ?? {},
15552
+ ...caps.logging ? { logging: caps.logging } : {}
15553
+ };
15554
+ },
15555
+ forward(method, params) {
15556
+ return client.request(
15557
+ { method, params },
15558
+ import_types2.ResultSchema,
15559
+ { timeout: REQUEST_TIMEOUT_MS }
15560
+ );
15561
+ },
15562
+ async close() {
15563
+ await client.close();
15564
+ }
15565
+ };
15566
+ }
15567
+ function transportForLaunch(launch) {
15568
+ if (launch.kind === "stdio") {
15569
+ return new import_stdio.StdioClientTransport({
15570
+ command: launch.command,
15571
+ args: launch.args,
15572
+ env: { ...(0, import_stdio.getDefaultEnvironment)(), ...launch.env },
15573
+ stderr: "ignore",
15574
+ ...launch.cwd ? { cwd: launch.cwd } : {}
15575
+ });
15576
+ }
15577
+ const url = new URL(launch.url);
15578
+ const requestInit = launch.headers ? { headers: launch.headers } : void 0;
15579
+ if (launch.kind === "sse") {
15580
+ return new import_sse.SSEClientTransport(url, requestInit ? { requestInit } : void 0);
15581
+ }
15582
+ return new import_streamableHttp2.StreamableHTTPClientTransport(url, requestInit ? { requestInit } : void 0);
15583
+ }
15584
+ var import_client, import_sse, import_stdio, import_streamableHttp2, import_types2, REQUEST_TIMEOUT_MS, UI_CAPABILITIES;
15585
+ var init_session = __esm({
15586
+ "src/mcp-ui/session.ts"() {
15587
+ "use strict";
15588
+ import_client = require("@modelcontextprotocol/sdk/client/index.js");
15589
+ import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
15590
+ import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
15591
+ import_streamableHttp2 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
15592
+ import_types2 = require("@modelcontextprotocol/sdk/types.js");
15593
+ init_meta();
15594
+ init_visibility();
15595
+ REQUEST_TIMEOUT_MS = 2e4;
15596
+ UI_CAPABILITIES = {
15597
+ extensions: {
15598
+ "io.modelcontextprotocol/ui": {
15599
+ mimeTypes: ["text/html;profile=mcp-app"]
15600
+ }
15601
+ }
15602
+ };
15603
+ }
15604
+ });
15605
+
15606
+ // src/mcp-ui/host.ts
15607
+ function getMcpUiHost() {
15608
+ if (!singleton) {
15609
+ singleton = new McpUiHost({
15610
+ entries: (projectDirs) => listUserClaudeMcpServerEntries((0, import_node_os16.homedir)(), projectDirs)
15611
+ });
15612
+ }
15613
+ return singleton;
15614
+ }
15615
+ async function proxyClaudeUserMcp(orchestrator, projectDirs) {
15616
+ if (orchestrator) return {};
15617
+ const dirs = projectDirs.filter((d) => typeof d === "string" && d.length > 0);
15618
+ return getMcpUiHost().proxyEntries(listUserClaudeMcpServerEntries((0, import_node_os16.homedir)(), dirs), dirs);
15619
+ }
15620
+ function closeMcpUiHost() {
15621
+ const host = singleton;
15622
+ singleton = void 0;
15623
+ if (host) void host.closeAll();
15624
+ }
15625
+ function dirsKeyFor(projectDirs) {
15626
+ if (!projectDirs?.length) return "";
15627
+ return (0, import_node_crypto12.createHash)("sha256").update(projectDirs.join("\0")).digest("hex").slice(0, 16);
15628
+ }
15629
+ var import_node_crypto12, import_node_os16, TOOL_TTL_MS, McpUiHost, singleton;
15630
+ var init_host = __esm({
15631
+ "src/mcp-ui/host.ts"() {
15632
+ "use strict";
15633
+ import_node_crypto12 = require("crypto");
15634
+ import_node_os16 = require("os");
15635
+ init_orch_mcp_isolation();
15636
+ init_meta();
15637
+ init_proxy_http();
15638
+ init_session();
15639
+ init_visibility();
15640
+ TOOL_TTL_MS = 5 * 60 * 1e3;
15641
+ McpUiHost = class {
15642
+ constructor(opts) {
15643
+ this.opts = opts;
15644
+ }
15645
+ opts;
15646
+ sessions = /* @__PURE__ */ new Map();
15647
+ toolLists = /* @__PURE__ */ new Map();
15648
+ proxy;
15649
+ proxyDirs = /* @__PURE__ */ new Map();
15650
+ async viewForClaudeTool(toolName, projectDirs) {
15651
+ const entries = this.userEntries(projectDirs);
15652
+ const match = matchClaudeMcpTool(toolName, Object.keys(entries));
15653
+ if (!match) return null;
15654
+ const launch = parseMcpServerLaunch(entries[match.server] ?? {});
15655
+ if (!launch) return null;
15656
+ try {
15657
+ const session = await this.sessionFor(match.server, launch);
15658
+ const tools = await this.toolsFor(match.server, launch, session);
15659
+ const tool = tools.find((item) => item.name === match.tool);
15660
+ if (!tool?.resourceUri) return null;
15661
+ const resource = await session.readResource(tool.resourceUri);
15662
+ if (!resource) return null;
15663
+ const html = htmlFromResourceContent(resource);
15664
+ if (!html) return null;
15665
+ const csp = resource.csp ?? tool.csp;
15666
+ return {
15667
+ server: match.server,
15668
+ tool: match.tool,
15669
+ uri: html.uri,
15670
+ mimeType: html.mimeType,
15671
+ html: html.html,
15672
+ csp: mcpUiContentSecurityPolicy(csp)
15673
+ };
15674
+ } catch (err) {
15675
+ console.error(
15676
+ `[mcp-ui] ${match.server}/${match.tool}:`,
15677
+ err instanceof Error ? err.message : err
15678
+ );
15679
+ return null;
15680
+ }
15681
+ }
15682
+ /**
15683
+ * Point Claude at loopback HTTP so it shares this host's upstream session
15684
+ * and never sees `visibility: ["app"]` tools.
15685
+ */
15686
+ async proxyEntries(entries, projectDirs) {
15687
+ const names = Object.keys(entries).filter((name) => name.trim());
15688
+ if (names.length === 0) return {};
15689
+ const dirsKey = dirsKeyFor(projectDirs);
15690
+ this.proxyDirs.set(dirsKey, projectDirs);
15691
+ try {
15692
+ const listen = await this.ensureProxy();
15693
+ const out = {};
15694
+ for (const name of names) {
15695
+ out[name] = {
15696
+ type: "http",
15697
+ url: listen.urlFor(name, dirsKey),
15698
+ headers: listen.headers
15699
+ };
15700
+ }
15701
+ return out;
15702
+ } catch (err) {
15703
+ console.error("[mcp-ui] proxy:", err instanceof Error ? err.message : err);
15704
+ return { ...entries };
15705
+ }
15706
+ }
15707
+ async callTool(server, tool, args, projectDirs) {
15708
+ const entry = this.userEntries(projectDirs)[server];
15709
+ if (!entry) throw new Error(`MCP server ${server} is not available`);
15710
+ const launch = parseMcpServerLaunch(entry);
15711
+ if (!launch) throw new Error(`MCP server ${server} is not available`);
15712
+ const session = await this.sessionFor(server, launch);
15713
+ const tools = await this.toolsFor(server, launch, session);
15714
+ const info = tools.find((item) => item.name === tool);
15715
+ if (!info || !toolVisibleTo(info.visibility, "app")) {
15716
+ throw new Error(`Tool ${tool} is not available to the app`);
15717
+ }
15718
+ return session.callTool(tool, args);
15719
+ }
15720
+ async readResource(server, uri, projectDirs) {
15721
+ const session = await this.sessionForName(server, projectDirs);
15722
+ if (!session) throw new Error(`MCP server ${server} is not available`);
15723
+ const resource = await session.readResource(uri);
15724
+ if (!resource) throw new Error(`Resource ${uri} was empty`);
15725
+ return {
15726
+ contents: [
15727
+ {
15728
+ uri: resource.uri,
15729
+ ...resource.mimeType ? { mimeType: resource.mimeType } : {},
15730
+ ...resource.text != null ? { text: resource.text } : {},
15731
+ ...resource.blob != null ? { blob: resource.blob } : {}
15732
+ }
15733
+ ]
15734
+ };
15735
+ }
15736
+ async closeAll() {
15737
+ const pending = [...this.sessions.values()];
15738
+ const proxy = this.proxy;
15739
+ this.sessions.clear();
15740
+ this.toolLists.clear();
15741
+ this.proxyDirs.clear();
15742
+ this.proxy = void 0;
15743
+ await Promise.all([
15744
+ ...pending.map((session) => session.then((open) => open.close()).catch(() => void 0)),
15745
+ proxy ? proxy.then((listen) => listen.close()).catch(() => void 0) : Promise.resolve()
15746
+ ]);
15747
+ }
15748
+ ensureProxy() {
15749
+ if (this.proxy) return this.proxy;
15750
+ this.proxy = listenMcpUiProxy({
15751
+ sessionFor: (server, dirsKey) => {
15752
+ if (dirsKey && !this.proxyDirs.has(dirsKey)) return Promise.resolve(null);
15753
+ return this.sessionForName(server, this.proxyDirs.get(dirsKey));
15754
+ }
15755
+ }).catch((err) => {
15756
+ this.proxy = void 0;
15757
+ throw err;
15758
+ });
15759
+ return this.proxy;
15760
+ }
15761
+ userEntries(projectDirs) {
15762
+ const out = {};
15763
+ for (const [name, entry] of Object.entries(this.opts.entries(projectDirs))) {
15764
+ if (!name.trim() || isInjectedOrchMcpName(name) || !entry) continue;
15765
+ out[name] = entry;
15766
+ }
15767
+ return out;
15768
+ }
15769
+ async sessionForName(server, projectDirs) {
15770
+ const entry = this.userEntries(projectDirs)[server];
15771
+ if (!entry) return null;
15772
+ const launch = parseMcpServerLaunch(entry);
15773
+ if (!launch) return null;
15774
+ return this.sessionFor(server, launch);
15775
+ }
15776
+ sessionFor(server, launch) {
15777
+ const key = `${server}
15778
+ ${JSON.stringify(launch)}`;
15779
+ const existing = this.sessions.get(key);
15780
+ if (existing) return existing;
15781
+ const open = this.opts.openSession ?? openMcpUiSession;
15782
+ const pending = open(launch).catch((err) => {
15783
+ this.sessions.delete(key);
15784
+ throw err;
15785
+ });
15786
+ this.sessions.set(key, pending);
15787
+ return pending;
15788
+ }
15789
+ async toolsFor(server, launch, session) {
15790
+ const key = `${server}
15791
+ ${JSON.stringify(launch)}`;
15792
+ const cached2 = this.toolLists.get(key);
15793
+ if (cached2 && cached2.key === key && Date.now() - cached2.at < TOOL_TTL_MS) return cached2.tools;
15794
+ const tools = await session.listTools();
15795
+ this.toolLists.set(key, { at: Date.now(), key, tools });
15796
+ return tools;
15797
+ }
15798
+ };
15799
+ }
15800
+ });
15801
+
15282
15802
  // src/agents/types.ts
15283
15803
  function planModeTurnInstruction(planMode) {
15284
15804
  return planMode ? PLAN_MODE_INSTRUCTION : PLAN_MODE_OFF_INSTRUCTION;
@@ -15474,7 +15994,7 @@ function entryDir() {
15474
15994
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
15475
15995
  if (cjsDir) return cjsDir;
15476
15996
  try {
15477
- return (0, import_node_path41.dirname)((0, import_node_url2.fileURLToPath)(import_meta3.url));
15997
+ return (0, import_node_path41.dirname)((0, import_node_url2.fileURLToPath)(import_meta5.url));
15478
15998
  } catch {
15479
15999
  try {
15480
16000
  const req = (0, import_node_module2.createRequire)(process.cwd() + "/");
@@ -15524,7 +16044,7 @@ async function buildClaudeRpcTurnCommand(req) {
15524
16044
  }
15525
16045
  };
15526
16046
  }
15527
- var import_node_fs38, import_node_module2, import_node_path41, import_node_url2, import_meta3;
16047
+ var import_node_fs38, import_node_module2, import_node_path41, import_node_url2, import_meta5;
15528
16048
  var init_claude_host = __esm({
15529
16049
  "src/agents/claude-host.ts"() {
15530
16050
  "use strict";
@@ -15536,7 +16056,7 @@ var init_claude_host = __esm({
15536
16056
  init_pilot_tools();
15537
16057
  init_node_launch();
15538
16058
  init_packaged_runtime();
15539
- import_meta3 = {};
16059
+ import_meta5 = {};
15540
16060
  }
15541
16061
  });
15542
16062
 
@@ -15939,16 +16459,15 @@ function parseIssuesJson(raw) {
15939
16459
  }
15940
16460
  return [];
15941
16461
  }
15942
- var import_node_fs39, import_node_os16, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
16462
+ var import_node_fs39, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
15943
16463
  var init_claude = __esm({
15944
16464
  "src/agents/claude.ts"() {
15945
16465
  "use strict";
15946
16466
  import_node_fs39 = require("fs");
15947
- import_node_os16 = require("os");
15948
16467
  init_run();
15949
16468
  init_app_settings();
15950
16469
  init_claude_mcp();
15951
- init_orch_mcp_isolation();
16470
+ init_host();
15952
16471
  init_error_detail();
15953
16472
  init_message_parts();
15954
16473
  init_injected_mcp();
@@ -16025,7 +16544,7 @@ var init_claude = __esm({
16025
16544
  });
16026
16545
  const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
16027
16546
  const chromeOn = claudeChromeEnabled();
16028
- const userMcpEntries = isOrchestrator ? {} : listUserClaudeMcpServerEntries((0, import_node_os16.homedir)(), [thread.repoPath, thread.worktreePath]);
16547
+ const userMcpEntries = await proxyClaudeUserMcp(isOrchestrator, [thread.repoPath, thread.worktreePath]);
16029
16548
  let allowedTools;
16030
16549
  if (isOrchestrator) {
16031
16550
  allowedTools = [
@@ -16327,7 +16846,7 @@ function unwrapCodexMcpResult(result) {
16327
16846
  return String(result);
16328
16847
  }
16329
16848
  }
16330
- function asRecord4(value) {
16849
+ function asRecord5(value) {
16331
16850
  if (value && typeof value === "object" && !Array.isArray(value)) {
16332
16851
  return value;
16333
16852
  }
@@ -16507,7 +17026,7 @@ var init_codex = __esm({
16507
17026
  }
16508
17027
  if (itemType === "mcp_tool_call" && item.id && item.server && item.tool) {
16509
17028
  const name = `mcp__${item.server}__${item.tool}`;
16510
- const input = asRecord4(item.arguments);
17029
+ const input = asRecord5(item.arguments);
16511
17030
  const events = [
16512
17031
  { type: "tool_use", id: item.id, name, input }
16513
17032
  ];
@@ -16654,7 +17173,7 @@ function thisModuleFile() {
16654
17173
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
16655
17174
  if (cjsDir) return (0, import_node_path43.join)(cjsDir, "cursor-sdk-resolve.js");
16656
17175
  try {
16657
- return (0, import_node_url3.fileURLToPath)(import_meta4.url);
17176
+ return (0, import_node_url3.fileURLToPath)(import_meta6.url);
16658
17177
  } catch {
16659
17178
  return (0, import_node_path43.join)(process.cwd(), "package.json");
16660
17179
  }
@@ -16753,7 +17272,7 @@ function cursorSdkRipgrepCandidate(opts) {
16753
17272
  const nested = (0, import_node_path43.join)(root, "node_modules", pkg, "bin", bin);
16754
17273
  return (0, import_node_fs41.existsSync)(nested) ? nested : null;
16755
17274
  }
16756
- var import_node_fs41, import_node_module3, import_node_path43, import_node_url3, import_meta4, CURSOR_SDK_INSTALL_HINT;
17275
+ var import_node_fs41, import_node_module3, import_node_path43, import_node_url3, import_meta6, CURSOR_SDK_INSTALL_HINT;
16757
17276
  var init_cursor_sdk_resolve = __esm({
16758
17277
  "src/agents/cursor-sdk-resolve.ts"() {
16759
17278
  "use strict";
@@ -16762,7 +17281,7 @@ var init_cursor_sdk_resolve = __esm({
16762
17281
  import_node_path43 = require("path");
16763
17282
  import_node_url3 = require("url");
16764
17283
  init_path();
16765
- import_meta4 = {};
17284
+ import_meta6 = {};
16766
17285
  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.";
16767
17286
  }
16768
17287
  });
@@ -16812,20 +17331,20 @@ function turnCostUsdFromCursorUsage(before, after) {
16812
17331
  if (!(delta >= 0) || !Number.isFinite(delta)) return void 0;
16813
17332
  return delta / 100;
16814
17333
  }
16815
- function asRecord5(value) {
17334
+ function asRecord6(value) {
16816
17335
  if (value && typeof value === "object" && !Array.isArray(value)) {
16817
17336
  return value;
16818
17337
  }
16819
17338
  return void 0;
16820
17339
  }
16821
17340
  function normalizeCursorToolCall(name, args) {
16822
- const raw = asRecord5(args);
17341
+ const raw = asRecord6(args);
16823
17342
  const toolName = typeof raw?.toolName === "string" && raw.toolName.trim() ? raw.toolName.trim() : null;
16824
- const looksLikeMcp = name === "mcp" || toolName != null && (typeof raw?.providerIdentifier === "string" || asRecord5(raw?.args) != null);
17343
+ const looksLikeMcp = name === "mcp" || toolName != null && (typeof raw?.providerIdentifier === "string" || asRecord6(raw?.args) != null);
16825
17344
  if (looksLikeMcp && toolName) {
16826
17345
  const providerRaw = typeof raw.providerIdentifier === "string" && raw.providerIdentifier.trim() ? raw.providerIdentifier.trim() : "sideboard";
16827
17346
  const provider = providerRaw.replace(/[^a-zA-Z0-9_-]/g, "_");
16828
- const nested = asRecord5(raw.args);
17347
+ const nested = asRecord6(raw.args);
16829
17348
  return {
16830
17349
  name: `mcp__${provider}__${toolName}`,
16831
17350
  input: nested ?? { toolName }
@@ -16836,7 +17355,7 @@ function normalizeCursorToolCall(name, args) {
16836
17355
  function unwrapCursorToolResult(result) {
16837
17356
  if (result == null) return void 0;
16838
17357
  if (typeof result === "string") return result;
16839
- const rec = asRecord5(result);
17358
+ const rec = asRecord6(result);
16840
17359
  if (!rec) {
16841
17360
  try {
16842
17361
  return JSON.stringify(result);
@@ -16847,18 +17366,18 @@ function unwrapCursorToolResult(result) {
16847
17366
  const collectTexts = (items) => {
16848
17367
  const texts = [];
16849
17368
  for (const item of items) {
16850
- const row2 = asRecord5(item);
17369
+ const row2 = asRecord6(item);
16851
17370
  if (!row2) continue;
16852
17371
  if (typeof row2.text === "string") {
16853
17372
  texts.push(row2.text);
16854
17373
  continue;
16855
17374
  }
16856
- const nested = asRecord5(row2.text);
17375
+ const nested = asRecord6(row2.text);
16857
17376
  if (typeof nested?.text === "string") texts.push(nested.text);
16858
17377
  }
16859
17378
  return texts;
16860
17379
  };
16861
- const value = asRecord5(rec.value);
17380
+ const value = asRecord6(rec.value);
16862
17381
  if (value && Array.isArray(value.content)) {
16863
17382
  const texts = collectTexts(value.content);
16864
17383
  if (texts.length) return texts.join("\n");
@@ -16875,10 +17394,10 @@ function unwrapCursorToolResult(result) {
16875
17394
  }
16876
17395
  function cursorToolResultIsError(status, result) {
16877
17396
  if (status === "error") return true;
16878
- const rec = asRecord5(result);
17397
+ const rec = asRecord6(result);
16879
17398
  if (!rec) return false;
16880
17399
  if (rec.status === "error") return true;
16881
- const value = asRecord5(rec.value);
17400
+ const value = asRecord6(rec.value);
16882
17401
  return value?.isError === true;
16883
17402
  }
16884
17403
  function cursorSdkMessageToEvents(msg) {
@@ -17097,7 +17616,7 @@ function entryDir2() {
17097
17616
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
17098
17617
  if (cjsDir) return cjsDir;
17099
17618
  try {
17100
- return (0, import_node_path45.dirname)((0, import_node_url4.fileURLToPath)(import_meta5.url));
17619
+ return (0, import_node_path45.dirname)((0, import_node_url4.fileURLToPath)(import_meta7.url));
17101
17620
  } catch {
17102
17621
  try {
17103
17622
  const req = (0, import_node_module5.createRequire)(process.cwd() + "/");
@@ -17126,7 +17645,7 @@ function cursorRunnerPath() {
17126
17645
  }
17127
17646
  return candidates[0];
17128
17647
  }
17129
- var import_node_fs43, import_node_module5, import_node_path45, import_node_url4, import_meta5, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
17648
+ var import_node_fs43, import_node_module5, import_node_path45, import_node_url4, import_meta7, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
17130
17649
  var init_cursor = __esm({
17131
17650
  "src/agents/cursor.ts"() {
17132
17651
  "use strict";
@@ -17146,7 +17665,7 @@ var init_cursor = __esm({
17146
17665
  init_packaged_runtime();
17147
17666
  init_turn_input();
17148
17667
  init_cursor_events();
17149
- import_meta5 = {};
17668
+ import_meta7 = {};
17150
17669
  FALLBACK_CURSOR_MODELS = [
17151
17670
  { id: "default", displayName: "Auto" },
17152
17671
  { id: "composer-2.5", displayName: "Composer 2.5" },
@@ -17420,7 +17939,7 @@ var init_opencode_host = __esm({
17420
17939
  });
17421
17940
 
17422
17941
  // src/agents/opencode.ts
17423
- function asRecord6(value) {
17942
+ function asRecord7(value) {
17424
17943
  if (value && typeof value === "object" && !Array.isArray(value)) {
17425
17944
  return value;
17426
17945
  }
@@ -17616,7 +18135,7 @@ var init_opencode = __esm({
17616
18135
  const name = part?.tool ?? part?.name ?? obj.tool ?? obj.name ?? "tool";
17617
18136
  const state = part?.state;
17618
18137
  const input = (state?.input && typeof state.input === "object" ? state.input : void 0) ?? part?.input ?? obj.input;
17619
- const metadata = asRecord6(state?.metadata);
18138
+ const metadata = asRecord7(state?.metadata);
17620
18139
  const id = opencodeToolId(
17621
18140
  name,
17622
18141
  part?.callID ?? part?.id ?? obj.id,
@@ -17651,7 +18170,7 @@ var init_opencode = __esm({
17651
18170
  }
17652
18171
  if (obj.type === "subtask_event") {
17653
18172
  const parentId = str4(obj.childSessionID) ?? str4(obj.childSessionId);
17654
- const part = asRecord6(obj.part);
18173
+ const part = asRecord7(obj.part);
17655
18174
  if (!parentId || !part) return null;
17656
18175
  if (part.type === "text" && str4(part.text)) {
17657
18176
  return withTaskParent(
@@ -17662,8 +18181,8 @@ var init_opencode = __esm({
17662
18181
  if (part.type === "tool" || str4(part.tool)) {
17663
18182
  const name = str4(part.tool) ?? str4(part.name) ?? "tool";
17664
18183
  const id = str4(part.callID) ?? str4(part.id) ?? `${parentId}-${name}`;
17665
- const state = asRecord6(part.state);
17666
- const input = asRecord6(state?.input) ?? asRecord6(part.input);
18184
+ const state = asRecord7(part.state);
18185
+ const input = asRecord7(state?.input) ?? asRecord7(part.input);
17667
18186
  const nested = [
17668
18187
  withEventParentId({ type: "tool_use", id, name, input }, parentId)
17669
18188
  ];
@@ -20081,7 +20600,7 @@ function forkMessageSlice(from, throughIndex) {
20081
20600
  function buildForkTranscriptAttachment(baseTitle, messages) {
20082
20601
  const title = baseTitle || "Chat";
20083
20602
  return {
20084
- id: (0, import_node_crypto11.randomUUID)(),
20603
+ id: (0, import_node_crypto13.randomUUID)(),
20085
20604
  name: `Transcript of ${title}.md`,
20086
20605
  kind: "transcript",
20087
20606
  content: formatTranscriptMarkdown(title, messages)
@@ -20140,11 +20659,11 @@ function forkChatTab(input) {
20140
20659
  }
20141
20660
  return tab;
20142
20661
  }
20143
- var import_node_crypto11;
20662
+ var import_node_crypto13;
20144
20663
  var init_chat_tabs = __esm({
20145
20664
  "src/threads/chat-tabs.ts"() {
20146
20665
  "use strict";
20147
- import_node_crypto11 = require("crypto");
20666
+ import_node_crypto13 = require("crypto");
20148
20667
  init_context_compact();
20149
20668
  init_teams();
20150
20669
  init_worktree_labels();
@@ -20443,7 +20962,7 @@ function buildReviewRequestAttachment(content, opts) {
20443
20962
  const path2 = opts?.path ?? REVIEW_SKILL_PATH;
20444
20963
  const name = opts?.name ?? (path2 === REVIEW_SKILL_PATH ? REVIEW_SKILL_NAME : path2 === REPO_REVIEW_PATH || path2 === CONTEXT_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
20445
20964
  return {
20446
- id: (0, import_node_crypto12.randomUUID)(),
20965
+ id: (0, import_node_crypto14.randomUUID)(),
20447
20966
  name,
20448
20967
  kind: "file",
20449
20968
  path: path2,
@@ -20499,11 +21018,11 @@ async function requestReview(threadRef, send3) {
20499
21018
  const started = await send3(tab.id, REVIEW_REQUEST_PREFILL);
20500
21019
  return { tab: started, from };
20501
21020
  }
20502
- var import_node_crypto12, import_node_fs49, import_node_path50, 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;
21021
+ var import_node_crypto14, import_node_fs49, import_node_path50, 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;
20503
21022
  var init_request_review = __esm({
20504
21023
  "src/review/request-review.ts"() {
20505
21024
  "use strict";
20506
- import_node_crypto12 = require("crypto");
21025
+ import_node_crypto14 = require("crypto");
20507
21026
  import_node_fs49 = require("fs");
20508
21027
  import_node_path50 = require("path");
20509
21028
  init_global_workspace();
@@ -22052,8 +22571,28 @@ var init_clone_repo = __esm({
22052
22571
  }
22053
22572
  });
22054
22573
 
22574
+ // src/mcp-ui/model-context.ts
22575
+ function stashMcpAppModelContext(threadId, text7) {
22576
+ const body = text7.trim().slice(0, 8e3);
22577
+ if (!body) return;
22578
+ updateThread(threadId, { mcpAppModelContext: body });
22579
+ }
22580
+ function takeMcpAppModelContext(threadId) {
22581
+ const thread = readThread(threadId);
22582
+ const text7 = thread?.mcpAppModelContext?.trim();
22583
+ if (!text7) return void 0;
22584
+ updateThread(threadId, { mcpAppModelContext: null });
22585
+ return text7;
22586
+ }
22587
+ var init_model_context = __esm({
22588
+ "src/mcp-ui/model-context.ts"() {
22589
+ "use strict";
22590
+ init_thread_store();
22591
+ }
22592
+ });
22593
+
22055
22594
  // src/plan/plan-present.ts
22056
- function asRecord7(v) {
22595
+ function asRecord8(v) {
22057
22596
  return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
22058
22597
  }
22059
22598
  function isPresentPlanToolName(name) {
@@ -22072,7 +22611,7 @@ function extractPresentedPlan(parts) {
22072
22611
  for (let i = parts.length - 1; i >= 0; i--) {
22073
22612
  const p = parts[i];
22074
22613
  if (p.type !== "tool" || !isPresentPlanToolName(p.name)) continue;
22075
- const input = asRecord7(p.input) ?? {};
22614
+ const input = asRecord8(p.input) ?? {};
22076
22615
  const content = typeof input.content === "string" ? input.content : typeof input.plan === "string" ? input.plan : typeof input.markdown === "string" ? input.markdown : "";
22077
22616
  if (!content.trim()) continue;
22078
22617
  const title = typeof input.title === "string" && input.title.trim() ? input.title.trim() : "Plan";
@@ -22527,7 +23066,7 @@ function isUnclaimedSteerRequest(req) {
22527
23066
  }
22528
23067
  function nextSteerRequest(existing) {
22529
23068
  if (existing?.requestId && !existing.claimedAt) return existing;
22530
- return { requestId: (0, import_node_crypto13.randomUUID)(), requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
23069
+ return { requestId: (0, import_node_crypto15.randomUUID)(), requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
22531
23070
  }
22532
23071
  function claimSteerRequest(threadId) {
22533
23072
  const pending = readThread(threadId)?.steerRequest;
@@ -22536,11 +23075,11 @@ function claimSteerRequest(threadId) {
22536
23075
  steerRequest: { ...pending, claimedAt: (/* @__PURE__ */ new Date()).toISOString() }
22537
23076
  });
22538
23077
  }
22539
- var import_node_crypto13;
23078
+ var import_node_crypto15;
22540
23079
  var init_steer_request = __esm({
22541
23080
  "src/orchestrator/steer-request.ts"() {
22542
23081
  "use strict";
22543
- import_node_crypto13 = require("crypto");
23082
+ import_node_crypto15 = require("crypto");
22544
23083
  init_thread_store();
22545
23084
  }
22546
23085
  });
@@ -22785,8 +23324,8 @@ function resolveAbleTimeOAuthHost(requested, saved) {
22785
23324
  return fromSaved || void 0;
22786
23325
  }
22787
23326
  function createAbleTimePkce() {
22788
- const verifier = (0, import_node_crypto14.randomBytes)(32).toString("base64url");
22789
- const challenge = (0, import_node_crypto14.createHash)("sha256").update(verifier).digest("base64url");
23327
+ const verifier = (0, import_node_crypto16.randomBytes)(32).toString("base64url");
23328
+ const challenge = (0, import_node_crypto16.createHash)("sha256").update(verifier).digest("base64url");
22790
23329
  return { verifier, challenge };
22791
23330
  }
22792
23331
  function ableTimeOAuthAuthorizeUrl(clientId, state, codeChallenge, host) {
@@ -22891,7 +23430,7 @@ async function startAbleTimeOAuth(opts) {
22891
23430
  opts?.host,
22892
23431
  loadAppSettings().integrations.abletimeHost
22893
23432
  );
22894
- const state = (0, import_node_crypto14.randomBytes)(16).toString("hex");
23433
+ const state = (0, import_node_crypto16.randomBytes)(16).toString("hex");
22895
23434
  const pkce = createAbleTimePkce();
22896
23435
  const authorizeUrl = ableTimeOAuthAuthorizeUrl(clientId, state, pkce.challenge, host);
22897
23436
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
@@ -22904,7 +23443,7 @@ async function startAbleTimeOAuth(opts) {
22904
23443
  if (err) reject(err);
22905
23444
  else resolve3(value);
22906
23445
  };
22907
- const server = (0, import_node_http.createServer)((req, res) => {
23446
+ const server = (0, import_node_http2.createServer)((req, res) => {
22908
23447
  try {
22909
23448
  const url = new URL(req.url || "/", `http://127.0.0.1:${ABLETIME_OAUTH_PORT}`);
22910
23449
  if (url.pathname !== "/callback") {
@@ -22991,12 +23530,12 @@ async function startAbleTimeOAuth(opts) {
22991
23530
  return loadAppSettings();
22992
23531
  }
22993
23532
  }
22994
- var import_node_crypto14, 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;
23533
+ var import_node_crypto16, 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;
22995
23534
  var init_abletime_oauth = __esm({
22996
23535
  "src/integrations/abletime-oauth.ts"() {
22997
23536
  "use strict";
22998
- import_node_crypto14 = require("crypto");
22999
- import_node_http = require("http");
23537
+ import_node_crypto16 = require("crypto");
23538
+ import_node_http2 = require("http");
23000
23539
  init_fetch();
23001
23540
  init_app_settings();
23002
23541
  init_abletime_mcp();
@@ -23029,7 +23568,7 @@ __export(abletime_rest_exports, {
23029
23568
  looksLikeAbleTimeUlid: () => looksLikeAbleTimeUlid,
23030
23569
  textFromAbleTimeDoc: () => textFromAbleTimeDoc
23031
23570
  });
23032
- function asRecord8(value) {
23571
+ function asRecord9(value) {
23033
23572
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
23034
23573
  }
23035
23574
  function asString(value) {
@@ -23037,7 +23576,7 @@ function asString(value) {
23037
23576
  }
23038
23577
  function asList(value) {
23039
23578
  if (Array.isArray(value)) return value;
23040
- const record = asRecord8(value);
23579
+ const record = asRecord9(value);
23041
23580
  if (!record) return [];
23042
23581
  for (const key of ["data", "tasks", "projects", "users", "items", "results"]) {
23043
23582
  if (Array.isArray(record[key])) return record[key];
@@ -23054,12 +23593,12 @@ function ableTimeDocFromText(text7) {
23054
23593
  }
23055
23594
  function textFromAbleTimeDoc(value) {
23056
23595
  if (typeof value === "string") return value.trim();
23057
- const record = asRecord8(value);
23596
+ const record = asRecord9(value);
23058
23597
  if (!record) return "";
23059
23598
  if (typeof record.text === "string" && record.text.trim()) return record.text.trim();
23060
23599
  const chunks = [];
23061
23600
  const walk = (node) => {
23062
- const rec = asRecord8(node);
23601
+ const rec = asRecord9(node);
23063
23602
  if (!rec) return;
23064
23603
  if (typeof rec.text === "string" && rec.text) chunks.push(rec.text);
23065
23604
  if (Array.isArray(rec.content)) rec.content.forEach(walk);
@@ -23121,7 +23660,7 @@ async function restPages(path2, opts, maxPages = 20) {
23121
23660
  query: { ...opts.query, ...cursor ? { cursor } : {} }
23122
23661
  });
23123
23662
  items.push(...asList(raw));
23124
- const page = asRecord8(asRecord8(raw)?.page);
23663
+ const page = asRecord9(asRecord9(raw)?.page);
23125
23664
  const next = page ? asString(page.nextCursor) : "";
23126
23665
  if (!next) break;
23127
23666
  cursor = next;
@@ -23129,7 +23668,7 @@ async function restPages(path2, opts, maxPages = 20) {
23129
23668
  return items;
23130
23669
  }
23131
23670
  function userDisplayName(raw) {
23132
- const rec = asRecord8(raw);
23671
+ const rec = asRecord9(raw);
23133
23672
  if (!rec) return null;
23134
23673
  const id = asString(rec.userId) || asString(rec.id);
23135
23674
  const name = [asString(rec.firstName), asString(rec.lastName)].filter(Boolean).join(" ") || asString(rec.username) || asString(rec.email) || asString(rec.name);
@@ -23137,7 +23676,7 @@ function userDisplayName(raw) {
23137
23676
  return { id: id || void 0, name };
23138
23677
  }
23139
23678
  function restTaskToRaw(raw, users) {
23140
- const rec = asRecord8(raw);
23679
+ const rec = asRecord9(raw);
23141
23680
  if (!rec) return null;
23142
23681
  const id = asString(rec.timeflowTaskId) || asString(rec.taskId) || asString(rec.id);
23143
23682
  const identifier = asString(rec.taskRef) || asString(rec.reference) || id;
@@ -23145,7 +23684,7 @@ function restTaskToRaw(raw, users) {
23145
23684
  if (!id && !identifier && !title) return null;
23146
23685
  const assignee = users.get(asString(rec.assignedUserId));
23147
23686
  const comments = asList(rec.comments).map((item) => {
23148
- const comment = asRecord8(item) ?? {};
23687
+ const comment = asRecord9(item) ?? {};
23149
23688
  const user = userDisplayName(comment);
23150
23689
  return {
23151
23690
  id: asString(comment.commentId) || asString(comment.id),
@@ -23175,13 +23714,13 @@ function restTaskToRaw(raw, users) {
23175
23714
  };
23176
23715
  }
23177
23716
  function restProjectToRaw(raw) {
23178
- const rec = asRecord8(raw);
23717
+ const rec = asRecord9(raw);
23179
23718
  if (!rec) return null;
23180
23719
  const id = asString(rec.projectId) || asString(rec.id);
23181
23720
  const name = asString(rec.projectName) || asString(rec.name);
23182
23721
  if (!id && !name) return null;
23183
23722
  const categories = asList(rec.categories).map((item) => {
23184
- const cat = asRecord8(item) ?? {};
23723
+ const cat = asRecord9(item) ?? {};
23185
23724
  return {
23186
23725
  id: asString(cat.projectCategoryId) || asString(cat.categoryId) || asString(cat.id),
23187
23726
  name: asString(cat.categoryName) || asString(cat.name)
@@ -23193,7 +23732,7 @@ async function listUsers(opts) {
23193
23732
  const users = /* @__PURE__ */ new Map();
23194
23733
  for (const item of await restPages("/users", opts).catch(() => [])) {
23195
23734
  const user = userDisplayName(item);
23196
- const rec = asRecord8(item);
23735
+ const rec = asRecord9(item);
23197
23736
  const id = user?.id || (rec ? asString(rec.userId) : "");
23198
23737
  if (user && id) users.set(id, user);
23199
23738
  }
@@ -23319,10 +23858,10 @@ async function callAbleTimeRestTool(name, args = {}, opts) {
23319
23858
  if (!project) throw new Error("AbleTime has no projects to create a task in");
23320
23859
  const categories = asList(project.categories);
23321
23860
  const wantedCategory = firstStringArg(args, ["category_id", "categoryId", "category"]);
23322
- const category = categories.map((item) => asRecord8(item)).find((item) => {
23861
+ const category = categories.map((item) => asRecord9(item)).find((item) => {
23323
23862
  if (!item || !wantedCategory) return false;
23324
23863
  return asString(item.id).toLowerCase() === wantedCategory.toLowerCase() || asString(item.name).toLowerCase() === wantedCategory.toLowerCase();
23325
- }) ?? asRecord8(categories[0]);
23864
+ }) ?? asRecord9(categories[0]);
23326
23865
  const parentArg = firstStringArg(args, ["parent", "parent_id", "parentId", "related_task_id"]);
23327
23866
  const parentTaskId = parentArg ? await resolveRestParentTaskId(parentArg, requestOpts) : void 0;
23328
23867
  const created = await abletimeRestRequest("/tasks", {
@@ -23336,7 +23875,7 @@ async function callAbleTimeRestTool(name, args = {}, opts) {
23336
23875
  }
23337
23876
  });
23338
23877
  const state = firstStringArg(args, ["state"]);
23339
- const createdId = asString(asRecord8(created)?.timeflowTaskId);
23878
+ const createdId = asString(asRecord9(created)?.timeflowTaskId);
23340
23879
  const description = asString(args.description);
23341
23880
  if (createdId && description) {
23342
23881
  await abletimeRestRequest(`/tasks/${encodeURIComponent(createdId)}`, {
@@ -23409,7 +23948,7 @@ async function callAbleTimeRestTool(name, args = {}, opts) {
23409
23948
  method: "POST",
23410
23949
  body: { content: ableTimeDocFromText(body) }
23411
23950
  });
23412
- const rec = asRecord8(created) ?? {};
23951
+ const rec = asRecord9(created) ?? {};
23413
23952
  return {
23414
23953
  id: asString(rec.commentId) || void 0,
23415
23954
  body: textFromAbleTimeDoc(rec.content) || body
@@ -23504,7 +24043,7 @@ function rewriteAbleTimeError(message) {
23504
24043
  }
23505
24044
  return message;
23506
24045
  }
23507
- function asRecord9(value) {
24046
+ function asRecord10(value) {
23508
24047
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
23509
24048
  }
23510
24049
  function parseSseJson(body) {
@@ -23526,12 +24065,12 @@ function parseSseJson(body) {
23526
24065
  }
23527
24066
  }
23528
24067
  function unwrapToolResult(result) {
23529
- const record = asRecord9(result);
24068
+ const record = asRecord10(result);
23530
24069
  if (!record) return result;
23531
24070
  const content = record.content;
23532
24071
  if (Array.isArray(content)) {
23533
24072
  const texts = content.map((part) => {
23534
- const item = asRecord9(part);
24073
+ const item = asRecord10(part);
23535
24074
  if (!item) return "";
23536
24075
  return typeof item.text === "string" ? item.text : "";
23537
24076
  }).filter(Boolean);
@@ -23553,7 +24092,7 @@ function unwrapToolResult(result) {
23553
24092
  return result;
23554
24093
  }
23555
24094
  function textsFromUnknown(value) {
23556
- const record = asRecord9(value);
24095
+ const record = asRecord10(value);
23557
24096
  if (!record) return "";
23558
24097
  if (typeof record.message === "string") return record.message;
23559
24098
  if (typeof record.error === "string") return record.error;
@@ -23600,7 +24139,7 @@ async function abletimeMcpRequest(method, params, opts) {
23600
24139
  return parseSseJson(body);
23601
24140
  }
23602
24141
  })();
23603
- const rpc = asRecord9(parsed);
24142
+ const rpc = asRecord10(parsed);
23604
24143
  if (rpc?.error) {
23605
24144
  const detail = typeof rpc.error.message === "string" && rpc.error.message.trim() ? rpc.error.message : `AbleTime MCP error ${rpc.error.code ?? ""}`.trim();
23606
24145
  throw new Error(rewriteAbleTimeError(detail));
@@ -23755,7 +24294,7 @@ function uniqueAttachmentFileName(dir, originalName) {
23755
24294
  const candidate = `${stem}-${i}${ext}`;
23756
24295
  if (!(0, import_node_fs55.existsSync)((0, import_node_path56.join)(dir, candidate))) return candidate;
23757
24296
  }
23758
- return `${stem}-${(0, import_node_crypto15.randomUUID)()}${ext}`;
24297
+ return `${stem}-${(0, import_node_crypto17.randomUUID)()}${ext}`;
23759
24298
  }
23760
24299
  function extractHttpUrls(markdown) {
23761
24300
  const text7 = markdown ?? "";
@@ -23861,13 +24400,13 @@ async function downloadAuthenticatedFile(opts) {
23861
24400
  ...contentType ? { contentType } : {}
23862
24401
  };
23863
24402
  }
23864
- var import_node_fs55, import_node_path56, import_node_crypto15, ISSUE_ATTACHMENT_MAX_BYTES, MARKDOWN_IMAGE_RE, MARKDOWN_LINK_RE, HTML_SRC_RE, BARE_URL_RE, EXT_BY_MIME;
24403
+ var import_node_fs55, import_node_path56, import_node_crypto17, ISSUE_ATTACHMENT_MAX_BYTES, MARKDOWN_IMAGE_RE, MARKDOWN_LINK_RE, HTML_SRC_RE, BARE_URL_RE, EXT_BY_MIME;
23865
24404
  var init_issue_attachments = __esm({
23866
24405
  "src/integrations/issue-attachments.ts"() {
23867
24406
  "use strict";
23868
24407
  import_node_fs55 = require("fs");
23869
24408
  import_node_path56 = require("path");
23870
- import_node_crypto15 = require("crypto");
24409
+ import_node_crypto17 = require("crypto");
23871
24410
  init_fetch();
23872
24411
  init_workspace_scratch();
23873
24412
  ISSUE_ATTACHMENT_MAX_BYTES = 25 * 1024 * 1024;
@@ -23892,7 +24431,7 @@ var init_issue_attachments = __esm({
23892
24431
  });
23893
24432
 
23894
24433
  // src/integrations/abletime-attachments.ts
23895
- function asRecord10(value) {
24434
+ function asRecord11(value) {
23896
24435
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
23897
24436
  }
23898
24437
  function asString2(value) {
@@ -23900,7 +24439,7 @@ function asString2(value) {
23900
24439
  }
23901
24440
  function asList2(value) {
23902
24441
  if (Array.isArray(value)) return value;
23903
- const record = asRecord10(value);
24442
+ const record = asRecord11(value);
23904
24443
  if (!record) return [];
23905
24444
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
23906
24445
  if (Array.isArray(record[key])) return record[key];
@@ -23919,9 +24458,9 @@ function filesFromAbleTimeDoc(value) {
23919
24458
  const out = [];
23920
24459
  const seen = /* @__PURE__ */ new Set();
23921
24460
  const walk = (node) => {
23922
- const rec = asRecord10(node);
24461
+ const rec = asRecord11(node);
23923
24462
  if (!rec) return;
23924
- const attrs = asRecord10(rec.attrs) ?? rec;
24463
+ const attrs = asRecord11(rec.attrs) ?? rec;
23925
24464
  const url = asString2(attrs.src) || asString2(attrs.href) || asString2(attrs.url) || asString2(attrs.fileUrl) || asString2(attrs.downloadUrl);
23926
24465
  if (url && !seen.has(url)) {
23927
24466
  seen.add(url);
@@ -23939,7 +24478,7 @@ function filesFromAbleTimeDoc(value) {
23939
24478
  function listedAbleTimeFiles(raw) {
23940
24479
  const out = [];
23941
24480
  for (const item of asList2(raw)) {
23942
- const rec = asRecord10(item);
24481
+ const rec = asRecord11(item);
23943
24482
  if (!rec) continue;
23944
24483
  const url = firstString(rec, ["url", "href", "src", "fileUrl", "downloadUrl", "permalink"]);
23945
24484
  if (!url) continue;
@@ -23959,7 +24498,7 @@ function mapAbleTimeAttachments(record) {
23959
24498
  ...filesFromAbleTimeDoc(record.description),
23960
24499
  ...filesFromAbleTimeDoc(record.content),
23961
24500
  ...asList2(record.comments).flatMap((item) => {
23962
- const rec = asRecord10(item);
24501
+ const rec = asRecord11(item);
23963
24502
  return rec ? filesFromAbleTimeDoc(rec.content ?? rec.body) : [];
23964
24503
  })
23965
24504
  ].map((file) => ({
@@ -24001,7 +24540,7 @@ async function resolveAbleTimeAttachmentUrl(id, opts) {
24001
24540
  const json = await res.json().catch(() => null);
24002
24541
  const rec = json && typeof json === "object" && !Array.isArray(json) ? json.data && typeof json.data === "object" ? json.data : json : null;
24003
24542
  if (!rec) continue;
24004
- const url = firstString(rec, ["url", "href", "src", "fileUrl", "downloadUrl"]) || firstString(asRecord10(rec.file), ["url", "href"]);
24543
+ const url = firstString(rec, ["url", "href", "src", "fileUrl", "downloadUrl"]) || firstString(asRecord11(rec.file), ["url", "href"]);
24005
24544
  if (url) {
24006
24545
  return {
24007
24546
  url,
@@ -24043,7 +24582,7 @@ var init_abletime_attachments = __esm({
24043
24582
  });
24044
24583
 
24045
24584
  // src/integrations/abletime-projects.ts
24046
- function asRecord11(value) {
24585
+ function asRecord12(value) {
24047
24586
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
24048
24587
  }
24049
24588
  function firstString2(record, keys) {
@@ -24056,7 +24595,7 @@ function firstString2(record, keys) {
24056
24595
  }
24057
24596
  function asList3(value) {
24058
24597
  if (Array.isArray(value)) return value;
24059
- const record = asRecord11(value);
24598
+ const record = asRecord12(value);
24060
24599
  if (!record) return [];
24061
24600
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
24062
24601
  if (Array.isArray(record[key])) return record[key];
@@ -24064,7 +24603,7 @@ function asList3(value) {
24064
24603
  return [];
24065
24604
  }
24066
24605
  function ableTimeProjectFields(nested) {
24067
- const projectRecord = asRecord11(nested.project);
24606
+ const projectRecord = asRecord12(nested.project);
24068
24607
  const projectId = firstString2(nested, ["project_id", "projectId"]) || firstString2(projectRecord, ["id"]) || void 0;
24069
24608
  const projectName = firstString2(nested, ["project_name", "projectName"]) || firstString2(projectRecord, ["name", "title"]) || void 0;
24070
24609
  if (!projectId && !projectName) return {};
@@ -24077,13 +24616,13 @@ function ableTimeProjectFields(nested) {
24077
24616
  };
24078
24617
  }
24079
24618
  function mapAbleTimeProject(raw) {
24080
- const record = asRecord11(raw);
24619
+ const record = asRecord12(raw);
24081
24620
  if (!record) return null;
24082
24621
  const id = firstString2(record, ["id", "project_id", "projectId"]);
24083
24622
  const name = firstString2(record, ["name", "title", "projectName"]);
24084
24623
  if (!id && !name) return null;
24085
24624
  const categories = asList3(record.categories ?? record.category).map((item) => {
24086
- const rec = asRecord11(item);
24625
+ const rec = asRecord12(item);
24087
24626
  if (!rec) return null;
24088
24627
  const categoryId = firstString2(rec, ["id", "category_id", "projectCategoryId", "categoryId"]);
24089
24628
  const categoryName = firstString2(rec, ["name", "title", "categoryName"]);
@@ -24119,7 +24658,7 @@ __export(abletime_exports, {
24119
24658
  updateAbleTimeTask: () => updateAbleTimeTask,
24120
24659
  verifyAbleTimeConnection: () => verifyAbleTimeConnection
24121
24660
  });
24122
- function asRecord12(value) {
24661
+ function asRecord13(value) {
24123
24662
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
24124
24663
  }
24125
24664
  function asString3(value) {
@@ -24136,14 +24675,14 @@ function firstString3(record, keys) {
24136
24675
  function firstRecord(record, keys) {
24137
24676
  if (!record) return null;
24138
24677
  for (const key of keys) {
24139
- const value = asRecord12(record[key]);
24678
+ const value = asRecord13(record[key]);
24140
24679
  if (value) return value;
24141
24680
  }
24142
24681
  return null;
24143
24682
  }
24144
24683
  function asList4(value) {
24145
24684
  if (Array.isArray(value)) return value;
24146
- const record = asRecord12(value);
24685
+ const record = asRecord13(value);
24147
24686
  if (!record) return [];
24148
24687
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
24149
24688
  if (Array.isArray(record[key])) return record[key];
@@ -24155,18 +24694,18 @@ function labelsOf(record) {
24155
24694
  if (!Array.isArray(raw)) return [];
24156
24695
  return raw.map((item) => {
24157
24696
  if (typeof item === "string") return item.trim();
24158
- const rec = asRecord12(item);
24697
+ const rec = asRecord13(item);
24159
24698
  return rec ? firstString3(rec, ["name", "title", "label"]) : "";
24160
24699
  }).filter(Boolean);
24161
24700
  }
24162
24701
  function commentsOf(record) {
24163
24702
  const out = [];
24164
24703
  for (const item of asList4(record.comments ?? record.notes ?? record.discussion)) {
24165
- const rec = asRecord12(item);
24704
+ const rec = asRecord13(item);
24166
24705
  if (!rec) continue;
24167
24706
  const body = firstString3(rec, ["body", "text", "comment", "message", "contentSanitized"]) || textFromAbleTimeDoc(rec.content);
24168
24707
  if (!body) continue;
24169
- const user = firstString3(asRecord12(rec.user) ?? asRecord12(rec.author), ["name", "display_name", "username"]) || firstString3(rec, ["user_name", "author", "username"]);
24708
+ const user = firstString3(asRecord13(rec.user) ?? asRecord13(rec.author), ["name", "display_name", "username"]) || firstString3(rec, ["user_name", "author", "username"]);
24170
24709
  const id = firstString3(rec, ["id", "comment_id", "commentId"]);
24171
24710
  const url = firstString3(rec, ["url", "permalink"]);
24172
24711
  const createdAt = firstString3(rec, ["created_at", "createdAt", "created"]);
@@ -24193,7 +24732,7 @@ function taskUrl(task, host) {
24193
24732
  return `${base}/tasks/${slug}`;
24194
24733
  }
24195
24734
  function mapAbleTimeTask(raw, host) {
24196
- const record = asRecord12(raw);
24735
+ const record = asRecord13(raw);
24197
24736
  if (!record) return null;
24198
24737
  const nested = firstRecord(record, ["task", "data"]) ?? record;
24199
24738
  const id = firstString3(nested, ["id", "task_id", "taskId", "timeflowTaskId"]);
@@ -24211,7 +24750,7 @@ function mapAbleTimeTask(raw, host) {
24211
24750
  description: firstString3(nested, ["description", "body", "details", "descriptionSanitized"]) || textFromAbleTimeDoc(nested.description) || void 0,
24212
24751
  state: firstString3(nested, ["state", "board_state", "boardState", "status", "taskState"]) || void 0,
24213
24752
  ...ableTimeProjectFields(nested),
24214
- categoryId: firstString3(nested, ["category_id", "categoryId", "projectCategoryId"]) || firstString3(asRecord12(nested.category), ["id"]) || void 0,
24753
+ categoryId: firstString3(nested, ["category_id", "categoryId", "projectCategoryId"]) || firstString3(asRecord13(nested.category), ["id"]) || void 0,
24215
24754
  assignee: assigneeOf(nested),
24216
24755
  labels: labelsOf(nested),
24217
24756
  comments: commentsOf(nested),
@@ -24261,7 +24800,7 @@ function isOpenTask(task) {
24261
24800
  }
24262
24801
  async function getAbleTimeOrientation(opts) {
24263
24802
  const raw = await callAbleTimeTool("orientation", {}, opts);
24264
- const record = asRecord12(raw);
24803
+ const record = asRecord13(raw);
24265
24804
  const viewerRec = firstRecord(record, ["viewer", "principal", "user", "actor", "me", "person"]) ?? record;
24266
24805
  const viewer = {
24267
24806
  id: firstString3(viewerRec, ["id", "user_id"]) || void 0,
@@ -24340,7 +24879,7 @@ async function commentAbleTimeTask(input, opts) {
24340
24879
  { id, task_id: id, task: id, body, comment: body, text: body },
24341
24880
  opts
24342
24881
  );
24343
- const rec = asRecord12(raw);
24882
+ const rec = asRecord13(raw);
24344
24883
  return {
24345
24884
  id: rec ? firstString3(rec, ["id", "comment_id"]) || void 0 : void 0,
24346
24885
  body: rec ? firstString3(rec, ["body", "text", "comment"]) || body : body
@@ -25493,7 +26032,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
25493
26032
  ...instructions
25494
26033
  ].join("\n");
25495
26034
  return {
25496
- id: (0, import_node_crypto16.randomUUID)(),
26035
+ id: (0, import_node_crypto18.randomUUID)(),
25497
26036
  name: orch ? "Orchestration quota handoff.md" : "Quota handoff.md",
25498
26037
  kind: "transcript",
25499
26038
  content: body
@@ -25525,11 +26064,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText, opts) {
25525
26064
  }
25526
26065
  return updateThread(tab.id, patch);
25527
26066
  }
25528
- var import_node_crypto16, QUOTA_CONTINUE_PROMPT, QUOTA_CONTINUE_ACCOUNT_PROMPT, QUOTA_RESUME_PROMPT;
26067
+ var import_node_crypto18, QUOTA_CONTINUE_PROMPT, QUOTA_CONTINUE_ACCOUNT_PROMPT, QUOTA_RESUME_PROMPT;
25529
26068
  var init_quota_failover = __esm({
25530
26069
  "src/orchestrator/quota-failover.ts"() {
25531
26070
  "use strict";
25532
- import_node_crypto16 = require("crypto");
26071
+ import_node_crypto18 = require("crypto");
25533
26072
  init_session_quota();
25534
26073
  init_app_settings();
25535
26074
  init_managed_accounts();
@@ -27605,7 +28144,7 @@ function compiledModuleDir() {
27605
28144
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
27606
28145
  if (cjsDir) return cjsDir;
27607
28146
  try {
27608
- const url = import_meta6.url;
28147
+ const url = import_meta8.url;
27609
28148
  if (typeof url === "string" && url.length > 0) {
27610
28149
  return (0, import_node_path62.dirname)((0, import_node_url5.fileURLToPath)(url));
27611
28150
  }
@@ -27644,7 +28183,7 @@ function formatDetachedJobInvoke(scriptPath) {
27644
28183
  if (resolved) return `node ${JSON.stringify(resolved)}`;
27645
28184
  return "node scripts/detached-job.cjs";
27646
28185
  }
27647
- var import_node_fs63, import_node_path62, import_node_url5, import_meta6, DETACHED_JOB_FILENAMES;
28186
+ var import_node_fs63, import_node_path62, import_node_url5, import_meta8, DETACHED_JOB_FILENAMES;
27648
28187
  var init_detached_job_path = __esm({
27649
28188
  "src/skills/detached-job-path.ts"() {
27650
28189
  "use strict";
@@ -27652,7 +28191,7 @@ var init_detached_job_path = __esm({
27652
28191
  import_node_path62 = require("path");
27653
28192
  import_node_url5 = require("url");
27654
28193
  init_packaged_runtime();
27655
- import_meta6 = {};
28194
+ import_meta8 = {};
27656
28195
  DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
27657
28196
  }
27658
28197
  });
@@ -28052,7 +28591,7 @@ function normalizeServiceOrigin(raw, fallback) {
28052
28591
  return fallback;
28053
28592
  }
28054
28593
  }
28055
- function asRecord13(value) {
28594
+ function asRecord14(value) {
28056
28595
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
28057
28596
  }
28058
28597
  function asString4(value) {
@@ -28085,7 +28624,7 @@ async function authorizedGet(url, token) {
28085
28624
  });
28086
28625
  const body = await readJson2(res);
28087
28626
  if (!res.ok) {
28088
- const record = asRecord13(body);
28627
+ const record = asRecord14(body);
28089
28628
  const message = firstString4(record, ["message", "error", "error_description", "detail"]) || (typeof body === "string" ? body.trim().slice(0, 160) : "");
28090
28629
  throw new Error(
28091
28630
  message ? `${res.status}: ${message}` : `Request failed (${res.status})`
@@ -28099,28 +28638,28 @@ async function verifyOptionalService(input) {
28099
28638
  const spec = optionalServiceSpec(input.id);
28100
28639
  try {
28101
28640
  if (input.id === "vercel") {
28102
- const body2 = asRecord13(await authorizedGet("https://api.vercel.com/v2/user", token));
28103
- const user = asRecord13(body2?.user) ?? body2;
28641
+ const body2 = asRecord14(await authorizedGet("https://api.vercel.com/v2/user", token));
28642
+ const user = asRecord14(body2?.user) ?? body2;
28104
28643
  const name2 = firstString4(user, ["username", "name", "email"]) || "Vercel user";
28105
28644
  return { viewerName: name2 };
28106
28645
  }
28107
28646
  if (input.id === "supabase") {
28108
28647
  const body2 = await authorizedGet("https://api.supabase.com/v1/organizations", token);
28109
28648
  const list2 = Array.isArray(body2) ? body2 : [];
28110
- const first2 = asRecord13(list2[0]);
28649
+ const first2 = asRecord14(list2[0]);
28111
28650
  const name2 = firstString4(first2, ["name", "slug"]) || "Supabase";
28112
28651
  return { viewerName: list2.length > 1 ? `${name2} +${list2.length - 1}` : name2 };
28113
28652
  }
28114
28653
  if (input.id === "posthog") {
28115
28654
  const host2 = normalizeServiceOrigin(input.host, spec.defaultHost ?? "https://us.posthog.com");
28116
- const body2 = asRecord13(await authorizedGet(`${host2}/api/users/@me/`, token));
28655
+ const body2 = asRecord14(await authorizedGet(`${host2}/api/users/@me/`, token));
28117
28656
  const name2 = firstString4(body2, ["email", "distinct_id"]) || [firstString4(body2, ["first_name"]), firstString4(body2, ["last_name"])].filter(Boolean).join(" ") || "PostHog user";
28118
28657
  return { viewerName: name2, host: host2 };
28119
28658
  }
28120
28659
  const host = normalizeServiceOrigin(input.host, spec.defaultHost ?? "https://sentry.io");
28121
28660
  const body = await authorizedGet(`${host}/api/0/organizations/`, token);
28122
28661
  const list = Array.isArray(body) ? body : [];
28123
- const first = asRecord13(list[0]);
28662
+ const first = asRecord14(list[0]);
28124
28663
  const name = firstString4(first, ["slug", "name"]) || "Sentry";
28125
28664
  return { viewerName: list.length > 1 ? `${name} +${list.length - 1}` : name, host };
28126
28665
  } catch (err) {
@@ -28994,10 +29533,10 @@ function collectActiveRunPorts(runs) {
28994
29533
  return [...ports];
28995
29534
  }
28996
29535
  function getOrchestrator() {
28997
- if (!singleton) {
28998
- singleton = new Orchestrator();
29536
+ if (!singleton2) {
29537
+ singleton2 = new Orchestrator();
28999
29538
  }
29000
- return singleton;
29539
+ return singleton2;
29001
29540
  }
29002
29541
  async function startOrchestration(opts) {
29003
29542
  const repoPath = opts.repoPath?.trim();
@@ -29057,7 +29596,7 @@ async function startOrchestration(opts) {
29057
29596
  }
29058
29597
  return updated;
29059
29598
  }
29060
- var import_node_events, import_node_crypto17, import_node_fs67, LIVE_TURN_SPAWN_GRACE_MS, RUN_SCRIPT_DESKTOP_ADOPT_MS, STALE_AGENT_PID_WAIT_MS, LIGHT_RECONCILE_THROTTLE_MS, Orchestrator, singleton;
29599
+ var import_node_events, import_node_crypto19, import_node_fs67, LIVE_TURN_SPAWN_GRACE_MS, RUN_SCRIPT_DESKTOP_ADOPT_MS, STALE_AGENT_PID_WAIT_MS, LIGHT_RECONCILE_THROTTLE_MS, Orchestrator, singleton2;
29061
29600
  var init_orchestrator = __esm({
29062
29601
  "src/orchestrator/orchestrator.ts"() {
29063
29602
  "use strict";
@@ -29065,7 +29604,7 @@ var init_orchestrator = __esm({
29065
29604
  init_injected_notices();
29066
29605
  init_task_state();
29067
29606
  init_wait_for_turn();
29068
- import_node_crypto17 = require("crypto");
29607
+ import_node_crypto19 = require("crypto");
29069
29608
  import_node_fs67 = require("fs");
29070
29609
  init_error_detail();
29071
29610
  init_run();
@@ -29091,6 +29630,7 @@ var init_orchestrator = __esm({
29091
29630
  init_usage();
29092
29631
  init_thread_store();
29093
29632
  init_desktop_host();
29633
+ init_model_context();
29094
29634
  init_agent_block();
29095
29635
  init_child_halt();
29096
29636
  init_stop_reason();
@@ -30089,6 +30629,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
30089
30629
  orchestrationReminder,
30090
30630
  prGateDirective,
30091
30631
  injectedNoticeContext,
30632
+ takeMcpAppModelContext(threadId),
30092
30633
  expandedPrompt
30093
30634
  ].filter(Boolean).join("\n\n");
30094
30635
  if (!autoContinue && sentAttachments) {
@@ -30717,7 +31258,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
30717
31258
  const request = sameIntent ? pending : {
30718
31259
  op,
30719
31260
  scriptName: scriptName ?? null,
30720
- requestId: (0, import_node_crypto17.randomUUID)(),
31261
+ requestId: (0, import_node_crypto19.randomUUID)(),
30721
31262
  requestedAt: (/* @__PURE__ */ new Date()).toISOString()
30722
31263
  };
30723
31264
  if (!sameIntent) {
@@ -31838,7 +32379,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
31838
32379
  return thread;
31839
32380
  }
31840
32381
  };
31841
- singleton = null;
32382
+ singleton2 = null;
31842
32383
  }
31843
32384
  });
31844
32385
 
@@ -32955,6 +33496,7 @@ __export(index_exports, {
32955
33496
  startRelayServer: () => startRelayServer,
32956
33497
  startRemoteAccountLogin: () => startRemoteAccountLogin,
32957
33498
  startSlackOAuth: () => startSlackOAuth,
33499
+ stashMcpAppModelContext: () => stashMcpAppModelContext,
32958
33500
  statWorktreePath: () => statWorktreePath,
32959
33501
  stopAgentRpcServer: () => stopAgentRpcServer,
32960
33502
  stripBrightsyNdjsonNoise: () => stripBrightsyNdjsonNoise,
@@ -33521,7 +34063,7 @@ init_schedule_runner();
33521
34063
 
33522
34064
  // src/host/serve.ts
33523
34065
  var import_node_child_process7 = require("child_process");
33524
- var import_node_os22 = require("os");
34066
+ var import_node_os21 = require("os");
33525
34067
  init_system_ca();
33526
34068
  init_app_settings();
33527
34069
  init_desktop_host();
@@ -33534,7 +34076,7 @@ init_system_ca();
33534
34076
 
33535
34077
  // src/slack/public-dns.ts
33536
34078
  var import_node_dns = require("dns");
33537
- var import_node_http2 = require("http");
34079
+ var import_node_http3 = require("http");
33538
34080
  var import_node_https = require("https");
33539
34081
  var PUBLIC_DNS = ["1.1.1.1", "8.8.8.8"];
33540
34082
  function finishLookup(options, callback, address, family) {
@@ -33664,213 +34206,12 @@ function buildPathRefAttachment(input) {
33664
34206
  init_stage_files();
33665
34207
  init_orchestrator();
33666
34208
 
33667
- // src/mcp-ui/host.ts
33668
- var import_node_os21 = require("os");
33669
- init_orch_mcp_isolation();
33670
- init_meta();
33671
-
33672
- // src/mcp-ui/session.ts
33673
- var import_client2 = require("@modelcontextprotocol/sdk/client/index.js");
33674
- var import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
33675
- var import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
33676
- var import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
33677
- init_meta();
33678
- var REQUEST_TIMEOUT_MS = 2e4;
33679
- var UI_CAPABILITIES = {
33680
- extensions: {
33681
- "io.modelcontextprotocol/ui": {
33682
- mimeTypes: ["text/html;profile=mcp-app"]
33683
- }
33684
- }
33685
- };
33686
- async function openMcpUiSession(launch) {
33687
- return openMcpUiSessionFromTransport(transportForLaunch(launch));
33688
- }
33689
- async function openMcpUiSessionFromTransport(transport) {
33690
- const client = new import_client2.Client(
33691
- { name: "sideboard", version: "1.0.0" },
33692
- { capabilities: UI_CAPABILITIES }
33693
- );
33694
- await client.connect(transport, { timeout: REQUEST_TIMEOUT_MS });
33695
- return {
33696
- async listTools() {
33697
- const listed = await client.listTools(void 0, { timeout: REQUEST_TIMEOUT_MS });
33698
- return listed.tools.map((tool) => {
33699
- const meta = tool._meta;
33700
- return {
33701
- name: tool.name,
33702
- ...tool.description ? { description: tool.description } : {},
33703
- ...tool.inputSchema ? { inputSchema: tool.inputSchema } : {},
33704
- resourceUri: uiResourceUriFromMeta(meta),
33705
- ...cspFromMeta(meta) ? { csp: cspFromMeta(meta) } : {}
33706
- };
33707
- });
33708
- },
33709
- async readResource(uri) {
33710
- const result = await client.readResource({ uri }, { timeout: REQUEST_TIMEOUT_MS });
33711
- const content = result.contents?.[0];
33712
- if (!content) return null;
33713
- const text7 = "text" in content && typeof content.text === "string" ? content.text : void 0;
33714
- const blob = "blob" in content && typeof content.blob === "string" ? content.blob : void 0;
33715
- return {
33716
- uri: content.uri,
33717
- ...content.mimeType ? { mimeType: content.mimeType } : {},
33718
- ...text7 != null ? { text: text7 } : {},
33719
- ...blob != null ? { blob } : {},
33720
- ...cspFromMeta(content._meta) ? { csp: cspFromMeta(content._meta) } : {}
33721
- };
33722
- },
33723
- async callTool(name, args) {
33724
- return client.callTool({ name, arguments: args }, void 0, { timeout: REQUEST_TIMEOUT_MS });
33725
- },
33726
- async close() {
33727
- await client.close();
33728
- }
33729
- };
33730
- }
33731
- function transportForLaunch(launch) {
33732
- if (launch.kind === "stdio") {
33733
- return new import_stdio.StdioClientTransport({
33734
- command: launch.command,
33735
- args: launch.args,
33736
- env: { ...(0, import_stdio.getDefaultEnvironment)(), ...launch.env },
33737
- stderr: "ignore",
33738
- ...launch.cwd ? { cwd: launch.cwd } : {}
33739
- });
33740
- }
33741
- const url = new URL(launch.url);
33742
- const requestInit = launch.headers ? { headers: launch.headers } : void 0;
33743
- if (launch.kind === "sse") {
33744
- return new import_sse.SSEClientTransport(url, requestInit ? { requestInit } : void 0);
33745
- }
33746
- return new import_streamableHttp.StreamableHTTPClientTransport(url, requestInit ? { requestInit } : void 0);
33747
- }
33748
-
33749
- // src/mcp-ui/host.ts
33750
- var TOOL_TTL_MS = 5 * 60 * 1e3;
33751
- var McpUiHost = class {
33752
- constructor(opts) {
33753
- this.opts = opts;
33754
- }
33755
- opts;
33756
- sessions = /* @__PURE__ */ new Map();
33757
- toolLists = /* @__PURE__ */ new Map();
33758
- async viewForClaudeTool(toolName, projectDirs) {
33759
- const entries = this.userEntries(projectDirs);
33760
- const match = matchClaudeMcpTool(toolName, Object.keys(entries));
33761
- if (!match) return null;
33762
- const launch = parseMcpServerLaunch(entries[match.server] ?? {});
33763
- if (!launch) return null;
33764
- try {
33765
- const session = await this.sessionFor(match.server, launch);
33766
- const tools = await this.toolsFor(match.server, launch, session);
33767
- const tool = tools.find((item) => item.name === match.tool);
33768
- if (!tool?.resourceUri) return null;
33769
- const resource = await session.readResource(tool.resourceUri);
33770
- if (!resource) return null;
33771
- const html = htmlFromResourceContent(resource);
33772
- if (!html) return null;
33773
- const csp = resource.csp ?? tool.csp;
33774
- return {
33775
- server: match.server,
33776
- tool: match.tool,
33777
- uri: html.uri,
33778
- mimeType: html.mimeType,
33779
- html: html.html,
33780
- csp: mcpUiContentSecurityPolicy(csp)
33781
- };
33782
- } catch (err) {
33783
- console.error(
33784
- `[mcp-ui] ${match.server}/${match.tool}:`,
33785
- err instanceof Error ? err.message : err
33786
- );
33787
- return null;
33788
- }
33789
- }
33790
- async callTool(server, tool, args, projectDirs) {
33791
- const session = await this.sessionForName(server, projectDirs);
33792
- if (!session) throw new Error(`MCP server ${server} is not available`);
33793
- return session.callTool(tool, args);
33794
- }
33795
- async readResource(server, uri, projectDirs) {
33796
- const session = await this.sessionForName(server, projectDirs);
33797
- if (!session) throw new Error(`MCP server ${server} is not available`);
33798
- const resource = await session.readResource(uri);
33799
- if (!resource) throw new Error(`Resource ${uri} was empty`);
33800
- return {
33801
- contents: [
33802
- {
33803
- uri: resource.uri,
33804
- ...resource.mimeType ? { mimeType: resource.mimeType } : {},
33805
- ...resource.text != null ? { text: resource.text } : {},
33806
- ...resource.blob != null ? { blob: resource.blob } : {}
33807
- }
33808
- ]
33809
- };
33810
- }
33811
- async closeAll() {
33812
- const pending = [...this.sessions.values()];
33813
- this.sessions.clear();
33814
- this.toolLists.clear();
33815
- await Promise.all(
33816
- pending.map((session) => session.then((open) => open.close()).catch(() => void 0))
33817
- );
33818
- }
33819
- userEntries(projectDirs) {
33820
- const out = {};
33821
- for (const [name, entry] of Object.entries(this.opts.entries(projectDirs))) {
33822
- if (!name.trim() || isInjectedOrchMcpName(name) || !entry) continue;
33823
- out[name] = entry;
33824
- }
33825
- return out;
33826
- }
33827
- async sessionForName(server, projectDirs) {
33828
- const entry = this.userEntries(projectDirs)[server];
33829
- if (!entry) return null;
33830
- const launch = parseMcpServerLaunch(entry);
33831
- if (!launch) return null;
33832
- return this.sessionFor(server, launch);
33833
- }
33834
- sessionFor(server, launch) {
33835
- const key = `${server}
33836
- ${JSON.stringify(launch)}`;
33837
- const existing = this.sessions.get(key);
33838
- if (existing) return existing;
33839
- const open = this.opts.openSession ?? openMcpUiSession;
33840
- const pending = open(launch).catch((err) => {
33841
- this.sessions.delete(key);
33842
- throw err;
33843
- });
33844
- this.sessions.set(key, pending);
33845
- return pending;
33846
- }
33847
- async toolsFor(server, launch, session) {
33848
- const key = `${server}
33849
- ${JSON.stringify(launch)}`;
33850
- const cached2 = this.toolLists.get(key);
33851
- if (cached2 && cached2.key === key && Date.now() - cached2.at < TOOL_TTL_MS) return cached2.tools;
33852
- const tools = await session.listTools();
33853
- this.toolLists.set(key, { at: Date.now(), key, tools });
33854
- return tools;
33855
- }
33856
- };
33857
- var singleton2;
33858
- function getMcpUiHost() {
33859
- if (!singleton2) {
33860
- singleton2 = new McpUiHost({
33861
- entries: (projectDirs) => listUserClaudeMcpServerEntries((0, import_node_os21.homedir)(), projectDirs)
33862
- });
33863
- }
33864
- return singleton2;
33865
- }
33866
- function closeMcpUiHost() {
33867
- const host = singleton2;
33868
- singleton2 = void 0;
33869
- if (host) void host.closeAll();
33870
- }
34209
+ // src/remote/phone-mcp-ui-listen.ts
34210
+ init_host();
34211
+ init_model_context();
33871
34212
 
33872
34213
  // src/remote/phone-chats.ts
33873
- var import_node_crypto18 = require("crypto");
34214
+ var import_node_crypto20 = require("crypto");
33874
34215
  init_list_models();
33875
34216
  init_home_board();
33876
34217
  init_stage_files();
@@ -33892,10 +34233,10 @@ var MIN_MD_CHARS = 120;
33892
34233
  function oneLine2(value, max) {
33893
34234
  return value.replace(/[`\r\n]/g, " ").replace(/\s+/g, " ").trim().slice(0, max);
33894
34235
  }
33895
- function asRecord14(value) {
34236
+ function asRecord15(value) {
33896
34237
  if (typeof value === "string") {
33897
34238
  try {
33898
- return asRecord14(JSON.parse(value));
34239
+ return asRecord15(JSON.parse(value));
33899
34240
  } catch {
33900
34241
  return void 0;
33901
34242
  }
@@ -33970,7 +34311,7 @@ function toolContent(input) {
33970
34311
  if (!input) return "";
33971
34312
  const direct = input.content;
33972
34313
  if (typeof direct === "string") return direct;
33973
- const nested = asRecord14(direct);
34314
+ const nested = asRecord15(direct);
33974
34315
  if (!nested) return "";
33975
34316
  for (const key of ["html", "markup", "text", "source", "content", "code"]) {
33976
34317
  if (typeof nested[key] === "string") return nested[key];
@@ -33993,7 +34334,7 @@ function phoneArtifactText(text7, parts, limit = Number.POSITIVE_INFINITY) {
33993
34334
  const extras = [];
33994
34335
  for (const part of parts ?? []) {
33995
34336
  if (part.type !== "tool" || part.parentId || !isArtifactTool(part.name)) continue;
33996
- const input = asRecord14(part.input);
34337
+ const input = asRecord15(part.input);
33997
34338
  const content = toolContent(input);
33998
34339
  const hint = artifactHint(content);
33999
34340
  if (hint && hints.has(hint)) continue;
@@ -34012,7 +34353,7 @@ function phoneArtifactText(text7, parts, limit = Number.POSITIVE_INFINITY) {
34012
34353
  ${tail}` : tail;
34013
34354
  }
34014
34355
  function parseOpenArtifact(value, chatId) {
34015
- const record = asRecord14(value);
34356
+ const record = asRecord15(value);
34016
34357
  if (!record) return "invalid";
34017
34358
  const title = oneLine2(str5(record.title), 200);
34018
34359
  if (!title) return "invalid";
@@ -34035,7 +34376,7 @@ function phoneArtifactDocuments(sources) {
34035
34376
  }
34036
34377
  for (const part of source.parts ?? []) {
34037
34378
  if (part.type !== "tool" || part.parentId || !isArtifactTool(part.name)) continue;
34038
- const input = asRecord14(part.input);
34379
+ const input = asRecord15(part.input);
34039
34380
  const content = toolContent(input);
34040
34381
  const hint = artifactHint(content);
34041
34382
  if (hint && hints.has(hint)) continue;
@@ -34480,13 +34821,21 @@ function parsePhoneMcpUi(value, op, chatId) {
34480
34821
  }
34481
34822
  return { op: "mcp-ui", chatId, tool: tool2, callId, reqId: reqId2 };
34482
34823
  }
34824
+ if (op === "mcp-ui-context") {
34825
+ const reqId2 = controlText(value, "reqId");
34826
+ const text7 = controlText(value, "text");
34827
+ if (!reqId2 || reqId2.length > 40 || !text7 || text7.length > 8e3) return "invalid";
34828
+ return { op, chatId, reqId: reqId2, text: text7 };
34829
+ }
34483
34830
  if (op !== "mcp-ui-call" && op !== "mcp-ui-read") return "invalid";
34484
34831
  const reqId = controlText(value, "reqId");
34485
34832
  const server = controlText(value, "server");
34486
34833
  if (!reqId || reqId.length > 40 || !server || server.length > 120) return "invalid";
34487
34834
  if (op === "mcp-ui-read") {
34488
34835
  const uri = controlText(value, "uri");
34489
- if (!uri.startsWith("ui://") || uri.length > 500) return "invalid";
34836
+ if (!uri || uri.length > 500 || /\s/.test(uri) || /^(javascript|data):/i.test(uri)) {
34837
+ return "invalid";
34838
+ }
34490
34839
  return { op, chatId, reqId, server, uri };
34491
34840
  }
34492
34841
  const tool = controlText(value, "tool");
@@ -34708,7 +35057,7 @@ function parsePhoneControl(value) {
34708
35057
  }
34709
35058
  if (op === "pane") return parsePhonePane(value, chatId);
34710
35059
  if (op === "open-artifact") return parseOpenArtifact(value, chatId);
34711
- if (op === "mcp-ui" || op === "mcp-ui-call" || op === "mcp-ui-read") return parsePhoneMcpUi(value, op, chatId);
35060
+ if (op === "mcp-ui" || op === "mcp-ui-call" || op === "mcp-ui-read" || op === "mcp-ui-context") return parsePhoneMcpUi(value, op, chatId);
34712
35061
  if (op === "open-file" || op === "media") {
34713
35062
  const path2 = textField(value, "path");
34714
35063
  if (!path2 || path2.length > 2e3) return "invalid";
@@ -34975,13 +35324,13 @@ function phoneComposerOptions(thread) {
34975
35324
  }
34976
35325
  function phoneTurnAttachments(draft) {
34977
35326
  const links = (draft.links ?? []).map((link) => ({
34978
- id: (0, import_node_crypto18.randomUUID)(),
35327
+ id: (0, import_node_crypto20.randomUUID)(),
34979
35328
  name: link.ref,
34980
35329
  kind: "issue",
34981
35330
  content: [`Linked issue: ${link.ref} \u2014 ${link.title}`, link.url ? `URL: ${link.url}` : null].filter(Boolean).join("\n")
34982
35331
  }));
34983
35332
  const files = (draft.files ?? []).map((file) => ({
34984
- id: (0, import_node_crypto18.randomUUID)(),
35333
+ id: (0, import_node_crypto20.randomUUID)(),
34985
35334
  name: file.name,
34986
35335
  kind: "file",
34987
35336
  previewDataUrl: `data:application/octet-stream;base64,${file.dataBase64}`,
@@ -35317,7 +35666,9 @@ function deliverPhoneTurn(opts, chatId) {
35317
35666
  // src/remote/phone-mcp-ui-listen.ts
35318
35667
  var PHONE_HTML_CAP = 7e5;
35319
35668
  function dispatchPhoneMcpUi(cmd, opts) {
35320
- if (cmd.op !== "mcp-ui" && cmd.op !== "mcp-ui-call" && cmd.op !== "mcp-ui-read") return false;
35669
+ if (cmd.op !== "mcp-ui" && cmd.op !== "mcp-ui-call" && cmd.op !== "mcp-ui-read" && cmd.op !== "mcp-ui-context") {
35670
+ return false;
35671
+ }
35321
35672
  const thread = readPhoneThread(cmd.chatId);
35322
35673
  const reqId = cmd.reqId;
35323
35674
  const chatId = cmd.chatId;
@@ -35327,6 +35678,11 @@ function dispatchPhoneMcpUi(cmd, opts) {
35327
35678
  return true;
35328
35679
  }
35329
35680
  const projectDirs = [thread.repoPath, thread.worktreePath];
35681
+ if (cmd.op === "mcp-ui-context") {
35682
+ stashMcpAppModelContext(thread.id, cmd.text);
35683
+ emitControl(opts, { op: "mcp-ui-context", chatId, reqId, result: {} });
35684
+ return true;
35685
+ }
35330
35686
  if (cmd.op === "mcp-ui-call") {
35331
35687
  void getMcpUiHost().callTool(cmd.server, cmd.tool, cmd.args, projectDirs).then((result) => emitControl(opts, { op: "mcp-ui-call", chatId, reqId, result })).catch(
35332
35688
  (err) => emitControl(opts, { op: "mcp-ui-call", chatId, reqId, error: phoneError(err) })
@@ -36163,8 +36519,8 @@ init_fetch();
36163
36519
  init_issue_attachments();
36164
36520
 
36165
36521
  // src/integrations/linear-oauth.ts
36166
- var import_node_crypto19 = require("crypto");
36167
- var import_node_http3 = require("http");
36522
+ var import_node_crypto21 = require("crypto");
36523
+ var import_node_http4 = require("http");
36168
36524
  init_app_settings();
36169
36525
  init_fetch();
36170
36526
 
@@ -36195,8 +36551,8 @@ function linearOAuthCredentials() {
36195
36551
  return { clientId, clientSecret };
36196
36552
  }
36197
36553
  function createLinearPkce() {
36198
- const verifier = (0, import_node_crypto19.randomBytes)(32).toString("base64url");
36199
- const challenge = (0, import_node_crypto19.createHash)("sha256").update(verifier).digest("base64url");
36554
+ const verifier = (0, import_node_crypto21.randomBytes)(32).toString("base64url");
36555
+ const challenge = (0, import_node_crypto21.createHash)("sha256").update(verifier).digest("base64url");
36200
36556
  return { verifier, challenge };
36201
36557
  }
36202
36558
  function linearOAuthAuthorizeUrl(clientId, state, codeChallenge) {
@@ -36327,7 +36683,7 @@ async function startLinearOAuth(opts) {
36327
36683
  throw new LinearOAuthCancelledError();
36328
36684
  }
36329
36685
  const { clientId, clientSecret } = linearOAuthCredentials();
36330
- const state = (0, import_node_crypto19.randomBytes)(16).toString("hex");
36686
+ const state = (0, import_node_crypto21.randomBytes)(16).toString("hex");
36331
36687
  const pkce = createLinearPkce();
36332
36688
  const authorizeUrl = linearOAuthAuthorizeUrl(clientId, state, pkce.challenge);
36333
36689
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
@@ -36340,7 +36696,7 @@ async function startLinearOAuth(opts) {
36340
36696
  if (err) reject(err);
36341
36697
  else resolve3(value);
36342
36698
  };
36343
- const server = (0, import_node_http3.createServer)((req, res) => {
36699
+ const server = (0, import_node_http4.createServer)((req, res) => {
36344
36700
  try {
36345
36701
  const url = new URL(req.url || "/", `http://127.0.0.1:${LINEAR_OAUTH_PORT}`);
36346
36702
  if (url.pathname !== "/callback") {
@@ -38753,7 +39109,7 @@ function runRemoteHost(opts) {
38753
39109
  init_protocol();
38754
39110
 
38755
39111
  // src/relay/server.ts
38756
- var import_node_http4 = require("http");
39112
+ var import_node_http5 = require("http");
38757
39113
  var import_ws4 = require("ws");
38758
39114
 
38759
39115
  // src/slack/oauth-redirect.ts
@@ -39162,7 +39518,7 @@ function connectSession(ws, opts, log) {
39162
39518
  }
39163
39519
 
39164
39520
  // src/slack/relay-protocol.ts
39165
- var import_node_crypto20 = require("crypto");
39521
+ var import_node_crypto22 = require("crypto");
39166
39522
  var SLACK_RELAY_PING_INTERVAL_MS = 2e4;
39167
39523
  var SLACK_RELAY_PONG_TIMEOUT_MS = 15e3;
39168
39524
  function slackRelaySessionKey(teamId, userId, deviceId) {
@@ -39172,7 +39528,7 @@ function slackRelayUserKey(teamId, userId) {
39172
39528
  return `${teamId.trim()}:${userId.trim()}`;
39173
39529
  }
39174
39530
  function newSlackRelayEventId() {
39175
- return (0, import_node_crypto20.randomBytes)(12).toString("hex");
39531
+ return (0, import_node_crypto22.randomBytes)(12).toString("hex");
39176
39532
  }
39177
39533
  function parseSlackRelayClientMessage(raw) {
39178
39534
  try {
@@ -39700,7 +40056,7 @@ async function tryServeStatic(req, res, root) {
39700
40056
  }
39701
40057
 
39702
40058
  // src/remote/hub.ts
39703
- var import_node_crypto21 = require("crypto");
40059
+ var import_node_crypto23 = require("crypto");
39704
40060
  init_protocol();
39705
40061
  var CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
39706
40062
  var CODE_TTL_MS = 10 * 60 * 1e3;
@@ -39711,7 +40067,7 @@ function send(socket, msg) {
39711
40067
  }
39712
40068
  }
39713
40069
  function newCode() {
39714
- const bytes = (0, import_node_crypto21.randomBytes)(6);
40070
+ const bytes = (0, import_node_crypto23.randomBytes)(6);
39715
40071
  let code = "";
39716
40072
  for (let i = 0; i < 6; i++) code += CODE_ALPHABET[bytes[i] % CODE_ALPHABET.length];
39717
40073
  return code;
@@ -39970,7 +40326,7 @@ var RemoteHub = class {
39970
40326
  host.phone = null;
39971
40327
  }
39972
40328
  const created = sessionToken == null;
39973
- const token = sessionToken ?? (0, import_node_crypto21.randomBytes)(24).toString("hex");
40329
+ const token = sessionToken ?? (0, import_node_crypto23.randomBytes)(24).toString("hex");
39974
40330
  if (host.sessionToken && host.sessionToken !== token) {
39975
40331
  this.phonesByToken.delete(host.sessionToken);
39976
40332
  }
@@ -40022,7 +40378,7 @@ var RemoteHub = class {
40022
40378
  init_protocol();
40023
40379
 
40024
40380
  // src/relay/accounts.ts
40025
- var import_node_crypto22 = require("crypto");
40381
+ var import_node_crypto24 = require("crypto");
40026
40382
  var import_node_fs70 = require("fs");
40027
40383
  var import_node_module7 = require("module");
40028
40384
  var import_node_path68 = require("path");
@@ -40041,12 +40397,12 @@ function openDatabase(filePath) {
40041
40397
  return db;
40042
40398
  }
40043
40399
  function sha2562(value) {
40044
- return (0, import_node_crypto22.createHash)("sha256").update(value).digest("hex");
40400
+ return (0, import_node_crypto24.createHash)("sha256").update(value).digest("hex");
40045
40401
  }
40046
40402
  function sameHash(a, b) {
40047
40403
  const left = Buffer.from(a);
40048
40404
  const right = Buffer.from(b);
40049
- return left.length === right.length && (0, import_node_crypto22.timingSafeEqual)(left, right);
40405
+ return left.length === right.length && (0, import_node_crypto24.timingSafeEqual)(left, right);
40050
40406
  }
40051
40407
  function normalizeEmail(email) {
40052
40408
  const trimmed = email?.trim().toLowerCase() ?? "";
@@ -40151,7 +40507,7 @@ var RelayAccountStore = class {
40151
40507
  this.insertIdentity(issuer, subject, accountId, email, verified);
40152
40508
  } else {
40153
40509
  const byEmail = verified ? this.accountIdForVerifiedEmail(email) : void 0;
40154
- accountId = byEmail ?? (0, import_node_crypto22.randomUUID)();
40510
+ accountId = byEmail ?? (0, import_node_crypto24.randomUUID)();
40155
40511
  if (!byEmail) {
40156
40512
  this.db.prepare("INSERT INTO accounts (id, created_at, device_cap) VALUES (?, ?, ?)").run(accountId, Date.now(), RELAY_DEVICE_CAP);
40157
40513
  }
@@ -40273,7 +40629,7 @@ var RelayAccountStore = class {
40273
40629
  ).run(issuer, subject, accountId, email, verified ? 1 : 0);
40274
40630
  }
40275
40631
  mintCredential(accountId) {
40276
- const credential = (0, import_node_crypto22.randomBytes)(32).toString("hex");
40632
+ const credential = (0, import_node_crypto24.randomBytes)(32).toString("hex");
40277
40633
  this.db.prepare("INSERT INTO credentials (token_hash, account_id, created_at) VALUES (?, ?, ?)").run(sha2562(credential), accountId, Date.now());
40278
40634
  return credential;
40279
40635
  }
@@ -40748,7 +41104,7 @@ async function startRelayServer(opts) {
40748
41104
  sendJson2(res, 200, { ok: true, ...value.payload });
40749
41105
  return true;
40750
41106
  };
40751
- const httpServer = (0, import_node_http4.createServer)((req, res) => {
41107
+ const httpServer = (0, import_node_http5.createServer)((req, res) => {
40752
41108
  void (async () => {
40753
41109
  const reqUrl = req.url || "/";
40754
41110
  if (accountStore && await handleRelayAccountHttp({
@@ -41085,7 +41441,7 @@ async function bindAbort(signal, stop) {
41085
41441
  }
41086
41442
  function flatInterfaces() {
41087
41443
  const out = [];
41088
- for (const rows of Object.values((0, import_node_os22.networkInterfaces)())) {
41444
+ for (const rows of Object.values((0, import_node_os21.networkInterfaces)())) {
41089
41445
  if (!rows) continue;
41090
41446
  out.push(...rows);
41091
41447
  }
@@ -41182,7 +41538,7 @@ init_stage_files();
41182
41538
  init_consume_attachments();
41183
41539
 
41184
41540
  // src/composer/pasted-text.ts
41185
- var import_node_crypto23 = require("crypto");
41541
+ var import_node_crypto25 = require("crypto");
41186
41542
  var PASTE_ATTACH_MIN_CHARS = 1200;
41187
41543
  var PASTE_ATTACH_MIN_LINES = 15;
41188
41544
  var PASTED_NAME_RE = /^Pasted text #(\d+)\.txt$/i;
@@ -41209,7 +41565,7 @@ function nextPastedTextName(existing) {
41209
41565
  }
41210
41566
  function buildPastedTextAttachment(text7, opts) {
41211
41567
  return {
41212
- id: opts?.id ?? (0, import_node_crypto23.randomUUID)(),
41568
+ id: opts?.id ?? (0, import_node_crypto25.randomUUID)(),
41213
41569
  name: opts?.name ?? "Pasted text #1.txt",
41214
41570
  kind: "file",
41215
41571
  path: opts?.path,
@@ -41385,7 +41741,7 @@ function createClippedMcpServer() {
41385
41741
 
41386
41742
  // src/mcp/server.ts
41387
41743
  var import_zod11 = require("zod");
41388
- var import_node_crypto26 = require("crypto");
41744
+ var import_node_crypto28 = require("crypto");
41389
41745
  var import_node_path73 = require("path");
41390
41746
  init_orchestrator();
41391
41747
  init_task_state();
@@ -42977,7 +43333,7 @@ function registerScheduleTools(server) {
42977
43333
  }
42978
43334
 
42979
43335
  // src/agent-rpc/handlers.ts
42980
- var import_node_crypto24 = require("crypto");
43336
+ var import_node_crypto26 = require("crypto");
42981
43337
  var import_node_path70 = require("path");
42982
43338
  init_wait_for_job();
42983
43339
  init_wait_for_turn();
@@ -43008,7 +43364,7 @@ setToolPermissionPublisher({
43008
43364
  }
43009
43365
  }
43010
43366
  });
43011
- function asRecord15(value) {
43367
+ function asRecord16(value) {
43012
43368
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
43013
43369
  return value;
43014
43370
  }
@@ -43019,12 +43375,12 @@ function invalidParams(message) {
43019
43375
  return Object.assign(new Error(message), { rpcCode: -32602 });
43020
43376
  }
43021
43377
  var handlePermissionAsk = async (params, _ctx) => {
43022
- const rec = asRecord15(params);
43378
+ const rec = asRecord16(params);
43023
43379
  const threadId = str6(rec.threadId).trim();
43024
43380
  const toolName = str6(rec.toolName).trim();
43025
43381
  if (!threadId) throw invalidParams("threadId is required");
43026
43382
  if (!toolName) throw invalidParams("toolName is required");
43027
- const input = asRecord15(rec.input);
43383
+ const input = asRecord16(rec.input);
43028
43384
  const ask = {
43029
43385
  threadId,
43030
43386
  toolName,
@@ -43060,7 +43416,7 @@ var handlePermissionAsk = async (params, _ctx) => {
43060
43416
  // src/agent-rpc/handlers.ts
43061
43417
  init_protocol2();
43062
43418
  function presentArtifactResult(input) {
43063
- const id = input.artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${(0, import_node_crypto24.randomUUID)().slice(0, 8)}`;
43419
+ const id = input.artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${(0, import_node_crypto26.randomUUID)().slice(0, 8)}`;
43064
43420
  return {
43065
43421
  ok: true,
43066
43422
  artifact_id: id,
@@ -43072,7 +43428,7 @@ function presentArtifactResult(input) {
43072
43428
  message: input.type === "log" ? "Log accepted. Same artifact_id appends; send only new lines next time." : "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
43073
43429
  };
43074
43430
  }
43075
- function asRecord16(value) {
43431
+ function asRecord17(value) {
43076
43432
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
43077
43433
  return value;
43078
43434
  }
@@ -43096,7 +43452,7 @@ async function handleRuntimePing(_params, _ctx) {
43096
43452
  };
43097
43453
  }
43098
43454
  async function handlePresentArtifact(params, _ctx) {
43099
- const rec = asRecord16(params);
43455
+ const rec = asRecord17(params);
43100
43456
  const title = str7(rec.title).trim();
43101
43457
  const type = str7(rec.type).trim();
43102
43458
  const allowed = ["html", "svg", "markdown", "react", "log"];
@@ -43120,7 +43476,7 @@ async function handlePresentArtifact(params, _ctx) {
43120
43476
  });
43121
43477
  }
43122
43478
  async function handleJobWait(params, ctx, waitFn = waitForDetachedJob) {
43123
- const rec = asRecord16(params);
43479
+ const rec = asRecord17(params);
43124
43480
  const id = str7(rec.id).trim();
43125
43481
  if (!id) throw invalidParams2("id is required");
43126
43482
  const cwd = jobCwd(rec);
@@ -43144,7 +43500,7 @@ async function handleJobWait(params, ctx, waitFn = waitForDetachedJob) {
43144
43500
  };
43145
43501
  }
43146
43502
  async function handleJobStop(params, _ctx) {
43147
- const rec = asRecord16(params);
43503
+ const rec = asRecord17(params);
43148
43504
  const id = str7(rec.id).trim();
43149
43505
  if (!id) throw invalidParams2("id is required");
43150
43506
  const reason = str7(rec.reason).trim() || void 0;
@@ -43172,7 +43528,7 @@ async function defaultTurnWait(ref, timeoutMs) {
43172
43528
  });
43173
43529
  }
43174
43530
  async function handleTurnWait(params, ctx, waitFn = defaultTurnWait) {
43175
- const rec = asRecord16(params);
43531
+ const rec = asRecord17(params);
43176
43532
  const ref = str7(rec.ref).trim();
43177
43533
  if (!ref) throw invalidParams2("ref is required");
43178
43534
  const requested = typeof rec.timeoutMs === "number" && Number.isFinite(rec.timeoutMs) ? rec.timeoutMs : void 0;
@@ -43224,7 +43580,7 @@ init_worktree();
43224
43580
  init_paths();
43225
43581
 
43226
43582
  // src/board/board-pins.ts
43227
- var import_node_crypto25 = require("crypto");
43583
+ var import_node_crypto27 = require("crypto");
43228
43584
  var import_node_fs72 = require("fs");
43229
43585
  var import_node_path71 = require("path");
43230
43586
  init_paths();
@@ -43256,7 +43612,7 @@ function addBoardPin(input) {
43256
43612
  const ref = input.ref.trim();
43257
43613
  if (!ref) throw new Error("Board item needs a ref");
43258
43614
  const pin = {
43259
- id: (0, import_node_crypto25.randomUUID)(),
43615
+ id: (0, import_node_crypto27.randomUUID)(),
43260
43616
  kind: input.kind,
43261
43617
  ref,
43262
43618
  repoPath: input.repoPath.trim(),
@@ -44189,7 +44545,7 @@ async function startMcpServer() {
44189
44545
  const ident = issue?.identifier ?? ref.trim();
44190
44546
  const attachments = issue ? [
44191
44547
  {
44192
- id: (0, import_node_crypto26.randomUUID)(),
44548
+ id: (0, import_node_crypto28.randomUUID)(),
44193
44549
  name: issue.identifier,
44194
44550
  kind: "issue",
44195
44551
  content: [
@@ -44751,8 +45107,8 @@ init_protocol2();
44751
45107
  init_metadata();
44752
45108
 
44753
45109
  // src/agent-rpc/server.ts
44754
- var import_node_crypto27 = require("crypto");
44755
- var import_node_http5 = require("http");
45110
+ var import_node_crypto29 = require("crypto");
45111
+ var import_node_http6 = require("http");
44756
45112
  var import_ws5 = require("ws");
44757
45113
  init_protocol2();
44758
45114
  init_metadata();
@@ -44760,7 +45116,7 @@ function tokensEqual(a, b) {
44760
45116
  const left = Buffer.from(a);
44761
45117
  const right = Buffer.from(b);
44762
45118
  if (left.length !== right.length) return false;
44763
- return (0, import_node_crypto27.timingSafeEqual)(left, right);
45119
+ return (0, import_node_crypto29.timingSafeEqual)(left, right);
44764
45120
  }
44765
45121
  function authTokenFromUpgrade(req) {
44766
45122
  try {
@@ -44793,16 +45149,16 @@ var AgentRpcServer = class {
44793
45149
  async listen(opts = {}) {
44794
45150
  const host = opts.host ?? "127.0.0.1";
44795
45151
  const port = opts.port ?? 0;
44796
- const authToken = (0, import_node_crypto27.randomBytes)(24).toString("base64url");
45152
+ const authToken = (0, import_node_crypto29.randomBytes)(24).toString("base64url");
44797
45153
  this.meta = {
44798
- runtimeId: (0, import_node_crypto27.randomUUID)(),
45154
+ runtimeId: (0, import_node_crypto29.randomUUID)(),
44799
45155
  pid: process.pid,
44800
45156
  protocolVersion: AGENT_RPC_PROTOCOL_VERSION,
44801
45157
  authToken,
44802
45158
  startedAt: Date.now(),
44803
45159
  transports: []
44804
45160
  };
44805
- const http = (0, import_node_http5.createServer)((_req, res) => {
45161
+ const http = (0, import_node_http6.createServer)((_req, res) => {
44806
45162
  res.statusCode = 404;
44807
45163
  res.end();
44808
45164
  });
@@ -45288,6 +45644,8 @@ init_oauth();
45288
45644
  init_injected_mcp();
45289
45645
  init_user_mcp_config();
45290
45646
  init_orch_mcp_isolation();
45647
+ init_host();
45648
+ init_model_context();
45291
45649
  init_meta();
45292
45650
 
45293
45651
  // src/slack/workspaces.ts
@@ -45386,7 +45744,7 @@ function slackTokenFor(ws, kind = "read") {
45386
45744
  }
45387
45745
 
45388
45746
  // src/slack/oauth.ts
45389
- var import_node_crypto28 = require("crypto");
45747
+ var import_node_crypto30 = require("crypto");
45390
45748
  init_app_settings();
45391
45749
  var SLACK_BOT_SCOPES = [
45392
45750
  "app_mentions:read",
@@ -45480,7 +45838,7 @@ async function startSlackOAuth(opts) {
45480
45838
  throw new SlackOAuthCancelledError();
45481
45839
  }
45482
45840
  const { clientId } = slackOAuthCredentials();
45483
- const state = (0, import_node_crypto28.randomBytes)(16).toString("hex");
45841
+ const state = (0, import_node_crypto30.randomBytes)(16).toString("hex");
45484
45842
  const authorizeUrl = slackOAuthAuthorizeUrl(clientId, state);
45485
45843
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
45486
45844
  const pollIntervalMs = opts?.pollIntervalMs ?? 400;
@@ -46389,7 +46747,7 @@ function resolveSlackListenMode(opts) {
46389
46747
  init_protocol();
46390
46748
 
46391
46749
  // src/relay/account-login.ts
46392
- var import_node_crypto29 = require("crypto");
46750
+ var import_node_crypto31 = require("crypto");
46393
46751
  init_fetch();
46394
46752
  init_protocol();
46395
46753
  var RemoteAccountLoginCancelled = class extends Error {
@@ -46457,7 +46815,7 @@ async function readJson4(res, url) {
46457
46815
  async function startRemoteAccountLogin(opts) {
46458
46816
  if (opts.signal?.aborted) throw new RemoteAccountLoginCancelled();
46459
46817
  const origin = (opts.origin ?? remoteAccountHttpOrigin()).replace(/\/+$/, "");
46460
- const state = (0, import_node_crypto29.randomBytes)(16).toString("hex");
46818
+ const state = (0, import_node_crypto31.randomBytes)(16).toString("hex");
46461
46819
  const timeoutMs = opts.timeoutMs ?? 5 * 6e4;
46462
46820
  const pollIntervalMs = opts.pollIntervalMs ?? 400;
46463
46821
  const linkToken = opts.linkToken?.trim() ?? "";
@@ -47853,6 +48211,7 @@ async function pollSlackOutboundWatches(opts) {
47853
48211
  startRelayServer,
47854
48212
  startRemoteAccountLogin,
47855
48213
  startSlackOAuth,
48214
+ stashMcpAppModelContext,
47856
48215
  statWorktreePath,
47857
48216
  stopAgentRpcServer,
47858
48217
  stripBrightsyNdjsonNoise,