@renseiai/agentfactory 0.8.7 → 0.8.9

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 (276) hide show
  1. package/dist/src/config/index.d.ts +1 -1
  2. package/dist/src/config/index.d.ts.map +1 -1
  3. package/dist/src/config/index.js +1 -1
  4. package/dist/src/config/repository-config.d.ts +37 -0
  5. package/dist/src/config/repository-config.d.ts.map +1 -1
  6. package/dist/src/config/repository-config.js +47 -0
  7. package/dist/src/config/repository-config.test.js +140 -1
  8. package/dist/src/governor/decision-engine.d.ts +3 -0
  9. package/dist/src/governor/decision-engine.d.ts.map +1 -1
  10. package/dist/src/governor/decision-engine.js +11 -0
  11. package/dist/src/governor/decision-engine.test.js +33 -0
  12. package/dist/src/governor/event-types.d.ts +18 -1
  13. package/dist/src/governor/event-types.d.ts.map +1 -1
  14. package/dist/src/governor/event-types.js +4 -0
  15. package/dist/src/governor/governor-types.d.ts +1 -1
  16. package/dist/src/governor/governor-types.d.ts.map +1 -1
  17. package/dist/src/governor/governor.d.ts +17 -1
  18. package/dist/src/governor/governor.d.ts.map +1 -1
  19. package/dist/src/governor/governor.js +112 -1
  20. package/dist/src/governor/governor.test.js +155 -0
  21. package/dist/src/index.d.ts +1 -0
  22. package/dist/src/index.d.ts.map +1 -1
  23. package/dist/src/index.js +1 -0
  24. package/dist/src/merge-queue/adapters/github-native.d.ts +22 -0
  25. package/dist/src/merge-queue/adapters/github-native.d.ts.map +1 -0
  26. package/dist/src/merge-queue/adapters/github-native.js +243 -0
  27. package/dist/src/merge-queue/adapters/github-native.test.d.ts +2 -0
  28. package/dist/src/merge-queue/adapters/github-native.test.d.ts.map +1 -0
  29. package/dist/src/merge-queue/adapters/github-native.test.js +384 -0
  30. package/dist/src/merge-queue/index.d.ts +18 -0
  31. package/dist/src/merge-queue/index.d.ts.map +1 -0
  32. package/dist/src/merge-queue/index.js +28 -0
  33. package/dist/src/merge-queue/merge-queue.integration.test.d.ts +2 -0
  34. package/dist/src/merge-queue/merge-queue.integration.test.d.ts.map +1 -0
  35. package/dist/src/merge-queue/merge-queue.integration.test.js +128 -0
  36. package/dist/src/merge-queue/types.d.ts +48 -0
  37. package/dist/src/merge-queue/types.d.ts.map +1 -0
  38. package/dist/src/merge-queue/types.js +8 -0
  39. package/dist/src/orchestrator/artifact-tracker.d.ts +93 -0
  40. package/dist/src/orchestrator/artifact-tracker.d.ts.map +1 -0
  41. package/dist/src/orchestrator/artifact-tracker.js +235 -0
  42. package/dist/src/orchestrator/artifact-tracker.test.d.ts +2 -0
  43. package/dist/src/orchestrator/artifact-tracker.test.d.ts.map +1 -0
  44. package/dist/src/orchestrator/artifact-tracker.test.js +189 -0
  45. package/dist/src/orchestrator/context-manager.d.ts +72 -0
  46. package/dist/src/orchestrator/context-manager.d.ts.map +1 -0
  47. package/dist/src/orchestrator/context-manager.js +120 -0
  48. package/dist/src/orchestrator/context-manager.test.d.ts +2 -0
  49. package/dist/src/orchestrator/context-manager.test.d.ts.map +1 -0
  50. package/dist/src/orchestrator/context-manager.test.js +137 -0
  51. package/dist/src/orchestrator/index.d.ts +8 -2
  52. package/dist/src/orchestrator/index.d.ts.map +1 -1
  53. package/dist/src/orchestrator/index.js +8 -1
  54. package/dist/src/orchestrator/issue-tracker-client.d.ts +4 -0
  55. package/dist/src/orchestrator/issue-tracker-client.d.ts.map +1 -1
  56. package/dist/src/orchestrator/orchestrator.d.ts +12 -0
  57. package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
  58. package/dist/src/orchestrator/orchestrator.js +282 -2
  59. package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -1
  60. package/dist/src/orchestrator/parse-work-result.js +6 -0
  61. package/dist/src/orchestrator/parse-work-result.test.js +19 -0
  62. package/dist/src/orchestrator/state-recovery.d.ts +21 -2
  63. package/dist/src/orchestrator/state-recovery.d.ts.map +1 -1
  64. package/dist/src/orchestrator/state-recovery.js +54 -2
  65. package/dist/src/orchestrator/state-recovery.test.js +106 -2
  66. package/dist/src/orchestrator/state-types.d.ts +62 -0
  67. package/dist/src/orchestrator/state-types.d.ts.map +1 -1
  68. package/dist/src/orchestrator/state-types.js +5 -1
  69. package/dist/src/orchestrator/summary-builder.d.ts +47 -0
  70. package/dist/src/orchestrator/summary-builder.d.ts.map +1 -0
  71. package/dist/src/orchestrator/summary-builder.js +240 -0
  72. package/dist/src/orchestrator/summary-builder.test.d.ts +2 -0
  73. package/dist/src/orchestrator/summary-builder.test.d.ts.map +1 -0
  74. package/dist/src/orchestrator/summary-builder.test.js +236 -0
  75. package/dist/src/orchestrator/types.d.ts +2 -0
  76. package/dist/src/orchestrator/types.d.ts.map +1 -1
  77. package/dist/src/orchestrator/work-types.d.ts +1 -1
  78. package/dist/src/orchestrator/work-types.d.ts.map +1 -1
  79. package/dist/src/providers/index.d.ts +64 -1
  80. package/dist/src/providers/index.d.ts.map +1 -1
  81. package/dist/src/providers/index.js +132 -1
  82. package/dist/src/providers/index.test.js +340 -2
  83. package/dist/src/routing/index.d.ts +7 -0
  84. package/dist/src/routing/index.d.ts.map +1 -0
  85. package/dist/src/routing/index.js +6 -0
  86. package/dist/src/routing/observation-recorder.d.ts +19 -0
  87. package/dist/src/routing/observation-recorder.d.ts.map +1 -0
  88. package/dist/src/routing/observation-recorder.js +73 -0
  89. package/dist/src/routing/observation-recorder.test.d.ts +2 -0
  90. package/dist/src/routing/observation-recorder.test.d.ts.map +1 -0
  91. package/dist/src/routing/observation-recorder.test.js +322 -0
  92. package/dist/src/routing/observation-store.d.ts +40 -0
  93. package/dist/src/routing/observation-store.d.ts.map +1 -0
  94. package/dist/src/routing/observation-store.js +1 -0
  95. package/dist/src/routing/observation-store.test.d.ts +2 -0
  96. package/dist/src/routing/observation-store.test.d.ts.map +1 -0
  97. package/dist/src/routing/observation-store.test.js +138 -0
  98. package/dist/src/routing/posterior-store.d.ts +12 -0
  99. package/dist/src/routing/posterior-store.d.ts.map +1 -0
  100. package/dist/src/routing/posterior-store.js +13 -0
  101. package/dist/src/routing/posterior-store.test.d.ts +2 -0
  102. package/dist/src/routing/posterior-store.test.d.ts.map +1 -0
  103. package/dist/src/routing/posterior-store.test.js +37 -0
  104. package/dist/src/routing/reward.d.ts +16 -0
  105. package/dist/src/routing/reward.d.ts.map +1 -0
  106. package/dist/src/routing/reward.js +29 -0
  107. package/dist/src/routing/reward.test.d.ts +2 -0
  108. package/dist/src/routing/reward.test.d.ts.map +1 -0
  109. package/dist/src/routing/reward.test.js +210 -0
  110. package/dist/src/routing/routing-engine.d.ts +20 -0
  111. package/dist/src/routing/routing-engine.d.ts.map +1 -0
  112. package/dist/src/routing/routing-engine.js +113 -0
  113. package/dist/src/routing/routing-engine.test.d.ts +2 -0
  114. package/dist/src/routing/routing-engine.test.d.ts.map +1 -0
  115. package/dist/src/routing/routing-engine.test.js +310 -0
  116. package/dist/src/routing/types.d.ts +157 -0
  117. package/dist/src/routing/types.d.ts.map +1 -0
  118. package/dist/src/routing/types.js +68 -0
  119. package/dist/src/routing/types.test.d.ts +2 -0
  120. package/dist/src/routing/types.test.d.ts.map +1 -0
  121. package/dist/src/routing/types.test.js +184 -0
  122. package/dist/src/templates/registry.test.js +2 -2
  123. package/dist/src/templates/types.d.ts +5 -0
  124. package/dist/src/templates/types.d.ts.map +1 -1
  125. package/dist/src/templates/types.js +3 -0
  126. package/dist/src/workflow/agent-cancellation.d.ts +37 -0
  127. package/dist/src/workflow/agent-cancellation.d.ts.map +1 -0
  128. package/dist/src/workflow/agent-cancellation.js +41 -0
  129. package/dist/src/workflow/agent-cancellation.test.d.ts +2 -0
  130. package/dist/src/workflow/agent-cancellation.test.d.ts.map +1 -0
  131. package/dist/src/workflow/agent-cancellation.test.js +86 -0
  132. package/dist/src/workflow/branching-router.d.ts +38 -0
  133. package/dist/src/workflow/branching-router.d.ts.map +1 -0
  134. package/dist/src/workflow/branching-router.js +52 -0
  135. package/dist/src/workflow/branching-router.test.d.ts +2 -0
  136. package/dist/src/workflow/branching-router.test.d.ts.map +1 -0
  137. package/dist/src/workflow/branching-router.test.js +209 -0
  138. package/dist/src/workflow/concurrency-semaphore.d.ts +21 -0
  139. package/dist/src/workflow/concurrency-semaphore.d.ts.map +1 -0
  140. package/dist/src/workflow/concurrency-semaphore.js +46 -0
  141. package/dist/src/workflow/concurrency-semaphore.test.d.ts +2 -0
  142. package/dist/src/workflow/concurrency-semaphore.test.d.ts.map +1 -0
  143. package/dist/src/workflow/concurrency-semaphore.test.js +183 -0
  144. package/dist/src/workflow/duration.d.ts +28 -0
  145. package/dist/src/workflow/duration.d.ts.map +1 -0
  146. package/dist/src/workflow/duration.js +57 -0
  147. package/dist/src/workflow/duration.test.d.ts +2 -0
  148. package/dist/src/workflow/duration.test.d.ts.map +1 -0
  149. package/dist/src/workflow/duration.test.js +74 -0
  150. package/dist/src/workflow/expression/ast.d.ts +53 -0
  151. package/dist/src/workflow/expression/ast.d.ts.map +1 -0
  152. package/dist/src/workflow/expression/ast.js +8 -0
  153. package/dist/src/workflow/expression/context.d.ts +40 -0
  154. package/dist/src/workflow/expression/context.d.ts.map +1 -0
  155. package/dist/src/workflow/expression/context.js +37 -0
  156. package/dist/src/workflow/expression/evaluator.d.ts +28 -0
  157. package/dist/src/workflow/expression/evaluator.d.ts.map +1 -0
  158. package/dist/src/workflow/expression/evaluator.js +165 -0
  159. package/dist/src/workflow/expression/evaluator.test.d.ts +2 -0
  160. package/dist/src/workflow/expression/evaluator.test.d.ts.map +1 -0
  161. package/dist/src/workflow/expression/evaluator.test.js +792 -0
  162. package/dist/src/workflow/expression/expression.test.d.ts +2 -0
  163. package/dist/src/workflow/expression/expression.test.d.ts.map +1 -0
  164. package/dist/src/workflow/expression/expression.test.js +516 -0
  165. package/dist/src/workflow/expression/helpers.d.ts +21 -0
  166. package/dist/src/workflow/expression/helpers.d.ts.map +1 -0
  167. package/dist/src/workflow/expression/helpers.js +56 -0
  168. package/dist/src/workflow/expression/index.d.ts +55 -0
  169. package/dist/src/workflow/expression/index.d.ts.map +1 -0
  170. package/dist/src/workflow/expression/index.js +71 -0
  171. package/dist/src/workflow/expression/lexer.d.ts +37 -0
  172. package/dist/src/workflow/expression/lexer.d.ts.map +1 -0
  173. package/dist/src/workflow/expression/lexer.js +166 -0
  174. package/dist/src/workflow/expression/parser.d.ts +23 -0
  175. package/dist/src/workflow/expression/parser.d.ts.map +1 -0
  176. package/dist/src/workflow/expression/parser.js +181 -0
  177. package/dist/src/workflow/gate-state.d.ts +115 -0
  178. package/dist/src/workflow/gate-state.d.ts.map +1 -0
  179. package/dist/src/workflow/gate-state.js +185 -0
  180. package/dist/src/workflow/gate-state.test.d.ts +2 -0
  181. package/dist/src/workflow/gate-state.test.d.ts.map +1 -0
  182. package/dist/src/workflow/gate-state.test.js +251 -0
  183. package/dist/src/workflow/gates/gate-evaluator.d.ts +119 -0
  184. package/dist/src/workflow/gates/gate-evaluator.d.ts.map +1 -0
  185. package/dist/src/workflow/gates/gate-evaluator.js +243 -0
  186. package/dist/src/workflow/gates/gate-evaluator.test.d.ts +2 -0
  187. package/dist/src/workflow/gates/gate-evaluator.test.d.ts.map +1 -0
  188. package/dist/src/workflow/gates/gate-evaluator.test.js +240 -0
  189. package/dist/src/workflow/gates/signal-gate.d.ts +114 -0
  190. package/dist/src/workflow/gates/signal-gate.d.ts.map +1 -0
  191. package/dist/src/workflow/gates/signal-gate.js +216 -0
  192. package/dist/src/workflow/gates/signal-gate.test.d.ts +2 -0
  193. package/dist/src/workflow/gates/signal-gate.test.d.ts.map +1 -0
  194. package/dist/src/workflow/gates/signal-gate.test.js +199 -0
  195. package/dist/src/workflow/gates/timeout-engine.d.ts +96 -0
  196. package/dist/src/workflow/gates/timeout-engine.d.ts.map +1 -0
  197. package/dist/src/workflow/gates/timeout-engine.js +162 -0
  198. package/dist/src/workflow/gates/timeout-engine.test.d.ts +2 -0
  199. package/dist/src/workflow/gates/timeout-engine.test.d.ts.map +1 -0
  200. package/dist/src/workflow/gates/timeout-engine.test.js +186 -0
  201. package/dist/src/workflow/gates/timer-gate.d.ts +125 -0
  202. package/dist/src/workflow/gates/timer-gate.d.ts.map +1 -0
  203. package/dist/src/workflow/gates/timer-gate.js +381 -0
  204. package/dist/src/workflow/gates/timer-gate.test.d.ts +2 -0
  205. package/dist/src/workflow/gates/timer-gate.test.d.ts.map +1 -0
  206. package/dist/src/workflow/gates/timer-gate.test.js +211 -0
  207. package/dist/src/workflow/gates/webhook-gate.d.ts +132 -0
  208. package/dist/src/workflow/gates/webhook-gate.d.ts.map +1 -0
  209. package/dist/src/workflow/gates/webhook-gate.js +216 -0
  210. package/dist/src/workflow/gates/webhook-gate.test.d.ts +2 -0
  211. package/dist/src/workflow/gates/webhook-gate.test.d.ts.map +1 -0
  212. package/dist/src/workflow/gates/webhook-gate.test.js +182 -0
  213. package/dist/src/workflow/index.d.ts +31 -3
  214. package/dist/src/workflow/index.d.ts.map +1 -1
  215. package/dist/src/workflow/index.js +20 -1
  216. package/dist/src/workflow/parallelism-executor.d.ts +25 -0
  217. package/dist/src/workflow/parallelism-executor.d.ts.map +1 -0
  218. package/dist/src/workflow/parallelism-executor.js +53 -0
  219. package/dist/src/workflow/parallelism-executor.test.d.ts +2 -0
  220. package/dist/src/workflow/parallelism-executor.test.d.ts.map +1 -0
  221. package/dist/src/workflow/parallelism-executor.test.js +191 -0
  222. package/dist/src/workflow/parallelism-types.d.ts +80 -0
  223. package/dist/src/workflow/parallelism-types.d.ts.map +1 -0
  224. package/dist/src/workflow/parallelism-types.js +8 -0
  225. package/dist/src/workflow/phase-context-injector.d.ts +29 -0
  226. package/dist/src/workflow/phase-context-injector.d.ts.map +1 -0
  227. package/dist/src/workflow/phase-context-injector.js +43 -0
  228. package/dist/src/workflow/phase-context-injector.test.d.ts +2 -0
  229. package/dist/src/workflow/phase-context-injector.test.d.ts.map +1 -0
  230. package/dist/src/workflow/phase-context-injector.test.js +123 -0
  231. package/dist/src/workflow/phase-output-collector.d.ts +39 -0
  232. package/dist/src/workflow/phase-output-collector.d.ts.map +1 -0
  233. package/dist/src/workflow/phase-output-collector.js +141 -0
  234. package/dist/src/workflow/phase-output-collector.test.d.ts +2 -0
  235. package/dist/src/workflow/phase-output-collector.test.d.ts.map +1 -0
  236. package/dist/src/workflow/phase-output-collector.test.js +179 -0
  237. package/dist/src/workflow/retry-resolver.d.ts +51 -0
  238. package/dist/src/workflow/retry-resolver.d.ts.map +1 -0
  239. package/dist/src/workflow/retry-resolver.js +70 -0
  240. package/dist/src/workflow/retry-resolver.test.d.ts +2 -0
  241. package/dist/src/workflow/retry-resolver.test.d.ts.map +1 -0
  242. package/dist/src/workflow/retry-resolver.test.js +149 -0
  243. package/dist/src/workflow/strategies/fan-in-strategy.d.ts +21 -0
  244. package/dist/src/workflow/strategies/fan-in-strategy.d.ts.map +1 -0
  245. package/dist/src/workflow/strategies/fan-in-strategy.js +92 -0
  246. package/dist/src/workflow/strategies/fan-in-strategy.test.d.ts +2 -0
  247. package/dist/src/workflow/strategies/fan-in-strategy.test.d.ts.map +1 -0
  248. package/dist/src/workflow/strategies/fan-in-strategy.test.js +182 -0
  249. package/dist/src/workflow/strategies/fan-out-strategy.d.ts +16 -0
  250. package/dist/src/workflow/strategies/fan-out-strategy.d.ts.map +1 -0
  251. package/dist/src/workflow/strategies/fan-out-strategy.js +47 -0
  252. package/dist/src/workflow/strategies/fan-out-strategy.test.d.ts +2 -0
  253. package/dist/src/workflow/strategies/fan-out-strategy.test.d.ts.map +1 -0
  254. package/dist/src/workflow/strategies/fan-out-strategy.test.js +97 -0
  255. package/dist/src/workflow/strategies/index.d.ts +4 -0
  256. package/dist/src/workflow/strategies/index.d.ts.map +1 -0
  257. package/dist/src/workflow/strategies/index.js +3 -0
  258. package/dist/src/workflow/strategies/race-strategy.d.ts +19 -0
  259. package/dist/src/workflow/strategies/race-strategy.d.ts.map +1 -0
  260. package/dist/src/workflow/strategies/race-strategy.js +92 -0
  261. package/dist/src/workflow/strategies/race-strategy.test.d.ts +2 -0
  262. package/dist/src/workflow/strategies/race-strategy.test.d.ts.map +1 -0
  263. package/dist/src/workflow/strategies/race-strategy.test.js +318 -0
  264. package/dist/src/workflow/transition-engine.d.ts +3 -1
  265. package/dist/src/workflow/transition-engine.d.ts.map +1 -1
  266. package/dist/src/workflow/transition-engine.js +26 -7
  267. package/dist/src/workflow/transition-engine.test.js +215 -11
  268. package/dist/src/workflow/workflow-registry.d.ts +46 -1
  269. package/dist/src/workflow/workflow-registry.d.ts.map +1 -1
  270. package/dist/src/workflow/workflow-registry.js +74 -0
  271. package/dist/src/workflow/workflow-registry.test.js +54 -0
  272. package/dist/src/workflow/workflow-types.d.ts +330 -12
  273. package/dist/src/workflow/workflow-types.d.ts.map +1 -1
  274. package/dist/src/workflow/workflow-types.js +100 -5
  275. package/dist/src/workflow/workflow-types.test.js +293 -2
  276. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { resolveProviderName, resolveProviderWithSource, extractProviderFromLabels, extractProviderFromMention, PROVIDER_ALIASES, isValidProviderName, } from './index.js';
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { resolveProviderName, resolveProviderWithSource, resolveProviderWithSourceAsync, resolveProviderNameAsync, extractProviderFromLabels, extractProviderFromMention, PROVIDER_ALIASES, isValidProviderName, } from './index.js';
3
3
  describe('extractProviderFromLabels', () => {
4
4
  it('extracts provider from "provider:<name>" label', () => {
5
5
  expect(extractProviderFromLabels(['Bug', 'provider:codex', 'Feature'])).toBe('codex');
@@ -223,3 +223,341 @@ describe('isValidProviderName', () => {
223
223
  expect(isValidProviderName('opus')).toBe(false); // alias, not a provider name
224
224
  });
225
225
  });
226
+ // ---------------------------------------------------------------------------
227
+ // Async provider resolution (with MAB routing)
228
+ // ---------------------------------------------------------------------------
229
+ // Mock the routing engine module
230
+ vi.mock('../routing/routing-engine.js', () => ({
231
+ selectProvider: vi.fn(),
232
+ }));
233
+ // Mock the logger to suppress warnings in tests
234
+ vi.mock('../logger.js', () => ({
235
+ logger: {
236
+ info: vi.fn(),
237
+ warn: vi.fn(),
238
+ error: vi.fn(),
239
+ debug: vi.fn(),
240
+ },
241
+ }));
242
+ function createMockPosteriorStore() {
243
+ return {
244
+ getPosterior: vi.fn(),
245
+ updatePosterior: vi.fn(),
246
+ getAllPosteriors: vi.fn(),
247
+ resetPosterior: vi.fn(),
248
+ };
249
+ }
250
+ function createMockRoutingConfig(overrides) {
251
+ return {
252
+ enabled: true,
253
+ explorationRate: 0.1,
254
+ windowSize: 100,
255
+ discountFactor: 0.99,
256
+ minObservationsForExploit: 5,
257
+ changeDetectionThreshold: 0.2,
258
+ ...overrides,
259
+ };
260
+ }
261
+ function createHighConfidenceDecision(provider = 'codex') {
262
+ return {
263
+ selectedProvider: provider,
264
+ confidence: 0.85,
265
+ expectedReward: 0.9,
266
+ source: 'mab-routing',
267
+ alternatives: [],
268
+ };
269
+ }
270
+ function createLowConfidenceDecision() {
271
+ return {
272
+ selectedProvider: 'codex',
273
+ confidence: 0.2,
274
+ expectedReward: 0.5,
275
+ explorationReason: 'uncertainty',
276
+ source: 'mab-routing',
277
+ alternatives: [],
278
+ };
279
+ }
280
+ describe('resolveProviderWithSourceAsync — full cascade with MAB routing', () => {
281
+ const envBackup = {};
282
+ beforeEach(async () => {
283
+ // Save and clear relevant env vars
284
+ for (const key of ['AGENT_PROVIDER', 'AGENT_PROVIDER_QA', 'AGENT_PROVIDER_DEVELOPMENT', 'AGENT_PROVIDER_SOCIAL', 'AGENT_PROVIDER_AGENT']) {
285
+ envBackup[key] = process.env[key];
286
+ delete process.env[key];
287
+ }
288
+ // Reset the routing engine mock
289
+ const { selectProvider } = await import('../routing/routing-engine.js');
290
+ vi.mocked(selectProvider).mockReset();
291
+ });
292
+ afterEach(() => {
293
+ // Restore env vars
294
+ for (const [key, value] of Object.entries(envBackup)) {
295
+ if (value === undefined) {
296
+ delete process.env[key];
297
+ }
298
+ else {
299
+ process.env[key] = value;
300
+ }
301
+ }
302
+ });
303
+ it('10. defaults to claude when no context provided', async () => {
304
+ const result = await resolveProviderWithSourceAsync();
305
+ expect(result).toEqual({ name: 'claude', source: 'default' });
306
+ });
307
+ it('1. label overrides everything including MAB', async () => {
308
+ const { selectProvider } = await import('../routing/routing-engine.js');
309
+ vi.mocked(selectProvider).mockResolvedValue(createHighConfidenceDecision('amp'));
310
+ process.env.AGENT_PROVIDER = 'amp';
311
+ process.env.AGENT_PROVIDER_QA = 'amp';
312
+ const result = await resolveProviderWithSourceAsync({
313
+ labels: ['provider:codex'],
314
+ mentionContext: 'use amp',
315
+ workType: 'qa',
316
+ project: 'Social',
317
+ configProviders: {
318
+ default: 'amp',
319
+ byWorkType: { qa: 'amp' },
320
+ byProject: { Social: 'amp' },
321
+ },
322
+ routingContext: {
323
+ posteriorStore: createMockPosteriorStore(),
324
+ routingConfig: createMockRoutingConfig(),
325
+ },
326
+ });
327
+ expect(result.name).toBe('codex');
328
+ expect(result.source).toContain('label');
329
+ });
330
+ it('2. mention overrides MAB, config, and env', async () => {
331
+ const { selectProvider } = await import('../routing/routing-engine.js');
332
+ vi.mocked(selectProvider).mockResolvedValue(createHighConfidenceDecision('amp'));
333
+ process.env.AGENT_PROVIDER = 'amp';
334
+ const result = await resolveProviderWithSourceAsync({
335
+ mentionContext: 'use codex',
336
+ workType: 'qa',
337
+ configProviders: { byWorkType: { qa: 'amp' } },
338
+ routingContext: {
339
+ posteriorStore: createMockPosteriorStore(),
340
+ routingConfig: createMockRoutingConfig(),
341
+ },
342
+ });
343
+ expect(result.name).toBe('codex');
344
+ expect(result.source).toContain('mention');
345
+ });
346
+ it('3. config byWorkType overrides MAB, env, and config defaults', async () => {
347
+ const { selectProvider } = await import('../routing/routing-engine.js');
348
+ vi.mocked(selectProvider).mockResolvedValue(createHighConfidenceDecision('amp'));
349
+ process.env.AGENT_PROVIDER_QA = 'amp';
350
+ const result = await resolveProviderWithSourceAsync({
351
+ workType: 'qa',
352
+ configProviders: { byWorkType: { qa: 'codex' }, default: 'amp' },
353
+ routingContext: {
354
+ posteriorStore: createMockPosteriorStore(),
355
+ routingConfig: createMockRoutingConfig(),
356
+ },
357
+ });
358
+ expect(result.name).toBe('codex');
359
+ expect(result.source).toContain('config providers.byWorkType.qa');
360
+ });
361
+ it('4. config byProject overrides MAB and env vars', async () => {
362
+ const { selectProvider } = await import('../routing/routing-engine.js');
363
+ vi.mocked(selectProvider).mockResolvedValue(createHighConfidenceDecision('amp'));
364
+ process.env.AGENT_PROVIDER_SOCIAL = 'amp';
365
+ const result = await resolveProviderWithSourceAsync({
366
+ project: 'Social',
367
+ workType: 'qa',
368
+ configProviders: { byProject: { Social: 'codex' } },
369
+ routingContext: {
370
+ posteriorStore: createMockPosteriorStore(),
371
+ routingConfig: createMockRoutingConfig(),
372
+ },
373
+ });
374
+ expect(result.name).toBe('codex');
375
+ expect(result.source).toContain('config providers.byProject.Social');
376
+ });
377
+ it('5. MAB routing selects provider when enabled with high confidence', async () => {
378
+ const { selectProvider } = await import('../routing/routing-engine.js');
379
+ vi.mocked(selectProvider).mockResolvedValue(createHighConfidenceDecision('codex'));
380
+ process.env.AGENT_PROVIDER_QA = 'amp';
381
+ process.env.AGENT_PROVIDER = 'amp';
382
+ const result = await resolveProviderWithSourceAsync({
383
+ workType: 'qa',
384
+ routingContext: {
385
+ posteriorStore: createMockPosteriorStore(),
386
+ routingConfig: createMockRoutingConfig(),
387
+ },
388
+ });
389
+ expect(result.name).toBe('codex');
390
+ expect(result.source).toBe('mab-routing');
391
+ });
392
+ it('5. MAB routing passes available providers and config to selectProvider', async () => {
393
+ const { selectProvider } = await import('../routing/routing-engine.js');
394
+ vi.mocked(selectProvider).mockResolvedValue(createHighConfidenceDecision('amp'));
395
+ const mockStore = createMockPosteriorStore();
396
+ const mockConfig = createMockRoutingConfig({ explorationRate: 0.05 });
397
+ const result = await resolveProviderWithSourceAsync({
398
+ workType: 'development',
399
+ routingContext: {
400
+ posteriorStore: mockStore,
401
+ routingConfig: mockConfig,
402
+ availableProviders: ['claude', 'codex', 'amp'],
403
+ },
404
+ });
405
+ expect(result.name).toBe('amp');
406
+ expect(result.source).toBe('mab-routing');
407
+ expect(vi.mocked(selectProvider)).toHaveBeenCalledWith(mockStore, 'development', ['claude', 'codex', 'amp'], mockConfig);
408
+ });
409
+ it('5. low-confidence MAB decisions fall through to env var', async () => {
410
+ const { selectProvider } = await import('../routing/routing-engine.js');
411
+ vi.mocked(selectProvider).mockResolvedValue(createLowConfidenceDecision());
412
+ process.env.AGENT_PROVIDER_QA = 'amp';
413
+ const result = await resolveProviderWithSourceAsync({
414
+ workType: 'qa',
415
+ routingContext: {
416
+ posteriorStore: createMockPosteriorStore(),
417
+ routingConfig: createMockRoutingConfig(),
418
+ },
419
+ });
420
+ expect(result.name).toBe('amp');
421
+ expect(result.source).toBe('env AGENT_PROVIDER_QA');
422
+ });
423
+ it('5. MAB errors gracefully fall through to env var', async () => {
424
+ const { selectProvider } = await import('../routing/routing-engine.js');
425
+ vi.mocked(selectProvider).mockRejectedValue(new Error('Redis connection failed'));
426
+ process.env.AGENT_PROVIDER_QA = 'amp';
427
+ const result = await resolveProviderWithSourceAsync({
428
+ workType: 'qa',
429
+ routingContext: {
430
+ posteriorStore: createMockPosteriorStore(),
431
+ routingConfig: createMockRoutingConfig(),
432
+ },
433
+ });
434
+ expect(result.name).toBe('amp');
435
+ expect(result.source).toBe('env AGENT_PROVIDER_QA');
436
+ });
437
+ it('5. MAB tier skipped when routing disabled (default)', async () => {
438
+ const { selectProvider } = await import('../routing/routing-engine.js');
439
+ process.env.AGENT_PROVIDER_QA = 'amp';
440
+ const result = await resolveProviderWithSourceAsync({
441
+ workType: 'qa',
442
+ routingContext: {
443
+ posteriorStore: createMockPosteriorStore(),
444
+ routingConfig: createMockRoutingConfig({ enabled: false }),
445
+ },
446
+ });
447
+ expect(result.name).toBe('amp');
448
+ expect(result.source).toBe('env AGENT_PROVIDER_QA');
449
+ // selectProvider should NOT have been called
450
+ expect(vi.mocked(selectProvider)).not.toHaveBeenCalled();
451
+ });
452
+ it('5. MAB tier skipped when no posteriorStore provided', async () => {
453
+ const { selectProvider } = await import('../routing/routing-engine.js');
454
+ process.env.AGENT_PROVIDER_QA = 'amp';
455
+ const result = await resolveProviderWithSourceAsync({
456
+ workType: 'qa',
457
+ routingContext: {
458
+ routingConfig: createMockRoutingConfig(),
459
+ },
460
+ });
461
+ expect(result.name).toBe('amp');
462
+ expect(result.source).toBe('env AGENT_PROVIDER_QA');
463
+ expect(vi.mocked(selectProvider)).not.toHaveBeenCalled();
464
+ });
465
+ it('5. MAB tier skipped when no workType provided', async () => {
466
+ const { selectProvider } = await import('../routing/routing-engine.js');
467
+ const result = await resolveProviderWithSourceAsync({
468
+ routingContext: {
469
+ posteriorStore: createMockPosteriorStore(),
470
+ routingConfig: createMockRoutingConfig(),
471
+ },
472
+ });
473
+ // Should fall through to hardcoded default
474
+ expect(result.name).toBe('claude');
475
+ expect(result.source).toBe('default');
476
+ expect(vi.mocked(selectProvider)).not.toHaveBeenCalled();
477
+ });
478
+ it('5. MAB tier skipped when no routingContext provided', async () => {
479
+ const { selectProvider } = await import('../routing/routing-engine.js');
480
+ process.env.AGENT_PROVIDER_QA = 'amp';
481
+ const result = await resolveProviderWithSourceAsync({
482
+ workType: 'qa',
483
+ });
484
+ expect(result.name).toBe('amp');
485
+ expect(result.source).toBe('env AGENT_PROVIDER_QA');
486
+ expect(vi.mocked(selectProvider)).not.toHaveBeenCalled();
487
+ });
488
+ it('6. env AGENT_PROVIDER_{WORKTYPE} overrides env project and defaults', async () => {
489
+ process.env.AGENT_PROVIDER_QA = 'codex';
490
+ process.env.AGENT_PROVIDER_SOCIAL = 'amp';
491
+ process.env.AGENT_PROVIDER = 'amp';
492
+ const result = await resolveProviderWithSourceAsync({
493
+ workType: 'qa',
494
+ project: 'Social',
495
+ });
496
+ expect(result.name).toBe('codex');
497
+ expect(result.source).toBe('env AGENT_PROVIDER_QA');
498
+ });
499
+ it('7. env AGENT_PROVIDER_{PROJECT} overrides global default', async () => {
500
+ process.env.AGENT_PROVIDER_SOCIAL = 'codex';
501
+ process.env.AGENT_PROVIDER = 'amp';
502
+ const result = await resolveProviderWithSourceAsync({
503
+ project: 'Social',
504
+ });
505
+ expect(result.name).toBe('codex');
506
+ expect(result.source).toBe('env AGENT_PROVIDER_SOCIAL');
507
+ });
508
+ it('8. config providers.default overrides env AGENT_PROVIDER', async () => {
509
+ process.env.AGENT_PROVIDER = 'amp';
510
+ const result = await resolveProviderWithSourceAsync({
511
+ configProviders: { default: 'codex' },
512
+ });
513
+ expect(result.name).toBe('codex');
514
+ expect(result.source).toBe('config providers.default');
515
+ });
516
+ it('9. env AGENT_PROVIDER overrides hardcoded default', async () => {
517
+ process.env.AGENT_PROVIDER = 'codex';
518
+ const result = await resolveProviderWithSourceAsync();
519
+ expect(result.name).toBe('codex');
520
+ expect(result.source).toBe('env AGENT_PROVIDER');
521
+ });
522
+ });
523
+ describe('resolveProviderNameAsync — backwards compatibility', () => {
524
+ const envBackup = {};
525
+ beforeEach(() => {
526
+ for (const key of ['AGENT_PROVIDER', 'AGENT_PROVIDER_QA', 'AGENT_PROVIDER_SOCIAL']) {
527
+ envBackup[key] = process.env[key];
528
+ delete process.env[key];
529
+ }
530
+ });
531
+ afterEach(() => {
532
+ for (const [key, value] of Object.entries(envBackup)) {
533
+ if (value === undefined) {
534
+ delete process.env[key];
535
+ }
536
+ else {
537
+ process.env[key] = value;
538
+ }
539
+ }
540
+ });
541
+ it('returns claude by default', async () => {
542
+ expect(await resolveProviderNameAsync()).toBe('claude');
543
+ });
544
+ it('respects { project, workType } shape', async () => {
545
+ process.env.AGENT_PROVIDER_QA = 'codex';
546
+ expect(await resolveProviderNameAsync({ workType: 'qa' })).toBe('codex');
547
+ });
548
+ it('accepts labels in context', async () => {
549
+ expect(await resolveProviderNameAsync({ labels: ['provider:codex'] })).toBe('codex');
550
+ });
551
+ it('uses MAB routing when configured', async () => {
552
+ const { selectProvider } = await import('../routing/routing-engine.js');
553
+ vi.mocked(selectProvider).mockResolvedValue(createHighConfidenceDecision('amp'));
554
+ const result = await resolveProviderNameAsync({
555
+ workType: 'qa',
556
+ routingContext: {
557
+ posteriorStore: createMockPosteriorStore(),
558
+ routingConfig: createMockRoutingConfig(),
559
+ },
560
+ });
561
+ expect(result).toBe('amp');
562
+ });
563
+ });
@@ -0,0 +1,7 @@
1
+ export * from './types.js';
2
+ export * from './observation-store.js';
3
+ export * from './reward.js';
4
+ export * from './posterior-store.js';
5
+ export * from './observation-recorder.js';
6
+ export * from './routing-engine.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routing/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,wBAAwB,CAAA;AACtC,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA;AACpC,cAAc,2BAA2B,CAAA;AACzC,cAAc,qBAAqB,CAAA"}
@@ -0,0 +1,6 @@
1
+ export * from './types.js';
2
+ export * from './observation-store.js';
3
+ export * from './reward.js';
4
+ export * from './posterior-store.js';
5
+ export * from './observation-recorder.js';
6
+ export * from './routing-engine.js';
@@ -0,0 +1,19 @@
1
+ import type { AgentProcess, OrchestratorEvents } from '../orchestrator/types.js';
2
+ import type { RoutingObservation } from './types.js';
3
+ import type { ObservationStore } from './observation-store.js';
4
+ import type { PosteriorStore } from './posterior-store.js';
5
+ export interface RoutingRecorderOptions {
6
+ observationStore: ObservationStore;
7
+ posteriorStore: PosteriorStore;
8
+ }
9
+ /**
10
+ * Create a routing observation from an AgentProcess.
11
+ * Returns null if provider or workType is missing (can't record without them).
12
+ */
13
+ export declare function buildObservation(agent: AgentProcess): RoutingObservation | null;
14
+ /**
15
+ * Wrap OrchestratorEvents to record routing observations on agent completion.
16
+ * Best-effort: failures are logged but don't block agent completion flow.
17
+ */
18
+ export declare function wrapEventsWithRecorder(events: OrchestratorEvents, options: RoutingRecorderOptions): OrchestratorEvents;
19
+ //# sourceMappingURL=observation-recorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observation-recorder.d.ts","sourceRoot":"","sources":["../../../src/routing/observation-recorder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAG1D,MAAM,WAAW,sBAAsB;IACrC,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,cAAc,EAAE,cAAc,CAAA;CAC/B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,kBAAkB,GAAG,IAAI,CA4B/E;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,EAAE,sBAAsB,GAC9B,kBAAkB,CAqCpB"}
@@ -0,0 +1,73 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { computeReward, extractRewardFromProcess } from './reward.js';
3
+ /**
4
+ * Create a routing observation from an AgentProcess.
5
+ * Returns null if provider or workType is missing (can't record without them).
6
+ */
7
+ export function buildObservation(agent) {
8
+ if (!agent.providerName || !agent.workType) {
9
+ return null;
10
+ }
11
+ const rewardInput = extractRewardFromProcess(agent);
12
+ const reward = computeReward(rewardInput);
13
+ return {
14
+ id: randomUUID(),
15
+ provider: agent.providerName,
16
+ workType: agent.workType,
17
+ project: undefined, // will be populated when integrated
18
+ issueIdentifier: agent.identifier,
19
+ sessionId: agent.sessionId ?? '',
20
+ reward,
21
+ taskCompleted: agent.status === 'completed',
22
+ prCreated: agent.pullRequestUrl !== undefined,
23
+ qaResult: agent.workResult ?? 'unknown',
24
+ totalCostUsd: agent.totalCostUsd ?? 0,
25
+ wallClockMs: agent.completedAt && agent.startedAt
26
+ ? agent.completedAt.getTime() - agent.startedAt.getTime()
27
+ : 0,
28
+ timestamp: Date.now(),
29
+ confidence: 0, // populated once routing engine is active
30
+ explorationReason: undefined,
31
+ };
32
+ }
33
+ /**
34
+ * Wrap OrchestratorEvents to record routing observations on agent completion.
35
+ * Best-effort: failures are logged but don't block agent completion flow.
36
+ */
37
+ export function wrapEventsWithRecorder(events, options) {
38
+ const { observationStore, posteriorStore } = options;
39
+ async function recordObservation(agent) {
40
+ const obs = buildObservation(agent);
41
+ if (!obs)
42
+ return;
43
+ try {
44
+ await observationStore.recordObservation(obs);
45
+ await posteriorStore.updatePosterior(obs.provider, obs.workType, obs.reward);
46
+ }
47
+ catch (error) {
48
+ console.error('[routing] Failed to record observation', {
49
+ error: error instanceof Error ? error.message : String(error),
50
+ identifier: agent.identifier,
51
+ });
52
+ }
53
+ }
54
+ return {
55
+ ...events,
56
+ onAgentComplete: (agent) => {
57
+ events.onAgentComplete?.(agent);
58
+ void recordObservation(agent);
59
+ },
60
+ onAgentStopped: (agent) => {
61
+ events.onAgentStopped?.(agent);
62
+ void recordObservation(agent);
63
+ },
64
+ onAgentError: (agent, error) => {
65
+ events.onAgentError?.(agent, error);
66
+ void recordObservation(agent);
67
+ },
68
+ onAgentIncomplete: (agent) => {
69
+ events.onAgentIncomplete?.(agent);
70
+ void recordObservation(agent);
71
+ },
72
+ };
73
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=observation-recorder.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observation-recorder.test.d.ts","sourceRoot":"","sources":["../../../src/routing/observation-recorder.test.ts"],"names":[],"mappings":""}