@desplega.ai/agent-swarm 1.121.0 → 1.122.0

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 (84) hide show
  1. package/README.md +3 -3
  2. package/dist/{actions-mwshtkf7.js → actions-k3bwtmgy.js} +5 -5
  3. package/dist/{app-skbty91x.js → app-knevny0f.js} +3 -3
  4. package/dist/{assistant-tg7dhfj8.js → assistant-as8087ft.js} +8 -8
  5. package/dist/{boot-reembed-ntjmeyh2.js → boot-reembed-1dcc1yyk.js} +3 -3
  6. package/dist/{boot-reembed-q5x0xpcp.js → boot-reembed-j305cpdr.js} +4 -4
  7. package/dist/{boot-scrub-logs-5gznsfbe.js → boot-scrub-logs-w9qkjzkm.js} +2 -2
  8. package/dist/{cli-s8bdcke2.js → cli-00yr0kxv.js} +1 -1
  9. package/dist/{cli-6s3ytc5x.js → cli-16dv125e.js} +27 -2
  10. package/dist/cli-3dpj7atn.js +11 -0
  11. package/dist/{cli-srjqn7fd.js → cli-3ptjc655.js} +1 -1
  12. package/dist/{cli-vg3zrw6h.js → cli-586x669g.js} +4 -4
  13. package/dist/{cli-5hpvd15x.js → cli-60q15ncp.js} +2 -2
  14. package/dist/{cli-ghq3v4c9.js → cli-7jb8mk4n.js} +1 -1
  15. package/dist/{cli-ky8esb8t.js → cli-8sv69q06.js} +3 -3
  16. package/dist/{cli-072t49hf.js → cli-8x745f40.js} +3 -3
  17. package/dist/{cli-3b1ayz83.js → cli-9fhxk91h.js} +5 -5
  18. package/dist/{cli-nf7sadev.js → cli-bxdb36d2.js} +2 -2
  19. package/dist/{cli-g9vva2qh.js → cli-fc1gvt9c.js} +1 -1
  20. package/dist/{cli-agdrxyex.js → cli-haeq79yf.js} +1 -1
  21. package/dist/{cli-7pzae66m.js → cli-hjq6c6vz.js} +1 -1
  22. package/dist/{cli-b2mqkg1h.js → cli-j89ph4c4.js} +2 -2
  23. package/dist/{cli-4pfweq4x.js → cli-kpfsabp2.js} +5 -5
  24. package/dist/{cli-95rbe11h.js → cli-m7dmk462.js} +12 -9
  25. package/dist/{cli-0df2g86x.js → cli-mjxnr12j.js} +1 -1
  26. package/dist/{cli-3fxqwfh3.js → cli-qf0qxzw5.js} +5 -5
  27. package/dist/{cli-vp9r4ap4.js → cli-qnmj37m5.js} +2 -2
  28. package/dist/{cli-tv7vekye.js → cli-tc8xk5zh.js} +1 -1
  29. package/dist/{cli-dmzv0986.js → cli-vh3h1grg.js} +10 -1
  30. package/dist/{cli-46etyprn.js → cli-w48hgc2y.js} +1 -1
  31. package/dist/{cli-avw9ev3x.js → cli-wmx631p8.js} +14 -11
  32. package/dist/{cli-gdpx70nc.js → cli-y3an9mk7.js} +19 -19
  33. package/dist/{cli-1728qh70.js → cli-z3hcrghs.js} +1 -1
  34. package/dist/{cli-p9w7kb3h.js → cli-zpa6gjwq.js} +1 -1
  35. package/dist/{cli-7pt9sc83.js → cli-zweca3q3.js} +3 -2
  36. package/dist/cli.js +10 -10
  37. package/dist/{codex-adapter-mmy741zd.js → codex-adapter-0kv2meed.js} +3 -2
  38. package/dist/{codex-session-runner-sazmx9zt.js → codex-session-runner-m1e1v4r0.js} +3 -2
  39. package/dist/{commands-cxxye5e4.js → commands-6j54dz62.js} +2 -2
  40. package/dist/{db-ab1954b7.js → db-gcnz2tqx.js} +2 -2
  41. package/dist/{handlers-fmfe2343.js → handlers-c43sm2h1.js} +8 -8
  42. package/dist/{hook-ygnw3cvt.js → hook-y1p25vm5.js} +3 -2
  43. package/dist/{http-kactcc0z.js → http-bsz1rze1.js} +35 -34
  44. package/dist/{index-gmzs6a2q.js → index-03zn2rz7.js} +10 -9
  45. package/dist/{index-g86j2yt5.js → index-7swcv6rv.js} +7 -7
  46. package/dist/{index-hmp82wn0.js → index-8m5gd1vs.js} +12 -11
  47. package/dist/{index-6erq0wvk.js → index-qyzfh62q.js} +11 -10
  48. package/dist/{keepalive-3cvt26dw.js → keepalive-69v93p45.js} +5 -5
  49. package/dist/{lead-ysf26cjr.js → lead-xn8kfchy.js} +23 -22
  50. package/dist/{maintenance-9npwptbb.js → maintenance-g2x7q13q.js} +4 -4
  51. package/dist/{oauth-refresh-sweep-0r2v76vw.js → oauth-refresh-sweep-j80szsyr.js} +4 -4
  52. package/dist/{onboard-ddb1674a.js → onboard-e3xcjt8b.js} +2 -2
  53. package/dist/{opencode-adapter-h6rkxwrk.js → opencode-adapter-s3nhtp9m.js} +4 -1
  54. package/dist/{otel-impl-b43bkq8e.js → otel-impl-24egvjaa.js} +1 -1
  55. package/dist/{pi-mono-adapter-ke789738.js → pi-mono-adapter-k3z6e5fn.js} +102 -4
  56. package/dist/{pricing-refresh-2rqf114z.js → pricing-refresh-01kg9gky.js} +4 -4
  57. package/dist/{rbac-roles-6vtafvzb.js → rbac-roles-f9bdj93y.js} +2 -2
  58. package/dist/{rbac-roles-5wkhc8xv.js → rbac-roles-jqfp0zza.js} +3 -3
  59. package/dist/{seed-pricing-23wrb873.js → seed-pricing-z2t7rank.js} +3 -3
  60. package/dist/{setup-6rny4fk6.js → setup-kw2ps002.js} +2 -2
  61. package/dist/{worker-yapewe14.js → worker-336w8930.js} +23 -22
  62. package/openapi.json +1 -1
  63. package/package.json +1 -1
  64. package/src/be/memory/constants.ts +7 -6
  65. package/src/be/memory/graph-expansion.ts +3 -3
  66. package/src/be/memory/raters/llm-summarizer.ts +10 -2
  67. package/src/commands/provider-credentials.ts +2 -1
  68. package/src/http/memory.ts +2 -1
  69. package/src/providers/codex-adapter.ts +3 -0
  70. package/src/providers/opencode-adapter.ts +40 -0
  71. package/src/providers/pi-mono-adapter.ts +179 -1
  72. package/src/providers/pi-mono-extension.ts +9 -0
  73. package/src/tests/agent-avatar-resolution.test.ts +170 -0
  74. package/src/tests/memory-feature-flags.test.ts +45 -0
  75. package/src/tests/memory-rater-llm-summarizer.test.ts +24 -0
  76. package/src/tests/opencode-plugin.test.ts +43 -0
  77. package/src/tests/openrouter-base-url.test.ts +256 -0
  78. package/src/tests/pi-mono-adapter.test.ts +90 -0
  79. package/src/tools/memory-search.ts +2 -1
  80. package/src/utils/internal-ai/complete-structured.ts +19 -1
  81. package/src/utils/internal-ai/summarize-session.ts +7 -0
  82. package/src/utils/openrouter-base-url.ts +28 -0
  83. package/src/workflows/executors/raw-llm.ts +2 -1
  84. package/src/workflows/executors/validate.ts +2 -1
