@sideboard-ai/core 0.1.310 → 0.1.312
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agents-XXXRQJJO.js → agents-IOJRGGK3.js} +1 -1
- package/dist/{agents-QIIZYNOM.js → agents-XDI6QHBA.js} +1 -1
- package/dist/{chunk-GQJOLTZU.js → chunk-2P2HYXA2.js} +620 -18
- package/dist/{chunk-4YBQBVYL.js → chunk-4FZUUH6W.js} +514 -22
- package/dist/{chunk-JXOW5JAW.js → chunk-LLTWP4PK.js} +53 -3
- package/dist/{chunk-VBVSJUM2.js → chunk-QC2TQMPJ.js} +31 -20
- package/dist/{chunk-IT6A4FZF.js → chunk-VIVXKN5N.js} +60 -4
- package/dist/{chunk-AILQRQ26.js → chunk-ZW7RSQHY.js} +25 -9
- package/dist/{conductor-AMZ5SKG2.js → conductor-CTWR6NH4.js} +1 -1
- package/dist/{conductor-OFXA5BST.js → conductor-YOBSYPLT.js} +1 -1
- package/dist/index.cjs +993 -570
- package/dist/index.d.cts +83 -17
- package/dist/index.d.ts +83 -17
- package/dist/index.js +51 -226
- package/dist/mcp/run-stdio.cjs +823 -124
- package/dist/mcp/run-stdio.js +3 -3
- package/dist/{orchestrator-XFE3U3RP.js → orchestrator-BYTSILLH.js} +3 -3
- package/dist/{orchestrator-7374OI7T.js → orchestrator-S44BMNN7.js} +3 -3
- package/package.json +1 -1
|
@@ -447,6 +447,9 @@ var brightsyAdapter = {
|
|
|
447
447
|
|
|
448
448
|
// src/agents/claude.ts
|
|
449
449
|
import { existsSync as existsSync11 } from "fs";
|
|
450
|
+
|
|
451
|
+
// src/mcp-ui/host.ts
|
|
452
|
+
import { createHash } from "crypto";
|
|
450
453
|
import { homedir as homedir5 } from "os";
|
|
451
454
|
|
|
452
455
|
// src/agents/orch-mcp-isolation.ts
|
|
@@ -458,6 +461,20 @@ import { homedir } from "os";
|
|
|
458
461
|
import { isAbsolute, join, relative, resolve } from "path";
|
|
459
462
|
|
|
460
463
|
// src/mcp-ui/meta.ts
|
|
464
|
+
var HTML_MAX = 15e5;
|
|
465
|
+
function matchClaudeMcpTool(toolName, serverNames) {
|
|
466
|
+
const name = toolName.trim();
|
|
467
|
+
if (!name.startsWith("mcp__")) return null;
|
|
468
|
+
const ranked = [...serverNames].filter((s) => s.trim()).sort((a, b) => b.length - a.length);
|
|
469
|
+
for (const server of ranked) {
|
|
470
|
+
const prefix = `mcp__${server}__`;
|
|
471
|
+
if (!name.startsWith(prefix)) continue;
|
|
472
|
+
const tool = name.slice(prefix.length).trim();
|
|
473
|
+
if (!tool) continue;
|
|
474
|
+
return { server, tool };
|
|
475
|
+
}
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
461
478
|
function parseMcpServerLaunch(entry) {
|
|
462
479
|
const command = typeof entry.command === "string" ? entry.command.trim() : "";
|
|
463
480
|
if (command) {
|
|
@@ -489,6 +506,105 @@ function parseMcpServerLaunch(entry) {
|
|
|
489
506
|
...headers ? { headers } : {}
|
|
490
507
|
};
|
|
491
508
|
}
|
|
509
|
+
function uiResourceUriFromMeta(meta) {
|
|
510
|
+
const rec = asRecord(meta);
|
|
511
|
+
const nested = asRecord(rec.ui);
|
|
512
|
+
const fromNested = typeof nested.resourceUri === "string" ? nested.resourceUri.trim() : "";
|
|
513
|
+
const legacy = typeof rec["ui/resourceUri"] === "string" ? rec["ui/resourceUri"].trim() : "";
|
|
514
|
+
const uri = fromNested || legacy;
|
|
515
|
+
return uri.startsWith("ui://") ? uri : null;
|
|
516
|
+
}
|
|
517
|
+
function cspFromMeta(meta) {
|
|
518
|
+
const ui = asRecord(asRecord(meta).ui);
|
|
519
|
+
const csp = asRecord(ui.csp);
|
|
520
|
+
const connectDomains = originList(csp.connectDomains);
|
|
521
|
+
const resourceDomains = originList(csp.resourceDomains);
|
|
522
|
+
const frameDomains = originList(csp.frameDomains);
|
|
523
|
+
const baseUriDomains = originList(csp.baseUriDomains);
|
|
524
|
+
if (!connectDomains?.length && !resourceDomains?.length && !frameDomains?.length && !baseUriDomains?.length) {
|
|
525
|
+
return void 0;
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
...connectDomains?.length ? { connectDomains } : {},
|
|
529
|
+
...resourceDomains?.length ? { resourceDomains } : {},
|
|
530
|
+
...frameDomains?.length ? { frameDomains } : {},
|
|
531
|
+
...baseUriDomains?.length ? { baseUriDomains } : {}
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function htmlFromResourceContent(content) {
|
|
535
|
+
const uri = content.uri?.trim() ?? "";
|
|
536
|
+
if (!uri.startsWith("ui://")) return null;
|
|
537
|
+
const mimeType = content.mimeType?.trim() || "text/html";
|
|
538
|
+
if (!isHtmlMime(mimeType)) return null;
|
|
539
|
+
const html = htmlText(content.text, content.blob);
|
|
540
|
+
if (!html) return null;
|
|
541
|
+
return { uri, mimeType, html };
|
|
542
|
+
}
|
|
543
|
+
function mcpUiContentSecurityPolicy(csp) {
|
|
544
|
+
const resource = (csp?.resourceDomains ?? []).join(" ");
|
|
545
|
+
const connect = (csp?.connectDomains ?? []).join(" ");
|
|
546
|
+
const frames = csp?.frameDomains?.length ? csp.frameDomains.join(" ") : "'none'";
|
|
547
|
+
const base = csp?.baseUriDomains?.length ? csp.baseUriDomains.join(" ") : "'none'";
|
|
548
|
+
const connectSrc = connect ? `'self' ${connect}`.trim() : "'none'";
|
|
549
|
+
return [
|
|
550
|
+
"default-src 'none'",
|
|
551
|
+
`script-src 'self' 'unsafe-inline' ${resource}`.trim(),
|
|
552
|
+
`style-src 'self' 'unsafe-inline' ${resource}`.trim(),
|
|
553
|
+
`img-src 'self' data: ${resource}`.trim(),
|
|
554
|
+
`font-src 'self' ${resource}`.trim(),
|
|
555
|
+
`connect-src ${connectSrc}`,
|
|
556
|
+
`media-src 'self' data: ${resource}`.trim(),
|
|
557
|
+
`frame-src ${frames}`,
|
|
558
|
+
`base-uri ${base}`,
|
|
559
|
+
"object-src 'none'"
|
|
560
|
+
].join("; ");
|
|
561
|
+
}
|
|
562
|
+
function htmlText(text, blob) {
|
|
563
|
+
if (typeof text === "string" && text.trim()) {
|
|
564
|
+
return text.length <= HTML_MAX ? text : null;
|
|
565
|
+
}
|
|
566
|
+
if (typeof blob !== "string" || !blob.trim()) return null;
|
|
567
|
+
try {
|
|
568
|
+
const decoded = decodeBase64(blob.trim());
|
|
569
|
+
if (!decoded.trim() || decoded.length > HTML_MAX) return null;
|
|
570
|
+
return decoded;
|
|
571
|
+
} catch {
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
function decodeBase64(blob) {
|
|
576
|
+
if (typeof Buffer !== "undefined") return Buffer.from(blob, "base64").toString("utf8");
|
|
577
|
+
const bin = atob(blob);
|
|
578
|
+
const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));
|
|
579
|
+
return new TextDecoder().decode(bytes);
|
|
580
|
+
}
|
|
581
|
+
function isHtmlMime(mime) {
|
|
582
|
+
const base = mime.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
583
|
+
return base === "text/html";
|
|
584
|
+
}
|
|
585
|
+
function originList(value) {
|
|
586
|
+
if (!Array.isArray(value)) return void 0;
|
|
587
|
+
const out = [];
|
|
588
|
+
for (const item of value) {
|
|
589
|
+
if (typeof item !== "string") continue;
|
|
590
|
+
const origin = originToken(item);
|
|
591
|
+
if (origin && !out.includes(origin)) out.push(origin);
|
|
592
|
+
}
|
|
593
|
+
return out;
|
|
594
|
+
}
|
|
595
|
+
function originToken(value) {
|
|
596
|
+
const trimmed = value.trim();
|
|
597
|
+
if (/^(https?|wss?):\/\/\*\.[a-z0-9.-]+(?::\d+)?$/i.test(trimmed)) return trimmed;
|
|
598
|
+
try {
|
|
599
|
+
const url = new URL(trimmed);
|
|
600
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" && url.protocol !== "wss:" && url.protocol !== "ws:") {
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
return url.origin;
|
|
604
|
+
} catch {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
492
608
|
function stringRecord(value) {
|
|
493
609
|
const rec = asRecord(value);
|
|
494
610
|
const out = {};
|
|
@@ -1620,6 +1736,492 @@ function toCodexUnattendedAppsArgs() {
|
|
|
1620
1736
|
];
|
|
1621
1737
|
}
|
|
1622
1738
|
|
|
1739
|
+
// src/mcp-ui/proxy-http.ts
|
|
1740
|
+
import { randomBytes, randomUUID } from "crypto";
|
|
1741
|
+
import { createServer } from "http";
|
|
1742
|
+
import { Server as Server2 } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1743
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
1744
|
+
|
|
1745
|
+
// src/mcp-ui/proxy-server.ts
|
|
1746
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1747
|
+
import {
|
|
1748
|
+
CallToolRequestSchema,
|
|
1749
|
+
GetPromptRequestSchema,
|
|
1750
|
+
ListPromptsRequestSchema,
|
|
1751
|
+
ListResourcesRequestSchema,
|
|
1752
|
+
ListResourceTemplatesRequestSchema,
|
|
1753
|
+
ListToolsRequestSchema,
|
|
1754
|
+
ReadResourceRequestSchema
|
|
1755
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
1756
|
+
|
|
1757
|
+
// src/mcp-ui/visibility.ts
|
|
1758
|
+
function visibilityFromMeta(meta) {
|
|
1759
|
+
const rec = asRecord2(meta);
|
|
1760
|
+
const ui = asRecord2(rec.ui);
|
|
1761
|
+
const raw = ui.visibility;
|
|
1762
|
+
if (!Array.isArray(raw)) return ["model", "app"];
|
|
1763
|
+
const out = [];
|
|
1764
|
+
for (const item of raw) {
|
|
1765
|
+
if (item !== "model" && item !== "app") continue;
|
|
1766
|
+
if (!out.includes(item)) out.push(item);
|
|
1767
|
+
}
|
|
1768
|
+
return out.length ? out : ["model", "app"];
|
|
1769
|
+
}
|
|
1770
|
+
function toolVisibleTo(visibility, audience) {
|
|
1771
|
+
const vis = visibility?.length ? visibility : ["model", "app"];
|
|
1772
|
+
return vis.includes(audience);
|
|
1773
|
+
}
|
|
1774
|
+
function asRecord2(value) {
|
|
1775
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
1776
|
+
return value;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// src/mcp-ui/proxy-server.ts
|
|
1780
|
+
function attachMcpUiProxyHandlers(server, session) {
|
|
1781
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1782
|
+
const tools = await session.listTools();
|
|
1783
|
+
return { tools: tools.filter((tool) => toolVisibleTo(tool.visibility, "model")).map(listedTool) };
|
|
1784
|
+
});
|
|
1785
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1786
|
+
const name = request.params.name;
|
|
1787
|
+
const tools = await session.listTools();
|
|
1788
|
+
const tool = tools.find((item) => item.name === name);
|
|
1789
|
+
if (!tool || !toolVisibleTo(tool.visibility, "model")) {
|
|
1790
|
+
throw new Error(`Tool ${name} is not available to the model`);
|
|
1791
|
+
}
|
|
1792
|
+
return await session.callTool(name, request.params.arguments ?? {});
|
|
1793
|
+
});
|
|
1794
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
1795
|
+
const resource = await session.readResource(request.params.uri);
|
|
1796
|
+
if (!resource) throw new Error(`Resource ${request.params.uri} was empty`);
|
|
1797
|
+
return {
|
|
1798
|
+
contents: [
|
|
1799
|
+
{
|
|
1800
|
+
uri: resource.uri,
|
|
1801
|
+
...resource.mimeType ? { mimeType: resource.mimeType } : {},
|
|
1802
|
+
...resource.text != null ? { text: resource.text } : {},
|
|
1803
|
+
...resource.blob != null ? { blob: resource.blob } : {}
|
|
1804
|
+
}
|
|
1805
|
+
]
|
|
1806
|
+
};
|
|
1807
|
+
});
|
|
1808
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => session.listResources());
|
|
1809
|
+
server.setRequestHandler(
|
|
1810
|
+
ListResourceTemplatesRequestSchema,
|
|
1811
|
+
async () => session.listResourceTemplates()
|
|
1812
|
+
);
|
|
1813
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => session.listPrompts());
|
|
1814
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
1815
|
+
return await session.getPrompt(request.params.name, request.params.arguments);
|
|
1816
|
+
});
|
|
1817
|
+
server.fallbackRequestHandler = async (request) => {
|
|
1818
|
+
return await session.forward(request.method, request.params);
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
function listedTool(tool) {
|
|
1822
|
+
return {
|
|
1823
|
+
name: tool.name,
|
|
1824
|
+
...tool.description ? { description: tool.description } : {},
|
|
1825
|
+
inputSchema: tool.inputSchema ?? { type: "object", properties: {} },
|
|
1826
|
+
_meta: {
|
|
1827
|
+
ui: {
|
|
1828
|
+
...tool.resourceUri ? { resourceUri: tool.resourceUri } : {},
|
|
1829
|
+
visibility: tool.visibility
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
// src/mcp-ui/proxy-http.ts
|
|
1836
|
+
async function listenMcpUiProxy(opts) {
|
|
1837
|
+
const token = randomBytes(24).toString("hex");
|
|
1838
|
+
const transports = /* @__PURE__ */ new Map();
|
|
1839
|
+
const httpServer = createServer((req, res) => {
|
|
1840
|
+
void handle(req, res);
|
|
1841
|
+
});
|
|
1842
|
+
await new Promise((resolve2, reject) => {
|
|
1843
|
+
httpServer.once("error", reject);
|
|
1844
|
+
httpServer.listen(0, "127.0.0.1", () => resolve2());
|
|
1845
|
+
});
|
|
1846
|
+
const addr = httpServer.address();
|
|
1847
|
+
if (!addr || typeof addr === "string") throw new Error("mcp-ui proxy failed to bind");
|
|
1848
|
+
const origin = `http://127.0.0.1:${addr.port}`;
|
|
1849
|
+
async function handle(req, res) {
|
|
1850
|
+
if (req.headers.authorization !== `Bearer ${token}`) {
|
|
1851
|
+
res.writeHead(401).end();
|
|
1852
|
+
return;
|
|
1853
|
+
}
|
|
1854
|
+
const target = parseMcpUiProxyPath(req.url);
|
|
1855
|
+
if (!target) {
|
|
1856
|
+
res.writeHead(404).end();
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
try {
|
|
1860
|
+
const session = await opts.sessionFor(target.server, target.dirsKey);
|
|
1861
|
+
if (!session) {
|
|
1862
|
+
res.writeHead(404).end();
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
const header = req.headers["mcp-session-id"];
|
|
1866
|
+
const sid = typeof header === "string" ? header : "";
|
|
1867
|
+
const sessionKey = `${target.server}:${target.dirsKey}`;
|
|
1868
|
+
let transport = sid ? transports.get(`${sessionKey}:${sid}`) : void 0;
|
|
1869
|
+
if (!transport) {
|
|
1870
|
+
if (req.method !== "POST") {
|
|
1871
|
+
res.writeHead(400).end();
|
|
1872
|
+
return;
|
|
1873
|
+
}
|
|
1874
|
+
const created = new StreamableHTTPServerTransport({
|
|
1875
|
+
sessionIdGenerator: () => randomUUID(),
|
|
1876
|
+
allowedHosts: ["127.0.0.1", "localhost"],
|
|
1877
|
+
onsessioninitialized: (id) => {
|
|
1878
|
+
transports.set(`${sessionKey}:${id}`, created);
|
|
1879
|
+
},
|
|
1880
|
+
onsessionclosed: (id) => {
|
|
1881
|
+
transports.delete(`${sessionKey}:${id}`);
|
|
1882
|
+
}
|
|
1883
|
+
});
|
|
1884
|
+
transport = created;
|
|
1885
|
+
const mcp = new Server2(
|
|
1886
|
+
{ name: `sideboard-ui-proxy-${target.server}`, version: "1.0.0" },
|
|
1887
|
+
{ capabilities: session.capabilities() }
|
|
1888
|
+
);
|
|
1889
|
+
attachMcpUiProxyHandlers(mcp, session);
|
|
1890
|
+
await mcp.connect(created);
|
|
1891
|
+
}
|
|
1892
|
+
await transport.handleRequest(req, res);
|
|
1893
|
+
} catch {
|
|
1894
|
+
if (!res.headersSent) res.writeHead(500).end();
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
return {
|
|
1898
|
+
urlFor(server, dirsKey) {
|
|
1899
|
+
const base = `${origin}/s/${encodeURIComponent(server)}`;
|
|
1900
|
+
return dirsKey ? `${base}/d/${dirsKey}` : base;
|
|
1901
|
+
},
|
|
1902
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
1903
|
+
async close() {
|
|
1904
|
+
await Promise.all([...transports.values()].map((item) => item.close().catch(() => void 0)));
|
|
1905
|
+
transports.clear();
|
|
1906
|
+
await new Promise((resolve2) => httpServer.close(() => resolve2()));
|
|
1907
|
+
}
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
function parseMcpUiProxyPath(url) {
|
|
1911
|
+
const path = (url ?? "").split("?")[0] ?? "";
|
|
1912
|
+
if (!path.startsWith("/s/")) return null;
|
|
1913
|
+
const rest = path.slice(3).replace(/\/$/, "");
|
|
1914
|
+
const split = rest.indexOf("/d/");
|
|
1915
|
+
try {
|
|
1916
|
+
if (split < 0) {
|
|
1917
|
+
const server2 = decodeURIComponent(rest).trim();
|
|
1918
|
+
return server2 ? { server: server2, dirsKey: "" } : null;
|
|
1919
|
+
}
|
|
1920
|
+
const server = decodeURIComponent(rest.slice(0, split)).trim();
|
|
1921
|
+
const dirsKey = rest.slice(split + 3);
|
|
1922
|
+
if (!server || !/^[a-f0-9]{16}$/.test(dirsKey)) return null;
|
|
1923
|
+
return { server, dirsKey };
|
|
1924
|
+
} catch {
|
|
1925
|
+
return null;
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
// src/mcp-ui/session.ts
|
|
1930
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
1931
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
1932
|
+
import { StdioClientTransport, getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
1933
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
1934
|
+
import { ResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
1935
|
+
var REQUEST_TIMEOUT_MS = 2e4;
|
|
1936
|
+
async function orEmpty(run2, fallback) {
|
|
1937
|
+
try {
|
|
1938
|
+
return await run2();
|
|
1939
|
+
} catch {
|
|
1940
|
+
return fallback;
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
var UI_CAPABILITIES = {
|
|
1944
|
+
extensions: {
|
|
1945
|
+
"io.modelcontextprotocol/ui": {
|
|
1946
|
+
mimeTypes: ["text/html;profile=mcp-app"]
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
};
|
|
1950
|
+
async function openMcpUiSession(launch) {
|
|
1951
|
+
return openMcpUiSessionFromTransport(transportForLaunch(launch));
|
|
1952
|
+
}
|
|
1953
|
+
async function openMcpUiSessionFromTransport(transport) {
|
|
1954
|
+
const client = new Client(
|
|
1955
|
+
{ name: "sideboard", version: "1.0.0" },
|
|
1956
|
+
{ capabilities: UI_CAPABILITIES }
|
|
1957
|
+
);
|
|
1958
|
+
await client.connect(transport, { timeout: REQUEST_TIMEOUT_MS });
|
|
1959
|
+
return {
|
|
1960
|
+
async listTools() {
|
|
1961
|
+
const listed = await client.listTools(void 0, { timeout: REQUEST_TIMEOUT_MS });
|
|
1962
|
+
return listed.tools.map((tool) => {
|
|
1963
|
+
const meta = tool._meta;
|
|
1964
|
+
return {
|
|
1965
|
+
name: tool.name,
|
|
1966
|
+
...tool.description ? { description: tool.description } : {},
|
|
1967
|
+
...tool.inputSchema ? { inputSchema: tool.inputSchema } : {},
|
|
1968
|
+
resourceUri: uiResourceUriFromMeta(meta),
|
|
1969
|
+
visibility: visibilityFromMeta(meta),
|
|
1970
|
+
...cspFromMeta(meta) ? { csp: cspFromMeta(meta) } : {}
|
|
1971
|
+
};
|
|
1972
|
+
});
|
|
1973
|
+
},
|
|
1974
|
+
listResources: () => orEmpty(
|
|
1975
|
+
() => client.listResources(void 0, { timeout: REQUEST_TIMEOUT_MS }),
|
|
1976
|
+
{ resources: [] }
|
|
1977
|
+
),
|
|
1978
|
+
listResourceTemplates: () => orEmpty(
|
|
1979
|
+
() => client.listResourceTemplates(void 0, { timeout: REQUEST_TIMEOUT_MS }),
|
|
1980
|
+
{ resourceTemplates: [] }
|
|
1981
|
+
),
|
|
1982
|
+
listPrompts: () => orEmpty(
|
|
1983
|
+
() => client.listPrompts(void 0, { timeout: REQUEST_TIMEOUT_MS }),
|
|
1984
|
+
{ prompts: [] }
|
|
1985
|
+
),
|
|
1986
|
+
getPrompt(name, args) {
|
|
1987
|
+
return client.getPrompt({ name, arguments: args }, { timeout: REQUEST_TIMEOUT_MS });
|
|
1988
|
+
},
|
|
1989
|
+
async readResource(uri) {
|
|
1990
|
+
const result = await client.readResource({ uri }, { timeout: REQUEST_TIMEOUT_MS });
|
|
1991
|
+
const content = result.contents?.[0];
|
|
1992
|
+
if (!content) return null;
|
|
1993
|
+
const text = "text" in content && typeof content.text === "string" ? content.text : void 0;
|
|
1994
|
+
const blob = "blob" in content && typeof content.blob === "string" ? content.blob : void 0;
|
|
1995
|
+
return {
|
|
1996
|
+
uri: content.uri,
|
|
1997
|
+
...content.mimeType ? { mimeType: content.mimeType } : {},
|
|
1998
|
+
...text != null ? { text } : {},
|
|
1999
|
+
...blob != null ? { blob } : {},
|
|
2000
|
+
...cspFromMeta(content._meta) ? { csp: cspFromMeta(content._meta) } : {}
|
|
2001
|
+
};
|
|
2002
|
+
},
|
|
2003
|
+
async callTool(name, args) {
|
|
2004
|
+
return client.callTool({ name, arguments: args }, void 0, { timeout: REQUEST_TIMEOUT_MS });
|
|
2005
|
+
},
|
|
2006
|
+
capabilities() {
|
|
2007
|
+
const caps = client.getServerCapabilities() ?? {};
|
|
2008
|
+
return {
|
|
2009
|
+
tools: caps.tools ?? {},
|
|
2010
|
+
resources: caps.resources ?? {},
|
|
2011
|
+
prompts: caps.prompts ?? {},
|
|
2012
|
+
completions: caps.completions ?? {},
|
|
2013
|
+
...caps.logging ? { logging: caps.logging } : {}
|
|
2014
|
+
};
|
|
2015
|
+
},
|
|
2016
|
+
forward(method, params) {
|
|
2017
|
+
return client.request(
|
|
2018
|
+
{ method, params },
|
|
2019
|
+
ResultSchema,
|
|
2020
|
+
{ timeout: REQUEST_TIMEOUT_MS }
|
|
2021
|
+
);
|
|
2022
|
+
},
|
|
2023
|
+
async close() {
|
|
2024
|
+
await client.close();
|
|
2025
|
+
}
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
function transportForLaunch(launch) {
|
|
2029
|
+
if (launch.kind === "stdio") {
|
|
2030
|
+
return new StdioClientTransport({
|
|
2031
|
+
command: launch.command,
|
|
2032
|
+
args: launch.args,
|
|
2033
|
+
env: { ...getDefaultEnvironment(), ...launch.env },
|
|
2034
|
+
stderr: "ignore",
|
|
2035
|
+
...launch.cwd ? { cwd: launch.cwd } : {}
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
const url = new URL(launch.url);
|
|
2039
|
+
const requestInit = launch.headers ? { headers: launch.headers } : void 0;
|
|
2040
|
+
if (launch.kind === "sse") {
|
|
2041
|
+
return new SSEClientTransport(url, requestInit ? { requestInit } : void 0);
|
|
2042
|
+
}
|
|
2043
|
+
return new StreamableHTTPClientTransport(url, requestInit ? { requestInit } : void 0);
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
// src/mcp-ui/host.ts
|
|
2047
|
+
var TOOL_TTL_MS = 5 * 60 * 1e3;
|
|
2048
|
+
var McpUiHost = class {
|
|
2049
|
+
constructor(opts) {
|
|
2050
|
+
this.opts = opts;
|
|
2051
|
+
}
|
|
2052
|
+
opts;
|
|
2053
|
+
sessions = /* @__PURE__ */ new Map();
|
|
2054
|
+
toolLists = /* @__PURE__ */ new Map();
|
|
2055
|
+
proxy;
|
|
2056
|
+
proxyDirs = /* @__PURE__ */ new Map();
|
|
2057
|
+
async viewForClaudeTool(toolName, projectDirs) {
|
|
2058
|
+
const entries = this.userEntries(projectDirs);
|
|
2059
|
+
const match = matchClaudeMcpTool(toolName, Object.keys(entries));
|
|
2060
|
+
if (!match) return null;
|
|
2061
|
+
const launch = parseMcpServerLaunch(entries[match.server] ?? {});
|
|
2062
|
+
if (!launch) return null;
|
|
2063
|
+
try {
|
|
2064
|
+
const session = await this.sessionFor(match.server, launch);
|
|
2065
|
+
const tools = await this.toolsFor(match.server, launch, session);
|
|
2066
|
+
const tool = tools.find((item) => item.name === match.tool);
|
|
2067
|
+
if (!tool?.resourceUri) return null;
|
|
2068
|
+
const resource = await session.readResource(tool.resourceUri);
|
|
2069
|
+
if (!resource) return null;
|
|
2070
|
+
const html = htmlFromResourceContent(resource);
|
|
2071
|
+
if (!html) return null;
|
|
2072
|
+
const csp = resource.csp ?? tool.csp;
|
|
2073
|
+
return {
|
|
2074
|
+
server: match.server,
|
|
2075
|
+
tool: match.tool,
|
|
2076
|
+
uri: html.uri,
|
|
2077
|
+
mimeType: html.mimeType,
|
|
2078
|
+
html: html.html,
|
|
2079
|
+
csp: mcpUiContentSecurityPolicy(csp)
|
|
2080
|
+
};
|
|
2081
|
+
} catch (err) {
|
|
2082
|
+
console.error(
|
|
2083
|
+
`[mcp-ui] ${match.server}/${match.tool}:`,
|
|
2084
|
+
err instanceof Error ? err.message : err
|
|
2085
|
+
);
|
|
2086
|
+
return null;
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
/**
|
|
2090
|
+
* Point Claude at loopback HTTP so it shares this host's upstream session
|
|
2091
|
+
* and never sees `visibility: ["app"]` tools.
|
|
2092
|
+
*/
|
|
2093
|
+
async proxyEntries(entries, projectDirs) {
|
|
2094
|
+
const names = Object.keys(entries).filter((name) => name.trim());
|
|
2095
|
+
if (names.length === 0) return {};
|
|
2096
|
+
const dirsKey = dirsKeyFor(projectDirs);
|
|
2097
|
+
this.proxyDirs.set(dirsKey, projectDirs);
|
|
2098
|
+
try {
|
|
2099
|
+
const listen = await this.ensureProxy();
|
|
2100
|
+
const out = {};
|
|
2101
|
+
for (const name of names) {
|
|
2102
|
+
out[name] = {
|
|
2103
|
+
type: "http",
|
|
2104
|
+
url: listen.urlFor(name, dirsKey),
|
|
2105
|
+
headers: listen.headers
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
return out;
|
|
2109
|
+
} catch (err) {
|
|
2110
|
+
console.error("[mcp-ui] proxy:", err instanceof Error ? err.message : err);
|
|
2111
|
+
return { ...entries };
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
async callTool(server, tool, args, projectDirs) {
|
|
2115
|
+
const entry = this.userEntries(projectDirs)[server];
|
|
2116
|
+
if (!entry) throw new Error(`MCP server ${server} is not available`);
|
|
2117
|
+
const launch = parseMcpServerLaunch(entry);
|
|
2118
|
+
if (!launch) throw new Error(`MCP server ${server} is not available`);
|
|
2119
|
+
const session = await this.sessionFor(server, launch);
|
|
2120
|
+
const tools = await this.toolsFor(server, launch, session);
|
|
2121
|
+
const info = tools.find((item) => item.name === tool);
|
|
2122
|
+
if (!info || !toolVisibleTo(info.visibility, "app")) {
|
|
2123
|
+
throw new Error(`Tool ${tool} is not available to the app`);
|
|
2124
|
+
}
|
|
2125
|
+
return session.callTool(tool, args);
|
|
2126
|
+
}
|
|
2127
|
+
async readResource(server, uri, projectDirs) {
|
|
2128
|
+
const session = await this.sessionForName(server, projectDirs);
|
|
2129
|
+
if (!session) throw new Error(`MCP server ${server} is not available`);
|
|
2130
|
+
const resource = await session.readResource(uri);
|
|
2131
|
+
if (!resource) throw new Error(`Resource ${uri} was empty`);
|
|
2132
|
+
return {
|
|
2133
|
+
contents: [
|
|
2134
|
+
{
|
|
2135
|
+
uri: resource.uri,
|
|
2136
|
+
...resource.mimeType ? { mimeType: resource.mimeType } : {},
|
|
2137
|
+
...resource.text != null ? { text: resource.text } : {},
|
|
2138
|
+
...resource.blob != null ? { blob: resource.blob } : {}
|
|
2139
|
+
}
|
|
2140
|
+
]
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
async closeAll() {
|
|
2144
|
+
const pending = [...this.sessions.values()];
|
|
2145
|
+
const proxy = this.proxy;
|
|
2146
|
+
this.sessions.clear();
|
|
2147
|
+
this.toolLists.clear();
|
|
2148
|
+
this.proxyDirs.clear();
|
|
2149
|
+
this.proxy = void 0;
|
|
2150
|
+
await Promise.all([
|
|
2151
|
+
...pending.map((session) => session.then((open) => open.close()).catch(() => void 0)),
|
|
2152
|
+
proxy ? proxy.then((listen) => listen.close()).catch(() => void 0) : Promise.resolve()
|
|
2153
|
+
]);
|
|
2154
|
+
}
|
|
2155
|
+
ensureProxy() {
|
|
2156
|
+
if (this.proxy) return this.proxy;
|
|
2157
|
+
this.proxy = listenMcpUiProxy({
|
|
2158
|
+
sessionFor: (server, dirsKey) => {
|
|
2159
|
+
if (dirsKey && !this.proxyDirs.has(dirsKey)) return Promise.resolve(null);
|
|
2160
|
+
return this.sessionForName(server, this.proxyDirs.get(dirsKey));
|
|
2161
|
+
}
|
|
2162
|
+
}).catch((err) => {
|
|
2163
|
+
this.proxy = void 0;
|
|
2164
|
+
throw err;
|
|
2165
|
+
});
|
|
2166
|
+
return this.proxy;
|
|
2167
|
+
}
|
|
2168
|
+
userEntries(projectDirs) {
|
|
2169
|
+
const out = {};
|
|
2170
|
+
for (const [name, entry] of Object.entries(this.opts.entries(projectDirs))) {
|
|
2171
|
+
if (!name.trim() || isInjectedOrchMcpName(name) || !entry) continue;
|
|
2172
|
+
out[name] = entry;
|
|
2173
|
+
}
|
|
2174
|
+
return out;
|
|
2175
|
+
}
|
|
2176
|
+
async sessionForName(server, projectDirs) {
|
|
2177
|
+
const entry = this.userEntries(projectDirs)[server];
|
|
2178
|
+
if (!entry) return null;
|
|
2179
|
+
const launch = parseMcpServerLaunch(entry);
|
|
2180
|
+
if (!launch) return null;
|
|
2181
|
+
return this.sessionFor(server, launch);
|
|
2182
|
+
}
|
|
2183
|
+
sessionFor(server, launch) {
|
|
2184
|
+
const key = `${server}
|
|
2185
|
+
${JSON.stringify(launch)}`;
|
|
2186
|
+
const existing = this.sessions.get(key);
|
|
2187
|
+
if (existing) return existing;
|
|
2188
|
+
const open = this.opts.openSession ?? openMcpUiSession;
|
|
2189
|
+
const pending = open(launch).catch((err) => {
|
|
2190
|
+
this.sessions.delete(key);
|
|
2191
|
+
throw err;
|
|
2192
|
+
});
|
|
2193
|
+
this.sessions.set(key, pending);
|
|
2194
|
+
return pending;
|
|
2195
|
+
}
|
|
2196
|
+
async toolsFor(server, launch, session) {
|
|
2197
|
+
const key = `${server}
|
|
2198
|
+
${JSON.stringify(launch)}`;
|
|
2199
|
+
const cached = this.toolLists.get(key);
|
|
2200
|
+
if (cached && cached.key === key && Date.now() - cached.at < TOOL_TTL_MS) return cached.tools;
|
|
2201
|
+
const tools = await session.listTools();
|
|
2202
|
+
this.toolLists.set(key, { at: Date.now(), key, tools });
|
|
2203
|
+
return tools;
|
|
2204
|
+
}
|
|
2205
|
+
};
|
|
2206
|
+
var singleton;
|
|
2207
|
+
function getMcpUiHost() {
|
|
2208
|
+
if (!singleton) {
|
|
2209
|
+
singleton = new McpUiHost({
|
|
2210
|
+
entries: (projectDirs) => listUserClaudeMcpServerEntries(homedir5(), projectDirs)
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
return singleton;
|
|
2214
|
+
}
|
|
2215
|
+
async function proxyClaudeUserMcp(orchestrator, projectDirs) {
|
|
2216
|
+
if (orchestrator) return {};
|
|
2217
|
+
const dirs = projectDirs.filter((d) => typeof d === "string" && d.length > 0);
|
|
2218
|
+
return getMcpUiHost().proxyEntries(listUserClaudeMcpServerEntries(homedir5(), dirs), dirs);
|
|
2219
|
+
}
|
|
2220
|
+
function dirsKeyFor(projectDirs) {
|
|
2221
|
+
if (!projectDirs?.length) return "";
|
|
2222
|
+
return createHash("sha256").update(projectDirs.join("\0")).digest("hex").slice(0, 16);
|
|
2223
|
+
}
|
|
2224
|
+
|
|
1623
2225
|
// src/agents/types.ts
|
|
1624
2226
|
var PLAN_MODE_INSTRUCTION = "Plan mode is active and must remain active until the user turns Plan mode off in the UI (or Approves / Hands off the plan). Analyze the codebase, search and read files as needed, and produce or refine a clear implementation plan. Do not modify, create, or delete any project files except via Sideboard MCP present_plan (writes .context/attachments/plan.md). When you need a clarifying decision (approach forks, auth choice, scope) \u2014 not greetings, check-ins, or an invented task menu: (1) first write a short chat message that explains the decision and what each option means (tradeoffs, when to pick it) \u2014 do not leave the user staring at bare labels; (2) then call Sideboard MCP ask_user with the same options, including a description on every option. If one option is the obvious default, proceed without asking. Sideboard shows questions in the composer and mirrors them in chat. After ask_user, wait for the user's next message with their answers before finalizing the plan. When the plan is ready for approval: (1) call present_plan with the full markdown plan (title + content) so Sideboard saves .context/attachments/plan.md and shows it in chat for Approve / Hand off / Copy; (2) Claude should also call ExitPlanMode after present_plan. Do not skip present_plan \u2014 the plan must be a markdown file, not only chat prose.";
|
|
1625
2227
|
var PLAN_MODE_OFF_INSTRUCTION = "Plan mode is off. Do not call present_plan or ExitPlanMode \u2014 those only work when the user turns Plan on in the composer. If you need to propose an approach, write it in chat.";
|
|
@@ -2264,7 +2866,7 @@ var claudeAdapter = {
|
|
|
2264
2866
|
});
|
|
2265
2867
|
const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
|
|
2266
2868
|
const chromeOn = claudeChromeEnabled();
|
|
2267
|
-
const userMcpEntries =
|
|
2869
|
+
const userMcpEntries = await proxyClaudeUserMcp(isOrchestrator, [thread.repoPath, thread.worktreePath]);
|
|
2268
2870
|
let allowedTools;
|
|
2269
2871
|
if (isOrchestrator) {
|
|
2270
2872
|
allowedTools = [
|
|
@@ -2602,7 +3204,7 @@ function unwrapCodexMcpResult(result) {
|
|
|
2602
3204
|
return String(result);
|
|
2603
3205
|
}
|
|
2604
3206
|
}
|
|
2605
|
-
function
|
|
3207
|
+
function asRecord3(value) {
|
|
2606
3208
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
2607
3209
|
return value;
|
|
2608
3210
|
}
|
|
@@ -2755,7 +3357,7 @@ var codexAdapter = {
|
|
|
2755
3357
|
}
|
|
2756
3358
|
if (itemType === "mcp_tool_call" && item.id && item.server && item.tool) {
|
|
2757
3359
|
const name = `mcp__${item.server}__${item.tool}`;
|
|
2758
|
-
const input =
|
|
3360
|
+
const input = asRecord3(item.arguments);
|
|
2759
3361
|
const events = [
|
|
2760
3362
|
{ type: "tool_use", id: item.id, name, input }
|
|
2761
3363
|
];
|
|
@@ -3056,20 +3658,20 @@ function turnCostUsdFromCursorUsage(before, after) {
|
|
|
3056
3658
|
if (!(delta >= 0) || !Number.isFinite(delta)) return void 0;
|
|
3057
3659
|
return delta / 100;
|
|
3058
3660
|
}
|
|
3059
|
-
function
|
|
3661
|
+
function asRecord4(value) {
|
|
3060
3662
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
3061
3663
|
return value;
|
|
3062
3664
|
}
|
|
3063
3665
|
return void 0;
|
|
3064
3666
|
}
|
|
3065
3667
|
function normalizeCursorToolCall(name, args) {
|
|
3066
|
-
const raw =
|
|
3668
|
+
const raw = asRecord4(args);
|
|
3067
3669
|
const toolName = typeof raw?.toolName === "string" && raw.toolName.trim() ? raw.toolName.trim() : null;
|
|
3068
|
-
const looksLikeMcp = name === "mcp" || toolName != null && (typeof raw?.providerIdentifier === "string" ||
|
|
3670
|
+
const looksLikeMcp = name === "mcp" || toolName != null && (typeof raw?.providerIdentifier === "string" || asRecord4(raw?.args) != null);
|
|
3069
3671
|
if (looksLikeMcp && toolName) {
|
|
3070
3672
|
const providerRaw = typeof raw.providerIdentifier === "string" && raw.providerIdentifier.trim() ? raw.providerIdentifier.trim() : "sideboard";
|
|
3071
3673
|
const provider = providerRaw.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3072
|
-
const nested =
|
|
3674
|
+
const nested = asRecord4(raw.args);
|
|
3073
3675
|
return {
|
|
3074
3676
|
name: `mcp__${provider}__${toolName}`,
|
|
3075
3677
|
input: nested ?? { toolName }
|
|
@@ -3080,7 +3682,7 @@ function normalizeCursorToolCall(name, args) {
|
|
|
3080
3682
|
function unwrapCursorToolResult(result) {
|
|
3081
3683
|
if (result == null) return void 0;
|
|
3082
3684
|
if (typeof result === "string") return result;
|
|
3083
|
-
const rec =
|
|
3685
|
+
const rec = asRecord4(result);
|
|
3084
3686
|
if (!rec) {
|
|
3085
3687
|
try {
|
|
3086
3688
|
return JSON.stringify(result);
|
|
@@ -3091,18 +3693,18 @@ function unwrapCursorToolResult(result) {
|
|
|
3091
3693
|
const collectTexts = (items) => {
|
|
3092
3694
|
const texts = [];
|
|
3093
3695
|
for (const item of items) {
|
|
3094
|
-
const row =
|
|
3696
|
+
const row = asRecord4(item);
|
|
3095
3697
|
if (!row) continue;
|
|
3096
3698
|
if (typeof row.text === "string") {
|
|
3097
3699
|
texts.push(row.text);
|
|
3098
3700
|
continue;
|
|
3099
3701
|
}
|
|
3100
|
-
const nested =
|
|
3702
|
+
const nested = asRecord4(row.text);
|
|
3101
3703
|
if (typeof nested?.text === "string") texts.push(nested.text);
|
|
3102
3704
|
}
|
|
3103
3705
|
return texts;
|
|
3104
3706
|
};
|
|
3105
|
-
const value =
|
|
3707
|
+
const value = asRecord4(rec.value);
|
|
3106
3708
|
if (value && Array.isArray(value.content)) {
|
|
3107
3709
|
const texts = collectTexts(value.content);
|
|
3108
3710
|
if (texts.length) return texts.join("\n");
|
|
@@ -3119,10 +3721,10 @@ function unwrapCursorToolResult(result) {
|
|
|
3119
3721
|
}
|
|
3120
3722
|
function cursorToolResultIsError(status, result) {
|
|
3121
3723
|
if (status === "error") return true;
|
|
3122
|
-
const rec =
|
|
3724
|
+
const rec = asRecord4(result);
|
|
3123
3725
|
if (!rec) return false;
|
|
3124
3726
|
if (rec.status === "error") return true;
|
|
3125
|
-
const value =
|
|
3727
|
+
const value = asRecord4(rec.value);
|
|
3126
3728
|
return value?.isError === true;
|
|
3127
3729
|
}
|
|
3128
3730
|
function cursorSdkMessageToEvents(msg) {
|
|
@@ -3623,7 +4225,7 @@ var FALLBACK_OPENCODE_MODELS = [
|
|
|
3623
4225
|
];
|
|
3624
4226
|
var cachedOpencodeModels = null;
|
|
3625
4227
|
var OPENCODE_MODEL_CACHE_MS = 5 * 60 * 1e3;
|
|
3626
|
-
function
|
|
4228
|
+
function asRecord5(value) {
|
|
3627
4229
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
3628
4230
|
return value;
|
|
3629
4231
|
}
|
|
@@ -3794,7 +4396,7 @@ var opencodeAdapter = {
|
|
|
3794
4396
|
const name = part?.tool ?? part?.name ?? obj.tool ?? obj.name ?? "tool";
|
|
3795
4397
|
const state = part?.state;
|
|
3796
4398
|
const input = (state?.input && typeof state.input === "object" ? state.input : void 0) ?? part?.input ?? obj.input;
|
|
3797
|
-
const metadata =
|
|
4399
|
+
const metadata = asRecord5(state?.metadata);
|
|
3798
4400
|
const id = opencodeToolId(
|
|
3799
4401
|
name,
|
|
3800
4402
|
part?.callID ?? part?.id ?? obj.id,
|
|
@@ -3829,7 +4431,7 @@ var opencodeAdapter = {
|
|
|
3829
4431
|
}
|
|
3830
4432
|
if (obj.type === "subtask_event") {
|
|
3831
4433
|
const parentId = str(obj.childSessionID) ?? str(obj.childSessionId);
|
|
3832
|
-
const part =
|
|
4434
|
+
const part = asRecord5(obj.part);
|
|
3833
4435
|
if (!parentId || !part) return null;
|
|
3834
4436
|
if (part.type === "text" && str(part.text)) {
|
|
3835
4437
|
return withTaskParent(
|
|
@@ -3840,8 +4442,8 @@ var opencodeAdapter = {
|
|
|
3840
4442
|
if (part.type === "tool" || str(part.tool)) {
|
|
3841
4443
|
const name = str(part.tool) ?? str(part.name) ?? "tool";
|
|
3842
4444
|
const id = str(part.callID) ?? str(part.id) ?? `${parentId}-${name}`;
|
|
3843
|
-
const state =
|
|
3844
|
-
const input =
|
|
4445
|
+
const state = asRecord5(part.state);
|
|
4446
|
+
const input = asRecord5(state?.input) ?? asRecord5(part.input);
|
|
3845
4447
|
const nested = [
|
|
3846
4448
|
withEventParentId({ type: "tool_use", id, name, input }, parentId)
|
|
3847
4449
|
];
|