@0xmaxma/claude-gateway 2.0.6 → 2.0.7

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 (364) hide show
  1. package/README.md +14 -1
  2. package/dist/agent/builtin-commands.js +5 -5
  3. package/dist/agent/builtin-commands.js.map +1 -1
  4. package/dist/agent/command-help.d.ts +9 -0
  5. package/dist/agent/command-help.d.ts.map +1 -0
  6. package/dist/agent/command-help.js +50 -0
  7. package/dist/agent/command-help.js.map +1 -0
  8. package/dist/agent/dreaming/index.d.ts +6 -3
  9. package/dist/agent/dreaming/index.d.ts.map +1 -1
  10. package/dist/agent/dreaming/index.js +21 -1
  11. package/dist/agent/dreaming/index.js.map +1 -1
  12. package/dist/agent/knowledge/reflection.d.ts +7 -4
  13. package/dist/agent/knowledge/reflection.d.ts.map +1 -1
  14. package/dist/agent/knowledge/reflection.js +19 -1
  15. package/dist/agent/knowledge/reflection.js.map +1 -1
  16. package/dist/agent/runner.d.ts +37 -3
  17. package/dist/agent/runner.d.ts.map +1 -1
  18. package/dist/agent/runner.js +164 -47
  19. package/dist/agent/runner.js.map +1 -1
  20. package/dist/agent/session-confirmations.d.ts +12 -0
  21. package/dist/agent/session-confirmations.d.ts.map +1 -0
  22. package/dist/agent/session-confirmations.js +43 -0
  23. package/dist/agent/session-confirmations.js.map +1 -0
  24. package/dist/agent/session-status.d.ts +7 -0
  25. package/dist/agent/session-status.d.ts.map +1 -0
  26. package/dist/agent/session-status.js +16 -0
  27. package/dist/agent/session-status.js.map +1 -0
  28. package/dist/agent/skill-learning/index.d.ts +5 -1
  29. package/dist/agent/skill-learning/index.d.ts.map +1 -1
  30. package/dist/agent/skill-learning/index.js +23 -1
  31. package/dist/agent/skill-learning/index.js.map +1 -1
  32. package/dist/agent/skill-learning/notifier.d.ts +2 -1
  33. package/dist/agent/skill-learning/notifier.d.ts.map +1 -1
  34. package/dist/agent/skill-learning/notifier.js +1 -0
  35. package/dist/agent/skill-learning/notifier.js.map +1 -1
  36. package/dist/api/apps-router.d.ts.map +1 -1
  37. package/dist/api/apps-router.js +1 -3
  38. package/dist/api/apps-router.js.map +1 -1
  39. package/dist/api/cron-router.d.ts +1 -1
  40. package/dist/api/cron-router.d.ts.map +1 -1
  41. package/dist/api/cron-router.js +4 -4
  42. package/dist/api/cron-router.js.map +1 -1
  43. package/dist/api/dashboard-processes.d.ts +86 -0
  44. package/dist/api/dashboard-processes.d.ts.map +1 -0
  45. package/dist/api/dashboard-processes.js +155 -0
  46. package/dist/api/dashboard-processes.js.map +1 -0
  47. package/dist/api/dashboard-sessions.d.ts +15 -0
  48. package/dist/api/dashboard-sessions.d.ts.map +1 -0
  49. package/dist/api/dashboard-sessions.js +66 -0
  50. package/dist/api/dashboard-sessions.js.map +1 -0
  51. package/dist/api/gateway-router.d.ts +3 -4
  52. package/dist/api/gateway-router.d.ts.map +1 -1
  53. package/dist/api/gateway-router.js +77 -56
  54. package/dist/api/gateway-router.js.map +1 -1
  55. package/dist/api/oauth-connectors-router.d.ts +1 -1
  56. package/dist/api/oauth-connectors-router.d.ts.map +1 -1
  57. package/dist/api/oauth-connectors-router.js +11 -21
  58. package/dist/api/oauth-connectors-router.js.map +1 -1
  59. package/dist/api/router.d.ts +1 -1
  60. package/dist/api/router.d.ts.map +1 -1
  61. package/dist/api/router.js +3 -9
  62. package/dist/api/router.js.map +1 -1
  63. package/dist/api/share-router.d.ts +1 -1
  64. package/dist/api/share-router.d.ts.map +1 -1
  65. package/dist/api/share-router.js +1 -1
  66. package/dist/api/share-router.js.map +1 -1
  67. package/dist/api/slack-webhook-router.js +1 -1
  68. package/dist/api/slack-webhook-router.js.map +1 -1
  69. package/dist/api/voice-router.d.ts +1 -0
  70. package/dist/api/voice-router.d.ts.map +1 -1
  71. package/dist/api/voice-router.js +7 -0
  72. package/dist/api/voice-router.js.map +1 -1
  73. package/dist/apps/agent-container-migration.d.ts +2 -0
  74. package/dist/apps/agent-container-migration.d.ts.map +1 -1
  75. package/dist/apps/agent-container-migration.js +59 -2
  76. package/dist/apps/agent-container-migration.js.map +1 -1
  77. package/dist/apps/agent-manager.d.ts +4 -2
  78. package/dist/apps/agent-manager.d.ts.map +1 -1
  79. package/dist/apps/agent-manager.js +39 -3
  80. package/dist/apps/agent-manager.js.map +1 -1
  81. package/dist/apps/installer.d.ts +4 -3
  82. package/dist/apps/installer.d.ts.map +1 -1
  83. package/dist/apps/installer.js +8 -4
  84. package/dist/apps/installer.js.map +1 -1
  85. package/dist/build-provenance.json +8 -0
  86. package/dist/cli/args.js +1 -1
  87. package/dist/cli/args.js.map +1 -1
  88. package/dist/cli/commands/app.d.ts.map +1 -1
  89. package/dist/cli/commands/app.js +39 -3
  90. package/dist/cli/commands/app.js.map +1 -1
  91. package/dist/cli/commands/doctor.d.ts.map +1 -1
  92. package/dist/cli/commands/doctor.js +6 -2
  93. package/dist/cli/commands/doctor.js.map +1 -1
  94. package/dist/cli/commands/safemode.d.ts +2 -0
  95. package/dist/cli/commands/safemode.d.ts.map +1 -0
  96. package/dist/cli/commands/safemode.js +279 -0
  97. package/dist/cli/commands/safemode.js.map +1 -0
  98. package/dist/cli/dependencies.d.ts +2 -0
  99. package/dist/cli/dependencies.d.ts.map +1 -1
  100. package/dist/cli/dependencies.js +81 -0
  101. package/dist/cli/dependencies.js.map +1 -1
  102. package/dist/cli/index.d.ts.map +1 -1
  103. package/dist/cli/index.js +11 -1
  104. package/dist/cli/index.js.map +1 -1
  105. package/dist/cli/manager.d.ts.map +1 -1
  106. package/dist/cli/manager.js +4 -1
  107. package/dist/cli/manager.js.map +1 -1
  108. package/dist/config/agent-lifecycle.d.ts +26 -0
  109. package/dist/config/agent-lifecycle.d.ts.map +1 -0
  110. package/dist/config/agent-lifecycle.js +63 -0
  111. package/dist/config/agent-lifecycle.js.map +1 -0
  112. package/dist/config/loader.d.ts.map +1 -1
  113. package/dist/config/loader.js +11 -0
  114. package/dist/config/loader.js.map +1 -1
  115. package/dist/config/reload-policy.d.ts +9 -0
  116. package/dist/config/reload-policy.d.ts.map +1 -0
  117. package/dist/config/reload-policy.js +62 -0
  118. package/dist/config/reload-policy.js.map +1 -0
  119. package/dist/config/watcher.d.ts +3 -2
  120. package/dist/config/watcher.d.ts.map +1 -1
  121. package/dist/config/watcher.js +34 -51
  122. package/dist/config/watcher.js.map +1 -1
  123. package/dist/cron/tool-schemas.d.ts +296 -0
  124. package/dist/cron/tool-schemas.d.ts.map +1 -0
  125. package/dist/cron/tool-schemas.js +122 -0
  126. package/dist/cron/tool-schemas.js.map +1 -0
  127. package/dist/index.js +141 -158
  128. package/dist/index.js.map +1 -1
  129. package/dist/orchestration/agent-tool-schemas.js +4 -4
  130. package/dist/orchestration/agent-tool-schemas.js.map +1 -1
  131. package/dist/orchestration/background-work.d.ts +9 -0
  132. package/dist/orchestration/background-work.d.ts.map +1 -0
  133. package/dist/orchestration/background-work.js +49 -0
  134. package/dist/orchestration/background-work.js.map +1 -0
  135. package/dist/orchestration/bridge.d.ts +6 -15
  136. package/dist/orchestration/bridge.d.ts.map +1 -1
  137. package/dist/orchestration/bridge.js +67 -32
  138. package/dist/orchestration/bridge.js.map +1 -1
  139. package/dist/orchestration/capabilities.d.ts.map +1 -1
  140. package/dist/orchestration/capabilities.js +18 -10
  141. package/dist/orchestration/capabilities.js.map +1 -1
  142. package/dist/orchestration/capacity.d.ts +3 -2
  143. package/dist/orchestration/capacity.d.ts.map +1 -1
  144. package/dist/orchestration/capacity.js +7 -0
  145. package/dist/orchestration/capacity.js.map +1 -1
  146. package/dist/orchestration/channel-controls.d.ts.map +1 -1
  147. package/dist/orchestration/channel-controls.js +3 -2
  148. package/dist/orchestration/channel-controls.js.map +1 -1
  149. package/dist/orchestration/cli-skills.d.ts +5 -0
  150. package/dist/orchestration/cli-skills.d.ts.map +1 -1
  151. package/dist/orchestration/container-tool-schemas.d.ts +37 -0
  152. package/dist/orchestration/container-tool-schemas.d.ts.map +1 -0
  153. package/dist/orchestration/container-tool-schemas.js +27 -0
  154. package/dist/orchestration/container-tool-schemas.js.map +1 -0
  155. package/dist/orchestration/container.d.ts.map +1 -1
  156. package/dist/orchestration/container.js +7 -0
  157. package/dist/orchestration/container.js.map +1 -1
  158. package/dist/orchestration/dashboard-reader-worker.js +7 -7
  159. package/dist/orchestration/dashboard-reader-worker.js.map +1 -1
  160. package/dist/orchestration/decision-context.js +1 -1
  161. package/dist/orchestration/decision-context.js.map +1 -1
  162. package/dist/orchestration/gateway-tasks/controller.d.ts +29 -0
  163. package/dist/orchestration/gateway-tasks/controller.d.ts.map +1 -0
  164. package/dist/orchestration/gateway-tasks/controller.js +139 -0
  165. package/dist/orchestration/gateway-tasks/controller.js.map +1 -0
  166. package/dist/orchestration/gateway-tasks/safemode.d.ts +19 -0
  167. package/dist/orchestration/gateway-tasks/safemode.d.ts.map +1 -0
  168. package/dist/orchestration/gateway-tasks/safemode.js +128 -0
  169. package/dist/orchestration/gateway-tasks/safemode.js.map +1 -0
  170. package/dist/orchestration/process-turn.d.ts +11 -2
  171. package/dist/orchestration/process-turn.d.ts.map +1 -1
  172. package/dist/orchestration/process-turn.js +47 -7
  173. package/dist/orchestration/process-turn.js.map +1 -1
  174. package/dist/orchestration/recovery.d.ts.map +1 -1
  175. package/dist/orchestration/recovery.js +4 -2
  176. package/dist/orchestration/recovery.js.map +1 -1
  177. package/dist/orchestration/runtime.d.ts +10 -0
  178. package/dist/orchestration/runtime.d.ts.map +1 -1
  179. package/dist/orchestration/runtime.js +31 -4
  180. package/dist/orchestration/runtime.js.map +1 -1
  181. package/dist/orchestration/skills.d.ts +2 -0
  182. package/dist/orchestration/skills.d.ts.map +1 -1
  183. package/dist/orchestration/skills.js +4 -3
  184. package/dist/orchestration/skills.js.map +1 -1
  185. package/dist/orchestration/task-controls.d.ts +12 -0
  186. package/dist/orchestration/task-controls.d.ts.map +1 -1
  187. package/dist/orchestration/task-controls.js +3 -1
  188. package/dist/orchestration/task-controls.js.map +1 -1
  189. package/dist/orchestration/tasks/driver.d.ts.map +1 -1
  190. package/dist/orchestration/tasks/driver.js +99 -12
  191. package/dist/orchestration/tasks/driver.js.map +1 -1
  192. package/dist/orchestration/tasks/pool.d.ts +2 -0
  193. package/dist/orchestration/tasks/pool.d.ts.map +1 -1
  194. package/dist/orchestration/tasks/pool.js +4 -0
  195. package/dist/orchestration/tasks/pool.js.map +1 -1
  196. package/dist/orchestration/tasks/scheduler.d.ts.map +1 -1
  197. package/dist/orchestration/tasks/scheduler.js +4 -0
  198. package/dist/orchestration/tasks/scheduler.js.map +1 -1
  199. package/dist/orchestration/tasks/service.d.ts +4 -0
  200. package/dist/orchestration/tasks/service.d.ts.map +1 -1
  201. package/dist/orchestration/tasks/service.js +38 -8
  202. package/dist/orchestration/tasks/service.js.map +1 -1
  203. package/dist/orchestration/token-ledger.d.ts +1 -0
  204. package/dist/orchestration/token-ledger.d.ts.map +1 -1
  205. package/dist/orchestration/token-ledger.js +6 -2
  206. package/dist/orchestration/token-ledger.js.map +1 -1
  207. package/dist/orchestration/token-usage.d.ts +3 -0
  208. package/dist/orchestration/token-usage.d.ts.map +1 -1
  209. package/dist/orchestration/token-usage.js +12 -3
  210. package/dist/orchestration/token-usage.js.map +1 -1
  211. package/dist/orchestration/types.d.ts +19 -1
  212. package/dist/orchestration/types.d.ts.map +1 -1
  213. package/dist/orchestration/types.js.map +1 -1
  214. package/dist/orchestration/worker-crons.d.ts +5 -0
  215. package/dist/orchestration/worker-crons.d.ts.map +1 -0
  216. package/dist/orchestration/worker-crons.js +84 -0
  217. package/dist/orchestration/worker-crons.js.map +1 -0
  218. package/dist/orchestration/worker-harness.d.ts +15 -0
  219. package/dist/orchestration/worker-harness.d.ts.map +1 -0
  220. package/dist/orchestration/worker-harness.js +72 -0
  221. package/dist/orchestration/worker-harness.js.map +1 -0
  222. package/dist/safemode/config.d.ts +7 -0
  223. package/dist/safemode/config.d.ts.map +1 -0
  224. package/dist/safemode/config.js +67 -0
  225. package/dist/safemode/config.js.map +1 -0
  226. package/dist/safemode/context.d.ts +7 -0
  227. package/dist/safemode/context.d.ts.map +1 -0
  228. package/dist/safemode/context.js +397 -0
  229. package/dist/safemode/context.js.map +1 -0
  230. package/dist/safemode/external-owners.d.ts +19 -0
  231. package/dist/safemode/external-owners.d.ts.map +1 -0
  232. package/dist/safemode/external-owners.js +229 -0
  233. package/dist/safemode/external-owners.js.map +1 -0
  234. package/dist/safemode/native.d.ts +38 -0
  235. package/dist/safemode/native.d.ts.map +1 -0
  236. package/dist/safemode/native.js +220 -0
  237. package/dist/safemode/native.js.map +1 -0
  238. package/dist/safemode/params.d.ts +10 -0
  239. package/dist/safemode/params.d.ts.map +1 -0
  240. package/dist/safemode/params.js +86 -0
  241. package/dist/safemode/params.js.map +1 -0
  242. package/dist/safemode/provenance.d.ts +45 -0
  243. package/dist/safemode/provenance.d.ts.map +1 -0
  244. package/dist/safemode/provenance.js +153 -0
  245. package/dist/safemode/provenance.js.map +1 -0
  246. package/dist/safemode/runner.d.ts +17 -0
  247. package/dist/safemode/runner.d.ts.map +1 -0
  248. package/dist/safemode/runner.js +398 -0
  249. package/dist/safemode/runner.js.map +1 -0
  250. package/dist/safemode/store.d.ts +56 -0
  251. package/dist/safemode/store.d.ts.map +1 -0
  252. package/dist/safemode/store.js +451 -0
  253. package/dist/safemode/store.js.map +1 -0
  254. package/dist/session/codex-auth.d.ts +37 -0
  255. package/dist/session/codex-auth.d.ts.map +1 -0
  256. package/dist/session/codex-auth.js +266 -0
  257. package/dist/session/codex-auth.js.map +1 -0
  258. package/dist/session/codex-container-runtime.d.ts +15 -0
  259. package/dist/session/codex-container-runtime.d.ts.map +1 -0
  260. package/dist/session/codex-container-runtime.js +85 -0
  261. package/dist/session/codex-container-runtime.js.map +1 -0
  262. package/dist/session/codex-context-defaults.json +5 -0
  263. package/dist/session/codex-context.d.ts +13 -0
  264. package/dist/session/codex-context.d.ts.map +1 -0
  265. package/dist/session/codex-context.js +27 -0
  266. package/dist/session/codex-context.js.map +1 -0
  267. package/dist/session/codex-extension-discovery.d.ts +21 -0
  268. package/dist/session/codex-extension-discovery.d.ts.map +1 -0
  269. package/dist/session/codex-extension-discovery.js +132 -0
  270. package/dist/session/codex-extension-discovery.js.map +1 -0
  271. package/dist/session/codex-native-mcp-client.d.ts +37 -0
  272. package/dist/session/codex-native-mcp-client.d.ts.map +1 -0
  273. package/dist/session/codex-native-mcp-client.js +164 -0
  274. package/dist/session/codex-native-mcp-client.js.map +1 -0
  275. package/dist/session/codex-policy.d.ts +6 -0
  276. package/dist/session/codex-policy.d.ts.map +1 -0
  277. package/dist/session/codex-policy.js +17 -0
  278. package/dist/session/codex-policy.js.map +1 -0
  279. package/dist/session/codex-process.d.ts +114 -0
  280. package/dist/session/codex-process.d.ts.map +1 -0
  281. package/dist/session/codex-process.js +759 -0
  282. package/dist/session/codex-process.js.map +1 -0
  283. package/dist/session/codex-runtime.d.ts +20 -0
  284. package/dist/session/codex-runtime.d.ts.map +1 -0
  285. package/dist/session/codex-runtime.js +182 -0
  286. package/dist/session/codex-runtime.js.map +1 -0
  287. package/dist/session/codex-tool-capture.d.ts +35 -0
  288. package/dist/session/codex-tool-capture.d.ts.map +1 -0
  289. package/dist/session/codex-tool-capture.js +178 -0
  290. package/dist/session/codex-tool-capture.js.map +1 -0
  291. package/dist/session/container-connectors.d.ts +10 -0
  292. package/dist/session/container-connectors.d.ts.map +1 -0
  293. package/dist/session/container-connectors.js +41 -0
  294. package/dist/session/container-connectors.js.map +1 -0
  295. package/dist/session/container-extensions.d.ts +4 -0
  296. package/dist/session/container-extensions.d.ts.map +1 -0
  297. package/dist/session/container-extensions.js +45 -0
  298. package/dist/session/container-extensions.js.map +1 -0
  299. package/dist/session/managed-connectors.d.ts +16 -0
  300. package/dist/session/managed-connectors.d.ts.map +1 -0
  301. package/dist/session/managed-connectors.js +25 -0
  302. package/dist/session/managed-connectors.js.map +1 -0
  303. package/dist/session/process-diagnostics.d.ts +14 -0
  304. package/dist/session/process-diagnostics.d.ts.map +1 -0
  305. package/dist/session/process-diagnostics.js +39 -0
  306. package/dist/session/process-diagnostics.js.map +1 -0
  307. package/dist/session/process.d.ts +4 -0
  308. package/dist/session/process.d.ts.map +1 -1
  309. package/dist/session/process.js +21 -16
  310. package/dist/session/process.js.map +1 -1
  311. package/dist/session/request-tool-capture.d.ts +1 -1
  312. package/dist/session/request-tool-capture.d.ts.map +1 -1
  313. package/dist/session/runtime-profile.d.ts +8 -0
  314. package/dist/session/runtime-profile.d.ts.map +1 -1
  315. package/dist/session/runtime-profile.js +3 -2
  316. package/dist/session/runtime-profile.js.map +1 -1
  317. package/dist/session/worker-environment.d.ts +5 -0
  318. package/dist/session/worker-environment.d.ts.map +1 -0
  319. package/dist/session/worker-environment.js +27 -0
  320. package/dist/session/worker-environment.js.map +1 -0
  321. package/dist/session/worker-extensions.d.ts +20 -0
  322. package/dist/session/worker-extensions.d.ts.map +1 -0
  323. package/dist/session/worker-extensions.js +322 -0
  324. package/dist/session/worker-extensions.js.map +1 -0
  325. package/dist/skills/loader.d.ts +2 -0
  326. package/dist/skills/loader.d.ts.map +1 -1
  327. package/dist/skills/loader.js.map +1 -1
  328. package/dist/types.d.ts +30 -0
  329. package/dist/types.d.ts.map +1 -1
  330. package/dist/ui/codex-context.d.ts +4 -0
  331. package/dist/ui/codex-context.d.ts.map +1 -0
  332. package/dist/ui/codex-context.js +15 -0
  333. package/dist/ui/codex-context.js.map +1 -0
  334. package/dist/ui/dashboard-client.d.ts.map +1 -1
  335. package/dist/ui/dashboard-client.js +14 -11
  336. package/dist/ui/dashboard-client.js.map +1 -1
  337. package/dist/ui/dashboard-presentation.d.ts +1 -1
  338. package/dist/ui/dashboard-presentation.d.ts.map +1 -1
  339. package/dist/ui/dashboard-presentation.js +2 -2
  340. package/dist/ui/dashboard-presentation.js.map +1 -1
  341. package/dist/ui/token-report.d.ts +3 -0
  342. package/dist/ui/token-report.d.ts.map +1 -1
  343. package/dist/ui/token-report.js +22 -10
  344. package/dist/ui/token-report.js.map +1 -1
  345. package/dist/ui/web-ui.d.ts.map +1 -1
  346. package/dist/ui/web-ui.js +53 -132
  347. package/dist/ui/web-ui.js.map +1 -1
  348. package/mcp/capability-catalog.ts +2 -0
  349. package/mcp/extension-catalog.ts +18 -0
  350. package/mcp/lazy-connector.ts +4 -2
  351. package/mcp/lazy-tools.test.ts +29 -0
  352. package/mcp/modules.ts +1 -0
  353. package/mcp/server.ts +2 -2
  354. package/mcp/tools/cron/module.ts +8 -114
  355. package/mcp/tools/discord/commands.ts +4 -1
  356. package/mcp/tools/safemode/module.test.ts +29 -0
  357. package/mcp/tools/telegram/receiver-server.ts +2 -22
  358. package/mcp/tools/telegram/task-detail.ts +2 -1
  359. package/package.json +3 -2
  360. package/scripts/orchestration/audit-codex-context.cjs +83 -0
  361. package/scripts/orchestration/benchmark-worker-harnesses.cjs +112 -0
  362. package/scripts/orchestration/smoke-codex-extensions.ts +29 -0
  363. package/scripts/orchestration/smoke-codex-worker.cjs +166 -0
  364. package/scripts/orchestration/smoke-safemode-codex.cjs +66 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmaxma/claude-gateway",
