@shipfox/api-workflows 8.0.0 → 9.0.1
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.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +65 -0
- package/dist/core/agent-tools.d.ts +3 -1
- package/dist/core/agent-tools.d.ts.map +1 -1
- package/dist/core/agent-tools.js.map +1 -1
- package/dist/core/errors.d.ts +0 -4
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +0 -6
- package/dist/core/errors.js.map +1 -1
- package/dist/core/job-execution.d.ts +0 -2
- package/dist/core/job-execution.d.ts.map +1 -1
- package/dist/core/job-execution.js +3 -7
- package/dist/core/job-execution.js.map +1 -1
- package/dist/db/workflow-runs/job-executions.d.ts +0 -5
- package/dist/db/workflow-runs/job-executions.d.ts.map +1 -1
- package/dist/db/workflow-runs/job-executions.js +0 -7
- package/dist/db/workflow-runs/job-executions.js.map +1 -1
- package/dist/db/workflow-runs.d.ts +1 -1
- package/dist/db/workflow-runs.d.ts.map +1 -1
- package/dist/db/workflow-runs.js +1 -1
- package/dist/db/workflow-runs.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/next-step.d.ts.map +1 -1
- package/dist/presentation/routes/next-step.js +11 -8
- package/dist/presentation/routes/next-step.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +26 -28
- package/src/core/agent-tool-capability-warning.test.ts +25 -55
- package/src/core/agent-tools.ts +7 -5
- package/src/core/errors.ts +0 -7
- package/src/core/job-execution.ts +3 -10
- package/src/core/run-workflow.test.ts +20 -55
- package/src/core/step-config/materialize-job-execution-steps.test.ts +1 -2
- package/src/core/step-config/materialize-workflow-model.test.ts +11 -23
- package/src/db/workflow-runs/job-executions.ts +0 -21
- package/src/db/workflow-runs/run-create.test.ts +31 -33
- package/src/db/workflow-runs.ts +0 -1
- package/src/index.ts +1 -1
- package/src/presentation/dto/checkout-token.test.ts +7 -1
- package/src/presentation/routes/agent-runtime-config.test.ts +38 -97
- package/src/presentation/routes/next-step.test.ts +18 -12
- package/src/presentation/routes/next-step.ts +9 -5
- package/test/factories/workflow-model.ts +3 -1
- package/test/fixtures/active-lease-token.ts +5 -32
- package/test/fixtures/agent-inter-module.ts +3 -2
- package/test/fixtures/runners-inter-module.ts +40 -48
- package/test/globalSetup.ts +0 -38
- package/test/setup.ts +2 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/db/runner-lease-table.d.ts +0 -469
- package/dist/db/runner-lease-table.d.ts.map +0 -1
- package/dist/db/runner-lease-table.js +0 -43
- package/dist/db/runner-lease-table.js.map +0 -1
- package/src/db/runner-lease-table.ts +0 -39
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/api-workflows",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "9.0.1",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -11,10 +11,6 @@
|
|
|
11
11
|
"type": "module",
|
|
12
12
|
"main": "dist/index.js",
|
|
13
13
|
"types": "dist/index.d.ts",
|
|
14
|
-
"imports": {
|
|
15
|
-
"#test/*": "./test/*",
|
|
16
|
-
"#*": "./dist/*"
|
|
17
|
-
},
|
|
18
14
|
"exports": {
|
|
19
15
|
".": {
|
|
20
16
|
"types": "./dist/index.d.ts",
|
|
@@ -26,29 +22,31 @@
|
|
|
26
22
|
"@temporalio/workflow": "1.18.1",
|
|
27
23
|
"drizzle-orm": "^0.45.2",
|
|
28
24
|
"zod": "^4.4.3",
|
|
29
|
-
"@shipfox/api-auth-
|
|
30
|
-
"@shipfox/api-
|
|
31
|
-
"@shipfox/
|
|
32
|
-
"@shipfox/
|
|
33
|
-
"@shipfox/api-definitions-dto": "
|
|
34
|
-
"@shipfox/api-projects-dto": "
|
|
35
|
-
"@shipfox/api-
|
|
36
|
-
"@shipfox/api-
|
|
37
|
-
"@shipfox/api-
|
|
38
|
-
"@shipfox/
|
|
39
|
-
"@shipfox/api-
|
|
40
|
-
"@shipfox/
|
|
41
|
-
"@shipfox/
|
|
42
|
-
"@shipfox/node-
|
|
43
|
-
"@shipfox/node-
|
|
44
|
-
"@shipfox/node-
|
|
45
|
-
"@shipfox/node-
|
|
46
|
-
"@shipfox/node-
|
|
47
|
-
"@shipfox/node-
|
|
48
|
-
"@shipfox/
|
|
49
|
-
"@shipfox/node-
|
|
50
|
-
"@shipfox/
|
|
51
|
-
|
|
25
|
+
"@shipfox/api-auth-dto": "9.0.1",
|
|
26
|
+
"@shipfox/api-agent-dto": "9.0.1",
|
|
27
|
+
"@shipfox/annotations-dto": "9.0.1",
|
|
28
|
+
"@shipfox/api-auth-context": "9.0.1",
|
|
29
|
+
"@shipfox/api-definitions-dto": "9.0.1",
|
|
30
|
+
"@shipfox/api-projects-dto": "9.0.1",
|
|
31
|
+
"@shipfox/api-secrets-dto": "9.0.1",
|
|
32
|
+
"@shipfox/api-workflows-dto": "9.0.1",
|
|
33
|
+
"@shipfox/api-integration-core-dto": "9.0.1",
|
|
34
|
+
"@shipfox/inter-module": "0.2.1",
|
|
35
|
+
"@shipfox/api-runners-dto": "9.0.1",
|
|
36
|
+
"@shipfox/expression": "1.1.4",
|
|
37
|
+
"@shipfox/node-error-monitoring": "0.2.1",
|
|
38
|
+
"@shipfox/node-fastify": "0.3.1",
|
|
39
|
+
"@shipfox/node-opentelemetry": "0.6.1",
|
|
40
|
+
"@shipfox/node-outbox": "0.2.5",
|
|
41
|
+
"@shipfox/node-postgres": "0.4.3",
|
|
42
|
+
"@shipfox/node-module": "1.0.0",
|
|
43
|
+
"@shipfox/node-temporal": "0.4.1",
|
|
44
|
+
"@shipfox/runner-labels": "0.1.2",
|
|
45
|
+
"@shipfox/node-drizzle": "0.3.3",
|
|
46
|
+
"@shipfox/workflow-document": "2.1.2"
|
|
47
|
+
},
|
|
48
|
+
"imports": {
|
|
49
|
+
"#*": "./dist/*"
|
|
52
50
|
},
|
|
53
51
|
"scripts": {
|
|
54
52
|
"build": "shipfox-swc && shipfox-temporal-bundle dist/temporal/workflows/index.js",
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type {AnnotationsInterModuleClient} from '@shipfox/annotations-dto/inter-module';
|
|
2
2
|
import type {JobLeaseTokenClaims} from '@shipfox/api-auth-dto';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {runnersTestClient} from '#test/fixtures/runners-inter-module.js';
|
|
3
|
+
import type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';
|
|
4
|
+
import {runnersTestClient, setRunnerToolCapabilities} from '#test/fixtures/runners-inter-module.js';
|
|
6
5
|
import {warnAgentToolCapabilityMismatchOnDispatch as warnAgentToolCapabilityMismatchOnDispatchImpl} from './agent-tool-capability-warning.js';
|
|
7
6
|
import type {Step} from './entities/step.js';
|
|
8
7
|
|
|
@@ -86,45 +85,20 @@ function agentStep(params: Partial<Step> = {}): Step {
|
|
|
86
85
|
};
|
|
87
86
|
}
|
|
88
87
|
|
|
89
|
-
|
|
88
|
+
function setRunnerSessionCapabilities(params: {
|
|
90
89
|
runnerSessionId: string;
|
|
91
90
|
workspaceId: string;
|
|
92
|
-
toolCapabilities:
|
|
91
|
+
toolCapabilities:
|
|
92
|
+
| Awaited<
|
|
93
|
+
ReturnType<RunnersInterModuleClient['getEffectiveRunnerToolCapabilities']>
|
|
94
|
+
>['capabilities']
|
|
95
|
+
| null;
|
|
93
96
|
reportedAt?: 'fresh' | 'stale' | 'missing';
|
|
94
|
-
}):
|
|
95
|
-
|
|
96
|
-
params.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
? sql`now() - interval '1 hour'`
|
|
100
|
-
: sql`now()`;
|
|
101
|
-
const toolCapabilities =
|
|
102
|
-
params.toolCapabilities === null
|
|
103
|
-
? sql`NULL`
|
|
104
|
-
: sql`${JSON.stringify(params.toolCapabilities)}::jsonb`;
|
|
105
|
-
|
|
106
|
-
await db().execute(sql`
|
|
107
|
-
INSERT INTO runners_runner_sessions (
|
|
108
|
-
id,
|
|
109
|
-
workspace_id,
|
|
110
|
-
scope,
|
|
111
|
-
registration_token_id,
|
|
112
|
-
registration_token_kind,
|
|
113
|
-
labels,
|
|
114
|
-
tool_capabilities,
|
|
115
|
-
tool_capabilities_reported_at
|
|
116
|
-
)
|
|
117
|
-
VALUES (
|
|
118
|
-
${params.runnerSessionId},
|
|
119
|
-
${params.workspaceId},
|
|
120
|
-
'workspace',
|
|
121
|
-
${crypto.randomUUID()},
|
|
122
|
-
'manual',
|
|
123
|
-
ARRAY['linux'],
|
|
124
|
-
${toolCapabilities},
|
|
125
|
-
${reportedAt}
|
|
126
|
-
)
|
|
127
|
-
`);
|
|
97
|
+
}): void {
|
|
98
|
+
setRunnerToolCapabilities(params.runnerSessionId, {
|
|
99
|
+
capabilities: params.toolCapabilities === null ? {harnesses: {}} : params.toolCapabilities,
|
|
100
|
+
reportFresh: params.reportedAt !== 'missing' && params.reportedAt !== 'stale',
|
|
101
|
+
});
|
|
128
102
|
}
|
|
129
103
|
|
|
130
104
|
function annotationsFor(jobExecutionId: string): Array<{context: string; body: string}> {
|
|
@@ -138,7 +112,7 @@ describe('warnAgentToolCapabilityMismatchOnDispatch', () => {
|
|
|
138
112
|
it('writes a warning when the matched runner advertised a set without requested tools', async () => {
|
|
139
113
|
const identity = lease();
|
|
140
114
|
const step = agentStep({jobExecutionId: identity.jobExecutionId});
|
|
141
|
-
|
|
115
|
+
setRunnerSessionCapabilities({
|
|
142
116
|
runnerSessionId: identity.runnerSessionId,
|
|
143
117
|
workspaceId: identity.workspaceId,
|
|
144
118
|
toolCapabilities: {harnesses: {pi: {tools: ['read']}}},
|
|
@@ -156,7 +130,7 @@ describe('warnAgentToolCapabilityMismatchOnDispatch', () => {
|
|
|
156
130
|
it('writes unknown wording when capabilities are missing', async () => {
|
|
157
131
|
const identity = lease();
|
|
158
132
|
const step = agentStep({jobExecutionId: identity.jobExecutionId});
|
|
159
|
-
|
|
133
|
+
setRunnerSessionCapabilities({
|
|
160
134
|
runnerSessionId: identity.runnerSessionId,
|
|
161
135
|
workspaceId: identity.workspaceId,
|
|
162
136
|
toolCapabilities: null,
|
|
@@ -173,7 +147,7 @@ describe('warnAgentToolCapabilityMismatchOnDispatch', () => {
|
|
|
173
147
|
it('writes missing-harness wording when fresh capabilities omit the harness', async () => {
|
|
174
148
|
const identity = lease();
|
|
175
149
|
const step = agentStep({jobExecutionId: identity.jobExecutionId});
|
|
176
|
-
|
|
150
|
+
setRunnerSessionCapabilities({
|
|
177
151
|
runnerSessionId: identity.runnerSessionId,
|
|
178
152
|
workspaceId: identity.workspaceId,
|
|
179
153
|
toolCapabilities: {harnesses: {claude: {tools: ['read', 'web_search']}}},
|
|
@@ -199,7 +173,7 @@ describe('warnAgentToolCapabilityMismatchOnDispatch', () => {
|
|
|
199
173
|
prompt: 'Fix it.',
|
|
200
174
|
},
|
|
201
175
|
});
|
|
202
|
-
|
|
176
|
+
setRunnerSessionCapabilities({
|
|
203
177
|
runnerSessionId: identity.runnerSessionId,
|
|
204
178
|
workspaceId: identity.workspaceId,
|
|
205
179
|
toolCapabilities: {harnesses: {pi: {tools: ['read']}}},
|
|
@@ -214,20 +188,16 @@ describe('warnAgentToolCapabilityMismatchOnDispatch', () => {
|
|
|
214
188
|
it('removes an existing warning when a later dispatch has all requested tools', async () => {
|
|
215
189
|
const identity = lease();
|
|
216
190
|
const step = agentStep({jobExecutionId: identity.jobExecutionId});
|
|
217
|
-
|
|
191
|
+
setRunnerSessionCapabilities({
|
|
218
192
|
runnerSessionId: identity.runnerSessionId,
|
|
219
193
|
workspaceId: identity.workspaceId,
|
|
220
194
|
toolCapabilities: {harnesses: {pi: {tools: ['read']}}},
|
|
221
195
|
});
|
|
222
196
|
await warnAgentToolCapabilityMismatchOnDispatch({leaseIdentity: identity, step});
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
})}::jsonb,
|
|
228
|
-
tool_capabilities_reported_at = now()
|
|
229
|
-
WHERE id = ${identity.runnerSessionId}
|
|
230
|
-
`);
|
|
197
|
+
setRunnerToolCapabilities(identity.runnerSessionId, {
|
|
198
|
+
capabilities: {harnesses: {pi: {tools: ['read', 'web_search']}}},
|
|
199
|
+
reportFresh: true,
|
|
200
|
+
});
|
|
231
201
|
|
|
232
202
|
await warnAgentToolCapabilityMismatchOnDispatch({leaseIdentity: identity, step});
|
|
233
203
|
|
|
@@ -247,7 +217,7 @@ describe('warnAgentToolCapabilityMismatchOnDispatch', () => {
|
|
|
247
217
|
prompt: 'Fix it.',
|
|
248
218
|
},
|
|
249
219
|
});
|
|
250
|
-
|
|
220
|
+
setRunnerSessionCapabilities({
|
|
251
221
|
runnerSessionId: identity.runnerSessionId,
|
|
252
222
|
workspaceId: identity.workspaceId,
|
|
253
223
|
toolCapabilities: null,
|
|
@@ -265,7 +235,7 @@ describe('warnAgentToolCapabilityMismatchOnDispatch', () => {
|
|
|
265
235
|
type: 'run',
|
|
266
236
|
config: {run: 'echo ok'},
|
|
267
237
|
});
|
|
268
|
-
|
|
238
|
+
setRunnerSessionCapabilities({
|
|
269
239
|
runnerSessionId: identity.runnerSessionId,
|
|
270
240
|
workspaceId: identity.workspaceId,
|
|
271
241
|
toolCapabilities: null,
|
|
@@ -290,7 +260,7 @@ describe('warnAgentToolCapabilityMismatchOnDispatch', () => {
|
|
|
290
260
|
prompt: 'Fix it.',
|
|
291
261
|
},
|
|
292
262
|
});
|
|
293
|
-
|
|
263
|
+
setRunnerSessionCapabilities({
|
|
294
264
|
runnerSessionId: identity.runnerSessionId,
|
|
295
265
|
workspaceId: identity.workspaceId,
|
|
296
266
|
toolCapabilities: {harnesses: {pi: {tools: []}}},
|
package/src/core/agent-tools.ts
CHANGED
|
@@ -3,11 +3,6 @@ import type {
|
|
|
3
3
|
MaterializedAgentIntegrationToolConfigDto,
|
|
4
4
|
} from '@shipfox/api-agent-dto';
|
|
5
5
|
import type {WorkflowModel} from '@shipfox/api-definitions-dto';
|
|
6
|
-
import type {
|
|
7
|
-
AgentToolCatalogEntry,
|
|
8
|
-
AgentToolCatalogMethod,
|
|
9
|
-
IntegrationProviderKind,
|
|
10
|
-
} from '@shipfox/api-integration-core-dto';
|
|
11
6
|
import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto/inter-module';
|
|
12
7
|
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
|
|
13
8
|
import {AgentIntegrationMaterializationError} from './errors.js';
|
|
@@ -15,6 +10,13 @@ import {AgentIntegrationMaterializationError} from './errors.js';
|
|
|
15
10
|
type WorkflowModelJob = WorkflowModel['jobs'][number];
|
|
16
11
|
type WorkflowModelAgentStep = Extract<WorkflowModelJob['steps'][number], {kind: 'agent'}>;
|
|
17
12
|
type WorkflowModelStepIntegration = NonNullable<WorkflowModelAgentStep['integrations']>[number];
|
|
13
|
+
type IntegrationsAgentToolsContext = Awaited<
|
|
14
|
+
ReturnType<IntegrationsModuleClient['getAgentToolsContext']>
|
|
15
|
+
>;
|
|
16
|
+
export type AgentToolCatalogEntry =
|
|
17
|
+
IntegrationsAgentToolsContext['catalogs'][number]['tools'][number];
|
|
18
|
+
type AgentToolCatalogMethod = NonNullable<AgentToolCatalogEntry['methods']>[number];
|
|
19
|
+
type IntegrationProviderKind = IntegrationsAgentToolsContext['catalogs'][number]['provider'];
|
|
18
20
|
type AgentToolCatalogs = ReadonlyMap<IntegrationProviderKind, readonly AgentToolCatalogEntry[]>;
|
|
19
21
|
type WorkspaceConnectionSnapshot = ReadonlyMap<
|
|
20
22
|
string,
|
package/src/core/errors.ts
CHANGED
|
@@ -104,13 +104,6 @@ export class JobNotFoundError extends Error {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
export class JobLeaseNotActiveError extends Error {
|
|
108
|
-
constructor(readonly jobExecutionId: string) {
|
|
109
|
-
super(`Job lease is no longer active: ${jobExecutionId}`);
|
|
110
|
-
this.name = 'JobLeaseNotActiveError';
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
107
|
export class InvalidJobRunnerLabelsError extends Error {
|
|
115
108
|
constructor(readonly labels: readonly string[]) {
|
|
116
109
|
super(`Job runner labels are invalid: ${labels.join(', ')}`);
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
getStepAttemptsByJobExecutionId,
|
|
17
17
|
getStepsByJobExecutionIdForUpdate,
|
|
18
18
|
insertRunningStepAttempt,
|
|
19
|
-
lockActiveJobExecutionLeaseForUpdate,
|
|
20
19
|
markStepRunning,
|
|
21
20
|
markStepSkipped,
|
|
22
21
|
settleJobFailed,
|
|
@@ -33,7 +32,6 @@ import type {PersistedEvaluationTraceEntry, Step, StepStatusReason} from './enti
|
|
|
33
32
|
import {
|
|
34
33
|
AgentConfigUnresolvableError,
|
|
35
34
|
InterpolationUnresolvableError,
|
|
36
|
-
JobLeaseNotActiveError,
|
|
37
35
|
JobNotFoundError,
|
|
38
36
|
StepAttemptAheadError,
|
|
39
37
|
StepNotFoundError,
|
|
@@ -336,21 +334,16 @@ function dispatchConfigError(error: DispatchConfigError): Record<string, unknown
|
|
|
336
334
|
}
|
|
337
335
|
|
|
338
336
|
export interface NextStepForLeasedJobExecutionParams {
|
|
339
|
-
jobId: string;
|
|
340
337
|
jobExecutionId: string;
|
|
341
|
-
runnerSessionId: string;
|
|
342
338
|
agent?: AgentInterModuleClient | undefined;
|
|
343
339
|
}
|
|
344
340
|
|
|
345
341
|
export function nextStepForLeasedJobExecution(
|
|
346
342
|
params: NextStepForLeasedJobExecutionParams,
|
|
347
343
|
): Promise<NextStep> {
|
|
348
|
-
return withTransaction(
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
return nextStepForJobExecutionInTransaction(params.jobExecutionId, tx, params.agent);
|
|
353
|
-
});
|
|
344
|
+
return withTransaction((tx) =>
|
|
345
|
+
nextStepForJobExecutionInTransaction(params.jobExecutionId, tx, params.agent),
|
|
346
|
+
);
|
|
354
347
|
}
|
|
355
348
|
|
|
356
349
|
export function nextStepForJob(
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type {AgentInterModuleClient} from '@shipfox/api-agent-dto/inter-module';
|
|
2
|
-
import type
|
|
3
|
-
import {createWorkflowModelSnapshot} from '@shipfox/api-definitions-dto';
|
|
2
|
+
import {createWorkflowModelSnapshot, type WorkflowModel} from '@shipfox/api-definitions-dto';
|
|
4
3
|
import type {DefinitionsInterModuleClient} from '@shipfox/api-definitions-dto/inter-module';
|
|
5
4
|
import {agentValidationCatalog} from '#test/fixtures/agent-inter-module.js';
|
|
6
5
|
import {workflowModel} from '#test/index.js';
|
|
@@ -17,61 +16,26 @@ const mockResolveAgentConfig = vi.hoisted(() =>
|
|
|
17
16
|
}),
|
|
18
17
|
);
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
persistCredentials: true,
|
|
24
|
-
},
|
|
25
|
-
getDefinitionById: vi.fn(),
|
|
26
|
-
}));
|
|
19
|
+
type DefinitionForWorkflowRun = NonNullable<
|
|
20
|
+
Awaited<ReturnType<DefinitionsInterModuleClient['getDefinitionForWorkflowRun']>>['definition']
|
|
21
|
+
>;
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const mockGetDefinitionById = vi.mocked(getDefinitionById);
|
|
23
|
+
let definitionResponse: DefinitionForWorkflowRun | null = null;
|
|
31
24
|
const definitions: DefinitionsInterModuleClient = {
|
|
32
|
-
getDefinitionForWorkflowRun: async (
|
|
33
|
-
const definition = await mockGetDefinitionById(definitionId);
|
|
34
|
-
return {
|
|
35
|
-
definition:
|
|
36
|
-
definition === undefined
|
|
37
|
-
? null
|
|
38
|
-
: {
|
|
39
|
-
id: definition.id,
|
|
40
|
-
projectId: definition.projectId,
|
|
41
|
-
name: definition.name,
|
|
42
|
-
model: createWorkflowModelSnapshot(definition.model),
|
|
43
|
-
sourceSnapshot: definition.sourceSnapshot,
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
},
|
|
25
|
+
getDefinitionForWorkflowRun: async () => ({definition: definitionResponse}),
|
|
47
26
|
};
|
|
48
27
|
|
|
49
|
-
function buildDefinition(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
jobs: {
|
|
54
|
-
build: {steps: [{run: 'echo hello'}]},
|
|
55
|
-
},
|
|
56
|
-
};
|
|
28
|
+
function buildDefinition(
|
|
29
|
+
overrides: Partial<Omit<DefinitionForWorkflowRun, 'model'>> & {model?: WorkflowModel} = {},
|
|
30
|
+
): DefinitionForWorkflowRun {
|
|
31
|
+
const {model = workflowModel(), ...definitionOverrides} = overrides;
|
|
57
32
|
return {
|
|
58
33
|
id: crypto.randomUUID(),
|
|
59
34
|
projectId: crypto.randomUUID(),
|
|
60
|
-
configPath: '.shipfox/workflows/test.yml',
|
|
61
|
-
source: 'manual',
|
|
62
|
-
sha: null,
|
|
63
|
-
ref: null,
|
|
64
35
|
name: 'Test Workflow',
|
|
65
|
-
|
|
66
|
-
document,
|
|
67
|
-
model,
|
|
36
|
+
model: createWorkflowModelSnapshot(model),
|
|
68
37
|
sourceSnapshot: null,
|
|
69
|
-
|
|
70
|
-
fetchedAt: new Date(),
|
|
71
|
-
createdAt: new Date(),
|
|
72
|
-
updatedAt: new Date(),
|
|
73
|
-
deletedAt: null,
|
|
74
|
-
...overrides,
|
|
38
|
+
...definitionOverrides,
|
|
75
39
|
};
|
|
76
40
|
}
|
|
77
41
|
|
|
@@ -97,11 +61,12 @@ describe('runWorkflow', () => {
|
|
|
97
61
|
workspaceId = crypto.randomUUID();
|
|
98
62
|
projectId = crypto.randomUUID();
|
|
99
63
|
mockResolveAgentConfig.mockClear();
|
|
64
|
+
definitionResponse = null;
|
|
100
65
|
});
|
|
101
66
|
|
|
102
67
|
test('creates a run from a valid definition with the provided manual trigger payload', async () => {
|
|
103
68
|
const definition = buildDefinition({projectId});
|
|
104
|
-
|
|
69
|
+
definitionResponse = definition;
|
|
105
70
|
const triggerPayload = manualPayload();
|
|
106
71
|
|
|
107
72
|
const run = await runWorkflow(definitions, {
|
|
@@ -131,7 +96,7 @@ describe('runWorkflow', () => {
|
|
|
131
96
|
},
|
|
132
97
|
});
|
|
133
98
|
const definition = buildDefinition({projectId, model});
|
|
134
|
-
|
|
99
|
+
definitionResponse = definition;
|
|
135
100
|
|
|
136
101
|
const run = await runWorkflow(definitions, {
|
|
137
102
|
workspaceId,
|
|
@@ -147,7 +112,7 @@ describe('runWorkflow', () => {
|
|
|
147
112
|
|
|
148
113
|
test('persists an integration trigger payload intact', async () => {
|
|
149
114
|
const definition = buildDefinition({projectId});
|
|
150
|
-
|
|
115
|
+
definitionResponse = definition;
|
|
151
116
|
const triggerPayload: TriggerPayload = {
|
|
152
117
|
provider: 'github',
|
|
153
118
|
source: 'github_acme',
|
|
@@ -173,7 +138,7 @@ describe('runWorkflow', () => {
|
|
|
173
138
|
test('creates the run with the definition source snapshot', async () => {
|
|
174
139
|
const sourceSnapshot = {content: 'name: Test Workflow\njobs: {}\n', format: 'yaml'} as const;
|
|
175
140
|
const definition = buildDefinition({projectId, sourceSnapshot});
|
|
176
|
-
|
|
141
|
+
definitionResponse = definition;
|
|
177
142
|
|
|
178
143
|
const run = await runWorkflow(definitions, {
|
|
179
144
|
workspaceId,
|
|
@@ -187,7 +152,7 @@ describe('runWorkflow', () => {
|
|
|
187
152
|
});
|
|
188
153
|
|
|
189
154
|
test('throws DefinitionNotFoundError for unknown definition', async () => {
|
|
190
|
-
|
|
155
|
+
definitionResponse = null;
|
|
191
156
|
|
|
192
157
|
const unknownId = crypto.randomUUID();
|
|
193
158
|
|
|
@@ -205,7 +170,7 @@ describe('runWorkflow', () => {
|
|
|
205
170
|
test('throws ProjectMismatchError when definition.projectId does not match', async () => {
|
|
206
171
|
const otherProjectId = crypto.randomUUID();
|
|
207
172
|
const definition = buildDefinition({projectId: otherProjectId});
|
|
208
|
-
|
|
173
|
+
definitionResponse = definition;
|
|
209
174
|
|
|
210
175
|
await expect(
|
|
211
176
|
runWorkflow(definitions, {
|
|
@@ -220,7 +185,7 @@ describe('runWorkflow', () => {
|
|
|
220
185
|
|
|
221
186
|
test('passes inputs through to the run', async () => {
|
|
222
187
|
const definition = buildDefinition({projectId});
|
|
223
|
-
|
|
188
|
+
definitionResponse = definition;
|
|
224
189
|
|
|
225
190
|
const run = await runWorkflow(definitions, {
|
|
226
191
|
workspaceId,
|
|
@@ -5,10 +5,9 @@ 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 type {AgentToolCatalogEntry} from '@shipfox/api-integration-core';
|
|
9
8
|
import {createInterModuleKnownError} from '@shipfox/inter-module';
|
|
10
9
|
import type {AgentDefaultsResolver} from '#core/agent-defaults.js';
|
|
11
|
-
import type {AgentToolMaterializationContext} from '#core/agent-tools.js';
|
|
10
|
+
import type {AgentToolCatalogEntry, AgentToolMaterializationContext} from '#core/agent-tools.js';
|
|
12
11
|
import {AgentConfigUnresolvableError, InterpolationUnresolvableError} from '#core/errors.js';
|
|
13
12
|
import {resolveTestAgentDefaults} from '#test/fixtures/agent-inter-module.js';
|
|
14
13
|
import {workflowModel} from '#test/index.js';
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import {agentInterModuleContract} from '@shipfox/api-agent-dto/inter-module';
|
|
2
|
-
import {
|
|
3
|
-
DEFAULT_JOB_CHECKOUT,
|
|
4
|
-
normalizeWorkflowDocument as normalizeWorkflowDocumentImpl,
|
|
5
|
-
type WorkflowModel,
|
|
6
|
-
} from '@shipfox/api-definitions';
|
|
2
|
+
import {DEFAULT_JOB_CHECKOUT, type WorkflowModel} from '@shipfox/api-definitions-dto';
|
|
7
3
|
import {createInterModuleKnownError} from '@shipfox/inter-module';
|
|
8
4
|
import type {AgentDefaultsResolver} from '#core/agent-defaults.js';
|
|
9
5
|
import {AgentConfigUnresolvableError, InterpolationUnresolvableError} from '#core/errors.js';
|
|
10
|
-
import {
|
|
11
|
-
agentValidationCatalog,
|
|
12
|
-
resolveTestAgentDefaults,
|
|
13
|
-
} from '#test/fixtures/agent-inter-module.js';
|
|
6
|
+
import {resolveTestAgentDefaults} from '#test/fixtures/agent-inter-module.js';
|
|
14
7
|
import {workflowModel} from '#test/index.js';
|
|
15
8
|
import {
|
|
16
9
|
materializeJobRunner,
|
|
@@ -29,12 +22,6 @@ function materializeWorkflowModel(
|
|
|
29
22
|
return materializeWorkflowModelImpl({resolveAgentDefaults: resolveTestAgentDefaults, ...params});
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
function normalizeWorkflowDocument(
|
|
33
|
-
document: Parameters<typeof normalizeWorkflowDocumentImpl>[0],
|
|
34
|
-
): ReturnType<typeof normalizeWorkflowDocumentImpl> {
|
|
35
|
-
return normalizeWorkflowDocumentImpl(document, {agentValidationCatalog});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
25
|
function expression(source: string): TestWorkflowExpression {
|
|
39
26
|
return {language: 'cel', check: 'typed', source: source as TestWorkflowExpression['source']};
|
|
40
27
|
}
|
|
@@ -548,7 +535,7 @@ describe('materializeWorkflowModel', () => {
|
|
|
548
535
|
});
|
|
549
536
|
|
|
550
537
|
it('freezes vars from the run-creation context into step config', async () => {
|
|
551
|
-
const model =
|
|
538
|
+
const model = workflowModel({
|
|
552
539
|
name: 'vars workflow',
|
|
553
540
|
runner: 'ubuntu-latest',
|
|
554
541
|
jobs: {
|
|
@@ -1008,14 +995,15 @@ describe('materializeWorkflowModel', () => {
|
|
|
1008
995
|
|
|
1009
996
|
it('skips listening job steps at workflow-run creation', async () => {
|
|
1010
997
|
const stepNameSource = `Review ${template('execution.index')}`;
|
|
1011
|
-
const model =
|
|
998
|
+
const model = workflowModel({
|
|
1012
999
|
name: 'Listening workflow',
|
|
1013
1000
|
runner: 'ubuntu-latest',
|
|
1014
1001
|
jobs: {
|
|
1015
1002
|
review: {
|
|
1016
1003
|
listening: {
|
|
1017
1004
|
on: [{source: 'github', event: 'pull_request_review'}],
|
|
1018
|
-
|
|
1005
|
+
maxExecutions: 3,
|
|
1006
|
+
onResolve: 'finish',
|
|
1019
1007
|
},
|
|
1020
1008
|
steps: [{name: stepNameSource, prompt: 'Summarize the review.'}],
|
|
1021
1009
|
},
|
|
@@ -1036,14 +1024,15 @@ describe('materializeWorkflowModel', () => {
|
|
|
1036
1024
|
});
|
|
1037
1025
|
|
|
1038
1026
|
it('materializes one-shot siblings while skipping listening steps', async () => {
|
|
1039
|
-
const model =
|
|
1027
|
+
const model = workflowModel({
|
|
1040
1028
|
name: 'Mixed workflow',
|
|
1041
1029
|
runner: 'ubuntu-latest',
|
|
1042
1030
|
jobs: {
|
|
1043
1031
|
review: {
|
|
1044
1032
|
listening: {
|
|
1045
1033
|
on: [{source: 'github', event: 'pull_request_review'}],
|
|
1046
|
-
|
|
1034
|
+
maxExecutions: 3,
|
|
1035
|
+
onResolve: 'finish',
|
|
1047
1036
|
},
|
|
1048
1037
|
steps: [{name: `Review ${template('execution.index')}`, prompt: 'Summarize it.'}],
|
|
1049
1038
|
},
|
|
@@ -1104,12 +1093,12 @@ describe('materializeWorkflowModel', () => {
|
|
|
1104
1093
|
});
|
|
1105
1094
|
|
|
1106
1095
|
it('materializes step if as server-owned condition outside runner config', async () => {
|
|
1107
|
-
const model =
|
|
1096
|
+
const model = workflowModel({
|
|
1108
1097
|
name: 'Conditional workflow',
|
|
1109
1098
|
runner: 'ubuntu-latest',
|
|
1110
1099
|
jobs: {
|
|
1111
1100
|
build: {
|
|
1112
|
-
steps: [{if:
|
|
1101
|
+
steps: [{if: expression('false'), run: 'npm test'}],
|
|
1113
1102
|
},
|
|
1114
1103
|
},
|
|
1115
1104
|
});
|
|
@@ -1121,7 +1110,6 @@ describe('materializeWorkflowModel', () => {
|
|
|
1121
1110
|
language: 'cel',
|
|
1122
1111
|
source: 'false',
|
|
1123
1112
|
check: 'typed',
|
|
1124
|
-
resultType: 'bool',
|
|
1125
1113
|
});
|
|
1126
1114
|
expect(step?.config).toEqual({run: 'npm test'});
|
|
1127
1115
|
});
|
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
} from '#metrics/instance.js';
|
|
18
18
|
import {db, type Tx} from '../db.js';
|
|
19
19
|
import {writeWorkflowsOutboxEvent} from '../outbox-writes.js';
|
|
20
|
-
import {runningJobExecutions} from '../runner-lease-table.js';
|
|
21
20
|
import {jobExecutions, toJobExecution} from '../schema/job-executions.js';
|
|
22
21
|
import {jobs, toJob} from '../schema/jobs.js';
|
|
23
22
|
import {stepAttempts, toStepAttempt} from '../schema/step-attempts.js';
|
|
@@ -33,26 +32,6 @@ import {
|
|
|
33
32
|
} from './shared.js';
|
|
34
33
|
import {bulkUpdateStepStatuses, getStepsByJobExecutionIdForUpdate} from './steps.js';
|
|
35
34
|
|
|
36
|
-
export async function lockActiveJobExecutionLeaseForUpdate(
|
|
37
|
-
params: {jobId: string; jobExecutionId: string; runnerSessionId: string},
|
|
38
|
-
tx: Tx,
|
|
39
|
-
): Promise<boolean> {
|
|
40
|
-
const rows = await tx
|
|
41
|
-
.select({id: runningJobExecutions.id})
|
|
42
|
-
.from(runningJobExecutions)
|
|
43
|
-
.where(
|
|
44
|
-
and(
|
|
45
|
-
eq(runningJobExecutions.jobId, params.jobId),
|
|
46
|
-
eq(runningJobExecutions.jobExecutionId, params.jobExecutionId),
|
|
47
|
-
eq(runningJobExecutions.runnerSessionId, params.runnerSessionId),
|
|
48
|
-
),
|
|
49
|
-
)
|
|
50
|
-
.limit(1)
|
|
51
|
-
.for('update');
|
|
52
|
-
|
|
53
|
-
return rows.length > 0;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
35
|
export async function getJobExecutionById(id: string, tx?: Tx): Promise<JobExecution | undefined> {
|
|
57
36
|
const rows = await (tx ?? db())
|
|
58
37
|
.select()
|