reasonix 0.4.3 → 0.4.5
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/cli/index.js +161 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +95 -4
- package/dist/index.js +97 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -722,7 +722,19 @@ function scavengeToolCalls(reasoningContent, opts) {
|
|
|
722
722
|
const max = opts.maxCalls ?? 4;
|
|
723
723
|
const notes = [];
|
|
724
724
|
const out = [];
|
|
725
|
-
for (const
|
|
725
|
+
for (const invoke of iterateDsmlInvokes(reasoningContent)) {
|
|
726
|
+
if (out.length >= max) break;
|
|
727
|
+
if (!opts.allowedNames.has(invoke.name)) continue;
|
|
728
|
+
out.push({
|
|
729
|
+
function: {
|
|
730
|
+
name: invoke.name,
|
|
731
|
+
arguments: JSON.stringify(invoke.args)
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
notes.push(`scavenged DSML call: ${invoke.name}`);
|
|
735
|
+
}
|
|
736
|
+
const nonDsml = stripDsmlBlocks(reasoningContent);
|
|
737
|
+
for (const candidate of iterateJsonObjects(nonDsml)) {
|
|
726
738
|
if (out.length >= max) break;
|
|
727
739
|
const call = coerceToToolCall(candidate, opts.allowedNames);
|
|
728
740
|
if (call) {
|
|
@@ -732,6 +744,40 @@ function scavengeToolCalls(reasoningContent, opts) {
|
|
|
732
744
|
}
|
|
733
745
|
return { calls: out, notes };
|
|
734
746
|
}
|
|
747
|
+
function stripDsmlBlocks(text) {
|
|
748
|
+
let out = text;
|
|
749
|
+
out = out.replace(/<[||]DSML[||]function_calls>[\s\S]*?<\/?[||]DSML[||]function_calls>/g, "");
|
|
750
|
+
out = out.replace(/<[||]DSML[||]invoke\s+[^>]*>[\s\S]*?<\/[||]DSML[||]invoke>/g, "");
|
|
751
|
+
return out;
|
|
752
|
+
}
|
|
753
|
+
function* iterateDsmlInvokes(text) {
|
|
754
|
+
const INVOKE_RE = /<[||]DSML[||]invoke\s+name="([^"]+)">([\s\S]*?)<\/[||]DSML[||]invoke>/g;
|
|
755
|
+
for (const match of text.matchAll(INVOKE_RE)) {
|
|
756
|
+
const name = match[1];
|
|
757
|
+
const body = match[2];
|
|
758
|
+
if (!name || body === void 0) continue;
|
|
759
|
+
yield { name, args: parseDsmlParameters(body) };
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
function parseDsmlParameters(body) {
|
|
763
|
+
const PARAM_RE = /<[||]DSML[||]parameter\s+name="([^"]+)"(?:\s+string="(true|false)")?\s*>([\s\S]*?)<\/[||]DSML[||]parameter>/g;
|
|
764
|
+
const args = {};
|
|
765
|
+
for (const m of body.matchAll(PARAM_RE)) {
|
|
766
|
+
const key = m[1];
|
|
767
|
+
const stringFlag = m[2];
|
|
768
|
+
const raw = (m[3] ?? "").trim();
|
|
769
|
+
if (!key) continue;
|
|
770
|
+
if (stringFlag === "false") {
|
|
771
|
+
try {
|
|
772
|
+
args[key] = JSON.parse(raw);
|
|
773
|
+
continue;
|
|
774
|
+
} catch {
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
args[key] = raw;
|
|
778
|
+
}
|
|
779
|
+
return args;
|
|
780
|
+
}
|
|
735
781
|
function* iterateJsonObjects(text) {
|
|
736
782
|
for (let i = 0; i < text.length; i++) {
|
|
737
783
|
if (text[i] !== "{") continue;
|
|
@@ -917,14 +963,15 @@ var ToolCallRepair = class {
|
|
|
917
963
|
this.opts = opts;
|
|
918
964
|
this.storm = new StormBreaker(opts.stormWindow ?? 6, opts.stormThreshold ?? 3);
|
|
919
965
|
}
|
|
920
|
-
process(declaredCalls, reasoningContent) {
|
|
966
|
+
process(declaredCalls, reasoningContent, content = null) {
|
|
921
967
|
const report = {
|
|
922
968
|
scavenged: 0,
|
|
923
969
|
truncationsFixed: 0,
|
|
924
970
|
stormsBroken: 0,
|
|
925
971
|
notes: []
|
|
926
972
|
};
|
|
927
|
-
const
|
|
973
|
+
const combined = [reasoningContent ?? "", content ?? ""].filter(Boolean).join("\n");
|
|
974
|
+
const scavenged = scavengeToolCalls(combined || null, {
|
|
928
975
|
allowedNames: this.opts.allowedToolNames,
|
|
929
976
|
maxCalls: this.opts.maxScavenge ?? 4
|
|
930
977
|
});
|
|
@@ -1502,7 +1549,8 @@ var CacheFirstLoop = class {
|
|
|
1502
1549
|
const planState = preHarvestedPlanState ? preHarvestedPlanState : this.harvestEnabled ? await harvest(reasoningContent || null, this.client, this.harvestOptions) : emptyPlanState();
|
|
1503
1550
|
const { calls: repairedCalls, report } = this.repair.process(
|
|
1504
1551
|
toolCalls,
|
|
1505
|
-
reasoningContent || null
|
|
1552
|
+
reasoningContent || null,
|
|
1553
|
+
assistantContent || null
|
|
1506
1554
|
);
|
|
1507
1555
|
this.appendAndPersist(this.assistantMessage(assistantContent, repairedCalls));
|
|
1508
1556
|
yield {
|
|
@@ -2246,7 +2294,12 @@ var McpClient = class {
|
|
|
2246
2294
|
this.startReaderIfNeeded();
|
|
2247
2295
|
const result = await this.request("initialize", {
|
|
2248
2296
|
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
2249
|
-
|
|
2297
|
+
// Advertise every method the client can consume so servers know
|
|
2298
|
+
// they can send listChanged notifications etc. Sub-feature flags
|
|
2299
|
+
// (e.g. `resources.subscribe`) are omitted — we don't implement
|
|
2300
|
+
// those yet and the empty object means "method-level support, no
|
|
2301
|
+
// sub-features."
|
|
2302
|
+
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
2250
2303
|
clientInfo: this.clientInfo
|
|
2251
2304
|
});
|
|
2252
2305
|
this._serverCapabilities = result.capabilities ?? {};
|
|
@@ -2270,6 +2323,45 @@ var McpClient = class {
|
|
|
2270
2323
|
arguments: args ?? {}
|
|
2271
2324
|
});
|
|
2272
2325
|
}
|
|
2326
|
+
/**
|
|
2327
|
+
* List resources the server exposes. Supports a pagination cursor;
|
|
2328
|
+
* callers interested in the full set should loop on `nextCursor`.
|
|
2329
|
+
* Servers that don't support resources respond with method-not-found
|
|
2330
|
+
* (−32601) — we surface that as a thrown Error so callers can gate
|
|
2331
|
+
* on the `serverCapabilities.resources` field first.
|
|
2332
|
+
*/
|
|
2333
|
+
async listResources(cursor) {
|
|
2334
|
+
this.assertInitialized();
|
|
2335
|
+
return this.request("resources/list", {
|
|
2336
|
+
...cursor ? { cursor } : {}
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
/** Read the contents of a resource by URI. */
|
|
2340
|
+
async readResource(uri) {
|
|
2341
|
+
this.assertInitialized();
|
|
2342
|
+
return this.request("resources/read", {
|
|
2343
|
+
uri
|
|
2344
|
+
});
|
|
2345
|
+
}
|
|
2346
|
+
/** List prompt templates the server exposes. */
|
|
2347
|
+
async listPrompts(cursor) {
|
|
2348
|
+
this.assertInitialized();
|
|
2349
|
+
return this.request("prompts/list", {
|
|
2350
|
+
...cursor ? { cursor } : {}
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
/**
|
|
2354
|
+
* Fetch a rendered prompt by name. `args` supplies values for any
|
|
2355
|
+
* required template arguments; the server validates. Returns messages
|
|
2356
|
+
* ready to prepend to the model's input.
|
|
2357
|
+
*/
|
|
2358
|
+
async getPrompt(name, args) {
|
|
2359
|
+
this.assertInitialized();
|
|
2360
|
+
return this.request("prompts/get", {
|
|
2361
|
+
name,
|
|
2362
|
+
...args ? { arguments: args } : {}
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2273
2365
|
/** Close the transport and reject any outstanding requests. */
|
|
2274
2366
|
async close() {
|
|
2275
2367
|
for (const [, pending] of this.pending) {
|
|
@@ -3209,6 +3301,7 @@ function handleSlash(cmd, args, loop, ctx = {}) {
|
|
|
3209
3301
|
" /setup (exit + reconfigure) \u2192 run `reasonix setup`",
|
|
3210
3302
|
" /compact [cap] shrink large tool results in history (default 4k/result)",
|
|
3211
3303
|
" /think dump the most recent turn's full R1 reasoning (reasoner only)",
|
|
3304
|
+
" /tool [N] list tool calls (or dump full output of #N, 1=most recent)",
|
|
3212
3305
|
" /retry truncate & resend your last message (fresh sample from the model)",
|
|
3213
3306
|
" /apply (code mode) commit the pending edit blocks to disk",
|
|
3214
3307
|
" /discard (code mode) drop pending edits without writing",
|
|
@@ -3280,6 +3373,38 @@ function handleSlash(cmd, args, loop, ctx = {}) {
|
|
|
3280
3373
|
|
|
3281
3374
|
${raw.trim()}` };
|
|
3282
3375
|
}
|
|
3376
|
+
case "tool": {
|
|
3377
|
+
const history = ctx.toolHistory?.() ?? [];
|
|
3378
|
+
if (history.length === 0) {
|
|
3379
|
+
return {
|
|
3380
|
+
info: "no tool calls yet in this session. `/tool` lists them once the model has actually used a tool; `/tool N` dumps the full (untruncated) output of the Nth-most-recent."
|
|
3381
|
+
};
|
|
3382
|
+
}
|
|
3383
|
+
const raw = (args[0] ?? "").toLowerCase();
|
|
3384
|
+
if (raw === "" || raw === "list" || raw === "ls") {
|
|
3385
|
+
return { info: formatToolList(history) };
|
|
3386
|
+
}
|
|
3387
|
+
const n = Number.parseInt(raw, 10);
|
|
3388
|
+
if (!Number.isFinite(n) || n < 1) {
|
|
3389
|
+
return {
|
|
3390
|
+
info: "usage: /tool [N] (no arg \u2192 list; N=1 \u2192 most recent result in full, N=2 \u2192 previous, \u2026)"
|
|
3391
|
+
};
|
|
3392
|
+
}
|
|
3393
|
+
if (n > history.length) {
|
|
3394
|
+
return {
|
|
3395
|
+
info: `only ${history.length} tool call(s) in history \u2014 asked for #${n}. Try /tool with no arg to see the list.`
|
|
3396
|
+
};
|
|
3397
|
+
}
|
|
3398
|
+
const entry = history[history.length - n];
|
|
3399
|
+
if (!entry) {
|
|
3400
|
+
return { info: `could not read tool call #${n}` };
|
|
3401
|
+
}
|
|
3402
|
+
return {
|
|
3403
|
+
info: `\u21B3 tool<${entry.toolName}> #${n} (${entry.text.length} chars):
|
|
3404
|
+
|
|
3405
|
+
${entry.text}`
|
|
3406
|
+
};
|
|
3407
|
+
}
|
|
3283
3408
|
case "undo": {
|
|
3284
3409
|
if (!ctx.codeUndo) {
|
|
3285
3410
|
return {
|
|
@@ -3434,6 +3559,29 @@ ${raw.trim()}` };
|
|
|
3434
3559
|
return { unknown: true, info: `unknown command: /${cmd} (try /help)` };
|
|
3435
3560
|
}
|
|
3436
3561
|
}
|
|
3562
|
+
function formatToolList(history) {
|
|
3563
|
+
const total = history.length;
|
|
3564
|
+
const header = `Tool calls in this session (${total}, most recent first):`;
|
|
3565
|
+
const shown = Math.min(total, 10);
|
|
3566
|
+
const lines = [header];
|
|
3567
|
+
for (let i = 0; i < shown; i++) {
|
|
3568
|
+
const entry = history[total - 1 - i];
|
|
3569
|
+
if (!entry) continue;
|
|
3570
|
+
const idx = i + 1;
|
|
3571
|
+
const flat = entry.text.replace(/\s+/g, " ").trim();
|
|
3572
|
+
const preview = flat.length > 80 ? `${flat.slice(0, 80)}\u2026` : flat;
|
|
3573
|
+
const name = entry.toolName.length > 24 ? `${entry.toolName.slice(0, 23)}\u2026` : entry.toolName;
|
|
3574
|
+
lines.push(
|
|
3575
|
+
` #${String(idx).padStart(2)} ${name.padEnd(24)} ${String(entry.text.length).padStart(6)} chars ${preview}`
|
|
3576
|
+
);
|
|
3577
|
+
}
|
|
3578
|
+
if (total > shown) {
|
|
3579
|
+
lines.push(` \u2026 (${total - shown} earlier, reach with /tool N)`);
|
|
3580
|
+
}
|
|
3581
|
+
lines.push("");
|
|
3582
|
+
lines.push("View full output: /tool N (N=1 \u2192 most recent)");
|
|
3583
|
+
return lines.join("\n");
|
|
3584
|
+
}
|
|
3437
3585
|
function compactNum(n) {
|
|
3438
3586
|
if (n < 1e3) return String(n);
|
|
3439
3587
|
const k = n / 1e3;
|
|
@@ -3496,6 +3644,7 @@ function App({
|
|
|
3496
3644
|
const pendingEdits = useRef([]);
|
|
3497
3645
|
const promptHistory = useRef([]);
|
|
3498
3646
|
const historyCursor = useRef(-1);
|
|
3647
|
+
const toolHistoryRef = useRef([]);
|
|
3499
3648
|
const [summary, setSummary] = useState2({
|
|
3500
3649
|
turns: 0,
|
|
3501
3650
|
totalCostUsd: 0,
|
|
@@ -3645,7 +3794,8 @@ function App({
|
|
|
3645
3794
|
codeApply: codeMode ? codeApply : void 0,
|
|
3646
3795
|
codeDiscard: codeMode ? codeDiscard : void 0,
|
|
3647
3796
|
codeRoot: codeMode?.rootDir,
|
|
3648
|
-
pendingEditCount: codeMode ? pendingEdits.current.length : void 0
|
|
3797
|
+
pendingEditCount: codeMode ? pendingEdits.current.length : void 0,
|
|
3798
|
+
toolHistory: () => toolHistoryRef.current
|
|
3649
3799
|
});
|
|
3650
3800
|
if (result.exit) {
|
|
3651
3801
|
transcriptRef.current?.end();
|
|
@@ -3758,6 +3908,10 @@ function App({
|
|
|
3758
3908
|
} else if (ev.role === "tool") {
|
|
3759
3909
|
flush();
|
|
3760
3910
|
setOngoingTool(null);
|
|
3911
|
+
toolHistoryRef.current.push({
|
|
3912
|
+
toolName: ev.toolName ?? "?",
|
|
3913
|
+
text: ev.content
|
|
3914
|
+
});
|
|
3761
3915
|
setHistorical((prev) => [
|
|
3762
3916
|
...prev,
|
|
3763
3917
|
{
|
|
@@ -3858,7 +4012,7 @@ function summarizeToolArgs(name, args) {
|
|
|
3858
4012
|
return args.length > 80 ? `${args.slice(0, 80)}\u2026` : args;
|
|
3859
4013
|
}
|
|
3860
4014
|
function CommandStrip({ codeMode }) {
|
|
3861
|
-
return /* @__PURE__ */ React6.createElement(Box6, { paddingX: 2, flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "/help \xB7 /preset ", "<fast|smart|max>", " \xB7 /mcp \xB7 /compact \xB7 /sessions \xB7 /setup \xB7 /clear \xB7 /exit"), codeMode ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, 'code mode: /apply (y) \xB7 /discard (n) \xB7 /undo \xB7 /commit "msg" \u2014 edits NEVER write without /apply') : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u2191/\u2193 recall prompts \xB7 /retry re-send last \xB7 /think see R1 full
|
|
4015
|
+
return /* @__PURE__ */ React6.createElement(Box6, { paddingX: 2, flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "/help \xB7 /preset ", "<fast|smart|max>", " \xB7 /mcp \xB7 /compact \xB7 /sessions \xB7 /setup \xB7 /clear \xB7 /exit"), codeMode ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, 'code mode: /apply (y) \xB7 /discard (n) \xB7 /undo \xB7 /commit "msg" \u2014 edits NEVER write without /apply') : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u2191/\u2193 recall prompts \xB7 /retry re-send last \xB7 /think see R1 reasoning \xB7 /tool N full tool output"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "Esc (while thinking) \u2014 abort & summarize what was found so far"));
|
|
3862
4016
|
}
|
|
3863
4017
|
function formatEditResults(results) {
|
|
3864
4018
|
const lines = results.map((r) => {
|