@output.ai/cli 0.5.6 → 0.7.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/README.md +1 -0
- package/dist/api/generated/api.d.ts +25 -0
- package/dist/api/generated/api.js +11 -0
- package/dist/commands/agents/init.d.ts +0 -1
- package/dist/commands/agents/init.js +9 -16
- package/dist/commands/agents/init.spec.js +49 -224
- package/dist/commands/workflow/generate.js +2 -2
- package/dist/commands/workflow/plan.js +3 -3
- package/dist/commands/workflow/plan.spec.js +13 -13
- package/dist/commands/workflow/terminate.d.ts +13 -0
- package/dist/commands/workflow/terminate.js +39 -0
- package/dist/services/claude_client.d.ts +4 -4
- package/dist/services/claude_client.integration.test.js +2 -2
- package/dist/services/claude_client.js +7 -7
- package/dist/services/claude_client.spec.js +3 -3
- package/dist/services/coding_agents.d.ts +10 -24
- package/dist/services/coding_agents.js +112 -368
- package/dist/services/coding_agents.spec.js +101 -290
- package/dist/services/project_scaffold.js +3 -3
- package/dist/services/workflow_builder.d.ts +1 -1
- package/dist/services/workflow_builder.js +1 -1
- package/dist/services/workflow_planner.js +5 -3
- package/dist/services/workflow_planner.spec.js +4 -5
- package/dist/templates/agent_instructions/dotclaude/settings.json.template +29 -0
- package/dist/templates/agent_instructions/{AGENTS.md.template → dotoutputai/AGENTS.md.template} +12 -10
- package/dist/utils/claude.d.ts +5 -0
- package/dist/utils/claude.js +19 -0
- package/dist/utils/claude.spec.d.ts +1 -0
- package/dist/utils/claude.spec.js +119 -0
- package/dist/utils/paths.d.ts +0 -4
- package/dist/utils/paths.js +0 -6
- package/package.json +3 -3
- package/dist/templates/agent_instructions/agents/workflow_context_fetcher.md.template +0 -82
- package/dist/templates/agent_instructions/agents/workflow_debugger.md.template +0 -98
- package/dist/templates/agent_instructions/agents/workflow_planner.md.template +0 -113
- package/dist/templates/agent_instructions/agents/workflow_prompt_writer.md.template +0 -595
- package/dist/templates/agent_instructions/agents/workflow_quality.md.template +0 -244
- package/dist/templates/agent_instructions/commands/build_workflow.md.template +0 -290
- package/dist/templates/agent_instructions/commands/debug_workflow.md.template +0 -198
- package/dist/templates/agent_instructions/commands/plan_workflow.md.template +0 -261
- package/dist/templates/agent_instructions/meta/post_flight.md.template +0 -94
- package/dist/templates/agent_instructions/meta/pre_flight.md.template +0 -60
- package/dist/templates/agent_instructions/skills/output-error-direct-io/SKILL.md.template +0 -249
- package/dist/templates/agent_instructions/skills/output-error-http-client/SKILL.md.template +0 -298
- package/dist/templates/agent_instructions/skills/output-error-missing-schemas/SKILL.md.template +0 -265
- package/dist/templates/agent_instructions/skills/output-error-nondeterminism/SKILL.md.template +0 -252
- package/dist/templates/agent_instructions/skills/output-error-try-catch/SKILL.md.template +0 -226
- package/dist/templates/agent_instructions/skills/output-error-zod-import/SKILL.md.template +0 -209
- package/dist/templates/agent_instructions/skills/output-services-check/SKILL.md.template +0 -128
- package/dist/templates/agent_instructions/skills/output-workflow-list/SKILL.md.template +0 -117
- package/dist/templates/agent_instructions/skills/output-workflow-result/SKILL.md.template +0 -199
- package/dist/templates/agent_instructions/skills/output-workflow-run/SKILL.md.template +0 -228
- package/dist/templates/agent_instructions/skills/output-workflow-runs-list/SKILL.md.template +0 -141
- package/dist/templates/agent_instructions/skills/output-workflow-start/SKILL.md.template +0 -201
- package/dist/templates/agent_instructions/skills/output-workflow-status/SKILL.md.template +0 -151
- package/dist/templates/agent_instructions/skills/output-workflow-stop/SKILL.md.template +0 -164
- package/dist/templates/agent_instructions/skills/output-workflow-trace/SKILL.md.template +0 -134
package/dist/templates/agent_instructions/skills/output-error-missing-schemas/SKILL.md.template
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: output-error-missing-schemas
|
|
3
|
-
description: Fix missing schema definitions in Output SDK steps. Use when seeing type errors, undefined properties at step boundaries, validation failures, or when step inputs/outputs aren't being properly typed.
|
|
4
|
-
allowed-tools: [Bash, Read]
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Fix Missing Schema Definitions
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
|
|
11
|
-
This skill helps diagnose and fix issues caused by steps that lack explicit `inputSchema` or `outputSchema` definitions. Schemas are essential for type safety, validation, and proper data serialization between steps.
|
|
12
|
-
|
|
13
|
-
## When to Use This Skill
|
|
14
|
-
|
|
15
|
-
You're seeing:
|
|
16
|
-
- Type errors at step boundaries
|
|
17
|
-
- Undefined properties in step inputs/outputs
|
|
18
|
-
- Validation failures when passing data between steps
|
|
19
|
-
- TypeScript errors about incompatible types
|
|
20
|
-
- Runtime errors about unexpected data shapes
|
|
21
|
-
|
|
22
|
-
## Root Cause
|
|
23
|
-
|
|
24
|
-
Steps without explicit schemas:
|
|
25
|
-
- Don't validate input data at runtime
|
|
26
|
-
- Don't provide TypeScript type inference
|
|
27
|
-
- May serialize/deserialize data incorrectly
|
|
28
|
-
- Can pass undefined or malformed data silently
|
|
29
|
-
|
|
30
|
-
## Symptoms
|
|
31
|
-
|
|
32
|
-
### Missing Input Schema
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
// WRONG: No input validation
|
|
36
|
-
export const processData = step({
|
|
37
|
-
name: 'processData',
|
|
38
|
-
// inputSchema: missing!
|
|
39
|
-
outputSchema: z.object({ result: z.string() }),
|
|
40
|
-
fn: async (input) => {
|
|
41
|
-
return { result: input.value }; // input.value might be undefined!
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### Missing Output Schema
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
// WRONG: No output validation
|
|
50
|
-
export const fetchData = step({
|
|
51
|
-
name: 'fetchData',
|
|
52
|
-
inputSchema: z.object({ id: z.string() }),
|
|
53
|
-
// outputSchema: missing!
|
|
54
|
-
fn: async (input) => {
|
|
55
|
-
return { data: await getFromApi(input.id) }; // Output shape not validated
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Both Schemas Missing
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
// WRONG: No validation at all
|
|
64
|
-
export const transformData = step({
|
|
65
|
-
name: 'transformData',
|
|
66
|
-
// No schemas!
|
|
67
|
-
fn: async (input) => {
|
|
68
|
-
return transform(input);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Solution
|
|
74
|
-
|
|
75
|
-
Always define both `inputSchema` and `outputSchema` for every step:
|
|
76
|
-
|
|
77
|
-
### Complete Step Definition
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
import { z, step } from '@output.ai/core';
|
|
81
|
-
|
|
82
|
-
export const processData = step({
|
|
83
|
-
name: 'processData',
|
|
84
|
-
inputSchema: z.object({
|
|
85
|
-
id: z.string(),
|
|
86
|
-
value: z.number(),
|
|
87
|
-
optional: z.string().optional(),
|
|
88
|
-
}),
|
|
89
|
-
outputSchema: z.object({
|
|
90
|
-
result: z.string(),
|
|
91
|
-
processedAt: z.number(),
|
|
92
|
-
}),
|
|
93
|
-
fn: async (input) => {
|
|
94
|
-
// input is fully typed: { id: string, value: number, optional?: string }
|
|
95
|
-
return {
|
|
96
|
-
result: `Processed ${input.id}`,
|
|
97
|
-
processedAt: Date.now(),
|
|
98
|
-
};
|
|
99
|
-
// output is validated against outputSchema
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Schema Definition Best Practices
|
|
105
|
-
|
|
106
|
-
### Use Descriptive Schemas
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
// Good: Clear, descriptive schema
|
|
110
|
-
inputSchema: z.object({
|
|
111
|
-
userId: z.string().uuid(),
|
|
112
|
-
email: z.string().email(),
|
|
113
|
-
age: z.number().int().positive(),
|
|
114
|
-
})
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### Handle Optional Fields
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
inputSchema: z.object({
|
|
121
|
-
required: z.string(),
|
|
122
|
-
optional: z.string().optional(),
|
|
123
|
-
withDefault: z.string().default('fallback'),
|
|
124
|
-
})
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Use Schema Composition
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
// Define reusable schemas
|
|
131
|
-
const userSchema = z.object({
|
|
132
|
-
id: z.string(),
|
|
133
|
-
name: z.string(),
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
const addressSchema = z.object({
|
|
137
|
-
street: z.string(),
|
|
138
|
-
city: z.string(),
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
// Compose in step
|
|
142
|
-
inputSchema: z.object({
|
|
143
|
-
user: userSchema,
|
|
144
|
-
address: addressSchema,
|
|
145
|
-
})
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### Handle Arrays and Nested Objects
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
inputSchema: z.object({
|
|
152
|
-
items: z.array(z.object({
|
|
153
|
-
id: z.string(),
|
|
154
|
-
quantity: z.number(),
|
|
155
|
-
})),
|
|
156
|
-
metadata: z.record(z.string()),
|
|
157
|
-
})
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## Finding Steps Without Schemas
|
|
161
|
-
|
|
162
|
-
Search your codebase:
|
|
163
|
-
|
|
164
|
-
```bash
|
|
165
|
-
# Find step definitions
|
|
166
|
-
grep -rn "step({" src/workflows/
|
|
167
|
-
|
|
168
|
-
# Look for steps without inputSchema
|
|
169
|
-
grep -A5 "step({" src/workflows/ | grep -B2 "fn:"
|
|
170
|
-
|
|
171
|
-
# Check if schemas are present
|
|
172
|
-
grep -rn "inputSchema:" src/workflows/
|
|
173
|
-
grep -rn "outputSchema:" src/workflows/
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Review each step definition to ensure both schemas are present.
|
|
177
|
-
|
|
178
|
-
## Benefits of Explicit Schemas
|
|
179
|
-
|
|
180
|
-
1. **Runtime Validation**: Catches data errors early
|
|
181
|
-
2. **Type Safety**: Full TypeScript inference in step functions
|
|
182
|
-
3. **Documentation**: Schemas document expected data shapes
|
|
183
|
-
4. **Serialization**: Ensures proper data serialization between steps
|
|
184
|
-
5. **Error Messages**: Clear validation errors when data is wrong
|
|
185
|
-
|
|
186
|
-
## Common Schema Patterns
|
|
187
|
-
|
|
188
|
-
### API Response Steps
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
export const fetchUser = step({
|
|
192
|
-
name: 'fetchUser',
|
|
193
|
-
inputSchema: z.object({
|
|
194
|
-
userId: z.string(),
|
|
195
|
-
}),
|
|
196
|
-
outputSchema: z.object({
|
|
197
|
-
user: z.object({
|
|
198
|
-
id: z.string(),
|
|
199
|
-
name: z.string(),
|
|
200
|
-
email: z.string(),
|
|
201
|
-
}).nullable(), // Handle not found
|
|
202
|
-
found: z.boolean(),
|
|
203
|
-
}),
|
|
204
|
-
fn: async (input) => {
|
|
205
|
-
const user = await api.getUser(input.userId);
|
|
206
|
-
return { user, found: user !== null };
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Transformation Steps
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
export const transformData = step({
|
|
215
|
-
name: 'transformData',
|
|
216
|
-
inputSchema: z.object({
|
|
217
|
-
raw: z.array(z.unknown()),
|
|
218
|
-
}),
|
|
219
|
-
outputSchema: z.object({
|
|
220
|
-
processed: z.array(z.object({
|
|
221
|
-
id: z.string(),
|
|
222
|
-
value: z.number(),
|
|
223
|
-
})),
|
|
224
|
-
count: z.number(),
|
|
225
|
-
}),
|
|
226
|
-
fn: async (input) => {
|
|
227
|
-
const processed = input.raw.map(transformItem);
|
|
228
|
-
return { processed, count: processed.length };
|
|
229
|
-
},
|
|
230
|
-
});
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### Void Output Steps
|
|
234
|
-
|
|
235
|
-
For steps that don't return meaningful data:
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
export const logEvent = step({
|
|
239
|
-
name: 'logEvent',
|
|
240
|
-
inputSchema: z.object({
|
|
241
|
-
event: z.string(),
|
|
242
|
-
data: z.record(z.unknown()),
|
|
243
|
-
}),
|
|
244
|
-
outputSchema: z.object({
|
|
245
|
-
logged: z.literal(true),
|
|
246
|
-
}),
|
|
247
|
-
fn: async (input) => {
|
|
248
|
-
await logger.log(input.event, input.data);
|
|
249
|
-
return { logged: true };
|
|
250
|
-
},
|
|
251
|
-
});
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## Verification
|
|
255
|
-
|
|
256
|
-
After adding schemas:
|
|
257
|
-
|
|
258
|
-
1. **TypeScript check**: `npm run output:workflow:build` should pass without type errors
|
|
259
|
-
2. **Runtime test**: `npx output workflow run <name> '<input>'` should validate correctly
|
|
260
|
-
3. **Invalid input test**: Pass invalid data and verify validation errors appear
|
|
261
|
-
|
|
262
|
-
## Related Issues
|
|
263
|
-
|
|
264
|
-
- For Zod import issues, see `output-error-zod-import`
|
|
265
|
-
- For type mismatches despite schemas, verify schema matches actual data
|
package/dist/templates/agent_instructions/skills/output-error-nondeterminism/SKILL.md.template
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: output-error-nondeterminism
|
|
3
|
-
description: Fix non-determinism errors in Output SDK workflows. Use when seeing replay failures, inconsistent results between runs, "non-deterministic" error messages, or workflows behaving differently on retry.
|
|
4
|
-
allowed-tools: [Bash, Read]
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Fix Non-Determinism Errors
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
|
|
11
|
-
This skill helps diagnose and fix non-determinism errors in Output SDK workflows. Workflows must be deterministic because Temporal may replay them during recovery or retries, and the replay must produce identical results.
|
|
12
|
-
|
|
13
|
-
## When to Use This Skill
|
|
14
|
-
|
|
15
|
-
You're seeing:
|
|
16
|
-
- "non-deterministic" error messages
|
|
17
|
-
- Replay failures after workflow restart
|
|
18
|
-
- Inconsistent results between runs with same input
|
|
19
|
-
- Errors during workflow recovery
|
|
20
|
-
- Warnings about determinism violations
|
|
21
|
-
|
|
22
|
-
## Root Cause
|
|
23
|
-
|
|
24
|
-
Temporal workflows must be deterministic: given the same input, they must always execute the same sequence of operations. This is because Temporal replays workflow history to recover state after crashes or restarts.
|
|
25
|
-
|
|
26
|
-
Non-deterministic operations break this replay mechanism because they produce different values each time.
|
|
27
|
-
|
|
28
|
-
## Common Causes and Solutions
|
|
29
|
-
|
|
30
|
-
### 1. Math.random()
|
|
31
|
-
|
|
32
|
-
**Problem**: Random values differ on each execution.
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
// WRONG: Non-deterministic
|
|
36
|
-
export default workflow({
|
|
37
|
-
fn: async (input) => {
|
|
38
|
-
const id = Math.random().toString(36); // Different each time!
|
|
39
|
-
return await processWithId({ id });
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
**Solution**: Pass random values as workflow input or generate in a step.
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
// Option 1: Pass as input
|
|
48
|
-
export default workflow({
|
|
49
|
-
inputSchema: z.object({
|
|
50
|
-
id: z.string() // Generate ID before calling workflow
|
|
51
|
-
}),
|
|
52
|
-
fn: async (input) => {
|
|
53
|
-
return await processWithId({ id: input.id });
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// Option 2: Generate in a step (steps can be non-deterministic)
|
|
58
|
-
export const generateId = step({
|
|
59
|
-
name: 'generateId',
|
|
60
|
-
fn: async () => ({ id: Math.random().toString(36) })
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
export default workflow({
|
|
64
|
-
fn: async (input) => {
|
|
65
|
-
const { id } = await generateId({});
|
|
66
|
-
return await processWithId({ id });
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### 2. Date.now() / new Date()
|
|
72
|
-
|
|
73
|
-
**Problem**: Timestamps change between executions.
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
// WRONG: Non-deterministic
|
|
77
|
-
export default workflow({
|
|
78
|
-
fn: async (input) => {
|
|
79
|
-
const timestamp = Date.now(); // Different each replay!
|
|
80
|
-
return await logEvent({ timestamp });
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
**Solution**: Pass timestamps as input or use Temporal's time API.
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
// Option 1: Pass as input
|
|
89
|
-
export default workflow({
|
|
90
|
-
inputSchema: z.object({
|
|
91
|
-
timestamp: z.number()
|
|
92
|
-
}),
|
|
93
|
-
fn: async (input) => {
|
|
94
|
-
return await logEvent({ timestamp: input.timestamp });
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// Option 2: Generate in a step
|
|
99
|
-
export const getTimestamp = step({
|
|
100
|
-
name: 'getTimestamp',
|
|
101
|
-
fn: async () => ({ timestamp: Date.now() })
|
|
102
|
-
});
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### 3. crypto.randomUUID()
|
|
106
|
-
|
|
107
|
-
**Problem**: UUIDs differ each execution.
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
// WRONG: Non-deterministic
|
|
111
|
-
import { randomUUID } from 'crypto';
|
|
112
|
-
|
|
113
|
-
export default workflow({
|
|
114
|
-
fn: async (input) => {
|
|
115
|
-
const requestId = randomUUID(); // Different each time!
|
|
116
|
-
return await makeRequest({ requestId });
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
**Solution**: Generate UUIDs as input or in steps.
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
// Correct: Generate in step
|
|
125
|
-
export const generateRequestId = step({
|
|
126
|
-
name: 'generateRequestId',
|
|
127
|
-
fn: async () => {
|
|
128
|
-
const { randomUUID } = await import('crypto');
|
|
129
|
-
return { requestId: randomUUID() };
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### 4. Dynamic Imports
|
|
135
|
-
|
|
136
|
-
**Problem**: Dynamic imports may resolve differently.
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
// WRONG: Non-deterministic import timing
|
|
140
|
-
export default workflow({
|
|
141
|
-
fn: async (input) => {
|
|
142
|
-
const module = await import(`./handlers/${input.type}`);
|
|
143
|
-
return module.handle(input);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
**Solution**: Use static imports and conditional logic.
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
// Correct: Static imports with conditional use
|
|
152
|
-
import { handleTypeA } from './handlers/typeA';
|
|
153
|
-
import { handleTypeB } from './handlers/typeB';
|
|
154
|
-
|
|
155
|
-
export default workflow({
|
|
156
|
-
fn: async (input) => {
|
|
157
|
-
if (input.type === 'A') {
|
|
158
|
-
return await handleTypeA(input);
|
|
159
|
-
} else {
|
|
160
|
-
return await handleTypeB(input);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### 5. Environment Variables
|
|
167
|
-
|
|
168
|
-
**Problem**: Environment may differ between replays.
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
// WRONG: Environment can change
|
|
172
|
-
export default workflow({
|
|
173
|
-
fn: async (input) => {
|
|
174
|
-
const apiUrl = process.env.API_URL; // May differ on different workers
|
|
175
|
-
return await callApi({ url: apiUrl });
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
**Solution**: Pass configuration as input or use constants.
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
// Correct: Pass as input
|
|
184
|
-
export default workflow({
|
|
185
|
-
inputSchema: z.object({
|
|
186
|
-
apiUrl: z.string()
|
|
187
|
-
}),
|
|
188
|
-
fn: async (input) => {
|
|
189
|
-
return await callApi({ url: input.apiUrl });
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
## How to Find Non-Deterministic Code
|
|
195
|
-
|
|
196
|
-
### Search for Common Patterns
|
|
197
|
-
|
|
198
|
-
```bash
|
|
199
|
-
# Find Math.random usage
|
|
200
|
-
grep -rn "Math.random" src/workflows/
|
|
201
|
-
|
|
202
|
-
# Find Date.now or new Date
|
|
203
|
-
grep -rn "Date.now\|new Date" src/workflows/
|
|
204
|
-
|
|
205
|
-
# Find crypto random functions
|
|
206
|
-
grep -rn "randomUUID\|randomBytes" src/workflows/
|
|
207
|
-
|
|
208
|
-
# Find dynamic imports
|
|
209
|
-
grep -rn "import(" src/workflows/
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### Review Workflow Files
|
|
213
|
-
|
|
214
|
-
Look at your workflow `fn` functions specifically. Non-deterministic code is only a problem **in workflow functions**, not in step functions.
|
|
215
|
-
|
|
216
|
-
## Verification Steps
|
|
217
|
-
|
|
218
|
-
1. **Fix the code** using solutions above
|
|
219
|
-
2. **Run the workflow**: `npx output workflow run <name> '<input>'`
|
|
220
|
-
3. **Run again with same input**: Result should be identical
|
|
221
|
-
4. **Check for errors**: No "non-deterministic" messages
|
|
222
|
-
|
|
223
|
-
## The Determinism Rule
|
|
224
|
-
|
|
225
|
-
**Workflow functions must be deterministic:**
|
|
226
|
-
- Same input = same execution path
|
|
227
|
-
- No side effects (network, filesystem, random values)
|
|
228
|
-
- Only orchestration logic and step calls
|
|
229
|
-
|
|
230
|
-
**Step functions can be non-deterministic:**
|
|
231
|
-
- Steps record their results in Temporal history
|
|
232
|
-
- Replays use recorded results, not re-execution
|
|
233
|
-
- All I/O should happen in steps
|
|
234
|
-
|
|
235
|
-
## Debugging Tip
|
|
236
|
-
|
|
237
|
-
If unsure whether code is causing issues:
|
|
238
|
-
|
|
239
|
-
```bash
|
|
240
|
-
# Run the workflow
|
|
241
|
-
npx output workflow start my-workflow '{"input": "test"}'
|
|
242
|
-
|
|
243
|
-
# Get the workflow ID and run debug to see replay behavior
|
|
244
|
-
npx output workflow debug <workflowId> --format json
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
Look for errors or warnings about non-determinism in the trace.
|
|
248
|
-
|
|
249
|
-
## Related Issues
|
|
250
|
-
|
|
251
|
-
- For I/O in workflow code, see `output-error-direct-io`
|
|
252
|
-
- For random values needed in logic, generate them in steps or pass as input
|