@@ -22,7 +22,7 @@ import {
22
22
  streamSimple,
23
23
  summarizeSession,
24
24
  validateToolArguments
25
- } from "./cli-dmzv0986.js";
25
+ } from "./cli-vh3h1grg.js";
26
26
  import {
27
27
  checkToolLoop,
28
28
  clearToolHistory
@@ -46,6 +46,10 @@ import"./cli-tk9vyqzh.js";
46
46
  import {
47
47
  applyReasoningEffort
48
48
  } from "./cli-e61fs2hp.js";
49
+ import {
50
+ DEFAULT_OPENROUTER_BASE_URL,
51
+ getOpenRouterBaseUrl
52
+ } from "./cli-3dpj7atn.js";
49
53
  import {
50
54
  McpHttpClient
51
55
  } from "./cli-jkhwmmgg.js";
@@ -161168,7 +161172,8 @@ async function summarizeSessionForPi(config, sessionFile, deps = {}) {
161168
161172
  prompt: taskDetails?.task
161169
161173
  },
161170
161174
  apiUrl: config.apiUrl,
161171
- apiKey: config.apiKey
161175
+ apiKey: config.apiKey,
161176
+ env: config.env
161172
161177
  });
161173
161178
  if (!result)
161174
161179
  return;
@@ -161564,6 +161569,95 @@ async function createPiRuntimeAuth(env = process.env) {
161564
161569
  }
