@shipfox/api-workflows 4.0.0 → 6.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 (277) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +104 -0
  3. package/dist/core/agent-defaults.d.ts +17 -0
  4. package/dist/core/agent-defaults.d.ts.map +1 -0
  5. package/dist/core/agent-defaults.js +21 -0
  6. package/dist/core/agent-defaults.js.map +1 -0
  7. package/dist/core/agent-tool-capability-warning.d.ts +4 -0
  8. package/dist/core/agent-tool-capability-warning.d.ts.map +1 -1
  9. package/dist/core/agent-tool-capability-warning.js +15 -17
  10. package/dist/core/agent-tool-capability-warning.js.map +1 -1
  11. package/dist/core/agent-tools.d.ts +11 -9
  12. package/dist/core/agent-tools.d.ts.map +1 -1
  13. package/dist/core/agent-tools.js +23 -18
  14. package/dist/core/agent-tools.js.map +1 -1
  15. package/dist/core/checkout.d.ts +21 -7
  16. package/dist/core/checkout.d.ts.map +1 -1
  17. package/dist/core/checkout.js +23 -11
  18. package/dist/core/checkout.js.map +1 -1
  19. package/dist/core/entities/workflow-run-attempt.d.ts +1 -1
  20. package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -1
  21. package/dist/core/entities/workflow-run-attempt.js.map +1 -1
  22. package/dist/core/job-execution.d.ts +3 -1
  23. package/dist/core/job-execution.d.ts.map +1 -1
  24. package/dist/core/job-execution.js +14 -12
  25. package/dist/core/job-execution.js.map +1 -1
  26. package/dist/core/job-transition/derive-job-execution-outputs.d.ts +1 -1
  27. package/dist/core/job-transition/derive-job-execution-outputs.d.ts.map +1 -1
  28. package/dist/core/job-transition/derive-job-execution-outputs.js.map +1 -1
  29. package/dist/core/job-transition/derive-job-success.js +1 -1
  30. package/dist/core/job-transition/derive-job-success.js.map +1 -1
  31. package/dist/core/listener-execution-materialization.d.ts +3 -3
  32. package/dist/core/listener-execution-materialization.d.ts.map +1 -1
  33. package/dist/core/listener-execution-materialization.js +3 -4
  34. package/dist/core/listener-execution-materialization.js.map +1 -1
  35. package/dist/core/run-workflow.d.ts +12 -1
  36. package/dist/core/run-workflow.d.ts.map +1 -1
  37. package/dist/core/run-workflow.js +13 -8
  38. package/dist/core/run-workflow.js.map +1 -1
  39. package/dist/core/step-config/agent.d.ts +8 -8
  40. package/dist/core/step-config/agent.d.ts.map +1 -1
  41. package/dist/core/step-config/agent.js +12 -10
  42. package/dist/core/step-config/agent.js.map +1 -1
  43. package/dist/core/step-config/complete-step-dispatch-config.d.ts +4 -4
  44. package/dist/core/step-config/complete-step-dispatch-config.d.ts.map +1 -1
  45. package/dist/core/step-config/complete-step-dispatch-config.js +2 -2
  46. package/dist/core/step-config/complete-step-dispatch-config.js.map +1 -1
  47. package/dist/core/step-config/materialize-job-execution-steps.d.ts +3 -3
  48. package/dist/core/step-config/materialize-job-execution-steps.d.ts.map +1 -1
  49. package/dist/core/step-config/materialize-job-execution-steps.js +5 -6
  50. package/dist/core/step-config/materialize-job-execution-steps.js.map +1 -1
  51. package/dist/core/step-config/materialize-workflow-model.d.ts +3 -3
  52. package/dist/core/step-config/materialize-workflow-model.d.ts.map +1 -1
  53. package/dist/core/step-config/materialize-workflow-model.js +4 -4
  54. package/dist/core/step-config/materialize-workflow-model.js.map +1 -1
  55. package/dist/core/step-config/resolve-job-execution-name.d.ts +1 -1
  56. package/dist/core/step-config/resolve-job-execution-name.d.ts.map +1 -1
  57. package/dist/core/step-config/resolve-job-execution-name.js.map +1 -1
  58. package/dist/core/step-config/resolve-step-config.d.ts +4 -4
  59. package/dist/core/step-config/resolve-step-config.d.ts.map +1 -1
  60. package/dist/core/step-config/resolve-step-config.js +6 -6
  61. package/dist/core/step-config/resolve-step-config.js.map +1 -1
  62. package/dist/core/step-config/run.d.ts +1 -1
  63. package/dist/core/step-config/run.d.ts.map +1 -1
  64. package/dist/core/step-config/run.js.map +1 -1
  65. package/dist/core/workflow-run-creation.d.ts +3 -3
  66. package/dist/core/workflow-run-creation.d.ts.map +1 -1
  67. package/dist/core/workflow-run-creation.js +3 -4
  68. package/dist/core/workflow-run-creation.js.map +1 -1
  69. package/dist/db/db.d.ts +16 -4
  70. package/dist/db/db.d.ts.map +1 -1
  71. package/dist/db/job-listeners.d.ts +7 -1
  72. package/dist/db/job-listeners.d.ts.map +1 -1
  73. package/dist/db/job-listeners.js +10 -5
  74. package/dist/db/job-listeners.js.map +1 -1
  75. package/dist/db/runner-lease-table.d.ts +5 -5
  76. package/dist/db/runner-lease-table.d.ts.map +1 -1
  77. package/dist/db/runner-lease-table.js +3 -2
  78. package/dist/db/runner-lease-table.js.map +1 -1
  79. package/dist/db/schema/workflow-run-attempts.d.ts +9 -3
  80. package/dist/db/schema/workflow-run-attempts.d.ts.map +1 -1
  81. package/dist/db/schema/workflow-run-attempts.js +2 -1
  82. package/dist/db/schema/workflow-run-attempts.js.map +1 -1
  83. package/dist/db/schema/workflow-runs.js +1 -1
  84. package/dist/db/schema/workflow-runs.js.map +1 -1
  85. package/dist/db/workflow-runs/job-executions.d.ts +5 -0
  86. package/dist/db/workflow-runs/job-executions.d.ts.map +1 -1
  87. package/dist/db/workflow-runs/job-executions.js +14 -7
  88. package/dist/db/workflow-runs/job-executions.js.map +1 -1
  89. package/dist/db/workflow-runs/jobs.d.ts.map +1 -1
  90. package/dist/db/workflow-runs/jobs.js +3 -1
  91. package/dist/db/workflow-runs/jobs.js.map +1 -1
  92. package/dist/db/workflow-runs/run-create.d.ts +9 -2
  93. package/dist/db/workflow-runs/run-create.d.ts.map +1 -1
  94. package/dist/db/workflow-runs/run-create.js +12 -9
  95. package/dist/db/workflow-runs/run-create.js.map +1 -1
  96. package/dist/db/workflow-runs/run-rerun.d.ts.map +1 -1
  97. package/dist/db/workflow-runs/run-rerun.js +3 -1
  98. package/dist/db/workflow-runs/run-rerun.js.map +1 -1
  99. package/dist/index.d.ts +18 -4
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +61 -37
  102. package/dist/index.js.map +1 -1
  103. package/dist/presentation/dto/checkout-token.d.ts +14 -1
  104. package/dist/presentation/dto/checkout-token.d.ts.map +1 -1
  105. package/dist/presentation/dto/checkout-token.js.map +1 -1
  106. package/dist/presentation/index.d.ts +1 -1
  107. package/dist/presentation/index.d.ts.map +1 -1
  108. package/dist/presentation/index.js +1 -1
  109. package/dist/presentation/index.js.map +1 -1
  110. package/dist/presentation/inter-module.d.ts +18 -0
  111. package/dist/presentation/inter-module.d.ts.map +1 -0
  112. package/dist/presentation/inter-module.js +113 -0
  113. package/dist/presentation/inter-module.js.map +1 -0
  114. package/dist/presentation/routes/agent-runtime-config.d.ts +6 -1
  115. package/dist/presentation/routes/agent-runtime-config.d.ts.map +1 -1
  116. package/dist/presentation/routes/agent-runtime-config.js +59 -57
  117. package/dist/presentation/routes/agent-runtime-config.js.map +1 -1
  118. package/dist/presentation/routes/cancel-run.d.ts +2 -1
  119. package/dist/presentation/routes/cancel-run.d.ts.map +1 -1
  120. package/dist/presentation/routes/cancel-run.js +37 -34
  121. package/dist/presentation/routes/cancel-run.js.map +1 -1
  122. package/dist/presentation/routes/checkout-token.d.ts +8 -1
  123. package/dist/presentation/routes/checkout-token.d.ts.map +1 -1
  124. package/dist/presentation/routes/checkout-token.js +65 -47
  125. package/dist/presentation/routes/checkout-token.js.map +1 -1
  126. package/dist/presentation/routes/get-run-aggregates.d.ts +2 -1
  127. package/dist/presentation/routes/get-run-aggregates.d.ts.map +1 -1
  128. package/dist/presentation/routes/get-run-aggregates.js +44 -53
  129. package/dist/presentation/routes/get-run-aggregates.js.map +1 -1
  130. package/dist/presentation/routes/get-run.d.ts +2 -1
  131. package/dist/presentation/routes/get-run.d.ts.map +1 -1
  132. package/dist/presentation/routes/get-run.js +53 -50
  133. package/dist/presentation/routes/get-run.js.map +1 -1
  134. package/dist/presentation/routes/get-step-secrets.d.ts +3 -1
  135. package/dist/presentation/routes/get-step-secrets.d.ts.map +1 -1
  136. package/dist/presentation/routes/get-step-secrets.js +72 -68
  137. package/dist/presentation/routes/get-step-secrets.js.map +1 -1
  138. package/dist/presentation/routes/index.d.ts +19 -2
  139. package/dist/presentation/routes/index.d.ts.map +1 -1
  140. package/dist/presentation/routes/index.js +38 -30
  141. package/dist/presentation/routes/index.js.map +1 -1
  142. package/dist/presentation/routes/leased-step.d.ts +2 -0
  143. package/dist/presentation/routes/leased-step.d.ts.map +1 -1
  144. package/dist/presentation/routes/leased-step.js +1 -2
  145. package/dist/presentation/routes/leased-step.js.map +1 -1
  146. package/dist/presentation/routes/list-run-attempts.d.ts +2 -1
  147. package/dist/presentation/routes/list-run-attempts.d.ts.map +1 -1
  148. package/dist/presentation/routes/list-run-attempts.js +29 -26
  149. package/dist/presentation/routes/list-run-attempts.js.map +1 -1
  150. package/dist/presentation/routes/list-runs.d.ts +2 -1
  151. package/dist/presentation/routes/list-runs.d.ts.map +1 -1
  152. package/dist/presentation/routes/list-runs.js +51 -60
  153. package/dist/presentation/routes/list-runs.js.map +1 -1
  154. package/dist/presentation/routes/next-step.d.ts +10 -1
  155. package/dist/presentation/routes/next-step.d.ts.map +1 -1
  156. package/dist/presentation/routes/next-step.js +64 -50
  157. package/dist/presentation/routes/next-step.js.map +1 -1
  158. package/dist/presentation/routes/project-access.d.ts +10 -0
  159. package/dist/presentation/routes/project-access.d.ts.map +1 -0
  160. package/dist/presentation/routes/project-access.js +17 -0
  161. package/dist/presentation/routes/project-access.js.map +1 -0
  162. package/dist/presentation/routes/report-step.d.ts +2 -1
  163. package/dist/presentation/routes/report-step.d.ts.map +1 -1
  164. package/dist/presentation/routes/report-step.js +62 -61
  165. package/dist/presentation/routes/report-step.js.map +1 -1
  166. package/dist/presentation/routes/require-accessible-run.d.ts +3 -1
  167. package/dist/presentation/routes/require-accessible-run.d.ts.map +1 -1
  168. package/dist/presentation/routes/require-accessible-run.js +3 -6
  169. package/dist/presentation/routes/require-accessible-run.js.map +1 -1
  170. package/dist/presentation/routes/rerun-run.d.ts +2 -1
  171. package/dist/presentation/routes/rerun-run.d.ts.map +1 -1
  172. package/dist/presentation/routes/rerun-run.js +46 -43
  173. package/dist/presentation/routes/rerun-run.js.map +1 -1
  174. package/dist/temporal/activities/index.d.ts +44 -9
  175. package/dist/temporal/activities/index.d.ts.map +1 -1
  176. package/dist/temporal/activities/index.js +13 -9
  177. package/dist/temporal/activities/index.js.map +1 -1
  178. package/dist/temporal/activities/orchestration-activities.d.ts +20 -11
  179. package/dist/temporal/activities/orchestration-activities.d.ts.map +1 -1
  180. package/dist/temporal/activities/orchestration-activities.js +48 -33
  181. package/dist/temporal/activities/orchestration-activities.js.map +1 -1
  182. package/dist/tsconfig.test.tsbuildinfo +1 -1
  183. package/package.json +41 -44
  184. package/src/core/agent-defaults.test.ts +28 -0
  185. package/src/core/agent-defaults.ts +36 -0
  186. package/src/core/agent-tool-capability-warning.test.ts +36 -11
  187. package/src/core/agent-tool-capability-warning.ts +27 -25
  188. package/src/core/agent-tools.ts +33 -36
  189. package/src/core/checkout.test.ts +38 -25
  190. package/src/core/checkout.ts +41 -14
  191. package/src/core/entities/workflow-run-attempt.ts +1 -1
  192. package/src/core/job-execution.ts +19 -9
  193. package/src/core/job-transition/derive-job-execution-outputs.ts +1 -1
  194. package/src/core/job-transition/derive-job-success.ts +1 -1
  195. package/src/core/listener-execution-materialization.test.ts +4 -2
  196. package/src/core/listener-execution-materialization.ts +6 -9
  197. package/src/core/run-workflow.test.ts +43 -38
  198. package/src/core/run-workflow.ts +26 -9
  199. package/src/core/step-config/agent.ts +24 -26
  200. package/src/core/step-config/complete-step-dispatch-config.test.ts +22 -23
  201. package/src/core/step-config/complete-step-dispatch-config.ts +6 -6
  202. package/src/core/step-config/materialize-job-execution-steps.test.ts +34 -23
  203. package/src/core/step-config/materialize-job-execution-steps.ts +38 -39
  204. package/src/core/step-config/materialize-workflow-model.test.ts +94 -82
  205. package/src/core/step-config/materialize-workflow-model.ts +31 -29
  206. package/src/core/step-config/resolve-job-execution-name.ts +1 -1
  207. package/src/core/step-config/resolve-step-config.ts +11 -9
  208. package/src/core/step-config/run.ts +1 -1
  209. package/src/core/workflow-run-creation.test.ts +14 -10
  210. package/src/core/workflow-run-creation.ts +6 -9
  211. package/src/db/job-listeners.ts +20 -5
  212. package/src/db/runner-lease-table.ts +2 -2
  213. package/src/db/schema/workflow-run-attempts.ts +7 -3
  214. package/src/db/schema/workflow-runs.ts +1 -1
  215. package/src/db/workflow-runs/job-executions.ts +15 -1
  216. package/src/db/workflow-runs/jobs.ts +4 -1
  217. package/src/db/workflow-runs/run-create.test.ts +7 -1
  218. package/src/db/workflow-runs/run-create.ts +25 -15
  219. package/src/db/workflow-runs/run-rerun.test.ts +1 -1
  220. package/src/db/workflow-runs/run-rerun.ts +6 -3
  221. package/src/index.ts +75 -33
  222. package/src/presentation/dto/checkout-token.ts +7 -1
  223. package/src/presentation/index.ts +1 -1
  224. package/src/presentation/inter-module.test.ts +149 -0
  225. package/src/presentation/inter-module.ts +132 -0
  226. package/src/presentation/routes/agent-runtime-config.test.ts +53 -10
  227. package/src/presentation/routes/agent-runtime-config.ts +89 -64
  228. package/src/presentation/routes/cancel-run.test.ts +10 -20
  229. package/src/presentation/routes/cancel-run.ts +30 -27
  230. package/src/presentation/routes/checkout-token.test.ts +52 -33
  231. package/src/presentation/routes/checkout-token.ts +109 -49
  232. package/src/presentation/routes/get-run-aggregates.test.ts +13 -7
  233. package/src/presentation/routes/get-run-aggregates.ts +54 -57
  234. package/src/presentation/routes/get-run.test.ts +10 -18
  235. package/src/presentation/routes/get-run.ts +51 -48
  236. package/src/presentation/routes/get-step-secrets.test.ts +29 -7
  237. package/src/presentation/routes/get-step-secrets.ts +72 -61
  238. package/src/presentation/routes/index.test.ts +16 -1
  239. package/src/presentation/routes/index.ts +54 -29
  240. package/src/presentation/routes/leased-step.ts +3 -2
  241. package/src/presentation/routes/list-run-attempts.test.ts +9 -19
  242. package/src/presentation/routes/list-run-attempts.ts +21 -18
  243. package/src/presentation/routes/list-runs.test.ts +13 -7
  244. package/src/presentation/routes/list-runs.ts +64 -67
  245. package/src/presentation/routes/next-step.test.ts +30 -11
  246. package/src/presentation/routes/next-step.ts +68 -48
  247. package/src/presentation/routes/project-access.ts +16 -0
  248. package/src/presentation/routes/report-step.test.ts +18 -2
  249. package/src/presentation/routes/report-step.ts +51 -49
  250. package/src/presentation/routes/require-accessible-run.ts +5 -2
  251. package/src/presentation/routes/rerun-run.test.ts +9 -19
  252. package/src/presentation/routes/rerun-run.ts +39 -36
  253. package/src/temporal/activities/index.ts +32 -12
  254. package/src/temporal/activities/orchestration-activities.test.ts +10 -8
  255. package/src/temporal/activities/orchestration-activities.ts +74 -50
  256. package/src/temporal/workflows/job-execution-orchestration.test.ts +47 -2
  257. package/test/env.ts +1 -3
  258. package/test/factories/project.ts +10 -5
  259. package/test/factories/workflow-model.test.ts +5 -1
  260. package/test/factories/workflow-model.ts +1 -1
  261. package/test/factories/workflow-run.ts +1 -1
  262. package/test/fixtures/agent-inter-module.ts +27 -0
  263. package/test/fixtures/annotations-inter-module.ts +5 -0
  264. package/test/fixtures/auth-inter-module.ts +22 -0
  265. package/test/fixtures/lease-token.ts +2 -1
  266. package/test/fixtures/projects-inter-module.ts +8 -0
  267. package/test/fixtures/runners-inter-module.ts +52 -0
  268. package/test/fixtures/secrets-inter-module.ts +61 -0
  269. package/test/globalSetup.ts +11 -11
  270. package/tsconfig.build.tsbuildinfo +1 -1
  271. package/dist/core/source-control.d.ts +0 -5
  272. package/dist/core/source-control.d.ts.map +0 -1
  273. package/dist/core/source-control.js +0 -19
  274. package/dist/core/source-control.js.map +0 -1
  275. package/src/core/source-control.test.ts +0 -17
  276. package/src/core/source-control.ts +0 -22
  277. package/src/temporal/workflows/job-execution-orchestration-timeout.test.ts +0 -165
