@oscharko-dev/keiko 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +7 -0
  3. package/README.md +621 -0
  4. package/TRADEMARKS.md +41 -0
  5. package/dist/audit/aggregate.d.ts +5 -0
  6. package/dist/audit/aggregate.js +25 -0
  7. package/dist/audit/build.d.ts +2 -0
  8. package/dist/audit/build.js +224 -0
  9. package/dist/audit/errors.d.ts +25 -0
  10. package/dist/audit/errors.js +39 -0
  11. package/dist/audit/index-api.d.ts +14 -0
  12. package/dist/audit/index-api.js +131 -0
  13. package/dist/audit/index.d.ts +12 -0
  14. package/dist/audit/index.js +17 -0
  15. package/dist/audit/persist.d.ts +8 -0
  16. package/dist/audit/persist.js +40 -0
  17. package/dist/audit/redaction.d.ts +3 -0
  18. package/dist/audit/redaction.js +61 -0
  19. package/dist/audit/report.d.ts +18 -0
  20. package/dist/audit/report.js +50 -0
  21. package/dist/audit/retention.d.ts +3 -0
  22. package/dist/audit/retention.js +95 -0
  23. package/dist/audit/runid.d.ts +1 -0
  24. package/dist/audit/runid.js +29 -0
  25. package/dist/audit/side-file.d.ts +12 -0
  26. package/dist/audit/side-file.js +82 -0
  27. package/dist/audit/store.d.ts +12 -0
  28. package/dist/audit/store.js +198 -0
  29. package/dist/audit/types.d.ts +188 -0
  30. package/dist/audit/types.js +8 -0
  31. package/dist/audit/workflow-evidence.d.ts +27 -0
  32. package/dist/audit/workflow-evidence.js +145 -0
  33. package/dist/cli/context.d.ts +2 -0
  34. package/dist/cli/context.js +102 -0
  35. package/dist/cli/evaluate.d.ts +7 -0
  36. package/dist/cli/evaluate.js +207 -0
  37. package/dist/cli/evidence.d.ts +8 -0
  38. package/dist/cli/evidence.js +88 -0
  39. package/dist/cli/gateway-config.d.ts +10 -0
  40. package/dist/cli/gateway-config.js +12 -0
  41. package/dist/cli/gen-tests.d.ts +7 -0
  42. package/dist/cli/gen-tests.js +208 -0
  43. package/dist/cli/index.d.ts +2 -0
  44. package/dist/cli/index.js +14 -0
  45. package/dist/cli/investigate.d.ts +8 -0
  46. package/dist/cli/investigate.js +242 -0
  47. package/dist/cli/models.d.ts +3 -0
  48. package/dist/cli/models.js +64 -0
  49. package/dist/cli/run.d.ts +7 -0
  50. package/dist/cli/run.js +187 -0
  51. package/dist/cli/runner.d.ts +6 -0
  52. package/dist/cli/runner.js +83 -0
  53. package/dist/cli/ui.d.ts +31 -0
  54. package/dist/cli/ui.js +240 -0
  55. package/dist/cli/verify.d.ts +2 -0
  56. package/dist/cli/verify.js +103 -0
  57. package/dist/evaluations/fixtures/bug-investigation/happy-path.d.ts +2 -0
  58. package/dist/evaluations/fixtures/bug-investigation/happy-path.js +66 -0
  59. package/dist/evaluations/fixtures/bug-investigation/investigation-only.d.ts +2 -0
  60. package/dist/evaluations/fixtures/bug-investigation/investigation-only.js +39 -0
  61. package/dist/evaluations/fixtures/bug-investigation/unsafe-action.d.ts +2 -0
  62. package/dist/evaluations/fixtures/bug-investigation/unsafe-action.js +37 -0
  63. package/dist/evaluations/fixtures/index.d.ts +7 -0
  64. package/dist/evaluations/fixtures/index.js +35 -0
  65. package/dist/evaluations/fixtures/support.d.ts +5 -0
  66. package/dist/evaluations/fixtures/support.js +42 -0
  67. package/dist/evaluations/fixtures/unit-tests/happy-path.d.ts +2 -0
  68. package/dist/evaluations/fixtures/unit-tests/happy-path.js +40 -0
  69. package/dist/evaluations/fixtures/unit-tests/retry-then-accept.d.ts +2 -0
  70. package/dist/evaluations/fixtures/unit-tests/retry-then-accept.js +39 -0
  71. package/dist/evaluations/fixtures/unit-tests/unsafe-action.d.ts +2 -0
  72. package/dist/evaluations/fixtures/unit-tests/unsafe-action.js +32 -0
  73. package/dist/evaluations/index.d.ts +12 -0
  74. package/dist/evaluations/index.js +12 -0
  75. package/dist/evaluations/manifest-check.d.ts +1 -0
  76. package/dist/evaluations/manifest-check.js +48 -0
  77. package/dist/evaluations/model-provider.d.ts +12 -0
  78. package/dist/evaluations/model-provider.js +26 -0
  79. package/dist/evaluations/render.d.ts +2 -0
  80. package/dist/evaluations/render.js +59 -0
  81. package/dist/evaluations/runner-support.d.ts +27 -0
  82. package/dist/evaluations/runner-support.js +163 -0
  83. package/dist/evaluations/runner.d.ts +20 -0
  84. package/dist/evaluations/runner.js +174 -0
  85. package/dist/evaluations/scorer.d.ts +14 -0
  86. package/dist/evaluations/scorer.js +131 -0
  87. package/dist/evaluations/scripted-model.d.ts +6 -0
  88. package/dist/evaluations/scripted-model.js +26 -0
  89. package/dist/evaluations/surface-parity.d.ts +2 -0
  90. package/dist/evaluations/surface-parity.js +184 -0
  91. package/dist/evaluations/types.d.ts +74 -0
  92. package/dist/evaluations/types.js +16 -0
  93. package/dist/gateway/capabilities.d.ts +11 -0
  94. package/dist/gateway/capabilities.data.d.ts +2 -0
  95. package/dist/gateway/capabilities.data.js +203 -0
  96. package/dist/gateway/capabilities.js +41 -0
  97. package/dist/gateway/config.d.ts +15 -0
  98. package/dist/gateway/config.js +154 -0
  99. package/dist/gateway/errors.d.ts +72 -0
  100. package/dist/gateway/errors.js +82 -0
  101. package/dist/gateway/gateway.d.ts +19 -0
  102. package/dist/gateway/gateway.js +94 -0
  103. package/dist/gateway/index.d.ts +10 -0
  104. package/dist/gateway/index.js +11 -0
  105. package/dist/gateway/model-selection.d.ts +9 -0
  106. package/dist/gateway/model-selection.js +36 -0
  107. package/dist/gateway/normalize.d.ts +7 -0
  108. package/dist/gateway/normalize.js +93 -0
  109. package/dist/gateway/openai-adapter.d.ts +20 -0
  110. package/dist/gateway/openai-adapter.js +263 -0
  111. package/dist/gateway/redaction.d.ts +1 -0
  112. package/dist/gateway/redaction.js +51 -0
  113. package/dist/gateway/resilience.d.ts +24 -0
  114. package/dist/gateway/resilience.js +166 -0
  115. package/dist/gateway/types.d.ts +108 -0
  116. package/dist/gateway/types.js +2 -0
  117. package/dist/harness/adapters.d.ts +23 -0
  118. package/dist/harness/adapters.js +38 -0
  119. package/dist/harness/context.d.ts +33 -0
  120. package/dist/harness/context.js +21 -0
  121. package/dist/harness/emitter.d.ts +15 -0
  122. package/dist/harness/emitter.js +72 -0
  123. package/dist/harness/errors.d.ts +21 -0
  124. package/dist/harness/errors.js +39 -0
  125. package/dist/harness/executor.d.ts +3 -0
  126. package/dist/harness/executor.js +211 -0
  127. package/dist/harness/fingerprint.d.ts +6 -0
  128. package/dist/harness/fingerprint.js +43 -0
  129. package/dist/harness/index.d.ts +9 -0
  130. package/dist/harness/index.js +13 -0
  131. package/dist/harness/loop.d.ts +3 -0
  132. package/dist/harness/loop.js +159 -0
  133. package/dist/harness/patcher.d.ts +4 -0
  134. package/dist/harness/patcher.js +49 -0
  135. package/dist/harness/planner.d.ts +3 -0
  136. package/dist/harness/planner.js +21 -0
  137. package/dist/harness/ports.d.ts +61 -0
  138. package/dist/harness/ports.js +4 -0
  139. package/dist/harness/session.d.ts +25 -0
  140. package/dist/harness/session.js +116 -0
  141. package/dist/harness/sinks.d.ts +30 -0
  142. package/dist/harness/sinks.js +72 -0
  143. package/dist/harness/tasks/explain-plan.d.ts +3 -0
  144. package/dist/harness/tasks/explain-plan.js +29 -0
  145. package/dist/harness/tasks/generate-unit-tests.d.ts +3 -0
  146. package/dist/harness/tasks/generate-unit-tests.js +28 -0
  147. package/dist/harness/tasks/investigate-bug.d.ts +3 -0
  148. package/dist/harness/tasks/investigate-bug.js +31 -0
  149. package/dist/harness/tasks/policy.d.ts +11 -0
  150. package/dist/harness/tasks/policy.js +22 -0
  151. package/dist/harness/tasks/verify.d.ts +3 -0
  152. package/dist/harness/tasks/verify.js +16 -0
  153. package/dist/harness/types.d.ts +270 -0
  154. package/dist/harness/types.js +33 -0
  155. package/dist/index.d.ts +11 -0
  156. package/dist/index.js +36 -0
  157. package/dist/sdk/index.d.ts +9 -0
  158. package/dist/sdk/index.js +37 -0
  159. package/dist/sdk/run-agent.d.ts +16 -0
  160. package/dist/sdk/run-agent.js +56 -0
  161. package/dist/tools/browser/cdp-client.d.ts +35 -0
  162. package/dist/tools/browser/cdp-client.js +218 -0
  163. package/dist/tools/browser/errors.d.ts +25 -0
  164. package/dist/tools/browser/errors.js +55 -0
  165. package/dist/tools/browser/index.d.ts +5 -0
  166. package/dist/tools/browser/index.js +6 -0
  167. package/dist/tools/browser/session.d.ts +44 -0
  168. package/dist/tools/browser/session.js +748 -0
  169. package/dist/tools/browser/types.d.ts +48 -0
  170. package/dist/tools/browser/types.js +2 -0
  171. package/dist/tools/browser/validators.d.ts +5 -0
  172. package/dist/tools/browser/validators.js +97 -0
  173. package/dist/tools/errors.d.ts +59 -0
  174. package/dist/tools/errors.js +94 -0
  175. package/dist/tools/exec.d.ts +42 -0
  176. package/dist/tools/exec.js +327 -0
  177. package/dist/tools/index.d.ts +11 -0
  178. package/dist/tools/index.js +14 -0
  179. package/dist/tools/patch-content.d.ts +10 -0
  180. package/dist/tools/patch-content.js +126 -0
  181. package/dist/tools/patch-normalize.d.ts +1 -0
  182. package/dist/tools/patch-normalize.js +80 -0
  183. package/dist/tools/patch-parse.d.ts +8 -0
  184. package/dist/tools/patch-parse.js +201 -0
  185. package/dist/tools/patch.d.ts +18 -0
  186. package/dist/tools/patch.js +403 -0
  187. package/dist/tools/registry.d.ts +36 -0
  188. package/dist/tools/registry.js +231 -0
  189. package/dist/tools/sandbox.d.ts +8 -0
  190. package/dist/tools/sandbox.js +121 -0
  191. package/dist/tools/schemas.d.ts +2 -0
  192. package/dist/tools/schemas.js +51 -0
  193. package/dist/tools/terminal-policy.d.ts +9 -0
  194. package/dist/tools/terminal-policy.js +313 -0
  195. package/dist/tools/types.d.ts +99 -0
  196. package/dist/tools/types.js +103 -0
  197. package/dist/tools/writer.d.ts +7 -0
  198. package/dist/tools/writer.js +20 -0
  199. package/dist/ui/browser.d.ts +10 -0
  200. package/dist/ui/browser.js +231 -0
  201. package/dist/ui/chat-handlers.d.ts +4 -0
  202. package/dist/ui/chat-handlers.js +281 -0
  203. package/dist/ui/csp-hashes.json +17 -0
  204. package/dist/ui/csp.d.ts +2 -0
  205. package/dist/ui/csp.js +66 -0
  206. package/dist/ui/deps.d.ts +34 -0
  207. package/dist/ui/deps.js +137 -0
  208. package/dist/ui/evidence.d.ts +27 -0
  209. package/dist/ui/evidence.js +142 -0
  210. package/dist/ui/files-deny.d.ts +2 -0
  211. package/dist/ui/files-deny.js +12 -0
  212. package/dist/ui/files.d.ts +65 -0
  213. package/dist/ui/files.js +492 -0
  214. package/dist/ui/headers.d.ts +2 -0
  215. package/dist/ui/headers.js +21 -0
  216. package/dist/ui/host-check.d.ts +2 -0
  217. package/dist/ui/host-check.js +58 -0
  218. package/dist/ui/index.d.ts +20 -0
  219. package/dist/ui/index.js +23 -0
  220. package/dist/ui/load-csp.d.ts +1 -0
  221. package/dist/ui/load-csp.js +28 -0
  222. package/dist/ui/read-handlers.d.ts +8 -0
  223. package/dist/ui/read-handlers.js +247 -0
  224. package/dist/ui/routes.d.ts +36 -0
  225. package/dist/ui/routes.js +129 -0
  226. package/dist/ui/run-engine.d.ts +20 -0
  227. package/dist/ui/run-engine.js +345 -0
  228. package/dist/ui/run-handlers.d.ts +8 -0
  229. package/dist/ui/run-handlers.js +431 -0
  230. package/dist/ui/run-request.d.ts +13 -0
  231. package/dist/ui/run-request.js +219 -0
  232. package/dist/ui/runs.d.ts +43 -0
  233. package/dist/ui/runs.js +92 -0
  234. package/dist/ui/server.d.ts +11 -0
  235. package/dist/ui/server.js +143 -0
  236. package/dist/ui/sink.d.ts +27 -0
  237. package/dist/ui/sink.js +80 -0
  238. package/dist/ui/sse.d.ts +7 -0
  239. package/dist/ui/sse.js +27 -0
  240. package/dist/ui/static/404.html +1 -0
  241. package/dist/ui/static/_next/static/ca-A01hy9W98aRvMZKdAw/_buildManifest.js +1 -0
  242. package/dist/ui/static/_next/static/ca-A01hy9W98aRvMZKdAw/_ssgManifest.js +1 -0
  243. package/dist/ui/static/_next/static/chunks/255-d47fd57964443afe.js +1 -0
  244. package/dist/ui/static/_next/static/chunks/4-be1fef693af8e088.js +1 -0
  245. package/dist/ui/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
  246. package/dist/ui/static/_next/static/chunks/app/_not-found/page-75825b09bcecad97.js +1 -0
  247. package/dist/ui/static/_next/static/chunks/app/launch/page-9c86a13c29884245.js +1 -0
  248. package/dist/ui/static/_next/static/chunks/app/layout-bdea63fe87947d50.js +1 -0
  249. package/dist/ui/static/_next/static/chunks/app/page-4168c12c68b7a853.js +1 -0
  250. package/dist/ui/static/_next/static/chunks/framework-a6e0b7e30f98059a.js +1 -0
  251. package/dist/ui/static/_next/static/chunks/main-778a50aebff02192.js +1 -0
  252. package/dist/ui/static/_next/static/chunks/main-app-30679af7240d63e9.js +1 -0
  253. package/dist/ui/static/_next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
  254. package/dist/ui/static/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
  255. package/dist/ui/static/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  256. package/dist/ui/static/_next/static/chunks/webpack-4a462cecab786e93.js +1 -0
  257. package/dist/ui/static/_next/static/css/be7cb54d5c5673b6.css +1 -0
  258. package/dist/ui/static/assets/editors/goland.svg +35 -0
  259. package/dist/ui/static/assets/editors/intellij.svg +39 -0
  260. package/dist/ui/static/assets/editors/pycharm.svg +58 -0
  261. package/dist/ui/static/assets/editors/rustrover.svg +19 -0
  262. package/dist/ui/static/assets/editors/vscode.svg +1 -0
  263. package/dist/ui/static/assets/editors/webstorm.svg +21 -0
  264. package/dist/ui/static/assets/icons/anthropic.svg +1 -0
  265. package/dist/ui/static/assets/icons/brave.svg +1 -0
  266. package/dist/ui/static/assets/icons/css3.svg +1 -0
  267. package/dist/ui/static/assets/icons/docker.svg +1 -0
  268. package/dist/ui/static/assets/icons/git.svg +1 -0
  269. package/dist/ui/static/assets/icons/github.svg +1 -0
  270. package/dist/ui/static/assets/icons/go.svg +1 -0
  271. package/dist/ui/static/assets/icons/gradle.svg +1 -0
  272. package/dist/ui/static/assets/icons/grafana.svg +1 -0
  273. package/dist/ui/static/assets/icons/graphql.svg +1 -0
  274. package/dist/ui/static/assets/icons/html5.svg +1 -0
  275. package/dist/ui/static/assets/icons/image.svg +1 -0
  276. package/dist/ui/static/assets/icons/java.svg +1 -0
  277. package/dist/ui/static/assets/icons/javascript.svg +1 -0
  278. package/dist/ui/static/assets/icons/json.svg +1 -0
  279. package/dist/ui/static/assets/icons/kafka.svg +1 -0
  280. package/dist/ui/static/assets/icons/kubernetes.svg +1 -0
  281. package/dist/ui/static/assets/icons/linear.svg +1 -0
  282. package/dist/ui/static/assets/icons/markdown.svg +1 -0
  283. package/dist/ui/static/assets/icons/nginx.svg +1 -0
  284. package/dist/ui/static/assets/icons/nodejs.svg +1 -0
  285. package/dist/ui/static/assets/icons/notion.svg +1 -0
  286. package/dist/ui/static/assets/icons/openai.svg +1 -0
  287. package/dist/ui/static/assets/icons/playwright.svg +1 -0
  288. package/dist/ui/static/assets/icons/postgresql.svg +1 -0
  289. package/dist/ui/static/assets/icons/prometheus.svg +1 -0
  290. package/dist/ui/static/assets/icons/properties.svg +1 -0
  291. package/dist/ui/static/assets/icons/puppeteer.svg +1 -0
  292. package/dist/ui/static/assets/icons/python.svg +1 -0
  293. package/dist/ui/static/assets/icons/react.svg +1 -0
  294. package/dist/ui/static/assets/icons/redis.svg +1 -0
  295. package/dist/ui/static/assets/icons/rust.svg +1 -0
  296. package/dist/ui/static/assets/icons/sentry.svg +1 -0
  297. package/dist/ui/static/assets/icons/slack.svg +1 -0
  298. package/dist/ui/static/assets/icons/spring.svg +1 -0
  299. package/dist/ui/static/assets/icons/typescript.svg +1 -0
  300. package/dist/ui/static/assets/icons/upstash.svg +1 -0
  301. package/dist/ui/static/assets/icons/yaml.svg +1 -0
  302. package/dist/ui/static/assets/keiko-logo.svg +10 -0
  303. package/dist/ui/static/index.html +1 -0
  304. package/dist/ui/static/index.txt +19 -0
  305. package/dist/ui/static/keiko-logo.svg +10 -0
  306. package/dist/ui/static/launch.html +1 -0
  307. package/dist/ui/static/launch.txt +19 -0
  308. package/dist/ui/static.d.ts +3 -0
  309. package/dist/ui/static.js +72 -0
  310. package/dist/ui/store/chats.d.ts +14 -0
  311. package/dist/ui/store/chats.js +110 -0
  312. package/dist/ui/store/db.d.ts +6 -0
  313. package/dist/ui/store/db.js +182 -0
  314. package/dist/ui/store/errors.d.ts +12 -0
  315. package/dist/ui/store/errors.js +30 -0
  316. package/dist/ui/store/index.d.ts +6 -0
  317. package/dist/ui/store/index.js +6 -0
  318. package/dist/ui/store/messages.d.ts +5 -0
  319. package/dist/ui/store/messages.js +137 -0
  320. package/dist/ui/store/paths.d.ts +4 -0
  321. package/dist/ui/store/paths.js +69 -0
  322. package/dist/ui/store/projects.d.ts +7 -0
  323. package/dist/ui/store/projects.js +61 -0
  324. package/dist/ui/store/schema.d.ts +3 -0
  325. package/dist/ui/store/schema.js +77 -0
  326. package/dist/ui/store/types.d.ts +80 -0
  327. package/dist/ui/store/types.js +3 -0
  328. package/dist/ui/store/validation.d.ts +4 -0
  329. package/dist/ui/store/validation.js +72 -0
  330. package/dist/ui/store-handlers.d.ts +16 -0
  331. package/dist/ui/store-handlers.js +465 -0
  332. package/dist/ui/terminal-errors.d.ts +21 -0
  333. package/dist/ui/terminal-errors.js +45 -0
  334. package/dist/ui/terminal-evidence.d.ts +20 -0
  335. package/dist/ui/terminal-evidence.js +65 -0
  336. package/dist/ui/terminal-routes.d.ts +9 -0
  337. package/dist/ui/terminal-routes.js +219 -0
  338. package/dist/ui/terminal.d.ts +67 -0
  339. package/dist/ui/terminal.js +835 -0
  340. package/dist/verification/classify.d.ts +10 -0
  341. package/dist/verification/classify.js +53 -0
  342. package/dist/verification/detect.d.ts +4 -0
  343. package/dist/verification/detect.js +81 -0
  344. package/dist/verification/errors.d.ts +11 -0
  345. package/dist/verification/errors.js +21 -0
  346. package/dist/verification/index.d.ts +17 -0
  347. package/dist/verification/index.js +13 -0
  348. package/dist/verification/limits.d.ts +3 -0
  349. package/dist/verification/limits.js +40 -0
  350. package/dist/verification/monitor.d.ts +4 -0
  351. package/dist/verification/monitor.js +58 -0
  352. package/dist/verification/orchestrator.d.ts +16 -0
  353. package/dist/verification/orchestrator.js +363 -0
  354. package/dist/verification/plan.d.ts +9 -0
  355. package/dist/verification/plan.js +125 -0
  356. package/dist/verification/summary.d.ts +40 -0
  357. package/dist/verification/summary.js +67 -0
  358. package/dist/verification/types.d.ts +63 -0
  359. package/dist/verification/types.js +13 -0
  360. package/dist/workflows/bug-investigation/context.d.ts +7 -0
  361. package/dist/workflows/bug-investigation/context.js +119 -0
  362. package/dist/workflows/bug-investigation/descriptor.d.ts +3 -0
  363. package/dist/workflows/bug-investigation/descriptor.js +46 -0
  364. package/dist/workflows/bug-investigation/emit.d.ts +12 -0
  365. package/dist/workflows/bug-investigation/emit.js +35 -0
  366. package/dist/workflows/bug-investigation/events.d.ts +81 -0
  367. package/dist/workflows/bug-investigation/events.js +9 -0
  368. package/dist/workflows/bug-investigation/failure-parse.d.ts +3 -0
  369. package/dist/workflows/bug-investigation/failure-parse.js +154 -0
  370. package/dist/workflows/bug-investigation/guard.d.ts +2 -0
  371. package/dist/workflows/bug-investigation/guard.js +69 -0
  372. package/dist/workflows/bug-investigation/index.d.ts +7 -0
  373. package/dist/workflows/bug-investigation/index.js +13 -0
  374. package/dist/workflows/bug-investigation/internal.d.ts +37 -0
  375. package/dist/workflows/bug-investigation/internal.js +64 -0
  376. package/dist/workflows/bug-investigation/model-loop.d.ts +4 -0
  377. package/dist/workflows/bug-investigation/model-loop.js +223 -0
  378. package/dist/workflows/bug-investigation/parse.d.ts +3 -0
  379. package/dist/workflows/bug-investigation/parse.js +123 -0
  380. package/dist/workflows/bug-investigation/prompt.d.ts +4 -0
  381. package/dist/workflows/bug-investigation/prompt.js +107 -0
  382. package/dist/workflows/bug-investigation/report.d.ts +23 -0
  383. package/dist/workflows/bug-investigation/report.js +151 -0
  384. package/dist/workflows/bug-investigation/stages.d.ts +13 -0
  385. package/dist/workflows/bug-investigation/stages.js +242 -0
  386. package/dist/workflows/bug-investigation/types.d.ts +91 -0
  387. package/dist/workflows/bug-investigation/types.js +14 -0
  388. package/dist/workflows/bug-investigation/verify-stage.d.ts +10 -0
  389. package/dist/workflows/bug-investigation/verify-stage.js +91 -0
  390. package/dist/workflows/bug-investigation/workflow.d.ts +2 -0
  391. package/dist/workflows/bug-investigation/workflow.js +74 -0
  392. package/dist/workflows/descriptor.d.ts +20 -0
  393. package/dist/workflows/descriptor.js +8 -0
  394. package/dist/workflows/index.d.ts +3 -0
  395. package/dist/workflows/index.js +2 -0
  396. package/dist/workflows/unit-tests/context.d.ts +7 -0
  397. package/dist/workflows/unit-tests/context.js +129 -0
  398. package/dist/workflows/unit-tests/conventions.d.ts +4 -0
  399. package/dist/workflows/unit-tests/conventions.js +87 -0
  400. package/dist/workflows/unit-tests/descriptor.d.ts +4 -0
  401. package/dist/workflows/unit-tests/descriptor.js +43 -0
  402. package/dist/workflows/unit-tests/emit.d.ts +12 -0
  403. package/dist/workflows/unit-tests/emit.js +35 -0
  404. package/dist/workflows/unit-tests/events.d.ts +78 -0
  405. package/dist/workflows/unit-tests/events.js +7 -0
  406. package/dist/workflows/unit-tests/index.d.ts +6 -0
  407. package/dist/workflows/unit-tests/index.js +10 -0
  408. package/dist/workflows/unit-tests/internal.d.ts +35 -0
  409. package/dist/workflows/unit-tests/internal.js +43 -0
  410. package/dist/workflows/unit-tests/model-loop.d.ts +4 -0
  411. package/dist/workflows/unit-tests/model-loop.js +95 -0
  412. package/dist/workflows/unit-tests/parse.d.ts +6 -0
  413. package/dist/workflows/unit-tests/parse.js +68 -0
  414. package/dist/workflows/unit-tests/prompt.d.ts +4 -0
  415. package/dist/workflows/unit-tests/prompt.js +71 -0
  416. package/dist/workflows/unit-tests/report.d.ts +21 -0
  417. package/dist/workflows/unit-tests/report.js +90 -0
  418. package/dist/workflows/unit-tests/stages.d.ts +9 -0
  419. package/dist/workflows/unit-tests/stages.js +155 -0
  420. package/dist/workflows/unit-tests/types.d.ts +70 -0
  421. package/dist/workflows/unit-tests/types.js +11 -0
  422. package/dist/workflows/unit-tests/verify-stage.d.ts +9 -0
  423. package/dist/workflows/unit-tests/verify-stage.js +56 -0
  424. package/dist/workflows/unit-tests/workflow.d.ts +2 -0
  425. package/dist/workflows/unit-tests/workflow.js +58 -0
  426. package/dist/workspace/contextPack.d.ts +9 -0
  427. package/dist/workspace/contextPack.js +94 -0
  428. package/dist/workspace/detect.d.ts +3 -0
  429. package/dist/workspace/detect.js +135 -0
  430. package/dist/workspace/discovery.d.ts +9 -0
  431. package/dist/workspace/discovery.js +167 -0
  432. package/dist/workspace/errors.d.ts +39 -0
  433. package/dist/workspace/errors.js +66 -0
  434. package/dist/workspace/fs.d.ts +21 -0
  435. package/dist/workspace/fs.js +36 -0
  436. package/dist/workspace/ignore.d.ts +14 -0
  437. package/dist/workspace/ignore.js +176 -0
  438. package/dist/workspace/index.d.ts +11 -0
  439. package/dist/workspace/index.js +13 -0
  440. package/dist/workspace/paths.d.ts +2 -0
  441. package/dist/workspace/paths.js +38 -0
  442. package/dist/workspace/realpath.d.ts +7 -0
  443. package/dist/workspace/realpath.js +72 -0
  444. package/dist/workspace/retrieval.d.ts +9 -0
  445. package/dist/workspace/retrieval.js +74 -0
  446. package/dist/workspace/summary.d.ts +3 -0
  447. package/dist/workspace/summary.js +54 -0
  448. package/dist/workspace/types.d.ts +103 -0
  449. package/dist/workspace/types.js +27 -0
  450. package/package.json +58 -0
