@shipfox/api-workflows 16.1.0 → 17.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -4
- package/.turbo/turbo-type.log +0 -1
- package/CHANGELOG.md +35 -0
- package/dist/presentation/inter-module.d.ts.map +1 -1
- package/dist/presentation/inter-module.js +80 -19
- package/dist/presentation/inter-module.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/presentation/inter-module.test.ts +197 -0
- package/src/presentation/inter-module.ts +97 -23
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/api-workflows",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "17.1.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -23,30 +23,30 @@
|
|
|
23
23
|
"drizzle-orm": "^0.45.2",
|
|
24
24
|
"js-yaml": "^4.1.0",
|
|
25
25
|
"zod": "^4.4.3",
|
|
26
|
-
"@shipfox/api-auth-context": "
|
|
27
|
-
"@shipfox/api-auth-dto": "
|
|
28
|
-
"@shipfox/api-agent-dto": "
|
|
26
|
+
"@shipfox/api-auth-context": "17.0.0",
|
|
27
|
+
"@shipfox/api-auth-dto": "17.0.0",
|
|
28
|
+
"@shipfox/api-agent-dto": "17.1.0",
|
|
29
29
|
"@shipfox/annotations-dto": "12.3.0",
|
|
30
|
-
"@shipfox/api-definitions-dto": "
|
|
30
|
+
"@shipfox/api-definitions-dto": "17.0.0",
|
|
31
31
|
"@shipfox/api-projects-dto": "15.0.0",
|
|
32
32
|
"@shipfox/api-integration-core-dto": "16.0.0",
|
|
33
33
|
"@shipfox/api-runners-dto": "16.0.0",
|
|
34
34
|
"@shipfox/api-secrets-dto": "12.0.0",
|
|
35
|
-
"@shipfox/api-workflows-dto": "
|
|
35
|
+
"@shipfox/api-workflows-dto": "17.1.0",
|
|
36
36
|
"@shipfox/api-workspaces-dto": "15.0.0",
|
|
37
37
|
"@shipfox/config": "1.2.4",
|
|
38
38
|
"@shipfox/inter-module": "0.2.3",
|
|
39
|
-
"@shipfox/expression": "2.4.
|
|
39
|
+
"@shipfox/expression": "2.4.1",
|
|
40
40
|
"@shipfox/node-drizzle": "0.3.5",
|
|
41
41
|
"@shipfox/node-error-monitoring": "0.3.0",
|
|
42
42
|
"@shipfox/node-fastify": "0.4.3",
|
|
43
43
|
"@shipfox/node-module": "1.0.7",
|
|
44
44
|
"@shipfox/node-opentelemetry": "0.6.5",
|
|
45
45
|
"@shipfox/node-outbox": "0.2.6",
|
|
46
|
-
"@shipfox/node-postgres": "0.5.
|
|
46
|
+
"@shipfox/node-postgres": "0.5.1",
|
|
47
47
|
"@shipfox/node-temporal": "0.4.6",
|
|
48
48
|
"@shipfox/runner-labels": "0.2.0",
|
|
49
|
-
"@shipfox/workflow-document": "3.3.
|
|
49
|
+
"@shipfox/workflow-document": "3.3.1"
|
|
50
50
|
},
|
|
51
51
|
"imports": {
|
|
52
52
|
"#*": "./dist/*"
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
const mocks = vi.hoisted(() => ({
|
|
22
22
|
deliverEventToListener: vi.fn(),
|
|
23
23
|
getJobScope: vi.fn(),
|
|
24
|
+
getStepAttemptDetail: vi.fn(),
|
|
24
25
|
getStepById: vi.fn(),
|
|
25
26
|
getStepByIdForJobExecution: vi.fn(),
|
|
26
27
|
listStepAttemptIdsByJobId: vi.fn(),
|
|
@@ -49,6 +50,7 @@ describe('Workflows inter-module presentation', () => {
|
|
|
49
50
|
beforeEach(() => {
|
|
50
51
|
mocks.getJobScope.mockReset();
|
|
51
52
|
mocks.listStepAttemptIdsByJobId.mockReset();
|
|
53
|
+
mocks.getStepAttemptDetail.mockReset();
|
|
52
54
|
mocks.getStepById.mockReset();
|
|
53
55
|
mocks.getStepByIdForJobExecution.mockReset();
|
|
54
56
|
mocks.deliverEventToListener.mockReset();
|
|
@@ -162,6 +164,201 @@ describe('Workflows inter-module presentation', () => {
|
|
|
162
164
|
});
|
|
163
165
|
});
|
|
164
166
|
|
|
167
|
+
describe('getLeasedAgentSessionContext', () => {
|
|
168
|
+
const input = {
|
|
169
|
+
jobId: '00000000-0000-4000-8000-000000000006',
|
|
170
|
+
jobExecutionId: '00000000-0000-4000-8000-000000000007',
|
|
171
|
+
runnerSessionId: '00000000-0000-4000-8000-000000000008',
|
|
172
|
+
stepId: '00000000-0000-4000-8000-000000000009',
|
|
173
|
+
attempt: 1,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const method = workflowsInterModuleContract.methods.getLeasedAgentSessionContext;
|
|
177
|
+
|
|
178
|
+
function presentation(runners: {getLeaseState: ReturnType<typeof vi.fn>}) {
|
|
179
|
+
return createWorkflowsInterModulePresentation({
|
|
180
|
+
agent: {} as never,
|
|
181
|
+
definitions: {} as never,
|
|
182
|
+
integrations: {} as never,
|
|
183
|
+
projects: {} as never,
|
|
184
|
+
runners: runners as never,
|
|
185
|
+
secrets: {} as never,
|
|
186
|
+
workspaces: {getWorkspaceOperatingState: vi.fn()} as never,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function expectKnownError(call: Promise<unknown> | unknown, code: string): Promise<void> {
|
|
191
|
+
try {
|
|
192
|
+
await call;
|
|
193
|
+
throw new Error(`Expected contract error ${code}, but the call succeeded`);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
expect(isInterModuleKnownError(method, error) && (error as {code: string}).code).toBe(code);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function arrangeRunningAgentStep() {
|
|
200
|
+
mocks.getStepByIdForJobExecution.mockResolvedValue({
|
|
201
|
+
currentAttempt: input.attempt,
|
|
202
|
+
status: 'running',
|
|
203
|
+
type: 'agent',
|
|
204
|
+
config: {},
|
|
205
|
+
});
|
|
206
|
+
mocks.getJobScope.mockResolvedValue({
|
|
207
|
+
workspaceId: '00000000-0000-4000-8000-000000000010',
|
|
208
|
+
projectId: '00000000-0000-4000-8000-000000000011',
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
it('returns the recorded session descriptor with the resolved scope', async () => {
|
|
213
|
+
const stepAttemptId = '00000000-0000-4000-8000-000000000012';
|
|
214
|
+
const sessionId = '00000000-0000-4000-8000-000000000013';
|
|
215
|
+
arrangeRunningAgentStep();
|
|
216
|
+
mocks.getStepAttemptDetail.mockResolvedValue({
|
|
217
|
+
workflowRunId: '00000000-0000-4000-8000-000000000014',
|
|
218
|
+
workflowRunAttemptId: '00000000-0000-4000-8000-000000000015',
|
|
219
|
+
step: {},
|
|
220
|
+
attempt: {
|
|
221
|
+
id: stepAttemptId,
|
|
222
|
+
config: {
|
|
223
|
+
session: {id: sessionId, key: 'main', mode: 'resume', segment: 3},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
const runners = {getLeaseState: vi.fn().mockResolvedValue({active: true})};
|
|
228
|
+
|
|
229
|
+
const result = await presentation(runners).handlers.getLeasedAgentSessionContext(input, {
|
|
230
|
+
signal: new AbortController().signal,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
expect(result).toEqual({
|
|
234
|
+
workspaceId: '00000000-0000-4000-8000-000000000010',
|
|
235
|
+
projectId: '00000000-0000-4000-8000-000000000011',
|
|
236
|
+
workflowRunAttemptId: '00000000-0000-4000-8000-000000000015',
|
|
237
|
+
stepAttemptId,
|
|
238
|
+
session: {id: sessionId, key: 'main', mode: 'resume', segment: 3},
|
|
239
|
+
});
|
|
240
|
+
expect(mocks.getStepAttemptDetail).toHaveBeenCalledWith({
|
|
241
|
+
stepId: input.stepId,
|
|
242
|
+
attempt: input.attempt,
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('returns a null session when the step has no recorded descriptor', async () => {
|
|
247
|
+
arrangeRunningAgentStep();
|
|
248
|
+
mocks.getStepAttemptDetail.mockResolvedValue({
|
|
249
|
+
workflowRunId: '00000000-0000-4000-8000-000000000014',
|
|
250
|
+
workflowRunAttemptId: '00000000-0000-4000-8000-000000000015',
|
|
251
|
+
step: {},
|
|
252
|
+
attempt: {id: '00000000-0000-4000-8000-000000000012', config: null},
|
|
253
|
+
});
|
|
254
|
+
const runners = {getLeaseState: vi.fn().mockResolvedValue({active: true})};
|
|
255
|
+
|
|
256
|
+
const result = await presentation(runners).handlers.getLeasedAgentSessionContext(input, {
|
|
257
|
+
signal: new AbortController().signal,
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
expect(result.session).toBeNull();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('fails fast when the lease is not active', async () => {
|
|
264
|
+
arrangeRunningAgentStep();
|
|
265
|
+
const runners = {getLeaseState: vi.fn().mockResolvedValue({active: false})};
|
|
266
|
+
|
|
267
|
+
await expectKnownError(
|
|
268
|
+
presentation(runners).handlers.getLeasedAgentSessionContext(input, {
|
|
269
|
+
signal: new AbortController().signal,
|
|
270
|
+
}),
|
|
271
|
+
'lease-not-active',
|
|
272
|
+
);
|
|
273
|
+
expect(mocks.getStepAttemptDetail).not.toHaveBeenCalled();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('rejects a malformed recorded descriptor', async () => {
|
|
277
|
+
arrangeRunningAgentStep();
|
|
278
|
+
mocks.getStepAttemptDetail.mockResolvedValue({
|
|
279
|
+
workflowRunId: '00000000-0000-4000-8000-000000000014',
|
|
280
|
+
workflowRunAttemptId: '00000000-0000-4000-8000-000000000015',
|
|
281
|
+
step: {},
|
|
282
|
+
attempt: {
|
|
283
|
+
id: '00000000-0000-4000-8000-000000000012',
|
|
284
|
+
config: {session: {id: 'not-a-uuid', key: 'main', mode: 'resume', segment: 1}},
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
const runners = {getLeaseState: vi.fn().mockResolvedValue({active: true})};
|
|
288
|
+
|
|
289
|
+
await expectKnownError(
|
|
290
|
+
presentation(runners).handlers.getLeasedAgentSessionContext(input, {
|
|
291
|
+
signal: new AbortController().signal,
|
|
292
|
+
}),
|
|
293
|
+
'step-session-config-invalid',
|
|
294
|
+
);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test.each([
|
|
298
|
+
['step-not-found', undefined],
|
|
299
|
+
['step-attempt-mismatch', {currentAttempt: 2, status: 'running', type: 'agent'}],
|
|
300
|
+
['step-not-running', {currentAttempt: 1, status: 'succeeded', type: 'agent'}],
|
|
301
|
+
['leased-step-not-agent', {currentAttempt: 1, status: 'running', type: 'run'}],
|
|
302
|
+
] as const)('maps a %s step to the published contract error', async (code, step) => {
|
|
303
|
+
const runners = {getLeaseState: vi.fn().mockResolvedValue({active: true})};
|
|
304
|
+
mocks.getStepByIdForJobExecution.mockResolvedValue(step);
|
|
305
|
+
if (step !== undefined) {
|
|
306
|
+
mocks.getJobScope.mockResolvedValue({
|
|
307
|
+
workspaceId: '00000000-0000-4000-8000-000000000010',
|
|
308
|
+
projectId: '00000000-0000-4000-8000-000000000011',
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
await expectKnownError(
|
|
313
|
+
presentation(runners).handlers.getLeasedAgentSessionContext(input, {
|
|
314
|
+
signal: new AbortController().signal,
|
|
315
|
+
}),
|
|
316
|
+
code,
|
|
317
|
+
);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('maps a missing job scope to job-not-found', async () => {
|
|
321
|
+
mocks.getStepByIdForJobExecution.mockResolvedValue({
|
|
322
|
+
currentAttempt: input.attempt,
|
|
323
|
+
status: 'running',
|
|
324
|
+
type: 'agent',
|
|
325
|
+
config: {},
|
|
326
|
+
});
|
|
327
|
+
mocks.getJobScope.mockResolvedValue(undefined);
|
|
328
|
+
const runners = {getLeaseState: vi.fn().mockResolvedValue({active: true})};
|
|
329
|
+
|
|
330
|
+
await expectKnownError(
|
|
331
|
+
presentation(runners).handlers.getLeasedAgentSessionContext(input, {
|
|
332
|
+
signal: new AbortController().signal,
|
|
333
|
+
}),
|
|
334
|
+
'job-not-found',
|
|
335
|
+
);
|
|
336
|
+
expect(mocks.getStepAttemptDetail).not.toHaveBeenCalled();
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('maps a missing step attempt detail to step-attempt-mismatch', async () => {
|
|
340
|
+
mocks.getStepByIdForJobExecution.mockResolvedValue({
|
|
341
|
+
currentAttempt: input.attempt,
|
|
342
|
+
status: 'running',
|
|
343
|
+
type: 'agent',
|
|
344
|
+
config: {},
|
|
345
|
+
});
|
|
346
|
+
mocks.getJobScope.mockResolvedValue({
|
|
347
|
+
workspaceId: '00000000-0000-4000-8000-000000000010',
|
|
348
|
+
projectId: '00000000-0000-4000-8000-000000000011',
|
|
349
|
+
});
|
|
350
|
+
mocks.getStepAttemptDetail.mockResolvedValue(undefined);
|
|
351
|
+
const runners = {getLeaseState: vi.fn().mockResolvedValue({active: true})};
|
|
352
|
+
|
|
353
|
+
await expectKnownError(
|
|
354
|
+
presentation(runners).handlers.getLeasedAgentSessionContext(input, {
|
|
355
|
+
signal: new AbortController().signal,
|
|
356
|
+
}),
|
|
357
|
+
'step-attempt-mismatch',
|
|
358
|
+
);
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
165
362
|
test.each([
|
|
166
363
|
['definition-not-found', () => new DefinitionNotFoundError(input.definitionId)],
|
|
167
364
|
['project-mismatch', () => new ProjectMismatchError(input.projectId, input.definitionId)],
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
agentSessionDescriptorSchema,
|
|
3
|
+
materializedAgentStepConfigSchema,
|
|
4
|
+
} from '@shipfox/api-agent-dto';
|
|
2
5
|
import type {AgentInterModuleClient} from '@shipfox/api-agent-dto/inter-module';
|
|
3
6
|
import type {DefinitionsInterModuleClient} from '@shipfox/api-definitions-dto/inter-module';
|
|
4
7
|
import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto/inter-module';
|
|
@@ -14,6 +17,7 @@ import {
|
|
|
14
17
|
type InterModulePresentation,
|
|
15
18
|
} from '@shipfox/inter-module';
|
|
16
19
|
import {DEFAULT_HARNESS, harnessSchema} from '@shipfox/workflow-document';
|
|
20
|
+
import type {Step} from '#core/entities/step.js';
|
|
17
21
|
import type {WorkflowRunTriggerReference} from '#core/entities/workflow-run.js';
|
|
18
22
|
import {
|
|
19
23
|
InvalidJobRunnerLabelsError,
|
|
@@ -34,6 +38,7 @@ import {resolveWorkflowRunTriggerReference} from '#core/resolve-trigger-referenc
|
|
|
34
38
|
import {assertWorkspaceAdmitsNewJobs} from '#core/workspace-admission.js';
|
|
35
39
|
import {
|
|
36
40
|
getJobScope,
|
|
41
|
+
getStepAttemptDetail,
|
|
37
42
|
getStepById,
|
|
38
43
|
getStepByIdForJobExecution,
|
|
39
44
|
listStepAttemptIdsByJobId,
|
|
@@ -53,6 +58,65 @@ export function createWorkflowsInterModulePresentation(params: {
|
|
|
53
58
|
integrations: IntegrationsModuleClient;
|
|
54
59
|
projects: ProjectsModuleClient;
|
|
55
60
|
}): InterModulePresentation<typeof workflowsInterModuleContract> {
|
|
61
|
+
/**
|
|
62
|
+
* Shared lease + running-agent-step resolution for the lease-authed
|
|
63
|
+
* inter-module methods (`getLeasedAgentToolContext` and
|
|
64
|
+
* `getLeasedAgentSessionContext`): verifies the runner lease, resolves the
|
|
65
|
+
* step for the job execution, then validates the job scope, attempt, status,
|
|
66
|
+
* and agent type in one canonical order. The two seams previously
|
|
67
|
+
* copy-pasted this chain with diverging check orders (the tool method
|
|
68
|
+
* resolved the job scope before the attempt/status checks, the session
|
|
69
|
+
* method after), so the same underlying state surfaced different errors
|
|
70
|
+
* depending on the method; a single helper keeps error codes and check
|
|
71
|
+
* ordering from drifting.
|
|
72
|
+
*
|
|
73
|
+
* `loadRunningLeasedStep` (routes) deliberately keeps its own copy: it
|
|
74
|
+
* returns the run scope (trigger reference/origin state), skips the
|
|
75
|
+
* agent-type check, and speaks HTTP `ClientError`s instead of contract
|
|
76
|
+
* known errors.
|
|
77
|
+
*/
|
|
78
|
+
async function resolveLeasedAgentStep(resolution: {
|
|
79
|
+
method:
|
|
80
|
+
| typeof workflowsInterModuleContract.methods.getLeasedAgentToolContext
|
|
81
|
+
| typeof workflowsInterModuleContract.methods.getLeasedAgentSessionContext;
|
|
82
|
+
input: {
|
|
83
|
+
jobId: string;
|
|
84
|
+
jobExecutionId: string;
|
|
85
|
+
runnerSessionId: string;
|
|
86
|
+
stepId: string;
|
|
87
|
+
attempt: number;
|
|
88
|
+
};
|
|
89
|
+
}): Promise<{
|
|
90
|
+
step: Step;
|
|
91
|
+
scope: NonNullable<Awaited<ReturnType<typeof getJobScope>>>;
|
|
92
|
+
}> {
|
|
93
|
+
const {active: leaseIsActive} = await params.runners.getLeaseState({
|
|
94
|
+
jobId: resolution.input.jobId,
|
|
95
|
+
jobExecutionId: resolution.input.jobExecutionId,
|
|
96
|
+
runnerSessionId: resolution.input.runnerSessionId,
|
|
97
|
+
});
|
|
98
|
+
if (!leaseIsActive)
|
|
99
|
+
throw createInterModuleKnownError(resolution.method, 'lease-not-active', {});
|
|
100
|
+
|
|
101
|
+
const step = await getStepByIdForJobExecution({
|
|
102
|
+
stepId: resolution.input.stepId,
|
|
103
|
+
jobExecutionId: resolution.input.jobExecutionId,
|
|
104
|
+
});
|
|
105
|
+
if (!step) throw createInterModuleKnownError(resolution.method, 'step-not-found', {});
|
|
106
|
+
|
|
107
|
+
const scope = await getJobScope(resolution.input.jobId);
|
|
108
|
+
if (!scope) throw createInterModuleKnownError(resolution.method, 'job-not-found', {});
|
|
109
|
+
if (step.currentAttempt !== resolution.input.attempt) {
|
|
110
|
+
throw createInterModuleKnownError(resolution.method, 'step-attempt-mismatch', {});
|
|
111
|
+
}
|
|
112
|
+
if (step.status !== 'running')
|
|
113
|
+
throw createInterModuleKnownError(resolution.method, 'step-not-running', {});
|
|
114
|
+
if (step.type !== 'agent')
|
|
115
|
+
throw createInterModuleKnownError(resolution.method, 'leased-step-not-agent', {});
|
|
116
|
+
|
|
117
|
+
return {step, scope};
|
|
118
|
+
}
|
|
119
|
+
|
|
56
120
|
return defineInterModulePresentation(workflowsInterModuleContract, {
|
|
57
121
|
startRunFromTrigger: async (input) => {
|
|
58
122
|
try {
|
|
@@ -161,28 +225,7 @@ export function createWorkflowsInterModulePresentation(params: {
|
|
|
161
225
|
},
|
|
162
226
|
getLeasedAgentToolContext: async (input) => {
|
|
163
227
|
const method = workflowsInterModuleContract.methods.getLeasedAgentToolContext;
|
|
164
|
-
const {
|
|
165
|
-
jobId: input.jobId,
|
|
166
|
-
jobExecutionId: input.jobExecutionId,
|
|
167
|
-
runnerSessionId: input.runnerSessionId,
|
|
168
|
-
});
|
|
169
|
-
if (!leaseIsActive) throw createInterModuleKnownError(method, 'lease-not-active', {});
|
|
170
|
-
|
|
171
|
-
const step = await getStepByIdForJobExecution({
|
|
172
|
-
stepId: input.stepId,
|
|
173
|
-
jobExecutionId: input.jobExecutionId,
|
|
174
|
-
});
|
|
175
|
-
if (!step) throw createInterModuleKnownError(method, 'step-not-found', {});
|
|
176
|
-
|
|
177
|
-
const scope = await getJobScope(input.jobId);
|
|
178
|
-
if (!scope) throw createInterModuleKnownError(method, 'job-not-found', {});
|
|
179
|
-
if (step.currentAttempt !== input.attempt) {
|
|
180
|
-
throw createInterModuleKnownError(method, 'step-attempt-mismatch', {});
|
|
181
|
-
}
|
|
182
|
-
if (step.status !== 'running')
|
|
183
|
-
throw createInterModuleKnownError(method, 'step-not-running', {});
|
|
184
|
-
if (step.type !== 'agent')
|
|
185
|
-
throw createInterModuleKnownError(method, 'leased-step-not-agent', {});
|
|
228
|
+
const {step, scope} = await resolveLeasedAgentStep({method, input});
|
|
186
229
|
|
|
187
230
|
const config = materializedAgentStepConfigSchema.safeParse(step.config);
|
|
188
231
|
if (!config.success) {
|
|
@@ -190,6 +233,37 @@ export function createWorkflowsInterModulePresentation(params: {
|
|
|
190
233
|
}
|
|
191
234
|
return {workspaceId: scope.workspaceId, integrations: config.data.integrations ?? []};
|
|
192
235
|
},
|
|
236
|
+
getLeasedAgentSessionContext: async (input) => {
|
|
237
|
+
const method = workflowsInterModuleContract.methods.getLeasedAgentSessionContext;
|
|
238
|
+
const {scope} = await resolveLeasedAgentStep({method, input});
|
|
239
|
+
|
|
240
|
+
const detail = await getStepAttemptDetail({stepId: input.stepId, attempt: input.attempt});
|
|
241
|
+
if (!detail) throw createInterModuleKnownError(method, 'step-attempt-mismatch', {});
|
|
242
|
+
|
|
243
|
+
// The dispatch integration records the resolved descriptor on the step
|
|
244
|
+
// attempt's config; anything else is a recording we cannot trust.
|
|
245
|
+
const recorded = detail.attempt.config?.session;
|
|
246
|
+
if (recorded === undefined || recorded === null) {
|
|
247
|
+
return {
|
|
248
|
+
workspaceId: scope.workspaceId,
|
|
249
|
+
projectId: scope.projectId,
|
|
250
|
+
workflowRunAttemptId: detail.workflowRunAttemptId,
|
|
251
|
+
stepAttemptId: detail.attempt.id,
|
|
252
|
+
session: null,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const parsed = agentSessionDescriptorSchema.safeParse(recorded);
|
|
256
|
+
if (!parsed.success) {
|
|
257
|
+
throw createInterModuleKnownError(method, 'step-session-config-invalid', {});
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
workspaceId: scope.workspaceId,
|
|
261
|
+
projectId: scope.projectId,
|
|
262
|
+
workflowRunAttemptId: detail.workflowRunAttemptId,
|
|
263
|
+
stepAttemptId: detail.attempt.id,
|
|
264
|
+
session: parsed.data,
|
|
265
|
+
};
|
|
266
|
+
},
|
|
193
267
|
});
|
|
194
268
|
}
|
|
195
269
|
|