161565
161570
  return modelRuntime;
161566
161571
  }
161572
+ var OPENROUTER_OVERRIDE_MARKER = ".agent-swarm-openrouter-override.json";
161573
+ async function readJsonFile(path12) {
161574
+ try {
161575
+ const file = Bun.file(path12);
161576
+ if (!await file.exists())
161577
+ return;
161578
+ const parsed = JSON.parse(await file.text());
161579
+ return typeof parsed === "object" && parsed !== null ? parsed : undefined;
161580
+ } catch {
161581
+ return;
161582
+ }
161583
+ }
161584
+ async function ensureOpenRouterModelsOverride(agentDir, env = process.env) {
161585
+ const baseUrl = getOpenRouterBaseUrl(env);
161586
+ const modelsPath = join39(agentDir, "models.json");
161587
+ const markerPath = join39(agentDir, OPENROUTER_OVERRIDE_MARKER);
161588
+ if (baseUrl === DEFAULT_OPENROUTER_BASE_URL) {
161589
+ await revertOpenRouterModelsOverride(modelsPath, markerPath);
161590
+ return;
161591
+ }
161592
+ let existing = {};
161593
+ try {
161594
+ const file = Bun.file(modelsPath);
161595
+ if (await file.exists()) {
161596
+ existing = JSON.parse(await file.text());
161597
+ }
161598
+ } catch (err2) {
161599
+ console.warn(`[pi-mono] ${modelsPath} is unreadable/invalid (${err2 instanceof Error ? err2.message : err2}); rewriting with the OpenRouter gateway override`);
161600
+ existing = {};
161601
+ }
161602
+ const providers = typeof existing.providers === "object" && existing.providers !== null ? existing.providers : {};
161603
+ const openrouter = typeof providers.openrouter === "object" && providers.openrouter !== null ? providers.openrouter : {};
161604
+ const priorMarker = await readJsonFile(markerPath);
161605
+ const currentBaseUrl = typeof openrouter.baseUrl === "string" ? openrouter.baseUrl : undefined;
161606
+ const previous = priorMarker ? priorMarker.previous : currentBaseUrl !== baseUrl ? currentBaseUrl : undefined;
161607
+ const next = {
161608
+ ...existing,
161609
+ providers: {
161610
+ ...providers,
161611
+ openrouter: { ...openrouter, baseUrl }
161612
+ }
161613
+ };
161614
+ await Bun.write(modelsPath, `${JSON.stringify(next, null, 2)}
161615
+ `);
161616
+ const marker = {
161617
+ baseUrl,
161618
+ ...previous !== undefined ? { previous } : {}
161619
+ };
161620
+ await Bun.write(markerPath, `${JSON.stringify(marker, null, 2)}
161621
+ `);
161622
+ }
161623
+ async function revertOpenRouterModelsOverride(modelsPath, markerPath) {
161624
+ const marker = await readJsonFile(markerPath);
161625
+ if (!marker)
161626
+ return;
161627
+ const removeMarker = () => Bun.file(markerPath).delete();
161628
+ const existing = await readJsonFile(modelsPath);
161629
+ if (!existing) {
161630
+ await removeMarker();
161631
+ return;
161632
+ }
161633
+ const providers = typeof existing.providers === "object" && existing.providers !== null ? { ...existing.providers } : undefined;
161634
+ const openrouter = providers && typeof providers.openrouter === "object" && providers.openrouter !== null ? { ...providers.openrouter } : undefined;
161635
+ if (!providers || !openrouter || openrouter.baseUrl !== marker.baseUrl) {
161636
+ await removeMarker();
161637
+ return;
161638
+ }
161639
+ if (marker.previous !== undefined) {
161640
+ openrouter.baseUrl = marker.previous;
161641
+ } else {
161642
+ delete openrouter.baseUrl;
161643
+ }
161644
+ if (Object.keys(openrouter).length === 0) {
161645
+ delete providers.openrouter;
161646
+ } else {
161647
+ providers.openrouter = openrouter;
161648
+ }
161649
+ const next = { ...existing, providers };
161650
+ if (Object.keys(providers).length === 0) {
161651
+ delete next.providers;
161652
+ }
161653
+ if (Object.keys(next).length === 0) {
161654
+ await Bun.file(modelsPath).delete();
161655
+ } else {
161656
+ await Bun.write(modelsPath, `${JSON.stringify(next, null, 2)}
161657
+ `);
161658
+ }
161659
+ await removeMarker();
161660
+ }
161567
161661
  function resolveModel(modelStr, env = process.env) {
161568
161662
  if (!modelStr)
161569
161663
  return;
@@ -161972,14 +162066,17 @@ class PiMonoAdapter {
161972
162066
  } catch {}
161973
162067
  }