@@ -0,0 +1,263 @@
1
+ // Zero-dependency OpenAI-compatible HTTP adapter built on globalThis.fetch and
2
+ // AbortSignal. fetch, clock, request-id, and cost class are injected so tests run
3
+ // with no network I/O and no real time. The raw provider body is never echoed into
4
+ // an error; only a redacted, status-level summary is surfaced.
5
+ import { request as httpsRequest } from "node:https";
6
+ import { rootCertificates } from "node:tls";
7
+ import { AuthenticationError, CancelledError, ContextOverflowError, ModelRefusalError, ProviderError, RateLimitError, TimeoutError, TransportError, } from "./errors.js";
8
+ import { normalizeChatResponse } from "./normalize.js";
9
+ import { redact } from "./redaction.js";
10
+ function headersFromNode(headers) {
11
+ const out = new Headers();
12
+ for (const [name, value] of Object.entries(headers)) {
13
+ if (Array.isArray(value)) {
14
+ for (const item of value)
15
+ out.append(name, item);
16
+ }
17
+ else if (value !== undefined) {
18
+ out.set(name, value);
19
+ }
20
+ }
21
+ return out;
22
+ }
23
+ function isMissingIssuerError(error) {
24
+ const cause = isRecord(error) ? error.cause : undefined;
25
+ const candidates = [error, cause];
26
+ return candidates.some((item) => {
27
+ if (!isRecord(item))
28
+ return false;
29
+ return item.code === "UNABLE_TO_GET_ISSUER_CERT_LOCALLY";
30
+ });
31
+ }
32
+ function usesHttps(url) {
33
+ try {
34
+ return new URL(url).protocol === "https:";
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ function buildMessage(message) {
41
+ const toolCalls = message.toolCalls?.map((call) => ({
42
+ id: call.id,
43
+ type: "function",
44
+ function: { name: call.name, arguments: JSON.stringify(call.arguments) },
45
+ }));
46
+ return {
47
+ role: message.role,
48
+ content: message.role === "assistant" && toolCalls !== undefined && toolCalls.length > 0
49
+ ? null
50
+ : message.content,
51
+ ...(message.role === "tool" && message.toolCallId !== undefined
52
+ ? { tool_call_id: message.toolCallId }
53
+ : {}),
54
+ ...(toolCalls !== undefined && toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),
55
+ };
56
+ }
57
+ function buildBody(request) {
58
+ const messages = request.messages.map(buildMessage);
59
+ const base = { model: request.modelId, messages };
60
+ const tools = request.tools === undefined
61
+ ? undefined
62
+ : request.tools.map((t) => ({
63
+ type: "function",
64
+ function: { name: t.name, description: t.description, parameters: t.parameters },
65
+ }));
66
+ const responseFormat = request.responseFormat?.type === "json_schema"
67
+ ? { type: "json_schema", json_schema: { schema: request.responseFormat.schema } }
68
+ : undefined;
69
+ return {
70
+ ...base,
71
+ ...(tools ? { tools } : {}),
72
+ ...(responseFormat ? { response_format: responseFormat } : {}),
73
+ };
74
+ }
75
+ function retryAfterMs(response) {
76
+ const header = response.headers.get("retry-after");
77
+ if (header === null) {
78
+ return null;
79
+ }
80
+ const seconds = Number(header);
81
+ return Number.isFinite(seconds) && seconds >= 0 ? seconds * 1000 : null;
82
+ }
83
+ function isRecord(value) {
84
+ return typeof value === "object" && value !== null && !Array.isArray(value);
85
+ }
86
+ function redactUnknown(value, secrets) {
87
+ if (typeof value === "string") {
88
+ return redact(value, secrets);
89
+ }
90
+ if (Array.isArray(value)) {
91
+ return value.map((item) => redactUnknown(item, secrets));
92
+ }
93
+ if (isRecord(value)) {
94
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, redactUnknown(item, secrets)]));
95
+ }
96
+ return value;
97
+ }
98
+ function redactRecord(value, secrets) {
99
+ return value === null ? null : redactUnknown(value, secrets);
100
+ }
101
+ function redactToolCall(call, secrets) {
102
+ return {
103
+ ...call,
104
+ name: redact(call.name, secrets),
105
+ arguments: redactUnknown(call.arguments, secrets),
106
+ };
107
+ }
108
+ function redactResponse(response, secrets) {
109
+ return {
110
+ ...response,
111
+ content: redact(response.content, secrets),
112
+ toolCalls: response.toolCalls.map((call) => redactToolCall(call, secrets)),
113
+ structuredOutput: redactRecord(response.structuredOutput, secrets),
114
+ };
115
+ }
116
+ function errorSignal(payload) {
117
+ const error = isRecord(payload) && isRecord(payload.error) ? payload.error : payload;
118
+ if (!isRecord(error)) {
119
+ return "";
120
+ }
121
+ return [error.code, error.type, error.message]
122
+ .filter((value) => typeof value === "string")
123
+ .join(" ")
124
+ .toLowerCase();
125
+ }
126
+ function isContextOverflow(response, payload) {
127
+ if (response.status !== 400 && response.status !== 413 && response.status !== 422) {
128
+ return false;
129
+ }
130
+ return /context[_ -]?length[_ -]?exceeded|context window|context.*exceed|maximum context|too many tokens|prompt too long|context overflow/.test(errorSignal(payload));
131
+ }
132
+ function isModelRefusal(payload) {
133
+ return /content[_ -]?filter|refus|safety|policy/.test(errorSignal(payload));
134
+ }
135
+ function mapHttpError(response, modelId, secrets, payload) {
136
+ if (isContextOverflow(response, payload)) {
137
+ throw new ContextOverflowError(`provider reported context overflow for '${modelId}'`, secrets);
138
+ }
139
+ if (isModelRefusal(payload)) {
140
+ throw new ModelRefusalError(`provider refused the request for '${modelId}'`, secrets);
141
+ }
142
+ if (response.status === 401 || response.status === 403) {
143
+ throw new AuthenticationError(`provider rejected credentials for '${modelId}'`, secrets);
144
+ }
145
+ if (response.status === 429) {
146
+ throw new RateLimitError(`provider rate limited '${modelId}'`, retryAfterMs(response), secrets);
147
+ }
148
+ throw new ProviderError(`provider returned HTTP ${String(response.status)} for '${modelId}'`, response.status, secrets);
149
+ }
150
+ export class OpenAiAdapter {
151
+ deps;
152
+ fetchImpl;
153
+ now;
154
+ useBundledCaFallback;
155
+ constructor(deps) {
156
+ this.deps = deps;
157
+ this.fetchImpl = deps.fetchImpl ?? globalThis.fetch;
158
+ this.now = deps.now ?? Date.now;
159
+ this.useBundledCaFallback = deps.fetchImpl === undefined;
160
+ }
161
+ call = async (request, config) => {
162
+ const secrets = [config.apiKey, config.baseUrl];
163
+ if (request.cancellationSignal?.aborted === true) {
164
+ throw new CancelledError(`request for '${config.modelId}' cancelled before dispatch`, secrets);
165
+ }
166
+ const start = this.now();
167
+ const response = await this.dispatch(request, config, secrets);
168
+ if (!response.ok) {
169
+ const errorPayload = await this.readErrorBody(response);
170
+ mapHttpError(response, config.modelId, secrets, errorPayload);
171
+ }
172
+ const payload = await this.readBody(response, config, secrets);
173
+ return redactResponse(normalizeChatResponse(payload, config.modelId, {
174
+ requestId: this.deps.requestId,
175
+ latencyMs: this.now() - start,
176
+ costClass: this.deps.costClass,
177
+ }, request.responseFormat?.type === "json_schema"), secrets);
178
+ };
179
+ async dispatch(request, config, secrets) {
180
+ const timeoutSignal = AbortSignal.timeout(config.timeoutMs);
181
+ const cancel = request.cancellationSignal;
182
+ const signal = cancel ? AbortSignal.any([timeoutSignal, cancel]) : timeoutSignal;
183
+ const url = `${config.baseUrl}/chat/completions`;
184
+ const body = JSON.stringify(buildBody(request));
185
+ const headers = {
186
+ "content-type": "application/json",
187
+ authorization: `Bearer ${config.apiKey}`,
188
+ };
189
+ try {
190
+ return await this.fetchImpl(url, {
191
+ method: "POST",
192
+ headers,
193
+ body,
194
+ signal,
195
+ });
196
+ }
197
+ catch (error) {
198
+ if (this.useBundledCaFallback && usesHttps(url) && isMissingIssuerError(error)) {
199
+ try {
200
+ // Some shared-OpenSSL Node builds do not use Node's bundled public CA set by default.
201
+ return await this.dispatchWithBundledCa(url, headers, body, signal);
202
+ }
203
+ catch (fallbackError) {
204
+ throw this.mapDispatchError(fallbackError, config, cancel, timeoutSignal, secrets);
205
+ }
206
+ }
207
+ throw this.mapDispatchError(error, config, cancel, timeoutSignal, secrets);
208
+ }
209
+ }
210
+ dispatchWithBundledCa(url, headers, body, signal) {
211
+ return new Promise((resolve, reject) => {
212
+ const req = httpsRequest(url, {
213
+ method: "POST",
214
+ headers,
215
+ ca: Array.from(rootCertificates),
216
+ signal,
217
+ }, (res) => {
218
+ const chunks = [];
219
+ res.on("data", (chunk) => {
220
+ chunks.push(chunk);
221
+ });
222
+ res.on("end", () => {
223
+ resolve(new Response(Buffer.concat(chunks), {
224
+ status: res.statusCode ?? 500,
225
+ statusText: res.statusMessage ?? "",
226
+ headers: headersFromNode(res.headers),
227
+ }));
228
+ });
229
+ res.on("error", reject);
230
+ });
231
+ req.on("error", reject);
232
+ req.end(body);
233
+ });
234
+ }
235
+ mapDispatchError(error, config, cancel, timeout, secrets) {
236
+ if (cancel?.aborted === true) {
237
+ return new CancelledError(`request for '${config.modelId}' cancelled`, secrets);
238
+ }
239
+ if (timeout.aborted) {
240
+ return new TimeoutError(`request for '${config.modelId}' timed out`, secrets);
241
+ }
242
+ if (error instanceof DOMException && error.name === "TimeoutError") {
243
+ return new TimeoutError(`request for '${config.modelId}' timed out`, secrets);
244
+ }
245
+ return new TransportError(`transport failure contacting '${config.modelId}'`, secrets);
246
+ }
247
+ async readBody(response, config, secrets) {
248
+ try {
249
+ return await response.json();
250
+ }
251
+ catch {
252
+ throw new TransportError(`provider sent an unreadable body for '${config.modelId}'`, secrets);
253
+ }
254
+ }
255
+ async readErrorBody(response) {
256
+ try {
257
+ return await response.json();
258
+ }
259
+ catch {
260
+ return null;
261
+ }
262
+ }
263
+ }
@@ -0,0 +1 @@
1
+ export declare function redact(input: string, additionalSecrets?: readonly string[]): string;
@@ -0,0 +1,51 @@
1
+ // Secret redaction at the boundary. Every provider-derived string passes through
2
+ // redact() before it can reach an error message, log call, or serialised artefact.
3
+ const REDACTED = "[REDACTED]";
4
+ // Bearer <token>: keep the scheme, drop the credential.
5
+ const BEARER_PATTERN = /\bBearer\s+[\w.\-+/=]+/gi;
6
+ const BASIC_AUTH_PATTERN = /\bBasic\s+[\w.\-+/=]+/gi;
7
+ // OpenAI-style keys (sk-, sk-proj-, etc.): a prefix followed by >= 16 secret chars.
8
+ const API_KEY_PATTERN = /\bsk-[A-Za-z0-9_-]{16,}/g;
9
+ // Common third-party credential shapes. Each is a single linear character class with one
10
+ // bounded/open quantifier (no nesting), so none can backtrack catastrophically — this keeps
11
+ // CodeQL js/polynomial-redos satisfied while scrubbing non-OpenAI secrets from tool output.
12
+ const GITHUB_TOKEN_PATTERN = /\bgh[pousr]_[A-Za-z0-9]{20,}/g;
13
+ const AWS_ACCESS_KEY_PATTERN = /\bAKIA[0-9A-Z]{16}\b/g;
14
+ const SLACK_TOKEN_PATTERN = /\bxox[baprs]-[A-Za-z0-9-]{10,}/g;
15
+ const GOOGLE_API_KEY_PATTERN = /\bAIza[0-9A-Za-z_-]{20,}/g;
16
+ const PEM_PRIVATE_KEY_BLOCK_PATTERN = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g;
17
+ const PEM_PRIVATE_KEY_HEADER_PATTERN = /-----BEGIN [A-Z ]*PRIVATE KEY-----/g;
18
+ const GENERIC_API_KEY_HEADER_PATTERN = /\b(x-api-key\s*:\s*)[^\s"'`,;]+/gi;
19
+ const GENERIC_API_KEY_ASSIGNMENT_PATTERN = /\b(api[_-]?key\s*[=:]\s*)[^\s"'`,;&]+/gi;
20
+ const BUILTIN_PATTERNS = [
21
+ GITHUB_TOKEN_PATTERN,
22
+ AWS_ACCESS_KEY_PATTERN,
23
+ SLACK_TOKEN_PATTERN,
24
+ GOOGLE_API_KEY_PATTERN,
25
+ PEM_PRIVATE_KEY_BLOCK_PATTERN,
26
+ PEM_PRIVATE_KEY_HEADER_PATTERN,
27
+ ];
28
+ function escapeRegExp(value) {
29
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
30
+ }
31
+ // Strips known secret shapes and any caller-supplied literal secrets from `input`.
32
+ // `additionalSecrets` lets the gateway pass the exact apiKey/baseUrl values it holds
33
+ // so even non-standard key formats are scrubbed.
34
+ export function redact(input, additionalSecrets = []) {
35
+ let output = input
36
+ .replace(BEARER_PATTERN, `Bearer ${REDACTED}`)
37
+ .replace(BASIC_AUTH_PATTERN, `Basic ${REDACTED}`)
38
+ .replace(GENERIC_API_KEY_HEADER_PATTERN, `$1${REDACTED}`)
39
+ .replace(GENERIC_API_KEY_ASSIGNMENT_PATTERN, `$1${REDACTED}`)
40
+ .replace(API_KEY_PATTERN, REDACTED);
41
+ for (const pattern of BUILTIN_PATTERNS) {
42
+ output = output.replace(pattern, REDACTED);
43
+ }
44
+ for (const secret of additionalSecrets) {
45
+ if (secret.length === 0) {
46
+ continue;
47
+ }
48
+ output = output.replace(new RegExp(escapeRegExp(secret), "g"), REDACTED);
49
+ }
50
+ return output;
51
+ }
@@ -0,0 +1,24 @@
1
+ import type { CircuitBreakerConfig, CircuitBreakerStatus, Clock } from "./types.js";
2
+ export declare const systemClock: Clock;
3
+ export interface RetryConfig {
4
+ readonly maxRetries: number;
5
+ readonly retryBaseDelayMs: number;
6
+ readonly timeoutMs?: number | undefined;
7
+ }
8
+ export declare function executeWithRetry<T>(operation: (attemptTimeoutMs?: number) => Promise<T>, config: RetryConfig, clock: Clock, signal?: AbortSignal): Promise<T>;
9
+ export declare class CircuitBreaker {
10
+ private readonly modelId;
11
+ private readonly config;
12
+ private readonly clock;
13
+ private state;
14
+ private consecutiveFailures;
15
+ private openedAt;
16
+ private probesRemaining;
17
+ constructor(modelId: string, config: CircuitBreakerConfig, clock: Clock);
18
+ assertAllowed(): void;
19
+ recordSuccess(): void;
20
+ recordFailure(): void;
21
+ status(modelId: string): CircuitBreakerStatus;
22
+ private open;
23
+ private close;
24
+ }
@@ -0,0 +1,166 @@
1
+ // Resilience primitives: a real-time Clock, a bounded exponential-backoff retry
2
+ // loop, and a per-(model,endpoint) circuit breaker. All time-dependent behaviour
3
+ // flows through the injectable Clock so tests are deterministic and instant.
4
+ import { CancelledError, CircuitOpenError, GatewayError, RateLimitError } from "./errors.js";
5
+ const MAX_BACKOFF_MS = 30_000;
6
+ export const systemClock = {
7
+ now: () => Date.now(),
8
+ sleep: (ms, signal) => {
9
+ if (signal?.aborted === true) {
10
+ return Promise.reject(new DOMException("cancelled", "AbortError"));
11
+ }
12
+ return new Promise((resolve, reject) => {
13
+ const timeout = setTimeout(() => {
14
+ signal?.removeEventListener("abort", onAbort);
15
+ resolve();
16
+ }, ms);
17
+ function onAbort() {
18
+ clearTimeout(timeout);
19
+ reject(new DOMException("cancelled", "AbortError"));
20
+ }
21
+ signal?.addEventListener("abort", onAbort, { once: true });
22
+ });
23
+ },
24
+ };
25
+ function backoffDelayMs(attempt, base) {
26
+ return Math.min(base * 2 ** (attempt - 1), MAX_BACKOFF_MS);
27
+ }
28
+ // A RateLimitError with an explicit retryAfterMs is honoured; otherwise the error's
29
+ // own `retryable` flag decides. Non-GatewayErrors are never retried.
30
+ function retryDelayMs(error, attempt, base) {
31
+ if (!(error instanceof GatewayError) || !error.retryable) {
32
+ return null;
33
+ }
34
+ if (error instanceof RateLimitError && error.retryAfterMs !== null && error.retryAfterMs > 0) {
35
+ return Math.min(error.retryAfterMs, MAX_BACKOFF_MS);
36
+ }
37
+ return backoffDelayMs(attempt, base);
38
+ }
39
+ function assertNotAborted(signal) {
40
+ if (signal?.aborted === true) {
41
+ throw new CancelledError("request cancelled during retry");
42
+ }
43
+ }
44
+ function remainingBudgetMs(start, timeoutMs, clock) {
45
+ if (timeoutMs === undefined) {
46
+ return Number.POSITIVE_INFINITY;
47
+ }
48
+ return Math.max(0, timeoutMs - (clock.now() - start));
49
+ }
50
+ function asError(error) {
51
+ if (error instanceof Error) {
52
+ return error;
53
+ }
54
+ return new Error(String(error));
55
+ }
56
+ async function sleepWithCancellation(clock, delayMs, signal) {
57
+ assertNotAborted(signal);
58
+ try {
59
+ await clock.sleep(delayMs, signal);
60
+ }
61
+ catch (error) {
62
+ if (signal?.aborted === true) {
63
+ throw new CancelledError("request cancelled during retry backoff");
64
+ }
65
+ throw error;
66
+ }
67
+ assertNotAborted(signal);
68
+ }
69
+ export async function executeWithRetry(operation, config, clock, signal) {
70
+ let lastError;
71
+ const start = clock.now();
72
+ for (let attempt = 1; attempt <= config.maxRetries + 1; attempt += 1) {
73
+ assertNotAborted(signal);
74
+ const attemptBudget = remainingBudgetMs(start, config.timeoutMs, clock);
75
+ if (attemptBudget <= 0) {
76
+ if (lastError) {
77
+ throw lastError;
78
+ }
79
+ throw new CancelledError("request timeout budget exhausted before provider call");
80
+ }
81
+ try {
82
+ return await operation(Number.isFinite(attemptBudget) ? Math.max(1, Math.floor(attemptBudget)) : undefined);
83
+ }
84
+ catch (error) {
85
+ lastError = asError(error);
86
+ const delay = attempt <= config.maxRetries
87
+ ? retryDelayMs(lastError, attempt, config.retryBaseDelayMs)
88
+ : null;
89
+ if (delay === null) {
90
+ throw lastError;
91
+ }
92
+ const remaining = remainingBudgetMs(start, config.timeoutMs, clock);
93
+ if (remaining <= 0) {
94
+ throw lastError;
95
+ }
96
+ await sleepWithCancellation(clock, Math.min(delay, remaining), signal);
97
+ }
98
+ }
99
+ throw lastError ?? new CancelledError("request timeout budget exhausted after retries");
100
+ }
101
+ export class CircuitBreaker {
102
+ modelId;
103
+ config;
104
+ clock;
105
+ state = "closed";
106
+ consecutiveFailures = 0;
107
+ openedAt = null;
108
+ probesRemaining = 0;
109
+ constructor(modelId, config, clock) {
110
+ this.modelId = modelId;
111
+ this.config = config;
112
+ this.clock = clock;
113
+ }
114
+ // Called before forwarding a request. Throws CircuitOpenError when the breaker is
115
+ // open and the cooldown has not elapsed; otherwise lets the call through (entering
116
+ // half-open as a side effect when cooldown has passed).
117
+ assertAllowed() {
118
+ if (this.state === "open") {
119
+ if (this.openedAt !== null && this.clock.now() - this.openedAt >= this.config.cooldownMs) {
120
+ this.state = "half-open";
121
+ this.probesRemaining = this.config.halfOpenProbes;
122
+ return;
123
+ }
124
+ throw new CircuitOpenError(`circuit open for model '${this.modelId}'`);
125
+ }
126
+ }
127
+ recordSuccess() {
128
+ if (this.state === "half-open") {
129
+ this.probesRemaining -= 1;
130
+ if (this.probesRemaining <= 0) {
131
+ this.close();
132
+ }
133
+ return;
134
+ }
135
+ this.consecutiveFailures = 0;
136
+ }
137
+ recordFailure() {
138
+ if (this.state === "half-open") {
139
+ this.open();
140
+ return;
141
+ }
142
+ this.consecutiveFailures += 1;
143
+ if (this.consecutiveFailures >= this.config.failureThreshold) {
144
+ this.open();
145
+ }
146
+ }
147
+ status(modelId) {
148
+ return {
149
+ modelId,
150
+ state: this.state,
151
+ consecutiveFailures: this.consecutiveFailures,
152
+ openedAt: this.openedAt,
153
+ };
154
+ }
155
+ open() {
156
+ this.state = "open";
157
+ this.openedAt = this.clock.now();
158
+ this.probesRemaining = 0;
159
+ }
160
+ close() {
161
+ this.state = "closed";
162
+ this.consecutiveFailures = 0;
163
+ this.openedAt = null;
164
+ this.probesRemaining = 0;
165
+ }
166
+ }
@@ -0,0 +1,108 @@
1
+ export type ModelKind = "chat" | "embedding" | "ocr-vision";
2
+ export type CostClass = "low" | "medium" | "high";
3
+ export type LatencyClass = "fast" | "standard" | "slow";
4
+ export interface ModelCapability {
5
+ readonly id: string;
6
+ readonly kind: ModelKind;
7
+ readonly contextWindow: number;
8
+ readonly maxOutputTokens: number;
9
+ readonly toolCalling: boolean;
10
+ readonly structuredOutput: boolean;
11
+ readonly streaming: boolean;
12
+ readonly costClass: CostClass;
13
+ readonly latencyClass: LatencyClass;
14
+ readonly throughputHint: string;
15
+ readonly preferredUseCases: readonly string[];
16
+ readonly knownLimitations: readonly string[];
17
+ }
18
+ export interface ModelProviderConfig {
19
+ readonly modelId: string;
20
+ readonly baseUrl: string;
21
+ readonly apiKey: string;
22
+ readonly timeoutMs: number;
23
+ readonly maxRetries: number;
24
+ readonly retryBaseDelayMs: number;
25
+ }
26
+ export interface CircuitBreakerConfig {
27
+ readonly failureThreshold: number;
28
+ readonly cooldownMs: number;
29
+ readonly halfOpenProbes: number;
30
+ }
31
+ export interface GatewayConfig {
32
+ readonly providers: readonly ModelProviderConfig[];
33
+ readonly circuitBreaker: CircuitBreakerConfig;
34
+ }
35
+ export interface ChatMessage {
36
+ readonly role: "system" | "user" | "assistant" | "tool";
37
+ readonly content: string;
38
+ readonly toolCallId?: string | undefined;
39
+ readonly toolCalls?: readonly NormalizedToolCall[] | undefined;
40
+ }
41
+ export interface ToolDefinition {
42
+ readonly name: string;
43
+ readonly description: string;
44
+ readonly parameters: Record<string, unknown>;
45
+ }
46
+ export type ResponseFormat = {
47
+ readonly type: "text";
48
+ } | {
49
+ readonly type: "json_schema";
50
+ readonly schema: Record<string, unknown>;
51
+ };
52
+ export interface GatewayRequest {
53
+ readonly modelId: string;
54
+ readonly messages: readonly ChatMessage[];
55
+ readonly tools?: readonly ToolDefinition[] | undefined;
56
+ readonly responseFormat?: ResponseFormat | undefined;
57
+ readonly stream?: boolean | undefined;
58
+ readonly cancellationSignal?: AbortSignal | undefined;
59
+ }
60
+ export interface NormalizedToolCall {
61
+ readonly id: string;
62
+ readonly name: string;
63
+ readonly arguments: Record<string, unknown>;
64
+ }
65
+ export interface UsageMetadata {
66
+ readonly requestId: string;
67
+ readonly promptTokens: number;
68
+ readonly completionTokens: number;
69
+ readonly latencyMs: number;
70
+ readonly costClass: CostClass;
71
+ }
72
+ export type FinishReason = "stop" | "tool_calls" | "length" | "content_filter" | "error" | "cancelled";
73
+ export interface NormalizedResponse {
74
+ readonly modelId: string;
75
+ readonly content: string;
76
+ readonly finishReason: FinishReason;
77
+ readonly toolCalls: readonly NormalizedToolCall[];
78
+ readonly structuredOutput: Record<string, unknown> | null;
79
+ readonly usage: UsageMetadata;
80
+ }
81
+ export interface StreamDelta {
82
+ readonly role?: "assistant" | undefined;
83
+ readonly contentDelta?: string | undefined;
84
+ readonly toolCallDelta?: Partial<NormalizedToolCall> | undefined;
85
+ readonly finishReason?: FinishReason | undefined;
86
+ readonly usage?: UsageMetadata | undefined;
87
+ }
88
+ export type StreamEvent = {
89
+ readonly type: "delta";
90
+ readonly delta: StreamDelta;
91
+ } | {
92
+ readonly type: "done";
93
+ readonly response: NormalizedResponse;
94
+ };
95
+ export interface ProviderAdapter {
96
+ readonly call: (request: GatewayRequest, config: ModelProviderConfig) => Promise<NormalizedResponse>;
97
+ }
98
+ export interface Clock {
99
+ readonly now: () => number;
100
+ readonly sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
101
+ }
102
+ export type CircuitState = "closed" | "open" | "half-open";
103
+ export interface CircuitBreakerStatus {
104
+ readonly modelId: string;
105
+ readonly state: CircuitState;
106
+ readonly consecutiveFailures: number;
107
+ readonly openedAt: number | null;
108
+ }
@@ -0,0 +1,2 @@
1
+ // All gateway interfaces and type aliases. No runtime code lives here.
2
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { GatewayRequest, NormalizedResponse, ToolDefinition } from "../gateway/types.js";
2
+ import type { ModelPort, ToolCallRequest, ToolCallResult, ToolPort } from "./ports.js";
3
+ export interface ChatModel {
4
+ readonly chat: (request: GatewayRequest) => Promise<NormalizedResponse>;
5
+ }
6
+ export declare class GatewayModelPort implements ModelPort {
7
+ private readonly gateway;
8
+ constructor(gateway: ChatModel);
9
+ call(request: GatewayRequest, signal: AbortSignal): Promise<NormalizedResponse>;
10
+ }
11
+ export interface RecordedToolCall {
12
+ readonly toolCallId: string;
13
+ readonly toolName: string;
14
+ readonly arguments: Record<string, unknown>;
15
+ }
16
+ export declare class DryRunToolPort implements ToolPort {
17
+ private readonly tools;
18
+ private readonly recorded;
19
+ constructor(tools?: readonly ToolDefinition[]);
20
+ execute(request: ToolCallRequest): Promise<ToolCallResult>;
21
+ listTools(): readonly ToolDefinition[];
22
+ calls(): readonly RecordedToolCall[];
23
+ }
@@ -0,0 +1,38 @@
1
+ // Production-side port adapters. GatewayModelPort wraps the ADR-0003 Gateway and
2
+ // propagates the run's AbortSignal as GatewayRequest.cancellationSignal. DryRunToolPort
3
+ // records tool calls without executing them — the Wave-1 no-op executor that keeps the
4
+ // CLI dry-run path free of any side effect (real executors land in issue #6).
5
+ import { CancelledError } from "../gateway/errors.js";
6
+ export class GatewayModelPort {
7
+ gateway;
8
+ constructor(gateway) {
9
+ this.gateway = gateway;
10
+ }
11
+ async call(request, signal) {
12
+ return this.gateway.chat({ ...request, cancellationSignal: signal });
13
+ }
14
+ }
15
+ export class DryRunToolPort {
16
+ tools;
17
+ recorded = [];
18
+ constructor(tools = []) {
19
+ this.tools = tools;
20
+ }
21
+ execute(request) {
22
+ if (request.signal.aborted) {
23
+ return Promise.reject(new CancelledError("tool execution aborted before start"));
24
+ }
25
+ this.recorded.push({
26
+ toolCallId: request.toolCallId,
27
+ toolName: request.toolName,
28
+ arguments: request.arguments,
29
+ });
30
+ return Promise.resolve({ toolCallId: request.toolCallId, output: "", durationMs: 0 });
31
+ }
32
+ listTools() {
33
+ return this.tools;
34
+ }
35
+ calls() {
36
+ return this.recorded;
37
+ }
38
+ }