@shipfox/api-workflows 11.0.0 → 12.0.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 (346) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/CHANGELOG.md +100 -0
  3. package/README.md +75 -0
  4. package/dist/core/agent-defaults.d.ts +2 -1
  5. package/dist/core/agent-defaults.d.ts.map +1 -1
  6. package/dist/core/agent-defaults.js.map +1 -1
  7. package/dist/core/checkout.d.ts +7 -21
  8. package/dist/core/checkout.d.ts.map +1 -1
  9. package/dist/core/checkout.js +122 -41
  10. package/dist/core/checkout.js.map +1 -1
  11. package/dist/core/entities/job-execution.d.ts +8 -0
  12. package/dist/core/entities/job-execution.d.ts.map +1 -1
  13. package/dist/core/entities/job-execution.js +13 -1
  14. package/dist/core/entities/job-execution.js.map +1 -1
  15. package/dist/core/entities/job-listener-event.d.ts +2 -0
  16. package/dist/core/entities/job-listener-event.d.ts.map +1 -1
  17. package/dist/core/entities/job-listener-event.js.map +1 -1
  18. package/dist/core/entities/step.d.ts +13 -3
  19. package/dist/core/entities/step.d.ts.map +1 -1
  20. package/dist/core/entities/step.js +6 -0
  21. package/dist/core/entities/step.js.map +1 -1
  22. package/dist/core/entities/workflow-run-attempt.d.ts +1 -0
  23. package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -1
  24. package/dist/core/entities/workflow-run-attempt.js.map +1 -1
  25. package/dist/core/entities/workflow-run.d.ts +17 -0
  26. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  27. package/dist/core/entities/workflow-run.js.map +1 -1
  28. package/dist/core/errors.d.ts +9 -2
  29. package/dist/core/errors.d.ts.map +1 -1
  30. package/dist/core/errors.js +10 -5
  31. package/dist/core/errors.js.map +1 -1
  32. package/dist/core/job-execution.d.ts.map +1 -1
  33. package/dist/core/job-execution.js +14 -8
  34. package/dist/core/job-execution.js.map +1 -1
  35. package/dist/core/job-transition/decide-job-activation.d.ts +1 -0
  36. package/dist/core/job-transition/decide-job-activation.d.ts.map +1 -1
  37. package/dist/core/job-transition/decide-job-activation.js +1 -0
  38. package/dist/core/job-transition/decide-job-activation.js.map +1 -1
  39. package/dist/core/job-transition/derive-job-success.d.ts +1 -0
  40. package/dist/core/job-transition/derive-job-success.d.ts.map +1 -1
  41. package/dist/core/job-transition/derive-job-success.js +5 -8
  42. package/dist/core/job-transition/derive-job-success.js.map +1 -1
  43. package/dist/core/listener-execution-materialization.d.ts +6 -3
  44. package/dist/core/listener-execution-materialization.d.ts.map +1 -1
  45. package/dist/core/listener-execution-materialization.js +44 -6
  46. package/dist/core/listener-execution-materialization.js.map +1 -1
  47. package/dist/core/resolve-trigger-reference.d.ts +11 -0
  48. package/dist/core/resolve-trigger-reference.d.ts.map +1 -0
  49. package/dist/core/resolve-trigger-reference.js +39 -0
  50. package/dist/core/resolve-trigger-reference.js.map +1 -0
  51. package/dist/core/run-workflow.d.ts +1 -0
  52. package/dist/core/run-workflow.d.ts.map +1 -1
  53. package/dist/core/run-workflow.js +1 -0
  54. package/dist/core/run-workflow.js.map +1 -1
  55. package/dist/core/step-config/agent.d.ts +1 -2
  56. package/dist/core/step-config/agent.d.ts.map +1 -1
  57. package/dist/core/step-config/agent.js +22 -7
  58. package/dist/core/step-config/agent.js.map +1 -1
  59. package/dist/core/step-config/assemble-run-context.d.ts +19 -8
  60. package/dist/core/step-config/assemble-run-context.d.ts.map +1 -1
  61. package/dist/core/step-config/assemble-run-context.js +51 -19
  62. package/dist/core/step-config/assemble-run-context.js.map +1 -1
  63. package/dist/core/step-config/complete-step-dispatch-config.d.ts.map +1 -1
  64. package/dist/core/step-config/complete-step-dispatch-config.js +72 -4
  65. package/dist/core/step-config/complete-step-dispatch-config.js.map +1 -1
  66. package/dist/core/step-config/index.d.ts +1 -0
  67. package/dist/core/step-config/index.d.ts.map +1 -1
  68. package/dist/core/step-config/index.js +1 -0
  69. package/dist/core/step-config/index.js.map +1 -1
  70. package/dist/core/step-config/materialize-job-execution-steps.d.ts +1 -1
  71. package/dist/core/step-config/materialize-job-execution-steps.d.ts.map +1 -1
  72. package/dist/core/step-config/materialize-job-execution-steps.js +49 -8
  73. package/dist/core/step-config/materialize-job-execution-steps.js.map +1 -1
  74. package/dist/core/step-config/materialize-workflow-model.d.ts +3 -3
  75. package/dist/core/step-config/materialize-workflow-model.d.ts.map +1 -1
  76. package/dist/core/step-config/materialize-workflow-model.js +9 -5
  77. package/dist/core/step-config/materialize-workflow-model.js.map +1 -1
  78. package/dist/core/step-config/resolve-job-execution-name.d.ts +3 -3
  79. package/dist/core/step-config/resolve-job-execution-name.d.ts.map +1 -1
  80. package/dist/core/step-config/resolve-job-execution-name.js +24 -13
  81. package/dist/core/step-config/resolve-job-execution-name.js.map +1 -1
  82. package/dist/core/step-config/resolve-step-config.d.ts.map +1 -1
  83. package/dist/core/step-config/resolve-step-config.js +174 -9
  84. package/dist/core/step-config/resolve-step-config.js.map +1 -1
  85. package/dist/core/step-config/resolve-workflow-run-name.d.ts +19 -0
  86. package/dist/core/step-config/resolve-workflow-run-name.d.ts.map +1 -0
  87. package/dist/core/step-config/resolve-workflow-run-name.js +73 -0
  88. package/dist/core/step-config/resolve-workflow-run-name.js.map +1 -0
  89. package/dist/core/step-transition/decide-step-transition.js.map +1 -1
  90. package/dist/core/step-transition/evaluate-gate.d.ts +2 -1
  91. package/dist/core/step-transition/evaluate-gate.d.ts.map +1 -1
  92. package/dist/core/step-transition/evaluate-gate.js +5 -3
  93. package/dist/core/step-transition/evaluate-gate.js.map +1 -1
  94. package/dist/core/workflow-run-creation.d.ts +3 -0
  95. package/dist/core/workflow-run-creation.d.ts.map +1 -1
  96. package/dist/core/workflow-run-creation.js +22 -7
  97. package/dist/core/workflow-run-creation.js.map +1 -1
  98. package/dist/db/db.d.ts +278 -10
  99. package/dist/db/db.d.ts.map +1 -1
  100. package/dist/db/db.js +2 -0
  101. package/dist/db/db.js.map +1 -1
  102. package/dist/db/index.d.ts +3 -2
  103. package/dist/db/index.d.ts.map +1 -1
  104. package/dist/db/index.js +2 -1
  105. package/dist/db/index.js.map +1 -1
  106. package/dist/db/job-listener-events.d.ts +2 -0
  107. package/dist/db/job-listener-events.d.ts.map +1 -1
  108. package/dist/db/job-listener-events.js +1 -0
  109. package/dist/db/job-listener-events.js.map +1 -1
  110. package/dist/db/job-listeners.d.ts +2 -0
  111. package/dist/db/job-listeners.d.ts.map +1 -1
  112. package/dist/db/job-listeners.js +81 -21
  113. package/dist/db/job-listeners.js.map +1 -1
  114. package/dist/db/schema/job-executions.d.ts +3 -3
  115. package/dist/db/schema/job-executions.d.ts.map +1 -1
  116. package/dist/db/schema/job-executions.js +8 -4
  117. package/dist/db/schema/job-executions.js.map +1 -1
  118. package/dist/db/schema/job-listener-events.d.ts +20 -0
  119. package/dist/db/schema/job-listener-events.d.ts.map +1 -1
  120. package/dist/db/schema/job-listener-events.js +2 -0
  121. package/dist/db/schema/job-listener-events.js.map +1 -1
  122. package/dist/db/schema/step-attempts.js +2 -2
  123. package/dist/db/schema/step-attempts.js.map +1 -1
  124. package/dist/db/schema/steps.d.ts +4 -2
  125. package/dist/db/schema/steps.d.ts.map +1 -1
  126. package/dist/db/schema/steps.js +1 -1
  127. package/dist/db/schema/steps.js.map +1 -1
  128. package/dist/db/schema/workflow-run-attempts.d.ts +21 -2
  129. package/dist/db/schema/workflow-run-attempts.d.ts.map +1 -1
  130. package/dist/db/schema/workflow-run-attempts.js +2 -0
  131. package/dist/db/schema/workflow-run-attempts.js.map +1 -1
  132. package/dist/db/schema/workflow-run-counters.d.ts +43 -0
  133. package/dist/db/schema/workflow-run-counters.d.ts.map +1 -0
  134. package/dist/db/schema/workflow-run-counters.js +10 -0
  135. package/dist/db/schema/workflow-run-counters.js.map +1 -0
  136. package/dist/db/schema/workflow-runs.d.ts +54 -1
  137. package/dist/db/schema/workflow-runs.d.ts.map +1 -1
  138. package/dist/db/schema/workflow-runs.js +10 -2
  139. package/dist/db/schema/workflow-runs.js.map +1 -1
  140. package/dist/db/workflow-runs/job-executions.d.ts.map +1 -1
  141. package/dist/db/workflow-runs/job-executions.js +35 -14
  142. package/dist/db/workflow-runs/job-executions.js.map +1 -1
  143. package/dist/db/workflow-runs/jobs.d.ts +2 -0
  144. package/dist/db/workflow-runs/jobs.d.ts.map +1 -1
  145. package/dist/db/workflow-runs/jobs.js +9 -5
  146. package/dist/db/workflow-runs/jobs.js.map +1 -1
  147. package/dist/db/workflow-runs/queries.d.ts +51 -0
  148. package/dist/db/workflow-runs/queries.d.ts.map +1 -1
  149. package/dist/db/workflow-runs/queries.js +82 -2
  150. package/dist/db/workflow-runs/queries.js.map +1 -1
  151. package/dist/db/workflow-runs/run-create.d.ts +1 -0
  152. package/dist/db/workflow-runs/run-create.d.ts.map +1 -1
  153. package/dist/db/workflow-runs/run-create.js +159 -32
  154. package/dist/db/workflow-runs/run-create.js.map +1 -1
  155. package/dist/db/workflow-runs/run-rerun.d.ts.map +1 -1
  156. package/dist/db/workflow-runs/run-rerun.js +13 -5
  157. package/dist/db/workflow-runs/run-rerun.js.map +1 -1
  158. package/dist/db/workflow-runs/shared.d.ts +1 -0
  159. package/dist/db/workflow-runs/shared.d.ts.map +1 -1
  160. package/dist/db/workflow-runs/shared.js +2 -1
  161. package/dist/db/workflow-runs/shared.js.map +1 -1
  162. package/dist/db/workflow-runs/steps.js +3 -3
  163. package/dist/db/workflow-runs/steps.js.map +1 -1
  164. package/dist/db/workflow-runs.d.ts +4 -3
  165. package/dist/db/workflow-runs.d.ts.map +1 -1
  166. package/dist/db/workflow-runs.js +3 -2
  167. package/dist/db/workflow-runs.js.map +1 -1
  168. package/dist/metrics/instance.d.ts +1 -0
  169. package/dist/metrics/instance.d.ts.map +1 -1
  170. package/dist/metrics/instance.js +9 -0
  171. package/dist/metrics/instance.js.map +1 -1
  172. package/dist/presentation/dto/checkout-token.d.ts +1 -0
  173. package/dist/presentation/dto/checkout-token.d.ts.map +1 -1
  174. package/dist/presentation/dto/checkout-token.js +1 -0
  175. package/dist/presentation/dto/checkout-token.js.map +1 -1
  176. package/dist/presentation/dto/index.d.ts +1 -1
  177. package/dist/presentation/dto/index.d.ts.map +1 -1
  178. package/dist/presentation/dto/index.js +1 -1
  179. package/dist/presentation/dto/index.js.map +1 -1
  180. package/dist/presentation/dto/step.d.ts.map +1 -1
  181. package/dist/presentation/dto/step.js +2 -2
  182. package/dist/presentation/dto/step.js.map +1 -1
  183. package/dist/presentation/dto/workflow-run.d.ts +3 -1
  184. package/dist/presentation/dto/workflow-run.d.ts.map +1 -1
  185. package/dist/presentation/dto/workflow-run.js +34 -0
  186. package/dist/presentation/dto/workflow-run.js.map +1 -1
  187. package/dist/presentation/inter-module.d.ts.map +1 -1
  188. package/dist/presentation/inter-module.js +31 -3
  189. package/dist/presentation/inter-module.js.map +1 -1
  190. package/dist/presentation/routes/checkout-token.d.ts +1 -1
  191. package/dist/presentation/routes/checkout-token.d.ts.map +1 -1
  192. package/dist/presentation/routes/checkout-token.js +32 -20
  193. package/dist/presentation/routes/checkout-token.js.map +1 -1
  194. package/dist/presentation/routes/leased-step.d.ts +2 -0
  195. package/dist/presentation/routes/leased-step.d.ts.map +1 -1
  196. package/dist/presentation/routes/leased-step.js +2 -1
  197. package/dist/presentation/routes/leased-step.js.map +1 -1
  198. package/dist/presentation/routes/list-runs.d.ts.map +1 -1
  199. package/dist/presentation/routes/list-runs.js +9 -3
  200. package/dist/presentation/routes/list-runs.js.map +1 -1
  201. package/dist/presentation/routes/project-access.d.ts +1 -0
  202. package/dist/presentation/routes/project-access.d.ts.map +1 -1
  203. package/dist/presentation/routes/report-step.d.ts.map +1 -1
  204. package/dist/presentation/routes/report-step.js +9 -2
  205. package/dist/presentation/routes/report-step.js.map +1 -1
  206. package/dist/presentation/subscribers/on-job-steps-settled.js +1 -1
  207. package/dist/presentation/subscribers/on-job-steps-settled.js.map +1 -1
  208. package/dist/presentation/subscribers/on-runner-job-claimed.d.ts.map +1 -1
  209. package/dist/presentation/subscribers/on-runner-job-claimed.js +21 -0
  210. package/dist/presentation/subscribers/on-runner-job-claimed.js.map +1 -1
  211. package/dist/temporal/activities/index.d.ts.map +1 -1
  212. package/dist/temporal/activities/index.js +2 -1
  213. package/dist/temporal/activities/index.js.map +1 -1
  214. package/dist/temporal/activities/orchestration-activities.d.ts +1 -0
  215. package/dist/temporal/activities/orchestration-activities.d.ts.map +1 -1
  216. package/dist/temporal/activities/orchestration-activities.js.map +1 -1
  217. package/dist/temporal/constants.d.ts +1 -0
  218. package/dist/temporal/constants.d.ts.map +1 -1
  219. package/dist/temporal/constants.js +1 -0
  220. package/dist/temporal/constants.js.map +1 -1
  221. package/dist/temporal/index.d.ts +1 -1
  222. package/dist/temporal/index.d.ts.map +1 -1
  223. package/dist/temporal/index.js +1 -1
  224. package/dist/temporal/index.js.map +1 -1
  225. package/dist/temporal/workflows/index.bundle.js +82 -43
  226. package/dist/temporal/workflows/index.d.ts +1 -1
  227. package/dist/temporal/workflows/index.d.ts.map +1 -1
  228. package/dist/temporal/workflows/index.js +1 -1
  229. package/dist/temporal/workflows/index.js.map +1 -1
  230. package/dist/temporal/workflows/job-execution-orchestration.d.ts +5 -0
  231. package/dist/temporal/workflows/job-execution-orchestration.d.ts.map +1 -1
  232. package/dist/temporal/workflows/job-execution-orchestration.js +77 -43
  233. package/dist/temporal/workflows/job-execution-orchestration.js.map +1 -1
  234. package/dist/temporal/workflows/test-env.d.ts +5 -1
  235. package/dist/temporal/workflows/test-env.d.ts.map +1 -1
  236. package/dist/temporal/workflows/test-env.js +32 -11
  237. package/dist/temporal/workflows/test-env.js.map +1 -1
  238. package/dist/tsconfig.test.tsbuildinfo +1 -1
  239. package/drizzle/0000_initial.sql +14 -2
  240. package/drizzle/0001_windy_sunfire.sql +1 -0
  241. package/drizzle/meta/0000_snapshot.json +93 -1
  242. package/drizzle/meta/0001_snapshot.json +1711 -0
  243. package/drizzle/meta/_journal.json +2 -2
  244. package/package.json +21 -21
  245. package/src/core/agent-defaults.ts +2 -1
  246. package/src/core/checkout.test.ts +378 -67
  247. package/src/core/checkout.ts +142 -59
  248. package/src/core/entities/job-execution.test.ts +21 -0
  249. package/src/core/entities/job-execution.ts +24 -0
  250. package/src/core/entities/job-listener-event.ts +3 -0
  251. package/src/core/entities/step.ts +15 -3
  252. package/src/core/entities/workflow-run-attempt.ts +1 -0
  253. package/src/core/entities/workflow-run.ts +16 -0
  254. package/src/core/errors.ts +21 -7
  255. package/src/core/job-execution.ts +27 -4
  256. package/src/core/job-transition/decide-job-activation.test.ts +4 -0
  257. package/src/core/job-transition/decide-job-activation.ts +2 -0
  258. package/src/core/job-transition/derive-job-success.test.ts +12 -0
  259. package/src/core/job-transition/derive-job-success.ts +6 -9
  260. package/src/core/listener-execution-materialization.test.ts +2 -0
  261. package/src/core/listener-execution-materialization.ts +57 -11
  262. package/src/core/resolve-trigger-reference.test.ts +160 -0
  263. package/src/core/resolve-trigger-reference.ts +53 -0
  264. package/src/core/run-workflow.ts +2 -0
  265. package/src/core/step-config/agent.ts +29 -12
  266. package/src/core/step-config/assemble-run-context.test.ts +149 -6
  267. package/src/core/step-config/assemble-run-context.ts +87 -25
  268. package/src/core/step-config/complete-step-dispatch-config.test.ts +125 -2
  269. package/src/core/step-config/complete-step-dispatch-config.ts +83 -1
  270. package/src/core/step-config/index.ts +7 -0
  271. package/src/core/step-config/materialize-job-execution-steps.test.ts +250 -1
  272. package/src/core/step-config/materialize-job-execution-steps.ts +57 -10
  273. package/src/core/step-config/materialize-workflow-model.test.ts +212 -4
  274. package/src/core/step-config/materialize-workflow-model.ts +33 -28
  275. package/src/core/step-config/resolve-job-execution-name.test.ts +37 -15
  276. package/src/core/step-config/resolve-job-execution-name.ts +30 -15
  277. package/src/core/step-config/resolve-step-config.ts +175 -10
  278. package/src/core/step-config/resolve-workflow-run-name.test.ts +115 -0
  279. package/src/core/step-config/resolve-workflow-run-name.ts +108 -0
  280. package/src/core/step-transition/decide-step-transition.ts +2 -2
  281. package/src/core/step-transition/evaluate-gate.test.ts +8 -0
  282. package/src/core/step-transition/evaluate-gate.ts +8 -1
  283. package/src/core/workflow-run-creation.test.ts +7 -4
  284. package/src/core/workflow-run-creation.ts +13 -7
  285. package/src/db/db.ts +2 -0
  286. package/src/db/index.ts +6 -0
  287. package/src/db/job-listener-events.test.ts +19 -0
  288. package/src/db/job-listener-events.ts +3 -0
  289. package/src/db/job-listeners.test.ts +187 -5
  290. package/src/db/job-listeners.ts +122 -30
  291. package/src/db/schema/job-executions.ts +12 -5
  292. package/src/db/schema/job-listener-events.ts +3 -0
  293. package/src/db/schema/step-attempts.ts +2 -2
  294. package/src/db/schema/steps.ts +2 -1
  295. package/src/db/schema/workflow-run-attempts.ts +2 -0
  296. package/src/db/schema/workflow-run-counters.ts +13 -0
  297. package/src/db/schema/workflow-runs.ts +11 -2
  298. package/src/db/workflow-runs/job-executions.ts +37 -15
  299. package/src/db/workflow-runs/jobs.ts +17 -4
  300. package/src/db/workflow-runs/queries.test.ts +206 -0
  301. package/src/db/workflow-runs/queries.ts +146 -2
  302. package/src/db/workflow-runs/run-create.test.ts +606 -8
  303. package/src/db/workflow-runs/run-create.ts +185 -40
  304. package/src/db/workflow-runs/run-rerun.test.ts +61 -2
  305. package/src/db/workflow-runs/run-rerun.ts +15 -5
  306. package/src/db/workflow-runs/shared.ts +2 -0
  307. package/src/db/workflow-runs/steps.ts +3 -3
  308. package/src/db/workflow-runs.ts +7 -0
  309. package/src/metrics/instance.ts +14 -0
  310. package/src/presentation/dto/checkout-token.test.ts +19 -9
  311. package/src/presentation/dto/checkout-token.ts +2 -1
  312. package/src/presentation/dto/index.ts +1 -1
  313. package/src/presentation/dto/job.test.ts +9 -0
  314. package/src/presentation/dto/step.test.ts +12 -0
  315. package/src/presentation/dto/step.ts +5 -2
  316. package/src/presentation/dto/workflow-run.ts +44 -2
  317. package/src/presentation/inter-module.test.ts +165 -0
  318. package/src/presentation/inter-module.ts +38 -4
  319. package/src/presentation/routes/checkout-token.test.ts +357 -142
  320. package/src/presentation/routes/checkout-token.ts +56 -26
  321. package/src/presentation/routes/get-run.test.ts +2 -0
  322. package/src/presentation/routes/get-step-secrets.test.ts +7 -2
  323. package/src/presentation/routes/leased-step.ts +9 -1
  324. package/src/presentation/routes/list-runs.test.ts +57 -0
  325. package/src/presentation/routes/list-runs.ts +8 -3
  326. package/src/presentation/routes/report-step.test.ts +25 -0
  327. package/src/presentation/routes/report-step.ts +10 -2
  328. package/src/presentation/subscribers/on-job-steps-settled.ts +1 -1
  329. package/src/presentation/subscribers/on-runner-job-claimed.test.ts +49 -0
  330. package/src/presentation/subscribers/on-runner-job-claimed.ts +22 -0
  331. package/src/temporal/activities/index.ts +1 -0
  332. package/src/temporal/activities/orchestration-activities.ts +1 -0
  333. package/src/temporal/constants.ts +1 -0
  334. package/src/temporal/index.ts +1 -0
  335. package/src/temporal/workflows/index.ts +1 -0
  336. package/src/temporal/workflows/job-execution-orchestration.test.ts +142 -6
  337. package/src/temporal/workflows/job-execution-orchestration.ts +99 -50
  338. package/src/temporal/workflows/run-orchestration.test.ts +1 -1
  339. package/src/temporal/workflows/test-env.ts +50 -13
  340. package/test/factories/project.ts +2 -0
  341. package/test/factories/workflow-model.ts +80 -26
  342. package/test/factories/workflow-run.ts +4 -1
  343. package/test/fixtures/agent-inter-module.ts +2 -1
  344. package/test/fixtures/job-with-steps.ts +1 -1
  345. package/tsconfig.build.tsbuildinfo +1 -1
  346. package/drizzle/0001_agent_tool_materialization.sql +0 -1