161974
162068
  const sessionEnv = config.env ?? process.env;
161975
- const model = resolveModel(config.model, sessionEnv);
162069
+ await ensureOpenRouterModelsOverride(getAgentDir(), sessionEnv);
162070
+ const builtinModel = resolveModel(config.model, sessionEnv);
161976
162071
  const modelRuntime = await createPiRuntimeAuth(sessionEnv);
162072
+ const model = builtinModel ? modelRuntime.getModel(builtinModel.provider, builtinModel.id) ?? builtinModel : builtinModel;
161977
162073
  const swarmExtension = createSwarmHooksExtension({
161978
162074
  apiUrl: config.apiUrl,
161979
162075
  apiKey: config.apiKey,
161980
162076
  agentId: config.agentId,
161981
162077
  taskId: config.taskId,
161982
- isLead: config.role === "lead"
162078
+ isLead: config.role === "lead",
162079
+ env: sessionEnv
161983
162080
  });
161984
162081
  const resourceLoader = new DefaultResourceLoader({
161985
162082
  cwd: config.cwd,
@@ -162018,6 +162115,7 @@ export {
162018
162115
  runBedrockSdkProbeAndEnumerate,
162019
162116
  resolveModel,
162020
162117
  extractPiAssistantText,
162118
+ ensureOpenRouterModelsOverride,
162021
162119
  createPiRuntimeAuth,
162022
162120
  checkPiMonoCredentials,
162023
162121
  PiMonoSession,
@@ -2,11 +2,11 @@ import {
2
2
  PRICING_REFRESH_INTERVAL_MS,
3
3
  refreshPricingFromModelsDev,
4
4
  startPricingRefreshLoop
5
- } from "./cli-b2mqkg1h.js";
6
- import"./cli-ghq3v4c9.js";
7
- import"./cli-0df2g86x.js";
5
+ } from "./cli-j89ph4c4.js";
6
+ import"./cli-7jb8mk4n.js";
7
+ import"./cli-mjxnr12j.js";
8
8
  import"./cli-z2zcxes1.js";
9
- import"./cli-46etyprn.js";
9
+ import"./cli-w48hgc2y.js";
10
10
  import"./cli-4j2mrzag.js";
11
11
  import"./cli-bw80ck94.js";
12
12
  import"./cli-xz9aq0rf.js";
@@ -4,9 +4,9 @@ import {
4
4
  import {
5
5
  getDb,
6
6
  init_db
7
- } from "./cli-0df2g86x.js";
7
+ } from "./cli-mjxnr12j.js";
8
8
  import"./cli-z2zcxes1.js";
9
- import"./cli-46etyprn.js";
9
+ import"./cli-w48hgc2y.js";
10
10
  import"./cli-4j2mrzag.js";
11
11
  import"./cli-bw80ck94.js";
12
12
  import"./cli-xz9aq0rf.js";
@@ -7,11 +7,11 @@ import {
7
7
  getUserGrant,
8
8
  listUserRoles,
9
9
  runRbacCliCommand
10
- } from "./cli-7pzae66m.js";
10
+ } from "./cli-hjq6c6vz.js";
11
11
  import"./cli-wwsaj97q.js";
12
- import"./cli-0df2g86x.js";
12
+ import"./cli-mjxnr12j.js";
13
13
  import"./cli-z2zcxes1.js";
14
- import"./cli-46etyprn.js";
14
+ import"./cli-w48hgc2y.js";
15
15
  import"./cli-4j2mrzag.js";
16
16
  import"./cli-bw80ck94.js";
17
17
  import"./cli-xz9aq0rf.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  buildModelsDevSeedRows,
3
3
  seedPricingFromModelsDev
4
- } from "./cli-ghq3v4c9.js";
5
- import"./cli-0df2g86x.js";
4
+ } from "./cli-7jb8mk4n.js";
5
+ import"./cli-mjxnr12j.js";
6
6
  import"./cli-z2zcxes1.js";
