@proxysoul/soulforge 2.20.19 → 2.20.20

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 +354 -7
  2. package/package.json +1 -1
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.20",
72642
72642
  description: "Graph-powered code intelligence \u2014 multi-agent coding with codebase-aware AI",
72643
72643
  repository: {
72644
72644
  type: "git",
@@ -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 });
@@ -409731,16 +409999,22 @@ var init_addons = __esm(() => {
409731
409999
  function isAnthropicModel(modelId) {
409732
410000
  return modelId.toLowerCase().startsWith("claude");
409733
410001
  }
410002
+ function leakRecoveryEnabled() {
410003
+ const v2 = process.env.SOULFORGE_PROXY_TOOL_RECOVERY;
410004
+ return v2 !== "0" && v2 !== "false";
410005
+ }
409734
410006
  var baseURL, GPT_55_INPUT_CONTEXT2 = 272000, proxy2;
409735
410007
  var init_proxy2 = __esm(() => {
409736
410008
  init_dist6();
409737
410009
  init_dist8();
410010
+ init_dist5();
409738
410011
  init_config2();
409739
410012
  init_key_resolver();
409740
410013
  init_lifecycle();
409741
410014
  init_compat_reasoning();
409742
410015
  init_context_windows();
409743
410016
  init_reasoning_fetch();
410017
+ init_recover_leaked_tool_calls();
409744
410018
  baseURL = process.env.PROXY_API_URL || "http://127.0.0.1:8317/v1";
409745
410019
  proxy2 = {
409746
410020
  id: "proxy",
@@ -409752,7 +410026,12 @@ var init_proxy2 = __esm(() => {
409752
410026
  createModel(modelId) {
409753
410027
  const apiKey = getActiveProxyApiKey();
409754
410028
  if (isAnthropicModel(modelId)) {
409755
- return createAnthropic({ baseURL, apiKey, fetch: withSessionHeaders() })(modelId);
410029
+ const model = createAnthropic({
410030
+ baseURL,
410031
+ apiKey,
410032
+ fetch: withSessionHeaders()
410033
+ })(modelId);
410034
+ return leakRecoveryEnabled() ? wrapLanguageModel({ model, middleware: recoverLeakedToolCallsMiddleware() }) : model;
409756
410035
  }
409757
410036
  const reasoningBody = getCompatReasoningBody(`proxy/${modelId}`, loadConfig());
409758
410037
  const reasoningFetch = createSessionFetchWrapper(reasoningBody);
@@ -489406,6 +489685,15 @@ function fuzzyMatch2(pattern, target) {
489406
489685
  }
489407
489686
  if (pi < p4.length)
489408
489687
  return null;
489688
+ const strip = (s2) => s2.startsWith("/") ? s2.slice(1) : s2;
489689
+ const sp = strip(p4);
489690
+ const st2 = strip(t2);
489691
+ if (sp.length > 0 && st2.startsWith(sp)) {
489692
+ score += 100;
489693
+ if (st2.length === sp.length)
489694
+ score += 50;
489695
+ score -= st2.length;
489696
+ }
489409
489697
  return { entry: target, score, indices };
489410
489698
  }
489411
489699
  function fuzzyFilter(pattern, entries2, limit = 50) {
@@ -496741,6 +497029,13 @@ function fuzzyScore3(query2, target) {
496741
497029
  score += 5;
496742
497030
  }
496743
497031
  score -= indices.length > 0 ? indices[0] : 0;
497032
+ const stripped = lower2.startsWith("/") ? lower2.slice(1) : lower2;
497033
+ if (stripped.startsWith(q3)) {
497034
+ score += 100;
497035
+ if (stripped.length === q3.length)
497036
+ score += 50;
497037
+ score -= stripped.length;
497038
+ }
496744
497039
  return { score, indices };
496745
497040
  }
496746
497041
  function OptionRow2({
@@ -498277,7 +498572,7 @@ function getStatusTag(id) {
498277
498572
  return s2.active;
498278
498573
  }
498279
498574
  function fuzzyMatch3(query2, target) {
498280
- const q3 = query2.toLowerCase();
498575
+ const q3 = query2.toLowerCase().replace(/\s+/g, "");
498281
498576
  const t2 = target.toLowerCase();
498282
498577
  let qi = 0;
498283
498578
  for (let ti = 0;ti < t2.length && qi < q3.length; ti++) {
@@ -510136,6 +510431,8 @@ function readValuesFromLayer(layer) {
510136
510431
  v3.openrouterExcludeReasoning = layer.performance.openrouterExcludeReasoning;
510137
510432
  if (layer.performance?.compatReasoningEffort !== undefined)
510138
510433
  v3.compatReasoningEffort = layer.performance.compatReasoningEffort;
510434
+ if (layer.performance?.llmgatewayReasoningEffort !== undefined)
510435
+ v3.llmgatewayReasoningEffort = layer.performance.llmgatewayReasoningEffort;
510139
510436
  if (layer.performance?.groqReasoningEffort !== undefined)
510140
510437
  v3.groqReasoningEffort = layer.performance.groqReasoningEffort;
510141
510438
  if (layer.performance?.openaiReasoningSummary !== undefined)
@@ -510215,6 +510512,10 @@ function buildPatch(key3, value) {
510215
510512
  return { performance: { openrouterExcludeReasoning: value } };
510216
510513
  case "compatReasoningEffort":
510217
510514
  return { performance: { compatReasoningEffort: value } };
510515
+ case "llmgatewayReasoningEffort":
510516
+ return {
510517
+ performance: { llmgatewayReasoningEffort: value }
510518
+ };
510218
510519
  case "groqReasoningEffort":
510219
510520
  return { performance: { groqReasoningEffort: value } };
510220
510521
  case "openaiReasoningSummary":
@@ -510282,7 +510583,7 @@ function ProviderSettings({
510282
510583
  const popupWidth = Math.min(MAX_POPUP_WIDTH4, Math.floor(termCols * 0.85));
510283
510584
  const maxVisible = Math.max(6, Math.floor(containerRows * 0.85) - CHROME_ROWS6);
510284
510585
  const t2 = useTheme();
510285
- const [tab, setTab] = import_react153.useState("claude");
510586
+ const [tab, setTab] = import_react153.useState("llmgateway");
510286
510587
  const [cursor3, setCursor] = import_react153.useState(0);
510287
510588
  const [scope, setScope] = import_react153.useState(() => detectInitialScope(projectConfig));
510288
510589
  const vals = effectiveValues(globalConfig2, projectConfig);
@@ -510413,6 +510714,12 @@ function ProviderSettings({
510413
510714
  title: "Provider Options",
510414
510715
  titleIcon: "system",
510415
510716
  tabs: [
510717
+ {
510718
+ id: "llmgateway",
510719
+ label: "LLM Gateway",
510720
+ icon: "cloud",
510721
+ blurb: "unified gateway \xB7 effort \xB7 cache \xB7 web search"
510722
+ },
510416
510723
  { id: "claude", label: "Claude", icon: "ai", blurb: "thinking \xB7 reasoning \xB7 beta" },
510417
510724
  { id: "openai", label: "OpenAI", icon: "ai", blurb: "reasoning \xB7 service tier" },
510418
510725
  { id: "google", label: "Gemini", icon: "ai", blurb: "thinking level \xB7 budget" },
@@ -510459,6 +510766,7 @@ function ProviderSettings({
510459
510766
  flexDirection: "row",
510460
510767
  backgroundColor: t2.bgPopup,
510461
510768
  paddingX: 1,
510769
+ height: 1,
510462
510770
  children: [
510463
510771
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
510464
510772
  bg: t2.bgPopup,
@@ -510482,9 +510790,11 @@ function ProviderSettings({
510482
510790
  flexDirection: "row",
510483
510791
  backgroundColor: t2.bgPopup,
510484
510792
  paddingX: 1,
510793
+ height: 1,
510485
510794
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
510486
510795
  bg: t2.bgPopup,
510487
510796
  fg: t2.textFaint,
510797
+ truncate: true,
510488
510798
  children: [
510489
510799
  "\u2139 ",
510490
510800
  item.text
@@ -510632,7 +510942,7 @@ function ProviderSettings({
510632
510942
  ]
510633
510943
  }, undefined, true, undefined, this);
510634
510944
  }
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;
510945
+ 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
510946
  var init_ProviderSettings = __esm(async () => {
510637
510947
  init_provider_options();
510638
510948
  init_theme();
@@ -510644,6 +510954,7 @@ var init_ProviderSettings = __esm(async () => {
510644
510954
  ]);
510645
510955
  import_react153 = __toESM(require_react(), 1);
510646
510956
  TABS6 = [
510957
+ "llmgateway",
510647
510958
  "claude",
510648
510959
  "openai",
510649
510960
  "google",
@@ -510890,10 +511201,43 @@ var init_ProviderSettings = __esm(async () => {
510890
511201
  type: "toggle"
510891
511202
  }
510892
511203
  ];
511204
+ LLMGATEWAY_ITEMS = [
511205
+ {
511206
+ type: "info",
511207
+ 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."
511208
+ },
511209
+ { type: "section", label: "Reasoning" },
511210
+ {
511211
+ key: "llmgatewayReasoningEffort",
511212
+ label: "Effort",
511213
+ desc: "Sent as reasoning_effort in the request body. Overrides the shared Other-tab compat effort when set.",
511214
+ type: "cycle",
511215
+ options: ["off", "low", "medium", "high", "xhigh"]
511216
+ },
511217
+ { type: "section", label: "Prompt caching" },
511218
+ {
511219
+ type: "info",
511220
+ text: "Ephemeral cache TTL for cacheable prefixes (system + tools). Gateway bills cache writes/reads separately."
511221
+ },
511222
+ {
511223
+ key: "cacheTtl",
511224
+ label: "Cache TTL",
511225
+ desc: "5m or 1h ephemeral cache window. Longer TTL costs more to write, cheaper on repeated reads.",
511226
+ type: "cycle",
511227
+ options: ["5m", "1h"]
511228
+ },
511229
+ { type: "section", label: "Tools" },
511230
+ {
511231
+ key: "webSearch",
511232
+ label: "Web search",
511233
+ desc: "Allow models to call the gateway's web_search tool. Billed as web_search_cost per request.",
511234
+ type: "toggle"
511235
+ }
511236
+ ];
510893
511237
  COMPAT_ITEMS = [
510894
511238
  {
510895
511239
  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."
511240
+ 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
511241
  },
510898
511242
  { type: "section", label: "Shared effort" },
510899
511243
  {
@@ -510924,6 +511268,7 @@ var init_ProviderSettings = __esm(async () => {
510924
511268
  }
510925
511269
  ];
510926
511270
  TAB_ITEMS = {
511271
+ llmgateway: LLMGATEWAY_ITEMS,
510927
511272
  claude: CLAUDE_ITEMS,
510928
511273
  openai: OPENAI_ITEMS,
510929
511274
  google: GOOGLE_ITEMS,
@@ -510961,6 +511306,7 @@ var init_ProviderSettings = __esm(async () => {
510961
511306
  openrouterReasoningMaxTokens: "off",
510962
511307
  openrouterExcludeReasoning: false,
510963
511308
  compatReasoningEffort: "off",
511309
+ llmgatewayReasoningEffort: "off",
510964
511310
  groqReasoningEffort: "off",
510965
511311
  openaiReasoningSummary: "off",
510966
511312
  openaiVerbosity: "off",
@@ -510972,7 +511318,8 @@ var init_ProviderSettings = __esm(async () => {
510972
511318
  deepseekReasoningEffort: () => "deepseek/deepseek-v4-pro",
510973
511319
  openaiReasoningEffort: () => "openai/gpt-5",
510974
511320
  xaiReasoningEffort: () => "xai/grok-4",
510975
- googleThinkingLevel: () => "google/gemini-3-pro"
511321
+ googleThinkingLevel: () => "google/gemini-3-pro",
511322
+ llmgatewayReasoningEffort: (m6) => `llmgateway/${m6.split("/").pop() ?? m6}`
510976
511323
  };
510977
511324
  });
510978
511325
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proxysoul/soulforge",
3
- "version": "2.20.19",
3
+ "version": "2.20.20",
4
4
  "description": "Graph-powered code intelligence — multi-agent coding with codebase-aware AI",
5
5
  "repository": {
6
6
  "type": "git",