@methodts/runtime 0.1.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 (537) hide show
  1. package/dist/__fixtures__/executor-fixtures.d.ts +10 -0
  2. package/dist/__fixtures__/executor-fixtures.d.ts.map +1 -0
  3. package/dist/__fixtures__/executor-fixtures.js +36 -0
  4. package/dist/__fixtures__/executor-fixtures.js.map +1 -0
  5. package/dist/architecture.test.d.ts +2 -0
  6. package/dist/architecture.test.d.ts.map +1 -0
  7. package/dist/architecture.test.js +143 -0
  8. package/dist/architecture.test.js.map +1 -0
  9. package/dist/config/cost-governor-config.d.ts +40 -0
  10. package/dist/config/cost-governor-config.d.ts.map +1 -0
  11. package/dist/config/cost-governor-config.js +48 -0
  12. package/dist/config/cost-governor-config.js.map +1 -0
  13. package/dist/config/index.d.ts +7 -0
  14. package/dist/config/index.d.ts.map +1 -0
  15. package/dist/config/index.js +6 -0
  16. package/dist/config/index.js.map +1 -0
  17. package/dist/config/sessions-config.d.ts +29 -0
  18. package/dist/config/sessions-config.d.ts.map +1 -0
  19. package/dist/config/sessions-config.js +37 -0
  20. package/dist/config/sessions-config.js.map +1 -0
  21. package/dist/config/strategies-config.d.ts +38 -0
  22. package/dist/config/strategies-config.d.ts.map +1 -0
  23. package/dist/config/strategies-config.js +41 -0
  24. package/dist/config/strategies-config.js.map +1 -0
  25. package/dist/cost-governor/backpressure-queue.d.ts +20 -0
  26. package/dist/cost-governor/backpressure-queue.d.ts.map +1 -0
  27. package/dist/cost-governor/backpressure-queue.js +78 -0
  28. package/dist/cost-governor/backpressure-queue.js.map +1 -0
  29. package/dist/cost-governor/backpressure-queue.test.d.ts +2 -0
  30. package/dist/cost-governor/backpressure-queue.test.d.ts.map +1 -0
  31. package/dist/cost-governor/backpressure-queue.test.js +48 -0
  32. package/dist/cost-governor/backpressure-queue.test.js.map +1 -0
  33. package/dist/cost-governor/cost-events.d.ts +65 -0
  34. package/dist/cost-governor/cost-events.d.ts.map +1 -0
  35. package/dist/cost-governor/cost-events.js +48 -0
  36. package/dist/cost-governor/cost-events.js.map +1 -0
  37. package/dist/cost-governor/cost-governor-app-id.test.d.ts +19 -0
  38. package/dist/cost-governor/cost-governor-app-id.test.d.ts.map +1 -0
  39. package/dist/cost-governor/cost-governor-app-id.test.js +201 -0
  40. package/dist/cost-governor/cost-governor-app-id.test.js.map +1 -0
  41. package/dist/cost-governor/cost-oracle-impl.d.ts +19 -0
  42. package/dist/cost-governor/cost-oracle-impl.d.ts.map +1 -0
  43. package/dist/cost-governor/cost-oracle-impl.js +52 -0
  44. package/dist/cost-governor/cost-oracle-impl.js.map +1 -0
  45. package/dist/cost-governor/estimator.d.ts +22 -0
  46. package/dist/cost-governor/estimator.d.ts.map +1 -0
  47. package/dist/cost-governor/estimator.js +119 -0
  48. package/dist/cost-governor/estimator.js.map +1 -0
  49. package/dist/cost-governor/estimator.test.d.ts +2 -0
  50. package/dist/cost-governor/estimator.test.d.ts.map +1 -0
  51. package/dist/cost-governor/estimator.test.js +141 -0
  52. package/dist/cost-governor/estimator.test.js.map +1 -0
  53. package/dist/cost-governor/index.d.ts +75 -0
  54. package/dist/cost-governor/index.d.ts.map +1 -0
  55. package/dist/cost-governor/index.js +120 -0
  56. package/dist/cost-governor/index.js.map +1 -0
  57. package/dist/cost-governor/observations-store.d.ts +49 -0
  58. package/dist/cost-governor/observations-store.d.ts.map +1 -0
  59. package/dist/cost-governor/observations-store.js +179 -0
  60. package/dist/cost-governor/observations-store.js.map +1 -0
  61. package/dist/cost-governor/observations-store.test.d.ts +2 -0
  62. package/dist/cost-governor/observations-store.test.d.ts.map +1 -0
  63. package/dist/cost-governor/observations-store.test.js +191 -0
  64. package/dist/cost-governor/observations-store.test.js.map +1 -0
  65. package/dist/cost-governor/percentile.d.ts +17 -0
  66. package/dist/cost-governor/percentile.d.ts.map +1 -0
  67. package/dist/cost-governor/percentile.js +33 -0
  68. package/dist/cost-governor/percentile.js.map +1 -0
  69. package/dist/cost-governor/percentile.test.d.ts +2 -0
  70. package/dist/cost-governor/percentile.test.d.ts.map +1 -0
  71. package/dist/cost-governor/percentile.test.js +46 -0
  72. package/dist/cost-governor/percentile.test.js.map +1 -0
  73. package/dist/cost-governor/rate-governor-impl.d.ts +73 -0
  74. package/dist/cost-governor/rate-governor-impl.d.ts.map +1 -0
  75. package/dist/cost-governor/rate-governor-impl.js +148 -0
  76. package/dist/cost-governor/rate-governor-impl.js.map +1 -0
  77. package/dist/cost-governor/signature-builder.d.ts +22 -0
  78. package/dist/cost-governor/signature-builder.d.ts.map +1 -0
  79. package/dist/cost-governor/signature-builder.js +43 -0
  80. package/dist/cost-governor/signature-builder.js.map +1 -0
  81. package/dist/cost-governor/signature-builder.test.d.ts +2 -0
  82. package/dist/cost-governor/signature-builder.test.d.ts.map +1 -0
  83. package/dist/cost-governor/signature-builder.test.js +58 -0
  84. package/dist/cost-governor/signature-builder.test.js.map +1 -0
  85. package/dist/cost-governor/token-bucket.d.ts +57 -0
  86. package/dist/cost-governor/token-bucket.d.ts.map +1 -0
  87. package/dist/cost-governor/token-bucket.js +109 -0
  88. package/dist/cost-governor/token-bucket.js.map +1 -0
  89. package/dist/cost-governor/token-bucket.test.d.ts +2 -0
  90. package/dist/cost-governor/token-bucket.test.d.ts.map +1 -0
  91. package/dist/cost-governor/token-bucket.test.js +67 -0
  92. package/dist/cost-governor/token-bucket.test.js.map +1 -0
  93. package/dist/dlq/cortex-dlq-observer.d.ts +22 -0
  94. package/dist/dlq/cortex-dlq-observer.d.ts.map +1 -0
  95. package/dist/dlq/cortex-dlq-observer.js +29 -0
  96. package/dist/dlq/cortex-dlq-observer.js.map +1 -0
  97. package/dist/dlq/dlq-observer.test.d.ts +8 -0
  98. package/dist/dlq/dlq-observer.test.d.ts.map +1 -0
  99. package/dist/dlq/dlq-observer.test.js +103 -0
  100. package/dist/dlq/dlq-observer.test.js.map +1 -0
  101. package/dist/dlq/index.d.ts +6 -0
  102. package/dist/dlq/index.d.ts.map +1 -0
  103. package/dist/dlq/index.js +6 -0
  104. package/dist/dlq/index.js.map +1 -0
  105. package/dist/event-bus/adapters.d.ts +50 -0
  106. package/dist/event-bus/adapters.d.ts.map +1 -0
  107. package/dist/event-bus/adapters.js +51 -0
  108. package/dist/event-bus/adapters.js.map +1 -0
  109. package/dist/event-bus/adapters.test.d.ts +5 -0
  110. package/dist/event-bus/adapters.test.d.ts.map +1 -0
  111. package/dist/event-bus/adapters.test.js +73 -0
  112. package/dist/event-bus/adapters.test.js.map +1 -0
  113. package/dist/event-bus/agent-event-adapter.d.ts +22 -0
  114. package/dist/event-bus/agent-event-adapter.d.ts.map +1 -0
  115. package/dist/event-bus/agent-event-adapter.js +49 -0
  116. package/dist/event-bus/agent-event-adapter.js.map +1 -0
  117. package/dist/event-bus/agent-event-adapter.test.d.ts +5 -0
  118. package/dist/event-bus/agent-event-adapter.test.d.ts.map +1 -0
  119. package/dist/event-bus/agent-event-adapter.test.js +170 -0
  120. package/dist/event-bus/agent-event-adapter.test.js.map +1 -0
  121. package/dist/event-bus/channel-sink.d.ts +71 -0
  122. package/dist/event-bus/channel-sink.d.ts.map +1 -0
  123. package/dist/event-bus/channel-sink.js +159 -0
  124. package/dist/event-bus/channel-sink.js.map +1 -0
  125. package/dist/event-bus/channel-sink.test.d.ts +5 -0
  126. package/dist/event-bus/channel-sink.test.d.ts.map +1 -0
  127. package/dist/event-bus/channel-sink.test.js +234 -0
  128. package/dist/event-bus/channel-sink.test.js.map +1 -0
  129. package/dist/event-bus/event-types.snapshot.test.d.ts +27 -0
  130. package/dist/event-bus/event-types.snapshot.test.d.ts.map +1 -0
  131. package/dist/event-bus/event-types.snapshot.test.js +165 -0
  132. package/dist/event-bus/event-types.snapshot.test.js.map +1 -0
  133. package/dist/event-bus/genesis-sink.d.ts +55 -0
  134. package/dist/event-bus/genesis-sink.d.ts.map +1 -0
  135. package/dist/event-bus/genesis-sink.js +141 -0
  136. package/dist/event-bus/genesis-sink.js.map +1 -0
  137. package/dist/event-bus/genesis-sink.test.d.ts +5 -0
  138. package/dist/event-bus/genesis-sink.test.d.ts.map +1 -0
  139. package/dist/event-bus/genesis-sink.test.js +160 -0
  140. package/dist/event-bus/genesis-sink.test.js.map +1 -0
  141. package/dist/event-bus/in-memory-event-bus.d.ts +60 -0
  142. package/dist/event-bus/in-memory-event-bus.d.ts.map +1 -0
  143. package/dist/event-bus/in-memory-event-bus.js +274 -0
  144. package/dist/event-bus/in-memory-event-bus.js.map +1 -0
  145. package/dist/event-bus/in-memory-event-bus.test.d.ts +5 -0
  146. package/dist/event-bus/in-memory-event-bus.test.d.ts.map +1 -0
  147. package/dist/event-bus/in-memory-event-bus.test.js +457 -0
  148. package/dist/event-bus/in-memory-event-bus.test.js.map +1 -0
  149. package/dist/event-bus/index.d.ts +22 -0
  150. package/dist/event-bus/index.d.ts.map +1 -0
  151. package/dist/event-bus/index.js +17 -0
  152. package/dist/event-bus/index.js.map +1 -0
  153. package/dist/event-bus/persistence-sink.d.ts +74 -0
  154. package/dist/event-bus/persistence-sink.d.ts.map +1 -0
  155. package/dist/event-bus/persistence-sink.js +193 -0
  156. package/dist/event-bus/persistence-sink.js.map +1 -0
  157. package/dist/event-bus/persistence-sink.test.d.ts +6 -0
  158. package/dist/event-bus/persistence-sink.test.d.ts.map +1 -0
  159. package/dist/event-bus/persistence-sink.test.js +319 -0
  160. package/dist/event-bus/persistence-sink.test.js.map +1 -0
  161. package/dist/event-bus/session-checkpoint-sink.d.ts +91 -0
  162. package/dist/event-bus/session-checkpoint-sink.d.ts.map +1 -0
  163. package/dist/event-bus/session-checkpoint-sink.js +107 -0
  164. package/dist/event-bus/session-checkpoint-sink.js.map +1 -0
  165. package/dist/event-bus/session-checkpoint-sink.test.d.ts +5 -0
  166. package/dist/event-bus/session-checkpoint-sink.test.d.ts.map +1 -0
  167. package/dist/event-bus/session-checkpoint-sink.test.js +215 -0
  168. package/dist/event-bus/session-checkpoint-sink.test.js.map +1 -0
  169. package/dist/event-bus/webhook-connector.d.ts +59 -0
  170. package/dist/event-bus/webhook-connector.d.ts.map +1 -0
  171. package/dist/event-bus/webhook-connector.js +191 -0
  172. package/dist/event-bus/webhook-connector.js.map +1 -0
  173. package/dist/event-bus/webhook-connector.test.d.ts +5 -0
  174. package/dist/event-bus/webhook-connector.test.d.ts.map +1 -0
  175. package/dist/event-bus/webhook-connector.test.js +214 -0
  176. package/dist/event-bus/webhook-connector.test.js.map +1 -0
  177. package/dist/executors/cortex-job-backed-executor.d.ts +137 -0
  178. package/dist/executors/cortex-job-backed-executor.d.ts.map +1 -0
  179. package/dist/executors/cortex-job-backed-executor.js +441 -0
  180. package/dist/executors/cortex-job-backed-executor.js.map +1 -0
  181. package/dist/executors/cortex-job-backed-executor.test.d.ts +13 -0
  182. package/dist/executors/cortex-job-backed-executor.test.d.ts.map +1 -0
  183. package/dist/executors/cortex-job-backed-executor.test.js +303 -0
  184. package/dist/executors/cortex-job-backed-executor.test.js.map +1 -0
  185. package/dist/executors/index.d.ts +9 -0
  186. package/dist/executors/index.d.ts.map +1 -0
  187. package/dist/executors/index.js +9 -0
  188. package/dist/executors/index.js.map +1 -0
  189. package/dist/index.d.ts +2 -0
  190. package/dist/index.d.ts.map +1 -0
  191. package/dist/index.js +13 -0
  192. package/dist/index.js.map +1 -0
  193. package/dist/ports/checkpoint-sink.d.ts +69 -0
  194. package/dist/ports/checkpoint-sink.d.ts.map +1 -0
  195. package/dist/ports/checkpoint-sink.js +16 -0
  196. package/dist/ports/checkpoint-sink.js.map +1 -0
  197. package/dist/ports/checkpoint.d.ts +68 -0
  198. package/dist/ports/checkpoint.d.ts.map +1 -0
  199. package/dist/ports/checkpoint.js +14 -0
  200. package/dist/ports/checkpoint.js.map +1 -0
  201. package/dist/ports/continuation-envelope-cross-app.test.d.ts +10 -0
  202. package/dist/ports/continuation-envelope-cross-app.test.d.ts.map +1 -0
  203. package/dist/ports/continuation-envelope-cross-app.test.js +95 -0
  204. package/dist/ports/continuation-envelope-cross-app.test.js.map +1 -0
  205. package/dist/ports/continuation-envelope.d.ts +199 -0
  206. package/dist/ports/continuation-envelope.d.ts.map +1 -0
  207. package/dist/ports/continuation-envelope.js +69 -0
  208. package/dist/ports/continuation-envelope.js.map +1 -0
  209. package/dist/ports/conversation.d.ts +64 -0
  210. package/dist/ports/conversation.d.ts.map +1 -0
  211. package/dist/ports/conversation.js +19 -0
  212. package/dist/ports/conversation.js.map +1 -0
  213. package/dist/ports/cost-oracle.d.ts +26 -0
  214. package/dist/ports/cost-oracle.d.ts.map +1 -0
  215. package/dist/ports/cost-oracle.js +9 -0
  216. package/dist/ports/cost-oracle.js.map +1 -0
  217. package/dist/ports/cross-app-invoker.d.ts +198 -0
  218. package/dist/ports/cross-app-invoker.d.ts.map +1 -0
  219. package/dist/ports/cross-app-invoker.js +157 -0
  220. package/dist/ports/cross-app-invoker.js.map +1 -0
  221. package/dist/ports/dlq-observer.d.ts +40 -0
  222. package/dist/ports/dlq-observer.d.ts.map +1 -0
  223. package/dist/ports/dlq-observer.js +20 -0
  224. package/dist/ports/dlq-observer.js.map +1 -0
  225. package/dist/ports/event-bus.d.ts +169 -0
  226. package/dist/ports/event-bus.d.ts.map +1 -0
  227. package/dist/ports/event-bus.js +20 -0
  228. package/dist/ports/event-bus.js.map +1 -0
  229. package/dist/ports/event-reader.d.ts +21 -0
  230. package/dist/ports/event-reader.d.ts.map +1 -0
  231. package/dist/ports/event-reader.js +12 -0
  232. package/dist/ports/event-reader.js.map +1 -0
  233. package/dist/ports/event-rotator.d.ts +39 -0
  234. package/dist/ports/event-rotator.d.ts.map +1 -0
  235. package/dist/ports/event-rotator.js +15 -0
  236. package/dist/ports/event-rotator.js.map +1 -0
  237. package/dist/ports/file-system.d.ts +48 -0
  238. package/dist/ports/file-system.d.ts.map +1 -0
  239. package/dist/ports/file-system.js +10 -0
  240. package/dist/ports/file-system.js.map +1 -0
  241. package/dist/ports/historical-observations.d.ts +39 -0
  242. package/dist/ports/historical-observations.d.ts.map +1 -0
  243. package/dist/ports/historical-observations.js +12 -0
  244. package/dist/ports/historical-observations.js.map +1 -0
  245. package/dist/ports/in-memory-source.d.ts +46 -0
  246. package/dist/ports/in-memory-source.d.ts.map +1 -0
  247. package/dist/ports/in-memory-source.js +69 -0
  248. package/dist/ports/in-memory-source.js.map +1 -0
  249. package/dist/ports/index.d.ts +32 -0
  250. package/dist/ports/index.d.ts.map +1 -0
  251. package/dist/ports/index.js +10 -0
  252. package/dist/ports/index.js.map +1 -0
  253. package/dist/ports/job-backed-executor.d.ts +139 -0
  254. package/dist/ports/job-backed-executor.d.ts.map +1 -0
  255. package/dist/ports/job-backed-executor.js +56 -0
  256. package/dist/ports/job-backed-executor.js.map +1 -0
  257. package/dist/ports/methodology-source.d.ts +95 -0
  258. package/dist/ports/methodology-source.d.ts.map +1 -0
  259. package/dist/ports/methodology-source.js +26 -0
  260. package/dist/ports/methodology-source.js.map +1 -0
  261. package/dist/ports/native-session-discovery.d.ts +20 -0
  262. package/dist/ports/native-session-discovery.d.ts.map +1 -0
  263. package/dist/ports/native-session-discovery.js +13 -0
  264. package/dist/ports/native-session-discovery.js.map +1 -0
  265. package/dist/ports/projection-store.d.ts +48 -0
  266. package/dist/ports/projection-store.d.ts.map +1 -0
  267. package/dist/ports/projection-store.js +17 -0
  268. package/dist/ports/projection-store.js.map +1 -0
  269. package/dist/ports/projection.d.ts +29 -0
  270. package/dist/ports/projection.d.ts.map +1 -0
  271. package/dist/ports/projection.js +13 -0
  272. package/dist/ports/projection.js.map +1 -0
  273. package/dist/ports/rate-governor.d.ts +17 -0
  274. package/dist/ports/rate-governor.d.ts.map +1 -0
  275. package/dist/ports/rate-governor.js +11 -0
  276. package/dist/ports/rate-governor.js.map +1 -0
  277. package/dist/ports/schedule-client.d.ts +29 -0
  278. package/dist/ports/schedule-client.d.ts.map +1 -0
  279. package/dist/ports/schedule-client.js +2 -0
  280. package/dist/ports/schedule-client.js.map +1 -0
  281. package/dist/ports/session-pool.d.ts +162 -0
  282. package/dist/ports/session-pool.d.ts.map +1 -0
  283. package/dist/ports/session-pool.js +21 -0
  284. package/dist/ports/session-pool.js.map +1 -0
  285. package/dist/ports/session-store-errors.d.ts +22 -0
  286. package/dist/ports/session-store-errors.d.ts.map +1 -0
  287. package/dist/ports/session-store-errors.js +29 -0
  288. package/dist/ports/session-store-errors.js.map +1 -0
  289. package/dist/ports/session-store-types.d.ts +132 -0
  290. package/dist/ports/session-store-types.d.ts.map +1 -0
  291. package/dist/ports/session-store-types.js +14 -0
  292. package/dist/ports/session-store-types.js.map +1 -0
  293. package/dist/ports/session-store.d.ts +69 -0
  294. package/dist/ports/session-store.d.ts.map +1 -0
  295. package/dist/ports/session-store.js +23 -0
  296. package/dist/ports/session-store.js.map +1 -0
  297. package/dist/ports/yaml-loader.d.ts +15 -0
  298. package/dist/ports/yaml-loader.d.ts.map +1 -0
  299. package/dist/ports/yaml-loader.js +13 -0
  300. package/dist/ports/yaml-loader.js.map +1 -0
  301. package/dist/scheduling/index.d.ts +6 -0
  302. package/dist/scheduling/index.d.ts.map +1 -0
  303. package/dist/scheduling/index.js +6 -0
  304. package/dist/scheduling/index.js.map +1 -0
  305. package/dist/scheduling/scheduled-pact.d.ts +97 -0
  306. package/dist/scheduling/scheduled-pact.d.ts.map +1 -0
  307. package/dist/scheduling/scheduled-pact.js +89 -0
  308. package/dist/scheduling/scheduled-pact.js.map +1 -0
  309. package/dist/sessions/__tests__/cognitive-modules.test.d.ts +10 -0
  310. package/dist/sessions/__tests__/cognitive-modules.test.d.ts.map +1 -0
  311. package/dist/sessions/__tests__/cognitive-modules.test.js +535 -0
  312. package/dist/sessions/__tests__/cognitive-modules.test.js.map +1 -0
  313. package/dist/sessions/__tests__/cognitive-provider.test.d.ts +13 -0
  314. package/dist/sessions/__tests__/cognitive-provider.test.d.ts.map +1 -0
  315. package/dist/sessions/__tests__/cognitive-provider.test.js +331 -0
  316. package/dist/sessions/__tests__/cognitive-provider.test.js.map +1 -0
  317. package/dist/sessions/__tests__/cognitive-sink.test.d.ts +19 -0
  318. package/dist/sessions/__tests__/cognitive-sink.test.d.ts.map +1 -0
  319. package/dist/sessions/__tests__/cognitive-sink.test.js +334 -0
  320. package/dist/sessions/__tests__/cognitive-sink.test.js.map +1 -0
  321. package/dist/sessions/__tests__/runtime-tools.test.d.ts +2 -0
  322. package/dist/sessions/__tests__/runtime-tools.test.d.ts.map +1 -0
  323. package/dist/sessions/__tests__/runtime-tools.test.js +83 -0
  324. package/dist/sessions/__tests__/runtime-tools.test.js.map +1 -0
  325. package/dist/sessions/auto-retro.d.ts +29 -0
  326. package/dist/sessions/auto-retro.d.ts.map +1 -0
  327. package/dist/sessions/auto-retro.js +181 -0
  328. package/dist/sessions/auto-retro.js.map +1 -0
  329. package/dist/sessions/auto-retro.test.d.ts +2 -0
  330. package/dist/sessions/auto-retro.test.d.ts.map +1 -0
  331. package/dist/sessions/auto-retro.test.js +361 -0
  332. package/dist/sessions/auto-retro.test.js.map +1 -0
  333. package/dist/sessions/channels.d.ts +55 -0
  334. package/dist/sessions/channels.d.ts.map +1 -0
  335. package/dist/sessions/channels.js +118 -0
  336. package/dist/sessions/channels.js.map +1 -0
  337. package/dist/sessions/channels.test.d.ts +2 -0
  338. package/dist/sessions/channels.test.d.ts.map +1 -0
  339. package/dist/sessions/channels.test.js +285 -0
  340. package/dist/sessions/channels.test.js.map +1 -0
  341. package/dist/sessions/cognitive-modules.d.ts +100 -0
  342. package/dist/sessions/cognitive-modules.d.ts.map +1 -0
  343. package/dist/sessions/cognitive-modules.js +458 -0
  344. package/dist/sessions/cognitive-modules.js.map +1 -0
  345. package/dist/sessions/cognitive-provider.d.ts +42 -0
  346. package/dist/sessions/cognitive-provider.d.ts.map +1 -0
  347. package/dist/sessions/cognitive-provider.js +208 -0
  348. package/dist/sessions/cognitive-provider.js.map +1 -0
  349. package/dist/sessions/cognitive-sink.d.ts +73 -0
  350. package/dist/sessions/cognitive-sink.d.ts.map +1 -0
  351. package/dist/sessions/cognitive-sink.js +154 -0
  352. package/dist/sessions/cognitive-sink.js.map +1 -0
  353. package/dist/sessions/diagnostics.d.ts +70 -0
  354. package/dist/sessions/diagnostics.d.ts.map +1 -0
  355. package/dist/sessions/diagnostics.js +129 -0
  356. package/dist/sessions/diagnostics.js.map +1 -0
  357. package/dist/sessions/diagnostics.test.d.ts +2 -0
  358. package/dist/sessions/diagnostics.test.d.ts.map +1 -0
  359. package/dist/sessions/diagnostics.test.js +135 -0
  360. package/dist/sessions/diagnostics.test.js.map +1 -0
  361. package/dist/sessions/index.d.ts +32 -0
  362. package/dist/sessions/index.d.ts.map +1 -0
  363. package/dist/sessions/index.js +33 -0
  364. package/dist/sessions/index.js.map +1 -0
  365. package/dist/sessions/pool.d.ts +218 -0
  366. package/dist/sessions/pool.d.ts.map +1 -0
  367. package/dist/sessions/pool.js +991 -0
  368. package/dist/sessions/pool.js.map +1 -0
  369. package/dist/sessions/pool.test.d.ts +2 -0
  370. package/dist/sessions/pool.test.d.ts.map +1 -0
  371. package/dist/sessions/pool.test.js +633 -0
  372. package/dist/sessions/pool.test.js.map +1 -0
  373. package/dist/sessions/print-session.d.ts +142 -0
  374. package/dist/sessions/print-session.d.ts.map +1 -0
  375. package/dist/sessions/print-session.js +325 -0
  376. package/dist/sessions/print-session.js.map +1 -0
  377. package/dist/sessions/print-session.test.d.ts +2 -0
  378. package/dist/sessions/print-session.test.d.ts.map +1 -0
  379. package/dist/sessions/print-session.test.js +418 -0
  380. package/dist/sessions/print-session.test.js.map +1 -0
  381. package/dist/sessions/runtime-tools.d.ts +22 -0
  382. package/dist/sessions/runtime-tools.d.ts.map +1 -0
  383. package/dist/sessions/runtime-tools.js +162 -0
  384. package/dist/sessions/runtime-tools.js.map +1 -0
  385. package/dist/sessions/scope-hook.d.ts +77 -0
  386. package/dist/sessions/scope-hook.d.ts.map +1 -0
  387. package/dist/sessions/scope-hook.js +323 -0
  388. package/dist/sessions/scope-hook.js.map +1 -0
  389. package/dist/sessions/scope-hook.test.d.ts +2 -0
  390. package/dist/sessions/scope-hook.test.d.ts.map +1 -0
  391. package/dist/sessions/scope-hook.test.js +249 -0
  392. package/dist/sessions/scope-hook.test.js.map +1 -0
  393. package/dist/sessions/session-store/checkpoint-sink-impl.d.ts +16 -0
  394. package/dist/sessions/session-store/checkpoint-sink-impl.d.ts.map +1 -0
  395. package/dist/sessions/session-store/checkpoint-sink-impl.js +191 -0
  396. package/dist/sessions/session-store/checkpoint-sink-impl.js.map +1 -0
  397. package/dist/sessions/session-store/checkpoint-sink-impl.test.d.ts +5 -0
  398. package/dist/sessions/session-store/checkpoint-sink-impl.test.d.ts.map +1 -0
  399. package/dist/sessions/session-store/checkpoint-sink-impl.test.js +137 -0
  400. package/dist/sessions/session-store/checkpoint-sink-impl.test.js.map +1 -0
  401. package/dist/sessions/session-store/conformance.d.ts +59 -0
  402. package/dist/sessions/session-store/conformance.d.ts.map +1 -0
  403. package/dist/sessions/session-store/conformance.js +172 -0
  404. package/dist/sessions/session-store/conformance.js.map +1 -0
  405. package/dist/sessions/session-store/conformance.test.d.ts +7 -0
  406. package/dist/sessions/session-store/conformance.test.d.ts.map +1 -0
  407. package/dist/sessions/session-store/conformance.test.js +22 -0
  408. package/dist/sessions/session-store/conformance.test.js.map +1 -0
  409. package/dist/sessions/session-store/in-memory-session-store.d.ts +23 -0
  410. package/dist/sessions/session-store/in-memory-session-store.d.ts.map +1 -0
  411. package/dist/sessions/session-store/in-memory-session-store.js +197 -0
  412. package/dist/sessions/session-store/in-memory-session-store.js.map +1 -0
  413. package/dist/sessions/session-store/in-memory-session-store.test.d.ts +6 -0
  414. package/dist/sessions/session-store/in-memory-session-store.test.d.ts.map +1 -0
  415. package/dist/sessions/session-store/in-memory-session-store.test.js +183 -0
  416. package/dist/sessions/session-store/in-memory-session-store.test.js.map +1 -0
  417. package/dist/sessions/session-store/index.d.ts +20 -0
  418. package/dist/sessions/session-store/index.d.ts.map +1 -0
  419. package/dist/sessions/session-store/index.js +15 -0
  420. package/dist/sessions/session-store/index.js.map +1 -0
  421. package/dist/sessions/session-store/resume.d.ts +88 -0
  422. package/dist/sessions/session-store/resume.d.ts.map +1 -0
  423. package/dist/sessions/session-store/resume.js +96 -0
  424. package/dist/sessions/session-store/resume.js.map +1 -0
  425. package/dist/sessions/session-store/resume.test.d.ts +5 -0
  426. package/dist/sessions/session-store/resume.test.d.ts.map +1 -0
  427. package/dist/sessions/session-store/resume.test.js +119 -0
  428. package/dist/sessions/session-store/resume.test.js.map +1 -0
  429. package/dist/sessions/spawn-queue.d.ts +28 -0
  430. package/dist/sessions/spawn-queue.d.ts.map +1 -0
  431. package/dist/sessions/spawn-queue.js +63 -0
  432. package/dist/sessions/spawn-queue.js.map +1 -0
  433. package/dist/sessions/spawn-queue.test.d.ts +2 -0
  434. package/dist/sessions/spawn-queue.test.d.ts.map +1 -0
  435. package/dist/sessions/spawn-queue.test.js +65 -0
  436. package/dist/sessions/spawn-queue.test.js.map +1 -0
  437. package/dist/sessions/types.d.ts +16 -0
  438. package/dist/sessions/types.d.ts.map +1 -0
  439. package/dist/sessions/types.js +11 -0
  440. package/dist/sessions/types.js.map +1 -0
  441. package/dist/sessions/worktree-stale.test.d.ts +2 -0
  442. package/dist/sessions/worktree-stale.test.d.ts.map +1 -0
  443. package/dist/sessions/worktree-stale.test.js +468 -0
  444. package/dist/sessions/worktree-stale.test.js.map +1 -0
  445. package/dist/strategy/artifact-store.d.ts +12 -0
  446. package/dist/strategy/artifact-store.d.ts.map +1 -0
  447. package/dist/strategy/artifact-store.js +12 -0
  448. package/dist/strategy/artifact-store.js.map +1 -0
  449. package/dist/strategy/artifact-store.test.d.ts +2 -0
  450. package/dist/strategy/artifact-store.test.d.ts.map +1 -0
  451. package/dist/strategy/artifact-store.test.js +170 -0
  452. package/dist/strategy/artifact-store.test.js.map +1 -0
  453. package/dist/strategy/context-load-executor.d.ts +23 -0
  454. package/dist/strategy/context-load-executor.d.ts.map +1 -0
  455. package/dist/strategy/context-load-executor.js +87 -0
  456. package/dist/strategy/context-load-executor.js.map +1 -0
  457. package/dist/strategy/context-load-executor.test.d.ts +16 -0
  458. package/dist/strategy/context-load-executor.test.d.ts.map +1 -0
  459. package/dist/strategy/context-load-executor.test.js +158 -0
  460. package/dist/strategy/context-load-executor.test.js.map +1 -0
  461. package/dist/strategy/cortex-cross-app-invoker.stub.d.ts +65 -0
  462. package/dist/strategy/cortex-cross-app-invoker.stub.d.ts.map +1 -0
  463. package/dist/strategy/cortex-cross-app-invoker.stub.js +68 -0
  464. package/dist/strategy/cortex-cross-app-invoker.stub.js.map +1 -0
  465. package/dist/strategy/cross-app-node-executor.d.ts +54 -0
  466. package/dist/strategy/cross-app-node-executor.d.ts.map +1 -0
  467. package/dist/strategy/cross-app-node-executor.js +98 -0
  468. package/dist/strategy/cross-app-node-executor.js.map +1 -0
  469. package/dist/strategy/cross-app-node-executor.test.d.ts +13 -0
  470. package/dist/strategy/cross-app-node-executor.test.d.ts.map +1 -0
  471. package/dist/strategy/cross-app-node-executor.test.js +160 -0
  472. package/dist/strategy/cross-app-node-executor.test.js.map +1 -0
  473. package/dist/strategy/gates.d.ts +13 -0
  474. package/dist/strategy/gates.d.ts.map +1 -0
  475. package/dist/strategy/gates.js +13 -0
  476. package/dist/strategy/gates.js.map +1 -0
  477. package/dist/strategy/gates.test.d.ts +2 -0
  478. package/dist/strategy/gates.test.d.ts.map +1 -0
  479. package/dist/strategy/gates.test.js +299 -0
  480. package/dist/strategy/gates.test.js.map +1 -0
  481. package/dist/strategy/human-approval-resolver.d.ts +23 -0
  482. package/dist/strategy/human-approval-resolver.d.ts.map +1 -0
  483. package/dist/strategy/human-approval-resolver.js +94 -0
  484. package/dist/strategy/human-approval-resolver.js.map +1 -0
  485. package/dist/strategy/human-approval-resolver.test.d.ts +16 -0
  486. package/dist/strategy/human-approval-resolver.test.d.ts.map +1 -0
  487. package/dist/strategy/human-approval-resolver.test.js +200 -0
  488. package/dist/strategy/human-approval-resolver.test.js.map +1 -0
  489. package/dist/strategy/in-process-cross-app-invoker.d.ts +105 -0
  490. package/dist/strategy/in-process-cross-app-invoker.d.ts.map +1 -0
  491. package/dist/strategy/in-process-cross-app-invoker.js +206 -0
  492. package/dist/strategy/in-process-cross-app-invoker.js.map +1 -0
  493. package/dist/strategy/in-process-cross-app-invoker.test.d.ts +15 -0
  494. package/dist/strategy/in-process-cross-app-invoker.test.d.ts.map +1 -0
  495. package/dist/strategy/in-process-cross-app-invoker.test.js +190 -0
  496. package/dist/strategy/in-process-cross-app-invoker.test.js.map +1 -0
  497. package/dist/strategy/index.d.ts +29 -0
  498. package/dist/strategy/index.d.ts.map +1 -0
  499. package/dist/strategy/index.js +29 -0
  500. package/dist/strategy/index.js.map +1 -0
  501. package/dist/strategy/pacta-strategy.d.ts +97 -0
  502. package/dist/strategy/pacta-strategy.d.ts.map +1 -0
  503. package/dist/strategy/pacta-strategy.js +117 -0
  504. package/dist/strategy/pacta-strategy.js.map +1 -0
  505. package/dist/strategy/pacta-strategy.test.d.ts +2 -0
  506. package/dist/strategy/pacta-strategy.test.d.ts.map +1 -0
  507. package/dist/strategy/pacta-strategy.test.js +234 -0
  508. package/dist/strategy/pacta-strategy.test.js.map +1 -0
  509. package/dist/strategy/retro-generator.d.ts +18 -0
  510. package/dist/strategy/retro-generator.d.ts.map +1 -0
  511. package/dist/strategy/retro-generator.js +22 -0
  512. package/dist/strategy/retro-generator.js.map +1 -0
  513. package/dist/strategy/retro-writer.d.ts +25 -0
  514. package/dist/strategy/retro-writer.d.ts.map +1 -0
  515. package/dist/strategy/retro-writer.js +65 -0
  516. package/dist/strategy/retro-writer.js.map +1 -0
  517. package/dist/strategy/strategy-executor.d.ts +39 -0
  518. package/dist/strategy/strategy-executor.d.ts.map +1 -0
  519. package/dist/strategy/strategy-executor.js +253 -0
  520. package/dist/strategy/strategy-executor.js.map +1 -0
  521. package/dist/strategy/strategy-executor.test.d.ts +8 -0
  522. package/dist/strategy/strategy-executor.test.d.ts.map +1 -0
  523. package/dist/strategy/strategy-executor.test.js +1301 -0
  524. package/dist/strategy/strategy-executor.test.js.map +1 -0
  525. package/dist/strategy/strategy-parser.d.ts +30 -0
  526. package/dist/strategy/strategy-parser.d.ts.map +1 -0
  527. package/dist/strategy/strategy-parser.js +30 -0
  528. package/dist/strategy/strategy-parser.js.map +1 -0
  529. package/dist/strategy/sub-strategy-source.d.ts +27 -0
  530. package/dist/strategy/sub-strategy-source.d.ts.map +1 -0
  531. package/dist/strategy/sub-strategy-source.js +77 -0
  532. package/dist/strategy/sub-strategy-source.js.map +1 -0
  533. package/dist/strategy/types.d.ts +12 -0
  534. package/dist/strategy/types.d.ts.map +1 -0
  535. package/dist/strategy/types.js +9 -0
  536. package/dist/strategy/types.js.map +1 -0
  537. package/package.json +87 -0