7
- import"./cli-46etyprn.js";
7
+ import"./cli-w48hgc2y.js";
8
8
  import"./cli-4j2mrzag.js";
9
9
  import"./cli-bw80ck94.js";
10
10
  import"./cli-xz9aq0rf.js";
@@ -4,14 +4,14 @@ import {
4
4
  createDefaultMcpJson,
5
5
  createDefaultSettingsLocal,
6
6
  createHooksConfig
7
- } from "./cli-p9w7kb3h.js";
7
+ } from "./cli-zpa6gjwq.js";
8
8
  import {
9
9
  require_jsx_dev_runtime
10
10
  } from "./cli-wx82tw1k.js";
11
11
  import {
12
12
  getApiKey
13
13
  } from "./cli-f14fvzag.js";
14
- import"./cli-46etyprn.js";
14
+ import"./cli-w48hgc2y.js";
15
15
  import {
16
16
  Box_default,
17
17
  Spinner,
@@ -1,20 +1,20 @@
1
1
  import {
2
2
  runAgent
3
- } from "./cli-avw9ev3x.js";
4
- import"./cli-7pt9sc83.js";
3
+ } from "./cli-wmx631p8.js";
4
+ import"./cli-zweca3q3.js";
5
5
  import"./cli-5c4327dx.js";
6
6
  import"./cli-07er7wrs.js";
7
7
  import"./cli-tyfxrrdd.js";
8
- import"./cli-6s3ytc5x.js";
8
+ import"./cli-16dv125e.js";
9
9
  import"./cli-r94z9p1z.js";
10
- import"./cli-dmzv0986.js";
10
+ import"./cli-vh3h1grg.js";
11
11
  import"./cli-yv9dttkh.js";
12
12
  import"./cli-yazx6hap.js";
13
13
  import"./cli-d6n0thxw.js";
14
14
  import"./cli-v1mwc5v2.js";
15
15
  import {
16
16
  getEnabledCapabilities
17
- } from "./cli-gdpx70nc.js";
17
+ } from "./cli-y3an9mk7.js";
18
18
  import"./cli-1z7048dy.js";
19
19
  import"./cli-gz9sbyk3.js";
20
20
  import"./cli-ftyf58xj.js";
@@ -25,35 +25,36 @@ import"./cli-tng3131n.js";
25
25
  import"./cli-zm7njwzd.js";
26
26
  import"./cli-cw70hymf.js";
27
27
  import"./cli-e61fs2hp.js";
28
- import"./cli-vg3zrw6h.js";
29
- import"./cli-b2mqkg1h.js";
30
- import"./cli-ghq3v4c9.js";
31
- import"./cli-7pzae66m.js";
28
+ import"./cli-586x669g.js";
29
+ import"./cli-j89ph4c4.js";
30
+ import"./cli-7jb8mk4n.js";
31
+ import"./cli-hjq6c6vz.js";
32
32
  import"./cli-wwsaj97q.js";
33
- import"./cli-agdrxyex.js";
34
- import"./cli-95rbe11h.js";
33
+ import"./cli-haeq79yf.js";
34
+ import"./cli-m7dmk462.js";
35
35
  import"./cli-f14fvzag.js";
36
- import"./cli-tv7vekye.js";
37
- import"./cli-072t49hf.js";
36
+ import"./cli-3dpj7atn.js";
37
+ import"./cli-tc8xk5zh.js";
38
+ import"./cli-8x745f40.js";
38
39
  import"./cli-jkhwmmgg.js";
39
- import"./cli-5hpvd15x.js";
40
- import"./cli-3fxqwfh3.js";
40
+ import"./cli-60q15ncp.js";
41
+ import"./cli-qf0qxzw5.js";
41
42
  import"./cli-q25ejbyn.js";
42
- import"./cli-1728qh70.js";
43
+ import"./cli-z3hcrghs.js";
43
44
  import"./cli-fad8m16k.js";
44
45
  import"./cli-7zd05xb4.js";