@@ -9,6 +9,7 @@ import {
9
9
  import {agentInterModuleContract} from '@shipfox/api-agent-dto/inter-module';
10
10
  import {parseWorkflowTemplate, planInterpolationField} from '@shipfox/expression';
11
11
  import {createInterModuleKnownError} from '@shipfox/inter-module';
12
+ import {agentThinkingSchema} from '@shipfox/workflow-document';
12
13
  import type {AgentDefaultsResolver} from '#core/agent-defaults.js';
13
14
  import type {Step} from '#core/entities/step.js';
14
15
  import {AgentConfigUnresolvableError, InterpolationUnresolvableError} from '#core/errors.js';
@@ -68,7 +69,7 @@ const resolveAgentDefaults: AgentDefaultsResolver = (params) => ({
68
69
  harness: params.harness ?? 'pi',
69
70
  provider: params.provider ?? 'openai',
70
71
  model: params.model ?? 'gpt-5.5',
71
- thinking: params.thinking ?? 'off',
72
+ thinking: agentThinkingSchema.safeParse(params.thinking).data ?? 'off',
72
73
  });
73
74
 
74
75
  function materializedIntegration(): MaterializedAgentIntegrationConfigDto {
@@ -251,6 +252,128 @@ describe('completeStepDispatchConfig', () => {
251
252
  });
252
253
  });
