diff --git a/packages/workflow-executor/src/http/pending-data-validators.ts b/packages/workflow-executor/src/http/pending-data-validators.ts index 4e2d6aabe1..ea00602e63 100644 --- a/packages/workflow-executor/src/http/pending-data-validators.ts +++ b/packages/workflow-executor/src/http/pending-data-validators.ts @@ -26,6 +26,12 @@ const triggerActionPatchSchema = z // Whether the native submit actually executed the action, or only created an approval request // (non-blocking): downstream AI steps must be told an awaiting-approval action did NOT run. submissionOutcome: z.enum(['executed', 'pending-approval']).optional(), + // The approval request the native submit filed, when submissionOutcome is 'pending-approval'. + // Persisted so the run panel can deep-link to it (parity with the Full-AI path). + approvalRequest: z + .object({ id: z.string().min(1) }) + .strict() + .optional(), }) .strict(); diff --git a/packages/workflow-executor/test/http/pending-data-validators.test.ts b/packages/workflow-executor/test/http/pending-data-validators.test.ts index 4e09b0c503..5ca7fa6eba 100644 --- a/packages/workflow-executor/test/http/pending-data-validators.test.ts +++ b/packages/workflow-executor/test/http/pending-data-validators.test.ts @@ -125,6 +125,30 @@ describe('patchBodySchemas', () => { expect(parsed).toEqual({ userConfirmed: false }); }); + it('accepts a pending-approval submit carrying the approval request id', () => { + const parsed = schema.parse({ + userConfirmed: true, + submissionOutcome: 'pending-approval', + approvalRequest: { id: 'appr-1' }, + }); + + expect(parsed).toEqual({ + userConfirmed: true, + submissionOutcome: 'pending-approval', + approvalRequest: { id: 'appr-1' }, + }); + }); + + it('rejects an approvalRequest with an empty id', () => { + expect(() => schema.parse({ userConfirmed: true, approvalRequest: { id: '' } })).toThrow(); + }); + + it('rejects extra keys inside approvalRequest (strict)', () => { + expect(() => + schema.parse({ userConfirmed: true, approvalRequest: { id: 'a', extra: 'leak' } }), + ).toThrow(); + }); + it('rejects unknown fields (strict schema)', () => { expect(() => schema.parse({ userConfirmed: true, actionResult: {}, extra: 'leak' }),