@sideboard-ai/core 0.1.311 → 0.1.313

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
@@ -12769,24 +12769,47 @@ var init_turn_trace = __esm({
12769
12769
  });
12770
12770
 
12771
12771
  // src/brightsy/config.ts
12772
+ function resolveBrightsyConfigFile(path2) {
12773
+ try {
12774
+ if ((0, import_node_fs27.existsSync)(path2) && (0, import_node_fs27.statSync)(path2).isDirectory()) {
12775
+ return (0, import_node_path31.join)(path2, "config.json");
12776
+ }
12777
+ } catch {
12778
+ }
12779
+ return path2;
12780
+ }
12772
12781
  function brightsyConfigPath() {
12773
12782
  const override = process.env.BRIGHTSY_CONFIG?.trim();
12774
- if (override) return override;
12775
- return (0, import_node_path31.join)((0, import_node_os10.homedir)(), ".brightsy", "config.json");
12783
+ const raw = override || (0, import_node_path31.join)((0, import_node_os10.homedir)(), ".brightsy", "config.json");
12784
+ return resolveBrightsyConfigFile(raw);
12776
12785
  }
12777
12786
  function loadBrightsyConfig() {
12778
12787
  const path2 = brightsyConfigPath();
12779
12788
  if (!(0, import_node_fs27.existsSync)(path2)) {
12780
12789
  throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
12781
12790
  }
12782
- const raw = JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8"));
12791
+ let text7;
12792
+ try {
12793
+ text7 = (0, import_node_fs27.readFileSync)(path2, "utf8");
12794
+ } catch (err) {
12795
+ const code = err && typeof err === "object" && "code" in err ? String(err.code) : "";
12796
+ if (code === "EISDIR") {
12797
+ throw new Error(
12798
+ `Brightsy config is a directory (${path2}) \u2014 expected ~/.brightsy/config.json`
12799
+ );
12800
+ }
12801
+ throw err;
12802
+ }
12803
+ const raw = JSON.parse(text7);
12783
12804
  if (!raw.access_token || !raw.account_id) {
12784
12805
  throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
12785
12806
  }
12786
12807
  return raw;
12787
12808
  }
