super-dsh 0.1.6 → 0.1.10-e

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 (260) hide show
  1. package/README.md +25 -3
  2. package/agent-agy/cordis.patch.yml +7 -4
  3. package/agent-agy/dist/agent.d.ts +12 -11
  4. package/agent-agy/dist/agy-auth-gateway.d.ts +124 -0
  5. package/agent-agy/dist/agy-auth-gateway.js +233 -0
  6. package/agent-agy/dist/agy-auth.d.ts +153 -0
  7. package/agent-agy/dist/agy-auth.js +179 -0
  8. package/agent-agy/dist/agy-cli-client.d.ts +39 -9
  9. package/agent-agy/dist/agy-cli-client.js +82 -18
  10. package/agent-agy/dist/agy-store.d.ts +8 -0
  11. package/agent-agy/dist/index.d.ts +46 -16
  12. package/agent-agy/dist/index.js +82 -56
  13. package/agent-agy/dist/models.d.ts +70 -0
  14. package/agent-agy/dist/models.js +102 -3
  15. package/agent-agy/package.json +7 -5
  16. package/agent-agy-sdk/cordis.patch.yml +58 -0
  17. package/agent-agy-sdk/dist/adapter.d.ts +15 -0
  18. package/agent-agy-sdk/dist/adapter.js +42 -0
  19. package/agent-agy-sdk/dist/agent-preset-agy.d.ts +27 -0
  20. package/agent-agy-sdk/dist/agent-preset-agy.js +32 -0
  21. package/agent-agy-sdk/dist/agent.d.ts +138 -0
  22. package/agent-agy-sdk/dist/agent.js +494 -0
  23. package/{agent-agy → agent-agy-sdk}/dist/agy-client.d.ts +26 -6
  24. package/{agent-agy → agent-agy-sdk}/dist/agy-client.js +43 -25
  25. package/agent-agy-sdk/dist/agy-sessions.d.ts +25 -0
  26. package/agent-agy-sdk/dist/agy-sessions.js +107 -0
  27. package/agent-agy-sdk/dist/agy-store.d.ts +39 -0
  28. package/agent-agy-sdk/dist/agy-store.js +83 -0
  29. package/agent-agy-sdk/dist/inbox.d.ts +26 -0
  30. package/agent-agy-sdk/dist/inbox.js +53 -0
  31. package/agent-agy-sdk/dist/index.d.ts +93 -0
  32. package/agent-agy-sdk/dist/index.js +476 -0
  33. package/agent-agy-sdk/dist/models.d.ts +41 -0
  34. package/agent-agy-sdk/dist/models.js +74 -0
  35. package/agent-agy-sdk/dist/sdk-venv.d.ts +76 -0
  36. package/agent-agy-sdk/dist/sdk-venv.js +173 -0
  37. package/agent-agy-sdk/dist/world-plugin.d.ts +10 -0
  38. package/agent-agy-sdk/dist/world-plugin.js +82 -0
  39. package/agent-agy-sdk/package.json +64 -0
  40. package/agent-claude/dist/adapter.js +106 -31
  41. package/agent-claude/dist/agent.js +154 -17
  42. package/agent-claude/dist/claude-client.js +84 -43
  43. package/agent-claude/dist/claude-launch.js +101 -0
  44. package/agent-claude/dist/claude-native-config.js +169 -0
  45. package/agent-claude/dist/claude-provider-gateway.js +340 -0
  46. package/agent-claude/dist/claude-providers.js +391 -0
  47. package/agent-claude/dist/claude-route.js +152 -0
  48. package/agent-claude/dist/index.js +214 -37
  49. package/agent-claude/dist/models.js +14 -0
  50. package/agent-claude/dist/sdk-runtime.js +320 -0
  51. package/agent-claude/package.json +6 -4
  52. package/agent-codex/README.md +141 -36
  53. package/agent-codex/cordis.patch.yml +14 -10
  54. package/agent-codex/dist/adapter.js +64 -28
  55. package/agent-codex/dist/agent.js +255 -81
  56. package/agent-codex/dist/app-server-approvals.js +165 -0
  57. package/agent-codex/dist/app-server-client.js +723 -0
  58. package/agent-codex/dist/app-server-events.js +432 -0
  59. package/agent-codex/dist/app-server-protocol.js +95 -0
  60. package/agent-codex/dist/app-server-wire.js +299 -0
  61. package/agent-codex/dist/codex-launch.js +78 -0
  62. package/agent-codex/dist/codex-native-config.js +392 -0
  63. package/agent-codex/dist/codex-provider-gateway.js +402 -0
  64. package/agent-codex/dist/codex-providers.js +376 -0
  65. package/agent-codex/dist/codex-route.js +86 -0
  66. package/agent-codex/dist/index.js +256 -115
  67. package/agent-codex/dist/permission.js +75 -1
  68. package/agent-codex/package.json +1 -2
  69. package/agent-codex-sdk/README.md +79 -0
  70. package/agent-codex-sdk/cordis.patch.yml +81 -0
  71. package/agent-codex-sdk/dist/adapter.js +91 -0
  72. package/agent-codex-sdk/dist/agent-preset-codex.js +32 -0
  73. package/agent-codex-sdk/dist/agent.js +1085 -0
  74. package/agent-codex-sdk/dist/codex-native-config.js +392 -0
  75. package/agent-codex-sdk/dist/codex-provider-gateway.js +402 -0
  76. package/agent-codex-sdk/dist/codex-providers.js +376 -0
  77. package/agent-codex-sdk/dist/codex-route.js +86 -0
  78. package/{agent-codex/dist/codex-client.js → agent-codex-sdk/dist/codex-sdk-client.js} +253 -32
  79. package/agent-codex-sdk/dist/codex-sdk-launch.js +92 -0
  80. package/agent-codex-sdk/dist/codex-store.js +180 -0
  81. package/agent-codex-sdk/dist/inbox.js +53 -0
  82. package/agent-codex-sdk/dist/index.js +739 -0
  83. package/agent-codex-sdk/dist/permission.js +71 -0
  84. package/agent-codex-sdk/dist/sdk-runtime.js +307 -0
  85. package/agent-codex-sdk/dist/session-map.js +167 -0
  86. package/agent-codex-sdk/dist/world-plugin.js +93 -0
  87. package/agent-codex-sdk/package.json +79 -0
  88. package/agent-codex-sdk/scripts/fetch-sdk-runtime.mjs +57 -0
  89. package/agent-omp/README.md +72 -55
  90. package/agent-omp/dist/agent.js +36 -11
  91. package/agent-omp/dist/index.js +92 -52
  92. package/agent-omp/dist/lazy-rpc.js +11 -4
  93. package/agent-omp/dist/omp-cli.js +114 -97
  94. package/agent-omp/dist/omp-store.js +12 -4
  95. package/agent-omp/dist/permission.js +70 -5
  96. package/agent-omp/dist/rpc-client.js +409 -0
  97. package/agent-omp/dist/world-plugin.js +36 -3
  98. package/agent-omp/package.json +2 -6
  99. package/agent-omp-sdk/README.md +103 -0
  100. package/agent-omp-sdk/cordis.patch.yml +108 -0
  101. package/agent-omp-sdk/dist/adapter.d.ts +21 -0
  102. package/agent-omp-sdk/dist/adapter.js +36 -0
  103. package/agent-omp-sdk/dist/agent-preset-omp.d.ts +31 -0
  104. package/agent-omp-sdk/dist/agent-preset-omp.js +32 -0
  105. package/agent-omp-sdk/dist/agent.d.ts +64 -0
  106. package/agent-omp-sdk/dist/agent.js +1178 -0
  107. package/agent-omp-sdk/dist/discovery.d.ts +57 -0
  108. package/agent-omp-sdk/dist/discovery.js +116 -0
  109. package/agent-omp-sdk/dist/inbox.d.ts +28 -0
  110. package/agent-omp-sdk/dist/inbox.js +53 -0
  111. package/agent-omp-sdk/dist/index.d.ts +60 -0
  112. package/agent-omp-sdk/dist/index.js +632 -0
  113. package/agent-omp-sdk/dist/knobs.d.ts +34 -0
  114. package/agent-omp-sdk/dist/knobs.js +50 -0
  115. package/agent-omp-sdk/dist/lazy-rpc.d.ts +98 -0
  116. package/agent-omp-sdk/dist/lazy-rpc.js +178 -0
  117. package/agent-omp-sdk/dist/mobile/zoom-guard.d.ts +187 -0
  118. package/agent-omp-sdk/dist/mobile/zoom-guard.js +294 -0
  119. package/agent-omp-sdk/dist/mobile-boot.d.ts +49 -0
  120. package/agent-omp-sdk/dist/mobile-boot.js +38 -0
  121. package/agent-omp-sdk/dist/models.d.ts +39 -0
  122. package/agent-omp-sdk/dist/models.js +299 -0
  123. package/agent-omp-sdk/dist/omp-cli.d.ts +67 -0
  124. package/agent-omp-sdk/dist/omp-cli.js +164 -0
  125. package/agent-omp-sdk/dist/omp-disk-discovery.d.ts +27 -0
  126. package/agent-omp-sdk/dist/omp-disk-discovery.js +178 -0
  127. package/agent-omp-sdk/dist/omp-executable.d.ts +2 -0
  128. package/agent-omp-sdk/dist/omp-executable.js +30 -0
  129. package/agent-omp-sdk/dist/omp-store.d.ts +134 -0
  130. package/agent-omp-sdk/dist/omp-store.js +526 -0
  131. package/agent-omp-sdk/dist/pairing.d.ts +7 -0
  132. package/agent-omp-sdk/dist/pairing.js +45 -0
  133. package/agent-omp-sdk/dist/permission.d.ts +138 -0
  134. package/agent-omp-sdk/dist/permission.js +161 -0
  135. package/agent-omp-sdk/dist/protocol.d.ts +189 -0
  136. package/agent-omp-sdk/dist/rpc-types.d.ts +77 -0
  137. package/agent-omp-sdk/dist/rpc-types.js +1 -0
  138. package/agent-omp-sdk/dist/sdk-client.d.ts +160 -0
  139. package/{agent-omp → agent-omp-sdk}/dist/sdk-client.js +108 -56
  140. package/agent-omp-sdk/dist/sdk-runtime.d.ts +211 -0
  141. package/agent-omp-sdk/dist/sdk-runtime.js +418 -0
  142. package/agent-omp-sdk/dist/sidecar-client.d.ts +87 -0
  143. package/agent-omp-sdk/dist/sidecar-client.js +197 -0
  144. package/agent-omp-sdk/dist/store/adapter.d.ts +17 -0
  145. package/agent-omp-sdk/dist/store/adapter.js +47 -0
  146. package/agent-omp-sdk/dist/store/db.d.ts +70 -0
  147. package/agent-omp-sdk/dist/store/db.js +210 -0
  148. package/agent-omp-sdk/dist/store/index.d.ts +23 -0
  149. package/agent-omp-sdk/dist/store/index.js +59 -0
  150. package/agent-omp-sdk/dist/store/reconcile.d.ts +15 -0
  151. package/agent-omp-sdk/dist/store/reconcile.js +51 -0
  152. package/agent-omp-sdk/dist/store/schema.d.ts +8 -0
  153. package/agent-omp-sdk/dist/store/schema.js +40 -0
  154. package/agent-omp-sdk/dist/transport.d.ts +49 -0
  155. package/agent-omp-sdk/dist/transport.js +35 -0
  156. package/agent-omp-sdk/dist/world-home.d.ts +4 -0
  157. package/agent-omp-sdk/dist/world-home.js +23 -0
  158. package/agent-omp-sdk/dist/world-plugin.d.ts +10 -0
  159. package/agent-omp-sdk/dist/world-plugin.js +137 -0
  160. package/agent-omp-sdk/package.json +87 -0
  161. package/client/index.js +1883 -287
  162. package/cordis.patch.yml +80 -0
  163. package/package.json +23 -9
  164. package/superd-hub/dist/a2a-delivery.d.ts +189 -0
  165. package/superd-hub/dist/a2a-delivery.js +240 -0
  166. package/superd-hub/dist/a2a-host.d.ts +76 -0
  167. package/superd-hub/dist/a2a-host.js +145 -0
  168. package/superd-hub/dist/a2a-ingest.d.ts +31 -0
  169. package/superd-hub/dist/a2a-ingest.js +636 -0
  170. package/superd-hub/dist/a2a-log.d.ts +17 -0
  171. package/superd-hub/dist/a2a-log.js +42 -0
  172. package/superd-hub/dist/a2a-row.d.ts +65 -0
  173. package/superd-hub/dist/a2a-row.js +162 -0
  174. package/superd-hub/dist/a2a-service.d.ts +357 -0
  175. package/superd-hub/dist/a2a-service.js +980 -0
  176. package/superd-hub/dist/a2a-wire.d.ts +220 -0
  177. package/superd-hub/dist/a2a-wire.js +305 -0
  178. package/superd-hub/dist/agent-ids.d.ts +10 -0
  179. package/superd-hub/dist/agent-ids.js +68 -0
  180. package/superd-hub/dist/agent-roster.d.ts +29 -18
  181. package/superd-hub/dist/agent-roster.js +54 -15
  182. package/superd-hub/dist/agent-status.d.ts +119 -0
  183. package/superd-hub/dist/agent-status.js +386 -0
  184. package/superd-hub/dist/client/agent-settings.d.ts +210 -0
  185. package/superd-hub/dist/client/agent-settings.js +300 -0
  186. package/superd-hub/dist/client/mesh-api.d.ts +134 -0
  187. package/superd-hub/dist/client/mesh-api.js +219 -0
  188. package/superd-hub/dist/client/mesh-groups.d.ts +63 -0
  189. package/superd-hub/dist/client/mesh-groups.js +66 -0
  190. package/superd-hub/dist/client/roster.d.ts +48 -0
  191. package/superd-hub/dist/client/roster.js +102 -0
  192. package/superd-hub/dist/client/runtime-settings-api.d.ts +76 -0
  193. package/superd-hub/dist/client/runtime-settings-api.js +137 -0
  194. package/superd-hub/dist/client/switch.d.ts +165 -0
  195. package/superd-hub/dist/client/switch.js +295 -0
  196. package/superd-hub/dist/exec-pty-channel.d.ts +58 -0
  197. package/superd-hub/dist/exec-pty-channel.js +162 -0
  198. package/superd-hub/dist/exec-pty-framing.d.ts +32 -0
  199. package/superd-hub/dist/exec-pty-framing.js +81 -0
  200. package/superd-hub/dist/exec-pty-rpc.d.ts +21 -0
  201. package/superd-hub/dist/exec-pty-rpc.js +128 -0
  202. package/superd-hub/dist/exec-pty-session.d.ts +76 -0
  203. package/superd-hub/dist/exec-pty-session.js +234 -0
  204. package/superd-hub/dist/exec.d.ts +56 -0
  205. package/superd-hub/dist/exec.js +225 -0
  206. package/superd-hub/dist/gateway.d.ts +39 -2
  207. package/superd-hub/dist/gateway.js +62 -3
  208. package/superd-hub/dist/hub-tool.d.ts +174 -0
  209. package/superd-hub/dist/hub-tool.js +464 -0
  210. package/superd-hub/dist/index.d.ts +15 -1
  211. package/superd-hub/dist/index.js +16 -0
  212. package/superd-hub/dist/machines.d.ts +45 -0
  213. package/superd-hub/dist/machines.js +125 -0
  214. package/superd-hub/dist/mesh-federate.d.ts +138 -0
  215. package/superd-hub/dist/mesh-federate.js +223 -0
  216. package/superd-hub/dist/mesh-gateway.d.ts +67 -0
  217. package/superd-hub/dist/mesh-gateway.js +408 -0
  218. package/superd-hub/dist/mesh-link.d.ts +274 -0
  219. package/superd-hub/dist/mesh-link.js +441 -0
  220. package/superd-hub/dist/mesh-rpc.d.ts +123 -0
  221. package/superd-hub/dist/mesh-rpc.js +250 -0
  222. package/superd-hub/dist/mux-client.d.ts +71 -0
  223. package/superd-hub/dist/mux-client.js +281 -0
  224. package/superd-hub/dist/pack-version.d.ts +2 -0
  225. package/superd-hub/dist/pack-version.js +30 -0
  226. package/superd-hub/dist/roster.d.ts +6 -0
  227. package/superd-hub/dist/rpc.d.ts +18 -2
  228. package/superd-hub/dist/rpc.js +34 -4
  229. package/superd-hub/dist/spawn-world.js +3 -1
  230. package/superd-hub/dist/switch-core.d.ts +62 -25
  231. package/superd-hub/dist/switch-core.js +53 -30
  232. package/superd-hub/dist/system-head.d.ts +22 -12
  233. package/superd-hub/dist/system-head.js +100 -84
  234. package/superd-hub/dist/wire-endpoints.d.ts +264 -0
  235. package/superd-hub/dist/wire-endpoints.js +180 -0
  236. package/superd-hub/lib/client/index.js +1883 -287
  237. package/superd-hub/package.json +11 -2
  238. package/agent-agy/dist/agent-preset-projection.d.ts +0 -31
  239. package/agent-agy/dist/agent-preset-projection.js +0 -33
  240. package/agent-claude/dist/agent-preset-projection.js +0 -30
  241. package/agent-codex/dist/agent-preset-projection.js +0 -37
  242. package/agent-codex/dist/app-home.js +0 -65
  243. package/agent-codex/dist/models.js +0 -152
  244. package/agent-codex/dist/spike.js +0 -46
  245. package/agent-hermes/dist/agent-preset-projection.js +0 -34
  246. package/agent-omp/dist/agent-preset-projection.js +0 -36
  247. package/agent-omp/dist/app-home.js +0 -86
  248. package/agent-omp/dist/sidecar-client.js +0 -195
  249. package/agent-pi/dist/agent-preset-projection.js +0 -29
  250. package/superd-hub/dist/client-shim.d.ts +0 -28
  251. package/superd-hub/dist/client-shim.js +0 -251
  252. package/superd-hub/dist/index-pass.d.ts +0 -22
  253. package/superd-hub/dist/index-pass.js +0 -47
  254. package/superd-hub/dist/labels.d.ts +0 -45
  255. package/superd-hub/dist/labels.js +0 -83
  256. /package/{agent-agy → agent-agy-sdk}/bridge/agy_bridge.py +0 -0
  257. /package/{agent-agy → agent-agy-sdk}/bridge/gemini_openai_server.mjs +0 -0
  258. /package/{agent-codex/dist/codex-events.js → agent-codex-sdk/dist/codex-sdk-events.js} +0 -0
  259. /package/{agent-omp → agent-omp-sdk}/dist/protocol.js +0 -0
  260. /package/{agent-omp → agent-omp-sdk}/sidecar/main.ts +0 -0
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * agent-codex provider — Codex provider plugin for DeepSeek Harness
3
- * (Super D, SDK line).
3
+ * (Super D, app-server line).
4
4
  *