3
- "version": "2.0.6",
3
+ "version": "2.0.7",
4
4
  "description": "Multi-agent gateway for Claude",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,7 +28,7 @@
28
28
  "access": "public"
29
29
  },
30
30
  "scripts": {
31
- "build": "tsc",
31
+ "build": "tsc && node scripts/write-build-provenance.cjs",
32
32
  "start": "node --no-warnings=ExperimentalWarning --env-file-if-exists=.env dist/entry.js gateway start",
33
33
  "postinstall": "if command -v bun >/dev/null 2>&1; then bun install --cwd mcp; else echo 'Warning: bun not found. Run `cd mcp && bun install` manually to enable MCP tools.'; fi",
34
34
  "pretest": "npm run build",
@@ -61,6 +61,7 @@
61
61
  "pino": "^10.3.1",
62
62
  "qrcode": "^1.5.4",
63
63
  "sharp": "^0.35.4",
64
+ "smol-toml": "^1.8.0",
64
65
  "ws": "^8.21.0"
65
66
  },
66
67
  "devDependencies": {
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+ // Measure native CLI context metadata with a local Responses fixture, without model billing.
3
+ // This does NOT certify that an upstream provider accepts requests of that size.
4
+ require('ts-node/register/transpile-only');
5
+ const {CodexProcess}=require('../../src/session/codex-process');
6
+ const {resolveCodexRuntime}=require('../../src/session/codex-runtime');
7
+ const {resolveWorkerHarness}=require('../../src/orchestration/worker-harness');
8
+ const fs=require('fs/promises'),path=require('path'),os=require('os'),http=require('http');
9
+ const {execFileSync}=require('child_process'),{once}=require('events');
10
+ const args=process.argv.slice(2);
11
+ function option(name){const i=args.indexOf(name);return i<0?undefined:args[i+1];}
12
+ (async()=>{
13
+ const runtime=resolveCodexRuntime();
14
+ const catalog=option('--catalog-json')?JSON.parse(await fs.readFile(option('--catalog-json'),'utf8')).models:JSON.parse(execFileSync(runtime.executable,['debug','models','--bundled'],{encoding:'utf8',timeout:15000,maxBuffer:8*1024*1024})).models;
15
+ const supplied=option('--models-json')?JSON.parse(await fs.readFile(option('--models-json'),'utf8')):[];
16
+ const selected=new Map([...catalog.filter(m=>m.slug.includes('gpt')).map(m=>[m.slug,{id:m.slug}]),...supplied.map(m=>[m.id,m])]);
17
+ const directory=await fs.mkdtemp(path.join(os.tmpdir(),'gateway-context-audit-'));
18
+ const previous=process.env.GATEWAY_CONTEXT_AUDIT_KEY;
19
+ process.env.GATEWAY_CONTEXT_AUDIT_KEY='local-fixture-only';
20
+ const sockets=new Set(),adapters=new Set(),rows=[];
21
+ const server=http.createServer(async(req,res)=>{
22
+ try{
23
+ let raw='';for await(const chunk of req){raw+=chunk;if(raw.length>4*1024*1024)throw Error('Fixture request too large');}
24
+ const body=JSON.parse(raw),id='resp_fixture',item={type:'message',id:'msg_fixture',role:'assistant',phase:'final_answer',status:'completed',content:[{type:'output_text',text:'Context audit fixture complete.',annotations:[]}]};
25
+ const response={id,object:'response',created_at:Math.floor(Date.now()/1000),status:'completed',model:body.model,output:[item],usage:{input_tokens:100,input_tokens_details:{cached_tokens:0},output_tokens:5,total_tokens:105}};
26
+ res.writeHead(200,{'content-type':'text/event-stream'});
27
+ const emit=(type,payload)=>res.write('event: '+type+'\ndata: '+JSON.stringify({type,...payload})+'\n\n');
28
+ emit('response.created',{response:{...response,status:'in_progress',output:[]}});
29
+ emit('response.output_item.added',{output_index:0,item:{...item,status:'in_progress',content:[]}});
30
+ emit('response.content_part.added',{item_id:item.id,output_index:0,content_index:0,part:{type:'output_text',text:'',annotations:[]}});
31
+ emit('response.output_text.delta',{item_id:item.id,output_index:0,content_index:0,delta:item.content[0].text});
32
+ emit('response.output_text.done',{item_id:item.id,output_index:0,content_index:0,text:item.content[0].text});
33
+ emit('response.content_part.done',{item_id:item.id,output_index:0,content_index:0,part:item.content[0]});
34
+ emit('response.output_item.done',{output_index:0,item});
35
+ emit('response.completed',{response});res.end();
36
+ }catch(error){res.destroy(error);}
37
+ });
38
+ server.on('connection',socket=>{sockets.add(socket);socket.on('close',()=>sockets.delete(socket));});
39
+ try{
40
+ server.listen(0,'127.0.0.1');await once(server,'listening');
41
+ await fs.writeFile(path.join(directory,'mcp.json'),'{"mcpServers":{}}');
42
+ let index=0;
43
+ async function runNext(){
44
+ while(index<selected.size){
45
+ const record=[...selected.values()][index++],model=record.workerModel||record.id.replace(/\[(?:1m|200k)\]$/i,'');
46
+ const row={selection:record.id,nativeModel:model,advertisedGatewayWindow:record.contextWindow??null,requested:1000000};
47
+ const known=catalog.find(m=>m.slug===model);
48
+ row.catalog=known?{default:known.context_window,max:known.max_context_window,usablePercent:known.effective_context_window_percent}:null;
49
+ row.autoHarness=resolveWorkerHarness({workers:{harness:'auto'}},{gateway:{models:record.workerHarness?[record]:[]}},record.id).harness;
50
+ const adapter=new CodexProcess({agent:{workspace:directory},gateway:{gateway:{}},profile:{role:'worker',hostExecution:true,connectorsAllowed:false,mcpConfigPath:path.join(directory,'mcp.json'),overlay:'Local metadata audit. Return one short message.'},sessionId:'audit-'+index,stateDirectory:directory,
51
+ config:{model,contextWindow:1000000,bin:runtime.executable,baseUrl:'http://127.0.0.1:'+server.address().port+'/v1',apiKeyEnv:'GATEWAY_CONTEXT_AUDIT_KEY'}});
52
+ adapters.add(adapter);
53
+ const originalEvent=adapter.event.bind(adapter);
54
+ adapter.event=function(event){if(event.method==='thread/tokenUsage/updated')row.observedUsable=event.params?.tokenUsage?.modelContextWindow??null;return originalEvent(event);};
55
+ const originalRequest=adapter.request.bind(adapter);
56
+ adapter.request=async function(method,params){const result=await originalRequest(method,params);if(method==='config/read')row.effectiveConfiguredWindow=result.config?.model_context_window;return result;};
57
+ let timer;
58
+ try{
59
+ const complete=new Promise((resolve,reject)=>{timer=setTimeout(()=>reject(Error('Native audit timed out')),30000);adapter.on('startup-error',reject);adapter.on('output',line=>{const e=JSON.parse(line);if(e.contextWindow)row.context=e.contextWindow;if(e.type==='result')e.is_error?reject(Error(e.result)):resolve();});});
60
+ await adapter.start();
61
+ if(option('--catalog-json')){
62
+ const filename=path.join(adapter.home,'config.toml');
63
+ const config=await fs.readFile(filename,'utf8');
64
+ await fs.writeFile(filename,'model_catalog_json = '+JSON.stringify(path.resolve(option('--catalog-json')))+'\n'+config,{mode:0o600});
65
+ }
66
+ adapter.sendMessage('Return a short audit completion.');await complete;
67
+ row.status=typeof row.observedUsable==='number'?'measured':'missing-native-measurement';
68
+ }catch(error){row.status='error';row.error=error.message;}
69
+ finally{clearTimeout(timer);await adapter.stop();adapters.delete(adapter);}
70
+ rows.push(row);console.log(JSON.stringify(row));
71
+ }
72
+ }
73
+ await Promise.all([runNext(),runNext()]);
74
+ const report={measuredAt:new Date().toISOString(),version:execFileSync(runtime.executable,['--version'],{encoding:'utf8'}).trim(),catalogSource:option('--catalog-json')?'explicit snapshot':'bundled',method:'Real CodexProcess/app-server; explicit 1M request; local fake Responses API; observe native modelContextWindow. No upstream capacity/inference certification.',rows:rows.sort((a,b)=>a.selection.localeCompare(b.selection))};
75
+ if(option('--output'))await fs.writeFile(option('--output'),JSON.stringify(report,null,2)+'\n');
76
+ if(rows.some(row=>row.status!=='measured'))process.exitCode=1;
77
+ }finally{
78
+ await Promise.allSettled([...adapters].map(a=>a.stop()));
79
+ for(const socket of sockets)socket.destroy();server.close();
80
+ if(previous===undefined)delete process.env.GATEWAY_CONTEXT_AUDIT_KEY;else process.env.GATEWAY_CONTEXT_AUDIT_KEY=previous;
81
+ await fs.rm(directory,{recursive:true,force:true});
82
+ }
83
+ })().catch(error=>{console.error(error.message);process.exitCode=1;});
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ // Opt-in, billable native CLI smoke benchmark; deliberately not a gateway benchmark.
4
+ const fs = require('node:fs');
5
+ const os = require('node:os');
6
+ const path = require('node:path');
7
+ const { spawn, spawnSync } = require('node:child_process');
8
+ const args = process.argv.slice(2);
9
+ const option = (name, fallback) => args.includes(name) ? args[args.indexOf(name) + 1] : fallback;
10
+ if (!args.includes('--run')) {
11
+ console.log('Opt-in real calls: node scripts/orchestration/benchmark-worker-harnesses.cjs --run [--claude-model sonnet] [--codex-model gpt-5.6-luna] [--claude-base-url URL --claude-api-key-env ENV_NAME] [--codex-base-url URL --codex-api-key-env ENV_NAME] [--host-execution] [--output /tmp/worker-benchmark.json]');
12
+ process.exit(0);
13
+ }
14
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'gateway-worker-benchmark-'));
15
+ fs.chmodSync(root, 0o700);
16
+ const prompt = 'Implement sumIntegers(values) in sum.cjs, exporting { sumIntegers }. Accept an array of safe integers and return its sum. Throw TypeError for non-arrays or invalid elements and RangeError if any intermediate sum is not a safe integer. Empty arrays return 0. Use built-in Node only. Run a small Node verification. Modify only sum.cjs in this directory; do not inspect parent directories or credentials. Finish with a brief summary.';
17
+ const harness = `const assert=require('node:assert/strict');const {sumIntegers:s}=require(process.argv[1]);assert.equal(s([]),0);assert.equal(s([1,2,-3,4]),4);assert.equal(s([-4,-2]),-6);for(const x of [null,{},'1',[1.2],[NaN],[Infinity],['1'],[Number.MAX_SAFE_INTEGER+1]])assert.throws(()=>s(x),TypeError);assert.throws(()=>s([Number.MAX_SAFE_INTEGER,1]),RangeError);assert.throws(()=>s([-Number.MAX_SAFE_INTEGER,-1]),RangeError);`;
18
+ function execute(bin, argv, cwd, env) {
19
+ return new Promise(resolve => {
20
+ const started = Date.now(); let output = '', timedOut = false, spawnError = false;
21
+ const p = spawn(bin, argv, { cwd, env, detached: process.platform !== 'win32', stdio: ['pipe','pipe','pipe'] });
22
+ const kill = () => { try { process.platform === 'win32' ? p.kill('SIGKILL') : process.kill(-p.pid, 'SIGKILL'); } catch {} };
23
+ const timer = setTimeout(() => { timedOut = true; kill(); }, 120000);
24
+ p.stdout.on('data', b => { if (output.length < 8 * 1024 * 1024) output += b; else kill(); });
25
+ // Never persist stderr: providers may echo account/endpoint/auth information.
26
+ p.stderr.resume();
27
+ p.on('error', () => { spawnError = true; });
28
+ p.on('close', code => { clearTimeout(timer); kill(); resolve({ code, timedOut, spawnError, wallMs: Date.now() - started, output }); });
29
+ p.stdin.on('error', () => {}); p.stdin.end(prompt);
30
+ });
31
+ }
32
+ function metrics(harness, text) {
33
+ const result = { inputTokens: null, cachedInputTokens: null, cacheCreationTokens: null, outputTokens: null, toolErrors: 0, providerErrors: 0, failureCategory: null, toolCalls: 0 };
34
+ for (const line of text.split('\n')) {
35
+ let event; try { event = JSON.parse(line); } catch { continue; }
36
+ const diagnostic = JSON.stringify(event);
37
+ if (event.is_error || event.type === 'error' || event.type === 'turn.failed') {
38
+ result.failureCategory = /auth|token|credential|401|403/i.test(diagnostic) ? 'authentication' : /model.*(not|invalid|unknown)|unsupported.*model/i.test(diagnostic) ? 'model_unavailable' : /permission|sandbox|denied/i.test(diagnostic) ? 'permission' : 'provider_or_cli';
39
+ }
40
+ if (harness === 'codex') {
41
+ if (event.type === 'item.completed' && ['command_execution','mcp_tool_call','file_change'].includes(event.item?.type)) result.toolCalls++;
42
+ if (event.type === 'turn.completed' && event.usage) {
43
+ result.inputTokens = event.usage.input_tokens ?? null;
44
+ result.cachedInputTokens = event.usage.cached_input_tokens ?? null;
45
+ result.cacheCreationTokens = event.usage.cache_write_input_tokens ?? null;
46
+ result.outputTokens = event.usage.output_tokens ?? null;
47
+ }
48
+ if (event.type === 'item.completed' && (event.item?.status === 'failed' || (typeof event.item?.exit_code === 'number' && event.item.exit_code !== 0))) result.toolErrors++;
49
+ if (event.type === 'turn.failed' || event.type === 'error') result.providerErrors++;
50
+ } else {
51
+ if (event.type === 'result') {
52
+ const u = event.usage ?? {};
53
+ result.inputTokens = u.input_tokens ?? null;
54
+ result.cachedInputTokens = u.cache_read_input_tokens ?? null;
55
+ result.cacheCreationTokens = u.cache_creation_input_tokens ?? null;
56
+ result.outputTokens = u.output_tokens ?? null;
57
+ if (event.is_error) result.providerErrors++;
58
+ }
59
+ for (const block of event.message?.content ?? []) if (block.type === 'tool_use') result.toolCalls++;
60
+ for (const block of event.message?.content ?? []) if (block.type === 'tool_result' && block.is_error) result.toolErrors++;
61
+ }
62
+ }
63
+ // Codex includes reads and writes in input_tokens; Claude reports disjoint categories.
64
+ result.freshInputTokens = result.inputTokens === null ? null : harness === 'codex'
65
+ ? Math.max(0, result.inputTokens - (result.cachedInputTokens ?? 0) - (result.cacheCreationTokens ?? 0)) : result.inputTokens;
66
+ result.totalInputTokens = result.inputTokens === null ? null : harness === 'codex'
67
+ ? result.inputTokens : result.inputTokens + (result.cachedInputTokens ?? 0) + (result.cacheCreationTokens ?? 0);
68
+ return result;
69
+ }
70
+ (async () => {
71
+ let settings = {}; try { settings = JSON.parse(fs.readFileSync(path.join(os.homedir(), '.claude', 'settings.json'), 'utf8')); } catch {}
72
+ const claudeEnv = { ...process.env };
73
+ for (const key of ['ANTHROPIC_API_KEY','ANTHROPIC_AUTH_TOKEN','CLAUDE_CODE_OAUTH_TOKEN','ANTHROPIC_BASE_URL']) if (!claudeEnv[key] && typeof settings.env?.[key] === 'string') claudeEnv[key] = settings.env[key];
74
+ const claudeKeyEnv = option('--claude-api-key-env');
75
+ if (claudeKeyEnv) {
76
+ if (!/^[A-Z_][A-Z0-9_]*$/.test(claudeKeyEnv) || !process.env[claudeKeyEnv]) throw new Error('Missing explicit Claude credential');
77
+ delete claudeEnv.ANTHROPIC_AUTH_TOKEN;
78
+ delete claudeEnv.CLAUDE_CODE_OAUTH_TOKEN;
79
+ claudeEnv.ANTHROPIC_API_KEY = process.env[claudeKeyEnv];
80
+ }
81
+ if (option('--claude-base-url')) claudeEnv.ANTHROPIC_BASE_URL = option('--claude-base-url');
82
+ const codexKeyEnv = option('--codex-api-key-env', 'OPENAI_API_KEY');
83
+ if (!/^[A-Z_][A-Z0-9_]*$/.test(codexKeyEnv)) throw new Error('Invalid credential environment name');
84
+ const codexEnv = { ...process.env }; // CLI reads existing auth in-place; never copy it.
85
+ const routes = [
86
+ { harness: 'claude', bin: process.env.CLAUDE_BIN || 'claude', model: option('--claude-model','sonnet'), env: claudeEnv,
87
+ auth: ['ANTHROPIC_API_KEY','ANTHROPIC_AUTH_TOKEN','CLAUDE_CODE_OAUTH_TOKEN'].some(k => claudeEnv[k]) || fs.existsSync(path.join(os.homedir(),'.claude','.credentials.json')) },
88
+ { harness: 'codex', bin: process.env.CODEX_BIN || 'codex', model: option('--codex-model','gpt-5.6-luna'), env: codexEnv,
89
+ auth: Boolean(codexEnv[codexKeyEnv]) || (!option('--codex-base-url') && fs.existsSync(path.join(codexEnv.CODEX_HOME || path.join(os.homedir(),'.codex'),'auth.json'))) },
90
+ ];
91
+ const report = { kind: 'native-cli-smoke', codexSandbox: args.includes('--host-execution') ? 'danger-full-access' : 'workspace-write', note: 'Different models/provider/account routes may apply. Not a controlled timing/cost A/B, gateway integration test, or quality-parity claim. CLI token accounting differs; null means unavailable.', runs: [] };
92
+ for (const route of routes) {
93
+ if (!route.auth) { report.runs.push({ harness: route.harness, model: route.model, status: 'skipped_missing_credentials' }); continue; }
94
+ const cwd = path.join(root,route.harness); fs.mkdirSync(cwd, {mode:0o700});
95
+ fs.writeFileSync(path.join(cwd,'sum.cjs'),'module.exports = { sumIntegers() { throw new Error("TODO"); } };\n');
96
+ const argv = route.harness === 'codex'
97
+ ? ['exec','--ignore-user-config','--ignore-rules','--skip-git-repo-check','--ephemeral','--json','--sandbox',args.includes('--host-execution') ? 'danger-full-access' : 'workspace-write','-c','project_doc_max_bytes=0','-c','approval_policy="never"','-c','model_reasoning_effort="medium"','--model',route.model,'-']
98
+ : ['--print',...(claudeKeyEnv ? ['--bare','--setting-sources',''] : []),'--no-session-persistence','--output-format','stream-json','--verbose','--model',route.model,'--permission-mode','bypassPermissions','--tools','Read,Edit,Write,Bash','--strict-mcp-config','--mcp-config','{"mcpServers":{}}'];
99
+ if (route.harness === 'codex' && option('--codex-base-url')) {
100
+ const url = new URL(option('--codex-base-url'));
101
+ if (url.username || url.password || url.search || url.hash || !['https:', 'http:'].includes(url.protocol)) throw new Error('Invalid endpoint');
102
+ argv.splice(argv.length - 1, 0, '-c', 'model_provider="benchmark"', '-c', 'model_providers.benchmark.name="Benchmark"', '-c', 'model_providers.benchmark.wire_api="responses"', '-c', 'model_providers.benchmark.base_url=' + JSON.stringify(url.href), '-c', 'model_providers.benchmark.env_key=' + JSON.stringify(codexKeyEnv));
103
+ }
104
+ const run = await execute(route.bin,argv,cwd,route.env);
105
+ const checked = spawnSync(process.execPath,['-e',harness,path.join(cwd,'sum.cjs')],{timeout:5000,encoding:'utf8'});
106
+ report.runs.push({harness:route.harness,model:route.model,status:run.spawnError?'cli_unavailable':run.timedOut?'timeout':run.code===0?'finished':'cli_failed',exitCode:run.code,wallMs:run.wallMs,correctnessPassed:checked.status===0,validationFailure:checked.status===0?null:checked.error?'harness_error':(checked.stderr?.match(/(?:AssertionError|TypeError|RangeError|SyntaxError|ReferenceError|MODULE_NOT_FOUND)/)?.[0] ?? 'test_failed'),...metrics(route.harness,run.output)});
107
+ }
108
+ const json = JSON.stringify(report,null,2)+'\n';
109
+ if (option('--output')) fs.writeFileSync(option('--output'),json,{mode:0o600});
110
+ process.stdout.write(json);
111
+ process.exitCode = report.runs.every(r=>r.status==='finished'&&r.correctnessPassed&&r.providerErrors===0) ? 0 : 1;
112
+ })().catch(() => { console.error('Benchmark failed; raw provider output suppressed.'); process.exitCode=1; }).finally(()=>fs.rmSync(root,{recursive:true,force:true}));
@@ -0,0 +1,29 @@
1
+ // Opt-in local native CLI smoke; no model call, remote provider or real credential.
2
+ import { CodexNativeClient } from '../../src/session/codex-native-mcp-client';
3
+ import { resolveCodexRuntime } from '../../src/session/codex-runtime';
4
+ import { mkdtemp, mkdir, writeFile, rm } from 'fs/promises';
5
+ import { join } from 'path';
6
+ import { tmpdir } from 'os';
7
+ import assert from 'assert/strict';
8
+ const root = await mkdtemp(join(tmpdir(), 'gateway-extension-smoke-'));
9
+ const home = join(root, 'codex'); await mkdir(home);
10
+ const server = join(root, 'mcp.cjs');
11
+ await writeFile(server, String.raw`
12
+ require('readline').createInterface({input:process.stdin}).on('line',line=>{
13
+ const q=JSON.parse(line);if(q.id===undefined)return;let result={};
14
+ if(q.method==='initialize')result={protocolVersion:q.params.protocolVersion,capabilities:{tools:{}},serverInfo:{name:'local',version:'1'}};
15
+ if(q.method==='tools/list')result={tools:['echo','disabled'].map(name=>({name,description:'Fixture '+name,inputSchema:{type:'object',properties:{value:{type:'string'}},required:['value']}}))};
16
+ if(q.method==='tools/call')result={content:[{type:'text',text:'echo:'+q.params.arguments.value}]};
17
+ process.stdout.write(JSON.stringify({jsonrpc:'2.0',id:q.id,result})+'\n');
18
+ });`);
19
+ await writeFile(join(home, 'config.toml'), `model="gpt-5.6-luna"\nmodel_provider="fixture"\n[model_providers.fixture]\nname="Fixture"\nwire_api="responses"\nbase_url="http://127.0.0.1:1/v1"\nenv_key="FIXTURE_CODEX_KEY"\n[mcp_servers.fixture]\ncommand=${JSON.stringify(process.execPath)}\nargs=${JSON.stringify([server])}\ndisabled_tools=["disabled"]\n`);
20
+ const client = new CodexNativeClient({ bin: resolveCodexRuntime().executable, cwd: root, home, servers: ['fixture'], env: { FIXTURE_CODEX_KEY: 'local-fixture-not-a-real-key' } });
21
+ try {
22
+ const listing = await client.listTools();
23
+ assert(listing.tools.some(tool=>tool.name==='fixture__echo'));
24
+ assert(!listing.tools.some(tool=>tool.name==='fixture__disabled'),'native disabled tools leaked');
25
+ const result = await client.callTool({ name:'fixture__echo',arguments:{value:'ok'} });
26
+ assert.equal(result.content[0].text,'echo:ok');
27
+ await assert.rejects(client.callTool({name:'fixture__disabled',arguments:{value:'denied'}}));
28
+ console.log(JSON.stringify({pass:true,toolCount:listing.tools.length,roundtrip:true,disabledToolDenied:true,modelCalls:0}));
29
+ } finally {await client.close();await rm(root,{recursive:true,force:true});}
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env node
2
+ // Opt-in native-binary smoke: local fake Responses API, no model billing or real credentials.
3
+ require('ts-node/register/transpile-only');
4
+ const {resolveCodexRuntime}=require('../../src/session/codex-runtime');
5
+ const {CODEX_RUNTIME_LABEL}=require('../../src/session/codex-container-runtime');
6
+ const {CodexProcess} = require('../../src/session/codex-process');
7
+ const {mkdir,mkdtemp,writeFile,rm} = require('fs/promises');
8
+ const {join} = require('path');
9
+ const {homedir} = require('os');
10
+ const http = require('http');
11
+ const assert = require('assert/strict');
12
+ const {once} = require('events');
13
+ const {randomUUID} = require('crypto');
14
+ const {execFileSync} = require('child_process');
15
+ const shellEnvironmentMode = process.argv.includes('--shell-environment');
16
+ const contextWindowMode = process.argv.includes('--context-window');
17
+ const containerMode = process.argv.includes('--container');
18
+ const nativeAuthMode = process.argv.includes('--native-auth');
19
+ const originalCodexHome = process.env.CODEX_HOME;
20
+ const connectorMode = process.argv.includes('--connector');
21
+ const nativeMcpMode = process.argv.includes('--native-mcp');
22
+ const connectorNamespace=nativeMcpMode?'codex_native':containerMode?'claude__fixture':'fixture';
23
+ const MCP = String.raw`
24
+ const fs=require('fs'),readline=require('readline');
25
+ readline.createInterface({input:process.stdin}).on('line',line=>{const q=JSON.parse(line);if(q.id===undefined)return;let result={};
26
+ if(q.method==='initialize')result={protocolVersion:'2024-11-05',capabilities:{tools:{}},serverInfo:{name:'fixture',version:'1'}};
27
+ if(q.method==='tools/list')result={tools:[{name:'fixture_echo',description:'Return the supplied value.',inputSchema:{type:'object',properties:{value:{type:'string'}},required:['value']}}]};
28
+ if(q.method==='tools/call'){if(fs.readFileSync(process.env.FIXTURE_TICKET,'utf8')!=='fixture-ticket')throw Error('ticket missing');fs.appendFileSync(process.env.FIXTURE_CALLS,JSON.stringify(q.params)+'\n');result={content:[{type:'text',text:'MCP roundtrip: '+q.params.arguments.value}]};}
29
+ process.stdout.write(JSON.stringify({jsonrpc:'2.0',id:q.id,result})+'\n');});
30
+ `;
31
+ (async()=>{
32
+ const cache=join(homedir(),'.cache');await mkdir(cache,{recursive:true});const directory=await mkdtemp(join(cache,'gateway-codex-smoke-'));
33
+ let bridge; let createContainer;
34
+ const containerName = containerMode ? 'gateway-codex-smoke-' + randomUUID().slice(0,8) : undefined;
35
+ await mkdir(join(directory,'.codex'));
36
+ await writeFile(join(directory,'.codex','config.toml'),'[mcp_servers.unapproved]\ncommand = "node"\nargs = ["-e", "process.exit(91)"]\n');
37
+ const requests=[];const adapters=[];let sequence=0;let amended=false;let acknowledged=false;let hanging=false;const sockets=new Set();
38
+ const server=http.createServer(async(req,res)=>{
39
+ let raw='';for await(const chunk of req)raw+=chunk;
40
+ if(!req.url.endsWith('/responses')){res.writeHead(404);return res.end();}
41
+ const body=JSON.parse(raw);requests.push(body);
42
+ if(hanging)return;
43
+ if(sequence>0)await new Promise(resolve=>setTimeout(resolve,75));
44
+ const id='resp_'+(++sequence), messageId='msg_'+sequence;
45
+ const namespace=shellEnvironmentMode?undefined:body.tools?.find(t=>t.type==='namespace'&&t.name===(connectorMode?'mcp__'+connectorNamespace:'mcp__gateway'));
46
+ const toolName=shellEnvironmentMode?'exec_command':connectorMode?'tool_call':'fixture_echo';
47
+ const tool=shellEnvironmentMode?body.tools?.find(t=>t.name==='exec_command'):namespace?.tools.find(t=>t.name===toolName) ?? body.tools?.find(t=>t.name===(connectorMode?'mcp__'+connectorNamespace+'__tool_call':'mcp__gateway__fixture_echo'));
48
+ const toolArgs=shellEnvironmentMode?{cmd:"worker_fixture_account; printf '%s\\n' \"$WORKER_ENV_TEST\"; pwd",workdir:containerMode?'/workspace/project':join(directory,'project'),max_output_tokens:1000}:connectorMode?{name:nativeMcpMode?'fixture__fixture_echo':'fixture_echo',arguments:{value:'hello'}}:{value:'hello'};
49
+ const output=sequence===1&&tool?[{type:'function_call',id:'fc_1',call_id:'call_fixture_1',name:tool.name,...(namespace?{namespace:namespace.name}:{}),arguments:JSON.stringify(toolArgs)}]:[{type:'message',id:messageId,role:'assistant',phase:'final_answer',status:'completed',content:[{type:'output_text',text:'Canonical fixture result '+sequence,annotations:[]}]}];
50
+ const response={id,object:'response',created_at:Math.floor(Date.now()/1000),status:'completed',model:'gpt-test',output,usage:{input_tokens:100,input_tokens_details:{cached_tokens:40,cache_write_tokens:20},output_tokens:20,output_tokens_details:{reasoning_tokens:5},total_tokens:120}};
51
+ res.writeHead(200,{'content-type':'text/event-stream','cache-control':'no-cache'});
52
+ const emit=(type,payload)=>res.write('event: '+type+'\ndata: '+JSON.stringify({type,...payload})+'\n\n');
53
+ emit('response.created',{response:{...response,status:'in_progress',output:[]}});
54
+ for(const [index,item] of output.entries()){
55
+ emit('response.output_item.added',{output_index:index,item:item.type==='message'?{...item,status:'in_progress',content:[]}:item});
56
+ if(item.type==='message'){
57
+ emit('response.content_part.added',{item_id:item.id,output_index:index,content_index:0,part:{type:'output_text',text:'',annotations:[]}});
58
+ emit('response.output_text.delta',{item_id:item.id,output_index:index,content_index:0,delta:item.content[0].text});
59
+ emit('response.output_text.done',{item_id:item.id,output_index:index,content_index:0,text:item.content[0].text});
60
+ emit('response.content_part.done',{item_id:item.id,output_index:index,content_index:0,part:item.content[0]});
61
+ }
62
+ emit('response.output_item.done',{output_index:index,item});
63
+ }
64
+ emit('response.completed',{response});res.end();
65
+ });
66
+ server.on('connection',socket=>{sockets.add(socket);socket.on('close',()=>sockets.delete(socket));});
67
+ try {
68
+ server.listen(0,containerMode?'0.0.0.0':'127.0.0.1');await once(server,'listening');
69
+ process.env.GATEWAY_CODEX_SMOKE_KEY='fake-local-key';
70
+ const mcp=join(directory,'mcp.json'), calls=join(directory,'calls.jsonl');
71
+ await writeFile(join(directory,'mcp.cjs'),MCP);await writeFile(join(directory,'ticket'),'fixture-ticket');
72
+ await writeFile(mcp,JSON.stringify({mcpServers:{gateway:{command:process.execPath,args:[join(directory,'mcp.cjs')],env:{FIXTURE_TICKET:join(directory,'ticket'),FIXTURE_CALLS:calls}}}}));
73
+ if(containerMode){
74
+ const socket=join(directory,'fixture.sock');
75
+ bridge=http.createServer(async(req,res)=>{let body='';for await(const chunk of req)body+=chunk;await require('fs/promises').appendFile(calls,body+'\n');res.end(JSON.stringify({text:'MCP roundtrip: hello'}));});
76
+ bridge.listen(socket);await once(bridge,'listening');
77
+ const ticket=join(directory,'container-ticket.json');await writeFile(ticket,JSON.stringify({socket,token:'fixture-ticket',tools:[{name:'fixture_echo',description:'Return supplied value.',inputSchema:{type:'object',properties:{value:{type:'string'}},required:['value']}}]}));
78
+ await writeFile(mcp,JSON.stringify({mcpServers:{gateway:{command:'unused',args:[],env:{GATEWAY_ORCHESTRATION_TICKET_FILE:ticket}}}}));
79
+ const installerHome=homedir();
80
+ const runtime=resolveCodexRuntime();
81
+ assert(!runtime.containerError,runtime.containerError);
82
+ createContainer=()=>execFileSync('docker',['run','-d','--name',containerName,'--cap-drop','ALL','--security-opt','no-new-privileges','--add-host','host.docker.internal:host-gateway',...(nativeMcpMode?['-e','CODEX_HOME=/workspace/container-codex','-e','FIXTURE_CODEX_KEY=local-fixture-key']:[]),'--label',`${CODEX_RUNTIME_LABEL}=${runtime.fingerprint}`,...runtime.mounts.flatMap(m=>['--mount',`type=bind,src=${m.source},dst=${m.target},readonly`]),'--mount',`type=bind,src=${directory},dst=/workspace`,'--mount',`type=bind,src=${process.execPath},dst=/usr/bin/node,readonly`,process.env.GATEWAY_CODEX_SMOKE_IMAGE || 'debian:stable-slim','node','-e',`const fs=require('fs');fs.mkdirSync(${JSON.stringify(installerHome)},{recursive:true,mode:511});fs.chmodSync(${JSON.stringify(installerHome)},511);setInterval(()=>{},10000);`],{stdio:'pipe'});
83
+ }
84
+ if(createContainer)createContainer();
85
+ const options={agent:{workspace:directory,...(containerMode?{type:'app-agent',container:containerName}:{})},gateway:{gateway:{}},profile:{role:'worker',mcpConfigPath:mcp,overlay:'Use fixture_echo once, then return a brief result.',hostExecution:!containerMode,containerExecution:containerMode},sessionId:'fixture-logical-session',stateDirectory:join(directory,'state'),config:{model:'gpt-test',baseUrl:`http://${containerMode?'host.docker.internal':'127.0.0.1'}:${server.address().port}/v1`,apiKeyEnv:'GATEWAY_CODEX_SMOKE_KEY'}};
86
+ if(contextWindowMode) Object.assign(options.config,{model:"gpt-5.4-mini",contextWindow:1000000});
87
+ if(nativeAuthMode){
88
+ const home=join(directory,'native-auth');await mkdir(home,{mode:0o700});
89
+ await writeFile(join(home,'config.toml'), 'model_provider="fixture"\ncli_auth_credentials_store="file"\n[model_providers.fixture]\nname="Fixture"\nwire_api="responses"\nrequires_openai_auth=true\nbase_url='+JSON.stringify(options.config.baseUrl)+'\n',{mode:0o600});
90
+ execFileSync(resolveCodexRuntime().executable,['login','--with-api-key'],{env:{...process.env,CODEX_HOME:home},input:process.env.GATEWAY_CODEX_SMOKE_KEY,stdio:['pipe','pipe','pipe'],timeout:10000});
91
+ process.env.CODEX_HOME=home;delete options.config.baseUrl;delete options.config.apiKeyEnv;
92
+ }
93
+ if(connectorMode && !nativeMcpMode && !containerMode) options.gateway.gateway.customConnectors={fixture:{label:'Fixture connector',secretNames:[],credentialOwner:'none',config:{command:process.execPath,args:[join(directory,'mcp.cjs')],env:{FIXTURE_TICKET:join(directory,'ticket'),FIXTURE_CALLS:calls}}}};
94
+ if(connectorMode && !nativeMcpMode && containerMode){
95
+ await mkdir(join(directory,'.claude'),{recursive:true});
96
+ await writeFile(join(directory,'.claude','settings.json'),JSON.stringify({enableAllProjectMcpServers:true}));
97
+ await writeFile(join(directory,'.mcp.json'),JSON.stringify({mcpServers:{fixture:{command:'node',args:['/workspace/mcp.cjs'],env:{FIXTURE_TICKET:'/workspace/ticket',FIXTURE_CALLS:'/workspace/calls.jsonl'}}}}));
98
+ }
99
+ if(nativeMcpMode){
100
+ assert(nativeAuthMode && connectorMode,'--native-mcp requires --native-auth --connector');
101
+ const home=containerMode?join(directory,'container-codex'):process.env.CODEX_HOME;
102
+ await mkdir(home,{recursive:true});
103
+ const prefix=containerMode?'/workspace':directory;
104
+ const spec='\n[mcp_servers.fixture]\ncommand='+JSON.stringify(containerMode?'node':process.execPath)+'\nargs='+JSON.stringify([prefix+'/mcp.cjs'])+'\n[mcp_servers.fixture.env]\nFIXTURE_TICKET='+JSON.stringify(prefix+'/ticket')+'\nFIXTURE_CALLS='+JSON.stringify(prefix+'/calls.jsonl')+'\n';
105
+ if(containerMode)await writeFile(join(home,'config.toml'),'model="gpt-test"\nmodel_provider="fixture"\n[model_providers.fixture]\nname="Fixture"\nwire_api="responses"\nbase_url="http://127.0.0.1:1/v1"\nenv_key="FIXTURE_CODEX_KEY"\n'+spec);
106
+ else await require('fs/promises').appendFile(join(home,'config.toml'),spec);
107
+ }
108
+ if(shellEnvironmentMode){
109
+ await mkdir(join(directory,'project'));
110
+ await mkdir(join(directory,'zsh'));
111
+ await writeFile(join(directory,'hook.sh'),"worker_fixture_account() { case \"$PWD\" in */project) printf 'project-account\\n';; *) printf 'wrong-account\\n';; esac; }\n");
112
+ const location=containerMode?'/workspace':directory;
113
+ await writeFile(join(directory,'zsh','.zshenv'),'. '+JSON.stringify(location+'/hook.sh')+'\n');
114
+ options.gateway.gateway.workers={
115
+ environment:containerMode?{WORKER_ENV_TEST:'must-not-leak'}:{WORKER_ENV_TEST:'host-setting',BASH_ENV:location+'/hook.sh',ZDOTDIR:location+'/zsh'},
116
+ ...(containerMode?{containerEnvironment:{WORKER_ENV_TEST:'container-setting',BASH_ENV:location+'/hook.sh',ZDOTDIR:location+'/zsh'}}:{})
117
+ };
118
+ }
119
+ if(!shellEnvironmentMode) options.checkpoint=async()=>{if(amended)return;amended=true;return {text:'Apply the native checkpoint revision before finishing.',kind:'assignment',acknowledge:()=>{acknowledged=true;}};};
120
+ async function run(){
121
+ const adapter=new CodexProcess(options);adapters.push(adapter);const events=[];
122
+ const result=new Promise((resolve,reject)=>{const timeout=setTimeout(()=>reject(new Error('Smoke turn timed out')),45000);adapter.on('startup-error',e=>{clearTimeout(timeout);reject(e);});adapter.on('output',line=>{const e=JSON.parse(line);events.push(e);if(e.type==='result'){clearTimeout(timeout);e.is_error?reject(new Error(e.result)):resolve(e);}});});
123
+ try { await adapter.start();adapter.sendMessage('Complete the fixture request.'); } catch(error) { adapter.emit('startup-error',error); }
124
+ const terminal=await result;const schemas=await adapter.flushToolSchemas();await adapter.stop();return {terminal,events,schemas};
125
+ }
126
+ const first=await run();assert.match(first.terminal.result,/Canonical fixture result/);
127
+ if(contextWindowMode){
128
+ const context=first.events.filter(e=>e.subtype==='native_usage').at(-1)?.contextWindow;
129
+ assert.equal(context?.requested,1000000);assert.equal(context?.configured,400000);
130
+ assert(context.observed>0 && context.observed<=400000,'native usable context exceeds configured Mini ceiling');
131
+ console.log('PASS native context '+JSON.stringify(context));
132
+ return; // Dedicated context probe; the default smoke exercises MCP/resume separately.
133
+ }
134
+ assert(first.schemas.length>0,'actual request tool schemas were not captured');
135
+ assert(first.schemas.some(s=>s.loaded.some(name=>name.includes('fixture_echo')||name.includes('tool_call'))),'MCP schema absent from actual request capture: '+JSON.stringify(first.schemas.map(s=>s.loaded)));
136
+ assert(first.events.some(e=>e.type==='assistant'&&e.message.usage?.input_tokens===40&&e.message.usage.cache_read_input_tokens===40&&e.message.usage.cache_creation_input_tokens===20),'per-request trace usage was not normalized');
137
+ if(shellEnvironmentMode){
138
+ const outputs=requests.flatMap(r=>r.input).filter(i=>i.type==='function_call_output').map(i=>typeof i.output==='string'?i.output:JSON.stringify(i.output)).join('\n');
139
+ assert(outputs.includes('project-account'),outputs);
140
+ assert(outputs.includes(containerMode?'container-setting':'host-setting'),outputs);
141
+ assert(!outputs.includes('must-not-leak'),outputs);
142
+ console.log('PASS native Codex '+(containerMode?'container':'host')+': actual default-shell command, explicit startup hook, project cwd and isolated environment');
143
+ return;
144
+ }
145
+ const {readFile}=require('fs/promises');assert.match(await readFile(calls,'utf8'),/hello/);
146
+ assert(first.events.some(e=>e.type==='assistant'&&e.message.content.some(b=>b.name===(connectorMode?'mcp__'+connectorNamespace+'__tool_call':'mcp__gateway__fixture_echo'))),'native MCP tool was not observed');
147
+ assert(acknowledged,'native steering was not acknowledged');assert(requests.some(r=>JSON.stringify(r.input).includes('native checkpoint revision')),'native revision never reached Responses input');
148
+ assert.equal(first.terminal.usage.input_tokens,sequence*40);assert.equal(first.terminal.usage.cache_read_input_tokens,sequence*40);assert.equal(first.terminal.usage.cache_creation_input_tokens,sequence*20);assert.equal(first.terminal.usage.output_tokens,sequence*20);
149
+ const resumed=await run();assert.equal(resumed.terminal.usage.input_tokens,40);assert.equal(resumed.terminal.usage.cache_read_input_tokens,40);assert.equal(resumed.terminal.usage.cache_creation_input_tokens,20);assert.equal(resumed.terminal.usage.output_tokens,20);
150
+ assert(requests[requests.length-1].input.some(i=>i.type==='function_call_output'),'resumed transcript lost MCP history');
151
+ if(containerMode){
152
+ execFileSync('docker',['rm','-f',containerName],{stdio:'pipe'});createContainer();
153
+ const recreated=await run();
154
+ assert.match(recreated.terminal.result,/Canonical fixture result/);
155
+ assert(recreated.events.some(e=>e.subtype==='native_session_reset'),'recreated container must reset missing native session');
156
+ assert(!requests[requests.length-1].input.some(i=>i.type==='function_call_output'),'new container must not claim missing native history was resumed');
157
+ assert.equal(recreated.terminal.usage.input_tokens,40);
158
+ }
159
+ hanging=true;const cancelled=new CodexProcess({...options,sessionId:'cancel-fixture'});adapters.push(cancelled);await cancelled.start();cancelled.sendMessage('Wait for cancellation.');
160
+ const deadline=Date.now()+10000;const before=requests.length;while(requests.length===before&&Date.now()<deadline)await new Promise(r=>setTimeout(r,25));
161
+ await cancelled.stop();assert(cancelled.managedGroupStopped,'cancelled native process group survived');
162
+ console.log('PASS native Codex '+(containerMode?'container':connectorMode?'host custom connector':'host')+': local Responses, MCP ticket roundtrip, canonical summary, nonzero cache-write usage, explicit resume, recreation recovery, mid-turn revision, cancellation');
163
+ }finally{
164
+ await Promise.allSettled(adapters.map(a=>a.stop()));for(const socket of sockets)socket.destroy();server.close();bridge?.close();if(containerName){try{execFileSync('docker',['rm','-f',containerName],{stdio:'pipe'});}catch{}}delete process.env.GATEWAY_CODEX_SMOKE_KEY;if(originalCodexHome===undefined)delete process.env.CODEX_HOME;else process.env.CODEX_HOME=originalCodexHome;await rm(directory,{recursive:true,force:true});
165
+ }
166
+ })().catch(error=>{console.error(error.message);process.exitCode=1;});
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ // Opt-in native CLI regression: local mock Responses only; no provider credits.
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+ const http = require('http');
7
+ const assert = require('assert/strict');
8
+ const { spawn } = require('child_process');
9
+ const { buildNativeInvocation } = require('../../dist/safemode/native');
10
+
11
+ (async () => {
12
+ const cache = path.join(os.homedir(), '.cache');
13
+ fs.mkdirSync(cache, { recursive: true });
14
+ const root = fs.mkdtempSync(path.join(cache, 'safemode-policy-'));
15
+ const cwd = path.join(root, 'workspace');
16
+ fs.mkdirSync(cwd);
17
+ const marker = path.join(root, 'unexpected-notify');
18
+ const hook = path.join(root, 'notify.cjs');
19
+ fs.writeFileSync(hook, `require('fs').writeFileSync(${JSON.stringify(marker)}, 'ran');`);
20
+ let requests = 0;
21
+ const server = http.createServer(async (req, res) => {
22
+ for await (const chunk of req) { /* Drain local fixture input. */ }
23
+ if (!req.url.endsWith('/responses')) { res.writeHead(404); res.end(); return; }
24
+ requests++;
25
+ const item = { type: 'message', id: 'msg_fixture', role: 'assistant', phase: 'final_answer', status: 'completed', content: [{ type: 'output_text', text: 'Fixture complete', annotations: [] }] };
26
+ const response = { id: 'resp_fixture', object: 'response', created_at: Math.floor(Date.now() / 1000), status: 'completed', model: 'gpt-test', output: [item], usage: { input_tokens: 10, output_tokens: 3, total_tokens: 13 } };
27
+ res.writeHead(200, { 'content-type': 'text/event-stream' });
28
+ for (const [type, payload] of [
29
+ ['response.created', { response: { ...response, status: 'in_progress', output: [] } }],
30
+ ['response.output_item.added', { output_index: 0, item }],
31
+ ['response.output_item.done', { output_index: 0, item }],
32
+ ['response.completed', { response }],
33
+ ]) res.write('event: ' + type + '\ndata: ' + JSON.stringify({ type, ...payload }) + '\n\n');
34
+ res.end();
35
+ });
36
+ try {
37
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
38
+ fs.writeFileSync(path.join(root, 'config.toml'), `model="gpt-test"\nmodel_provider="fixture"\nnotify=[${JSON.stringify(process.execPath)},${JSON.stringify(hook)}]\n[features]\nhooks=true\nplugins=true\napps=true\n[model_providers.fixture]\nname="fixture"\nbase_url="http://127.0.0.1:${server.address().port}/v1"\nwire_api="responses"\nenv_key="FIXTURE_KEY"\n`);
39
+ const env = { ...process.env, CODEX_HOME: root, FIXTURE_KEY: 'fake-local-only' };
40
+ async function run(nativeSessionId) {
41
+ const inv = buildNativeInvocation({ cli: 'codex', mode: 'headless', cwd, env, nativeSessionId, resume: Boolean(nativeSessionId), prompt: 'Reply fixture complete without tools.' });
42
+ const output = await new Promise((resolve, reject) => {
43
+ const child = spawn(inv.command, inv.args, { cwd, env: { ...inv.env, FIXTURE_KEY: 'fake-local-only' }, stdio: ['ignore', 'pipe', 'pipe'] });
44
+ let text = '', error = '';
45
+ const timer = setTimeout(() => child.kill('SIGKILL'), 30000);
46
+ child.stdout.on('data', chunk => { text += chunk; });
47
+ child.stderr.on('data', chunk => { error += chunk; });
48
+ child.on('error', error => { clearTimeout(timer); reject(error); });
49
+ child.on('close', code => { clearTimeout(timer); code === 0 ? resolve(text) : reject(new Error(`Native exit ${code}: ${error}`)); });
50
+ });
51
+ const events = output.trim().split('\n').map(line => JSON.parse(line));
52
+ assert(events.some(event => event.type === 'turn.completed'));
53
+ const id = events.find(event => event.type === 'thread.started')?.thread_id;
54
+ assert(id); if (nativeSessionId) assert.equal(id, nativeSessionId);
55
+ await new Promise(resolve => setTimeout(resolve, 200));
56
+ assert.equal(fs.existsSync(marker), false, 'notify executed outside the read-only workspace');
57
+ return id;
58
+ }
59
+ await run(await run());
60
+ assert.equal(requests, 2);
61
+ console.log('PASS native Codex safemode: fresh and resumed turns complete; inherited notify never executes');
62
+ } finally {
63
+ server.closeAllConnections(); server.close();
64
+ fs.rmSync(root, { recursive: true, force: true });
65
+ }
66
+ })().catch(error => { console.error(error.message); process.exitCode = 1; });