@shipfox/api-agent-access-dto 21.0.0 → 21.2.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 (49) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-type.log +0 -1
  3. package/CHANGELOG.md +19 -0
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +4 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/schemas/diagnostic-tools.d.ts +3 -1
  9. package/dist/schemas/diagnostic-tools.d.ts.map +1 -1
  10. package/dist/schemas/diagnostic-tools.js +7 -8
  11. package/dist/schemas/diagnostic-tools.js.map +1 -1
  12. package/dist/schemas/log-tools.d.ts +215 -0
  13. package/dist/schemas/log-tools.d.ts.map +1 -0
  14. package/dist/schemas/log-tools.js +224 -0
  15. package/dist/schemas/log-tools.js.map +1 -0
  16. package/dist/schemas/paged-tools.d.ts.map +1 -1
  17. package/dist/schemas/paged-tools.js +1 -6
  18. package/dist/schemas/paged-tools.js.map +1 -1
  19. package/dist/schemas/primitives.d.ts +5 -0
  20. package/dist/schemas/primitives.d.ts.map +1 -0
  21. package/dist/schemas/primitives.js +9 -0
  22. package/dist/schemas/primitives.js.map +1 -0
  23. package/dist/schemas/workflow-diagnostics.d.ts +862 -0
  24. package/dist/schemas/workflow-diagnostics.d.ts.map +1 -0
  25. package/dist/schemas/workflow-diagnostics.js +876 -0
  26. package/dist/schemas/workflow-diagnostics.js.map +1 -0
  27. package/dist/schemas/workflow-execution-events.d.ts +272 -0
  28. package/dist/schemas/workflow-execution-events.d.ts.map +1 -0
  29. package/dist/schemas/workflow-execution-events.js +250 -0
  30. package/dist/schemas/workflow-execution-events.js.map +1 -0
  31. package/dist/schemas/workflow-tools.d.ts +1305 -0
  32. package/dist/schemas/workflow-tools.d.ts.map +1 -0
  33. package/dist/schemas/workflow-tools.js +938 -0
  34. package/dist/schemas/workflow-tools.js.map +1 -0
  35. package/dist/tsconfig.test.tsbuildinfo +1 -1
  36. package/package.json +3 -2
  37. package/src/index.ts +113 -0
  38. package/src/schemas/diagnostic-tools.test.ts +43 -0
  39. package/src/schemas/diagnostic-tools.ts +12 -14
  40. package/src/schemas/log-tools.ts +209 -0
  41. package/src/schemas/paged-tools.ts +1 -10
  42. package/src/schemas/primitives.ts +14 -0
  43. package/src/schemas/workflow-diagnostics.test.ts +277 -0
  44. package/src/schemas/workflow-diagnostics.ts +693 -0
  45. package/src/schemas/workflow-execution-events.test.ts +126 -0
  46. package/src/schemas/workflow-execution-events.ts +217 -0
  47. package/src/schemas/workflow-tools.test.ts +202 -0
  48. package/src/schemas/workflow-tools.ts +738 -0
  49. package/tsconfig.build.tsbuildinfo +1 -1