45
- import"./cli-g9vva2qh.js";
46
- import"./cli-srjqn7fd.js";
47
- import"./cli-s8bdcke2.js";
48
- import"./cli-3b1ayz83.js";
46
+ import"./cli-fc1gvt9c.js";
47
+ import"./cli-3ptjc655.js";
48
+ import"./cli-00yr0kxv.js";
49
+ import"./cli-9fhxk91h.js";
49
50
  import"./cli-rkndnn90.js";
50
51
  import"./cli-b0p7rfnd.js";
51
52
  import"./cli-ma04faw5.js";
52
53
  import"./cli-wspgs9bt.js";
53
54
  import"./cli-dh55d5fg.js";
54
- import"./cli-0df2g86x.js";
55
+ import"./cli-mjxnr12j.js";
55
56
  import"./cli-z2zcxes1.js";
56
- import"./cli-46etyprn.js";
57
+ import"./cli-w48hgc2y.js";
57
58
  import"./cli-4j2mrzag.js";
58
59
  import"./cli-bw80ck94.js";
59
60
  import"./cli-xz9aq0rf.js";
package/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Agent Swarm API",
5
- "version": "1.121.0",
5
+ "version": "1.122.0",
6
6
  "description": "Multi-agent orchestration API for Claude Code, Codex, and Gemini CLI. Enables task distribution, agent communication, and service discovery.\n\nMCP tools are documented separately in [MCP.md](./MCP.md)."
7
7
  },
8
8
  "servers": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@desplega.ai/agent-swarm",
3
- "version": "1.121.0",
3
+ "version": "1.122.0",
4
4
  "description": "Multi-agent orchestration for Claude Code, Codex, Gemini CLI, and other AI coding assistants",
5
5
  "license": "MIT",
6
6
  "author": "desplega.sh <contact@desplega.sh>",