253
254
 
255
+ it('resolves a deferred working directory at step dispatch', async () => {
256
+ const pending = step({
257
+ config: {},
258
+ configPlan: {
259
+ working_directory: plannedField(template('steps.build.outputs.path')),
260
+ },
261
+ });
262
+
263
+ const result = await completeStepDispatchConfig({
264
+ step: pending,
265
+ context: {
266
+ ...context,
267
+ values: {
268
+ ...context.values,
269
+ steps: {
270
+ build: {
271
+ outputs: {path: 'packages/api'},
272
+ },
273
+ },
274
+ },
275
+ },
276
+ resolveAgentDefaults,
277
+ definitionId: 'def-1',
278
+ });
279
+
280
+ expect(result).toEqual({
281
+ config: {working_directory: 'packages/api'},
282
+ trace: [
283
+ {
284
+ expression: 'steps.build.outputs.path',
285
+ roots: ['steps'],
286
+ fillTarget: 'step-dispatch',
287
+ evaluatedAt: 'step-dispatch',
288
+ value: 'packages/api',
289
+ field: 'step.working_directory',
290
+ },
291
+ ],
292
+ });
293
+ });
294
+
295
+ it('resolves deferred checkout targets at step dispatch', async () => {
296
+ const pending = step({
297
+ type: 'checkout',
298
+ config: {
299
+ checkout: {
300
+ fetch_depth: 1,
301
+ permissions: {contents: 'read'},
302
+ persist_credentials: true,
303
+ },
304
+ },
305
+ configPlan: {
306
+ checkout: {
307
+ repository: plannedField(template('steps.build.outputs.repository')),
308
+ ref: plannedField(template('steps.build.outputs.ref')),
309
+ },
310
+ },
311
+ });
312
+
313
+ const result = await completeStepDispatchConfig({
314
+ step: pending,
315
+ context: {
316
+ ...context,
317
+ values: {
318
+ ...context.values,
319
+ steps: {
320
+ build: {
321
+ outputs: {repository: 'acme/api', ref: 'refs/heads/main'},
322
+ },
323
+ },
324
+ },
325
+ },
326
+ resolveAgentDefaults,
327
+ definitionId: 'def-1',
328
+ });
329
+
330
+ expect(result).toEqual({
331
+ config: {
332
+ checkout: {
333
+ fetch_depth: 1,
334
+ permissions: {contents: 'read'},
335
+ persist_credentials: true,
336
+ repository: 'acme/api',
337
+ ref: 'refs/heads/main',
338
+ },
339
+ },
340
+ trace: [
341
+ {
342
+ expression: 'steps.build.outputs.repository',
343
+ roots: ['steps'],
344
+ fillTarget: 'step-dispatch',
345
+ evaluatedAt: 'step-dispatch',
346
+ value: 'acme/api',
347
+ field: 'checkout.repository',
348
+ },
349
+ {
350
+ expression: 'steps.build.outputs.ref',
351
+ roots: ['steps'],
352
+ fillTarget: 'step-dispatch',
353
+ evaluatedAt: 'step-dispatch',
354
+ value: 'refs/heads/main',
355
+ field: 'checkout.ref',
356
+ },
357
+ ],
358
+ });
359
+ });
360
+
361
+ it('rejects invalid resolved working directories', async () => {
362
+ const pending = step({
363
+ config: {working_directory: '../outside'},
364
+ configPlan: null,
365
+ });
366
+
367
+ await expect(
368
+ completeStepDispatchConfig({
369
+ step: pending,
370
+ context,
371
+ resolveAgentDefaults,
372
+ definitionId: 'def-1',
373
+ }),
374
+ ).rejects.toThrow('Invalid working_directory');
375
+ });
376
+
254
377
  it('completes deferred agent config with the resolved harness', async () => {
255
378
  const integration = materializedIntegration();
256
379
  const mcpServers = integrationMcpServers([integration]);
@@ -306,7 +429,7 @@ describe('completeStepDispatchConfig', () => {
306
429
  configPlan: {
307
430
  agent: {
308
431
  harness: 'claude',
309
- thinking: 'off',
432
+ thinking: plannedField('off'),
310
433
  prompt: plannedField(`Review ${template('steps.build.outputs.sha')}`),
311
434
  },
312
435
  },
@@ -1,7 +1,10 @@
1
+ import {WORKFLOW_MODEL_CHECKOUT_TARGET_FIELDS} from '@shipfox/api-definitions-dto';
2
+ import {assertWorkingDirectory} from '@shipfox/api-workflows-dto';
1
3
  import {capTraceEntries} from '@shipfox/expression';
2
4
  import type {AgentDefaultsResolver} from '#core/agent-defaults.js';
3
5
  import type {PersistedEvaluationTraceEntry, Step} from '#core/entities/step.js';
4
6
  import {completeAgentConfig} from './agent.js';
7
+ import {completeStepFieldWithTrace} from './fields.js';
5
8
  import {completeRunDispatchConfig} from './run.js';
6
9
  import type {WorkflowEvaluationContext} from './workflow-evaluation-context.js';
7
10
 
@@ -15,7 +18,10 @@ export async function completeStepDispatchConfig(params: {
15
18
  readonly trace: readonly PersistedEvaluationTraceEntry[];
16
19
  }> {
17
20
  const plan = params.step.configPlan;
18
- if (plan === null) return {config: params.step.config, trace: []};
21
+ if (plan === null) {
22
+ assertWorkingDirectoryIfPresent(params.step.config.working_directory);
23
+ return {config: params.step.config, trace: []};
24
+ }
19
25
 
20
26
  const config = {...params.step.config};
21
27
  delete config.secret_bindings;
@@ -35,6 +41,82 @@ export async function completeStepDispatchConfig(params: {
35
41
  definitionId: params.definitionId,
36
42
  trace,
37
43
  });
44
+ completeWorkingDirectoryConfig({
45
+ config,
46
+ plan,
47
+ context: params.context,
48
+ definitionId: params.definitionId,
49
+ trace,
50
+ });
51
+ completeCheckoutConfig({
52
+ config,
53
+ plan,
54
+ context: params.context,
55
+ definitionId: params.definitionId,
56
+ trace,
57
+ });
58
+ assertWorkingDirectoryIfPresent(config.working_directory);
38
59
 
39
60
  return {config, trace: capTraceEntries(trace)};
40
61
  }
62
+
63
+ function completeCheckoutConfig(params: {
64
+ readonly config: Record<string, unknown>;
65
+ readonly plan: Step['configPlan'] & object;
66
+ readonly context: WorkflowEvaluationContext;
67
+ readonly definitionId: string;
68
+ readonly trace: PersistedEvaluationTraceEntry[];
69
+ }): void {
70
+ const checkoutPlan = params.plan.checkout;
71
+ if (checkoutPlan === undefined) return;
72
+
73
+ const checkout = params.config.checkout;
74
+ if (checkout === null || typeof checkout !== 'object' || Array.isArray(checkout)) {
75
+ throw new Error('Checkout dispatch config is missing an object');
76
+ }
77
+
78
+ const resolvedCheckout = {...checkout} as Record<string, unknown>;
79
+ params.config.checkout = resolvedCheckout;
80
+
81
+ for (const [key, fieldName] of WORKFLOW_MODEL_CHECKOUT_TARGET_FIELDS) {
82
+ const field = checkoutPlan[key];
83
+ if (field === undefined) continue;
84
+
85
+ const resolved = completeStepFieldWithTrace({
86
+ field: fieldName,
87
+ template: field,
88
+ context: params.context,
89
+ definitionId: params.definitionId,
90
+ errorField: fieldName,
91
+ });
92
+ resolvedCheckout[key] = resolved.value;
93
+ params.trace.push(...resolved.trace.map((entry) => ({...entry, field: fieldName})));
94
+ }
95
+ }
96
+
97
+ function assertWorkingDirectoryIfPresent(value: unknown): void {
98
+ if (value !== undefined) assertWorkingDirectory(value);
99
+ }
100
+
101
+ function completeWorkingDirectoryConfig(params: {
102
+ readonly config: Record<string, unknown>;
103
+ readonly plan: Step['configPlan'] & object;
104
+ readonly context: WorkflowEvaluationContext;
105
+ readonly definitionId: string;
106
+ readonly trace: PersistedEvaluationTraceEntry[];
107
+ }): void {
108
+ const field = params.plan.working_directory;
109
+ if (field === undefined) return;
110
+
111
+ const resolved = completeStepFieldWithTrace({
112
+ field: 'step.working_directory',
113
+ template: field,
114
+ context: params.context,
115
+ definitionId: params.definitionId,
116
+ errorField: 'step.working_directory',
117
+ });
118
+ params.config.working_directory = resolved.value;
119
+ params.trace.push(
120
+ ...resolved.trace.map((entry) => ({...entry, field: 'step.working_directory' as const})),
121
+ );
122
+ }
@@ -38,4 +38,11 @@ export {
38
38
  resolveJobExecutionName,
39
39
  } from './resolve-job-execution-name.js';
40
40
  export type {WorkflowStepTemplateDiagnostic} from './resolve-step-config.js';
41
+ export {
42
+ type ResolveWorkflowRunNameResult,
43
+ resolveWorkflowRunName,
44
+ sanitizeWorkflowDisplayText,
45
+ type WorkflowRunNameDegradation,
46
+ type WorkflowRunNameResolutionCause,
47
+ } from './resolve-workflow-run-name.js';
41
48
  export type {WorkflowEvaluationContext} from './workflow-evaluation-context.js';
@@ -5,6 +5,7 @@ import {
5
5
  AGENT_INTEGRATION_MCP_TRANSPORT,
6
6
  } from '@shipfox/api-agent-dto';
7
7
  import {agentInterModuleContract} from '@shipfox/api-agent-dto/inter-module';
8
+ import {createWorkflowExpression} from '@shipfox/expression';
8
9
  import {createInterModuleKnownError} from '@shipfox/inter-module';
9
10
  import type {AgentDefaultsResolver} from '#core/agent-defaults.js';
10
11
  import type {AgentToolCatalogEntry, AgentToolMaterializationContext} from '#core/agent-tools.js';
@@ -31,6 +32,19 @@ function shellRef(name: string): string {
31
32
  return `\${${name}}`;
32
33
  }
33
34
 
35
+ function checkout(repository: string) {
36
+ return {
37
+ repository,
38
+ fetchDepth: 1,
39
+ permissions: {contents: 'read' as const},
40
+ persistCredentials: true,
41
+ };
42
+ }
43
+
44
+ function condition(source: string) {
45
+ return createWorkflowExpression({source, check: {mode: 'syntax'}});
46
+ }
47
+
34
48
  function jobExecutionContext(): WorkflowEvaluationContext {
35
49
  return {
36
50
  site: 'execution-creation',
@@ -58,6 +72,10 @@ function jobExecutionContext(): WorkflowEvaluationContext {
58
72
  event: 'pull_request_review',
59
73
  delivery_id: 'delivery-2',
60
74
  received_at: '2026-06-30T12:01:00.000Z',
75
+ project: null,
76
+ repository: null,
77
+ ref: null,
78
+ commit: null,
61
79
  data: {body: 'LGTM'},
62
80
  },
63
81
  ],
@@ -157,6 +175,232 @@ function githubAgentToolCatalog(): readonly AgentToolCatalogEntry[] {
157
175
  }
158
176
 
159
177
  describe('materializeJobExecutionSteps', () => {
178
+ it('resolves static job names through the job execution context', async () => {
179
+ const model = workflowModel({
180
+ jobs: {
181
+ review: {
182
+ name: 'Review',
183
+ steps: [{name: template('job.name'), run: 'echo review'}],
184
+ },
185
+ },
186
+ });
187
+ const job = model.jobs[0];
188
+ if (!job) throw new Error('Expected workflow job');
189
+
190
+ const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
191
+
192
+ expect(steps[1]).toMatchObject({
193
+ name: 'Review',
194
+ config: {run: 'echo review'},
195
+ });
196
+ });
197
+
198
+ it('materializes checkout steps with their resolved config and default name', async () => {
199
+ const model = workflowModel({
200
+ jobs: {
201
+ build: {
202
+ steps: [
203
+ {
204
+ checkout: {
205
+ repository: 'acme/api',
206
+ ref: 'refs/heads/main',
207
+ fetchDepth: 0,
208
+ path: 'target',
209
+ permissions: {contents: 'write'},
210
+ persistCredentials: false,
211
+ force: true,
212
+ },
213
+ },
214
+ ],
215
+ },
216
+ },
217
+ });
218
+ const job = model.jobs[0];
219
+ if (!job) throw new Error('Expected workflow job');
220
+
221
+ const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
222
+
223
+ expect(steps[0]).toMatchObject({
224
+ type: 'setup',
225
+ config: {},
226
+ });
227
+ expect(steps[1]).toEqual({
228
+ key: null,
229
+ name: 'Checkout',
230
+ sourceLocation: null,
231
+ status: 'pending',
232
+ type: 'checkout',
233
+ config: {
234
+ checkout: {
235
+ repository: 'acme/api',
236
+ ref: 'refs/heads/main',
237
+ fetch_depth: 0,
238
+ path: 'target',
239
+ permissions: {contents: 'write'},
240
+ persist_credentials: false,
241
+ force: true,
242
+ },
243
+ },
244
+ authoredConfig: null,
245
+ position: 1,
246
+ });
247
+ });
248
+
249
+ it('places a leading checkout at the job root when its path is omitted', async () => {
250
+ const model = workflowModel({
251
+ jobs: {
252
+ build: {
253
+ steps: [{checkout: checkout('acme/api')}, {run: 'echo use checkout'}],
254
+ },
255
+ },
256
+ });
257
+ const job = model.jobs[0];
258
+ if (!job) throw new Error('Expected workflow job');
259
+
260
+ const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
261
+
262
+ expect(steps[0]).toMatchObject({type: 'setup', config: {}});
263
+ expect(steps[1]).toMatchObject({
264
+ type: 'checkout',
265
+ config: {checkout: {repository: 'acme/api', path: '.'}},
266
+ });
267
+ expect(steps[2]).toMatchObject({type: 'run', config: {run: 'echo use checkout'}});
268
+ });
269
+
270
+ it('keeps the implicit checkout when an explicit checkout is not leading', async () => {
271
+ const model = workflowModel({
272
+ jobs: {
273
+ build: {
274
+ steps: [
275
+ {run: 'echo route'},
276
+ {checkout: checkout('acme/api')},
277
+ {run: 'echo use checkout'},
278
+ ],
279
+ },
280
+ },
281
+ });
282
+ const job = model.jobs[0];
283
+ if (!job) throw new Error('Expected workflow job');
284
+
285
+ const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
286
+
287
+ expect(steps[0]).toMatchObject({
288
+ type: 'setup',
289
+ config: {
290
+ checkout: {
291
+ permissions: {contents: 'read'},
292
+ persist_credentials: true,
293
+ },
294
+ },
295
+ });
296
+ expect(steps[2]).toMatchObject({
297
+ type: 'checkout',
298
+ config: {
299
+ checkout: {
300
+ repository: 'acme/api',
301
+ fetch_depth: 1,
302
+ permissions: {contents: 'read'},
303
+ persist_credentials: true,
304
+ },
305
+ },
306
+ });
307
+ expect(steps[2]?.config.checkout).not.toHaveProperty('path');
308
+ expect(steps[3]).toMatchObject({type: 'run', config: {run: 'echo use checkout'}});
309
+ });
310
+
311
+ it('omits the implicit checkout when the job opts out', async () => {
312
+ const model = workflowModel({
313
+ jobs: {
314
+ build: {
315
+ checkout: false,
316
+ steps: [{run: 'echo no repository'}],
317
+ },
318
+ },
319
+ });
320
+ const job = model.jobs[0];
321
+ if (!job) throw new Error('Expected workflow job');
322
+
323
+ const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
324
+
325
+ expect(steps[0]).toMatchObject({type: 'setup', config: {}});
326
+ });
327
+
328
+ it('does not restore the implicit checkout for a leading checkout with a false if', async () => {
329
+ const model = workflowModel({
330
+ jobs: {
331
+ build: {
332
+ steps: [{if: condition('false'), checkout: checkout('acme/api')}],
333
+ },
334
+ },
335
+ });
336
+ const job = model.jobs[0];
337
+ if (!job) throw new Error('Expected workflow job');
338
+
339
+ const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
340
+
341
+ expect(steps[0]).toMatchObject({type: 'setup', config: {}});
342
+ expect(steps[1]).toMatchObject({
343
+ type: 'checkout',
344
+ condition: expect.any(Object),
345
+ config: {checkout: {repository: 'acme/api', path: '.'}},
346
+ });
347
+ });
348
+
349
+ it('propagates a non-default job checkout policy into the setup step config', async () => {
350
+ const model = workflowModel({
351
+ jobs: {
352
+ build: {
353
+ checkout: {
354
+ permissions: {contents: 'write'},
355
+ persistCredentials: false,
356
+ },
357
+ steps: [{run: 'echo hello'}],
358
+ },
359
+ },
360
+ });
361
+ const job = model.jobs[0];
362
+ if (!job) throw new Error('Expected workflow job');
363
+
364
+ const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
365
+
366
+ expect(steps[0]).toMatchObject({
367
+ type: 'setup',
368
+ config: {
369
+ checkout: {
370
+ permissions: {contents: 'write'},
371
+ persist_credentials: false,
372
+ },
373
+ },
374
+ });
375
+ });
376
+
377
+ it('falls back to the default checkout policy for legacy jobs', async () => {
378
+ const model = workflowModel({
379
+ jobs: {
380
+ build: {steps: [{run: 'echo hello'}]},
381
+ },
382
+ });
383
+ const job = model.jobs[0];
384
+ if (!job) throw new Error('Expected workflow job');
385
+ const legacyJob = {...job, checkout: undefined} as unknown as typeof job;
386
+
387
+ const steps = await materializeJobExecutionSteps({
388
+ model,
389
+ job: legacyJob,
390
+ context: jobExecutionContext(),
391
+ });
392
+
393
+ expect(steps[0]).toMatchObject({
394
+ type: 'setup',
395
+ config: {
396
+ checkout: {
397
+ permissions: {contents: 'read'},
398
+ persist_credentials: true,
399
+ },
400
+ },
401
+ });
402
+ });
403
+
160
404
  it('prepends setup and resolves job-execution context fields', async () => {
161
405
  const model = workflowModel({
162
406
  jobs: {
@@ -183,7 +427,12 @@ describe('materializeJobExecutionSteps', () => {
183
427
  sourceLocation: null,
184
428
  status: 'pending',
185
429
  type: 'setup',
186
- config: {},
430
+ config: {
431
+ checkout: {
432
+ permissions: {contents: 'read'},
433
+ persist_credentials: true,
434
+ },
435
+ },
187
436
  authoredConfig: null,
188
437
  position: 0,
189
438
  },
@@ -1,4 +1,4 @@
1
- import type {WorkflowModel} from '@shipfox/api-definitions-dto';
1
+ import {DEFAULT_JOB_CHECKOUT, type WorkflowModel} from '@shipfox/api-definitions-dto';
2
2
  import type {WorkflowExpression} from '@shipfox/expression';
3
3
  import type {AgentDefaultsResolver} from '#core/agent-defaults.js';
4
4
  import type {
@@ -41,15 +41,14 @@ export interface MaterializeJobExecutionStepsParams {
41
41
 
42
42
  // Synthetic "Set up job" step prepended when a job execution's steps are materialized.
43
43
  // The runner prepares the workspace here; failures report through the normal step
44
- // protocol instead of hanging the job until the lease/timeout fires. Its config is
45
- // credential-free.
46
- const SETUP_STEP: MaterializedWorkflowStep = {
44
+ // protocol instead of hanging the job until the lease/timeout fires. Its config carries
45
+ // checkout policy, never credential material.
46
+ const SETUP_STEP: Omit<MaterializedWorkflowStep, 'config'> = {
47
47
  key: null,
48
48
  name: 'Set up job',
49
49
  sourceLocation: null,
50
50
  status: 'pending',
51
51
  type: 'setup',
52
- config: {},
53
52
  authoredConfig: null,
54
53
  position: 0,
55
54
  };
@@ -68,11 +67,13 @@ export async function materializeJobExecutionSteps(
68
67
  } = params;
69
68
 
70
69
  return [
71
- SETUP_STEP,
70
+ setupStepForJob(job),
72
71
  ...(await Promise.all(
73
72
  job.steps.map(async (step, stepPosition) => {
74
- // The trusted context exposes the stable job key; the authored display field remains `job.name`.
75
- const stepContext = {...context.values, job: {key: job.key}};
73
+ const stepContext = {
74
+ ...context.values,
75
+ job: {key: job.key, name: job.name ?? job.key},
76
+ };
76
77
  const resolved = await resolveStepConfig({
77
78
  jobKey: job.key,
78
79
  step,
@@ -93,7 +94,11 @@ export async function materializeJobExecutionSteps(
93
94
  sourceLocation: step.sourceLocation ?? null,
94
95
  status: 'pending' as const,
95
96
  type: step.kind,
96
- config: resolved.config,
97
+ config: materializedStepConfig({
98
+ config: resolved.config,
99
+ step,
100
+ stepPosition,
101
+ }),
97
102
  ...(step.if === undefined ? {} : {condition: step.if}),
98
103
  authoredConfig: resolved.authoredConfig,
99
104
  ...materializedConfigPlan(resolved.configPlan, resolved.trace),
@@ -105,6 +110,45 @@ export async function materializeJobExecutionSteps(
105
110
  ];
106
111
  }
107
112
 
113
+ function setupStepForJob(job: WorkflowModelJob): MaterializedWorkflowStep {
114
+ if (job.checkout === false || job.steps[0]?.kind === 'checkout') {
115
+ return {...SETUP_STEP, config: {}};
116
+ }
117
+
118
+ const checkout = job.checkout ?? DEFAULT_JOB_CHECKOUT;
119
+
120
+ return {
121
+ ...SETUP_STEP,
122
+ config: {
123
+ checkout: {
124
+ permissions: checkout.permissions,
125
+ persist_credentials: checkout.persistCredentials,
126
+ },
127
+ },
128
+ };
129
+ }
130
+
131
+ function materializedStepConfig(params: {
132
+ readonly config: Readonly<Record<string, unknown>>;
133
+ readonly step: WorkflowModelStep;
134
+ readonly stepPosition: number;
135
+ }): Readonly<Record<string, unknown>> {
136
+ if (
137
+ params.step.kind !== 'checkout' ||
138
+ params.stepPosition !== 0 ||
139
+ params.step.checkout.path !== undefined
140
+ ) {
141
+ return params.config;
142
+ }
143
+
144
+ const checkout = params.config.checkout;
145
+ if (checkout === null || typeof checkout !== 'object' || Array.isArray(checkout)) {
146
+ throw new Error('Checkout materialization config is missing an object');
147
+ }
148
+
149
+ return {...params.config, checkout: {...checkout, path: '.'}};
150
+ }
151
+
108
152
  function materializedConfigPlan(
109
153
  configPlan: StepConfigDispatchPlan | null,
110
154
  trace: NonNullable<StepConfigDispatchPlan['trace']>,
@@ -114,13 +158,16 @@ function materializedConfigPlan(
114
158
  }
115
159
 
116
160
  function stepDisplayName(step: WorkflowModelStep): string {
117
- switch (step.kind) {
161
+ const {kind} = step;
162
+ switch (kind) {
118
163
  case 'run':
119
164
  return firstLine(step.command.value);
120
165
  case 'agent':
121
166
  return step.model === undefined
122
167
  ? firstLine(step.prompt)
123
168
  : `${step.model} · ${firstLine(step.prompt)}`;
169
+ case 'checkout':
170
+ return 'Checkout';
124
171
  default:
125
172
  return assertNever(step);
126
173
  }