5
5
  * Replaces the built-in agent loop with an `AgentFactory` that drives
6
- * `@openai/codex-sdk` threads (one `CodexSdkClient` per session) and bridges
6
+ * `codex app-server` threads (one `CodexAppServerClient` per session) and bridges
7
7
  * the projected wire stream into the Dash Agent/Session contracts. Mirrors
8
8
  * `@deepseek-ai/dsh-agent-loop`'s creation transaction (prepare → owned
9
9
  * storage handle → setup → publish) so the session + agent publish as one
@@ -22,16 +22,19 @@
22
22
  import { existsSync, realpathSync, statSync } from "node:fs";
23
23
  import { join } from "node:path";
24
24
  import { Service } from "@deepseek-ai/cordis";
25
- import { installModelSelection } from "@deepseek-ai/dsh-agent";
26
25
  import { SessionLogOffset, SessionPreparation, interruptedTurnClosers, } from "@deepseek-ai/dsh-session";
27
- import { CodexSdkClient } from "./codex-client.js";
26
+ import { CodexAppServerClient } from "./app-server-client.js";
28
27
  import { CodexAgent } from "./agent.js";
29
- import { CodexLlmAdapter, CODEX_PROVIDER_ID } from "./adapter.js";
28
+ import { CodexLlmAdapter, routeIds } from "./adapter.js";
30
29
  import { registerCodexPreset } from "./agent-preset-codex.js";