@@ -59,17 +59,18 @@ export const ACCESS_BOOST_MAX_MULTIPLIER = numEnv("MEMORY_ACCESS_BOOST_MAX", 1.5
59
59
  export const ACCESS_BOOST_RECENCY_WINDOW_HOURS = numEnv("MEMORY_ACCESS_RECENCY_HOURS", 48);
60
60
  export const CANDIDATE_SET_MULTIPLIER = numEnv("MEMORY_CANDIDATE_MULTIPLIER", 3);
61
61
 
62
- // Feature flag: enable hybrid (FTS+vec) search. Off by default; set MEMORY_HYBRID_SEARCH=1 to enable.
62
+ // Feature flag: enable hybrid (FTS+vec) search. On by default; set
63
+ // MEMORY_HYBRID_SEARCH=0|false to fall back to vector-only search.
63
64
  export function isHybridSearchEnabled(): boolean {
64
- const val = process.env.MEMORY_HYBRID_SEARCH ?? "0";
65
- return val === "1" || val.toLowerCase() === "true";
65
+ const val = process.env.MEMORY_HYBRID_SEARCH?.trim().toLowerCase();
66
+ return val !== "0" && val !== "false";
66
67
  }
67
68
 
68
69
  // Feature flag: expand search candidates with 1-hop memory_link graph neighbors
69
- // (DES-639a). Off by default; set MEMORY_GRAPH_EXPANSION=1 to enable.
70
+ // (DES-639a). On by default; set MEMORY_GRAPH_EXPANSION=0|false to disable.
70
71
  export function isGraphExpansionEnabled(): boolean {
71
- const val = process.env.MEMORY_GRAPH_EXPANSION ?? "0";
72
- return val === "1" || val.toLowerCase() === "true";
72
+ const val = process.env.MEMORY_GRAPH_EXPANSION?.trim().toLowerCase();
73
+ return val !== "0" && val !== "false";
73
74
  }
74
75
 
75
76
  // Embedding defaults
@@ -12,9 +12,9 @@
12
12
  * - `rerank()` stays pure (no DB access),
13
13
  * - `recordRetrievals` downstream picks up `retrievalSource: "graph"` for free.
14
14
  *
15
- * Flag-gated: `MEMORY_GRAPH_EXPANSION=1|true` (default off — same rollout
16
- * pattern as MEMORY_HYBRID_SEARCH). With the flag off the input array is
17
- * returned unchanged (same reference), keeping results byte-identical.
15
+ * Enabled by default; set `MEMORY_GRAPH_EXPANSION=0|false` to disable it.
16
+ * With the flag off the input array is returned unchanged (same reference),
17
+ * keeping results byte-identical to the vector/hybrid candidate set.
18
18
  *
19
19
  * Plan: thoughts/taras/plans/2026-07-02-memory-retrieval-v2-graph-and-measurement.md Phase 4
20
20
  */
@@ -11,6 +11,7 @@
11
11
  * No `bun:sqlite` / `src/be/db` imports. Boundary script enforces this.
12
12
  */
13
13
  import { z } from "zod";
14
+ import { getOpenRouterBaseUrl } from "../../../utils/openrouter-base-url";
14
15
  import { type SummaryWithRatings, SummaryWithRatingsSchema } from "./llm";
15
16
 
16
17
  /**
@@ -27,7 +28,14 @@ export const DEFAULT_MEMORY_RATER_MODEL = "google/gemini-3-flash-preview";
27
28
  */
28
29
  export const MEMORY_RATER_SCHEMA_NAME = "memory_rater_output";
29
30
 
30
- const OPENROUTER_CHAT_COMPLETIONS_URL = "https://openrouter.ai/api/v1/chat/completions";
31
+ /**
32
+ * Chat-completions URL, resolved per call so `OPENROUTER_BASE_URL` (gateway
33
+ * routing — see `src/utils/openrouter-base-url.ts`) is honored even when it
34
+ * is set after module load (worker subprocess env resolution).
35
+ */
36
+ function openRouterChatCompletionsUrl(): string {
37
+ return `${getOpenRouterBaseUrl()}/chat/completions`;
38
+ }
31
39
 
32
40
  /**
33
41
  * JSON Schema derived from {@link SummaryWithRatingsSchema}, the source of
@@ -138,7 +146,7 @@ export async function runMemoryRater(opts: RunMemoryRaterOpts): Promise<RunMemor
138
146
 
139
147
  let res: Response;
140
148
  try {
141
- res = await fetchFn(OPENROUTER_CHAT_COMPLETIONS_URL, {
149
+ res = await fetchFn(openRouterChatCompletionsUrl(), {
142
150
  method: "POST",
143
151
  headers: {
144
152
  "Content-Type": "application/json",
@@ -25,6 +25,7 @@ import { checkDevinCredentials } from "../providers/devin-adapter";
25
25
  import { checkOpencodeCredentials } from "../providers/opencode-adapter";
26
26
  import type { CredCheckOptions, CredStatus } from "../providers/types";
27
27
  import type { AgentCredStatus, AgentLatestModel, ProviderName, ReasoningEffort } from "../types";
28
+ import { getOpenRouterBaseUrl } from "../utils/openrouter-base-url";
28
29
  import { scrubSecrets } from "../utils/secret-scrubber";
29
30
 
30
31
  export type SupportedProvider = "claude" | "claude-managed" | "codex" | "devin" | "opencode" | "pi";
@@ -202,7 +203,7 @@ async function checkOpenAiApiKey(apiKey: string): Promise<LiveValidationResult>
202
203
  }
203
204
 
204
205
  async function checkOpenRouter(apiKey: string): Promise<LiveValidationResult> {
205
- const r = await timedFetch("https://openrouter.ai/api/v1/models", {
206
+ const r = await timedFetch(`${getOpenRouterBaseUrl()}/models`, {
206
207
  method: "GET",
207
208
  headers: { Authorization: `Bearer ${apiKey}` },
208
209
  });
@@ -506,7 +506,8 @@ export async function handleMemory(
506
506
  isLead: false,
507
507
  queryText: query,
508
508
  });
509
- // 1-hop memory_link neighbor expansion (no-op unless MEMORY_GRAPH_EXPANSION=1).
509
+ // Default-on 1-hop memory_link neighbor expansion (disable with
510
+ // MEMORY_GRAPH_EXPANSION=0|false).
510
511
  const expanded = expandCandidatesWithGraph(candidates, myAgentId, {
511
512
  scope,
512
513
  source,
@@ -1301,6 +1301,9 @@ export class CodexSession implements ProviderSession {
1301
1301
  },
1302
1302
  apiUrl,
1303
1303
  apiKey,
1304
+ // Per-task resolved env — swarm_config overlays (e.g.
1305
+ // OPENROUTER_BASE_URL) never reach process.env.
1306
+ env: this.config.env,
1304
1307
  });
1305
1308
  // null = no auth resolved or wrapper exhausted retries (already logged inside)
1306
1309
  if (!result) return;
@@ -19,6 +19,7 @@ import {
19
19
  } from "../utils/context-window";
20
20
  import { validateOpencodeCredentials } from "../utils/credentials";
21
21
  import { fetchInstalledMcpServers } from "../utils/mcp-server-fetcher";
22
+ import { DEFAULT_OPENROUTER_BASE_URL, getOpenRouterBaseUrl } from "../utils/openrouter-base-url";
22
23
  import { scrubSecrets } from "../utils/secret-scrubber";
23
24
  import { resolveSlashSkillPrompt } from "./codex-skill-resolver";
24
25
  import { CTX_MODE_NUDGE_EVERY } from "./ctx-mode-env";
@@ -135,6 +136,31 @@ function formatUnknownError(err: unknown): string {
135
136
  return err instanceof Error ? err.message : String(err);
136
137
  }
137
138
 
139
+ /**
140
+ * Route OpenRouter traffic through the configured gateway (see
141
+ * src/utils/openrouter-base-url.ts). opencode's bundled openrouter provider
142
+ * honors `provider.openrouter.options.baseURL`. Mutates `opencodeConfig` in
143
+ * place; no-op when `OPENROUTER_BASE_URL` is unset/blank/default, so default
144
+ * openrouter.ai behavior is preserved. Exported for tests.
145
+ */
146
+ export function applyOpenRouterBaseUrlOverride(
147
+ opencodeConfig: Config,
148
+ env: Record<string, string | undefined> = process.env,
149
+ ): void {
150
+ const openRouterBaseUrl = getOpenRouterBaseUrl(env as NodeJS.ProcessEnv);
151
+ if (openRouterBaseUrl === DEFAULT_OPENROUTER_BASE_URL) return;
152
+ opencodeConfig.provider = {
153
+ ...opencodeConfig.provider,
154
+ openrouter: {
155
+ ...opencodeConfig.provider?.openrouter,
156
+ options: {
157
+ ...opencodeConfig.provider?.openrouter?.options,
158
+ baseURL: openRouterBaseUrl,
159
+ },
160
+ },
161
+ };
162
+ }
163
+
138
164
  async function refreshOpenRouterModelCache(
139
165
  opencodeConfig: Config & { plugin?: string[] },
140
166
  configFilePath: string,
@@ -726,6 +752,8 @@ export class OpencodeAdapter implements ProviderAdapter {
726
752
  };
727
753
  }
728
754
 
755
+ applyOpenRouterBaseUrlOverride(opencodeConfig, config.env ?? process.env);
756
+
729
757
  // Write per-task config file
730
758
  try {
731
759
  await Bun.write(configFilePath, JSON.stringify(opencodeConfig, null, 2));
@@ -743,12 +771,24 @@ export class OpencodeAdapter implements ProviderAdapter {
743
771
  SWARM_AGENT_ID: process.env.SWARM_AGENT_ID,
744
772
  SWARM_TASK_ID: process.env.SWARM_TASK_ID,
745
773
  SWARM_IS_LEAD: process.env.SWARM_IS_LEAD,
774
+ OPENROUTER_BASE_URL: process.env.OPENROUTER_BASE_URL,
746
775
  };
747
776
  process.env.SWARM_API_URL = config.apiUrl;
748
777
  process.env.SWARM_API_KEY = config.apiKey;
749
778
  process.env.SWARM_AGENT_ID = config.agentId;
750
779
  process.env.SWARM_TASK_ID = config.taskId;
751
780
  process.env.SWARM_IS_LEAD = config.role === "lead" ? "true" : "false";
781
+ // Mirror the resolved OpenRouter gateway into the spawn env: the
782
+ // summarize plugin (and any other plugin OpenRouter fetch) reads its own
783
+ // process.env inside the opencode subprocess, which inherits OURS — a
784
+ // per-task swarm_config value in `config.env` would otherwise be
785
+ // invisible there and the plugin would bypass the gateway.
786
+ const spawnOpenRouterBaseUrl = getOpenRouterBaseUrl(
787
+ (config.env ?? process.env) as NodeJS.ProcessEnv,
788
+ );
789
+ if (spawnOpenRouterBaseUrl !== DEFAULT_OPENROUTER_BASE_URL) {
790
+ process.env.OPENROUTER_BASE_URL = spawnOpenRouterBaseUrl;
791
+ }
752
792
  process.env.CONTEXT_MODE_EXTERNAL_MCP_NUDGE_EVERY = CTX_MODE_NUDGE_EVERY;
753
793
 
754
794
  // Set OPENCODE_CONFIG scoped to the spawn call (save + restore)