@@ -0,0 +1,126 @@
1
+ import {
2
+ AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PAGE_LIMIT,
3
+ AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PAGE_MAX,
4
+ AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PREVIEW_MAX_BYTES,
5
+ getExecutionTriggerEventInputJsonSchema,
6
+ getExecutionTriggerEventInputSchema,
7
+ getExecutionTriggerEventResultJsonSchema,
8
+ getExecutionTriggerEventResultSchema,
9
+ listExecutionTriggerEventsInputSchema,
10
+ listExecutionTriggerEventsResultJsonSchema,
11
+ listExecutionTriggerEventsResultSchema,
12
+ } from './workflow-execution-events.js';
13
+
14
+ const jobId = '00000000-0000-4000-8000-000000000001';
15
+ const executionId = '00000000-0000-4000-8000-000000000002';
16
+ const eventRef = '00000000-0000-4000-8000-000000000003';
17
+ const deliveryId = '00000000-0000-4000-8000-000000000004';
18
+ const receivedAt = '2026-08-31T12:00:00.000Z';
19
+
20
+ describe('workflow execution event Agent Access schemas', () => {
21
+ test('accepts bounded summaries and serialized JSON detail', () => {
22
+ expect(listExecutionTriggerEventsResultSchema.safeParse(listResult()).success).toBe(true);
23
+ expect(
24
+ getExecutionTriggerEventResultSchema.safeParse({
25
+ ...summary(),
26
+ payload_preview: '{"action":"opened"}',
27
+ }).success,
28
+ ).toBe(true);
29
+ });
30
+
31
+ test('requires non-empty event references and preserves long lookup keys', () => {
32
+ const longEventRef = `${eventRef}${'x'.repeat(512)}`;
33
+
34
+ expect(
35
+ getExecutionTriggerEventInputSchema.safeParse({
36
+ job_id: jobId,
37
+ execution_id: executionId,
38
+ event_ref: longEventRef,
39
+ }).success,
40
+ ).toBe(true);
41
+ expect(
42
+ getExecutionTriggerEventInputSchema.safeParse({
43
+ job_id: jobId,
44
+ execution_id: executionId,
45
+ event_ref: '',
46
+ }).success,
47
+ ).toBe(false);
48
+ expect(getExecutionTriggerEventInputJsonSchema.properties.event_ref).toMatchObject({
49
+ minLength: 1,
50
+ });
51
+ expect(
52
+ getExecutionTriggerEventResultSchema.safeParse({
53
+ ...summary(),
54
+ event_ref: longEventRef,
55
+ payload_preview: null,
56
+ }).success,
57
+ ).toBe(true);
58
+ });
59
+
60
+ test('uses the producer page default and rejects workspace-level input', () => {
61
+ expect(
62
+ listExecutionTriggerEventsInputSchema.parse({job_id: jobId, execution_id: executionId}),
63
+ ).toMatchObject({limit: AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PAGE_LIMIT});
64
+ expect(
65
+ listExecutionTriggerEventsInputSchema.safeParse({
66
+ job_id: jobId,
67
+ execution_id: executionId,
68
+ workspace_id: '00000000-0000-4000-8000-000000000005',
69
+ }).success,
70
+ ).toBe(false);
71
+ });
72
+
73
+ test('mirrors page, metadata, and preview bounds in JSON schemas', () => {
74
+ expect(listExecutionTriggerEventsResultJsonSchema.properties.trigger_events).toMatchObject({
75
+ type: 'array',
76
+ maxItems: AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PAGE_MAX,
77
+ });
78
+ expect(getExecutionTriggerEventResultJsonSchema.properties.payload_preview).toMatchObject({
79
+ anyOf: [
80
+ {
81
+ type: 'string',
82
+ maxLength: AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PREVIEW_MAX_BYTES,
83
+ contentMediaType: 'application/json',
84
+ },
85
+ {type: 'null'},
86
+ ],
87
+ });
88
+ });
89
+
90
+ test('rejects clipped or invalid payload previews', () => {
91
+ const invalidPreviews = [
92
+ 'not-json',
93
+ `{"body":"${'x'.repeat(AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PREVIEW_MAX_BYTES)}"}`,
94
+ ];
95
+ for (const payload_preview of invalidPreviews) {
96
+ expect(
97
+ getExecutionTriggerEventResultSchema.safeParse({...summary(), payload_preview}).success,
98
+ ).toBe(false);
99
+ }
100
+ });
101
+ });
102
+
103
+ function summary() {
104
+ return {
105
+ event_ref: eventRef,
106
+ delivery_id: deliveryId,
107
+ source: 'github',
108
+ event: 'push',
109
+ disposition: 'fire' as const,
110
+ outcome: 'consumed' as const,
111
+ outcome_reason: null,
112
+ received_at: receivedAt,
113
+ stored_payload_bytes: 32,
114
+ normalized_event_bytes: 128,
115
+ };
116
+ }
117
+
118
+ function listResult() {
119
+ return {
120
+ job_id: jobId,
121
+ execution_id: executionId,
122
+ trigger_events: [summary()],
123
+ next_cursor: null,
124
+ total: 1,
125
+ };
126
+ }
@@ -0,0 +1,217 @@
1
+ import {z} from 'zod';
2
+ import type {AgentAccessObjectSchema} from './envelope.js';
3
+ import {AGENT_ACCESS_PAGE_LIMIT_MAX, AGENT_ACCESS_TEXT_MAX_BYTES} from './paged-tools.js';
4
+ import {dateTimeSchema, idSchema, utf8CappedString} from './primitives.js';
5
+
6
+ /** Default page size for one execution's listener-event history. */
7
+ export const AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PAGE_LIMIT = 25;
8
+
9
+ /** Maximum number of execution listener-event summaries in one page. */
10
+ export const AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PAGE_MAX = AGENT_ACCESS_PAGE_LIMIT_MAX;
11
+
12
+ /** Maximum serialized UTF-8 size of one untrusted payload preview. */
13
+ export const AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PREVIEW_MAX_BYTES = 16 * 1024;
14
+
15
+ const textSchema = utf8CappedString(AGENT_ACCESS_TEXT_MAX_BYTES);
16
+ const payloadPreviewSchema = utf8CappedString(
17
+ AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PREVIEW_MAX_BYTES,
18
+ ).refine(isSerializedJson, {message: 'Payload preview must be serialized JSON'});
19
+
20
+ const executionTriggerEventDispositionSchema = z.enum(['fire', 'resolve']);
21
+ const executionTriggerEventOutcomeSchema = z.enum([
22
+ 'pending',
23
+ 'consumed',
24
+ 'honored',
25
+ 'rejected',
26
+ 'abandoned',
27
+ ]);
28
+ const executionTriggerEventOutcomeReasonSchema = z
29
+ .enum(['payload_too_large', 'until', 'timeout', 'max_executions', 'cancelled'])
30
+ .nullable();
31
+ const eventRefSchema = z.string().min(1);
32
+
33
+ const executionTriggerEventSummarySchema = z
34
+ .object({
35
+ event_ref: eventRefSchema,
36
+ delivery_id: textSchema,
37
+ source: textSchema,
38
+ event: textSchema,
39
+ disposition: executionTriggerEventDispositionSchema,
40
+ outcome: executionTriggerEventOutcomeSchema,
41
+ outcome_reason: executionTriggerEventOutcomeReasonSchema,
42
+ received_at: dateTimeSchema,
43
+ stored_payload_bytes: z.number().int().nonnegative(),
44
+ normalized_event_bytes: z.number().int().nonnegative(),
45
+ })
46
+ .strict();
47
+
48
+ export const listExecutionTriggerEventsInputSchema = z
49
+ .object({
50
+ job_id: idSchema,
51
+ execution_id: idSchema,
52
+ limit: z
53
+ .number()
54
+ .int()
55
+ .min(1)
56
+ .max(AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PAGE_MAX)
57
+ .default(AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PAGE_LIMIT),
58
+ cursor: z.string().min(1).optional(),
59
+ })
60
+ .strict();
61
+
62
+ export const getExecutionTriggerEventInputSchema = z
63
+ .object({
64
+ job_id: idSchema,
65
+ execution_id: idSchema,
66
+ event_ref: eventRefSchema,
67
+ })
68
+ .strict();
69
+
70
+ export type ListExecutionTriggerEventsInputDto = z.output<
71
+ typeof listExecutionTriggerEventsInputSchema
72
+ >;
73
+ export type GetExecutionTriggerEventInputDto = z.output<typeof getExecutionTriggerEventInputSchema>;
74
+
75
+ export const listExecutionTriggerEventsResultSchema = z
76
+ .object({
77
+ job_id: idSchema,
78
+ execution_id: idSchema,
79
+ trigger_events: z
80
+ .array(executionTriggerEventSummarySchema)
81
+ .max(AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PAGE_MAX),
82
+ next_cursor: z.string().nullable(),
83
+ total: z.number().int().nonnegative().optional(),
84
+ })
85
+ .strict();
86
+
87
+ export type ListExecutionTriggerEventsResultDto = z.infer<
88
+ typeof listExecutionTriggerEventsResultSchema
89
+ >;
90
+
91
+ export const getExecutionTriggerEventResultSchema = executionTriggerEventSummarySchema
92
+ .extend({
93
+ /** Serialized JSON text. It is untrusted data, not a typed workflow value. */
94
+ payload_preview: payloadPreviewSchema.nullable(),
95
+ payload_preview_truncated: z.literal(true).optional(),
96
+ payload_preview_total_bytes: z.number().int().nonnegative().optional(),
97
+ })
98
+ .strict();
99
+
100
+ export type GetExecutionTriggerEventResultDto = z.infer<
101
+ typeof getExecutionTriggerEventResultSchema
102
+ >;
103
+
104
+ const uuid = {type: 'string', format: 'uuid'} as const;
105
+ const dateTime = {type: 'string', format: 'date-time'} as const;
106
+ const text = {type: 'string', maxLength: AGENT_ACCESS_TEXT_MAX_BYTES} as const;
107
+ const eventRef = {type: 'string', minLength: 1} as const;
108
+ const serializedJson = {
109
+ type: 'string',
110
+ maxLength: AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PREVIEW_MAX_BYTES,
111
+ contentMediaType: 'application/json',
112
+ } as const;
113
+ const nullable = (schema: Record<string, unknown>) => ({anyOf: [schema, {type: 'null'}]}) as const;
114
+
115
+ export const listExecutionTriggerEventsInputJsonSchema = {
116
+ type: 'object',
117
+ properties: {
118
+ job_id: uuid,
119
+ execution_id: uuid,
120
+ limit: {
121
+ type: 'integer',
122
+ minimum: 1,
123
+ maximum: AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PAGE_MAX,
124
+ default: AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PAGE_LIMIT,
125
+ },
126
+ cursor: {type: 'string', minLength: 1},
127
+ },
128
+ required: ['job_id', 'execution_id'],
129
+ additionalProperties: false,
130
+ } as const satisfies AgentAccessObjectSchema;
131
+
132
+ export const getExecutionTriggerEventInputJsonSchema = {
133
+ type: 'object',
134
+ properties: {job_id: uuid, execution_id: uuid, event_ref: eventRef},
135
+ required: ['job_id', 'execution_id', 'event_ref'],
136
+ additionalProperties: false,
137
+ } as const satisfies AgentAccessObjectSchema;
138
+
139
+ const executionTriggerEventSummaryJson = {
140
+ type: 'object',
141
+ properties: {
142
+ event_ref: eventRef,
143
+ delivery_id: text,
144
+ source: text,
145
+ event: text,
146
+ disposition: {type: 'string', enum: ['fire', 'resolve']},
147
+ outcome: {
148
+ type: 'string',
149
+ enum: ['pending', 'consumed', 'honored', 'rejected', 'abandoned'],
150
+ },
151
+ outcome_reason: {
152
+ anyOf: [
153
+ {
154
+ type: 'string',
155
+ enum: ['payload_too_large', 'until', 'timeout', 'max_executions', 'cancelled'],
156
+ },
157
+ {type: 'null'},
158
+ ],
159
+ },
160
+ received_at: dateTime,
161
+ stored_payload_bytes: {type: 'integer', minimum: 0},
162
+ normalized_event_bytes: {type: 'integer', minimum: 0},
163
+ },
164
+ required: [
165
+ 'event_ref',
166
+ 'delivery_id',
167
+ 'source',
168
+ 'event',
169
+ 'disposition',
170
+ 'outcome',
171
+ 'outcome_reason',
172
+ 'received_at',
173
+ 'stored_payload_bytes',
174
+ 'normalized_event_bytes',
175
+ ],
176
+ additionalProperties: false,
177
+ } as const;
178
+
179
+ const executionTriggerEventDetailJson = {
180
+ ...executionTriggerEventSummaryJson,
181
+ properties: {
182
+ ...executionTriggerEventSummaryJson.properties,
183
+ payload_preview: nullable(serializedJson),
184
+ payload_preview_truncated: {const: true},
185
+ payload_preview_total_bytes: {type: 'integer', minimum: 0},
186
+ },
187
+ required: [...executionTriggerEventSummaryJson.required, 'payload_preview'],
188
+ } as const;
189
+
190
+ export const listExecutionTriggerEventsResultJsonSchema = {
191
+ type: 'object',
192
+ properties: {
193
+ job_id: uuid,
194
+ execution_id: uuid,
195
+ trigger_events: {
196
+ type: 'array',
197
+ maxItems: AGENT_ACCESS_EXECUTION_TRIGGER_EVENT_PAGE_MAX,
198
+ items: executionTriggerEventSummaryJson,
199
+ },
200
+ next_cursor: nullable({type: 'string', minLength: 1}),
201
+ total: {type: 'integer', minimum: 0},
202
+ },
203
+ required: ['job_id', 'execution_id', 'trigger_events', 'next_cursor'],
204
+ additionalProperties: false,
205
+ } as const satisfies AgentAccessObjectSchema;
206
+
207
+ export const getExecutionTriggerEventResultJsonSchema =
208
+ executionTriggerEventDetailJson satisfies AgentAccessObjectSchema;
209
+
210
+ function isSerializedJson(value: string): boolean {
211
+ try {
212
+ JSON.parse(value);
213
+ return true;
214
+ } catch {
215
+ return false;
216
+ }
217
+ }
@@ -0,0 +1,202 @@
1
+ import {
2
+ AGENT_ACCESS_WORKFLOW_EXECUTION_PAGE_LIMIT,
3
+ AGENT_ACCESS_WORKFLOW_JOB_PAGE_LIMIT,
4
+ AGENT_ACCESS_WORKFLOW_RUN_ATTEMPT_PAGE_LIMIT,
5
+ AGENT_ACCESS_WORKFLOW_STEP_ATTEMPT_PAGE_LIMIT,
6
+ AGENT_ACCESS_WORKFLOW_STEP_PAGE_LIMIT,
7
+ getWorkflowJobInputSchema,
8
+ getWorkflowJobResultJsonSchema,
9
+ getWorkflowJobResultSchema,
10
+ getWorkflowRunInputSchema,
11
+ getWorkflowRunResultJsonSchema,
12
+ getWorkflowRunResultSchema,
13
+ listWorkflowExecutionStepsInputSchema,
14
+ listWorkflowExecutionStepsResultJsonSchema,
15
+ listWorkflowExecutionStepsResultSchema,
16
+ listWorkflowJobExecutionsInputSchema,
17
+ listWorkflowJobExecutionsResultJsonSchema,
18
+ listWorkflowRunAttemptsInputSchema,
19
+ listWorkflowRunAttemptsResultJsonSchema,
20
+ listWorkflowRunJobsInputSchema,
21
+ listWorkflowRunJobsResultJsonSchema,
22
+ listWorkflowStepAttemptsInputSchema,
23
+ listWorkflowStepAttemptsResultJsonSchema,
24
+ } from './workflow-tools.js';
25
+
26
+ const runId = '00000000-0000-4000-8000-000000000001';
27
+ const jobId = '00000000-0000-4000-8000-000000000002';
28
+ const executionId = '00000000-0000-4000-8000-000000000003';
29
+ const stepId = '00000000-0000-4000-8000-000000000004';
30
+ const isoDate = '2026-08-01T00:00:00.000Z';
31
+
32
+ describe('workflow agent-access schemas', () => {
33
+ test('uses producer page defaults and keeps workspace implicit', () => {
34
+ expect(listWorkflowRunAttemptsInputSchema.parse({run_id: runId}).limit).toBe(
35
+ AGENT_ACCESS_WORKFLOW_RUN_ATTEMPT_PAGE_LIMIT,
36
+ );
37
+ expect(listWorkflowRunJobsInputSchema.parse({run_id: runId, attempt: 2}).limit).toBe(
38
+ AGENT_ACCESS_WORKFLOW_JOB_PAGE_LIMIT,
39
+ );
40
+ expect(listWorkflowJobExecutionsInputSchema.parse({job_id: jobId}).limit).toBe(
41
+ AGENT_ACCESS_WORKFLOW_EXECUTION_PAGE_LIMIT,
42
+ );
43
+ expect(
44
+ listWorkflowExecutionStepsInputSchema.parse({job_id: jobId, execution_id: executionId}).limit,
45
+ ).toBe(AGENT_ACCESS_WORKFLOW_STEP_PAGE_LIMIT);
46
+ expect(listWorkflowStepAttemptsInputSchema.parse({step_id: stepId}).limit).toBe(
47
+ AGENT_ACCESS_WORKFLOW_STEP_ATTEMPT_PAGE_LIMIT,
48
+ );
49
+ expect(getWorkflowRunInputSchema.safeParse({run_id: runId, workspace_id: runId}).success).toBe(
50
+ false,
51
+ );
52
+ expect(getWorkflowJobInputSchema.safeParse({job_id: jobId, extra: true}).success).toBe(false);
53
+ });
54
+
55
+ test('requires the run attempt when listing jobs', () => {
56
+ expect(listWorkflowRunJobsInputSchema.safeParse({run_id: runId}).success).toBe(false);
57
+ expect(listWorkflowRunJobsInputSchema.safeParse({run_id: runId, attempt: 0}).success).toBe(
58
+ false,
59
+ );
60
+ });
61
+
62
+ test('declares one object result schema without embedded traversal children', () => {
63
+ const schemas = [
64
+ getWorkflowRunResultJsonSchema,
65
+ listWorkflowRunAttemptsResultJsonSchema,
66
+ listWorkflowRunJobsResultJsonSchema,
67
+ getWorkflowJobResultJsonSchema,
68
+ listWorkflowJobExecutionsResultJsonSchema,
69
+ listWorkflowExecutionStepsResultJsonSchema,
70
+ listWorkflowStepAttemptsResultJsonSchema,
71
+ ];
72
+ for (const schema of schemas) expect(schema).not.toHaveProperty('oneOf');
73
+
74
+ expect(getWorkflowRunResultJsonSchema.properties).not.toHaveProperty('jobs');
75
+ expect(getWorkflowJobResultJsonSchema.properties).toHaveProperty('selected_execution');
76
+ expect(listWorkflowExecutionStepsResultJsonSchema.properties.steps).toBeDefined();
77
+ expect(listWorkflowStepAttemptsResultJsonSchema.properties.attempts).toBeDefined();
78
+ });
79
+
80
+ test('rejects diagnostic and child collection fields from traversal results', () => {
81
+ const validRun = runResult();
82
+ expect(getWorkflowRunResultSchema.safeParse(validRun).success).toBe(true);
83
+ expect(getWorkflowRunResultSchema.safeParse({...validRun, jobs: []}).success).toBe(false);
84
+
85
+ const validJob = jobResult();
86
+ expect(getWorkflowJobResultSchema.safeParse(validJob).success).toBe(true);
87
+ expect(
88
+ getWorkflowJobResultSchema.safeParse({
89
+ ...validJob,
90
+ job: {...validJob.job, dependencies: []},
91
+ }).success,
92
+ ).toBe(false);
93
+
94
+ const validSteps = {
95
+ job_id: jobId,
96
+ execution_id: executionId,
97
+ steps: [stepResult()],
98
+ next_cursor: null,
99
+ };
100
+ expect(listWorkflowExecutionStepsResultSchema.safeParse(validSteps).success).toBe(true);
101
+ expect(
102
+ listWorkflowExecutionStepsResultSchema.safeParse({
103
+ ...validSteps,
104
+ steps: [{...stepResult(), attempts: {items: [], next_cursor: null}}],
105
+ }).success,
106
+ ).toBe(false);
107
+ });
108
+
109
+ test('enforces text caps by UTF-8 bytes', () => {
110
+ const validRun = runResult();
111
+ const underByteCap = {...validRun, name: `${'🙂'.repeat(127)}a`};
112
+ const overByteCap = {...validRun, name: '🙂'.repeat(129)};
113
+
114
+ expect(getWorkflowRunResultSchema.safeParse(underByteCap).success).toBe(true);
115
+ expect(getWorkflowRunResultSchema.safeParse(overByteCap).success).toBe(false);
116
+ });
117
+ });
118
+
119
+ function runResult() {
120
+ return {
121
+ id: runId,
122
+ project_id: runId,
123
+ definition_id: jobId,
124
+ number: 1,
125
+ name: 'Run',
126
+ workflow_name: 'Workflow',
127
+ status: 'failed' as const,
128
+ origin: 'synced' as const,
129
+ dev_source: null,
130
+ trigger_provider: 'github',
131
+ trigger_source: 'push',
132
+ trigger_event: 'push',
133
+ trigger_reference: {repository: 'shipfox/platform', ref: 'main', commit: 'abc', actor: 'noe'},
134
+ created_at: isoDate,
135
+ started_at: isoDate,
136
+ finished_at: isoDate,
137
+ attempt: {
138
+ id: executionId,
139
+ workflow_run_id: runId,
140
+ attempt: 1,
141
+ status: 'failed' as const,
142
+ created_at: isoDate,
143
+ started_at: isoDate,
144
+ finished_at: isoDate,
145
+ rerun_mode: null,
146
+ },
147
+ job_status_counts: [{status: 'failed' as const, count: 1}],
148
+ has_started_job_execution: true,
149
+ };
150
+ }
151
+
152
+ function jobResult() {
153
+ return {
154
+ workflow_run_id: runId,
155
+ workflow_run_attempt: 1,
156
+ job: {
157
+ id: jobId,
158
+ key: 'build',
159
+ name: 'Build',
160
+ position: 0,
161
+ status: 'failed' as const,
162
+ status_reason: 'step_failed' as const,
163
+ mode: 'one_shot' as const,
164
+ listener_status: 'inactive' as const,
165
+ carried_over: false,
166
+ execution_count: 1,
167
+ execution_status_counts: {pending: 0, running: 0, succeeded: 0, failed: 1, cancelled: 0},
168
+ default_execution: executionResult(),
169
+ },
170
+ selected_execution: {...executionResult(), has_context: true},
171
+ };
172
+ }
173
+
174
+ function executionResult() {
175
+ return {
176
+ id: executionId,
177
+ sequence: 1,
178
+ name: 'Build execution',
179
+ status: 'failed' as const,
180
+ display_status: 'failed' as const,
181
+ status_reason: 'step_failed' as const,
182
+ status_reason_message: 'The step failed',
183
+ queued_at: isoDate,
184
+ started_at: isoDate,
185
+ finished_at: isoDate,
186
+ timed_out_at: null,
187
+ updated_at: isoDate,
188
+ };
189
+ }
190
+
191
+ function stepResult() {
192
+ return {
193
+ id: stepId,
194
+ key: 'run',
195
+ name: 'Run command',
196
+ type: 'run' as const,
197
+ position: 0,
198
+ status: 'failed' as const,
199
+ status_reason: null,
200
+ current_attempt: 1,
201
+ };
202
+ }