12788
12809
  function saveBrightsyConfig(cfg) {
12789
- (0, import_node_fs27.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
12810
+ const path2 = brightsyConfigPath();
12811
+ (0, import_node_fs27.mkdirSync)((0, import_node_path31.dirname)(path2), { recursive: true });
12812
+ (0, import_node_fs27.writeFileSync)(path2, `${JSON.stringify(cfg, null, 2)}
12790
12813
  `, {
12791
12814
  mode: 384
12792
12815
  });
@@ -13110,6 +13133,7 @@ var connected_teams_exports = {};
13110
13133
  __export(connected_teams_exports, {
13111
13134
  applyConnectedTeamToCli: () => applyConnectedTeamToCli,
13112
13135
  brightsyMcpServerName: () => brightsyMcpServerName,
13136
+ brightsySessionShouldReplace: () => brightsySessionShouldReplace,
13113
13137
  connectBrightsyTeam: () => connectBrightsyTeam,
13114
13138
  disconnectBrightsyTeam: () => disconnectBrightsyTeam,
13115
13139
  ensureCliTeamTracked: () => ensureCliTeamTracked,
@@ -13150,6 +13174,25 @@ function listConnectedBrightsyTeams() {
13150
13174
  function getConnectedBrightsyTeamsRaw() {
13151
13175
  return readStore();
13152
13176
  }
13177
+ function teamFromCliSession(cfg, current) {
13178
+ return {
13179
+ ...current,
13180
+ access_token: cfg.access_token,
13181
+ refresh_token: cfg.refresh_token ?? current.refresh_token,
13182
+ expires_at: cfg.expires_at ?? current.expires_at,
13183
+ endpoint: cfg.endpoint ?? current.endpoint,
13184
+ oauth_client_id: cfg.oauth_client_id ?? current.oauth_client_id,
13185
+ slug: cfg.account_slug || current.slug
13186
+ };
13187
+ }
13188
+ function brightsySessionShouldReplace(a, b) {
13189
+ if (!a.access_token) return false;
13190
+ const aStale = brightsyAccessTokenNeedsRefresh(a.expires_at);
13191
+ const bStale = !b.access_token || brightsyAccessTokenNeedsRefresh(b.expires_at);
13192
+ if (!aStale && bStale) return true;
13193
+ if (aStale) return false;
13194
+ return (a.expires_at ?? 0) > (b.expires_at ?? 0);
13195
+ }
13153
13196
  function applyConnectedTeamToCli(team) {
13154
13197
  let base = {};
13155
13198
  try {
@@ -13236,22 +13279,34 @@ async function ensureConnectedBrightsyTeamTokens(opts) {
13236
13279
  }
13237
13280
  next.push(refreshed);
13238
13281
  }
13239
- if (changed) writeStore(next);
13240
13282
  try {
13241
13283
  const cfg2 = loadBrightsyConfig();
13242
- const active = next.find((t) => t.id === cfg2.account_id);
13243
- if (active && active.access_token !== cfg2.access_token) {
13244
- applyConnectedTeamToCli(active);
13284
+ const idx = next.findIndex((t) => t.id === cfg2.account_id);
13285
+ if (idx >= 0) {
13286
+ const active = next[idx];
13287
+ if (brightsySessionShouldReplace(cfg2, active)) {
13288
+ next[idx] = teamFromCliSession(cfg2, active);
13289
+ changed = true;
13290
+ } else if (active.access_token !== cfg2.access_token) {
13291
+ applyConnectedTeamToCli(active);
13292
+ }
13245
13293
  }
13246
13294
  } catch {
13247
13295
  }
13296
+ if (changed) writeStore(next);
13248
13297
  return next;
13249
13298
  }
13250
13299
  function ensureCliTeamTracked(meta) {
13251
13300
  try {
13252
13301
  const cfg = loadBrightsyConfig();
13253
13302
  const existing = readStore();
13254
- if (existing.some((t) => t.id === cfg.account_id)) {
13303
+ const idx = existing.findIndex((t) => t.id === cfg.account_id);
13304
+ if (idx >= 0) {
13305
+ const current = existing[idx];
13306
+ if (brightsySessionShouldReplace(cfg, current)) {
13307
+ existing[idx] = teamFromCliSession(cfg, current);
13308
+ writeStore(existing);
13309
+ }
13255
13310
  return listConnectedBrightsyTeams();
13256
13311
  }
13257
13312
  const team = {
@@ -13869,18 +13924,18 @@ function mcpUiContentSecurityPolicy(csp) {
13869
13924
  const connect = (csp?.connectDomains ?? []).join(" ");
13870
13925
  const frames = csp?.frameDomains?.length ? csp.frameDomains.join(" ") : "'none'";
13871
13926
  const base = csp?.baseUriDomains?.length ? csp.baseUriDomains.join(" ") : "'none'";
13927
+ const connectSrc = connect ? `'self' ${connect}`.trim() : "'none'";
13872
13928
  return [
13873
13929
  "default-src 'none'",
13874
- `script-src 'self' 'unsafe-inline' 'unsafe-eval' ${resource}`.trim(),
13930
+ `script-src 'self' 'unsafe-inline' ${resource}`.trim(),
13875
13931
  `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(),
13932
+ `img-src 'self' data: ${resource}`.trim(),
13933
+ `font-src 'self' ${resource}`.trim(),
13934
+ `connect-src ${connectSrc}`,
13935
+ `media-src 'self' data: ${resource}`.trim(),
13880
13936
  `frame-src ${frames}`,
13881
13937
  `base-uri ${base}`,
13882
- "object-src 'none'",
13883
- "form-action 'self'"
13938
+ "object-src 'none'"
13884
13939
  ].join("; ");
13885
13940
  }
13886
13941
  function htmlText(text7, blob) {
@@ -13917,8 +13972,10 @@ function originList(value) {
13917
13972
  return out;
13918
13973
  }
13919
13974
  function originToken(value) {
13975
+ const trimmed = value.trim();
13976
+ if (/^(https?|wss?):\/\/\*\.[a-z0-9.-]+(?::\d+)?$/i.test(trimmed)) return trimmed;
13920
13977
  try {
13921
- const url = new URL(value.trim());
13978
+ const url = new URL(trimmed);
13922
13979
  if (url.protocol !== "https:" && url.protocol !== "http:" && url.protocol !== "wss:" && url.protocol !== "ws:") {
13923
13980
  return null;
13924
13981
  }
@@ -15279,6 +15336,524 @@ var init_orch_mcp_isolation = __esm({
15279
15336
  }
15280
15337
  });
15281
15338
 
15339
+ // src/mcp-ui/visibility.ts
15340
+ function visibilityFromMeta(meta) {
15341
+ const rec = asRecord4(meta);
15342
+ const ui = asRecord4(rec.ui);
15343
+ const raw = ui.visibility;
15344
+ if (!Array.isArray(raw)) return ["model", "app"];
15345
+ const out = [];
15346
+ for (const item of raw) {
15347
+ if (item !== "model" && item !== "app") continue;
15348
+ if (!out.includes(item)) out.push(item);
15349
+ }
15350
+ return out.length ? out : ["model", "app"];
15351
+ }
15352
+ function toolVisibleTo(visibility, audience) {
15353
+ const vis = visibility?.length ? visibility : ["model", "app"];
15354
+ return vis.includes(audience);
15355
+ }
15356
+ function asRecord4(value) {
15357
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
15358
+ return value;
15359
+ }
15360
+ var init_visibility = __esm({
15361
+ "src/mcp-ui/visibility.ts"() {
15362
+ "use strict";
15363
+ }
15364
+ });
15365
+
15366
+ // src/mcp-ui/proxy-server.ts
15367
+ function attachMcpUiProxyHandlers(server, session) {
15368
+ server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
15369
+ const tools = await session.listTools();
15370
+ return { tools: tools.filter((tool) => toolVisibleTo(tool.visibility, "model")).map(listedTool) };
15371
+ });
15372
+ server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
15373
+ const name = request.params.name;
15374
+ const tools = await session.listTools();
15375
+ const tool = tools.find((item) => item.name === name);
15376
+ if (!tool || !toolVisibleTo(tool.visibility, "model")) {
15377
+ throw new Error(`Tool ${name} is not available to the model`);
15378
+ }
15379
+ return await session.callTool(name, request.params.arguments ?? {});
15380
+ });
15381
+ server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
15382
+ const resource = await session.readResource(request.params.uri);
15383
+ if (!resource) throw new Error(`Resource ${request.params.uri} was empty`);
15384
+ return {
15385
+ contents: [
15386
+ {
15387
+ uri: resource.uri,
15388
+ ...resource.mimeType ? { mimeType: resource.mimeType } : {},
15389
+ ...resource.text != null ? { text: resource.text } : {},
15390
+ ...resource.blob != null ? { blob: resource.blob } : {}
15391
+ }
15392
+ ]
15393
+ };
15394
+ });
15395
+ server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => session.listResources());
15396
+ server.setRequestHandler(
15397
+ import_types.ListResourceTemplatesRequestSchema,
15398
+ async () => session.listResourceTemplates()
15399
+ );
15400
+ server.setRequestHandler(import_types.ListPromptsRequestSchema, async () => session.listPrompts());
15401
+ server.setRequestHandler(import_types.GetPromptRequestSchema, async (request) => {
15402
+ return await session.getPrompt(request.params.name, request.params.arguments);
15403
+ });
15404
+ server.fallbackRequestHandler = async (request) => {
15405
+ return await session.forward(request.method, request.params);
15406
+ };
15407
+ }
15408
+ function listedTool(tool) {
15409
+ return {
15410
+ name: tool.name,
15411
+ ...tool.description ? { description: tool.description } : {},
15412
+ inputSchema: tool.inputSchema ?? { type: "object", properties: {} },
15413
+ _meta: {
15414
+ ui: {
15415
+ ...tool.resourceUri ? { resourceUri: tool.resourceUri } : {},
15416
+ visibility: tool.visibility
15417
+ }
15418
+ }
15419
+ };
15420
+ }
15421
+ var import_server, import_types;
15422
+ var init_proxy_server = __esm({
15423
+ "src/mcp-ui/proxy-server.ts"() {
15424
+ "use strict";
15425
+ import_server = require("@modelcontextprotocol/sdk/server/index.js");
15426
+ import_types = require("@modelcontextprotocol/sdk/types.js");
15427
+ init_visibility();
15428
+ }
15429
+ });
15430
+
15431
+ // src/mcp-ui/proxy-http.ts
15432
+ async function listenMcpUiProxy(opts) {
15433
+ const token = (0, import_node_crypto11.randomBytes)(24).toString("hex");
15434
+ const transports = /* @__PURE__ */ new Map();
15435
+ const httpServer = (0, import_node_http.createServer)((req, res) => {
15436
+ void handle(req, res);
15437
+ });
15438
+ await new Promise((resolve3, reject) => {
15439
+ httpServer.once("error", reject);
15440
+ httpServer.listen(0, "127.0.0.1", () => resolve3());
15441
+ });
15442
+ const addr = httpServer.address();
15443
+ if (!addr || typeof addr === "string") throw new Error("mcp-ui proxy failed to bind");
15444
+ const origin = `http://127.0.0.1:${addr.port}`;
15445
+ async function handle(req, res) {
15446
+ if (req.headers.authorization !== `Bearer ${token}`) {
15447
+ res.writeHead(401).end();
15448
+ return;
15449
+ }
15450
+ const target = parseMcpUiProxyPath(req.url);
15451
+ if (!target) {
15452
+ res.writeHead(404).end();
15453
+ return;
15454
+ }
15455
+ try {
15456
+ const session = await opts.sessionFor(target.server, target.dirsKey);
15457
+ if (!session) {
15458
+ res.writeHead(404).end();
15459
+ return;
15460
+ }
15461
+ const header = req.headers["mcp-session-id"];
15462
+ const sid = typeof header === "string" ? header : "";
15463
+ const sessionKey = `${target.server}:${target.dirsKey}`;
15464
+ let transport = sid ? transports.get(`${sessionKey}:${sid}`) : void 0;
15465
+ if (!transport) {
15466
+ if (req.method !== "POST") {
15467
+ res.writeHead(400).end();
15468
+ return;
15469
+ }
15470
+ const created = new import_streamableHttp.StreamableHTTPServerTransport({
15471
+ sessionIdGenerator: () => (0, import_node_crypto11.randomUUID)(),
15472
+ allowedHosts: ["127.0.0.1", "localhost"],
15473
+ onsessioninitialized: (id) => {
15474
+ transports.set(`${sessionKey}:${id}`, created);
15475
+ },
15476
+ onsessionclosed: (id) => {
15477
+ transports.delete(`${sessionKey}:${id}`);
15478
+ }
15479
+ });
15480
+ transport = created;
15481
+ const mcp = new import_server2.Server(
15482
+ { name: `sideboard-ui-proxy-${target.server}`, version: "1.0.0" },
15483
+ { capabilities: session.capabilities() }
15484
+ );
15485
+ attachMcpUiProxyHandlers(mcp, session);
15486
+ await mcp.connect(created);
15487
+ }
15488
+ await transport.handleRequest(req, res);
15489
+ } catch {
15490
+ if (!res.headersSent) res.writeHead(500).end();
15491
+ }
15492
+ }
15493
+ return {
15494
+ urlFor(server, dirsKey) {
15495
+ const base = `${origin}/s/${encodeURIComponent(server)}`;
15496
+ return dirsKey ? `${base}/d/${dirsKey}` : base;
15497
+ },
15498
+ headers: { Authorization: `Bearer ${token}` },
15499
+ async close() {
15500
+ await Promise.all([...transports.values()].map((item) => item.close().catch(() => void 0)));
15501
+ transports.clear();
15502
+ await new Promise((resolve3) => httpServer.close(() => resolve3()));
15503
+ }
15504
+ };
15505
+ }
15506
+ function parseMcpUiProxyPath(url) {
15507
+ const path2 = (url ?? "").split("?")[0] ?? "";
15508
+ if (!path2.startsWith("/s/")) return null;
15509
+ const rest = path2.slice(3).replace(/\/$/, "");
15510
+ const split = rest.indexOf("/d/");
15511
+ try {
15512
+ if (split < 0) {
15513
+ const server2 = decodeURIComponent(rest).trim();
15514
+ return server2 ? { server: server2, dirsKey: "" } : null;
15515
+ }
15516
+ const server = decodeURIComponent(rest.slice(0, split)).trim();
15517
+ const dirsKey = rest.slice(split + 3);
15518
+ if (!server || !/^[a-f0-9]{16}$/.test(dirsKey)) return null;
15519
+ return { server, dirsKey };
15520
+ } catch {
15521
+ return null;
15522
+ }
15523
+ }
15524
+ var import_node_crypto11, import_node_http, import_server2, import_streamableHttp;
15525
+ var init_proxy_http = __esm({
15526
+ "src/mcp-ui/proxy-http.ts"() {
15527
+ "use strict";
15528
+ import_node_crypto11 = require("crypto");
15529
+ import_node_http = require("http");
15530
+ import_server2 = require("@modelcontextprotocol/sdk/server/index.js");
15531
+ import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
15532
+ init_proxy_server();
15533
+ }
15534
+ });
15535
+
15536
+ // src/mcp-ui/session.ts
15537
+ async function orEmpty(run2, fallback) {
15538
+ try {
15539
+ return await run2();
15540
+ } catch {
15541
+ return fallback;
15542
+ }
15543
+ }
15544
+ async function openMcpUiSession(launch) {
15545
+ return openMcpUiSessionFromTransport(transportForLaunch(launch));
15546
+ }
15547
+ async function openMcpUiSessionFromTransport(transport) {
15548
+ const client = new import_client.Client(
15549
+ { name: "sideboard", version: "1.0.0" },
15550
+ { capabilities: UI_CAPABILITIES }
15551
+ );
15552
+ await client.connect(transport, { timeout: REQUEST_TIMEOUT_MS });
15553
+ return {
15554
+ async listTools() {
15555
+ const listed = await client.listTools(void 0, { timeout: REQUEST_TIMEOUT_MS });
15556
+ return listed.tools.map((tool) => {
15557
+ const meta = tool._meta;
15558
+ return {
15559
+ name: tool.name,
15560
+ ...tool.description ? { description: tool.description } : {},
15561
+ ...tool.inputSchema ? { inputSchema: tool.inputSchema } : {},
15562
+ resourceUri: uiResourceUriFromMeta(meta),
15563
+ visibility: visibilityFromMeta(meta),
15564
+ ...cspFromMeta(meta) ? { csp: cspFromMeta(meta) } : {}
15565
+ };
15566
+ });
15567
+ },
15568
+ listResources: () => orEmpty(
15569
+ () => client.listResources(void 0, { timeout: REQUEST_TIMEOUT_MS }),
15570
+ { resources: [] }
15571
+ ),
15572
+ listResourceTemplates: () => orEmpty(
15573
+ () => client.listResourceTemplates(void 0, { timeout: REQUEST_TIMEOUT_MS }),
15574
+ { resourceTemplates: [] }
15575
+ ),
15576
+ listPrompts: () => orEmpty(
15577
+ () => client.listPrompts(void 0, { timeout: REQUEST_TIMEOUT_MS }),
15578
+ { prompts: [] }
15579
+ ),
15580
+ getPrompt(name, args) {
15581
+ return client.getPrompt({ name, arguments: args }, { timeout: REQUEST_TIMEOUT_MS });
15582
+ },
15583
+ async readResource(uri) {
15584
+ const result = await client.readResource({ uri }, { timeout: REQUEST_TIMEOUT_MS });
15585
+ const content = result.contents?.[0];
15586
+ if (!content) return null;
15587
+ const text7 = "text" in content && typeof content.text === "string" ? content.text : void 0;
15588
+ const blob = "blob" in content && typeof content.blob === "string" ? content.blob : void 0;
15589
+ return {
15590
+ uri: content.uri,
15591
+ ...content.mimeType ? { mimeType: content.mimeType } : {},
15592
+ ...text7 != null ? { text: text7 } : {},
15593
+ ...blob != null ? { blob } : {},
15594
+ ...cspFromMeta(content._meta) ? { csp: cspFromMeta(content._meta) } : {}
15595
+ };
15596
+ },
15597
+ async callTool(name, args) {
15598
+ return client.callTool({ name, arguments: args }, void 0, { timeout: REQUEST_TIMEOUT_MS });
15599
+ },
15600
+ capabilities() {
15601
+ const caps = client.getServerCapabilities() ?? {};
15602
+ return {
15603
+ tools: caps.tools ?? {},
15604
+ resources: caps.resources ?? {},
15605
+ prompts: caps.prompts ?? {},
15606
+ completions: caps.completions ?? {},
15607
+ ...caps.logging ? { logging: caps.logging } : {}
15608
+ };
15609
+ },
15610
+ forward(method, params) {
15611
+ return client.request(
15612
+ { method, params },
15613
+ import_types2.ResultSchema,
15614
+ { timeout: REQUEST_TIMEOUT_MS }
15615
+ );
15616
+ },
15617
+ async close() {
15618
+ await client.close();
15619
+ }
15620
+ };
15621
+ }
15622
+ function transportForLaunch(launch) {
15623
+ if (launch.kind === "stdio") {
15624
+ return new import_stdio.StdioClientTransport({
15625
+ command: launch.command,
15626
+ args: launch.args,
15627
+ env: { ...(0, import_stdio.getDefaultEnvironment)(), ...launch.env },
15628
+ stderr: "ignore",
15629
+ ...launch.cwd ? { cwd: launch.cwd } : {}
15630
+ });
15631
+ }
15632
+ const url = new URL(launch.url);
15633
+ const requestInit = launch.headers ? { headers: launch.headers } : void 0;
15634
+ if (launch.kind === "sse") {
15635
+ return new import_sse.SSEClientTransport(url, requestInit ? { requestInit } : void 0);
15636
+ }
15637
+ return new import_streamableHttp2.StreamableHTTPClientTransport(url, requestInit ? { requestInit } : void 0);
15638
+ }
15639
+ var import_client, import_sse, import_stdio, import_streamableHttp2, import_types2, REQUEST_TIMEOUT_MS, UI_CAPABILITIES;
15640
+ var init_session = __esm({
15641
+ "src/mcp-ui/session.ts"() {
15642
+ "use strict";
15643
+ import_client = require("@modelcontextprotocol/sdk/client/index.js");
15644
+ import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
15645
+ import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
15646
+ import_streamableHttp2 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
15647
+ import_types2 = require("@modelcontextprotocol/sdk/types.js");
15648
+ init_meta();
15649
+ init_visibility();
15650
+ REQUEST_TIMEOUT_MS = 2e4;
15651
+ UI_CAPABILITIES = {
15652
+ extensions: {
15653
+ "io.modelcontextprotocol/ui": {
15654
+ mimeTypes: ["text/html;profile=mcp-app"]
15655
+ }
15656
+ }
15657
+ };
15658
+ }
15659
+ });
15660
+
15661
+ // src/mcp-ui/host.ts
15662
+ function getMcpUiHost() {
15663
+ if (!singleton) {
15664
+ singleton = new McpUiHost({
15665
+ entries: (projectDirs) => listUserClaudeMcpServerEntries((0, import_node_os16.homedir)(), projectDirs)
15666
+ });
15667
+ }
15668
+ return singleton;
15669
+ }
15670
+ async function proxyClaudeUserMcp(orchestrator, projectDirs) {
15671
+ if (orchestrator) return {};
15672
+ const dirs = projectDirs.filter((d) => typeof d === "string" && d.length > 0);
15673
+ return getMcpUiHost().proxyEntries(listUserClaudeMcpServerEntries((0, import_node_os16.homedir)(), dirs), dirs);
15674
+ }
15675
+ function closeMcpUiHost() {
15676
+ const host = singleton;
15677
+ singleton = void 0;
15678
+ if (host) void host.closeAll();
15679
+ }
15680
+ function dirsKeyFor(projectDirs) {
15681
+ if (!projectDirs?.length) return "";
15682
+ return (0, import_node_crypto12.createHash)("sha256").update(projectDirs.join("\0")).digest("hex").slice(0, 16);
15683
+ }
15684
+ var import_node_crypto12, import_node_os16, TOOL_TTL_MS, McpUiHost, singleton;
15685
+ var init_host = __esm({
15686
+ "src/mcp-ui/host.ts"() {
15687
+ "use strict";
15688
+ import_node_crypto12 = require("crypto");
15689
+ import_node_os16 = require("os");
15690
+ init_orch_mcp_isolation();
15691
+ init_meta();
15692
+ init_proxy_http();
15693
+ init_session();
15694
+ init_visibility();
15695
+ TOOL_TTL_MS = 5 * 60 * 1e3;
15696
+ McpUiHost = class {
15697
+ constructor(opts) {
15698
+ this.opts = opts;
15699
+ }
15700
+ opts;
15701
+ sessions = /* @__PURE__ */ new Map();
15702
+ toolLists = /* @__PURE__ */ new Map();
15703
+ proxy;
15704
+ proxyDirs = /* @__PURE__ */ new Map();
15705
+ async viewForClaudeTool(toolName, projectDirs) {
15706
+ const entries = this.userEntries(projectDirs);
15707
+ const match = matchClaudeMcpTool(toolName, Object.keys(entries));
15708
+ if (!match) return null;
15709
+ const launch = parseMcpServerLaunch(entries[match.server] ?? {});
15710
+ if (!launch) return null;
15711
+ try {
15712
+ const session = await this.sessionFor(match.server, launch);
15713
+ const tools = await this.toolsFor(match.server, launch, session);
15714
+ const tool = tools.find((item) => item.name === match.tool);
15715
+ if (!tool?.resourceUri) return null;
15716
+ const resource = await session.readResource(tool.resourceUri);
15717
+ if (!resource) return null;
15718
+ const html = htmlFromResourceContent(resource);
15719
+ if (!html) return null;
15720
+ const csp = resource.csp ?? tool.csp;
15721
+ return {
15722
+ server: match.server,
15723
+ tool: match.tool,
15724
+ uri: html.uri,
15725
+ mimeType: html.mimeType,
15726
+ html: html.html,
15727
+ csp: mcpUiContentSecurityPolicy(csp)
15728
+ };
15729
+ } catch (err) {
15730
+ console.error(
15731
+ `[mcp-ui] ${match.server}/${match.tool}:`,
15732
+ err instanceof Error ? err.message : err
15733
+ );
15734
+ return null;
15735
+ }
15736
+ }
15737
+ /**
15738
+ * Point Claude at loopback HTTP so it shares this host's upstream session
15739
+ * and never sees `visibility: ["app"]` tools.
15740
+ */
15741
+ async proxyEntries(entries, projectDirs) {
15742
+ const names = Object.keys(entries).filter((name) => name.trim());
15743
+ if (names.length === 0) return {};
15744
+ const dirsKey = dirsKeyFor(projectDirs);
15745
+ this.proxyDirs.set(dirsKey, projectDirs);
15746
+ try {
15747
+ const listen = await this.ensureProxy();
15748
+ const out = {};
15749
+ for (const name of names) {
15750
+ out[name] = {
15751
+ type: "http",
15752
+ url: listen.urlFor(name, dirsKey),
15753
+ headers: listen.headers
15754
+ };
15755
+ }
15756
+ return out;
15757
+ } catch (err) {
15758
+ console.error("[mcp-ui] proxy:", err instanceof Error ? err.message : err);
15759
+ return { ...entries };
15760
+ }
15761
+ }
15762
+ async callTool(server, tool, args, projectDirs) {
15763
+ const entry = this.userEntries(projectDirs)[server];
15764
+ if (!entry) throw new Error(`MCP server ${server} is not available`);
15765
+ const launch = parseMcpServerLaunch(entry);
15766
+ if (!launch) throw new Error(`MCP server ${server} is not available`);
15767
+ const session = await this.sessionFor(server, launch);
15768
+ const tools = await this.toolsFor(server, launch, session);
15769
+ const info = tools.find((item) => item.name === tool);
15770
+ if (!info || !toolVisibleTo(info.visibility, "app")) {
15771
+ throw new Error(`Tool ${tool} is not available to the app`);
15772
+ }
15773
+ return session.callTool(tool, args);
15774
+ }
15775
+ async readResource(server, uri, projectDirs) {
15776
+ const session = await this.sessionForName(server, projectDirs);
15777
+ if (!session) throw new Error(`MCP server ${server} is not available`);
15778
+ const resource = await session.readResource(uri);
15779
+ if (!resource) throw new Error(`Resource ${uri} was empty`);
15780
+ return {
15781
+ contents: [
15782
+ {
15783
+ uri: resource.uri,
15784
+ ...resource.mimeType ? { mimeType: resource.mimeType } : {},
15785
+ ...resource.text != null ? { text: resource.text } : {},
15786
+ ...resource.blob != null ? { blob: resource.blob } : {}
15787
+ }
15788
+ ]
15789
+ };
15790
+ }
15791
+ async closeAll() {
15792
+ const pending = [...this.sessions.values()];
15793
+ const proxy = this.proxy;
15794
+ this.sessions.clear();
15795
+ this.toolLists.clear();
15796
+ this.proxyDirs.clear();
15797
+ this.proxy = void 0;
15798
+ await Promise.all([
15799
+ ...pending.map((session) => session.then((open) => open.close()).catch(() => void 0)),
15800
+ proxy ? proxy.then((listen) => listen.close()).catch(() => void 0) : Promise.resolve()
15801
+ ]);
15802
+ }
15803
+ ensureProxy() {
15804
+ if (this.proxy) return this.proxy;
15805
+ this.proxy = listenMcpUiProxy({
15806
+ sessionFor: (server, dirsKey) => {
15807
+ if (dirsKey && !this.proxyDirs.has(dirsKey)) return Promise.resolve(null);
15808
+ return this.sessionForName(server, this.proxyDirs.get(dirsKey));
15809
+ }
15810
+ }).catch((err) => {
15811
+ this.proxy = void 0;
15812
+ throw err;
15813
+ });
15814
+ return this.proxy;
15815
+ }
15816
+ userEntries(projectDirs) {
15817
+ const out = {};
15818
+ for (const [name, entry] of Object.entries(this.opts.entries(projectDirs))) {
15819
+ if (!name.trim() || isInjectedOrchMcpName(name) || !entry) continue;
15820
+ out[name] = entry;
15821
+ }
15822
+ return out;
15823
+ }
15824
+ async sessionForName(server, projectDirs) {
15825
+ const entry = this.userEntries(projectDirs)[server];
15826
+ if (!entry) return null;
15827
+ const launch = parseMcpServerLaunch(entry);
15828
+ if (!launch) return null;
15829
+ return this.sessionFor(server, launch);
15830
+ }
15831
+ sessionFor(server, launch) {
15832
+ const key = `${server}
15833
+ ${JSON.stringify(launch)}`;
15834
+ const existing = this.sessions.get(key);
15835
+ if (existing) return existing;
15836
+ const open = this.opts.openSession ?? openMcpUiSession;
15837
+ const pending = open(launch).catch((err) => {
15838
+ this.sessions.delete(key);
15839
+ throw err;
15840
+ });
15841
+ this.sessions.set(key, pending);
15842
+ return pending;
15843
+ }
15844
+ async toolsFor(server, launch, session) {
15845
+ const key = `${server}
15846
+ ${JSON.stringify(launch)}`;
15847
+ const cached2 = this.toolLists.get(key);
15848
+ if (cached2 && cached2.key === key && Date.now() - cached2.at < TOOL_TTL_MS) return cached2.tools;
15849
+ const tools = await session.listTools();
15850
+ this.toolLists.set(key, { at: Date.now(), key, tools });
15851
+ return tools;
15852
+ }
15853
+ };
15854
+ }
15855
+ });
15856
+
15282
15857
  // src/agents/types.ts
15283
15858
  function planModeTurnInstruction(planMode) {
15284
15859
  return planMode ? PLAN_MODE_INSTRUCTION : PLAN_MODE_OFF_INSTRUCTION;
@@ -15474,7 +16049,7 @@ function entryDir() {
15474
16049
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
15475
16050
  if (cjsDir) return cjsDir;
15476
16051
  try {
15477
- return (0, import_node_path41.dirname)((0, import_node_url2.fileURLToPath)(import_meta3.url));
16052
+ return (0, import_node_path41.dirname)((0, import_node_url2.fileURLToPath)(import_meta5.url));
15478
16053
  } catch {
15479
16054
  try {
15480
16055
  const req = (0, import_node_module2.createRequire)(process.cwd() + "/");
@@ -15524,7 +16099,7 @@ async function buildClaudeRpcTurnCommand(req) {
15524
16099
  }
15525
16100
  };
15526
16101
  }
15527
- var import_node_fs38, import_node_module2, import_node_path41, import_node_url2, import_meta3;
16102
+ var import_node_fs38, import_node_module2, import_node_path41, import_node_url2, import_meta5;
15528
16103
  var init_claude_host = __esm({
15529
16104
  "src/agents/claude-host.ts"() {
15530
16105
  "use strict";
@@ -15536,7 +16111,7 @@ var init_claude_host = __esm({
15536
16111
  init_pilot_tools();
15537
16112
  init_node_launch();
15538
16113
  init_packaged_runtime();
15539
- import_meta3 = {};
16114
+ import_meta5 = {};
15540
16115
  }
15541
16116
  });
15542
16117
 
@@ -15939,16 +16514,15 @@ function parseIssuesJson(raw) {
15939
16514
  }
15940
16515
  return [];
15941
16516
  }
15942
- var import_node_fs39, import_node_os16, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
16517
+ var import_node_fs39, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
15943
16518
  var init_claude = __esm({
15944
16519
  "src/agents/claude.ts"() {
15945
16520
  "use strict";
15946
16521
  import_node_fs39 = require("fs");
15947
- import_node_os16 = require("os");
15948
16522
  init_run();
15949
16523
  init_app_settings();
15950
16524
  init_claude_mcp();
15951
- init_orch_mcp_isolation();
16525
+ init_host();
15952
16526
  init_error_detail();
15953
16527
  init_message_parts();
15954
16528
  init_injected_mcp();
@@ -16025,7 +16599,7 @@ var init_claude = __esm({
16025
16599
  });
16026
16600
  const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
16027
16601
  const chromeOn = claudeChromeEnabled();
16028
- const userMcpEntries = isOrchestrator ? {} : listUserClaudeMcpServerEntries((0, import_node_os16.homedir)(), [thread.repoPath, thread.worktreePath]);
16602
+ const userMcpEntries = await proxyClaudeUserMcp(isOrchestrator, [thread.repoPath, thread.worktreePath]);
16029
16603
  let allowedTools;
16030
16604
  if (isOrchestrator) {
16031
16605
  allowedTools = [
@@ -16327,7 +16901,7 @@ function unwrapCodexMcpResult(result) {
16327
16901
  return String(result);
16328
16902
  }
16329
16903
  }
16330
- function asRecord4(value) {
16904
+ function asRecord5(value) {
16331
16905
  if (value && typeof value === "object" && !Array.isArray(value)) {
16332
16906
  return value;
16333
16907
  }
@@ -16507,7 +17081,7 @@ var init_codex = __esm({
16507
17081
  }
16508
17082
  if (itemType === "mcp_tool_call" && item.id && item.server && item.tool) {
16509
17083
  const name = `mcp__${item.server}__${item.tool}`;
16510
- const input = asRecord4(item.arguments);
17084
+ const input = asRecord5(item.arguments);
16511
17085
  const events = [
16512
17086
  { type: "tool_use", id: item.id, name, input }
16513
17087
  ];
@@ -16654,7 +17228,7 @@ function thisModuleFile() {
16654
17228
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
16655
17229
  if (cjsDir) return (0, import_node_path43.join)(cjsDir, "cursor-sdk-resolve.js");
16656
17230
  try {
16657
- return (0, import_node_url3.fileURLToPath)(import_meta4.url);
17231
+ return (0, import_node_url3.fileURLToPath)(import_meta6.url);
16658
17232
  } catch {
16659
17233
  return (0, import_node_path43.join)(process.cwd(), "package.json");
16660
17234
  }
@@ -16753,7 +17327,7 @@ function cursorSdkRipgrepCandidate(opts) {
16753
17327
  const nested = (0, import_node_path43.join)(root, "node_modules", pkg, "bin", bin);
16754
17328
  return (0, import_node_fs41.existsSync)(nested) ? nested : null;
16755
17329
  }
16756
- var import_node_fs41, import_node_module3, import_node_path43, import_node_url3, import_meta4, CURSOR_SDK_INSTALL_HINT;
17330
+ var import_node_fs41, import_node_module3, import_node_path43, import_node_url3, import_meta6, CURSOR_SDK_INSTALL_HINT;
16757
17331
  var init_cursor_sdk_resolve = __esm({
16758
17332
  "src/agents/cursor-sdk-resolve.ts"() {
16759
17333
  "use strict";
@@ -16762,7 +17336,7 @@ var init_cursor_sdk_resolve = __esm({
16762
17336
  import_node_path43 = require("path");
16763
17337
  import_node_url3 = require("url");
16764
17338
  init_path();
16765
- import_meta4 = {};
17339
+ import_meta6 = {};
16766
17340
  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
17341
  }
16768
17342
  });
@@ -16812,20 +17386,20 @@ function turnCostUsdFromCursorUsage(before, after) {
16812
17386
  if (!(delta >= 0) || !Number.isFinite(delta)) return void 0;
16813
17387
  return delta / 100;
16814
17388
  }
16815
- function asRecord5(value) {
17389
+ function asRecord6(value) {
16816
17390
  if (value && typeof value === "object" && !Array.isArray(value)) {
16817
17391
  return value;
16818
17392
  }
16819
17393
  return void 0;
16820
17394
  }
16821
17395
  function normalizeCursorToolCall(name, args) {
16822
- const raw = asRecord5(args);
17396
+ const raw = asRecord6(args);
16823
17397
  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);
17398
+ const looksLikeMcp = name === "mcp" || toolName != null && (typeof raw?.providerIdentifier === "string" || asRecord6(raw?.args) != null);
16825
17399
  if (looksLikeMcp && toolName) {
16826
17400
  const providerRaw = typeof raw.providerIdentifier === "string" && raw.providerIdentifier.trim() ? raw.providerIdentifier.trim() : "sideboard";
16827
17401
  const provider = providerRaw.replace(/[^a-zA-Z0-9_-]/g, "_");
16828
- const nested = asRecord5(raw.args);
17402
+ const nested = asRecord6(raw.args);
16829
17403
  return {
16830
17404
  name: `mcp__${provider}__${toolName}`,
16831
17405
  input: nested ?? { toolName }
@@ -16836,7 +17410,7 @@ function normalizeCursorToolCall(name, args) {
16836
17410
  function unwrapCursorToolResult(result) {
16837
17411
  if (result == null) return void 0;
16838
17412
  if (typeof result === "string") return result;
16839
- const rec = asRecord5(result);
17413
+ const rec = asRecord6(result);
16840
17414
  if (!rec) {
16841
17415
  try {
16842
17416
  return JSON.stringify(result);
@@ -16847,18 +17421,18 @@ function unwrapCursorToolResult(result) {
16847
17421
  const collectTexts = (items) => {
16848
17422
  const texts = [];
16849
17423
  for (const item of items) {
16850
- const row2 = asRecord5(item);
17424
+ const row2 = asRecord6(item);
16851
17425
  if (!row2) continue;
16852
17426
  if (typeof row2.text === "string") {
16853
17427
  texts.push(row2.text);
16854
17428
  continue;
16855
17429
  }
16856
- const nested = asRecord5(row2.text);
17430
+ const nested = asRecord6(row2.text);
16857
17431
  if (typeof nested?.text === "string") texts.push(nested.text);
16858
17432
  }
16859
17433
  return texts;
16860
17434
  };
16861
- const value = asRecord5(rec.value);
17435
+ const value = asRecord6(rec.value);
16862
17436
  if (value && Array.isArray(value.content)) {
16863
17437
  const texts = collectTexts(value.content);
16864
17438
  if (texts.length) return texts.join("\n");
@@ -16875,10 +17449,10 @@ function unwrapCursorToolResult(result) {
16875
17449
  }
16876
17450
  function cursorToolResultIsError(status, result) {
16877
17451
  if (status === "error") return true;
16878
- const rec = asRecord5(result);
17452
+ const rec = asRecord6(result);
16879
17453
  if (!rec) return false;
16880
17454
  if (rec.status === "error") return true;
16881
- const value = asRecord5(rec.value);
17455
+ const value = asRecord6(rec.value);
16882
17456
  return value?.isError === true;
16883
17457
  }
16884
17458
  function cursorSdkMessageToEvents(msg) {
@@ -17097,7 +17671,7 @@ function entryDir2() {
17097
17671
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
17098
17672
  if (cjsDir) return cjsDir;
17099
17673
  try {
17100
- return (0, import_node_path45.dirname)((0, import_node_url4.fileURLToPath)(import_meta5.url));
17674
+ return (0, import_node_path45.dirname)((0, import_node_url4.fileURLToPath)(import_meta7.url));
17101
17675
  } catch {
17102
17676
  try {
17103
17677
  const req = (0, import_node_module5.createRequire)(process.cwd() + "/");
@@ -17126,7 +17700,7 @@ function cursorRunnerPath() {
17126
17700
  }
17127
17701
  return candidates[0];
17128
17702
  }
17129
- var import_node_fs43, import_node_module5, import_node_path45, import_node_url4, import_meta5, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
17703
+ var import_node_fs43, import_node_module5, import_node_path45, import_node_url4, import_meta7, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
17130
17704
  var init_cursor = __esm({
17131
17705
  "src/agents/cursor.ts"() {
17132
17706
  "use strict";
@@ -17146,7 +17720,7 @@ var init_cursor = __esm({
17146
17720
  init_packaged_runtime();
17147
17721
  init_turn_input();
17148
17722
  init_cursor_events();
17149
- import_meta5 = {};
17723
+ import_meta7 = {};
17150
17724
  FALLBACK_CURSOR_MODELS = [
17151
17725
  { id: "default", displayName: "Auto" },
17152
17726
  { id: "composer-2.5", displayName: "Composer 2.5" },
@@ -17420,7 +17994,7 @@ var init_opencode_host = __esm({
17420
17994
  });
17421
17995
 
17422
17996
  // src/agents/opencode.ts
17423
- function asRecord6(value) {
17997
+ function asRecord7(value) {
17424
17998
  if (value && typeof value === "object" && !Array.isArray(value)) {
17425
17999
  return value;
17426
18000
  }
@@ -17616,7 +18190,7 @@ var init_opencode = __esm({
17616
18190
  const name = part?.tool ?? part?.name ?? obj.tool ?? obj.name ?? "tool";
17617
18191
  const state = part?.state;
17618
18192
  const input = (state?.input && typeof state.input === "object" ? state.input : void 0) ?? part?.input ?? obj.input;
17619
- const metadata = asRecord6(state?.metadata);
18193
+ const metadata = asRecord7(state?.metadata);
17620
18194
  const id = opencodeToolId(
17621
18195
  name,
17622
18196
  part?.callID ?? part?.id ?? obj.id,
@@ -17651,7 +18225,7 @@ var init_opencode = __esm({
17651
18225
  }
17652
18226
  if (obj.type === "subtask_event") {
17653
18227
  const parentId = str4(obj.childSessionID) ?? str4(obj.childSessionId);
17654
- const part = asRecord6(obj.part);
18228
+ const part = asRecord7(obj.part);
17655
18229
  if (!parentId || !part) return null;
17656
18230
  if (part.type === "text" && str4(part.text)) {
17657
18231
  return withTaskParent(
@@ -17662,8 +18236,8 @@ var init_opencode = __esm({
17662
18236
  if (part.type === "tool" || str4(part.tool)) {
17663
18237
  const name = str4(part.tool) ?? str4(part.name) ?? "tool";
17664
18238
  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);
18239
+ const state = asRecord7(part.state);
18240
+ const input = asRecord7(state?.input) ?? asRecord7(part.input);
17667
18241
  const nested = [
17668
18242
  withEventParentId({ type: "tool_use", id, name, input }, parentId)
17669
18243
  ];
@@ -20081,7 +20655,7 @@ function forkMessageSlice(from, throughIndex) {
20081
20655
  function buildForkTranscriptAttachment(baseTitle, messages) {
20082
20656
  const title = baseTitle || "Chat";
20083
20657
  return {
20084
- id: (0, import_node_crypto11.randomUUID)(),
20658
+ id: (0, import_node_crypto13.randomUUID)(),
20085
20659
  name: `Transcript of ${title}.md`,
20086
20660
  kind: "transcript",
20087
20661
  content: formatTranscriptMarkdown(title, messages)
@@ -20140,11 +20714,11 @@ function forkChatTab(input) {
20140
20714
  }
20141
20715
  return tab;
20142
20716
  }
20143
- var import_node_crypto11;
20717
+ var import_node_crypto13;
20144
20718
  var init_chat_tabs = __esm({
20145
20719
  "src/threads/chat-tabs.ts"() {
20146
20720
  "use strict";
20147
- import_node_crypto11 = require("crypto");
20721
+ import_node_crypto13 = require("crypto");
20148
20722
  init_context_compact();
20149
20723
  init_teams();
20150
20724
  init_worktree_labels();
@@ -20443,7 +21017,7 @@ function buildReviewRequestAttachment(content, opts) {
20443
21017
  const path2 = opts?.path ?? REVIEW_SKILL_PATH;
20444
21018
  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
21019
  return {
20446
- id: (0, import_node_crypto12.randomUUID)(),
21020
+ id: (0, import_node_crypto14.randomUUID)(),
20447
21021
  name,
20448
21022
  kind: "file",
20449
21023
  path: path2,
@@ -20499,11 +21073,11 @@ async function requestReview(threadRef, send3) {
20499
21073
  const started = await send3(tab.id, REVIEW_REQUEST_PREFILL);
20500
21074
  return { tab: started, from };
20501
21075
  }
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;
21076
+ 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
21077
  var init_request_review = __esm({
20504
21078
  "src/review/request-review.ts"() {
20505
21079
  "use strict";
20506
- import_node_crypto12 = require("crypto");
21080
+ import_node_crypto14 = require("crypto");
20507
21081
  import_node_fs49 = require("fs");
20508
21082
  import_node_path50 = require("path");
20509
21083
  init_global_workspace();
@@ -22052,8 +22626,28 @@ var init_clone_repo = __esm({
22052
22626
  }
22053
22627
  });
22054
22628
 
22629
+ // src/mcp-ui/model-context.ts
22630
+ function stashMcpAppModelContext(threadId, text7) {
22631
+ const body = text7.trim().slice(0, 8e3);
22632
+ if (!body) return;
22633
+ updateThread(threadId, { mcpAppModelContext: body });
22634
+ }
22635
+ function takeMcpAppModelContext(threadId) {
22636
+ const thread = readThread(threadId);
22637
+ const text7 = thread?.mcpAppModelContext?.trim();
22638
+ if (!text7) return void 0;
22639
+ updateThread(threadId, { mcpAppModelContext: null });
22640
+ return text7;
22641
+ }
22642
+ var init_model_context = __esm({
22643
+ "src/mcp-ui/model-context.ts"() {
22644
+ "use strict";
22645
+ init_thread_store();
22646
+ }
22647
+ });
22648
+
22055
22649
  // src/plan/plan-present.ts
22056
- function asRecord7(v) {
22650
+ function asRecord8(v) {
22057
22651
  return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
22058
22652
  }
22059
22653
  function isPresentPlanToolName(name) {
@@ -22072,7 +22666,7 @@ function extractPresentedPlan(parts) {
22072
22666
  for (let i = parts.length - 1; i >= 0; i--) {
22073
22667
  const p = parts[i];
22074
22668
  if (p.type !== "tool" || !isPresentPlanToolName(p.name)) continue;
22075
- const input = asRecord7(p.input) ?? {};
22669
+ const input = asRecord8(p.input) ?? {};
22076
22670
  const content = typeof input.content === "string" ? input.content : typeof input.plan === "string" ? input.plan : typeof input.markdown === "string" ? input.markdown : "";
22077
22671
  if (!content.trim()) continue;
22078
22672
  const title = typeof input.title === "string" && input.title.trim() ? input.title.trim() : "Plan";
@@ -22527,7 +23121,7 @@ function isUnclaimedSteerRequest(req) {
22527
23121
  }
22528
23122
  function nextSteerRequest(existing) {
22529
23123
  if (existing?.requestId && !existing.claimedAt) return existing;
22530
- return { requestId: (0, import_node_crypto13.randomUUID)(), requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
23124
+ return { requestId: (0, import_node_crypto15.randomUUID)(), requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
22531
23125
  }
22532
23126
  function claimSteerRequest(threadId) {
22533
23127
  const pending = readThread(threadId)?.steerRequest;
@@ -22536,11 +23130,11 @@ function claimSteerRequest(threadId) {
22536
23130
  steerRequest: { ...pending, claimedAt: (/* @__PURE__ */ new Date()).toISOString() }
22537
23131
  });
22538
23132
  }
22539
- var import_node_crypto13;
23133
+ var import_node_crypto15;
22540
23134
  var init_steer_request = __esm({
22541
23135
  "src/orchestrator/steer-request.ts"() {
22542
23136
  "use strict";
22543
- import_node_crypto13 = require("crypto");
23137
+ import_node_crypto15 = require("crypto");
22544
23138
  init_thread_store();
22545
23139
  }
22546
23140
  });
@@ -22785,8 +23379,8 @@ function resolveAbleTimeOAuthHost(requested, saved) {
22785
23379
  return fromSaved || void 0;
22786
23380
  }
22787
23381
  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");
23382
+ const verifier = (0, import_node_crypto16.randomBytes)(32).toString("base64url");
23383
+ const challenge = (0, import_node_crypto16.createHash)("sha256").update(verifier).digest("base64url");
22790
23384
  return { verifier, challenge };
22791
23385
  }
22792
23386
  function ableTimeOAuthAuthorizeUrl(clientId, state, codeChallenge, host) {
@@ -22891,7 +23485,7 @@ async function startAbleTimeOAuth(opts) {
22891
23485
  opts?.host,
22892
23486
  loadAppSettings().integrations.abletimeHost
22893
23487
  );
22894
- const state = (0, import_node_crypto14.randomBytes)(16).toString("hex");
23488
+ const state = (0, import_node_crypto16.randomBytes)(16).toString("hex");
22895
23489
  const pkce = createAbleTimePkce();
22896
23490
  const authorizeUrl = ableTimeOAuthAuthorizeUrl(clientId, state, pkce.challenge, host);
22897
23491
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
@@ -22904,7 +23498,7 @@ async function startAbleTimeOAuth(opts) {
22904
23498
  if (err) reject(err);
22905
23499
  else resolve3(value);
22906
23500
  };
22907
- const server = (0, import_node_http.createServer)((req, res) => {
23501
+ const server = (0, import_node_http2.createServer)((req, res) => {
22908
23502
  try {
22909
23503
  const url = new URL(req.url || "/", `http://127.0.0.1:${ABLETIME_OAUTH_PORT}`);
22910
23504
  if (url.pathname !== "/callback") {
@@ -22991,12 +23585,12 @@ async function startAbleTimeOAuth(opts) {
22991
23585
  return loadAppSettings();
22992
23586
  }
22993
23587
  }
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;
23588
+ 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
23589
  var init_abletime_oauth = __esm({
22996
23590
  "src/integrations/abletime-oauth.ts"() {
22997
23591
  "use strict";
22998
- import_node_crypto14 = require("crypto");
22999
- import_node_http = require("http");
23592
+ import_node_crypto16 = require("crypto");
23593
+ import_node_http2 = require("http");
23000
23594
  init_fetch();
23001
23595
  init_app_settings();
23002
23596
  init_abletime_mcp();
@@ -23029,7 +23623,7 @@ __export(abletime_rest_exports, {
23029
23623
  looksLikeAbleTimeUlid: () => looksLikeAbleTimeUlid,
23030
23624
  textFromAbleTimeDoc: () => textFromAbleTimeDoc
23031
23625
  });
23032
- function asRecord8(value) {
23626
+ function asRecord9(value) {
23033
23627
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
23034
23628
  }
23035
23629
  function asString(value) {
@@ -23037,7 +23631,7 @@ function asString(value) {
23037
23631
  }
23038
23632
  function asList(value) {
23039
23633
  if (Array.isArray(value)) return value;
23040
- const record = asRecord8(value);
23634
+ const record = asRecord9(value);
23041
23635
  if (!record) return [];
23042
23636
  for (const key of ["data", "tasks", "projects", "users", "items", "results"]) {
23043
23637
  if (Array.isArray(record[key])) return record[key];
@@ -23054,12 +23648,12 @@ function ableTimeDocFromText(text7) {
23054
23648
  }
23055
23649
  function textFromAbleTimeDoc(value) {
23056
23650
  if (typeof value === "string") return value.trim();
23057
- const record = asRecord8(value);
23651
+ const record = asRecord9(value);
23058
23652
  if (!record) return "";
23059
23653
  if (typeof record.text === "string" && record.text.trim()) return record.text.trim();
23060
23654
  const chunks = [];
23061
23655
  const walk = (node) => {
23062
- const rec = asRecord8(node);
23656
+ const rec = asRecord9(node);
23063
23657
  if (!rec) return;
23064
23658
  if (typeof rec.text === "string" && rec.text) chunks.push(rec.text);
23065
23659
  if (Array.isArray(rec.content)) rec.content.forEach(walk);
@@ -23121,7 +23715,7 @@ async function restPages(path2, opts, maxPages = 20) {
23121
23715
  query: { ...opts.query, ...cursor ? { cursor } : {} }
23122
23716
  });
23123
23717
  items.push(...asList(raw));
23124
- const page = asRecord8(asRecord8(raw)?.page);
23718
+ const page = asRecord9(asRecord9(raw)?.page);
23125
23719
  const next = page ? asString(page.nextCursor) : "";
23126
23720
  if (!next) break;
23127
23721
  cursor = next;
@@ -23129,7 +23723,7 @@ async function restPages(path2, opts, maxPages = 20) {
23129
23723
  return items;
23130
23724
  }
23131
23725
  function userDisplayName(raw) {
23132
- const rec = asRecord8(raw);
23726
+ const rec = asRecord9(raw);
23133
23727
  if (!rec) return null;
23134
23728
  const id = asString(rec.userId) || asString(rec.id);
23135
23729
  const name = [asString(rec.firstName), asString(rec.lastName)].filter(Boolean).join(" ") || asString(rec.username) || asString(rec.email) || asString(rec.name);
@@ -23137,7 +23731,7 @@ function userDisplayName(raw) {
23137
23731
  return { id: id || void 0, name };
23138
23732
  }
23139
23733
  function restTaskToRaw(raw, users) {
23140
- const rec = asRecord8(raw);
23734
+ const rec = asRecord9(raw);
23141
23735
  if (!rec) return null;
23142
23736
  const id = asString(rec.timeflowTaskId) || asString(rec.taskId) || asString(rec.id);
23143
23737
  const identifier = asString(rec.taskRef) || asString(rec.reference) || id;
@@ -23145,7 +23739,7 @@ function restTaskToRaw(raw, users) {
23145
23739
  if (!id && !identifier && !title) return null;
23146
23740
  const assignee = users.get(asString(rec.assignedUserId));
23147
23741
  const comments = asList(rec.comments).map((item) => {
23148
- const comment = asRecord8(item) ?? {};
23742
+ const comment = asRecord9(item) ?? {};
23149
23743
  const user = userDisplayName(comment);
23150
23744
  return {
23151
23745
  id: asString(comment.commentId) || asString(comment.id),
@@ -23175,13 +23769,13 @@ function restTaskToRaw(raw, users) {
23175
23769
  };
23176
23770
  }
23177
23771
  function restProjectToRaw(raw) {
23178
- const rec = asRecord8(raw);
23772
+ const rec = asRecord9(raw);
23179
23773
  if (!rec) return null;
23180
23774
  const id = asString(rec.projectId) || asString(rec.id);
23181
23775
  const name = asString(rec.projectName) || asString(rec.name);
23182
23776
  if (!id && !name) return null;
23183
23777
  const categories = asList(rec.categories).map((item) => {
23184
- const cat = asRecord8(item) ?? {};
23778
+ const cat = asRecord9(item) ?? {};
23185
23779
  return {
23186
23780
  id: asString(cat.projectCategoryId) || asString(cat.categoryId) || asString(cat.id),
23187
23781
  name: asString(cat.categoryName) || asString(cat.name)
@@ -23193,7 +23787,7 @@ async function listUsers(opts) {
23193
23787
  const users = /* @__PURE__ */ new Map();
23194
23788
  for (const item of await restPages("/users", opts).catch(() => [])) {
23195
23789
  const user = userDisplayName(item);
23196
- const rec = asRecord8(item);
23790
+ const rec = asRecord9(item);
23197
23791
  const id = user?.id || (rec ? asString(rec.userId) : "");
23198
23792
  if (user && id) users.set(id, user);
23199
23793
  }
@@ -23319,10 +23913,10 @@ async function callAbleTimeRestTool(name, args = {}, opts) {
23319
23913
  if (!project) throw new Error("AbleTime has no projects to create a task in");
23320
23914
  const categories = asList(project.categories);
23321
23915
  const wantedCategory = firstStringArg(args, ["category_id", "categoryId", "category"]);
23322
- const category = categories.map((item) => asRecord8(item)).find((item) => {
23916
+ const category = categories.map((item) => asRecord9(item)).find((item) => {
23323
23917
  if (!item || !wantedCategory) return false;
23324
23918
  return asString(item.id).toLowerCase() === wantedCategory.toLowerCase() || asString(item.name).toLowerCase() === wantedCategory.toLowerCase();
23325
- }) ?? asRecord8(categories[0]);
23919
+ }) ?? asRecord9(categories[0]);
23326
23920
  const parentArg = firstStringArg(args, ["parent", "parent_id", "parentId", "related_task_id"]);
23327
23921
  const parentTaskId = parentArg ? await resolveRestParentTaskId(parentArg, requestOpts) : void 0;
23328
23922
  const created = await abletimeRestRequest("/tasks", {
@@ -23336,7 +23930,7 @@ async function callAbleTimeRestTool(name, args = {}, opts) {
23336
23930
  }
23337
23931
  });
23338
23932
  const state = firstStringArg(args, ["state"]);
23339
- const createdId = asString(asRecord8(created)?.timeflowTaskId);
23933
+ const createdId = asString(asRecord9(created)?.timeflowTaskId);
23340
23934
  const description = asString(args.description);
23341
23935
  if (createdId && description) {
23342
23936
  await abletimeRestRequest(`/tasks/${encodeURIComponent(createdId)}`, {
@@ -23409,7 +24003,7 @@ async function callAbleTimeRestTool(name, args = {}, opts) {
23409
24003
  method: "POST",
23410
24004
  body: { content: ableTimeDocFromText(body) }
23411
24005
  });
23412
- const rec = asRecord8(created) ?? {};
24006
+ const rec = asRecord9(created) ?? {};
23413
24007
  return {
23414
24008
  id: asString(rec.commentId) || void 0,
23415
24009
  body: textFromAbleTimeDoc(rec.content) || body
@@ -23504,7 +24098,7 @@ function rewriteAbleTimeError(message) {
23504
24098
  }
23505
24099
  return message;
23506
24100
  }
23507
- function asRecord9(value) {
24101
+ function asRecord10(value) {
23508
24102
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
23509
24103
  }
23510
24104
  function parseSseJson(body) {
@@ -23526,12 +24120,12 @@ function parseSseJson(body) {
23526
24120
  }
23527
24121
  }
23528
24122
  function unwrapToolResult(result) {
23529
- const record = asRecord9(result);
24123
+ const record = asRecord10(result);
23530
24124
  if (!record) return result;
23531
24125
  const content = record.content;
23532
24126
  if (Array.isArray(content)) {
23533
24127
  const texts = content.map((part) => {
23534
- const item = asRecord9(part);
24128
+ const item = asRecord10(part);
23535
24129
  if (!item) return "";
23536
24130
  return typeof item.text === "string" ? item.text : "";
23537
24131
  }).filter(Boolean);
@@ -23553,7 +24147,7 @@ function unwrapToolResult(result) {
23553
24147
  return result;
23554
24148
  }
23555
24149
  function textsFromUnknown(value) {
23556
- const record = asRecord9(value);
24150
+ const record = asRecord10(value);
23557
24151
  if (!record) return "";
23558
24152
  if (typeof record.message === "string") return record.message;
23559
24153
  if (typeof record.error === "string") return record.error;
@@ -23600,7 +24194,7 @@ async function abletimeMcpRequest(method, params, opts) {
23600
24194
  return parseSseJson(body);
23601
24195
  }
23602
24196
  })();
23603
- const rpc = asRecord9(parsed);
24197
+ const rpc = asRecord10(parsed);
23604
24198
  if (rpc?.error) {
23605
24199
  const detail = typeof rpc.error.message === "string" && rpc.error.message.trim() ? rpc.error.message : `AbleTime MCP error ${rpc.error.code ?? ""}`.trim();
23606
24200
  throw new Error(rewriteAbleTimeError(detail));
@@ -23755,7 +24349,7 @@ function uniqueAttachmentFileName(dir, originalName) {
23755
24349
  const candidate = `${stem}-${i}${ext}`;
23756
24350
  if (!(0, import_node_fs55.existsSync)((0, import_node_path56.join)(dir, candidate))) return candidate;
23757
24351
  }
23758
- return `${stem}-${(0, import_node_crypto15.randomUUID)()}${ext}`;
24352
+ return `${stem}-${(0, import_node_crypto17.randomUUID)()}${ext}`;
23759
24353
  }
23760
24354
  function extractHttpUrls(markdown) {
23761
24355
  const text7 = markdown ?? "";
@@ -23861,13 +24455,13 @@ async function downloadAuthenticatedFile(opts) {
23861
24455
  ...contentType ? { contentType } : {}
23862
24456
  };
23863
24457
  }
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;
24458
+ 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
24459
  var init_issue_attachments = __esm({
23866
24460
  "src/integrations/issue-attachments.ts"() {
23867
24461
  "use strict";
23868
24462
  import_node_fs55 = require("fs");
23869
24463
  import_node_path56 = require("path");
23870
- import_node_crypto15 = require("crypto");
24464
+ import_node_crypto17 = require("crypto");
23871
24465
  init_fetch();
23872
24466
  init_workspace_scratch();
23873
24467
  ISSUE_ATTACHMENT_MAX_BYTES = 25 * 1024 * 1024;
@@ -23892,7 +24486,7 @@ var init_issue_attachments = __esm({
23892
24486
  });
23893
24487
 
23894
24488
  // src/integrations/abletime-attachments.ts
23895
- function asRecord10(value) {
24489
+ function asRecord11(value) {
23896
24490
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
23897
24491
  }
23898
24492
  function asString2(value) {
@@ -23900,7 +24494,7 @@ function asString2(value) {
23900
24494
  }
23901
24495
  function asList2(value) {
23902
24496
  if (Array.isArray(value)) return value;
23903
- const record = asRecord10(value);
24497
+ const record = asRecord11(value);
23904
24498
  if (!record) return [];
23905
24499
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
23906
24500
  if (Array.isArray(record[key])) return record[key];
@@ -23919,9 +24513,9 @@ function filesFromAbleTimeDoc(value) {
23919
24513
  const out = [];
23920
24514
  const seen = /* @__PURE__ */ new Set();
23921
24515
  const walk = (node) => {
23922
- const rec = asRecord10(node);
24516
+ const rec = asRecord11(node);
23923
24517
  if (!rec) return;
23924
- const attrs = asRecord10(rec.attrs) ?? rec;
24518
+ const attrs = asRecord11(rec.attrs) ?? rec;
23925
24519
  const url = asString2(attrs.src) || asString2(attrs.href) || asString2(attrs.url) || asString2(attrs.fileUrl) || asString2(attrs.downloadUrl);
23926
24520
  if (url && !seen.has(url)) {
23927
24521
  seen.add(url);
@@ -23939,7 +24533,7 @@ function filesFromAbleTimeDoc(value) {
23939
24533
  function listedAbleTimeFiles(raw) {
23940
24534
  const out = [];
23941
24535
  for (const item of asList2(raw)) {
23942
- const rec = asRecord10(item);
24536
+ const rec = asRecord11(item);
23943
24537
  if (!rec) continue;
23944
24538
  const url = firstString(rec, ["url", "href", "src", "fileUrl", "downloadUrl", "permalink"]);
23945
24539
  if (!url) continue;
@@ -23959,7 +24553,7 @@ function mapAbleTimeAttachments(record) {
23959
24553
  ...filesFromAbleTimeDoc(record.description),
23960
24554
  ...filesFromAbleTimeDoc(record.content),
23961
24555
  ...asList2(record.comments).flatMap((item) => {
23962
- const rec = asRecord10(item);
24556
+ const rec = asRecord11(item);
23963
24557
  return rec ? filesFromAbleTimeDoc(rec.content ?? rec.body) : [];
23964
24558
  })
23965
24559
  ].map((file) => ({
@@ -24001,7 +24595,7 @@ async function resolveAbleTimeAttachmentUrl(id, opts) {
24001
24595
  const json = await res.json().catch(() => null);
24002
24596
  const rec = json && typeof json === "object" && !Array.isArray(json) ? json.data && typeof json.data === "object" ? json.data : json : null;
24003
24597
  if (!rec) continue;
24004
- const url = firstString(rec, ["url", "href", "src", "fileUrl", "downloadUrl"]) || firstString(asRecord10(rec.file), ["url", "href"]);
24598
+ const url = firstString(rec, ["url", "href", "src", "fileUrl", "downloadUrl"]) || firstString(asRecord11(rec.file), ["url", "href"]);
24005
24599
  if (url) {
24006
24600
  return {
24007
24601
  url,
@@ -24043,7 +24637,7 @@ var init_abletime_attachments = __esm({
24043
24637
  });
24044
24638
 
24045
24639
  // src/integrations/abletime-projects.ts
24046
- function asRecord11(value) {
24640
+ function asRecord12(value) {
24047
24641
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
24048
24642
  }
24049
24643
  function firstString2(record, keys) {
@@ -24056,7 +24650,7 @@ function firstString2(record, keys) {
24056
24650
  }
24057
24651
  function asList3(value) {
24058
24652
  if (Array.isArray(value)) return value;
24059
- const record = asRecord11(value);
24653
+ const record = asRecord12(value);
24060
24654
  if (!record) return [];
24061
24655
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
24062
24656
  if (Array.isArray(record[key])) return record[key];
@@ -24064,7 +24658,7 @@ function asList3(value) {
24064
24658
  return [];
24065
24659
  }
24066
24660
  function ableTimeProjectFields(nested) {
24067
- const projectRecord = asRecord11(nested.project);
24661
+ const projectRecord = asRecord12(nested.project);
24068
24662
  const projectId = firstString2(nested, ["project_id", "projectId"]) || firstString2(projectRecord, ["id"]) || void 0;
24069
24663
  const projectName = firstString2(nested, ["project_name", "projectName"]) || firstString2(projectRecord, ["name", "title"]) || void 0;
24070
24664
  if (!projectId && !projectName) return {};
@@ -24077,13 +24671,13 @@ function ableTimeProjectFields(nested) {
24077
24671
  };
24078
24672
  }
24079
24673
  function mapAbleTimeProject(raw) {
24080
- const record = asRecord11(raw);
24674
+ const record = asRecord12(raw);
24081
24675
  if (!record) return null;
24082
24676
  const id = firstString2(record, ["id", "project_id", "projectId"]);
24083
24677
  const name = firstString2(record, ["name", "title", "projectName"]);
24084
24678
  if (!id && !name) return null;
24085
24679
  const categories = asList3(record.categories ?? record.category).map((item) => {
24086
- const rec = asRecord11(item);
24680
+ const rec = asRecord12(item);
24087
24681
  if (!rec) return null;
24088
24682
  const categoryId = firstString2(rec, ["id", "category_id", "projectCategoryId", "categoryId"]);
24089
24683
  const categoryName = firstString2(rec, ["name", "title", "categoryName"]);
@@ -24119,7 +24713,7 @@ __export(abletime_exports, {
24119
24713
  updateAbleTimeTask: () => updateAbleTimeTask,
24120
24714
  verifyAbleTimeConnection: () => verifyAbleTimeConnection
24121
24715
  });
24122
- function asRecord12(value) {
24716
+ function asRecord13(value) {
24123
24717
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
24124
24718
  }
24125
24719
  function asString3(value) {
@@ -24136,14 +24730,14 @@ function firstString3(record, keys) {
24136
24730
  function firstRecord(record, keys) {
24137
24731
  if (!record) return null;
24138
24732
  for (const key of keys) {
24139
- const value = asRecord12(record[key]);
24733
+ const value = asRecord13(record[key]);
24140
24734
  if (value) return value;
24141
24735
  }
24142
24736
  return null;
24143
24737
  }
24144
24738
  function asList4(value) {
24145
24739
  if (Array.isArray(value)) return value;
24146
- const record = asRecord12(value);
24740
+ const record = asRecord13(value);
24147
24741
  if (!record) return [];
24148
24742
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
24149
24743
  if (Array.isArray(record[key])) return record[key];
@@ -24155,18 +24749,18 @@ function labelsOf(record) {
24155
24749
  if (!Array.isArray(raw)) return [];
24156
24750
  return raw.map((item) => {
24157
24751
  if (typeof item === "string") return item.trim();
24158
- const rec = asRecord12(item);
24752
+ const rec = asRecord13(item);
24159
24753
  return rec ? firstString3(rec, ["name", "title", "label"]) : "";
24160
24754
  }).filter(Boolean);
24161
24755
  }
24162
24756
  function commentsOf(record) {
24163
24757
  const out = [];
24164
24758
  for (const item of asList4(record.comments ?? record.notes ?? record.discussion)) {
24165
- const rec = asRecord12(item);
24759
+ const rec = asRecord13(item);
24166
24760
  if (!rec) continue;
24167
24761
  const body = firstString3(rec, ["body", "text", "comment", "message", "contentSanitized"]) || textFromAbleTimeDoc(rec.content);
24168
24762
  if (!body) continue;
24169
- const user = firstString3(asRecord12(rec.user) ?? asRecord12(rec.author), ["name", "display_name", "username"]) || firstString3(rec, ["user_name", "author", "username"]);
24763
+ const user = firstString3(asRecord13(rec.user) ?? asRecord13(rec.author), ["name", "display_name", "username"]) || firstString3(rec, ["user_name", "author", "username"]);
24170
24764
  const id = firstString3(rec, ["id", "comment_id", "commentId"]);
24171
24765
  const url = firstString3(rec, ["url", "permalink"]);
24172
24766
  const createdAt = firstString3(rec, ["created_at", "createdAt", "created"]);
@@ -24193,7 +24787,7 @@ function taskUrl(task, host) {
24193
24787
  return `${base}/tasks/${slug}`;
24194
24788
  }
24195
24789
  function mapAbleTimeTask(raw, host) {
24196
- const record = asRecord12(raw);
24790
+ const record = asRecord13(raw);
24197
24791
  if (!record) return null;
24198
24792
  const nested = firstRecord(record, ["task", "data"]) ?? record;
24199
24793
  const id = firstString3(nested, ["id", "task_id", "taskId", "timeflowTaskId"]);
@@ -24211,7 +24805,7 @@ function mapAbleTimeTask(raw, host) {
24211
24805
  description: firstString3(nested, ["description", "body", "details", "descriptionSanitized"]) || textFromAbleTimeDoc(nested.description) || void 0,
24212
24806
  state: firstString3(nested, ["state", "board_state", "boardState", "status", "taskState"]) || void 0,
24213
24807
  ...ableTimeProjectFields(nested),
24214
- categoryId: firstString3(nested, ["category_id", "categoryId", "projectCategoryId"]) || firstString3(asRecord12(nested.category), ["id"]) || void 0,
24808
+ categoryId: firstString3(nested, ["category_id", "categoryId", "projectCategoryId"]) || firstString3(asRecord13(nested.category), ["id"]) || void 0,
24215
24809
  assignee: assigneeOf(nested),
24216
24810
  labels: labelsOf(nested),
24217
24811
  comments: commentsOf(nested),
@@ -24261,7 +24855,7 @@ function isOpenTask(task) {
24261
24855
  }
24262
24856
  async function getAbleTimeOrientation(opts) {
24263
24857
  const raw = await callAbleTimeTool("orientation", {}, opts);
24264
- const record = asRecord12(raw);
24858
+ const record = asRecord13(raw);
24265
24859
  const viewerRec = firstRecord(record, ["viewer", "principal", "user", "actor", "me", "person"]) ?? record;
24266
24860
  const viewer = {
24267
24861
  id: firstString3(viewerRec, ["id", "user_id"]) || void 0,
@@ -24340,7 +24934,7 @@ async function commentAbleTimeTask(input, opts) {
24340
24934
  { id, task_id: id, task: id, body, comment: body, text: body },
24341
24935
  opts
24342
24936
  );
24343
- const rec = asRecord12(raw);
24937
+ const rec = asRecord13(raw);
24344
24938
  return {
24345
24939
  id: rec ? firstString3(rec, ["id", "comment_id"]) || void 0 : void 0,
24346
24940
  body: rec ? firstString3(rec, ["body", "text", "comment"]) || body : body
@@ -25493,7 +26087,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
25493
26087
  ...instructions
25494
26088
  ].join("\n");
25495
26089
  return {
25496
- id: (0, import_node_crypto16.randomUUID)(),
26090
+ id: (0, import_node_crypto18.randomUUID)(),
25497
26091
  name: orch ? "Orchestration quota handoff.md" : "Quota handoff.md",
25498
26092
  kind: "transcript",
25499
26093
  content: body
@@ -25525,11 +26119,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText, opts) {
25525
26119
  }
25526
26120
  return updateThread(tab.id, patch);
25527
26121
  }
25528
- var import_node_crypto16, QUOTA_CONTINUE_PROMPT, QUOTA_CONTINUE_ACCOUNT_PROMPT, QUOTA_RESUME_PROMPT;
26122
+ var import_node_crypto18, QUOTA_CONTINUE_PROMPT, QUOTA_CONTINUE_ACCOUNT_PROMPT, QUOTA_RESUME_PROMPT;
25529
26123
  var init_quota_failover = __esm({
25530
26124
  "src/orchestrator/quota-failover.ts"() {
25531
26125
  "use strict";
25532
- import_node_crypto16 = require("crypto");
26126
+ import_node_crypto18 = require("crypto");
25533
26127
  init_session_quota();
25534
26128
  init_app_settings();
25535
26129
  init_managed_accounts();
@@ -27605,7 +28199,7 @@ function compiledModuleDir() {
27605
28199
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
27606
28200
  if (cjsDir) return cjsDir;
27607
28201
  try {
27608
- const url = import_meta6.url;
28202
+ const url = import_meta8.url;
27609
28203
  if (typeof url === "string" && url.length > 0) {
27610
28204
  return (0, import_node_path62.dirname)((0, import_node_url5.fileURLToPath)(url));
27611
28205
  }
@@ -27644,7 +28238,7 @@ function formatDetachedJobInvoke(scriptPath) {
27644
28238
  if (resolved) return `node ${JSON.stringify(resolved)}`;
27645
28239
  return "node scripts/detached-job.cjs";
27646
28240
  }
27647
- var import_node_fs63, import_node_path62, import_node_url5, import_meta6, DETACHED_JOB_FILENAMES;
28241
+ var import_node_fs63, import_node_path62, import_node_url5, import_meta8, DETACHED_JOB_FILENAMES;
27648
28242
  var init_detached_job_path = __esm({
27649
28243
  "src/skills/detached-job-path.ts"() {
27650
28244
  "use strict";
@@ -27652,7 +28246,7 @@ var init_detached_job_path = __esm({
27652
28246
  import_node_path62 = require("path");
27653
28247
  import_node_url5 = require("url");
27654
28248
  init_packaged_runtime();
27655
- import_meta6 = {};
28249
+ import_meta8 = {};
27656
28250
  DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
27657
28251
  }
27658
28252
  });
@@ -28052,7 +28646,7 @@ function normalizeServiceOrigin(raw, fallback) {
28052
28646
  return fallback;
28053
28647
  }
28054
28648
  }
28055
- function asRecord13(value) {
28649
+ function asRecord14(value) {
28056
28650
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
28057
28651
  }
28058
28652
  function asString4(value) {
@@ -28085,7 +28679,7 @@ async function authorizedGet(url, token) {
28085
28679
  });
28086
28680
  const body = await readJson2(res);
28087
28681
  if (!res.ok) {
28088
- const record = asRecord13(body);
28682
+ const record = asRecord14(body);
28089
28683
  const message = firstString4(record, ["message", "error", "error_description", "detail"]) || (typeof body === "string" ? body.trim().slice(0, 160) : "");
28090
28684
  throw new Error(
28091
28685
  message ? `${res.status}: ${message}` : `Request failed (${res.status})`
@@ -28099,28 +28693,28 @@ async function verifyOptionalService(input) {
28099
28693
  const spec = optionalServiceSpec(input.id);
28100
28694
  try {
28101
28695
  if (input.id === "vercel") {
28102
- const body2 = asRecord13(await authorizedGet("https://api.vercel.com/v2/user", token));
28103
- const user = asRecord13(body2?.user) ?? body2;
28696
+ const body2 = asRecord14(await authorizedGet("https://api.vercel.com/v2/user", token));
28697
+ const user = asRecord14(body2?.user) ?? body2;
28104
28698
  const name2 = firstString4(user, ["username", "name", "email"]) || "Vercel user";
28105
28699
  return { viewerName: name2 };
28106
28700
  }
28107
28701
  if (input.id === "supabase") {
28108
28702
  const body2 = await authorizedGet("https://api.supabase.com/v1/organizations", token);
28109
28703
  const list2 = Array.isArray(body2) ? body2 : [];
28110
- const first2 = asRecord13(list2[0]);
28704
+ const first2 = asRecord14(list2[0]);
28111
28705
  const name2 = firstString4(first2, ["name", "slug"]) || "Supabase";
28112
28706
  return { viewerName: list2.length > 1 ? `${name2} +${list2.length - 1}` : name2 };
28113
28707
  }
28114
28708
  if (input.id === "posthog") {
28115
28709
  const host2 = normalizeServiceOrigin(input.host, spec.defaultHost ?? "https://us.posthog.com");
28116
- const body2 = asRecord13(await authorizedGet(`${host2}/api/users/@me/`, token));
28710
+ const body2 = asRecord14(await authorizedGet(`${host2}/api/users/@me/`, token));
28117
28711
  const name2 = firstString4(body2, ["email", "distinct_id"]) || [firstString4(body2, ["first_name"]), firstString4(body2, ["last_name"])].filter(Boolean).join(" ") || "PostHog user";
28118
28712
  return { viewerName: name2, host: host2 };
28119
28713
  }
28120
28714
  const host = normalizeServiceOrigin(input.host, spec.defaultHost ?? "https://sentry.io");
28121
28715
  const body = await authorizedGet(`${host}/api/0/organizations/`, token);
28122
28716
  const list = Array.isArray(body) ? body : [];
28123
- const first = asRecord13(list[0]);
28717
+ const first = asRecord14(list[0]);
28124
28718
  const name = firstString4(first, ["slug", "name"]) || "Sentry";
28125
28719
  return { viewerName: list.length > 1 ? `${name} +${list.length - 1}` : name, host };
28126
28720
  } catch (err) {
@@ -28994,10 +29588,10 @@ function collectActiveRunPorts(runs) {
28994
29588
  return [...ports];
28995
29589
  }
28996
29590
  function getOrchestrator() {
28997
- if (!singleton) {
28998
- singleton = new Orchestrator();
29591
+ if (!singleton2) {
29592
+ singleton2 = new Orchestrator();
28999
29593
  }
29000
- return singleton;
29594
+ return singleton2;
29001
29595
  }
29002
29596
  async function startOrchestration(opts) {
29003
29597
  const repoPath = opts.repoPath?.trim();
@@ -29057,7 +29651,7 @@ async function startOrchestration(opts) {
29057
29651
  }
29058
29652
  return updated;
29059
29653
  }
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;
29654
+ 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
29655
  var init_orchestrator = __esm({
29062
29656
  "src/orchestrator/orchestrator.ts"() {
29063
29657
  "use strict";
@@ -29065,7 +29659,7 @@ var init_orchestrator = __esm({
29065
29659
  init_injected_notices();
29066
29660
  init_task_state();
29067
29661
  init_wait_for_turn();
29068
- import_node_crypto17 = require("crypto");
29662
+ import_node_crypto19 = require("crypto");
29069
29663
  import_node_fs67 = require("fs");
29070
29664
  init_error_detail();
29071
29665
  init_run();
@@ -29091,6 +29685,7 @@ var init_orchestrator = __esm({
29091
29685
  init_usage();
29092
29686
  init_thread_store();
29093
29687
  init_desktop_host();
29688
+ init_model_context();
29094
29689
  init_agent_block();
29095
29690
  init_child_halt();
29096
29691
  init_stop_reason();
@@ -30089,6 +30684,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
30089
30684
  orchestrationReminder,
30090
30685
  prGateDirective,
30091
30686
  injectedNoticeContext,
30687
+ takeMcpAppModelContext(threadId),
30092
30688
  expandedPrompt
30093
30689
  ].filter(Boolean).join("\n\n");
30094
30690
  if (!autoContinue && sentAttachments) {
@@ -30717,7 +31313,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
30717
31313
  const request = sameIntent ? pending : {
30718
31314
  op,
30719
31315
  scriptName: scriptName ?? null,
30720
- requestId: (0, import_node_crypto17.randomUUID)(),
31316
+ requestId: (0, import_node_crypto19.randomUUID)(),
30721
31317
  requestedAt: (/* @__PURE__ */ new Date()).toISOString()
30722
31318
  };
30723
31319
  if (!sameIntent) {
@@ -31838,7 +32434,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
31838
32434
  return thread;
31839
32435
  }
31840
32436
  };
31841
- singleton = null;
32437
+ singleton2 = null;
31842
32438
  }
31843
32439
  });
31844
32440
 
@@ -32955,6 +33551,7 @@ __export(index_exports, {
32955
33551
  startRelayServer: () => startRelayServer,
32956
33552
  startRemoteAccountLogin: () => startRemoteAccountLogin,
32957
33553
  startSlackOAuth: () => startSlackOAuth,
33554
+ stashMcpAppModelContext: () => stashMcpAppModelContext,
32958
33555
  statWorktreePath: () => statWorktreePath,
32959
33556
  stopAgentRpcServer: () => stopAgentRpcServer,
32960
33557
  stripBrightsyNdjsonNoise: () => stripBrightsyNdjsonNoise,
@@ -33521,7 +34118,7 @@ init_schedule_runner();
33521
34118
 
33522
34119
  // src/host/serve.ts
33523
34120
  var import_node_child_process7 = require("child_process");
33524
- var import_node_os22 = require("os");
34121
+ var import_node_os21 = require("os");
33525
34122
  init_system_ca();
33526
34123
  init_app_settings();
33527
34124
  init_desktop_host();
@@ -33534,7 +34131,7 @@ init_system_ca();
33534
34131
 
33535
34132
  // src/slack/public-dns.ts
33536
34133
  var import_node_dns = require("dns");
33537
- var import_node_http2 = require("http");
34134
+ var import_node_http3 = require("http");
33538
34135
  var import_node_https = require("https");
33539
34136
  var PUBLIC_DNS = ["1.1.1.1", "8.8.8.8"];
33540
34137
  function finishLookup(options, callback, address, family) {
@@ -33664,213 +34261,12 @@ function buildPathRefAttachment(input) {
33664
34261
  init_stage_files();
33665
34262
  init_orchestrator();
33666
34263
 
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
- }
34264
+ // src/remote/phone-mcp-ui-listen.ts
34265
+ init_host();
34266
+ init_model_context();
33871
34267
 
33872
34268
  // src/remote/phone-chats.ts
33873
- var import_node_crypto18 = require("crypto");
34269
+ var import_node_crypto20 = require("crypto");
33874
34270
  init_list_models();
33875
34271
  init_home_board();
33876
34272
  init_stage_files();
@@ -33892,10 +34288,10 @@ var MIN_MD_CHARS = 120;
33892
34288
  function oneLine2(value, max) {
33893
34289
  return value.replace(/[`\r\n]/g, " ").replace(/\s+/g, " ").trim().slice(0, max);
33894
34290
  }
33895
- function asRecord14(value) {
34291
+ function asRecord15(value) {
33896
34292
  if (typeof value === "string") {
33897
34293
  try {
33898
- return asRecord14(JSON.parse(value));
34294
+ return asRecord15(JSON.parse(value));
33899
34295
  } catch {
33900
34296
  return void 0;
33901
34297
  }
@@ -33970,7 +34366,7 @@ function toolContent(input) {
33970
34366
  if (!input) return "";
33971
34367
  const direct = input.content;
33972
34368
  if (typeof direct === "string") return direct;
33973
- const nested = asRecord14(direct);
34369
+ const nested = asRecord15(direct);
33974
34370
  if (!nested) return "";
33975
34371
  for (const key of ["html", "markup", "text", "source", "content", "code"]) {
33976
34372
  if (typeof nested[key] === "string") return nested[key];
@@ -33993,7 +34389,7 @@ function phoneArtifactText(text7, parts, limit = Number.POSITIVE_INFINITY) {
33993
34389
  const extras = [];
33994
34390
  for (const part of parts ?? []) {
33995
34391
  if (part.type !== "tool" || part.parentId || !isArtifactTool(part.name)) continue;
33996
- const input = asRecord14(part.input);
34392
+ const input = asRecord15(part.input);
33997
34393
  const content = toolContent(input);
33998
34394
  const hint = artifactHint(content);
33999
34395
  if (hint && hints.has(hint)) continue;
@@ -34012,7 +34408,7 @@ function phoneArtifactText(text7, parts, limit = Number.POSITIVE_INFINITY) {
34012
34408
  ${tail}` : tail;
34013
34409
  }
34014
34410
  function parseOpenArtifact(value, chatId) {
34015
- const record = asRecord14(value);
34411
+ const record = asRecord15(value);
34016
34412
  if (!record) return "invalid";
34017
34413
  const title = oneLine2(str5(record.title), 200);
34018
34414
  if (!title) return "invalid";
@@ -34035,7 +34431,7 @@ function phoneArtifactDocuments(sources) {
34035
34431
  }
34036
34432
  for (const part of source.parts ?? []) {
34037
34433
  if (part.type !== "tool" || part.parentId || !isArtifactTool(part.name)) continue;
34038
- const input = asRecord14(part.input);
34434
+ const input = asRecord15(part.input);
34039
34435
  const content = toolContent(input);
34040
34436
  const hint = artifactHint(content);
34041
34437
  if (hint && hints.has(hint)) continue;
@@ -34480,13 +34876,21 @@ function parsePhoneMcpUi(value, op, chatId) {
34480
34876
  }
34481
34877
  return { op: "mcp-ui", chatId, tool: tool2, callId, reqId: reqId2 };
34482
34878
  }
34879
+ if (op === "mcp-ui-context") {
34880
+ const reqId2 = controlText(value, "reqId");
34881
+ const text7 = controlText(value, "text");
34882
+ if (!reqId2 || reqId2.length > 40 || !text7 || text7.length > 8e3) return "invalid";
34883
+ return { op, chatId, reqId: reqId2, text: text7 };
34884
+ }
34483
34885
  if (op !== "mcp-ui-call" && op !== "mcp-ui-read") return "invalid";
34484
34886
  const reqId = controlText(value, "reqId");
34485
34887
  const server = controlText(value, "server");
34486
34888
  if (!reqId || reqId.length > 40 || !server || server.length > 120) return "invalid";
34487
34889
  if (op === "mcp-ui-read") {
34488
34890
  const uri = controlText(value, "uri");
34489
- if (!uri.startsWith("ui://") || uri.length > 500) return "invalid";
34891
+ if (!uri || uri.length > 500 || /\s/.test(uri) || /^(javascript|data):/i.test(uri)) {
34892
+ return "invalid";
34893
+ }
34490
34894
  return { op, chatId, reqId, server, uri };
34491
34895
  }
34492
34896
  const tool = controlText(value, "tool");
@@ -34708,7 +35112,7 @@ function parsePhoneControl(value) {
34708
35112
  }
34709
35113
  if (op === "pane") return parsePhonePane(value, chatId);
34710
35114
  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);
35115
+ if (op === "mcp-ui" || op === "mcp-ui-call" || op === "mcp-ui-read" || op === "mcp-ui-context") return parsePhoneMcpUi(value, op, chatId);
34712
35116
  if (op === "open-file" || op === "media") {
34713
35117
  const path2 = textField(value, "path");
34714
35118
  if (!path2 || path2.length > 2e3) return "invalid";
@@ -34975,13 +35379,13 @@ function phoneComposerOptions(thread) {
34975
35379
  }
34976
35380
  function phoneTurnAttachments(draft) {
34977
35381
  const links = (draft.links ?? []).map((link) => ({
34978
- id: (0, import_node_crypto18.randomUUID)(),
35382
+ id: (0, import_node_crypto20.randomUUID)(),
34979
35383
  name: link.ref,
34980
35384
  kind: "issue",
34981
35385
  content: [`Linked issue: ${link.ref} \u2014 ${link.title}`, link.url ? `URL: ${link.url}` : null].filter(Boolean).join("\n")
34982
35386
  }));
34983
35387
  const files = (draft.files ?? []).map((file) => ({
34984
- id: (0, import_node_crypto18.randomUUID)(),
35388
+ id: (0, import_node_crypto20.randomUUID)(),
34985
35389
  name: file.name,
34986
35390
  kind: "file",
34987
35391
  previewDataUrl: `data:application/octet-stream;base64,${file.dataBase64}`,
@@ -35317,7 +35721,9 @@ function deliverPhoneTurn(opts, chatId) {
35317
35721
  // src/remote/phone-mcp-ui-listen.ts
35318
35722
  var PHONE_HTML_CAP = 7e5;
35319
35723
  function dispatchPhoneMcpUi(cmd, opts) {
35320
- if (cmd.op !== "mcp-ui" && cmd.op !== "mcp-ui-call" && cmd.op !== "mcp-ui-read") return false;
35724
+ if (cmd.op !== "mcp-ui" && cmd.op !== "mcp-ui-call" && cmd.op !== "mcp-ui-read" && cmd.op !== "mcp-ui-context") {
35725
+ return false;
35726
+ }
35321
35727
  const thread = readPhoneThread(cmd.chatId);
35322
35728
  const reqId = cmd.reqId;
35323
35729
  const chatId = cmd.chatId;
@@ -35327,6 +35733,11 @@ function dispatchPhoneMcpUi(cmd, opts) {
35327
35733
  return true;
35328
35734
  }
35329
35735
  const projectDirs = [thread.repoPath, thread.worktreePath];
35736
+ if (cmd.op === "mcp-ui-context") {
35737
+ stashMcpAppModelContext(thread.id, cmd.text);
35738
+ emitControl(opts, { op: "mcp-ui-context", chatId, reqId, result: {} });
35739
+ return true;
35740
+ }
35330
35741
  if (cmd.op === "mcp-ui-call") {
35331
35742
  void getMcpUiHost().callTool(cmd.server, cmd.tool, cmd.args, projectDirs).then((result) => emitControl(opts, { op: "mcp-ui-call", chatId, reqId, result })).catch(
35332
35743
  (err) => emitControl(opts, { op: "mcp-ui-call", chatId, reqId, error: phoneError(err) })
@@ -36163,8 +36574,8 @@ init_fetch();
36163
36574
  init_issue_attachments();
36164
36575
 
36165
36576
  // src/integrations/linear-oauth.ts
36166
- var import_node_crypto19 = require("crypto");
36167
- var import_node_http3 = require("http");
36577
+ var import_node_crypto21 = require("crypto");
36578
+ var import_node_http4 = require("http");
36168
36579
  init_app_settings();
36169
36580
  init_fetch();
36170
36581
 
@@ -36195,8 +36606,8 @@ function linearOAuthCredentials() {
36195
36606
  return { clientId, clientSecret };
36196
36607
  }
36197
36608
  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");
36609
+ const verifier = (0, import_node_crypto21.randomBytes)(32).toString("base64url");
36610
+ const challenge = (0, import_node_crypto21.createHash)("sha256").update(verifier).digest("base64url");
36200
36611
  return { verifier, challenge };
36201
36612
  }
36202
36613
  function linearOAuthAuthorizeUrl(clientId, state, codeChallenge) {
@@ -36327,7 +36738,7 @@ async function startLinearOAuth(opts) {
36327
36738
  throw new LinearOAuthCancelledError();
36328
36739
  }
36329
36740
  const { clientId, clientSecret } = linearOAuthCredentials();
36330
- const state = (0, import_node_crypto19.randomBytes)(16).toString("hex");
36741
+ const state = (0, import_node_crypto21.randomBytes)(16).toString("hex");
36331
36742
  const pkce = createLinearPkce();
36332
36743
  const authorizeUrl = linearOAuthAuthorizeUrl(clientId, state, pkce.challenge);
36333
36744
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
@@ -36340,7 +36751,7 @@ async function startLinearOAuth(opts) {
36340
36751
  if (err) reject(err);
36341
36752
  else resolve3(value);
36342
36753
  };
36343
- const server = (0, import_node_http3.createServer)((req, res) => {
36754
+ const server = (0, import_node_http4.createServer)((req, res) => {
36344
36755
  try {
36345
36756
  const url = new URL(req.url || "/", `http://127.0.0.1:${LINEAR_OAUTH_PORT}`);
36346
36757
  if (url.pathname !== "/callback") {
@@ -38753,7 +39164,7 @@ function runRemoteHost(opts) {
38753
39164
  init_protocol();
38754
39165
 
38755
39166
  // src/relay/server.ts
38756
- var import_node_http4 = require("http");
39167
+ var import_node_http5 = require("http");
38757
39168
  var import_ws4 = require("ws");
38758
39169
 
38759
39170
  // src/slack/oauth-redirect.ts
@@ -39162,7 +39573,7 @@ function connectSession(ws, opts, log) {
39162
39573
  }
39163
39574
 
39164
39575
  // src/slack/relay-protocol.ts
39165
- var import_node_crypto20 = require("crypto");
39576
+ var import_node_crypto22 = require("crypto");
39166
39577
  var SLACK_RELAY_PING_INTERVAL_MS = 2e4;
39167
39578
  var SLACK_RELAY_PONG_TIMEOUT_MS = 15e3;
39168
39579
  function slackRelaySessionKey(teamId, userId, deviceId) {
@@ -39172,7 +39583,7 @@ function slackRelayUserKey(teamId, userId) {
39172
39583
  return `${teamId.trim()}:${userId.trim()}`;
39173
39584
  }
39174
39585
  function newSlackRelayEventId() {
39175
- return (0, import_node_crypto20.randomBytes)(12).toString("hex");
39586
+ return (0, import_node_crypto22.randomBytes)(12).toString("hex");
39176
39587
  }
39177
39588
  function parseSlackRelayClientMessage(raw) {
39178
39589
  try {
@@ -39700,7 +40111,7 @@ async function tryServeStatic(req, res, root) {
39700
40111
  }
39701
40112
 
39702
40113
  // src/remote/hub.ts
39703
- var import_node_crypto21 = require("crypto");
40114
+ var import_node_crypto23 = require("crypto");
39704
40115
  init_protocol();
39705
40116
  var CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
39706
40117
  var CODE_TTL_MS = 10 * 60 * 1e3;
@@ -39711,7 +40122,7 @@ function send(socket, msg) {
39711
40122
  }
39712
40123
  }
39713
40124
  function newCode() {
39714
- const bytes = (0, import_node_crypto21.randomBytes)(6);
40125
+ const bytes = (0, import_node_crypto23.randomBytes)(6);
39715
40126
  let code = "";
39716
40127
  for (let i = 0; i < 6; i++) code += CODE_ALPHABET[bytes[i] % CODE_ALPHABET.length];
39717
40128
  return code;
@@ -39970,7 +40381,7 @@ var RemoteHub = class {
39970
40381
  host.phone = null;
39971
40382
  }
39972
40383
  const created = sessionToken == null;
39973
- const token = sessionToken ?? (0, import_node_crypto21.randomBytes)(24).toString("hex");
40384
+ const token = sessionToken ?? (0, import_node_crypto23.randomBytes)(24).toString("hex");
39974
40385
  if (host.sessionToken && host.sessionToken !== token) {
39975
40386
  this.phonesByToken.delete(host.sessionToken);
39976
40387
  }
@@ -40022,7 +40433,7 @@ var RemoteHub = class {
40022
40433
  init_protocol();
40023
40434
 
40024
40435
  // src/relay/accounts.ts
40025
- var import_node_crypto22 = require("crypto");
40436
+ var import_node_crypto24 = require("crypto");
40026
40437
  var import_node_fs70 = require("fs");
40027
40438
  var import_node_module7 = require("module");
40028
40439
  var import_node_path68 = require("path");
@@ -40041,12 +40452,12 @@ function openDatabase(filePath) {
40041
40452
  return db;
40042
40453
  }
40043
40454
  function sha2562(value) {
40044
- return (0, import_node_crypto22.createHash)("sha256").update(value).digest("hex");
40455
+ return (0, import_node_crypto24.createHash)("sha256").update(value).digest("hex");
40045
40456
  }
40046
40457
  function sameHash(a, b) {
40047
40458
  const left = Buffer.from(a);
40048
40459
  const right = Buffer.from(b);
40049
- return left.length === right.length && (0, import_node_crypto22.timingSafeEqual)(left, right);
40460
+ return left.length === right.length && (0, import_node_crypto24.timingSafeEqual)(left, right);
40050
40461
  }
40051
40462
  function normalizeEmail(email) {
40052
40463
  const trimmed = email?.trim().toLowerCase() ?? "";
@@ -40151,7 +40562,7 @@ var RelayAccountStore = class {
40151
40562
  this.insertIdentity(issuer, subject, accountId, email, verified);
40152
40563
  } else {
40153
40564
  const byEmail = verified ? this.accountIdForVerifiedEmail(email) : void 0;
40154
- accountId = byEmail ?? (0, import_node_crypto22.randomUUID)();
40565
+ accountId = byEmail ?? (0, import_node_crypto24.randomUUID)();
40155
40566
  if (!byEmail) {
40156
40567
  this.db.prepare("INSERT INTO accounts (id, created_at, device_cap) VALUES (?, ?, ?)").run(accountId, Date.now(), RELAY_DEVICE_CAP);
40157
40568
  }
@@ -40273,7 +40684,7 @@ var RelayAccountStore = class {
40273
40684
  ).run(issuer, subject, accountId, email, verified ? 1 : 0);
40274
40685
  }
40275
40686
  mintCredential(accountId) {
40276
- const credential = (0, import_node_crypto22.randomBytes)(32).toString("hex");
40687
+ const credential = (0, import_node_crypto24.randomBytes)(32).toString("hex");
40277
40688
  this.db.prepare("INSERT INTO credentials (token_hash, account_id, created_at) VALUES (?, ?, ?)").run(sha2562(credential), accountId, Date.now());
40278
40689
  return credential;
40279
40690
  }
@@ -40748,7 +41159,7 @@ async function startRelayServer(opts) {
40748
41159
  sendJson2(res, 200, { ok: true, ...value.payload });
40749
41160
  return true;
40750
41161
  };
40751
- const httpServer = (0, import_node_http4.createServer)((req, res) => {
41162
+ const httpServer = (0, import_node_http5.createServer)((req, res) => {
40752
41163
  void (async () => {
40753
41164
  const reqUrl = req.url || "/";
40754
41165
  if (accountStore && await handleRelayAccountHttp({
@@ -41085,7 +41496,7 @@ async function bindAbort(signal, stop) {
41085
41496
  }
41086
41497
  function flatInterfaces() {
41087
41498
  const out = [];
41088
- for (const rows of Object.values((0, import_node_os22.networkInterfaces)())) {
41499
+ for (const rows of Object.values((0, import_node_os21.networkInterfaces)())) {
41089
41500
  if (!rows) continue;
41090
41501
  out.push(...rows);
41091
41502
  }
@@ -41182,7 +41593,7 @@ init_stage_files();
41182
41593
  init_consume_attachments();
41183
41594
 
41184
41595
  // src/composer/pasted-text.ts
41185
- var import_node_crypto23 = require("crypto");
41596
+ var import_node_crypto25 = require("crypto");
41186
41597
  var PASTE_ATTACH_MIN_CHARS = 1200;
41187
41598
  var PASTE_ATTACH_MIN_LINES = 15;
41188
41599
  var PASTED_NAME_RE = /^Pasted text #(\d+)\.txt$/i;
@@ -41209,7 +41620,7 @@ function nextPastedTextName(existing) {
41209
41620
  }
41210
41621
  function buildPastedTextAttachment(text7, opts) {
41211
41622
  return {
41212
- id: opts?.id ?? (0, import_node_crypto23.randomUUID)(),
41623
+ id: opts?.id ?? (0, import_node_crypto25.randomUUID)(),
41213
41624
  name: opts?.name ?? "Pasted text #1.txt",
41214
41625
  kind: "file",
41215
41626
  path: opts?.path,
@@ -41385,7 +41796,7 @@ function createClippedMcpServer() {
41385
41796
 
41386
41797
  // src/mcp/server.ts
41387
41798
  var import_zod11 = require("zod");
41388
- var import_node_crypto26 = require("crypto");
41799
+ var import_node_crypto28 = require("crypto");
41389
41800
  var import_node_path73 = require("path");
41390
41801
  init_orchestrator();
41391
41802
  init_task_state();
@@ -42977,7 +43388,7 @@ function registerScheduleTools(server) {
42977
43388
  }
42978
43389
 
42979
43390
  // src/agent-rpc/handlers.ts
42980
- var import_node_crypto24 = require("crypto");
43391
+ var import_node_crypto26 = require("crypto");
42981
43392
  var import_node_path70 = require("path");
42982
43393
  init_wait_for_job();
42983
43394
  init_wait_for_turn();
@@ -43008,7 +43419,7 @@ setToolPermissionPublisher({
43008
43419
  }
43009
43420
  }
43010
43421
  });
43011
- function asRecord15(value) {
43422
+ function asRecord16(value) {
43012
43423
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
43013
43424
  return value;
43014
43425
  }
@@ -43019,12 +43430,12 @@ function invalidParams(message) {
43019
43430
  return Object.assign(new Error(message), { rpcCode: -32602 });
43020
43431
  }
43021
43432
  var handlePermissionAsk = async (params, _ctx) => {
43022
- const rec = asRecord15(params);
43433
+ const rec = asRecord16(params);
43023
43434
  const threadId = str6(rec.threadId).trim();
43024
43435
  const toolName = str6(rec.toolName).trim();
43025
43436
  if (!threadId) throw invalidParams("threadId is required");
43026
43437
  if (!toolName) throw invalidParams("toolName is required");
43027
- const input = asRecord15(rec.input);
43438
+ const input = asRecord16(rec.input);
43028
43439
  const ask = {
43029
43440
  threadId,
43030
43441
  toolName,
@@ -43060,7 +43471,7 @@ var handlePermissionAsk = async (params, _ctx) => {
43060
43471
  // src/agent-rpc/handlers.ts
43061
43472
  init_protocol2();
43062
43473
  function presentArtifactResult(input) {
43063
- const id = input.artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${(0, import_node_crypto24.randomUUID)().slice(0, 8)}`;
43474
+ const id = input.artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${(0, import_node_crypto26.randomUUID)().slice(0, 8)}`;
43064
43475
  return {
43065
43476
  ok: true,
43066
43477
  artifact_id: id,
@@ -43072,7 +43483,7 @@ function presentArtifactResult(input) {
43072
43483
  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
43484
  };
43074
43485
  }
43075
- function asRecord16(value) {
43486
+ function asRecord17(value) {
43076
43487
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
43077
43488
  return value;
43078
43489
  }
@@ -43096,7 +43507,7 @@ async function handleRuntimePing(_params, _ctx) {
43096
43507
  };
43097
43508
  }
43098
43509
  async function handlePresentArtifact(params, _ctx) {
43099
- const rec = asRecord16(params);
43510
+ const rec = asRecord17(params);
43100
43511
  const title = str7(rec.title).trim();
43101
43512
  const type = str7(rec.type).trim();
43102
43513
  const allowed = ["html", "svg", "markdown", "react", "log"];
@@ -43120,7 +43531,7 @@ async function handlePresentArtifact(params, _ctx) {
43120
43531
  });
43121
43532
  }
43122
43533
  async function handleJobWait(params, ctx, waitFn = waitForDetachedJob) {
43123
- const rec = asRecord16(params);
43534
+ const rec = asRecord17(params);
43124
43535
  const id = str7(rec.id).trim();
43125
43536
  if (!id) throw invalidParams2("id is required");
43126
43537
  const cwd = jobCwd(rec);
@@ -43144,7 +43555,7 @@ async function handleJobWait(params, ctx, waitFn = waitForDetachedJob) {
43144
43555
  };
43145
43556
  }
43146
43557
  async function handleJobStop(params, _ctx) {
43147
- const rec = asRecord16(params);
43558
+ const rec = asRecord17(params);
43148
43559
  const id = str7(rec.id).trim();
43149
43560
  if (!id) throw invalidParams2("id is required");
43150
43561
  const reason = str7(rec.reason).trim() || void 0;
@@ -43172,7 +43583,7 @@ async function defaultTurnWait(ref, timeoutMs) {
43172
43583
  });
43173
43584
  }
43174
43585
  async function handleTurnWait(params, ctx, waitFn = defaultTurnWait) {
43175
- const rec = asRecord16(params);
43586
+ const rec = asRecord17(params);
43176
43587
  const ref = str7(rec.ref).trim();
43177
43588
  if (!ref) throw invalidParams2("ref is required");
43178
43589
  const requested = typeof rec.timeoutMs === "number" && Number.isFinite(rec.timeoutMs) ? rec.timeoutMs : void 0;
@@ -43224,7 +43635,7 @@ init_worktree();
43224
43635
  init_paths();
43225
43636
 
43226
43637
  // src/board/board-pins.ts
43227
- var import_node_crypto25 = require("crypto");
43638
+ var import_node_crypto27 = require("crypto");
43228
43639
  var import_node_fs72 = require("fs");
43229
43640
  var import_node_path71 = require("path");
43230
43641
  init_paths();
@@ -43256,7 +43667,7 @@ function addBoardPin(input) {
43256
43667
  const ref = input.ref.trim();
43257
43668
  if (!ref) throw new Error("Board item needs a ref");
43258
43669
  const pin = {
43259
- id: (0, import_node_crypto25.randomUUID)(),
43670
+ id: (0, import_node_crypto27.randomUUID)(),
43260
43671
  kind: input.kind,
43261
43672
  ref,
43262
43673
  repoPath: input.repoPath.trim(),
@@ -44189,7 +44600,7 @@ async function startMcpServer() {
44189
44600
  const ident = issue?.identifier ?? ref.trim();
44190
44601
  const attachments = issue ? [
44191
44602
  {
44192
- id: (0, import_node_crypto26.randomUUID)(),
44603
+ id: (0, import_node_crypto28.randomUUID)(),
44193
44604
  name: issue.identifier,
44194
44605
  kind: "issue",
44195
44606
  content: [
@@ -44751,8 +45162,8 @@ init_protocol2();
44751
45162
  init_metadata();
44752
45163
 
44753
45164
  // src/agent-rpc/server.ts
44754
- var import_node_crypto27 = require("crypto");
44755
- var import_node_http5 = require("http");
45165
+ var import_node_crypto29 = require("crypto");
45166
+ var import_node_http6 = require("http");
44756
45167
  var import_ws5 = require("ws");
44757
45168
  init_protocol2();
44758
45169
  init_metadata();
@@ -44760,7 +45171,7 @@ function tokensEqual(a, b) {
44760
45171
  const left = Buffer.from(a);
44761
45172
  const right = Buffer.from(b);
44762
45173
  if (left.length !== right.length) return false;
44763
- return (0, import_node_crypto27.timingSafeEqual)(left, right);
45174
+ return (0, import_node_crypto29.timingSafeEqual)(left, right);
44764
45175
  }
44765
45176
  function authTokenFromUpgrade(req) {
44766
45177
  try {
@@ -44793,16 +45204,16 @@ var AgentRpcServer = class {
44793
45204
  async listen(opts = {}) {
44794
45205
  const host = opts.host ?? "127.0.0.1";
44795
45206
  const port = opts.port ?? 0;
44796
- const authToken = (0, import_node_crypto27.randomBytes)(24).toString("base64url");
45207
+ const authToken = (0, import_node_crypto29.randomBytes)(24).toString("base64url");
44797
45208
  this.meta = {
44798
- runtimeId: (0, import_node_crypto27.randomUUID)(),
45209
+ runtimeId: (0, import_node_crypto29.randomUUID)(),
44799
45210
  pid: process.pid,
44800
45211
  protocolVersion: AGENT_RPC_PROTOCOL_VERSION,
44801
45212
  authToken,
44802
45213
  startedAt: Date.now(),
44803
45214
  transports: []
44804
45215
  };
44805
- const http = (0, import_node_http5.createServer)((_req, res) => {
45216
+ const http = (0, import_node_http6.createServer)((_req, res) => {
44806
45217
  res.statusCode = 404;
44807
45218
  res.end();
44808
45219
  });
@@ -45288,6 +45699,8 @@ init_oauth();
45288
45699
  init_injected_mcp();
45289
45700
  init_user_mcp_config();
45290
45701
  init_orch_mcp_isolation();
45702
+ init_host();
45703
+ init_model_context();
45291
45704
  init_meta();
45292
45705
 
45293
45706
  // src/slack/workspaces.ts
@@ -45386,7 +45799,7 @@ function slackTokenFor(ws, kind = "read") {
45386
45799
  }
45387
45800
 
45388
45801
  // src/slack/oauth.ts
45389
- var import_node_crypto28 = require("crypto");
45802
+ var import_node_crypto30 = require("crypto");
45390
45803
  init_app_settings();
45391
45804
  var SLACK_BOT_SCOPES = [
45392
45805
  "app_mentions:read",
@@ -45480,7 +45893,7 @@ async function startSlackOAuth(opts) {
45480
45893
  throw new SlackOAuthCancelledError();
45481
45894
  }
45482
45895
  const { clientId } = slackOAuthCredentials();
45483
- const state = (0, import_node_crypto28.randomBytes)(16).toString("hex");
45896
+ const state = (0, import_node_crypto30.randomBytes)(16).toString("hex");
45484
45897
  const authorizeUrl = slackOAuthAuthorizeUrl(clientId, state);
45485
45898
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
45486
45899
  const pollIntervalMs = opts?.pollIntervalMs ?? 400;
@@ -46389,7 +46802,7 @@ function resolveSlackListenMode(opts) {
46389
46802
  init_protocol();
46390
46803
 
46391
46804
  // src/relay/account-login.ts
46392
- var import_node_crypto29 = require("crypto");
46805
+ var import_node_crypto31 = require("crypto");
46393
46806
  init_fetch();
46394
46807
  init_protocol();
46395
46808
  var RemoteAccountLoginCancelled = class extends Error {
@@ -46457,7 +46870,7 @@ async function readJson4(res, url) {
46457
46870
  async function startRemoteAccountLogin(opts) {
46458
46871
  if (opts.signal?.aborted) throw new RemoteAccountLoginCancelled();
46459
46872
  const origin = (opts.origin ?? remoteAccountHttpOrigin()).replace(/\/+$/, "");
46460
- const state = (0, import_node_crypto29.randomBytes)(16).toString("hex");
46873
+ const state = (0, import_node_crypto31.randomBytes)(16).toString("hex");
46461
46874
  const timeoutMs = opts.timeoutMs ?? 5 * 6e4;
46462
46875
  const pollIntervalMs = opts.pollIntervalMs ?? 400;
46463
46876
  const linkToken = opts.linkToken?.trim() ?? "";
@@ -47853,6 +48266,7 @@ async function pollSlackOutboundWatches(opts) {
47853
48266
  startRelayServer,
47854
48267
  startRemoteAccountLogin,
47855
48268
  startSlackOAuth,
48269
+ stashMcpAppModelContext,
47856
48270
  statWorktreePath,
47857
48271
  stopAgentRpcServer,
47858
48272
  stripBrightsyNdjsonNoise,