reasonix 0.2.2 → 0.3.0-alpha.2
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/README.md +33 -0
- package/dist/cli/index.js +734 -105
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +277 -2
- package/dist/index.js +347 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1355,6 +1355,14 @@ function recordFromLoopEvent(ev, extra) {
|
|
|
1355
1355
|
if (ev.toolName !== void 0) rec.tool = ev.toolName;
|
|
1356
1356
|
if (ev.toolArgs !== void 0) rec.args = ev.toolArgs;
|
|
1357
1357
|
if (ev.error !== void 0) rec.error = ev.error;
|
|
1358
|
+
if (ev.planState && !isPlanStateEmptyShape(ev.planState)) {
|
|
1359
|
+
rec.planState = {
|
|
1360
|
+
subgoals: [...ev.planState.subgoals],
|
|
1361
|
+
hypotheses: [...ev.planState.hypotheses],
|
|
1362
|
+
uncertainties: [...ev.planState.uncertainties],
|
|
1363
|
+
rejectedPaths: [...ev.planState.rejectedPaths]
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1358
1366
|
if (ev.stats) {
|
|
1359
1367
|
rec.usage = {
|
|
1360
1368
|
prompt_tokens: ev.stats.usage.promptTokens,
|
|
@@ -1390,6 +1398,9 @@ function readTranscript(path) {
|
|
|
1390
1398
|
const raw = readFileSync3(path, "utf8");
|
|
1391
1399
|
return parseTranscript(raw);
|
|
1392
1400
|
}
|
|
1401
|
+
function isPlanStateEmptyShape(s) {
|
|
1402
|
+
return s.subgoals.length === 0 && s.hypotheses.length === 0 && s.uncertainties.length === 0 && s.rejectedPaths.length === 0;
|
|
1403
|
+
}
|
|
1393
1404
|
function parseTranscript(raw) {
|
|
1394
1405
|
const out = { meta: null, records: [] };
|
|
1395
1406
|
for (const line of raw.split(/\r?\n/)) {
|
|
@@ -1425,12 +1436,20 @@ function computeReplayStats(records) {
|
|
|
1425
1436
|
const prefixHashes = /* @__PURE__ */ new Set();
|
|
1426
1437
|
let userTurns = 0;
|
|
1427
1438
|
let toolCalls = 0;
|
|
1439
|
+
let harvestedTurns = 0;
|
|
1440
|
+
let totalUncertainties = 0;
|
|
1441
|
+
let totalSubgoals = 0;
|
|
1428
1442
|
for (const rec of records) {
|
|
1429
1443
|
if (rec.role === "user") userTurns++;
|
|
1430
1444
|
else if (rec.role === "tool") toolCalls++;
|
|
1431
1445
|
else if (rec.role === "assistant_final") {
|
|
1432
1446
|
if (rec.model) models.add(rec.model);
|
|
1433
1447
|
if (rec.prefixHash) prefixHashes.add(rec.prefixHash);
|
|
1448
|
+
if (rec.planState) {
|
|
1449
|
+
harvestedTurns++;
|
|
1450
|
+
totalUncertainties += rec.planState.uncertainties.length;
|
|
1451
|
+
totalSubgoals += rec.planState.subgoals.length;
|
|
1452
|
+
}
|
|
1434
1453
|
if (rec.usage && rec.model) {
|
|
1435
1454
|
const u = new Usage(
|
|
1436
1455
|
rec.usage.prompt_tokens ?? 0,
|
|
@@ -1458,6 +1477,9 @@ function computeReplayStats(records) {
|
|
|
1458
1477
|
prefixHashes: [...prefixHashes],
|
|
1459
1478
|
userTurns,
|
|
1460
1479
|
toolCalls,
|
|
1480
|
+
harvestedTurns,
|
|
1481
|
+
totalUncertainties,
|
|
1482
|
+
totalSubgoals,
|
|
1461
1483
|
...summarizeTurns(turns)
|
|
1462
1484
|
};
|
|
1463
1485
|
}
|
|
@@ -1628,6 +1650,41 @@ function renderSummaryTable(report, _opts = {}) {
|
|
|
1628
1650
|
)
|
|
1629
1651
|
);
|
|
1630
1652
|
lines.push(statRow("prefix hashes", a.stats.prefixHashes.length, b.stats.prefixHashes.length));
|
|
1653
|
+
if (a.stats.harvestedTurns > 0 || b.stats.harvestedTurns > 0) {
|
|
1654
|
+
lines.push(
|
|
1655
|
+
row(
|
|
1656
|
+
[
|
|
1657
|
+
"harvest turns",
|
|
1658
|
+
`${a.stats.harvestedTurns}`,
|
|
1659
|
+
`${b.stats.harvestedTurns}`,
|
|
1660
|
+
signed(b.stats.harvestedTurns - a.stats.harvestedTurns)
|
|
1661
|
+
],
|
|
1662
|
+
[20, 14, 14, 14]
|
|
1663
|
+
)
|
|
1664
|
+
);
|
|
1665
|
+
lines.push(
|
|
1666
|
+
row(
|
|
1667
|
+
[
|
|
1668
|
+
" subgoals",
|
|
1669
|
+
`${a.stats.totalSubgoals}`,
|
|
1670
|
+
`${b.stats.totalSubgoals}`,
|
|
1671
|
+
signed(b.stats.totalSubgoals - a.stats.totalSubgoals)
|
|
1672
|
+
],
|
|
1673
|
+
[20, 14, 14, 14]
|
|
1674
|
+
)
|
|
1675
|
+
);
|
|
1676
|
+
lines.push(
|
|
1677
|
+
row(
|
|
1678
|
+
[
|
|
1679
|
+
" uncertainties",
|
|
1680
|
+
`${a.stats.totalUncertainties}`,
|
|
1681
|
+
`${b.stats.totalUncertainties}`,
|
|
1682
|
+
signed(b.stats.totalUncertainties - a.stats.totalUncertainties)
|
|
1683
|
+
],
|
|
1684
|
+
[20, 14, 14, 14]
|
|
1685
|
+
)
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1631
1688
|
lines.push("");
|
|
1632
1689
|
const aPrefixStable = a.stats.prefixHashes.length <= 1;
|
|
1633
1690
|
const bPrefixStable = b.stats.prefixHashes.length <= 1;
|
|
@@ -1699,6 +1756,17 @@ function renderMarkdown(report) {
|
|
|
1699
1756
|
out.push(
|
|
1700
1757
|
`| prefix hashes | ${a.stats.prefixHashes.length} | ${b.stats.prefixHashes.length} | \u2014 |`
|
|
1701
1758
|
);
|
|
1759
|
+
if (a.stats.harvestedTurns > 0 || b.stats.harvestedTurns > 0) {
|
|
1760
|
+
out.push(
|
|
1761
|
+
`| harvest turns | ${a.stats.harvestedTurns} | ${b.stats.harvestedTurns} | ${signed(b.stats.harvestedTurns - a.stats.harvestedTurns)} |`
|
|
1762
|
+
);
|
|
1763
|
+
out.push(
|
|
1764
|
+
`| harvest subgoals | ${a.stats.totalSubgoals} | ${b.stats.totalSubgoals} | ${signed(b.stats.totalSubgoals - a.stats.totalSubgoals)} |`
|
|
1765
|
+
);
|
|
1766
|
+
out.push(
|
|
1767
|
+
`| harvest uncertainties | ${a.stats.totalUncertainties} | ${b.stats.totalUncertainties} | ${signed(b.stats.totalUncertainties - a.stats.totalUncertainties)} |`
|
|
1768
|
+
);
|
|
1769
|
+
}
|
|
1702
1770
|
out.push("");
|
|
1703
1771
|
out.push("## Turn-by-turn");
|
|
1704
1772
|
out.push("");
|
|
@@ -1766,6 +1834,278 @@ function truncate(s, n) {
|
|
|
1766
1834
|
return s.length > n ? `${s.slice(0, n)}\u2026` : s;
|
|
1767
1835
|
}
|
|
1768
1836
|
|
|
1837
|
+
// src/mcp/types.ts
|
|
1838
|
+
var MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
1839
|
+
function isJsonRpcError(msg) {
|
|
1840
|
+
return "error" in msg;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
// src/mcp/client.ts
|
|
1844
|
+
var McpClient = class {
|
|
1845
|
+
transport;
|
|
1846
|
+
clientInfo;
|
|
1847
|
+
requestTimeoutMs;
|
|
1848
|
+
pending = /* @__PURE__ */ new Map();
|
|
1849
|
+
nextId = 1;
|
|
1850
|
+
readerStarted = false;
|
|
1851
|
+
initialized = false;
|
|
1852
|
+
_serverCapabilities = {};
|
|
1853
|
+
constructor(opts) {
|
|
1854
|
+
this.transport = opts.transport;
|
|
1855
|
+
this.clientInfo = opts.clientInfo ?? { name: "reasonix", version: "0.3.0-dev" };
|
|
1856
|
+
this.requestTimeoutMs = opts.requestTimeoutMs ?? 6e4;
|
|
1857
|
+
}
|
|
1858
|
+
/** Server's advertised capabilities, available after initialize(). */
|
|
1859
|
+
get serverCapabilities() {
|
|
1860
|
+
return this._serverCapabilities;
|
|
1861
|
+
}
|
|
1862
|
+
/**
|
|
1863
|
+
* Complete the initialize → initialized handshake. Must be called
|
|
1864
|
+
* before any other method (otherwise compliant servers reject).
|
|
1865
|
+
*/
|
|
1866
|
+
async initialize() {
|
|
1867
|
+
if (this.initialized) throw new Error("MCP client already initialized");
|
|
1868
|
+
this.startReaderIfNeeded();
|
|
1869
|
+
const result = await this.request("initialize", {
|
|
1870
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
1871
|
+
capabilities: { tools: {} },
|
|
1872
|
+
clientInfo: this.clientInfo
|
|
1873
|
+
});
|
|
1874
|
+
this._serverCapabilities = result.capabilities ?? {};
|
|
1875
|
+
await this.transport.send({
|
|
1876
|
+
jsonrpc: "2.0",
|
|
1877
|
+
method: "notifications/initialized"
|
|
1878
|
+
});
|
|
1879
|
+
this.initialized = true;
|
|
1880
|
+
return result;
|
|
1881
|
+
}
|
|
1882
|
+
/** List tools the server exposes. */
|
|
1883
|
+
async listTools() {
|
|
1884
|
+
this.assertInitialized();
|
|
1885
|
+
return this.request("tools/list", {});
|
|
1886
|
+
}
|
|
1887
|
+
/** Invoke a tool by name. Returns the raw MCP result (caller unwraps content). */
|
|
1888
|
+
async callTool(name, args) {
|
|
1889
|
+
this.assertInitialized();
|
|
1890
|
+
return this.request("tools/call", {
|
|
1891
|
+
name,
|
|
1892
|
+
arguments: args ?? {}
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
/** Close the transport and reject any outstanding requests. */
|
|
1896
|
+
async close() {
|
|
1897
|
+
for (const [, pending] of this.pending) {
|
|
1898
|
+
clearTimeout(pending.timeout);
|
|
1899
|
+
pending.reject(new Error("MCP client closed"));
|
|
1900
|
+
}
|
|
1901
|
+
this.pending.clear();
|
|
1902
|
+
await this.transport.close();
|
|
1903
|
+
}
|
|
1904
|
+
// ---------- internals ----------
|
|
1905
|
+
assertInitialized() {
|
|
1906
|
+
if (!this.initialized) throw new Error("MCP client not initialized \u2014 call initialize() first");
|
|
1907
|
+
}
|
|
1908
|
+
async request(method, params) {
|
|
1909
|
+
const id = this.nextId++;
|
|
1910
|
+
const frame = { jsonrpc: "2.0", id, method, params };
|
|
1911
|
+
const promise = new Promise((resolve2, reject) => {
|
|
1912
|
+
const timeout = setTimeout(() => {
|
|
1913
|
+
this.pending.delete(id);
|
|
1914
|
+
reject(
|
|
1915
|
+
new Error(`MCP request ${method} (id=${id}) timed out after ${this.requestTimeoutMs}ms`)
|
|
1916
|
+
);
|
|
1917
|
+
}, this.requestTimeoutMs);
|
|
1918
|
+
this.pending.set(id, {
|
|
1919
|
+
resolve: resolve2,
|
|
1920
|
+
reject,
|
|
1921
|
+
timeout
|
|
1922
|
+
});
|
|
1923
|
+
});
|
|
1924
|
+
await this.transport.send(frame);
|
|
1925
|
+
return promise;
|
|
1926
|
+
}
|
|
1927
|
+
startReaderIfNeeded() {
|
|
1928
|
+
if (this.readerStarted) return;
|
|
1929
|
+
this.readerStarted = true;
|
|
1930
|
+
void this.readLoop();
|
|
1931
|
+
}
|
|
1932
|
+
async readLoop() {
|
|
1933
|
+
try {
|
|
1934
|
+
for await (const msg of this.transport.messages()) {
|
|
1935
|
+
this.dispatch(msg);
|
|
1936
|
+
}
|
|
1937
|
+
} catch (err) {
|
|
1938
|
+
for (const [, pending] of this.pending) {
|
|
1939
|
+
clearTimeout(pending.timeout);
|
|
1940
|
+
pending.reject(err);
|
|
1941
|
+
}
|
|
1942
|
+
this.pending.clear();
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
dispatch(msg) {
|
|
1946
|
+
if (!("id" in msg) || msg.id === null || msg.id === void 0) return;
|
|
1947
|
+
if (!("result" in msg) && !("error" in msg)) return;
|
|
1948
|
+
const pending = this.pending.get(msg.id);
|
|
1949
|
+
if (!pending) return;
|
|
1950
|
+
this.pending.delete(msg.id);
|
|
1951
|
+
clearTimeout(pending.timeout);
|
|
1952
|
+
const resp = msg;
|
|
1953
|
+
if (isJsonRpcError(resp)) {
|
|
1954
|
+
pending.reject(new Error(`MCP ${resp.error.code}: ${resp.error.message}`));
|
|
1955
|
+
} else {
|
|
1956
|
+
pending.resolve(resp.result);
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
};
|
|
1960
|
+
|
|
1961
|
+
// src/mcp/stdio.ts
|
|
1962
|
+
import { spawn } from "child_process";
|
|
1963
|
+
var StdioTransport = class {
|
|
1964
|
+
child;
|
|
1965
|
+
queue = [];
|
|
1966
|
+
waiters = [];
|
|
1967
|
+
closed = false;
|
|
1968
|
+
stdoutBuffer = "";
|
|
1969
|
+
constructor(opts) {
|
|
1970
|
+
const env = opts.replaceEnv ? { ...opts.env ?? {} } : { ...process.env, ...opts.env ?? {} };
|
|
1971
|
+
const shell = opts.shell ?? process.platform === "win32";
|
|
1972
|
+
if (shell) {
|
|
1973
|
+
const line = [
|
|
1974
|
+
opts.command,
|
|
1975
|
+
...(opts.args ?? []).map((a) => quoteArg(a, process.platform === "win32"))
|
|
1976
|
+
].join(" ");
|
|
1977
|
+
this.child = spawn(line, [], {
|
|
1978
|
+
env,
|
|
1979
|
+
cwd: opts.cwd,
|
|
1980
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
1981
|
+
shell: true
|
|
1982
|
+
});
|
|
1983
|
+
} else {
|
|
1984
|
+
this.child = spawn(opts.command, opts.args ?? [], {
|
|
1985
|
+
env,
|
|
1986
|
+
cwd: opts.cwd,
|
|
1987
|
+
stdio: ["pipe", "pipe", "inherit"]
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
this.child.stdout.setEncoding("utf8");
|
|
1991
|
+
this.child.stdout.on("data", (chunk) => this.onStdout(chunk));
|
|
1992
|
+
this.child.on("close", () => this.onClose());
|
|
1993
|
+
this.child.on("error", (err) => {
|
|
1994
|
+
this.push({
|
|
1995
|
+
jsonrpc: "2.0",
|
|
1996
|
+
id: null,
|
|
1997
|
+
error: { code: -32e3, message: `transport error: ${err.message}` }
|
|
1998
|
+
});
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
async send(message) {
|
|
2002
|
+
if (this.closed) throw new Error("MCP transport is closed");
|
|
2003
|
+
return new Promise((resolve2, reject) => {
|
|
2004
|
+
const line = `${JSON.stringify(message)}
|
|
2005
|
+
`;
|
|
2006
|
+
this.child.stdin.write(line, "utf8", (err) => {
|
|
2007
|
+
if (err) reject(err);
|
|
2008
|
+
else resolve2();
|
|
2009
|
+
});
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
async *messages() {
|
|
2013
|
+
while (true) {
|
|
2014
|
+
if (this.queue.length > 0) {
|
|
2015
|
+
yield this.queue.shift();
|
|
2016
|
+
continue;
|
|
2017
|
+
}
|
|
2018
|
+
if (this.closed) return;
|
|
2019
|
+
const next = await new Promise((resolve2) => {
|
|
2020
|
+
this.waiters.push(resolve2);
|
|
2021
|
+
});
|
|
2022
|
+
if (next === null) return;
|
|
2023
|
+
yield next;
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
async close() {
|
|
2027
|
+
if (this.closed) return;
|
|
2028
|
+
this.closed = true;
|
|
2029
|
+
while (this.waiters.length > 0) this.waiters.shift()(null);
|
|
2030
|
+
try {
|
|
2031
|
+
this.child.stdin.end();
|
|
2032
|
+
} catch {
|
|
2033
|
+
}
|
|
2034
|
+
if (this.child.exitCode === null && !this.child.killed) {
|
|
2035
|
+
this.child.kill("SIGTERM");
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
/** Parse incoming stdout chunks into NDJSON messages. */
|
|
2039
|
+
onStdout(chunk) {
|
|
2040
|
+
this.stdoutBuffer += chunk;
|
|
2041
|
+
let newlineIdx;
|
|
2042
|
+
while ((newlineIdx = this.stdoutBuffer.indexOf("\n")) !== -1) {
|
|
2043
|
+
const line = this.stdoutBuffer.slice(0, newlineIdx).trim();
|
|
2044
|
+
this.stdoutBuffer = this.stdoutBuffer.slice(newlineIdx + 1);
|
|
2045
|
+
if (!line) continue;
|
|
2046
|
+
try {
|
|
2047
|
+
const msg = JSON.parse(line);
|
|
2048
|
+
this.push(msg);
|
|
2049
|
+
} catch {
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
onClose() {
|
|
2054
|
+
this.closed = true;
|
|
2055
|
+
while (this.waiters.length > 0) this.waiters.shift()(null);
|
|
2056
|
+
}
|
|
2057
|
+
push(msg) {
|
|
2058
|
+
const waiter = this.waiters.shift();
|
|
2059
|
+
if (waiter) waiter(msg);
|
|
2060
|
+
else this.queue.push(msg);
|
|
2061
|
+
}
|
|
2062
|
+
};
|
|
2063
|
+
function quoteArg(s, windows) {
|
|
2064
|
+
if (!windows) {
|
|
2065
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
2066
|
+
}
|
|
2067
|
+
return `"${s.replace(/"/g, '""')}"`;
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
// src/mcp/registry.ts
|
|
2071
|
+
async function bridgeMcpTools(client, opts = {}) {
|
|
2072
|
+
const registry = opts.registry ?? new ToolRegistry({ autoFlatten: opts.autoFlatten });
|
|
2073
|
+
const prefix = opts.namePrefix ?? "";
|
|
2074
|
+
const result = { registry, registeredNames: [], skipped: [] };
|
|
2075
|
+
const listed = await client.listTools();
|
|
2076
|
+
for (const mcpTool of listed.tools) {
|
|
2077
|
+
if (!mcpTool.name) {
|
|
2078
|
+
result.skipped.push({ name: "?", reason: "empty tool name" });
|
|
2079
|
+
continue;
|
|
2080
|
+
}
|
|
2081
|
+
const registeredName = `${prefix}${mcpTool.name}`;
|
|
2082
|
+
registry.register({
|
|
2083
|
+
name: registeredName,
|
|
2084
|
+
description: mcpTool.description ?? "",
|
|
2085
|
+
parameters: mcpTool.inputSchema,
|
|
2086
|
+
fn: async (args) => {
|
|
2087
|
+
const toolResult = await client.callTool(mcpTool.name, args);
|
|
2088
|
+
return flattenMcpResult(toolResult);
|
|
2089
|
+
}
|
|
2090
|
+
});
|
|
2091
|
+
result.registeredNames.push(registeredName);
|
|
2092
|
+
}
|
|
2093
|
+
return result;
|
|
2094
|
+
}
|
|
2095
|
+
function flattenMcpResult(result) {
|
|
2096
|
+
const parts = result.content.map(blockToString);
|
|
2097
|
+
const joined = parts.join("\n").trim();
|
|
2098
|
+
if (result.isError) {
|
|
2099
|
+
return `ERROR: ${joined || "(no error message from server)"}`;
|
|
2100
|
+
}
|
|
2101
|
+
return joined;
|
|
2102
|
+
}
|
|
2103
|
+
function blockToString(block) {
|
|
2104
|
+
if (block.type === "text") return block.text;
|
|
2105
|
+
if (block.type === "image") return `[image ${block.mimeType}, ${block.data.length} chars base64]`;
|
|
2106
|
+
return `[unknown block: ${JSON.stringify(block)}]`;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
1769
2109
|
// src/config.ts
|
|
1770
2110
|
import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
1771
2111
|
import { homedir as homedir2 } from "os";
|
|
@@ -1810,13 +2150,16 @@ function redactKey(key) {
|
|
|
1810
2150
|
}
|
|
1811
2151
|
|
|
1812
2152
|
// src/index.ts
|
|
1813
|
-
var VERSION = "0.
|
|
2153
|
+
var VERSION = "0.3.0-alpha.2";
|
|
1814
2154
|
export {
|
|
1815
2155
|
AppendOnlyLog,
|
|
1816
2156
|
CacheFirstLoop,
|
|
1817
2157
|
DeepSeekClient,
|
|
1818
2158
|
ImmutablePrefix,
|
|
2159
|
+
MCP_PROTOCOL_VERSION,
|
|
2160
|
+
McpClient,
|
|
1819
2161
|
SessionStats,
|
|
2162
|
+
StdioTransport,
|
|
1820
2163
|
StormBreaker,
|
|
1821
2164
|
ToolCallRepair,
|
|
1822
2165
|
ToolRegistry,
|
|
@@ -1826,6 +2169,7 @@ export {
|
|
|
1826
2169
|
aggregateBranchUsage,
|
|
1827
2170
|
analyzeSchema,
|
|
1828
2171
|
appendSessionMessage,
|
|
2172
|
+
bridgeMcpTools,
|
|
1829
2173
|
claudeEquivalentCost,
|
|
1830
2174
|
computeReplayStats,
|
|
1831
2175
|
costUsd,
|
|
@@ -1835,8 +2179,10 @@ export {
|
|
|
1835
2179
|
diffTranscripts,
|
|
1836
2180
|
emptyPlanState,
|
|
1837
2181
|
fetchWithRetry,
|
|
2182
|
+
flattenMcpResult,
|
|
1838
2183
|
flattenSchema,
|
|
1839
2184
|
harvest,
|
|
2185
|
+
isJsonRpcError,
|
|
1840
2186
|
isPlanStateEmpty,
|
|
1841
2187
|
isPlausibleKey,
|
|
1842
2188
|
listSessions,
|