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 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 candidate of iterateJsonObjects(reasoningContent)) {
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 scavenged = scavengeToolCalls(reasoningContent, {
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
- capabilities: { tools: {} },
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 reasoning"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "Esc (while thinking) \u2014 abort & summarize what was found so far"));
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) => {