@@ -1,81 +1,106 @@
1
- import {ModelProviderConfigNotFoundError} from '@shipfox/api-agent/core/errors';
2
- import {resolveRuntimeCredentials} from '@shipfox/api-agent/core/resolve-runtime-credentials';
3
1
  import {
4
2
  agentRuntimeCredentialsResponseSchema,
5
3
  type MaterializedAgentStepConfigDto,
6
4
  materializedAgentStepConfigSchema,
7
5
  } from '@shipfox/api-agent-dto';
8
- import {SecretDecryptionError} from '@shipfox/api-secrets';
6
+ import {
7
+ type AgentInterModuleClient,
8
+ agentInterModuleContract,
9
+ } from '@shipfox/api-agent-dto/inter-module';
10
+ import type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';
9
11
  import {agentRuntimeConfigQuerySchema} from '@shipfox/api-workflows-dto';
12
+ import {isInterModuleKnownError} from '@shipfox/inter-module';
10
13
  import {captureException} from '@shipfox/node-error-monitoring';
11
14
  import {ClientError, defineRoute} from '@shipfox/node-fastify';
12
15
  import {ZodError} from 'zod';
13
16
  import {loadRunningLeasedStep} from './leased-step.js';
14
17
 
15
- export const agentRuntimeConfigRoute = defineRoute({
16
- method: 'GET',
17
- path: '/agent-runtime-config',
18
- description:
19
- "Returns the resolved harness, provider, model, thinking effort, and decrypted provider credential bundle for the runner's currently leased running agent step. The job is identified by the lease token and the step is bound to that job before credentials are returned.",
20
- schema: {
21
- querystring: agentRuntimeConfigQuerySchema,
22
- response: {
23
- 200: agentRuntimeCredentialsResponseSchema,
18
+ export function createAgentRuntimeConfigRoute(params: {
19
+ agent: AgentInterModuleClient;
20
+ runners: RunnersInterModuleClient;
21
+ }) {
22
+ return defineRoute({
23
+ method: 'GET',
24
+ path: '/agent-runtime-config',
25
+ description:
26
+ "Returns the resolved harness, provider, model, thinking effort, and decrypted provider credential bundle for the runner's currently leased running agent step. The job is identified by the lease token and the step is bound to that job before credentials are returned.",
27
+ schema: {
28
+ querystring: agentRuntimeConfigQuerySchema,
29
+ response: {
30
+ 200: agentRuntimeCredentialsResponseSchema,
31
+ },
32
+ },
33
+ errorHandler: (error) => {
34
+ if (
35
+ isInterModuleKnownError(
36
+ agentInterModuleContract.methods.resolveRuntimeCredentials,
37
+ error,
38
+ ) &&
39
+ error.code === 'model-provider-credentials-invalid'
40
+ ) {
41
+ captureException(error);
42
+ throw new ClientError(
43
+ 'Model provider credentials could not be decrypted',
44
+ 'model-provider-credentials-invalid',
45
+ {
46
+ status: 409,
47
+ cause: error,
48
+ },
49
+ );
50
+ }
51
+ if (
52
+ isInterModuleKnownError(
53
+ agentInterModuleContract.methods.resolveRuntimeCredentials,
54
+ error,
55
+ ) &&
56
+ error.code === 'model-provider-not-configured'
57
+ ) {
58
+ throw new ClientError(
59
+ 'Model provider credentials are not configured',
60
+ 'model-provider-not-configured',
61
+ {
62
+ status: 409,
63
+ },
64
+ );
65
+ }
66
+ throw error;
24
67
  },
25
- },
26
- errorHandler: (error) => {
27
- if (error instanceof SecretDecryptionError) {
28
- captureException(error);
29
- throw new ClientError(
30
- 'Model provider credentials could not be decrypted',
31
- 'model-provider-credentials-invalid',
32
- {
33
- status: 409,
34
- cause: error,
35
- },
36
- );
37
- }
38
- if (error instanceof ModelProviderConfigNotFoundError) {
39
- throw new ClientError(
40
- 'Model provider credentials are not configured',
41
- 'model-provider-not-configured',
42
- {
43
- status: 409,
44
- },
45
- );
46
- }
47
- throw error;
48
- },
49
- handler: async (request, reply) => {
50
- const {step_id: stepId, attempt} = request.query;
51
- const {step, workspaceId} = await loadRunningLeasedStep({request, stepId, attempt});
68
+ handler: async (request, reply) => {
69
+ const {step_id: stepId, attempt} = request.query;
70
+ const {step, workspaceId} = await loadRunningLeasedStep({
71
+ runners: params.runners,
72
+ request,
73
+ stepId,
74
+ attempt,
75
+ });
52
76
 
53
- if (step.type !== 'agent') {
54
- throw new ClientError('Step is not an agent step', 'step-not-agent', {status: 409});
55
- }
77
+ if (step.type !== 'agent') {
78
+ throw new ClientError('Step is not an agent step', 'step-not-agent', {status: 409});
79
+ }
56
80
 
57
- let agentConfig: MaterializedAgentStepConfigDto;
58
- try {
59
- agentConfig = materializedAgentStepConfigSchema.parse(step.config);
60
- } catch (error) {
61
- if (error instanceof ZodError) {
62
- throw new ClientError('Agent step config is invalid', 'agent-step-config-invalid', {
63
- status: 409,
64
- cause: error,
65
- });
81
+ let agentConfig: MaterializedAgentStepConfigDto;
82
+ try {
83
+ agentConfig = materializedAgentStepConfigSchema.parse(step.config);
84
+ } catch (error) {
85
+ if (error instanceof ZodError) {
86
+ throw new ClientError('Agent step config is invalid', 'agent-step-config-invalid', {
87
+ status: 409,
88
+ cause: error,
89
+ });
90
+ }
91
+ throw error;
66
92
  }
67
- throw error;
68
- }
69
93
 
70
- const runtimeConfig = await resolveRuntimeCredentials({
71
- workspaceId,
72
- harness: agentConfig.harness,
73
- provider: agentConfig.provider,
74
- model: agentConfig.model,
75
- thinking: agentConfig.thinking,
76
- });
94
+ const runtimeConfig = await params.agent.resolveRuntimeCredentials({
95
+ workspaceId,
96
+ harness: agentConfig.harness,
97
+ provider: agentConfig.provider,
98
+ model: agentConfig.model,
99
+ thinking: agentConfig.thinking,
100
+ });
77
101
 
78
- reply.header('cache-control', 'no-store');
79
- return runtimeConfig;
80
- },
81
- });
102
+ reply.header('cache-control', 'no-store');
103
+ return runtimeConfig;
104
+ },
105
+ });
106
+ }
@@ -1,5 +1,5 @@
1
1
  import {buildUserContext, setUserContext} from '@shipfox/api-auth-context';
2
- import {requireProjectAccess} from '@shipfox/api-projects';
2
+ import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
3
3
  import {ClientError} from '@shipfox/node-fastify';
4
4
  import type {FastifyInstance} from 'fastify';
5
5
  import Fastify from 'fastify';
@@ -12,16 +12,11 @@ import {cancelRunRoute} from './cancel-run.js';
12
12
 
13
13
  const projectAccessState = vi.hoisted(() => ({workspaceId: ''}));
14
14
 
15
- vi.mock('@shipfox/api-projects', () => ({
16
- requireProjectAccess: vi.fn(({projectId}) =>
17
- Promise.resolve({
18
- project: {id: projectId, workspaceId: projectAccessState.workspaceId},
19
- workspaceId: projectAccessState.workspaceId,
20
- }),
21
- ),
22
- }));
23
-
24
- const mockRequireProjectAccess = vi.mocked(requireProjectAccess);
15
+ const getProjectById = vi.fn();
16
+ const projects = {
17
+ getProjectById,
18
+ requireProjectForWorkspace: vi.fn(),
19
+ } as unknown as ProjectsModuleClient;
25
20
 
26
21
  describe('POST /api/workflows/runs/:id/cancel', () => {
27
22
  let app: FastifyInstance;
@@ -44,7 +39,7 @@ describe('POST /api/workflows/runs/:id/cancel', () => {
44
39
  );
45
40
  done();
46
41
  });
47
- app.post('/api/workflows/runs/:id/cancel', cancelRunRoute);
42
+ app.post('/api/workflows/runs/:id/cancel', cancelRunRoute(projects));
48
43
  await app.ready();
49
44
  });
50
45
 
@@ -53,7 +48,7 @@ describe('POST /api/workflows/runs/:id/cancel', () => {
53
48
  projectId = crypto.randomUUID();
54
49
  definitionId = crypto.randomUUID();
55
50
  projectAccessState.workspaceId = workspaceId;
56
- mockRequireProjectAccess.mockImplementation(({projectId: requestedProjectId}) =>
51
+ getProjectById.mockImplementation(({projectId: requestedProjectId}) =>
57
52
  Promise.resolve({
58
53
  project: {
59
54
  id: requestedProjectId,
@@ -61,10 +56,7 @@ describe('POST /api/workflows/runs/:id/cancel', () => {
61
56
  sourceConnectionId: crypto.randomUUID(),
62
57
  sourceExternalRepositoryId: `repo:${crypto.randomUUID()}`,
63
58
  name: 'Project',
64
- createdAt: new Date(),
65
- updatedAt: new Date(),
66
59
  },
67
- workspaceId,
68
60
  }),
69
61
  );
70
62
  });
@@ -97,7 +89,7 @@ describe('POST /api/workflows/runs/:id/cancel', () => {
97
89
 
98
90
  test('maps a run disappearing during cancellation to 404', () => {
99
91
  let mapped: unknown;
100
- const errorHandler = cancelRunRoute.errorHandler as (error: Error) => void;
92
+ const errorHandler = cancelRunRoute(projects).errorHandler as (error: Error) => void;
101
93
 
102
94
  try {
103
95
  errorHandler(new WorkflowRunNotFoundError(crypto.randomUUID()));
@@ -111,9 +103,7 @@ describe('POST /api/workflows/runs/:id/cancel', () => {
111
103
 
112
104
  test('returns 404 when project access is denied', async () => {
113
105
  const run = await createRun();
114
- mockRequireProjectAccess.mockRejectedValueOnce(
115
- new ClientError('Forbidden', 'forbidden', {status: 403}),
116
- );
106
+ getProjectById.mockRejectedValueOnce(new ClientError('Forbidden', 'forbidden', {status: 403}));
117
107
 
118
108
  const res = await app.inject({
119
109
  method: 'POST',
@@ -1,3 +1,4 @@
1
+ import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
1
2
  import {workflowRunDtoSchema} from '@shipfox/api-workflows-dto';
2
3
  import {ClientError, defineRoute} from '@shipfox/node-fastify';
3
4
  import {z} from 'zod';
@@ -6,32 +7,34 @@ import {cancelWorkflowRun} from '#db/index.js';
6
7
  import {toRunDto} from '#presentation/dto/index.js';
7
8
  import {requireAccessibleRun} from './require-accessible-run.js';
8
9
 
9
- export const cancelRunRoute = defineRoute({
10
- method: 'POST',
11
- path: '/:id/cancel',
12
- description: 'Cancel an in-progress workflow run',
13
- schema: {
14
- params: z.object({
15
- id: z.string().uuid(),
16
- }),
17
- response: {
18
- 200: workflowRunDtoSchema,
10
+ export function cancelRunRoute(projects: ProjectsModuleClient) {
11
+ return defineRoute({
12
+ method: 'POST',
13
+ path: '/:id/cancel',
14
+ description: 'Cancel an in-progress workflow run',
15
+ schema: {
16
+ params: z.object({
17
+ id: z.string().uuid(),
18
+ }),
19
+ response: {
20
+ 200: workflowRunDtoSchema,
21
+ },
19
22
  },
20
- },
21
- errorHandler: (error) => {
22
- if (error instanceof WorkflowRunNotCancellableError) {
23
- throw new ClientError(error.message, 'run-already-finished', {status: 409});
24
- }
25
- if (error instanceof WorkflowRunNotFoundError) {
26
- throw new ClientError('Run not found', 'not-found', {status: 404});
27
- }
28
- throw error;
29
- },
30
- handler: async (request) => {
31
- const {id} = request.params;
32
- const run = await requireAccessibleRun({request, id});
23
+ errorHandler: (error) => {
24
+ if (error instanceof WorkflowRunNotCancellableError) {
25
+ throw new ClientError(error.message, 'run-already-finished', {status: 409});
26
+ }
27
+ if (error instanceof WorkflowRunNotFoundError) {
28
+ throw new ClientError('Run not found', 'not-found', {status: 404});
29
+ }
30
+ throw error;
31
+ },
32
+ handler: async (request) => {
33
+ const {id} = request.params;
34
+ const run = await requireAccessibleRun({request, id, projects});
33
35
 
34
- const cancelled = await cancelWorkflowRun({workflowRunId: run.id});
35
- return toRunDto(cancelled);
36
- },
37
- });
36
+ const cancelled = await cancelWorkflowRun({workflowRunId: run.id});
37
+ return toRunDto(cancelled);
38
+ },
39
+ });
40
+ }
@@ -1,27 +1,22 @@
1
1
  import {createLeaseTokenAuthMethod} from '@shipfox/api-auth';
2
- import {
3
- type CheckoutSpec,
4
- IntegrationCheckoutUnsupportedError,
5
- IntegrationConnectionInactiveError,
6
- IntegrationProviderError,
7
- type IntegrationSourceControlService,
8
- } from '@shipfox/api-integration-core';
9
- import {getProjectById} from '@shipfox/api-projects';
2
+ import {integrationsInterModuleContract} from '@shipfox/api-integration-core-dto';
3
+ import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
4
+ import {createInterModuleKnownError} from '@shipfox/inter-module';
10
5
  import {closeApp, createApp, type FastifyInstance} from '@shipfox/node-fastify';
11
6
  import {createCapturingLogger} from '@shipfox/node-log/test';
12
- import {clearSourceControl, setSourceControl} from '#core/source-control.js';
13
7
  import {jobFactory} from '#test/factories/job.js';
14
8
  import {projectFactory} from '#test/factories/project.js';
15
9
  import {mintActiveLeaseToken} from '#test/fixtures/active-lease-token.js';
16
10
  import {mintLeaseToken} from '#test/fixtures/lease-token.js';
17
- import {leaseTokenRouteGroup} from './index.js';
11
+ import {runnersTestClient} from '#test/fixtures/runners-inter-module.js';
12
+ import {createLeaseTokenRouteGroup} from './index.js';
18
13
 
19
- vi.mock('@shipfox/api-projects', () => ({getProjectById: vi.fn()}));
20
- const mockGetProjectById = vi.mocked(getProjectById);
14
+ const getProjectById = vi.fn();
15
+ const projects = {getProjectById} as Pick<ProjectsModuleClient, 'getProjectById'>;
21
16
 
22
17
  const URL = '/runs/jobs/current/checkout-token';
23
18
 
24
- const githubSpec = (token: string): CheckoutSpec => ({
19
+ const githubSpec = (token: string) => ({
25
20
  repositoryUrl: 'https://github.com/acme/repo.git',
26
21
  ref: 'main',
27
22
  credentials: {username: 'x-access-token', token, expiresAt: new Date('2026-06-10T12:00:00.000Z')},
@@ -33,10 +28,19 @@ describe('POST /runs/jobs/current/checkout-token', () => {
33
28
  const {logger, lines: logLines, clear: clearLogLines} = createCapturingLogger();
34
29
 
35
30
  beforeAll(async () => {
36
- setSourceControl({createCheckoutSpec} as unknown as IntegrationSourceControlService);
37
31
  app = await createApp({
38
32
  auth: [createLeaseTokenAuthMethod()],
39
- routes: [leaseTokenRouteGroup],
33
+ routes: [
34
+ createLeaseTokenRouteGroup({
35
+ agent: {} as never,
36
+ annotations: {} as never,
37
+ auth: {} as never,
38
+ integrations: {createCheckoutSpec} as never,
39
+ projects: projects as never,
40
+ runners: runnersTestClient,
41
+ secrets: {} as never,
42
+ }),
43
+ ],
40
44
  swagger: false,
41
45
  fastifyOptions: {loggerInstance: logger},
42
46
  });
@@ -45,13 +49,12 @@ describe('POST /runs/jobs/current/checkout-token', () => {
45
49
 
46
50
  beforeEach(() => {
47
51
  createCheckoutSpec.mockReset();
48
- mockGetProjectById.mockReset();
52
+ getProjectById.mockReset();
49
53
  clearLogLines();
50
54
  });
51
55
 
52
56
  afterAll(async () => {
53
57
  await closeApp();
54
- clearSourceControl();
55
58
  });
56
59
 
57
60
  describe('lease-token auth', () => {
@@ -97,7 +100,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
97
100
 
98
101
  test('returns basic auth for a GitHub-style spec with credentials', async () => {
99
102
  const project = projectFactory.build();
100
- mockGetProjectById.mockResolvedValue(project);
103
+ getProjectById.mockResolvedValue({project});
101
104
  const job = await jobFactory.create({}, {transient: {projectId: project.id}});
102
105
  createCheckoutSpec.mockResolvedValue(githubSpec('ghs-secret-token'));
103
106
  const token = await mintActiveLeaseToken({jobId: job.id});
@@ -127,12 +130,12 @@ describe('POST /runs/jobs/current/checkout-token', () => {
127
130
 
128
131
  test('omits auth for a credential-free (debug) spec', async () => {
129
132
  const project = projectFactory.build();
130
- mockGetProjectById.mockResolvedValue(project);
133
+ getProjectById.mockResolvedValue({project});
131
134
  const job = await jobFactory.create({}, {transient: {projectId: project.id}});
132
135
  createCheckoutSpec.mockResolvedValue({
133
136
  repositoryUrl: 'https://example.com/acme/repo.git',
134
137
  ref: 'trunk',
135
- } satisfies CheckoutSpec);
138
+ });
136
139
  const token = await mintActiveLeaseToken({jobId: job.id});
137
140
 
138
141
  const res = await app.inject({
@@ -172,7 +175,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
172
175
  'skipped',
173
176
  ] as const)('returns 404 and mints nothing without an active lease for %s job', async (status) => {
174
177
  const project = projectFactory.build();
175
- mockGetProjectById.mockResolvedValue(project);
178
+ getProjectById.mockResolvedValue({project});
176
179
  const job = await jobFactory.create({}, {transient: {projectId: project.id, status}});
177
180
  const token = await mintLeaseToken({jobId: job.id});
178
181
 
@@ -189,7 +192,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
189
192
 
190
193
  test('returns checkout credentials while the parent job projection is still pending', async () => {
191
194
  const project = projectFactory.build();
192
- mockGetProjectById.mockResolvedValue(project);
195
+ getProjectById.mockResolvedValue({project});
193
196
  const job = await jobFactory.create(
194
197
  {},
195
198
  {transient: {projectId: project.id, status: 'pending'}},
@@ -216,7 +219,9 @@ describe('POST /runs/jobs/current/checkout-token', () => {
216
219
  [projectA.id, projectA],
217
220
  [projectB.id, projectB],
218
221
  ]);
219
- mockGetProjectById.mockImplementation((id: string) => Promise.resolve(projectsById.get(id)));
222
+ getProjectById.mockImplementation((input: {projectId: string}) =>
223
+ Promise.resolve({project: projectsById.get(input.projectId) ?? null}),
224
+ );
220
225
  const jobA = await jobFactory.create({}, {transient: {projectId: projectA.id}});
221
226
  const jobB = await jobFactory.create({}, {transient: {projectId: projectB.id}});
222
227
  createCheckoutSpec.mockResolvedValue(githubSpec('token'));
@@ -242,7 +247,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
242
247
 
243
248
  test('passes the project workspace, never the lease workspace claim', async () => {
244
249
  const project = projectFactory.build();
245
- mockGetProjectById.mockResolvedValue(project);
250
+ getProjectById.mockResolvedValue({project});
246
251
  const job = await jobFactory.create({}, {transient: {projectId: project.id}});
247
252
  createCheckoutSpec.mockResolvedValue(githubSpec('token'));
248
253
  const token = await mintActiveLeaseToken({
@@ -258,7 +263,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
258
263
  });
259
264
 
260
265
  test('returns 404 when the run has no project linkage', async () => {
261
- mockGetProjectById.mockResolvedValue(undefined);
266
+ getProjectById.mockResolvedValue({project: null});
262
267
  const project = {id: crypto.randomUUID(), workspaceId: crypto.randomUUID()};
263
268
  const job = await jobFactory.create({}, {transient: {projectId: project.id}});
264
269
  const token = await mintActiveLeaseToken({jobId: job.id});
@@ -275,10 +280,14 @@ describe('POST /runs/jobs/current/checkout-token', () => {
275
280
 
276
281
  test('maps a rate-limited provider error to 429 with retry_after_seconds', async () => {
277
282
  const project = projectFactory.build();
278
- mockGetProjectById.mockResolvedValue(project);
283
+ getProjectById.mockResolvedValue({project});
279
284
  const job = await jobFactory.create({}, {transient: {projectId: project.id}});
280
285
  createCheckoutSpec.mockRejectedValue(
281
- new IntegrationProviderError('rate-limited', 'slow down', 60),
286
+ createInterModuleKnownError(
287
+ integrationsInterModuleContract.methods.createCheckoutSpec,
288
+ 'provider-failure',
289
+ {reason: 'rate-limited', retryAfterSeconds: 60},
290
+ ),
282
291
  );
283
292
  const token = await mintActiveLeaseToken({jobId: job.id});
284
293
 
@@ -295,10 +304,14 @@ describe('POST /runs/jobs/current/checkout-token', () => {
295
304
 
296
305
  test('maps an inactive connection error to 422', async () => {
297
306
  const project = projectFactory.build();
298
- mockGetProjectById.mockResolvedValue(project);
307
+ getProjectById.mockResolvedValue({project});
299
308
  const job = await jobFactory.create({}, {transient: {projectId: project.id}});
300
309
  createCheckoutSpec.mockRejectedValue(
301
- new IntegrationConnectionInactiveError(project.sourceConnectionId),
310
+ createInterModuleKnownError(
311
+ integrationsInterModuleContract.methods.createCheckoutSpec,
312
+ 'connection-inactive',
313
+ {connectionId: project.sourceConnectionId},
314
+ ),
302
315
  );
303
316
  const token = await mintActiveLeaseToken({jobId: job.id});
304
317
 
@@ -313,9 +326,15 @@ describe('POST /runs/jobs/current/checkout-token', () => {
313
326
 
314
327
  test('maps an unsupported-checkout provider error to 422', async () => {
315
328
  const project = projectFactory.build();
316
- mockGetProjectById.mockResolvedValue(project);
329
+ getProjectById.mockResolvedValue({project});
317
330
  const job = await jobFactory.create({}, {transient: {projectId: project.id}});
318
- createCheckoutSpec.mockRejectedValue(new IntegrationCheckoutUnsupportedError('github'));
331
+ createCheckoutSpec.mockRejectedValue(
332
+ createInterModuleKnownError(
333
+ integrationsInterModuleContract.methods.createCheckoutSpec,
334
+ 'checkout-unsupported',
335
+ {provider: 'github'},
336
+ ),
337
+ );
319
338
  const token = await mintActiveLeaseToken({jobId: job.id});
320
339
 
321
340
  const res = await app.inject({
@@ -330,7 +349,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
330
349
 
331
350
  test('surfaces an unexpected provider error as a 500 server-error', async () => {
332
351
  const project = projectFactory.build();
333
- mockGetProjectById.mockResolvedValue(project);
352
+ getProjectById.mockResolvedValue({project});
334
353
  const job = await jobFactory.create({}, {transient: {projectId: project.id}});
335
354
  createCheckoutSpec.mockRejectedValue(new Error('unexpected provider failure'));
336
355
  const token = await mintActiveLeaseToken({jobId: job.id});
@@ -347,7 +366,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
347
366
 
348
367
  test('never writes the minted token to a log line', async () => {
349
368
  const project = projectFactory.build();
350
- mockGetProjectById.mockResolvedValue(project);
369
+ getProjectById.mockResolvedValue({project});
351
370
  const job = await jobFactory.create({}, {transient: {projectId: project.id}});
352
371
  const secret = 'ghs-super-secret-token-value';
353
372
  createCheckoutSpec.mockResolvedValue(githubSpec(secret));