@proxysoul/soulforge 2.20.19 → 2.20.21

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.
Files changed (2) hide show
  1. package/dist/index.js +377 -105
  2. package/package.json +8 -8
package/dist/index.js CHANGED
@@ -72638,7 +72638,7 @@ var package_default;
72638
72638
  var init_package = __esm(() => {
72639
72639
  package_default = {
72640
72640
  name: "@proxysoul/soulforge",
72641
- version: "2.20.19",
72641
+ version: "2.20.21",
72642
72642
  description: "Graph-powered code intelligence \u2014 multi-agent coding with codebase-aware AI",
72643
72643
  repository: {
72644
72644
  type: "git",
@@ -72734,7 +72734,7 @@ var init_package = __esm(() => {
72734
72734
  "@modelcontextprotocol/sdk": "^1.29.0",
72735
72735
  "@mozilla/readability": "0.6.0",
72736
72736
  "@openrouter/ai-sdk-provider": "2.9.1",
72737
- "@opentui/react": "0.4.1",
72737
+ "@opentui/react": "0.4.2",
72738
72738
  ai: "^6.0.208",
72739
72739
  "ghostty-opentui": "1.5.0",
72740
72740
  ignore: "^7.0.5",
@@ -72756,12 +72756,12 @@ var init_package = __esm(() => {
72756
72756
  zustand: "5.0.14"
72757
72757
  },
72758
72758
  optionalDependencies: {
72759
- "@opentui/core-darwin-arm64": "0.4.1",
72760
- "@opentui/core-darwin-x64": "0.4.1",
72761
- "@opentui/core-linux-arm64": "0.4.1",
72762
- "@opentui/core-linux-x64": "0.4.1",
72763
- "@opentui/core-win32-arm64": "0.4.1",
72764
- "@opentui/core-win32-x64": "0.4.1"
72759
+ "@opentui/core-darwin-arm64": "0.4.2",
72760
+ "@opentui/core-darwin-x64": "0.4.2",
72761
+ "@opentui/core-linux-arm64": "0.4.2",
72762
+ "@opentui/core-linux-x64": "0.4.2",
72763
+ "@opentui/core-win32-arm64": "0.4.2",
72764
+ "@opentui/core-win32-x64": "0.4.2"
72765
72765
  }
72766
72766
  };
72767
72767
  });
@@ -73163,6 +73163,11 @@ function getCompatReasoningBody(modelId, config2) {
73163
73163
  if (g)
73164
73164
  effort = g === "off" ? "off" : g;
73165
73165
  }
73166
+ if (provider === "llmgateway") {
73167
+ const l = config2.performance?.llmgatewayReasoningEffort;
73168
+ if (l && l !== "off")
73169
+ effort = l;
73170
+ }
73166
73171
  if (!effort || effort === "off") {
73167
73172
  const e = config2.performance?.effort;
73168
73173
  if (e && e !== "off") {
@@ -101580,6 +101585,269 @@ var init_lifecycle = __esm(() => {
101580
101585
  VERSION_CACHE_TTL = 10 * 60 * 1000;
101581
101586
  });
101582
101587
 
101588
+ // src/core/llm/providers/recover-leaked-tool-calls.ts
101589
+ function invokeRe() {
101590
+ return /<(?:antml:)?invoke\s+name="([^"]+)"\s*>([\s\S]*?)<\/(?:antml:)?invoke>/g;
101591
+ }
101592
+ function paramRe() {
101593
+ return /<(?:antml:)?parameter\s+name="([^"]+)"\s*>([\s\S]*?)<\/(?:antml:)?parameter>/g;
101594
+ }
101595
+ function coerce(raw) {
101596
+ const trimmed = raw.trim();
101597
+ if (trimmed === "")
101598
+ return "";
101599
+ try {
101600
+ return JSON.parse(trimmed);
101601
+ } catch {
101602
+ return raw;
101603
+ }
101604
+ }
101605
+ function buildArgs(body2) {
101606
+ const args2 = {};
101607
+ for (const m of body2.matchAll(paramRe())) {
101608
+ const name30 = m[1];
101609
+ const raw = m[2];
101610
+ if (name30 === undefined || raw === undefined)
101611
+ continue;
101612
+ args2[name30] = coerce(raw);
101613
+ }
101614
+ return JSON.stringify(args2);
101615
+ }
101616
+ function makeToolCall(name30, body2) {
101617
+ return {
101618
+ type: "tool-call",
101619
+ toolCallId: `leak_${crypto.randomUUID()}`,
101620
+ toolName: name30,
101621
+ input: buildArgs(body2)
101622
+ };
101623
+ }
101624
+ function parseInvokeBlock(block, valid) {
101625
+ const calls = [];
101626
+ for (const m of block.matchAll(invokeRe())) {
101627
+ const name30 = m[1];
101628
+ const body2 = m[2];
101629
+ if (name30 === undefined || body2 === undefined || !valid.has(name30))
101630
+ continue;
101631
+ calls.push(makeToolCall(name30, body2));
101632
+ }
101633
+ return calls;
101634
+ }
101635
+ function validToolNames(params) {
101636
+ const names = new Set;
101637
+ for (const tool4 of params.tools ?? []) {
101638
+ if (tool4.type === "function")
101639
+ names.add(tool4.name);
101640
+ }
101641
+ return names;
101642
+ }
101643
+ function buildLeakTransform(valid) {
101644
+ let mode = "pass";
101645
+ let tail = "";
101646
+ let capture = "";
101647
+ let textId;
101648
+ let textOpen = false;
101649
+ let sawRealToolCall = false;
101650
+ const recovered = [];
101651
+ const suppressed = [];
101652
+ const emitText = (controller, text2) => {
101653
+ if (!text2)
101654
+ return;
101655
+ if (textOpen && textId) {
101656
+ controller.enqueue({ type: "text-delta", id: textId, delta: text2 });
101657
+ } else {
101658
+ const id = `leak_${crypto.randomUUID()}`;
101659
+ controller.enqueue({ type: "text-start", id });
101660
+ controller.enqueue({ type: "text-delta", id, delta: text2 });
101661
+ controller.enqueue({ type: "text-end", id });
101662
+ }
101663
+ };
101664
+ const holdLen = (buf) => {
101665
+ const lt = buf.lastIndexOf("<");
101666
+ if (lt === -1)
101667
+ return 0;
101668
+ const suffix = buf.slice(lt);
101669
+ for (const lit of SENTINEL_LITERALS) {
101670
+ if (suffix.length < lit.length && lit.startsWith(suffix))
101671
+ return suffix.length;
101672
+ }
101673
+ return 0;
101674
+ };
101675
+ const tryClose = (controller) => {
101676
+ const isWrapper = /^<(?:antml:)?function_calls\b/.test(capture);
101677
+ const closer = isWrapper ? /<\/(?:antml:)?function_calls>/ : /<\/(?:antml:)?invoke>/;
101678
+ const m = closer.exec(capture);
101679
+ if (!m)
101680
+ return null;
101681
+ const end = m.index + m[0].length;
101682
+ const block = capture.slice(0, end);
101683
+ const rest = capture.slice(end);
101684
+ const calls = parseInvokeBlock(block, valid);
101685
+ if (calls.length > 0) {
101686
+ recovered.push(...calls);
101687
+ suppressed.push(block);
101688
+ } else {
101689
+ emitText(controller, block);
101690
+ }
101691
+ mode = "pass";
101692
+ capture = "";
101693
+ return rest;
101694
+ };
101695
+ const pass = (controller, text2) => {
101696
+ let buf = tail + text2;
101697
+ tail = "";
101698
+ while (true) {
101699
+ const m = SENTINEL.exec(buf);
101700
+ if (!m) {
101701
+ const hold = holdLen(buf);
101702
+ emitText(controller, buf.slice(0, buf.length - hold));
101703
+ tail = buf.slice(buf.length - hold);
101704
+ return;
101705
+ }
101706
+ emitText(controller, buf.slice(0, m.index));
101707
+ mode = "capture";
101708
+ capture = buf.slice(m.index);
101709
+ const rest = tryClose(controller);
101710
+ if (rest === null)
101711
+ return;
101712
+ buf = rest;
101713
+ }
101714
+ };
101715
+ const flushPending = (controller) => {
101716
+ if (mode === "capture") {
101717
+ emitText(controller, capture);
101718
+ capture = "";
101719
+ mode = "pass";
101720
+ }
101721
+ if (tail) {
101722
+ emitText(controller, tail);
101723
+ tail = "";
101724
+ }
101725
+ };
101726
+ return new TransformStream({
101727
+ transform(part, controller) {
101728
+ switch (part.type) {
101729
+ case "text-start":
101730
+ textId = part.id;
101731
+ textOpen = true;
101732
+ controller.enqueue(part);
101733
+ return;
101734
+ case "text-delta":
101735
+ if (mode === "capture") {
101736
+ capture += part.delta;
101737
+ const rest = tryClose(controller);
101738
+ if (rest !== null && rest)
101739
+ pass(controller, rest);
101740
+ } else {
101741
+ pass(controller, part.delta);
101742
+ }
101743
+ return;
101744
+ case "text-end":
101745
+ flushPending(controller);
101746
+ controller.enqueue(part);
101747
+ textOpen = false;
101748
+ return;
101749
+ case "tool-call":
101750
+ case "tool-input-start":
101751
+ sawRealToolCall = true;
101752
+ flushPending(controller);
101753
+ if (recovered.length > 0) {
101754
+ emitText(controller, suppressed.join(""));
101755
+ recovered.length = 0;
101756
+ suppressed.length = 0;
101757
+ }
101758
+ controller.enqueue(part);
101759
+ return;
101760
+ case "finish":
101761
+ flushPending(controller);
101762
+ if (recovered.length > 0 && !sawRealToolCall) {
101763
+ for (const call of recovered)
101764
+ controller.enqueue(call);
101765
+ controller.enqueue({
101766
+ ...part,
101767
+ finishReason: { ...part.finishReason, unified: "tool-calls" }
101768
+ });
101769
+ } else {
101770
+ controller.enqueue(part);
101771
+ }
101772
+ return;
101773
+ default:
101774
+ controller.enqueue(part);
101775
+ }
101776
+ }
101777
+ });
101778
+ }
101779
+ function recoverGenerate(content, valid) {
101780
+ if (content.some((c) => c.type === "tool-call"))
101781
+ return { content, recovered: false };
101782
+ const out2 = [];
101783
+ const calls = [];
101784
+ for (const part of content) {
101785
+ if (part.type !== "text") {
101786
+ out2.push(part);
101787
+ continue;
101788
+ }
101789
+ let cleaned = "";
101790
+ let last = 0;
101791
+ let found = false;
101792
+ for (const m of part.text.matchAll(invokeRe())) {
101793
+ const name30 = m[1];
101794
+ const body2 = m[2];
101795
+ const whole = m[0];
101796
+ const idx = m.index;
101797
+ if (name30 === undefined || body2 === undefined || idx === undefined || !valid.has(name30)) {
101798
+ continue;
101799
+ }
101800
+ found = true;
101801
+ calls.push(makeToolCall(name30, body2));
101802
+ cleaned += part.text.slice(last, idx);
101803
+ last = idx + whole.length;
101804
+ }
101805
+ if (!found) {
101806
+ out2.push(part);
101807
+ continue;
101808
+ }
101809
+ cleaned += part.text.slice(last);
101810
+ cleaned = cleaned.replace(/<\/?(?:antml:)?function_calls>/g, "").trim();
101811
+ if (cleaned.length > 0)
101812
+ out2.push({ ...part, text: cleaned });
101813
+ }
101814
+ if (calls.length === 0)
101815
+ return { content, recovered: false };
101816
+ out2.push(...calls);
101817
+ return { content: out2, recovered: true };
101818
+ }
101819
+ function recoverLeakedToolCallsMiddleware() {
101820
+ return {
101821
+ specificationVersion: "v3",
101822
+ async wrapStream({ doStream, params }) {
101823
+ const valid = validToolNames(params);
101824
+ const result = await doStream();
101825
+ if (valid.size === 0)
101826
+ return result;
101827
+ return { ...result, stream: result.stream.pipeThrough(buildLeakTransform(valid)) };
101828
+ },
101829
+ async wrapGenerate({ doGenerate, params }) {
101830
+ const valid = validToolNames(params);
101831
+ const result = await doGenerate();
101832
+ if (valid.size === 0)
101833
+ return result;
101834
+ const { content, recovered } = recoverGenerate(result.content, valid);
101835
+ if (!recovered)
101836
+ return result;
101837
+ return {
101838
+ ...result,
101839
+ content,
101840
+ finishReason: { ...result.finishReason, unified: "tool-calls" }
101841
+ };
101842
+ }
101843
+ };
101844
+ }
101845
+ var SENTINEL, SENTINEL_LITERALS;
101846
+ var init_recover_leaked_tool_calls = __esm(() => {
101847
+ SENTINEL = /<(?:antml:)?(?:invoke\b|function_calls\b)/;
101848
+ SENTINEL_LITERALS = ["<invoke", "<function_calls", "<invoke", "<function_calls"];
101849
+ });
101850
+
101583
101851
  // node_modules/@msgpack/msgpack/dist/utils/int.js
101584
101852
  var require_int = __commonJS((exports) => {
101585
101853
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -395831,7 +396099,7 @@ var init_chunk_bdqvmfwv = __esm(async () => {
395831
396099
  react_devtools_core_default.connectToDevTools();
395832
396100
  });
395833
396101
 
395834
- // node_modules/@opentui/react/chunk-fm0c65gm.js
396102
+ // node_modules/@opentui/react/chunk-tgg3x73e.js
395835
396103
  import {
395836
396104
  ASCIIFontRenderable,
395837
396105
  BoxRenderable,
@@ -396004,7 +396272,7 @@ function createRoot(renderer) {
396004
396272
  var import_react2, import_react3, import_react4, import_jsx_dev_runtime, import_react_reconciler, import_constants, import_react5, import_constants2, textNodeKeys, SpanRenderable, TextModifierRenderable, BoldSpanRenderable, ItalicSpanRenderable, UnderlineSpanRenderable, LineBreakRenderable, LinkRenderable, baseComponents, componentCatalogue, AppContext, useAppContext = () => {
396005
396273
  return import_react2.useContext(AppContext);
396006
396274
  }, ErrorBoundary, package_default2, idCounter, currentUpdatePriority, hostConfig, reconciler, _r, flushSync, createPortal;
396007
- var init_chunk_fm0c65gm = __esm(async () => {
396275
+ var init_chunk_tgg3x73e = __esm(async () => {
396008
396276
  init_chunk_2mx7fq49();
396009
396277
  import_react2 = __toESM(require_react(), 1);
396010
396278
  import_react3 = __toESM(require_react(), 1);
@@ -396118,7 +396386,7 @@ var init_chunk_fm0c65gm = __esm(async () => {
396118
396386
  };
396119
396387
  package_default2 = {
396120
396388
  name: "@opentui/react",
396121
- version: "0.4.1",
396389
+ version: "0.4.2",
396122
396390
  description: "React renderer for building terminal user interfaces using OpenTUI core",
396123
396391
  license: "MIT",
396124
396392
  repository: {
@@ -396685,7 +396953,7 @@ var import_react6, import_react7, import_react8, import_react9, import_react10,
396685
396953
  };
396686
396954
  var init_react2 = __esm(async () => {
396687
396955
  init_chunk_2mx7fq49();
396688
- await init_chunk_fm0c65gm();
396956
+ await init_chunk_tgg3x73e();
396689
396957
  import_react6 = __toESM(require_react(), 1);
396690
396958
  import_react7 = __toESM(require_react(), 1);
396691
396959
  import_react8 = __toESM(require_react(), 1);
@@ -403975,67 +404243,6 @@ function flushEmergencySession() {
403975
404243
  }
403976
404244
  var _snapshot = null;
403977
404245
 
403978
- // src/core/terminal/resize-handler.ts
403979
- function setupTerminalResize(r) {
403980
- r.addInputHandler((sequence) => {
403981
- const m2 = sequence.match(/^\x1b\[48;(\d+);(\d+)(?:;\d+;\d+)?t$/);
403982
- if (!m2?.[1] || !m2[2])
403983
- return false;
403984
- const rows = Number.parseInt(m2[1], 10);
403985
- const cols = Number.parseInt(m2[2], 10);
403986
- if (rows > 0 && cols > 0)
403987
- r.resize(cols, rows);
403988
- return true;
403989
- });
403990
- if (process.stdout.isTTY) {
403991
- process.stdout.write("\x1B[?2048h");
403992
- }
403993
- const onResize = () => {
403994
- const cols = process.stdout.columns;
403995
- const rows = process.stdout.rows;
403996
- if (!cols || !rows)
403997
- return;
403998
- if (cols !== r.terminalWidth || rows !== r.terminalHeight) {
403999
- r.resize(cols, rows);
404000
- }
404001
- };
404002
- process.stdout.on("resize", onResize);
404003
- const onSigwinch = () => {
404004
- setTimeout(() => {
404005
- const cols = process.stdout.columns;
404006
- const rows = process.stdout.rows;
404007
- if (!cols || !rows)
404008
- return;
404009
- if (cols !== r.terminalWidth || rows !== r.terminalHeight) {
404010
- r.resize(cols, rows);
404011
- }
404012
- }, 50);
404013
- };
404014
- process.on("SIGWINCH", onSigwinch);
404015
- const resizePoll = setInterval(() => {
404016
- try {
404017
- const cols = process.stdout.columns;
404018
- const rows = process.stdout.rows;
404019
- if (!cols || !rows)
404020
- return;
404021
- if (cols !== r.terminalWidth || rows !== r.terminalHeight) {
404022
- r.resize(cols, rows);
404023
- }
404024
- } catch {}
404025
- }, 1000);
404026
- resizePoll.unref?.();
404027
- return () => {
404028
- process.stdout.removeListener("resize", onResize);
404029
- process.removeListener("SIGWINCH", onSigwinch);
404030
- clearInterval(resizePoll);
404031
- if (process.stdout.isTTY) {
404032
- try {
404033
- process.stdout.write("\x1B[?2048l");
404034
- } catch {}
404035
- }
404036
- };
404037
- }
404038
-
404039
404246
  // src/stores/statusbar.ts
404040
404247
  import { execFile as execFile2 } from "child_process";
404041
404248
  function matchCopilotPricing(model) {
@@ -404932,7 +405139,7 @@ function restoreTerminal() {
404932
405139
  }
404933
405140
  } catch {}
404934
405141
  try {
404935
- process.stdout.write("\x1B[?2048l\x1B[?25h\x1B[0m");
405142
+ process.stdout.write("\x1B[?25h\x1B[0m");
404936
405143
  } catch {}
404937
405144
  }
404938
405145
  function runCleanup() {
@@ -405237,9 +405444,6 @@ async function start2(opts) {
405237
405444
  } catch {}
405238
405445
  r.setMaxListeners(30);
405239
405446
  r.keyInput.setMaxListeners(30);
405240
- if (process.stdout.isTTY) {
405241
- setupTerminalResize(r);
405242
- }
405243
405447
  {
405244
405448
  const { extend: extend3 } = await init_react2().then(() => exports_react);
405245
405449
  const { TextTableRenderable } = await import("@opentui/core");
@@ -409731,16 +409935,22 @@ var init_addons = __esm(() => {
409731
409935
  function isAnthropicModel(modelId) {
409732
409936
  return modelId.toLowerCase().startsWith("claude");
409733
409937
  }
409938
+ function leakRecoveryEnabled() {
409939
+ const v2 = process.env.SOULFORGE_PROXY_TOOL_RECOVERY;
409940
+ return v2 !== "0" && v2 !== "false";
409941
+ }
409734
409942
  var baseURL, GPT_55_INPUT_CONTEXT2 = 272000, proxy2;
409735
409943
  var init_proxy2 = __esm(() => {
409736
409944
  init_dist6();
409737
409945
  init_dist8();
409946
+ init_dist5();
409738
409947
  init_config2();
409739
409948
  init_key_resolver();
409740
409949
  init_lifecycle();
409741
409950
  init_compat_reasoning();
409742
409951
  init_context_windows();
409743
409952
  init_reasoning_fetch();
409953
+ init_recover_leaked_tool_calls();
409744
409954
  baseURL = process.env.PROXY_API_URL || "http://127.0.0.1:8317/v1";
409745
409955
  proxy2 = {
409746
409956
  id: "proxy",
@@ -409752,7 +409962,12 @@ var init_proxy2 = __esm(() => {
409752
409962
  createModel(modelId) {
409753
409963
  const apiKey = getActiveProxyApiKey();
409754
409964
  if (isAnthropicModel(modelId)) {
409755
- return createAnthropic({ baseURL, apiKey, fetch: withSessionHeaders() })(modelId);
409965
+ const model = createAnthropic({
409966
+ baseURL,
409967
+ apiKey,
409968
+ fetch: withSessionHeaders()
409969
+ })(modelId);
409970
+ return leakRecoveryEnabled() ? wrapLanguageModel({ model, middleware: recoverLeakedToolCallsMiddleware() }) : model;
409756
409971
  }
409757
409972
  const reasoningBody = getCompatReasoningBody(`proxy/${modelId}`, loadConfig());
409758
409973
  const reasoningFetch = createSessionFetchWrapper(reasoningBody);
@@ -475266,7 +475481,7 @@ function mapNvimMode(raw2) {
475266
475481
  return raw2;
475267
475482
  }
475268
475483
  }
475269
- function useNeovim(active, nvimPath, nvimConfig, onExit, hasTabBar = true, splitPct = 60) {
475484
+ function useNeovim(active, nvimPath, nvimConfig, onExit, hasTabBar = true, splitPct = 60, termWidth = 120, termHeight = 40) {
475270
475485
  const nvimRef = import_react38.useRef(null);
475271
475486
  const mountedRef = import_react38.useRef(true);
475272
475487
  const launchingRef = import_react38.useRef(false);
@@ -475294,9 +475509,7 @@ function useNeovim(active, nvimPath, nvimConfig, onExit, hasTabBar = true, split
475294
475509
  launchingRef.current = false;
475295
475510
  return;
475296
475511
  }
475297
- const termCols = process.stdout.columns ?? 120;
475298
- const termRows = process.stdout.rows ?? 40;
475299
- const dims = getEditorDimensions(termCols, termRows, hasTabBar, splitPct);
475512
+ const dims = getEditorDimensions(termWidth, termHeight, hasTabBar, splitPct);
475300
475513
  launchNeovim(nvimPath ?? "nvim", dims.cols, dims.rows, nvimConfig).then((nvim) => {
475301
475514
  if (!mountedRef.current) {
475302
475515
  shutdownNeovim(nvim).catch(() => {});
@@ -475331,26 +475544,17 @@ function useNeovim(active, nvimPath, nvimConfig, onExit, hasTabBar = true, split
475331
475544
  }).finally(() => {
475332
475545
  launchingRef.current = false;
475333
475546
  });
475334
- }, [active, nvimPath, nvimConfig, hasTabBar, splitPct, launchGeneration]);
475547
+ }, [active, nvimPath, nvimConfig, hasTabBar, splitPct, launchGeneration, termWidth, termHeight]);
475335
475548
  import_react38.useEffect(() => {
475336
475549
  if (!ready || !active)
475337
475550
  return;
475338
- const onResize = () => {
475339
- const nvim = nvimRef.current;
475340
- if (!nvim || !mountedRef.current)
475341
- return;
475342
- const tc = process.stdout.columns ?? 120;
475343
- const tr2 = process.stdout.rows ?? 40;
475344
- const d3 = getEditorDimensions(tc, tr2, hasTabBar, splitPct);
475345
- nvim.pty.resize(d3.cols, d3.rows);
475346
- setNvimDims({ cols: d3.cols, rows: d3.rows });
475347
- };
475348
- onResize();
475349
- process.stdout.on("resize", onResize);
475350
- return () => {
475351
- process.stdout.removeListener("resize", onResize);
475352
- };
475353
- }, [ready, active, hasTabBar, splitPct]);
475551
+ const nvim = nvimRef.current;
475552
+ if (!nvim || !mountedRef.current)
475553
+ return;
475554
+ const d3 = getEditorDimensions(termWidth, termHeight, hasTabBar, splitPct);
475555
+ nvim.pty.resize(d3.cols, d3.rows);
475556
+ setNvimDims({ cols: d3.cols, rows: d3.rows });
475557
+ }, [ready, active, hasTabBar, splitPct, termWidth, termHeight]);
475354
475558
  import_react38.useEffect(() => {
475355
475559
  if (!ready || !active)
475356
475560
  return;
@@ -489406,6 +489610,15 @@ function fuzzyMatch2(pattern, target) {
489406
489610
  }
489407
489611
  if (pi < p4.length)
489408
489612
  return null;
489613
+ const strip = (s2) => s2.startsWith("/") ? s2.slice(1) : s2;
489614
+ const sp = strip(p4);
489615
+ const st2 = strip(t2);
489616
+ if (sp.length > 0 && st2.startsWith(sp)) {
489617
+ score += 100;
489618
+ if (st2.length === sp.length)
489619
+ score += 50;
489620
+ score -= st2.length;
489621
+ }
489409
489622
  return { entry: target, score, indices };
489410
489623
  }
489411
489624
  function fuzzyFilter(pattern, entries2, limit = 50) {
@@ -496741,6 +496954,13 @@ function fuzzyScore3(query2, target) {
496741
496954
  score += 5;
496742
496955
  }
496743
496956
  score -= indices.length > 0 ? indices[0] : 0;
496957
+ const stripped = lower2.startsWith("/") ? lower2.slice(1) : lower2;
496958
+ if (stripped.startsWith(q3)) {
496959
+ score += 100;
496960
+ if (stripped.length === q3.length)
496961
+ score += 50;
496962
+ score -= stripped.length;
496963
+ }
496744
496964
  return { score, indices };
496745
496965
  }
496746
496966
  function OptionRow2({
@@ -498277,7 +498497,7 @@ function getStatusTag(id) {
498277
498497
  return s2.active;
498278
498498
  }
498279
498499
  function fuzzyMatch3(query2, target) {
498280
- const q3 = query2.toLowerCase();
498500
+ const q3 = query2.toLowerCase().replace(/\s+/g, "");
498281
498501
  const t2 = target.toLowerCase();
498282
498502
  let qi = 0;
498283
498503
  for (let ti = 0;ti < t2.length && qi < q3.length; ti++) {
@@ -510136,6 +510356,8 @@ function readValuesFromLayer(layer) {
510136
510356
  v3.openrouterExcludeReasoning = layer.performance.openrouterExcludeReasoning;
510137
510357
  if (layer.performance?.compatReasoningEffort !== undefined)
510138
510358
  v3.compatReasoningEffort = layer.performance.compatReasoningEffort;
510359
+ if (layer.performance?.llmgatewayReasoningEffort !== undefined)
510360
+ v3.llmgatewayReasoningEffort = layer.performance.llmgatewayReasoningEffort;
510139
510361
  if (layer.performance?.groqReasoningEffort !== undefined)
510140
510362
  v3.groqReasoningEffort = layer.performance.groqReasoningEffort;
510141
510363
  if (layer.performance?.openaiReasoningSummary !== undefined)
@@ -510215,6 +510437,10 @@ function buildPatch(key3, value) {
510215
510437
  return { performance: { openrouterExcludeReasoning: value } };
510216
510438
  case "compatReasoningEffort":
510217
510439
  return { performance: { compatReasoningEffort: value } };
510440
+ case "llmgatewayReasoningEffort":
510441
+ return {
510442
+ performance: { llmgatewayReasoningEffort: value }
510443
+ };
510218
510444
  case "groqReasoningEffort":
510219
510445
  return { performance: { groqReasoningEffort: value } };
510220
510446
  case "openaiReasoningSummary":
@@ -510282,7 +510508,7 @@ function ProviderSettings({
510282
510508
  const popupWidth = Math.min(MAX_POPUP_WIDTH4, Math.floor(termCols * 0.85));
510283
510509
  const maxVisible = Math.max(6, Math.floor(containerRows * 0.85) - CHROME_ROWS6);
510284
510510
  const t2 = useTheme();
510285
- const [tab, setTab] = import_react153.useState("claude");
510511
+ const [tab, setTab] = import_react153.useState("llmgateway");
510286
510512
  const [cursor3, setCursor] = import_react153.useState(0);
510287
510513
  const [scope, setScope] = import_react153.useState(() => detectInitialScope(projectConfig));
510288
510514
  const vals = effectiveValues(globalConfig2, projectConfig);
@@ -510413,6 +510639,12 @@ function ProviderSettings({
510413
510639
  title: "Provider Options",
510414
510640
  titleIcon: "system",
510415
510641
  tabs: [
510642
+ {
510643
+ id: "llmgateway",
510644
+ label: "LLM Gateway",
510645
+ icon: "cloud",
510646
+ blurb: "unified gateway \xB7 effort \xB7 cache \xB7 web search"
510647
+ },
510416
510648
  { id: "claude", label: "Claude", icon: "ai", blurb: "thinking \xB7 reasoning \xB7 beta" },
510417
510649
  { id: "openai", label: "OpenAI", icon: "ai", blurb: "reasoning \xB7 service tier" },
510418
510650
  { id: "google", label: "Gemini", icon: "ai", blurb: "thinking level \xB7 budget" },
@@ -510459,6 +510691,7 @@ function ProviderSettings({
510459
510691
  flexDirection: "row",
510460
510692
  backgroundColor: t2.bgPopup,
510461
510693
  paddingX: 1,
510694
+ height: 1,
510462
510695
  children: [
510463
510696
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
510464
510697
  bg: t2.bgPopup,
@@ -510482,9 +510715,11 @@ function ProviderSettings({
510482
510715
  flexDirection: "row",
510483
510716
  backgroundColor: t2.bgPopup,
510484
510717
  paddingX: 1,
510718
+ height: 1,
510485
510719
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
510486
510720
  bg: t2.bgPopup,
510487
510721
  fg: t2.textFaint,
510722
+ truncate: true,
510488
510723
  children: [
510489
510724
  "\u2139 ",
510490
510725
  item.text
@@ -510632,7 +510867,7 @@ function ProviderSettings({
510632
510867
  ]
510633
510868
  }, undefined, true, undefined, this);
510634
510869
  }
510635
- var import_react153, MAX_POPUP_WIDTH4 = 110, CHROME_ROWS6 = 10, TABS6, CLAUDE_ITEMS, OPENAI_ITEMS, GENERAL_ITEMS, GOOGLE_ITEMS, XAI_ITEMS, DEEPSEEK_ITEMS, OPENROUTER_ITEMS, COMPAT_ITEMS, TAB_ITEMS, DEFAULTS, EFFORT_KEY_MODELS;
510870
+ var import_react153, MAX_POPUP_WIDTH4 = 110, CHROME_ROWS6 = 10, TABS6, CLAUDE_ITEMS, OPENAI_ITEMS, GENERAL_ITEMS, GOOGLE_ITEMS, XAI_ITEMS, DEEPSEEK_ITEMS, OPENROUTER_ITEMS, LLMGATEWAY_ITEMS, COMPAT_ITEMS, TAB_ITEMS, DEFAULTS, EFFORT_KEY_MODELS;
510636
510871
  var init_ProviderSettings = __esm(async () => {
510637
510872
  init_provider_options();
510638
510873
  init_theme();
@@ -510644,6 +510879,7 @@ var init_ProviderSettings = __esm(async () => {
510644
510879
  ]);
510645
510880
  import_react153 = __toESM(require_react(), 1);
510646
510881
  TABS6 = [
510882
+ "llmgateway",
510647
510883
  "claude",
510648
510884
  "openai",
510649
510885
  "google",
@@ -510890,10 +511126,43 @@ var init_ProviderSettings = __esm(async () => {
510890
511126
  type: "toggle"
510891
511127
  }
510892
511128
  ];
511129
+ LLMGATEWAY_ITEMS = [
511130
+ {
511131
+ type: "info",
511132
+ text: "api.llmgateway.io unified gateway \u2014 one key, every model. Effort emits reasoning_effort for non-Claude SKUs (DeepSeek \xB7 Qwen \xB7 GLM). Claude routes native Anthropic thinking via the Claude tab."
511133
+ },
511134
+ { type: "section", label: "Reasoning" },
511135
+ {
511136
+ key: "llmgatewayReasoningEffort",
511137
+ label: "Effort",
511138
+ desc: "Sent as reasoning_effort in the request body. Overrides the shared Other-tab compat effort when set.",
511139
+ type: "cycle",
511140
+ options: ["off", "low", "medium", "high", "xhigh"]
511141
+ },
511142
+ { type: "section", label: "Prompt caching" },
511143
+ {
511144
+ type: "info",
511145
+ text: "Ephemeral cache TTL for cacheable prefixes (system + tools). Gateway bills cache writes/reads separately."
511146
+ },
511147
+ {
511148
+ key: "cacheTtl",
511149
+ label: "Cache TTL",
511150
+ desc: "5m or 1h ephemeral cache window. Longer TTL costs more to write, cheaper on repeated reads.",
511151
+ type: "cycle",
511152
+ options: ["5m", "1h"]
511153
+ },
511154
+ { type: "section", label: "Tools" },
511155
+ {
511156
+ key: "webSearch",
511157
+ label: "Web search",
511158
+ desc: "Allow models to call the gateway's web_search tool. Billed as web_search_cost per request.",
511159
+ type: "toggle"
511160
+ }
511161
+ ];
510893
511162
  COMPAT_ITEMS = [
510894
511163
  {
510895
511164
  type: "info",
510896
- text: "Catch-all for providers without their own tab: Groq \xB7 Fireworks \xB7 OpenCode Zen \xB7 OpenCode Go \xB7 LM Studio \xB7 Ollama \xB7 Copilot \xB7 GitHub Models \xB7 MiniMax \xB7 Proxy \xB7 LLM Gateway (non-Claude lane). DeepSeek has its own tab."
511165
+ text: "Catch-all for providers without their own tab: Groq \xB7 Fireworks \xB7 OpenCode Zen \xB7 OpenCode Go \xB7 LM Studio \xB7 Ollama \xB7 Copilot \xB7 GitHub Models \xB7 MiniMax \xB7 Proxy. DeepSeek and LLM Gateway have their own tabs."
510897
511166
  },
510898
511167
  { type: "section", label: "Shared effort" },
510899
511168
  {
@@ -510924,6 +511193,7 @@ var init_ProviderSettings = __esm(async () => {
510924
511193
  }
510925
511194
  ];
510926
511195
  TAB_ITEMS = {
511196
+ llmgateway: LLMGATEWAY_ITEMS,
510927
511197
  claude: CLAUDE_ITEMS,
510928
511198
  openai: OPENAI_ITEMS,
510929
511199
  google: GOOGLE_ITEMS,
@@ -510961,6 +511231,7 @@ var init_ProviderSettings = __esm(async () => {
510961
511231
  openrouterReasoningMaxTokens: "off",
510962
511232
  openrouterExcludeReasoning: false,
510963
511233
  compatReasoningEffort: "off",
511234
+ llmgatewayReasoningEffort: "off",
510964
511235
  groqReasoningEffort: "off",
510965
511236
  openaiReasoningSummary: "off",
510966
511237
  openaiVerbosity: "off",
@@ -510972,7 +511243,8 @@ var init_ProviderSettings = __esm(async () => {
510972
511243
  deepseekReasoningEffort: () => "deepseek/deepseek-v4-pro",
510973
511244
  openaiReasoningEffort: () => "openai/gpt-5",
510974
511245
  xaiReasoningEffort: () => "xai/grok-4",
510975
- googleThinkingLevel: () => "google/gemini-3-pro"
511246
+ googleThinkingLevel: () => "google/gemini-3-pro",
511247
+ llmgatewayReasoningEffort: (m6) => `llmgateway/${m6.split("/").pop() ?? m6}`
510976
511248
  };
510977
511249
  });
510978
511250
 
@@ -513729,7 +514001,7 @@ function App({
513729
514001
  clearSelection: clearNvimSelection,
513730
514002
  openFile: nvimOpen,
513731
514003
  error: nvimError
513732
- } = useNeovim(true, effectiveConfig.nvimPath, effectiveConfig.nvimConfig, closeEditor, hasTabBarRef.current, editorSplitRef.current);
514004
+ } = useNeovim(true, effectiveConfig.nvimPath, effectiveConfig.nvimConfig, closeEditor, hasTabBarRef.current, editorSplitRef.current, termWidth, termHeight);
513733
514005
  const pendingEditorFileRef = import_react167.useRef(null);
513734
514006
  import_react167.useEffect(() => {
513735
514007
  if (nvimReady && pendingEditorFileRef.current) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proxysoul/soulforge",
3
- "version": "2.20.19",
3
+ "version": "2.20.21",
4
4
  "description": "Graph-powered code intelligence — multi-agent coding with codebase-aware AI",
5
5
  "repository": {
6
6
  "type": "git",
@@ -96,7 +96,7 @@
96
96
  "@modelcontextprotocol/sdk": "^1.29.0",
97
97
  "@mozilla/readability": "0.6.0",
98
98
  "@openrouter/ai-sdk-provider": "2.9.1",
99
- "@opentui/react": "0.4.1",
99
+ "@opentui/react": "0.4.2",
100
100
  "ai": "^6.0.208",
101
101
  "ghostty-opentui": "1.5.0",
102
102
  "ignore": "^7.0.5",
@@ -118,11 +118,11 @@
118
118
  "zustand": "5.0.14"
119
119
  },
120
120
  "optionalDependencies": {
121
- "@opentui/core-darwin-arm64": "0.4.1",
122
- "@opentui/core-darwin-x64": "0.4.1",
123
- "@opentui/core-linux-arm64": "0.4.1",
124
- "@opentui/core-linux-x64": "0.4.1",
125
- "@opentui/core-win32-arm64": "0.4.1",
126
- "@opentui/core-win32-x64": "0.4.1"
121
+ "@opentui/core-darwin-arm64": "0.4.2",
122
+ "@opentui/core-darwin-x64": "0.4.2",
123
+ "@opentui/core-linux-arm64": "0.4.2",
124
+ "@opentui/core-linux-x64": "0.4.2",
125
+ "@opentui/core-win32-arm64": "0.4.2",
126
+ "@opentui/core-win32-x64": "0.4.2"
127
127
  }
128
128
  }