@@ -0,0 +1,1301 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * PRD 017: Strategy Pipelines — DAG Executor Tests (Phase 1c)
4
+ *
5
+ * Tests for Strategy YAML parsing, DAG validation, topological sort,
6
+ * and end-to-end Strategy execution with mock LLM provider.
7
+ */
8
+ import { describe, it } from 'node:test';
9
+ import assert from 'node:assert/strict';
10
+ import { parseStrategyYaml, parseStrategyObject, validateStrategyDAG, topologicalSort, } from './strategy-parser.js';
11
+ import { StrategyExecutor } from './strategy-executor.js';
12
+ import { setStrategyParserYaml } from './strategy-parser.js';
13
+ import { setRetroGeneratorYaml } from './retro-generator.js';
14
+ import yaml from 'js-yaml';
15
+ // PRD 024 / PRD-057 C2: Configure YAML port for tests with an inline
16
+ // js-yaml-backed loader. The runtime stays transport-free — the bridge's
17
+ // JsYamlLoader implementation lives in @methodts/bridge.
18
+ const testYaml = {
19
+ load: (text) => yaml.load(text),
20
+ dump: (obj) => yaml.dump(obj),
21
+ };
22
+ setStrategyParserYaml(testYaml);
23
+ setRetroGeneratorYaml(testYaml);
24
+ // ── Test Fixtures ───────────────────────────────────────────────
25
+ const TEST_STRATEGY_YAML = `
26
+ strategy:
27
+ id: S-TEST-3NODE
28
+ name: "Test 3-Node Strategy"
29
+ version: "1.0"
30
+ context:
31
+ inputs:
32
+ - { name: task_description, type: string }
33
+ capabilities:
34
+ read_only: [Read, Glob, Grep]
35
+ implementation: [Read, Write, Edit, Bash, Glob, Grep]
36
+ dag:
37
+ nodes:
38
+ - id: analyze
39
+ type: methodology
40
+ methodology: P2-SD
41
+ method_hint: M7-PRDS
42
+ capabilities: [read_only]
43
+ inputs: [task_description]
44
+ outputs: [analysis]
45
+ gates:
46
+ - type: algorithmic
47
+ check: "output.analysis !== undefined"
48
+ - id: implement
49
+ type: methodology
50
+ methodology: P2-SD
51
+ method_hint: M1-IMPL
52
+ capabilities: [implementation]
53
+ inputs: [analysis]
54
+ outputs: [code_changes]
55
+ depends_on: [analyze]
56
+ gates:
57
+ - type: algorithmic
58
+ check: "output.tests_passed === true"
59
+ max_retries: 2
60
+ - id: summarize
61
+ type: script
62
+ script: "return { summary: 'Completed: ' + JSON.stringify(inputs.code_changes) };"
63
+ inputs: [code_changes]
64
+ outputs: [summary]
65
+ depends_on: [implement]
66
+ oversight:
67
+ rules:
68
+ - { condition: "total_cost_usd > 5.00", action: warn_human }
69
+ `;
70
+ // ── Mock Agent Provider ───────────────────────────────────────
71
+ function makeMockResult(overrides = {}) {
72
+ return {
73
+ output: '```json\n{"status": "ok"}\n```',
74
+ sessionId: 'mock-session',
75
+ completed: true,
76
+ stopReason: 'complete',
77
+ usage: {
78
+ inputTokens: 100,
79
+ outputTokens: 50,
80
+ cacheReadTokens: 0,
81
+ cacheWriteTokens: 0,
82
+ totalTokens: 150,
83
+ },
84
+ cost: { totalUsd: 0.05, perModel: {} },
85
+ durationMs: 100,
86
+ turns: 1,
87
+ ...overrides,
88
+ };
89
+ }
90
+ class MockAgentProvider {
91
+ name = 'mock';
92
+ responses = new Map();
93
+ invocations = [];
94
+ setResponse(promptContains, result) {
95
+ this.responses.set(promptContains, result);
96
+ }
97
+ capabilities() {
98
+ return {
99
+ modes: ['oneshot'],
100
+ streaming: false,
101
+ resumable: false,
102
+ budgetEnforcement: 'none',
103
+ outputValidation: 'client',
104
+ toolModel: 'builtin',
105
+ };
106
+ }
107
+ async invoke(_pact, request) {
108
+ this.invocations.push(request);
109
+ // Find matching result by checking if prompt contains any key
110
+ for (const [key, result] of this.responses) {
111
+ if (request.prompt.includes(key))
112
+ return result;
113
+ }
114
+ // Default result
115
+ return makeMockResult();
116
+ }
117
+ }
118
+ function makeExecutorConfig(overrides = {}) {
119
+ return {
120
+ maxParallel: 3,
121
+ defaultGateRetries: 3,
122
+ defaultTimeoutMs: 600000,
123
+ retroDir: '.method/retros',
124
+ ...overrides,
125
+ };
126
+ }
127
+ // ── Helper: build a StrategyYaml object programmatically ────────
128
+ function makeStrategyYaml(overrides = {}) {
129
+ return {
130
+ strategy: {
131
+ id: 'S-TEST',
132
+ name: 'Test Strategy',
133
+ version: '1.0',
134
+ capabilities: {
135
+ read_only: ['Read', 'Glob'],
136
+ impl: ['Read', 'Write', 'Edit'],
137
+ },
138
+ dag: {
139
+ nodes: [
140
+ {
141
+ id: 'node-a',
142
+ type: 'methodology',
143
+ methodology: 'P2-SD',
144
+ method_hint: 'M1-IMPL',
145
+ capabilities: ['read_only'],
146
+ inputs: ['task'],
147
+ outputs: ['result_a'],
148
+ gates: [
149
+ {
150
+ type: 'algorithmic',
151
+ check: 'output.result_a !== undefined',
152
+ },
153
+ ],
154
+ },
155
+ ],
156
+ },
157
+ ...overrides,
158
+ },
159
+ };
160
+ }
161
+ // ── Parser Tests ───────────────────────────────────────────────
162
+ describe('parseStrategyYaml', () => {
163
+ it('parses a valid Strategy YAML string into StrategyDAG', () => {
164
+ const dag = parseStrategyYaml(TEST_STRATEGY_YAML);
165
+ assert.equal(dag.id, 'S-TEST-3NODE');
166
+ assert.equal(dag.name, 'Test 3-Node Strategy');
167
+ assert.equal(dag.version, '1.0');
168
+ assert.equal(dag.nodes.length, 3);
169
+ assert.equal(dag.context_inputs.length, 1);
170
+ assert.equal(dag.context_inputs[0].name, 'task_description');
171
+ });
172
+ it('parses nodes with correct types and dependencies', () => {
173
+ const dag = parseStrategyYaml(TEST_STRATEGY_YAML);
174
+ const analyze = dag.nodes.find((n) => n.id === 'analyze');
175
+ assert.equal(analyze.type, 'methodology');
176
+ assert.deepEqual(analyze.depends_on, []);
177
+ assert.deepEqual(analyze.inputs, ['task_description']);
178
+ assert.deepEqual(analyze.outputs, ['analysis']);
179
+ assert.equal(analyze.config.type, 'methodology');
180
+ if (analyze.config.type === 'methodology') {
181
+ assert.equal(analyze.config.methodology, 'P2-SD');
182
+ assert.equal(analyze.config.method_hint, 'M7-PRDS');
183
+ assert.deepEqual(analyze.config.capabilities, ['read_only']);
184
+ }
185
+ const implement = dag.nodes.find((n) => n.id === 'implement');
186
+ assert.deepEqual(implement.depends_on, ['analyze']);
187
+ assert.deepEqual(implement.inputs, ['analysis']);
188
+ assert.deepEqual(implement.outputs, ['code_changes']);
189
+ const summarize = dag.nodes.find((n) => n.id === 'summarize');
190
+ assert.equal(summarize.type, 'script');
191
+ assert.deepEqual(summarize.depends_on, ['implement']);
192
+ assert.equal(summarize.config.type, 'script');
193
+ if (summarize.config.type === 'script') {
194
+ assert.ok(summarize.config.script.includes('JSON.stringify'));
195
+ }
196
+ });
197
+ it('applies default retries and timeout to gates', () => {
198
+ const dag = parseStrategyYaml(TEST_STRATEGY_YAML);
199
+ // analyze gate: no explicit retries/timeout → defaults
200
+ const analyzeGate = dag.nodes.find((n) => n.id === 'analyze').gates[0];
201
+ assert.equal(analyzeGate.max_retries, 3); // default for algorithmic
202
+ assert.equal(analyzeGate.timeout_ms, 5000); // default timeout
203
+ // implement gate: explicit max_retries=2
204
+ const implementGate = dag.nodes.find((n) => n.id === 'implement').gates[0];
205
+ assert.equal(implementGate.max_retries, 2);
206
+ assert.equal(implementGate.timeout_ms, 5000);
207
+ });
208
+ it('parses capabilities and oversight rules', () => {
209
+ const dag = parseStrategyYaml(TEST_STRATEGY_YAML);
210
+ assert.deepEqual(dag.capabilities['read_only'], ['Read', 'Glob', 'Grep']);
211
+ assert.deepEqual(dag.capabilities['implementation'], ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep']);
212
+ assert.equal(dag.oversight_rules.length, 1);
213
+ assert.equal(dag.oversight_rules[0].condition, 'total_cost_usd > 5.00');
214
+ assert.equal(dag.oversight_rules[0].action, 'warn_human');
215
+ });
216
+ it('parses a Strategy with script nodes', () => {
217
+ const yamlStr = `
218
+ strategy:
219
+ id: S-SCRIPT
220
+ name: "Script Strategy"
221
+ version: "1.0"
222
+ dag:
223
+ nodes:
224
+ - id: compute
225
+ type: script
226
+ script: "return { total: inputs.a + inputs.b };"
227
+ inputs: [a, b]
228
+ outputs: [total]
229
+ `;
230
+ const dag = parseStrategyYaml(yamlStr);
231
+ assert.equal(dag.nodes.length, 1);
232
+ assert.equal(dag.nodes[0].type, 'script');
233
+ assert.equal(dag.nodes[0].config.type, 'script');
234
+ if (dag.nodes[0].config.type === 'script') {
235
+ assert.ok(dag.nodes[0].config.script.includes('inputs.a + inputs.b'));
236
+ }
237
+ });
238
+ it('parses strategy_gates', () => {
239
+ const yamlStr = `
240
+ strategy:
241
+ id: S-SGATES
242
+ name: "Strategy Gates Test"
243
+ version: "1.0"
244
+ dag:
245
+ nodes:
246
+ - id: work
247
+ type: methodology
248
+ methodology: P2-SD
249
+ outputs: [result]
250
+ strategy_gates:
251
+ - id: final_check
252
+ depends_on: [work]
253
+ type: algorithmic
254
+ check: "artifacts.result !== undefined"
255
+ `;
256
+ const dag = parseStrategyYaml(yamlStr);
257
+ assert.equal(dag.strategy_gates.length, 1);
258
+ assert.equal(dag.strategy_gates[0].id, 'final_check');
259
+ assert.deepEqual(dag.strategy_gates[0].depends_on, ['work']);
260
+ assert.equal(dag.strategy_gates[0].gate.type, 'algorithmic');
261
+ assert.equal(dag.strategy_gates[0].gate.check, 'artifacts.result !== undefined');
262
+ assert.equal(dag.strategy_gates[0].gate.max_retries, 0); // forced to 0: strategy gates are single-shot
263
+ });
264
+ });
265
+ describe('parseStrategyObject', () => {
266
+ it('transforms a pre-parsed YAML object into StrategyDAG', () => {
267
+ const obj = makeStrategyYaml();
268
+ const dag = parseStrategyObject(obj);
269
+ assert.equal(dag.id, 'S-TEST');
270
+ assert.equal(dag.name, 'Test Strategy');
271
+ assert.equal(dag.nodes.length, 1);
272
+ assert.deepEqual(dag.capabilities['read_only'], ['Read', 'Glob']);
273
+ });
274
+ it('handles empty optional fields gracefully', () => {
275
+ const obj = {
276
+ strategy: {
277
+ id: 'S-MINIMAL',
278
+ name: 'Minimal',
279
+ version: '0.1',
280
+ dag: {
281
+ nodes: [
282
+ { id: 'a', type: 'script', script: 'return {};' },
283
+ ],
284
+ },
285
+ },
286
+ };
287
+ const dag = parseStrategyObject(obj);
288
+ assert.equal(dag.nodes.length, 1);
289
+ assert.deepEqual(dag.strategy_gates, []);
290
+ assert.deepEqual(dag.capabilities, {});
291
+ assert.deepEqual(dag.oversight_rules, []);
292
+ assert.deepEqual(dag.context_inputs, []);
293
+ assert.deepEqual(dag.nodes[0].depends_on, []);
294
+ assert.deepEqual(dag.nodes[0].inputs, []);
295
+ assert.deepEqual(dag.nodes[0].outputs, []);
296
+ assert.deepEqual(dag.nodes[0].gates, []);
297
+ });
298
+ });
299
+ // ── Validation Tests ───────────────────────────────────────────
300
+ describe('validateStrategyDAG', () => {
301
+ it('valid DAG passes validation', () => {
302
+ const dag = parseStrategyYaml(TEST_STRATEGY_YAML);
303
+ const result = validateStrategyDAG(dag);
304
+ assert.equal(result.valid, true);
305
+ assert.deepEqual(result.errors, []);
306
+ });
307
+ it('detects cyclic dependency (A -> B -> A)', () => {
308
+ const obj = {
309
+ strategy: {
310
+ id: 'S-CYCLE',
311
+ name: 'Cycle',
312
+ version: '1.0',
313
+ dag: {
314
+ nodes: [
315
+ { id: 'a', type: 'script', script: 'return {};', depends_on: ['b'] },
316
+ { id: 'b', type: 'script', script: 'return {};', depends_on: ['a'] },
317
+ ],
318
+ },
319
+ },
320
+ };
321
+ const dag = parseStrategyObject(obj);
322
+ const result = validateStrategyDAG(dag);
323
+ assert.equal(result.valid, false);
324
+ assert.ok(result.errors.some((e) => e.toLowerCase().includes('cyclic')));
325
+ });
326
+ it('detects missing depends_on reference', () => {
327
+ const obj = {
328
+ strategy: {
329
+ id: 'S-BAD-REF',
330
+ name: 'Bad Ref',
331
+ version: '1.0',
332
+ dag: {
333
+ nodes: [
334
+ { id: 'a', type: 'script', script: 'return {};', depends_on: ['nonexistent'] },
335
+ ],
336
+ },
337
+ },
338
+ };
339
+ const dag = parseStrategyObject(obj);
340
+ const result = validateStrategyDAG(dag);
341
+ assert.equal(result.valid, false);
342
+ assert.ok(result.errors.some((e) => e.includes('unknown node "nonexistent"')));
343
+ });
344
+ it('detects duplicate node IDs', () => {
345
+ const obj = {
346
+ strategy: {
347
+ id: 'S-DUP',
348
+ name: 'Dup',
349
+ version: '1.0',
350
+ dag: {
351
+ nodes: [
352
+ { id: 'a', type: 'script', script: 'return {};' },
353
+ { id: 'a', type: 'script', script: 'return {};' },
354
+ ],
355
+ },
356
+ },
357
+ };
358
+ const dag = parseStrategyObject(obj);
359
+ const result = validateStrategyDAG(dag);
360
+ assert.equal(result.valid, false);
361
+ assert.ok(result.errors.some((e) => e.includes('Duplicate node ID')));
362
+ });
363
+ it('detects invalid capability reference', () => {
364
+ const obj = {
365
+ strategy: {
366
+ id: 'S-BAD-CAP',
367
+ name: 'Bad Cap',
368
+ version: '1.0',
369
+ capabilities: {
370
+ read_only: ['Read'],
371
+ },
372
+ dag: {
373
+ nodes: [
374
+ {
375
+ id: 'a',
376
+ type: 'methodology',
377
+ methodology: 'P2-SD',
378
+ capabilities: ['nonexistent_cap'],
379
+ },
380
+ ],
381
+ },
382
+ },
383
+ };
384
+ const dag = parseStrategyObject(obj);
385
+ const result = validateStrategyDAG(dag);
386
+ assert.equal(result.valid, false);
387
+ assert.ok(result.errors.some((e) => e.includes('undefined capability set "nonexistent_cap"')));
388
+ });
389
+ it('detects gate expression syntax error', () => {
390
+ const obj = {
391
+ strategy: {
392
+ id: 'S-BAD-GATE',
393
+ name: 'Bad Gate',
394
+ version: '1.0',
395
+ dag: {
396
+ nodes: [
397
+ {
398
+ id: 'a',
399
+ type: 'methodology',
400
+ methodology: 'P2-SD',
401
+ gates: [
402
+ { type: 'algorithmic', check: 'output.result ===' }, // syntax error
403
+ ],
404
+ },
405
+ ],
406
+ },
407
+ },
408
+ };
409
+ const dag = parseStrategyObject(obj);
410
+ const result = validateStrategyDAG(dag);
411
+ assert.equal(result.valid, false);
412
+ assert.ok(result.errors.some((e) => e.includes('invalid check expression')));
413
+ });
414
+ it('returns multiple errors at once', () => {
415
+ const obj = {
416
+ strategy: {
417
+ id: 'S-MULTI-ERR',
418
+ name: 'Multi Err',
419
+ version: '1.0',
420
+ capabilities: {},
421
+ dag: {
422
+ nodes: [
423
+ {
424
+ id: 'a',
425
+ type: 'methodology',
426
+ methodology: 'P2-SD',
427
+ capabilities: ['nonexistent'],
428
+ depends_on: ['ghost'],
429
+ gates: [
430
+ { type: 'algorithmic', check: '!!!' }, // syntax error
431
+ ],
432
+ },
433
+ {
434
+ id: 'a', // duplicate ID
435
+ type: 'script',
436
+ script: 'return {};',
437
+ },
438
+ ],
439
+ },
440
+ },
441
+ };
442
+ const dag = parseStrategyObject(obj);
443
+ const result = validateStrategyDAG(dag);
444
+ assert.equal(result.valid, false);
445
+ // Should have at least 3 errors: duplicate ID, missing depends_on, bad capability
446
+ assert.ok(result.errors.length >= 3, `Expected >= 3 errors, got ${result.errors.length}: ${result.errors.join('; ')}`);
447
+ });
448
+ it('detects invalid strategy_gates depends_on reference', () => {
449
+ const obj = {
450
+ strategy: {
451
+ id: 'S-BAD-SG',
452
+ name: 'Bad Strategy Gate',
453
+ version: '1.0',
454
+ dag: {
455
+ nodes: [
456
+ { id: 'a', type: 'script', script: 'return {};' },
457
+ ],
458
+ strategy_gates: [
459
+ { id: 'sg1', depends_on: ['nonexistent'], type: 'algorithmic', check: 'true' },
460
+ ],
461
+ },
462
+ },
463
+ };
464
+ const dag = parseStrategyObject(obj);
465
+ const result = validateStrategyDAG(dag);
466
+ assert.equal(result.valid, false);
467
+ assert.ok(result.errors.some((e) => e.includes('Strategy gate "sg1"') && e.includes('unknown node')));
468
+ });
469
+ });
470
+ // ── Topological Sort Tests ──────────────────────────────────────
471
+ describe('topologicalSort', () => {
472
+ it('linear chain: A -> B -> C produces [[A], [B], [C]]', () => {
473
+ const obj = {
474
+ strategy: {
475
+ id: 'S-LINEAR',
476
+ name: 'Linear',
477
+ version: '1.0',
478
+ dag: {
479
+ nodes: [
480
+ { id: 'A', type: 'script', script: 'return {};' },
481
+ { id: 'B', type: 'script', script: 'return {};', depends_on: ['A'] },
482
+ { id: 'C', type: 'script', script: 'return {};', depends_on: ['B'] },
483
+ ],
484
+ },
485
+ },
486
+ };
487
+ const dag = parseStrategyObject(obj);
488
+ const levels = topologicalSort(dag);
489
+ assert.equal(levels.length, 3);
490
+ assert.deepEqual(levels[0], ['A']);
491
+ assert.deepEqual(levels[1], ['B']);
492
+ assert.deepEqual(levels[2], ['C']);
493
+ });
494
+ it('diamond: A -> B, A -> C, B -> D, C -> D produces [[A], [B, C], [D]]', () => {
495
+ const obj = {
496
+ strategy: {
497
+ id: 'S-DIAMOND',
498
+ name: 'Diamond',
499
+ version: '1.0',
500
+ dag: {
501
+ nodes: [
502
+ { id: 'A', type: 'script', script: 'return {};' },
503
+ { id: 'B', type: 'script', script: 'return {};', depends_on: ['A'] },
504
+ { id: 'C', type: 'script', script: 'return {};', depends_on: ['A'] },
505
+ { id: 'D', type: 'script', script: 'return {};', depends_on: ['B', 'C'] },
506
+ ],
507
+ },
508
+ },
509
+ };
510
+ const dag = parseStrategyObject(obj);
511
+ const levels = topologicalSort(dag);
512
+ assert.equal(levels.length, 3);
513
+ assert.deepEqual(levels[0], ['A']);
514
+ // B and C should be at the same level (order may vary)
515
+ assert.equal(levels[1].length, 2);
516
+ assert.ok(levels[1].includes('B'));
517
+ assert.ok(levels[1].includes('C'));
518
+ assert.deepEqual(levels[2], ['D']);
519
+ });
520
+ it('independent nodes at same level', () => {
521
+ const obj = {
522
+ strategy: {
523
+ id: 'S-INDEP',
524
+ name: 'Independent',
525
+ version: '1.0',
526
+ dag: {
527
+ nodes: [
528
+ { id: 'X', type: 'script', script: 'return {};' },
529
+ { id: 'Y', type: 'script', script: 'return {};' },
530
+ { id: 'Z', type: 'script', script: 'return {};' },
531
+ ],
532
+ },
533
+ },
534
+ };
535
+ const dag = parseStrategyObject(obj);
536
+ const levels = topologicalSort(dag);
537
+ assert.equal(levels.length, 1);
538
+ assert.equal(levels[0].length, 3);
539
+ assert.ok(levels[0].includes('X'));
540
+ assert.ok(levels[0].includes('Y'));
541
+ assert.ok(levels[0].includes('Z'));
542
+ });
543
+ it('single node produces [[node]]', () => {
544
+ const obj = {
545
+ strategy: {
546
+ id: 'S-SINGLE',
547
+ name: 'Single',
548
+ version: '1.0',
549
+ dag: {
550
+ nodes: [
551
+ { id: 'only', type: 'script', script: 'return {};' },
552
+ ],
553
+ },
554
+ },
555
+ };
556
+ const dag = parseStrategyObject(obj);
557
+ const levels = topologicalSort(dag);
558
+ assert.equal(levels.length, 1);
559
+ assert.deepEqual(levels[0], ['only']);
560
+ });
561
+ it('throws on cyclic DAG', () => {
562
+ const dag = {
563
+ id: 'S-CYCLE',
564
+ name: 'Cycle',
565
+ version: '1.0',
566
+ nodes: [
567
+ {
568
+ id: 'a',
569
+ type: 'script',
570
+ depends_on: ['b'],
571
+ inputs: [],
572
+ outputs: [],
573
+ gates: [],
574
+ config: { type: 'script', script: 'return {};' },
575
+ },
576
+ {
577
+ id: 'b',
578
+ type: 'script',
579
+ depends_on: ['a'],
580
+ inputs: [],
581
+ outputs: [],
582
+ gates: [],
583
+ config: { type: 'script', script: 'return {};' },
584
+ },
585
+ ],
586
+ strategy_gates: [],
587
+ capabilities: {},
588
+ oversight_rules: [],
589
+ context_inputs: [],
590
+ };
591
+ assert.throws(() => topologicalSort(dag), /cycle/i);
592
+ });
593
+ });
594
+ // ── Executor Integration Tests ──────────────────────────────────
595
+ describe('StrategyExecutor', () => {
596
+ it('3-node Strategy end-to-end: methodology -> methodology -> script', async () => {
597
+ const provider = new MockAgentProvider();
598
+ // Set up mock results for each node
599
+ provider.setResponse('node "analyze"', makeMockResult({
600
+ output: '```json\n{"analysis": "code needs refactoring"}\n```',
601
+ cost: { totalUsd: 0.10, perModel: {} },
602
+ turns: 3,
603
+ durationMs: 2000,
604
+ }));
605
+ provider.setResponse('node "implement"', makeMockResult({
606
+ output: '```json\n{"tests_passed": true, "code_changes": {"files": ["a.ts"]}}\n```',
607
+ cost: { totalUsd: 0.25, perModel: {} },
608
+ turns: 5,
609
+ durationMs: 5000,
610
+ }));
611
+ const config = makeExecutorConfig();
612
+ const executor = new StrategyExecutor(provider, config);
613
+ const dag = parseStrategyYaml(TEST_STRATEGY_YAML);
614
+ const result = await executor.execute(dag, { task_description: 'refactor module X' });
615
+ assert.equal(result.strategy_id, 'S-TEST-3NODE');
616
+ assert.equal(result.status, 'completed');
617
+ // All 3 nodes completed
618
+ assert.equal(Object.keys(result.node_results).length, 3);
619
+ assert.equal(result.node_results['analyze'].status, 'completed');
620
+ assert.equal(result.node_results['implement'].status, 'completed');
621
+ assert.equal(result.node_results['summarize'].status, 'completed');
622
+ // Cost tracked
623
+ assert.ok(result.cost_usd > 0, 'cost should be tracked');
624
+ // Artifacts flow correctly
625
+ assert.ok(result.artifacts['analysis'], 'analysis artifact should exist');
626
+ assert.ok(result.artifacts['code_changes'], 'code_changes artifact should exist');
627
+ assert.ok(result.artifacts['summary'], 'summary artifact should exist');
628
+ // Script node output: the 'summary' output key maps to the string value
629
+ // from { summary: 'Completed: ...' } returned by the script
630
+ const summaryContent = result.artifacts['summary'].content;
631
+ assert.ok(typeof summaryContent === 'string' &&
632
+ summaryContent.includes('Completed:'), 'summary artifact should contain the script output string');
633
+ });
634
+ it('parallel execution: two independent nodes', async () => {
635
+ // Create a provider with built-in delay
636
+ class DelayProvider {
637
+ name = 'delay';
638
+ invocationOrder = [];
639
+ capabilities() {
640
+ return { modes: ['oneshot'], streaming: false, resumable: false, budgetEnforcement: 'none', outputValidation: 'client', toolModel: 'builtin' };
641
+ }
642
+ async invoke(_pact, request) {
643
+ // Extract node name from prompt
644
+ const match = request.prompt.match(/node "(\w+)"/);
645
+ const nodeName = match ? match[1] : 'unknown';
646
+ this.invocationOrder.push(nodeName);
647
+ // Simulate 50ms work
648
+ await new Promise((resolve) => setTimeout(resolve, 50));
649
+ return makeMockResult({
650
+ output: `\`\`\`json\n{"output_${nodeName}": true}\n\`\`\``,
651
+ cost: { totalUsd: 0.05, perModel: {} },
652
+ });
653
+ }
654
+ }
655
+ const delayProvider = new DelayProvider();
656
+ const config = makeExecutorConfig({ maxParallel: 5 });
657
+ const executor = new StrategyExecutor(delayProvider, config);
658
+ // Two independent nodes + one dependent
659
+ const yamlStr = `
660
+ strategy:
661
+ id: S-PARALLEL
662
+ name: "Parallel Test"
663
+ version: "1.0"
664
+ dag:
665
+ nodes:
666
+ - id: alpha
667
+ type: methodology
668
+ methodology: P2-SD
669
+ outputs: [out_alpha]
670
+ - id: beta
671
+ type: methodology
672
+ methodology: P2-SD
673
+ outputs: [out_beta]
674
+ - id: merge
675
+ type: script
676
+ script: "return { merged: true };"
677
+ inputs: [out_alpha, out_beta]
678
+ outputs: [merged]
679
+ depends_on: [alpha, beta]
680
+ `;
681
+ const dag = parseStrategyYaml(yamlStr);
682
+ const startTime = Date.now();
683
+ const result = await executor.execute(dag, {});
684
+ const wallTime = Date.now() - startTime;
685
+ assert.equal(result.status, 'completed');
686
+ assert.equal(Object.keys(result.node_results).length, 3);
687
+ // Both alpha and beta should have been invoked (order may vary since they're parallel)
688
+ assert.ok(delayProvider.invocationOrder.includes('alpha'), 'alpha should have been invoked');
689
+ assert.ok(delayProvider.invocationOrder.includes('beta'), 'beta should have been invoked');
690
+ // Wall time should be less than 50ms * 3 (sequential would be ~150ms+)
691
+ // With parallel alpha+beta, it should be ~100ms+ (50ms for alpha|beta parallel + 0ms for script)
692
+ // Using a generous threshold to avoid flaky tests
693
+ assert.ok(wallTime < 300, `Wall time ${wallTime}ms should be less than sequential (300ms threshold)`);
694
+ });
695
+ it('gate failure with retry: first attempt fails, second succeeds', async () => {
696
+ let callCount = 0;
697
+ class RetryProvider {
698
+ name = 'retry';
699
+ capabilities() {
700
+ return { modes: ['oneshot'], streaming: false, resumable: false, budgetEnforcement: 'none', outputValidation: 'client', toolModel: 'builtin' };
701
+ }
702
+ async invoke(_pact, _request) {
703
+ callCount++;
704
+ if (callCount === 1) {
705
+ // First attempt: gate will fail (tests_passed is false)
706
+ return makeMockResult({
707
+ output: '```json\n{"tests_passed": false, "analysis": "needs work"}\n```',
708
+ cost: { totalUsd: 0.05, perModel: {} },
709
+ });
710
+ }
711
+ // Second attempt: gate will pass
712
+ return makeMockResult({
713
+ output: '```json\n{"tests_passed": true, "analysis": "looks good"}\n```',
714
+ cost: { totalUsd: 0.05, perModel: {} },
715
+ });
716
+ }
717
+ }
718
+ const config = makeExecutorConfig();
719
+ const executor = new StrategyExecutor(new RetryProvider(), config);
720
+ const yamlStr = `
721
+ strategy:
722
+ id: S-RETRY
723
+ name: "Retry Test"
724
+ version: "1.0"
725
+ dag:
726
+ nodes:
727
+ - id: work
728
+ type: methodology
729
+ methodology: P2-SD
730
+ outputs: [result]
731
+ gates:
732
+ - type: algorithmic
733
+ check: "output.tests_passed === true"
734
+ max_retries: 3
735
+ `;
736
+ const dag = parseStrategyYaml(yamlStr);
737
+ const result = await executor.execute(dag, {});
738
+ assert.equal(result.status, 'completed');
739
+ assert.equal(result.node_results['work'].status, 'completed');
740
+ assert.equal(result.node_results['work'].retries, 1);
741
+ assert.equal(callCount, 2, 'Provider should have been called twice (1 initial + 1 retry)');
742
+ });
743
+ it('gate failure exhausts retries: node status is gate_failed', async () => {
744
+ class AlwaysFailProvider {
745
+ name = 'always-fail';
746
+ capabilities() {
747
+ return { modes: ['oneshot'], streaming: false, resumable: false, budgetEnforcement: 'none', outputValidation: 'client', toolModel: 'builtin' };
748
+ }
749
+ async invoke() {
750
+ return makeMockResult({
751
+ output: '```json\n{"tests_passed": false}\n```',
752
+ cost: { totalUsd: 0.02, perModel: {} },
753
+ });
754
+ }
755
+ }
756
+ const config = makeExecutorConfig();
757
+ const executor = new StrategyExecutor(new AlwaysFailProvider(), config);
758
+ const yamlStr = `
759
+ strategy:
760
+ id: S-EXHAUST
761
+ name: "Exhaust Retries"
762
+ version: "1.0"
763
+ dag:
764
+ nodes:
765
+ - id: flaky
766
+ type: methodology
767
+ methodology: P2-SD
768
+ outputs: [result]
769
+ gates:
770
+ - type: algorithmic
771
+ check: "output.tests_passed === true"
772
+ max_retries: 2
773
+ `;
774
+ const dag = parseStrategyYaml(yamlStr);
775
+ const result = await executor.execute(dag, {});
776
+ assert.equal(result.status, 'failed');
777
+ assert.equal(result.node_results['flaky'].status, 'gate_failed');
778
+ assert.equal(result.node_results['flaky'].retries, 2);
779
+ });
780
+ it('script node executes correctly: inputs flow in, output stored', async () => {
781
+ const config = makeExecutorConfig();
782
+ const executor = new StrategyExecutor(new MockAgentProvider(), config);
783
+ const yamlStr = `
784
+ strategy:
785
+ id: S-SCRIPT-EXEC
786
+ name: "Script Execution"
787
+ version: "1.0"
788
+ context:
789
+ inputs:
790
+ - { name: count, type: number }
791
+ dag:
792
+ nodes:
793
+ - id: compute
794
+ type: script
795
+ script: "return { doubled: inputs.count * 2, label: 'result' };"
796
+ inputs: [count]
797
+ outputs: [doubled, label]
798
+ `;
799
+ const dag = parseStrategyYaml(yamlStr);
800
+ const result = await executor.execute(dag, { count: 21 });
801
+ assert.equal(result.status, 'completed');
802
+ assert.equal(result.node_results['compute'].status, 'completed');
803
+ // The output is stored as artifacts
804
+ const doubledArtifact = result.artifacts['doubled'];
805
+ assert.ok(doubledArtifact, 'doubled artifact should exist');
806
+ assert.equal(doubledArtifact.content, 42);
807
+ const labelArtifact = result.artifacts['label'];
808
+ assert.ok(labelArtifact, 'label artifact should exist');
809
+ assert.equal(labelArtifact.content, 'result');
810
+ });
811
+ it('artifact dependency filtering: node receives only declared inputs', async () => {
812
+ // Track what the second node receives
813
+ let secondNodePrompt = '';
814
+ class TrackingProvider {
815
+ name = 'tracking';
816
+ capabilities() {
817
+ return { modes: ['oneshot'], streaming: false, resumable: false, budgetEnforcement: 'none', outputValidation: 'client', toolModel: 'builtin' };
818
+ }
819
+ async invoke(_pact, request) {
820
+ if (request.prompt.includes('node "second"')) {
821
+ secondNodePrompt = request.prompt;
822
+ }
823
+ return makeMockResult({
824
+ output: '```json\n{"data": "from_node"}\n```',
825
+ cost: { totalUsd: 0.01, perModel: {} },
826
+ });
827
+ }
828
+ }
829
+ const config = makeExecutorConfig();
830
+ const executor = new StrategyExecutor(new TrackingProvider(), config);
831
+ const yamlStr = `
832
+ strategy:
833
+ id: S-FILTER
834
+ name: "Filter Test"
835
+ version: "1.0"
836
+ context:
837
+ inputs:
838
+ - { name: secret, type: string }
839
+ - { name: public_data, type: string }
840
+ dag:
841
+ nodes:
842
+ - id: first
843
+ type: methodology
844
+ methodology: P2-SD
845
+ inputs: [secret]
846
+ outputs: [processed]
847
+ - id: second
848
+ type: methodology
849
+ methodology: P2-SD
850
+ inputs: [public_data]
851
+ outputs: [result]
852
+ depends_on: [first]
853
+ `;
854
+ const dag = parseStrategyYaml(yamlStr);
855
+ const result = await executor.execute(dag, {
856
+ secret: 'top-secret-value',
857
+ public_data: 'public-info',
858
+ });
859
+ assert.equal(result.status, 'completed');
860
+ // The second node's prompt should contain public_data but NOT secret
861
+ assert.ok(secondNodePrompt.includes('public_data'), 'should receive public_data');
862
+ assert.ok(secondNodePrompt.includes('public-info'), 'should receive public_data value');
863
+ assert.ok(!secondNodePrompt.includes('top-secret-value'), 'should NOT receive secret value');
864
+ });
865
+ it('oversight rule triggers on high cost', async () => {
866
+ class ExpensiveProvider {
867
+ name = 'expensive';
868
+ capabilities() {
869
+ return { modes: ['oneshot'], streaming: false, resumable: false, budgetEnforcement: 'none', outputValidation: 'client', toolModel: 'builtin' };
870
+ }
871
+ async invoke() {
872
+ return makeMockResult({
873
+ output: '```json\n{"done": true}\n```',
874
+ cost: { totalUsd: 3.00, perModel: {} }, // Expensive!
875
+ });
876
+ }
877
+ }
878
+ const config = makeExecutorConfig();
879
+ const executor = new StrategyExecutor(new ExpensiveProvider(), config);
880
+ const yamlStr = `
881
+ strategy:
882
+ id: S-COSTLY
883
+ name: "Costly Strategy"
884
+ version: "1.0"
885
+ dag:
886
+ nodes:
887
+ - id: expensive_a
888
+ type: methodology
889
+ methodology: P2-SD
890
+ outputs: [out_a]
891
+ - id: expensive_b
892
+ type: methodology
893
+ methodology: P2-SD
894
+ outputs: [out_b]
895
+ - id: final
896
+ type: script
897
+ script: "return { done: true };"
898
+ depends_on: [expensive_a, expensive_b]
899
+ outputs: [result]
900
+ oversight:
901
+ rules:
902
+ - { condition: "total_cost_usd > 5.00", action: warn_human }
903
+ `;
904
+ const dag = parseStrategyYaml(yamlStr);
905
+ const result = await executor.execute(dag, {});
906
+ // Both expensive nodes cost $3 each = $6 total > $5 threshold
907
+ assert.equal(result.status, 'completed'); // warn_human doesn't suspend
908
+ assert.ok(result.oversight_events.length > 0, 'should have triggered oversight event');
909
+ assert.equal(result.oversight_events[0].rule.action, 'warn_human');
910
+ assert.equal(result.oversight_events[0].rule.condition, 'total_cost_usd > 5.00');
911
+ });
912
+ it('context inputs stored as initial artifacts', async () => {
913
+ const config = makeExecutorConfig();
914
+ const executor = new StrategyExecutor(new MockAgentProvider(), config);
915
+ const yamlStr = `
916
+ strategy:
917
+ id: S-CTX
918
+ name: "Context Test"
919
+ version: "1.0"
920
+ context:
921
+ inputs:
922
+ - { name: project_name, type: string }
923
+ - { name: target_version, type: string }
924
+ dag:
925
+ nodes:
926
+ - id: use_ctx
927
+ type: script
928
+ script: "return { msg: inputs.project_name + ' v' + inputs.target_version };"
929
+ inputs: [project_name, target_version]
930
+ outputs: [msg]
931
+ `;
932
+ const dag = parseStrategyYaml(yamlStr);
933
+ const result = await executor.execute(dag, {
934
+ project_name: 'method',
935
+ target_version: '2.0',
936
+ });
937
+ assert.equal(result.status, 'completed');
938
+ // Context inputs should be in artifacts
939
+ assert.ok(result.artifacts['project_name'], 'project_name should be in artifacts');
940
+ assert.equal(result.artifacts['project_name'].content, 'method');
941
+ assert.equal(result.artifacts['project_name'].producer_node_id, '__context__');
942
+ // Script output should have used the context inputs
943
+ const msg = result.artifacts['msg'];
944
+ assert.ok(msg, 'msg artifact should exist');
945
+ assert.equal(msg.content, 'method v2.0');
946
+ });
947
+ it('escalate_to_human oversight rule suspends execution', async () => {
948
+ class FailingProvider {
949
+ name = 'failing';
950
+ callCount = 0;
951
+ capabilities() {
952
+ return { modes: ['oneshot'], streaming: false, resumable: false, budgetEnforcement: 'none', outputValidation: 'client', toolModel: 'builtin' };
953
+ }
954
+ async invoke() {
955
+ this.callCount++;
956
+ return makeMockResult({
957
+ output: '```json\n{"fail": true}\n```',
958
+ cost: { totalUsd: 0.01, perModel: {} },
959
+ });
960
+ }
961
+ }
962
+ const config = makeExecutorConfig();
963
+ const executor = new StrategyExecutor(new FailingProvider(), config);
964
+ const yamlStr = `
965
+ strategy:
966
+ id: S-ESCALATE
967
+ name: "Escalate Test"
968
+ version: "1.0"
969
+ dag:
970
+ nodes:
971
+ - id: retry_node
972
+ type: methodology
973
+ methodology: P2-SD
974
+ outputs: [result]
975
+ gates:
976
+ - type: algorithmic
977
+ check: "output.success === true"
978
+ max_retries: 5
979
+ - id: after_node
980
+ type: script
981
+ script: "return { final: true };"
982
+ depends_on: [retry_node]
983
+ outputs: [final]
984
+ oversight:
985
+ rules:
986
+ - { condition: "gate_failures >= 3 on same step", action: escalate_to_human }
987
+ `;
988
+ const dag = parseStrategyYaml(yamlStr);
989
+ const result = await executor.execute(dag, {});
990
+ assert.equal(result.status, 'suspended');
991
+ assert.ok(result.oversight_events.length > 0);
992
+ assert.equal(result.oversight_events[0].rule.action, 'escalate_to_human');
993
+ // The after_node should NOT have executed
994
+ assert.ok(!result.node_results['after_node'] ||
995
+ result.node_results['after_node'].status === 'pending', 'after_node should not have executed');
996
+ });
997
+ it('getState() returns null before execution', () => {
998
+ const config = makeExecutorConfig();
999
+ const executor = new StrategyExecutor(new MockAgentProvider(), config);
1000
+ assert.equal(executor.getState(), null);
1001
+ });
1002
+ it('getState() returns state during/after execution', async () => {
1003
+ const config = makeExecutorConfig();
1004
+ const executor = new StrategyExecutor(new MockAgentProvider(), config);
1005
+ const yamlStr = `
1006
+ strategy:
1007
+ id: S-STATE
1008
+ name: "State Test"
1009
+ version: "1.0"
1010
+ dag:
1011
+ nodes:
1012
+ - id: simple
1013
+ type: script
1014
+ script: "return { done: true };"
1015
+ outputs: [result]
1016
+ `;
1017
+ const dag = parseStrategyYaml(yamlStr);
1018
+ await executor.execute(dag, {});
1019
+ const state = executor.getState();
1020
+ assert.ok(state, 'state should exist after execution');
1021
+ assert.equal(state.strategy_id, 'S-STATE');
1022
+ assert.ok(state.started_at);
1023
+ assert.ok(state.completed_at);
1024
+ assert.ok(state.levels.length > 0);
1025
+ });
1026
+ it('invalid DAG throws during execution', async () => {
1027
+ const config = makeExecutorConfig();
1028
+ const executor = new StrategyExecutor(new MockAgentProvider(), config);
1029
+ const dag = {
1030
+ id: 'S-INVALID',
1031
+ name: 'Invalid',
1032
+ version: '1.0',
1033
+ nodes: [
1034
+ {
1035
+ id: 'a',
1036
+ type: 'script',
1037
+ depends_on: ['nonexistent'],
1038
+ inputs: [],
1039
+ outputs: [],
1040
+ gates: [],
1041
+ config: { type: 'script', script: 'return {};' },
1042
+ },
1043
+ ],
1044
+ strategy_gates: [],
1045
+ capabilities: {},
1046
+ oversight_rules: [],
1047
+ context_inputs: [],
1048
+ };
1049
+ await assert.rejects(() => executor.execute(dag, {}), /Invalid Strategy DAG/);
1050
+ });
1051
+ it('strategy gate failure causes failed status', async () => {
1052
+ const config = makeExecutorConfig();
1053
+ const executor = new StrategyExecutor(new MockAgentProvider(), config);
1054
+ const yamlStr = `
1055
+ strategy:
1056
+ id: S-SG-FAIL
1057
+ name: "Strategy Gate Fail"
1058
+ version: "1.0"
1059
+ dag:
1060
+ nodes:
1061
+ - id: work
1062
+ type: script
1063
+ script: "return { count: 5 };"
1064
+ outputs: [count]
1065
+ strategy_gates:
1066
+ - id: minimum_count
1067
+ depends_on: [work]
1068
+ type: algorithmic
1069
+ check: "artifacts.count > 100"
1070
+ `;
1071
+ const dag = parseStrategyYaml(yamlStr);
1072
+ const result = await executor.execute(dag, {});
1073
+ // Node completed but strategy gate failed
1074
+ assert.equal(result.node_results['work'].status, 'completed');
1075
+ assert.equal(result.status, 'failed');
1076
+ assert.ok(result.gate_results.some((gr) => gr.gate_id === 'strategy:minimum_count' && !gr.passed), 'strategy gate should have failed');
1077
+ });
1078
+ it('methodology node with empty methodology field fails validation', () => {
1079
+ const obj = {
1080
+ strategy: {
1081
+ id: 'S-EMPTY-METH',
1082
+ name: 'Empty Methodology',
1083
+ version: '1.0',
1084
+ dag: {
1085
+ nodes: [
1086
+ {
1087
+ id: 'bad_node',
1088
+ type: 'methodology',
1089
+ methodology: '',
1090
+ outputs: ['result'],
1091
+ },
1092
+ ],
1093
+ },
1094
+ },
1095
+ };
1096
+ const dag = parseStrategyObject(obj);
1097
+ const validation = validateStrategyDAG(dag);
1098
+ assert.equal(validation.valid, false);
1099
+ assert.ok(validation.errors.some((e) => e.includes('non-empty "methodology" field')), `Expected methodology validation error, got: ${validation.errors.join('; ')}`);
1100
+ });
1101
+ it('methodology node with missing methodology field fails validation', () => {
1102
+ const obj = {
1103
+ strategy: {
1104
+ id: 'S-MISSING-METH',
1105
+ name: 'Missing Methodology',
1106
+ version: '1.0',
1107
+ dag: {
1108
+ nodes: [
1109
+ {
1110
+ id: 'bad_node',
1111
+ type: 'methodology',
1112
+ // methodology field omitted — parser defaults to ''
1113
+ outputs: ['result'],
1114
+ },
1115
+ ],
1116
+ },
1117
+ },
1118
+ };
1119
+ const dag = parseStrategyObject(obj);
1120
+ const validation = validateStrategyDAG(dag);
1121
+ assert.equal(validation.valid, false);
1122
+ assert.ok(validation.errors.some((e) => e.includes('non-empty "methodology" field')), `Expected methodology validation error, got: ${validation.errors.join('; ')}`);
1123
+ });
1124
+ it('LLM response without JSON code block is handled gracefully', async () => {
1125
+ class PlainTextProvider {
1126
+ name = 'plain-text';
1127
+ capabilities() {
1128
+ return { modes: ['oneshot'], streaming: false, resumable: false, budgetEnforcement: 'none', outputValidation: 'client', toolModel: 'builtin' };
1129
+ }
1130
+ async invoke() {
1131
+ return makeMockResult({
1132
+ output: 'I completed the task successfully. Everything looks good.',
1133
+ cost: { totalUsd: 0.03, perModel: {} },
1134
+ });
1135
+ }
1136
+ }
1137
+ const config = makeExecutorConfig();
1138
+ const executor = new StrategyExecutor(new PlainTextProvider(), config);
1139
+ const yamlStr = `
1140
+ strategy:
1141
+ id: S-PLAIN
1142
+ name: "Plain Response"
1143
+ version: "1.0"
1144
+ dag:
1145
+ nodes:
1146
+ - id: work
1147
+ type: methodology
1148
+ methodology: P2-SD
1149
+ outputs: [output]
1150
+ `;
1151
+ const dag = parseStrategyYaml(yamlStr);
1152
+ const result = await executor.execute(dag, {});
1153
+ // Should complete — output is the raw text
1154
+ assert.equal(result.status, 'completed');
1155
+ assert.equal(result.node_results['work'].status, 'completed');
1156
+ assert.ok(result.node_results['work'].output.result, 'should have raw result');
1157
+ });
1158
+ it('context continuity with refresh_context flag', async () => {
1159
+ // Test that:
1160
+ // 1. Without refresh_context: nodes 1 & 2 use the SAME session (continuous context)
1161
+ // 2. With refresh_context=true on node 2: node 3 gets a FRESH session
1162
+ //
1163
+ // Track sessionId and refreshSessionId in invocations to verify behavior
1164
+ class SessionTrackingProvider {
1165
+ name = 'session-tracking';
1166
+ invocations = [];
1167
+ capabilities() {
1168
+ return { modes: ['oneshot'], streaming: false, resumable: false, budgetEnforcement: 'none', outputValidation: 'client', toolModel: 'builtin' };
1169
+ }
1170
+ async invoke(_pact, request) {
1171
+ this.invocations.push(request);
1172
+ // Extract node name from prompt for debugging
1173
+ const match = request.prompt.match(/node "(\w+)"/);
1174
+ const nodeName = match ? match[1] : 'unknown';
1175
+ return makeMockResult({
1176
+ output: `\`\`\`json\n{"node": "${nodeName}", "done": true}\n\`\`\``,
1177
+ cost: { totalUsd: 0.02, perModel: {} },
1178
+ });
1179
+ }
1180
+ }
1181
+ const provider = new SessionTrackingProvider();
1182
+ const config = makeExecutorConfig();
1183
+ const executor = new StrategyExecutor(provider, config);
1184
+ const yamlStr = `
1185
+ strategy:
1186
+ id: S-CONTEXT-TEST
1187
+ name: "Context Continuity Test"
1188
+ version: "1.0"
1189
+ dag:
1190
+ nodes:
1191
+ - id: analyze
1192
+ type: methodology
1193
+ methodology: P2-SD
1194
+ outputs: [analysis]
1195
+ - id: design
1196
+ type: methodology
1197
+ methodology: P2-SD
1198
+ depends_on: [analyze]
1199
+ inputs: [analysis]
1200
+ outputs: [design]
1201
+ refresh_context: false
1202
+ - id: validate
1203
+ type: methodology
1204
+ methodology: P2-SD
1205
+ depends_on: [design]
1206
+ inputs: [design]
1207
+ outputs: [validation]
1208
+ refresh_context: true
1209
+ `;
1210
+ const dag = parseStrategyYaml(yamlStr);
1211
+ // Verify refresh_context fields were parsed correctly
1212
+ const analyzeNode = dag.nodes.find((n) => n.id === 'analyze');
1213
+ const designNode = dag.nodes.find((n) => n.id === 'design');
1214
+ const validateNode = dag.nodes.find((n) => n.id === 'validate');
1215
+ assert.equal(analyzeNode.refresh_context, false, 'analyze node should default to false');
1216
+ assert.equal(designNode.refresh_context, false, 'design node explicitly set to false');
1217
+ assert.equal(validateNode.refresh_context, true, 'validate node explicitly set to true');
1218
+ const result = await executor.execute(dag, {});
1219
+ // Verify execution completed successfully
1220
+ assert.equal(result.status, 'completed');
1221
+ assert.equal(result.node_results['analyze'].status, 'completed');
1222
+ assert.equal(result.node_results['design'].status, 'completed');
1223
+ assert.equal(result.node_results['validate'].status, 'completed');
1224
+ // Verify invocation tracking
1225
+ assert.equal(provider.invocations.length, 3, 'should have 3 invocations (one per node)');
1226
+ // Verify that each invocation has a prompt mentioning the correct node
1227
+ assert.ok(provider.invocations[0].prompt.includes('node "analyze"'), 'first invocation should be for analyze node');
1228
+ assert.ok(provider.invocations[1].prompt.includes('node "design"'), 'second invocation should be for design node');
1229
+ assert.ok(provider.invocations[2].prompt.includes('node "validate"'), 'third invocation should be for validate node');
1230
+ });
1231
+ });
1232
+ // ── Timeout Tests (isolated to avoid cascading cancellations) ──
1233
+ describe('StrategyExecutor — timeout enforcement', () => {
1234
+ it('provider that completes within timeout succeeds', async () => {
1235
+ // Verify that the timeout race doesn't interfere with normal execution.
1236
+ // Provider resolves in ~10ms, timeout is 5000ms — provider wins the race.
1237
+ class QuickProvider {
1238
+ name = 'quick';
1239
+ capabilities() {
1240
+ return { modes: ['oneshot'], streaming: false, resumable: false, budgetEnforcement: 'none', outputValidation: 'client', toolModel: 'builtin' };
1241
+ }
1242
+ async invoke(_pact, _request) {
1243
+ await new Promise((r) => setTimeout(r, 10));
1244
+ return makeMockResult({
1245
+ output: '```json\n{"fast": true}\n```',
1246
+ cost: { totalUsd: 0.01, perModel: {} },
1247
+ });
1248
+ }
1249
+ }
1250
+ const config = makeExecutorConfig({ defaultTimeoutMs: 5000 });
1251
+ const executor = new StrategyExecutor(new QuickProvider(), config);
1252
+ const yamlStr = `
1253
+ strategy:
1254
+ id: S-FAST
1255
+ name: "Fast Test"
1256
+ version: "1.0"
1257
+ dag:
1258
+ nodes:
1259
+ - id: quick_node
1260
+ type: methodology
1261
+ methodology: P2-SD
1262
+ outputs: [result]
1263
+ `;
1264
+ const dag = parseStrategyYaml(yamlStr);
1265
+ const result = await executor.execute(dag, {});
1266
+ assert.equal(result.status, 'completed');
1267
+ assert.equal(result.node_results['quick_node'].status, 'completed');
1268
+ });
1269
+ it('provider that rejects immediately is caught as node failure', async () => {
1270
+ // Verify that provider errors (not timeouts) are also properly caught.
1271
+ class FailingProvider {
1272
+ name = 'failing';
1273
+ capabilities() {
1274
+ return { modes: ['oneshot'], streaming: false, resumable: false, budgetEnforcement: 'none', outputValidation: 'client', toolModel: 'builtin' };
1275
+ }
1276
+ async invoke(_pact, _request) {
1277
+ throw new Error('Connection refused');
1278
+ }
1279
+ }
1280
+ const config = makeExecutorConfig({ defaultTimeoutMs: 5000 });
1281
+ const executor = new StrategyExecutor(new FailingProvider(), config);
1282
+ const yamlStr = `
1283
+ strategy:
1284
+ id: S-FAIL
1285
+ name: "Fail Test"
1286
+ version: "1.0"
1287
+ dag:
1288
+ nodes:
1289
+ - id: fail_node
1290
+ type: methodology
1291
+ methodology: P2-SD
1292
+ outputs: [result]
1293
+ `;
1294
+ const dag = parseStrategyYaml(yamlStr);
1295
+ const result = await executor.execute(dag, {});
1296
+ assert.equal(result.status, 'failed');
1297
+ assert.equal(result.node_results['fail_node'].status, 'failed');
1298
+ assert.ok(result.node_results['fail_node'].error?.includes('Connection refused'), `Expected connection error, got: ${result.node_results['fail_node'].error}`);
1299
+ });
1300
+ });
1301
+ //# sourceMappingURL=strategy-executor.test.js.map