31
30
  import { readRolloutHead, resolveCodexHome } from "./codex-store.js";
31
+ import { CodexProvidersGateway, syncFromNative } from "./codex-provider-gateway.js";
32
+ import { readProviderStore } from "./codex-providers.js";
33
+ import { readNativeSelection } from "./codex-native-config.js";
34
+ import { seedRoute } from "./codex-route.js";
35
+ import { providerLaunch } from "./codex-launch.js";
32
36
  import { CodexResumeError, sessionRecord, upsertSession } from "./session-map.js";
33
- import { readCodexModelCatalog, CODEX_DEFAULT_MODEL_PLACEHOLDER } from "./models.js";
34
- import { codexApprovalMode, defaultPermissionPreset, envApprovalMode, presetFromEvents } from "./permission.js";
37
+ import { codexApprovalMode, defaultPermissionPreset, envApprovalMode, presetFromEvents, sandboxModeForPreset } from "./permission.js";
35
38
  /** Realpath of a recorded cwd, accepted only when it names an existing directory. */
36
39
  function validatedCwd(cwd) {
37
40
  if (cwd === undefined)
@@ -49,6 +52,13 @@ const trace = (...parts) => {
49
52
  if (process.env.CODEX_TRACE === "1")
50
53
  process.stderr.write(`[codex-provider ${Date.now() % 1_000_000}] ${parts.join(" ")}\n`);
51
54
  };
55
+ /**
56
+ * How often the canonical store is reconciled with the user's `config.toml`
57
+ * (2026-10-04 user ruling: startup + every ten minutes). The file is the user's
58
+ * own and edits it with their own tools, so the sync is a poll rather than an
59
+ * event subscription — ten minutes is the granularity the ruling asked for.
60
+ */
61
+ const SYNC_INTERVAL_MS = 600_000;
52
62
  /** Open a read handle, pull the complete stored log, close. */
53
63
  async function readStoredEvents(persistence, id, signal) {
54
64
  const handle = await persistence.open(id, "read", signal === undefined ? {} : { signal });
@@ -82,6 +92,15 @@ export class CodexProvider extends Service {
82
92
  home;
83
93
  /** Echo/staleness guard for the app-wide default-model push. */
84
94
  defaultModelKey = "";
95
+ /**
96
+ * The live `ctx.llm` registration for the codex routes, when one exists.
97
+ * Its `replace()` is how the route SET follows the store: a provider gaining
98
+ * or losing its first model changes which routes exist, which re-registration
99
+ * alone can express.
100
+ */
101
+ routes;
102
+ /** Epoch ms of the last reconcile with the user's `config.toml`. */
103
+ syncedAt;
85
104
  constructor(ctx) {
86
105
  super(ctx, "codexProvider");
87
106
  this.runtime = { ctx };
@@ -93,7 +112,15 @@ export class CodexProvider extends Service {
93
112
  const codexHome = resolveCodexHome();
94
113
  trace(`home: worldHome=${this.home} codexHome=${codexHome} configExists=${existsSync(join(codexHome, "config.toml"))}`);
95
114
  ctx.effect(() => ctx.agents.setFactory(this), "codexProvider.setFactory()");
115
+ // The store is canonical and the native file is a source (2026-10-04).
116
+ // Register the routes the store already implies FIRST, so a malformed
117
+ // config.toml costs the world a reconcile, never its provider list; then
118
+ // reconcile, then keep reconciling on a timer.
96
119
  this.#registerModelCatalog();
120
+ this.syncNow();
121
+ const timer = setInterval(() => this.syncNow(), SYNC_INTERVAL_MS);
122
+ timer.unref?.();
123
+ ctx.effect(() => () => clearInterval(timer), "codexProvider.syncTimer()");
97
124
  this.#registerDefaultModel();
98
125
  // The roster must be visible to the API gateway's root-level remote
99
126
  // enumeration (dsh-host-apiproxy read it from the root service table);
@@ -107,23 +134,100 @@ export class CodexProvider extends Service {
107
134
  // pins its `default` to the Codex preset). The adapter only registers
108
135
  // its one fixed preset into the registry.
109
136
  registerCodexPreset(ctx);
137
+ // The Hub settings panel's data plane (2026-10-03 multi-provider ruling): an
138
+ // adapter-owned Typert Remote (`codexProviders/get|save|remove`) on this
139
+ // world's context. The hub's carrier forwards `envelope.method` verbatim,
140
+ // so the panel reaches it through the existing `/hub/api?agent=codex`
141
+ // family — no new hub route, and no provider knowledge leaks out of the
142
+ // adapter (dev-rules §12). Cordis keeps the instance alive as this
143
+ // context's `codexProviders` service. There is no `select`: which provider a
144
+ // session runs on is DSH's per-session `model/selection`.
145
+ //
146
+ // `resolveCodexHome` is passed as a THUNK on purpose: the native home is
147
+ // re-resolved per call (so a test's `CODEX_HOME` and an operator's override
148
+ // are both honored) and construction stays read-free — the panel's first
149
+ // `get` is what touches disk.
150
+ new CodexProvidersGateway(ctx, this.home, () => resolveCodexHome(), () => this.refreshRoutes());
151
+ }
152
+ /**
153
+ * Reconcile the canonical provider store with the user's `config.toml` and
154
+ * re-register the `ctx.llm` routes if either moved.
155
+ *
156
+ * Called once at startup and every {@link SYNC_INTERVAL_MS} (2026-10-04 user
157
+ * ruling), plus after every panel mutation through the gateway's `onChange`.
158
+ * Never throws: a provider list is not worth a boot.
159
+ */
160
+ syncNow() {
161
+ try {
162
+ const { store, added, updated } = syncFromNative(this.home, resolveCodexHome());
163
+ if (added.length > 0 || updated.length > 0) {
164
+ this.runtime.ctx.logger.info(`codex-provider: config.toml sync — added ${added.join(", ") || "none"}, refreshed ${updated.join(", ") || "none"}`);
165
+ }
166
+ this.syncedAt = Date.now();
167
+ this.#applyRoutes(store.providers);
168
+ }
169
+ catch (error) {
170
+ this.runtime.ctx.logger.warn(`codex-provider: provider sync failed: ${String(error)}`);
171
+ }
172
+ }
173
+ /** Re-register the `ctx.llm` routes from the current store (panel mutation path). */
174
+ refreshRoutes() {
175
+ try {
176
+ this.#applyRoutes(readProviderStore(this.home).providers);
177
+ }
178
+ catch (error) {
179
+ this.runtime.ctx.logger.warn(`codex-provider: route refresh failed: ${String(error)}`);
180
+ }
181
+ }
182
+ /**
183
+ * The provider id the user's own `config.toml` names, mapped into the store.
184
+ * It is the app-wide default's provider and the fallback for a session whose
185
+ * logged selection predates the multi-provider rework. `undefined` when the
186
+ * native file names nothing this world can serve.
187
+ */
188
+ nativeProviderId() {
189
+ const native = readNativeSelection(resolveCodexHome());
190
+ const wanted = native.providerId;
191
+ if (wanted === undefined)
192
+ return undefined;
193
+ const providers = readProviderStore(this.home).providers;
194
+ return providers.find((entry) => entry.id === wanted)?.id ?? undefined;
195
+ }
196
+ /** The store entry a DSH route id names. */
197
+ providerEntry(providerId) {
198
+ return readProviderStore(this.home).providers.find((entry) => entry.id === providerId);
110
199
  }
111
200
  /**
112
- * The factory owns its sessions' route (user directive 2026-09-15): the
113
- * ambient `agentDefaultModel` selection is NOT authoritative here — a
114
- * session created by this factory always runs on the codex route, with the
115
- * codex config's real default model unless the caller asked for a served
116
- * codex model. Without an explicit choice the model rides the thread
117
- * options only when the catalog is readable (the placeholder is omitted so
118
- * codex's own config default applies).
201
+ * The factory's session-level route fallback — always ONE coherent pair.
202
+ *
203
+ * The ambient `agentDefaultModel` selection is not authoritative here (user
204
+ * directive 2026-09-15), and neither is a global "active" provider any more:
205
+ * the session's OWN `model/selection` decides, resolved by the agent at each
206
+ * turn. These options are what a session with no selection of its own starts
207
+ * from.
208
+ *
209
+ * The one trap this method exists to close: a cached default can outlive the
210
+ * model list it named, because the panel may have removed that model since
211
+ * (upstream persists the last pick as the deployment default). Honouring a pair
212
+ * the provider no longer serves would seed a route nobody can run, so a request
213
+ * wins only when BOTH halves are still servable; otherwise the user's own
214
+ * `config.toml` pair takes over, which is authoritative for exactly the provider
215
+ * it is in force for.
119
216
  */
120
217
  sessionAgentOptions(requested) {
121
- const catalog = readCodexModelCatalog(resolveCodexHome());
122
- const explicit = requested?.provider === CODEX_PROVIDER_ID && typeof requested.model === "string" && requested.model !== CODEX_DEFAULT_MODEL_PLACEHOLDER
123
- ? requested.model
124
- : undefined;
125
- const model = explicit ?? (catalog.defaultModel === CODEX_DEFAULT_MODEL_PLACEHOLDER ? undefined : catalog.defaultModel);
126
- return { ...(requested ?? {}), provider: CODEX_PROVIDER_ID, ...(model === undefined ? {} : { model }) };
218
+ const native = readNativeSelection(resolveCodexHome());
219
+ const wanted = requested?.provider !== undefined && typeof requested.model === "string" ? requested : undefined;
220
+ const serves = wanted !== undefined
221
+ && this.providerEntry(wanted.provider)?.models.some((model) => model.id === wanted.model) === true;
222
+ if (wanted !== undefined && serves)
223
+ return { ...wanted, provider: wanted.provider, model: wanted.model };
224
+ const provider = this.nativeProviderId();
225
+ const model = native.model !== undefined && native.model !== "" ? native.model : undefined;
226
+ return {
227
+ ...(requested ?? {}),
228
+ ...(provider === undefined ? {} : { provider }),
229
+ ...(model === undefined ? {} : { model }),
230
+ };
127
231
  }
128
232
  /**
129
233
  * The write path is mandatory now (2026-09-16): this factory owns the DSH
@@ -151,12 +255,12 @@ export class CodexProvider extends Service {
151
255
  return { handle, storedCount: 0 };
152
256
  }
153
257
  /**
154
- * Runtime facts the agent stamps into the log: Codex's own system prompt
155
- * (rollout HEAD `base_instructions`, readable once the thread exists) and
156
- * the effective route metadata (selected codex model, else the catalog
157
- * default, plus the catalog's context window).
258
+ * Runtime facts the agent stamps into the log and needs to launch a child:
259
+ * Codex's own system prompt (rollout HEAD `base_instructions`, readable once
260
+ * the thread exists), the launch that pins a provider, and the provider's own
261
+ * context window for one of its models.
158
262
  */
159
- agentRuntime(client, id) {
263
+ agentRuntime(client, id, cwd) {
160
264
  const codexHome = resolveCodexHome();
161
265
  return {
162
266
  systemPrompt: () => {
@@ -165,25 +269,24 @@ export class CodexProvider extends Service {
165
269
  return undefined;
166
270
  return readRolloutHead(codexHome, threadId)?.baseInstructions;
167
271
  },
168
- routeContext: (preferredModel) => {
169
- const catalog = readCodexModelCatalog(codexHome);
170
- const model = preferredModel !== undefined && preferredModel !== "" && preferredModel !== CODEX_DEFAULT_MODEL_PLACEHOLDER
171
- ? preferredModel
172
- : catalog.defaultModel === CODEX_DEFAULT_MODEL_PLACEHOLDER
173
- ? undefined
174
- : catalog.defaultModel;
175
- if (model === undefined)
176
- return undefined;
177
- const entry = catalog.models.find((candidate) => candidate.id === model);
178
- return {
179
- provider: CODEX_PROVIDER_ID,
180
- model,
181
- ...(entry?.contextWindow !== undefined && entry.contextWindow > 0 ? { contextWindow: entry.contextWindow } : {}),
182
- };
272
+ // The whole on-the-fly mechanism in one call: the agent asks for the
273
+ // argv/env that pin a provider, and gets them without a single write to
274
+ // the user's home (the per-provider model catalog is ours).
275
+ launchFor: (providerId) => {
276
+ const entry = this.providerEntry(providerId);
277
+ return entry === undefined ? undefined : providerLaunch(this.home, codexHome, entry);
278
+ },
279
+ contextWindowFor: (providerId, model) => {
280
+ const entry = this.providerEntry(providerId);
281
+ const found = entry?.models.find((candidate) => candidate.id === model);
282
+ return found?.contextWindow;
183
283
  },
184
284
  // RC-4: turn-time native rejections mark the map so the next resume
185
285
  // fails fast (CodexResumeError NATIVE_REJECTED) instead of burning
186
286
  // another turn on a dead thread.
287
+ // The session workspace: the per-turn permission fold needs it for
288
+ // `workspaceWrite.writableRoots` (never a home — the user's workspace).
289
+ cwd: () => cwd,
187
290
  markNotResumable: () => {
188
291
  try {
189
292
  upsertSession(this.home, String(id), { resumable: false });
@@ -230,12 +333,17 @@ export class CodexProvider extends Service {
230
333
  const service = mCtx.get("agentDefaultModel");
231
334
  if (service === undefined)
232
335
  return;
233
- const catalog = readCodexModelCatalog(resolveCodexHome());
234
- if (catalog.defaultModel === CODEX_DEFAULT_MODEL_PLACEHOLDER) {
235
- trace("default model push skipped: catalog unreadable (placeholder)");
336
+ // The default is whatever the user's own codex config runs, expressed in
337
+ // the store's own route id so the harness can resolve it. A config naming
338
+ // a provider this world has no route for is not a default we can push.
339
+ const native = readNativeSelection(resolveCodexHome());
340
+ const provider = this.nativeProviderId();
341
+ const model = native.model;
342
+ if (provider === undefined || model === undefined || model === "") {
343
+ trace("default model push skipped: config.toml names no servable provider/model");
236
344
  return;
237
345
  }
238
- const target = { provider: CODEX_PROVIDER_ID, model: catalog.defaultModel };
346
+ const target = { provider, model };
239
347
  const targetKey = `${target.provider}/${target.model}`;
240
348
  const current = service.currentSelection?.();
241
349
  const currentKey = current === undefined ? undefined : `${current.provider}/${current.model}`;
@@ -256,14 +364,45 @@ export class CodexProvider extends Service {
256
364
  });
257
365
  });
258
366
  }
367
+ /**
368
+ * Register the codex routes on `ctx.llm` — ONE ROUTE PER PROVIDER with
369
+ * models (2026-10-04 rework).
370
+ *
371
+ * The harness's model selector is provider-grouped, and its per-session
372
+ * choice is durable (`model/selection` + the `modelSelection` projection), so
373
+ * expressing the Codex provider list as real routes is what makes the whole
374
+ * feature DSH-native: nothing in Super D has to model "the active provider" or
375
+ * remember which route a session last used.
376
+ *
377
+ * A provider with no models yet registers no route — codex itself cannot run
378
+ * it until the user says which model to ask for, and an empty group in the
379
+ * picker would be noise.
380
+ */
259
381
  #registerModelCatalog() {
382
+ this.#applyRoutes(readProviderStore(this.home).providers);
383
+ }
384
+ /** Register or atomically re-point the codex routes at the given providers. */
385
+ #applyRoutes(providers) {
260
386
  const llm = this.runtime.ctx.get("llm");
261
387
  if (llm === undefined)
262
388
  return;
263
- // The catalog is fail-soft (a `codex-default` placeholder when the home
264
- // is unreadable), so the route always registers and the selector is
265
- // never empty.
266
- llm.registerAdapter([CODEX_PROVIDER_ID], new CodexLlmAdapter(resolveCodexHome()));
389
+ const ids = routeIds({ version: 1, providers });
390
+ if (this.routes === undefined) {
391
+ if (ids.length === 0) {
392
+ // `registerAdapter` refuses an empty initial set, and there is nothing
393
+ // to serve yet; the next sync or panel save will register the routes.
394
+ trace("route registration deferred: no provider with models yet");
395
+ return;
396
+ }
397
+ this.routes = llm.registerAdapter([...ids], new CodexLlmAdapter(this.home));
398
+ trace(`routes registered: ${ids.join(", ")}`);
399
+ return;
400
+ }
401
+ // `replace` validates the whole candidate set before swapping, so a bad id
402
+ // cannot leave the registry half-updated — but a throw here must not take
403
+ // the caller (a timer, or a panel save) down with it.
404
+ this.routes.replace([...ids]);
405
+ trace(`routes replaced: ${ids.join(", ") || "none"}`);
267
406
  }
268
407
  async createAgent(ownerCtx, options) {
269
408
  const loopCtx = this.runtime.ctx;
@@ -281,30 +420,29 @@ export class CodexProvider extends Service {
281
420
  // the upstream persistence service a created session would persist
282
421
  // nothing, so require it up front.
283
422
  const persistence = this.requirePersistence("create");
284
- // Approval policy is pinned at Codex thread construction and the Dash
285
- // session does not exist yet at this point (it is prepared + announced —
286
- // which is what stamps `permission/preset` via pinInitialPermission —
287
- // only inside setupAndPublish, AFTER this spawn), so the effective
288
- // preset cannot come from session events: the env override
289
- // (CODEX_APPROVAL_MODE, headless runs) wins, else the permission
290
- // service's default, mapped onto the launch policy.
423
+ // App-server line (2026-10-03): the thread's LAUNCH posture still needs a
424
+ // decision here — the Dash session does not exist yet (it is prepared +
425
+ // announced, which is what stamps `permission/preset` via
426
+ // pinInitialPermission, only inside setupAndPublish AFTER this call) — so
427
+ // the env override (CODEX_APPROVAL_MODE, headless runs) wins, else the
428
+ // permission service's default. From the first turn on, `agent.ts` folds
429
+ // the LIVE session log per turn (`turnPolicyFromEvents`) and overrides both
430
+ // fields on every `turn/start`, which is what makes `/permission` real.
291
431
  const preset = envApprovalMode() ?? defaultPermissionPreset(loopCtx);
292
432
  const approvalPolicy = codexApprovalMode(preset);
433
+ const sandboxMode = sandboxModeForPreset(preset);
293
434
  const spawnCwd = validatedCwd(cwd) ?? process.cwd();
294
- trace(`create id=${id} preset=${preset ?? "none"} approval=${approvalPolicy} spawnCwd=${spawnCwd}${envApprovalMode() === undefined ? "" : " (env override)"}`);
295
- // Backend takes ZERO part in the UI's browser-local new-session draft
296
- // beyond the (zero-IO) thread construction: no rollout, no session
297
- // events — the session announces blank. The thread materializes on the
298
- // first prompt.
299
- const client = await CodexSdkClient.spawn(["--approval-mode", approvalPolicy], spawnCwd);
300
- // Deterministic per-session route (user wiring directive 2026-09-15): the
301
- // factory owns the model — the catalog's real default rides the first
302
- // turn; the placeholder (unreadable catalog) is OMITTED so the codex
303
- // config default applies.
304
- const createCatalog = readCodexModelCatalog(resolveCodexHome());
305
- if (createCatalog.defaultModel !== CODEX_DEFAULT_MODEL_PLACEHOLDER) {
306
- void client.setModel(CODEX_PROVIDER_ID, createCatalog.defaultModel).catch(() => { });
307
- }
435
+ trace(`create id=${id} preset=${preset ?? "none"} approval=${approvalPolicy} sandbox=${sandboxMode} spawnCwd=${spawnCwd}${envApprovalMode() === undefined ? "" : " (env override)"}`);
436
+ // Backend takes ZERO part in the UI's browser-local new-session draft: the
437
+ // app-server child is NOT spawned here (lazy-spawn gate, rules §14-i) —
438
+ // create() is pure construction, and the process materializes on the first
439
+ // real prompt.
440
+ const client = CodexAppServerClient.create({ cwd: spawnCwd, approvalPolicy, sandboxMode });
441
+ // The session's route is NOT pinned here any more (2026-10-04): the agent
442
+ // resolves it per turn from DSH's own model-selection state, and a launch
443
+ // costs nothing until the first real prompt. `setupAndPublish` still seeds a
444
+ // `model/selection` for a session that has none, so the transcript can name
445
+ // its route from turn one.
308
446
  const preparation = SessionPreparation.create(loopCtx.sessions.prepare(id, {
309
447
  ...(options.seed === undefined ? {} : { seed: options.seed }),
310
448
  ...(meta === undefined ? {} : { meta }),
@@ -324,7 +462,7 @@ export class CodexProvider extends Service {
324
462
  });
325
463
  this.followThreadId(id, client);
326
464
  handedOff = true;
327
- return await setupAndPublish(loopCtx, ownerCtx, id, this.sessionAgentOptions(options.agentOptions), options.setup, preparation.session, client, options.parentAgent, "startup", { enterSession: true, preparation }, stored, this.agentRuntime(client, id));
465
+ return await setupAndPublish(loopCtx, ownerCtx, id, this.sessionAgentOptions(options.agentOptions), options.setup, preparation.session, client, options.parentAgent, "startup", { enterSession: true, preparation }, stored, this.agentRuntime(client, id, spawnCwd));
328
466
  }
329
467
  catch (error) {
330
468
  trace(`create ${id} THREW: ${String(error)}`);
@@ -378,10 +516,10 @@ export class CodexProvider extends Service {
378
516
  if (spawnCwd === undefined) {
379
517
  throw new Error(`cannot resume session "${id}": its recorded working directory no longer exists`);
380
518
  }
381
- // Approval policy for the re-attached thread: launch-only, so it is
382
- // decided BEFORE the spawn from the recorded preset, else the persisted
383
- // session log, else the permission service's default. CODEX_APPROVAL_MODE
384
- // (headless runs) overrides; no preset → never.
519
+ // Approval policy / sandbox mode for the re-attached thread: the LAUNCH
520
+ // posture, decided from the recorded preset, else the persisted session log,
521
+ // else the permission service's default. CODEX_APPROVAL_MODE (headless
522
+ // runs) overrides. The per-turn fold in agent.ts takes over from turn one.
385
523
  const envMode = envApprovalMode();
386
524
  const preset = envMode !== undefined
387
525
  ? envMode
@@ -389,11 +527,17 @@ export class CodexProvider extends Service {
389
527
  presetFromEvents(await readStoredEvents(persistence, id, options.signal)) ??
390
528
  defaultPermissionPreset(loopCtx);
391
529
  const approvalPolicy = codexApprovalMode(preset);
392
- trace(`resume id=${id} spawnCwd=${spawnCwd} preset=${preset ?? "none"} approval=${approvalPolicy}`);
393
- // Re-attach to the Codex thread by its id (Codex owns the live agent
394
- // context and keeps generating it from here on; the model context is
395
- // restored by Codex itself from its rollout).
396
- const client = await CodexSdkClient.spawn(["--approval-mode", approvalPolicy, "--resume-thread", record.threadId], spawnCwd);
530
+ const sandboxMode = sandboxModeForPreset(preset);
531
+ trace(`resume id=${id} spawnCwd=${spawnCwd} preset=${preset ?? "none"} approval=${approvalPolicy} sandbox=${sandboxMode}`);
532
+ // Re-attach to the Codex thread by its id on the first real prompt (Codex
533
+ // owns the live agent context and restores the model context from its
534
+ // rollout). No process is spawned here — lazy-spawn gate, rules §14-i.
535
+ const client = CodexAppServerClient.create({
536
+ cwd: spawnCwd,
537
+ resumeThreadId: record.threadId,
538
+ approvalPolicy,
539
+ sandboxMode,
540
+ });
397
541
  let handle;
398
542
  let stored;
399
543
  let preparation;
@@ -419,7 +563,7 @@ export class CodexProvider extends Service {
419
563
  handle = undefined; // ownership passes to setupAndPublish
420
564
  this.followThreadId(id, client);
421
565
  handedOff = true;
422
- return await setupAndPublish(loopCtx, ownerCtx, id, this.sessionAgentOptions(options.agentOptions), options.setup, preparation.session, client, options.parentAgent, "resume", { enterSession: true, preparation }, stored, this.agentRuntime(client, id));
566
+ return await setupAndPublish(loopCtx, ownerCtx, id, this.sessionAgentOptions(options.agentOptions), options.setup, preparation.session, client, options.parentAgent, "resume", { enterSession: true, preparation }, stored, this.agentRuntime(client, id, spawnCwd));
423
567
  }
424
568
  catch (error) {
425
569
  trace(`resume ${id} THREW: ${String(error)} | stack=${error instanceof Error ? error.stack?.slice(0, 400) : "n/a"}`);
@@ -444,13 +588,15 @@ export class CodexProvider extends Service {
444
588
  * the client's event stream.
445
589
  */
446
590
  followThreadId = (id, client) => {
447
- // RE-ARMING observer (plan C6): newSession() swaps threads mid-session, so
448
- // the pairing must be re-upserted whenever the client's thread id CHANGES,
449
- // not just once. Unsubscribing after the first capture would leave the map
450
- // pointing at the discarded thread, and the next resume would re-attach to
451
- // a thread the session no longer runs on.
591
+ // RE-ARMING observer: newSession() swaps threads mid-session, so the pairing
592
+ // must be re-upserted whenever the client ATTACHES a new thread, not just
593
+ // once. Unsubscribing after the first capture would leave the map pointing
594
+ // at the discarded thread, and the next resume would re-attach to a thread
595
+ // the session no longer runs on.
452
596
  let lastRecorded = client.threadId;
453
597
  const observe = (threadId) => {
598
+ if (threadId === lastRecorded)
599
+ return;
454
600
  lastRecorded = threadId;
455
601
  try {
456
602
  upsertSession(this.home, String(id), { threadId });
@@ -460,14 +606,9 @@ export class CodexProvider extends Service {
460
606
  this.runtime.ctx.logger.warn(`codex-provider: failed to persist the Codex thread id for ${String(id)}: ${String(error)}`);
461
607
  }
462
608
  };
463
- if (lastRecorded !== null)
464
- observe(lastRecorded);
465
- client.on(() => {
466
- const observed = client.threadId;
467
- if (observed === null || observed === lastRecorded)
468
- return;
469
- observe(observed);
470
- });
609
+ // App-server line: the thread attaches lazily (first prompt), and
610
+ // `onThreadAttached` fires synchronously when one already exists.
611
+ client.onThreadAttached(observe);
471
612
  };
472
613
  }
473
614
  /**
@@ -500,37 +641,37 @@ async function setupAndPublish(loopCtx, ownerCtx, id, agentOptions, setup, sessi
500
641
  try {
501
642
  // Build the agent shim over the session and the live client.
502
643
  agent = new CodexAgent(loopCtx, id, agentOptions, session, client, () => idleExit?.(), runtimeInfo);
503
- // Pin THIS session to the codex route. The global default-model setting is
504
- // a best-effort push (the settings service may flush late, and a shared
505
- // test home can hold another line's default), so the factory must not let
506
- // the first prompt depend on it: `selectForNextRequest` is the same
507
- // per-session mechanism `session/selectModel` uses (appends the log-only
508
- // `model/selection` and caches the next request's route).
644
+ // Seed THIS session's route only when its log has none (2026-10-04).
645
+ //
646
+ // The pin used to run on every create AND resume, which would now stomp a
647
+ // choice the user made in the model selector: `model/selection` IS the
648
+ // session's durable route, so an existing one is the authority and only a
649
+ // session that has never named a route gets the boot default. The append is
650
+ // log-only — the agent resolves it at each turn (`#resolveRoute`).
509
651
  try {
510
- const route = runtimeInfo?.routeContext(agentOptions.model);
511
- if (route !== undefined) {
512
- const selection = { provider: route.provider, model: route.model };
513
- // Mirrors the upstream session-controller's `selectForNextRequest`:
514
- // the durable log-only `model/selection` plus an agent-scoped
515
- // `ModelSelectionRef` (same shape the controller installs) that the
516
- // next prompt's route check reads. Synchronous on purpose — the first
517
- // prompt must not depend on the global default's late flush.
518
- let picked = selection;
519
- installModelSelection(agent.ctx, {
520
- get current() { return picked; },
521
- set current(next) { picked = next; },
522
- assembled: undefined,
523
- });
652
+ // A narrow local cast for the same reason the append below uses one:
653
+ // `model/selection` is declared by the session-controller package's
654
+ // `SessionEventMap` augmentation, which this adapter deliberately does not
655
+ // depend on. The event name is the contract; its payload is not read here.
656
+ const events = session.snapshotEvents();
657
+ const alreadyRouted = events.some((event) => event.type === "model/selection");
658
+ // ONE source, on purpose: `agentOptions` is already the reconciled pair
659
+ // (`sessionAgentOptions`). Building the seed from the native provider id
660
+ // and the default model independently produced `zhipu-cn/deepseek-flash`,
661
+ // which no provider serves — see `seedRoute`.
662
+ const seed = seedRoute(agentOptions);
663
+ if (!alreadyRouted && seed !== undefined) {
664
+ const selection = { provider: seed.provider, model: seed.model };
524
665
  // `model/selection` is contributed to `SessionEventMap` by the upstream
525
666
  // session-controller package (declaration merging); this adapter does
526
667
  // not depend on it, so the append goes through a narrow local cast.
527
668
  session.append("model/selection", selection);
528
- trace(`session route pinned: ${route.provider}/${route.model}`);
669
+ trace(`session route seeded: ${seed.provider}/${seed.model}`);
529
670
  }
530
671
  }
531
672
  catch (error) {
532
673
  // Fail-soft: the boot-time default-model push remains the fallback.
533
- trace(`session route pin failed: ${String(error)}`);
674
+ trace(`session route seed failed: ${String(error)}`);
534
675
  }
535
676
  // Session identity (the preset stamp) and the Codex system prompt commit
536
677
  // on the first turn via CodexAgent's #bootstrapSessionIdentity /