smithers-orchestrator 0.7.0 → 0.8.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 +18 -13
- package/package.json +1 -1
- package/src/AgentLike.ts +14 -0
- package/src/GraphSnapshot.ts +9 -0
- package/src/OutputAccessor.ts +20 -0
- package/src/OutputKey.ts +1 -0
- package/src/RunOptions.ts +16 -0
- package/src/RunResult.ts +6 -0
- package/src/RunStatus.ts +6 -0
- package/src/SchemaRegistryEntry.ts +4 -0
- package/src/SmithersCtx.ts +29 -0
- package/src/SmithersError.ts +5 -0
- package/src/SmithersEvent.ts +155 -0
- package/src/SmithersWorkflow.ts +13 -0
- package/src/SmithersWorkflowOptions.ts +3 -0
- package/src/TaskDescriptor.ts +28 -0
- package/src/XmlNode.ts +13 -0
- package/src/agents/{cli.ts → BaseCliAgent.ts} +79 -624
- package/src/agents/ClaudeCodeAgent.ts +166 -0
- package/src/agents/CodexAgent.ts +121 -0
- package/src/agents/GeminiAgent.ts +82 -0
- package/src/agents/KimiAgent.ts +91 -0
- package/src/agents/PiAgent.ts +219 -0
- package/src/agents/index.ts +12 -0
- package/src/camelToSnake.ts +6 -0
- package/src/cli/index.ts +1 -1
- package/src/components/Branch.ts +14 -0
- package/src/components/MergeQueue.ts +22 -0
- package/src/components/Parallel.ts +18 -0
- package/src/components/Ralph.ts +15 -0
- package/src/components/Sequence.ts +11 -0
- package/src/{components.ts → components/Task.ts} +21 -61
- package/src/components/Workflow.ts +11 -0
- package/src/components/Worktree.ts +18 -0
- package/src/components/index.ts +23 -0
- package/src/context.ts +17 -4
- package/src/create.ts +9 -8
- package/src/db/output.ts +28 -37
- package/src/dom/extract.ts +8 -12
- package/src/engine/index.ts +240 -41
- package/src/engine/scheduler.ts +2 -1
- package/src/events.ts +1 -1
- package/src/index.ts +24 -38
- package/src/{mdx-components.ts → markdownComponents.ts} +0 -24
- package/src/pi-plugin/index.ts +1 -1
- package/src/renderMdx.ts +26 -0
- package/src/revert.ts +1 -1
- package/src/server/index.ts +12 -2
- package/src/tools/bash.ts +155 -0
- package/src/tools/edit.ts +51 -0
- package/src/tools/grep.ts +89 -0
- package/src/tools/logToolCall.ts +54 -0
- package/src/tools/read.ts +40 -0
- package/src/tools/tools.ts +8 -0
- package/src/tools/write.ts +43 -0
- package/src/unwrapZodType.ts +33 -0
- package/src/utils/xml.ts +1 -1
- package/src/zodToCreateTableSQL.ts +60 -0
- package/src/{zod-to-table.ts → zodToTable.ts} +2 -77
- package/src/types.ts +0 -417
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ There is no hidden in-memory state. Every task result is stored as:
|
|
|
23
23
|
## Example
|
|
24
24
|
|
|
25
25
|
```tsx
|
|
26
|
-
import { createSmithers,
|
|
26
|
+
import { createSmithers, Sequence } from "smithers-orchestrator";
|
|
27
27
|
import { z } from "zod";
|
|
28
28
|
|
|
29
29
|
const analyzeSchema = z.object({
|
|
@@ -36,7 +36,7 @@ const fixSchema = z.object({
|
|
|
36
36
|
explanation: z.string(),
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
const { Workflow, smithers } = createSmithers({
|
|
39
|
+
const { Workflow, Task, smithers, outputs } = createSmithers({
|
|
40
40
|
analyze: analyzeSchema,
|
|
41
41
|
fix: fixSchema,
|
|
42
42
|
});
|
|
@@ -44,11 +44,11 @@ const { Workflow, smithers } = createSmithers({
|
|
|
44
44
|
export default smithers((ctx) => (
|
|
45
45
|
<Workflow name="bugfix">
|
|
46
46
|
<Sequence>
|
|
47
|
-
<Task id="analyze" output=
|
|
47
|
+
<Task id="analyze" output={outputs.analyze} agent={analyzer}>
|
|
48
48
|
{`Analyze the bug: ${ctx.input.description}`}
|
|
49
49
|
</Task>
|
|
50
50
|
|
|
51
|
-
<Task id="fix" output=
|
|
51
|
+
<Task id="fix" output={outputs.fix} agent={fixer}>
|
|
52
52
|
{`Fix this issue: ${ctx.output("analyze", { nodeId: "analyze" }).summary}`}
|
|
53
53
|
</Task>
|
|
54
54
|
</Sequence>
|
|
@@ -103,16 +103,22 @@ If new nodes are unblocked, they become runnable.
|
|
|
103
103
|
|
|
104
104
|
### 2. Zod Schemas = Durable Tables
|
|
105
105
|
|
|
106
|
-
Each output schema becomes a SQLite table.
|
|
106
|
+
Each output schema becomes a SQLite table. Pass the schema directly to `<Task>` via the `outputs` object returned by `createSmithers`:
|
|
107
107
|
|
|
108
108
|
```ts
|
|
109
109
|
const analyzeSchema = z.object({
|
|
110
110
|
summary: z.string(),
|
|
111
111
|
severity: z.enum(["low", "medium", "high"]),
|
|
112
112
|
});
|
|
113
|
+
|
|
114
|
+
const { Workflow, Task, smithers, outputs } = createSmithers({
|
|
115
|
+
analyze: analyzeSchema,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// outputs.analyze === analyzeSchema (the ZodObject, by reference)
|
|
113
119
|
```
|
|
114
120
|
|
|
115
|
-
* Agent output must validate.
|
|
121
|
+
* Agent output must validate against the schema.
|
|
116
122
|
* If validation fails, the task retries (with error feedback).
|
|
117
123
|
* Validated output is persisted.
|
|
118
124
|
|
|
@@ -156,8 +162,7 @@ If an agent returns malformed JSON:
|
|
|
156
162
|
```tsx
|
|
157
163
|
<Task
|
|
158
164
|
id="analyze"
|
|
159
|
-
output=
|
|
160
|
-
outputSchema={analyzeSchema}
|
|
165
|
+
output={outputs.analyze}
|
|
161
166
|
agent={analyzer}
|
|
162
167
|
retries={2}
|
|
163
168
|
>
|
|
@@ -178,11 +183,11 @@ Each iteration is stored separately in the database.
|
|
|
178
183
|
until={ctx.latest("review", "validate")?.approved}
|
|
179
184
|
maxIterations={5}
|
|
180
185
|
>
|
|
181
|
-
<Task id="implement" output=
|
|
186
|
+
<Task id="implement" output={outputs.implement} agent={coder}>
|
|
182
187
|
Fix based on feedback
|
|
183
188
|
</Task>
|
|
184
189
|
|
|
185
|
-
<Task id="validate" output=
|
|
190
|
+
<Task id="validate" output={outputs.review} agent={reviewer}>
|
|
186
191
|
Review the implementation
|
|
187
192
|
</Task>
|
|
188
193
|
</Ralph>
|
|
@@ -195,16 +200,16 @@ Each iteration is stored separately in the database.
|
|
|
195
200
|
Because the workflow re-renders after each task, you can branch with normal JSX:
|
|
196
201
|
|
|
197
202
|
```tsx
|
|
198
|
-
<Task id="assess" output=
|
|
203
|
+
<Task id="assess" output={outputs.assess} agent={analyst}>
|
|
199
204
|
Assess complexity
|
|
200
205
|
</Task>
|
|
201
206
|
|
|
202
207
|
{ctx.output("assess", { nodeId: "assess" }).complexity === "high" ? (
|
|
203
|
-
<Task id="plan" output=
|
|
208
|
+
<Task id="plan" output={outputs.plan} agent={architect}>
|
|
204
209
|
Plan implementation
|
|
205
210
|
</Task>
|
|
206
211
|
) : (
|
|
207
|
-
<Task id="implement" output=
|
|
212
|
+
<Task id="implement" output={outputs.code} agent={coder}>
|
|
208
213
|
Quick fix
|
|
209
214
|
</Task>
|
|
210
215
|
)}
|
package/package.json
CHANGED
package/src/AgentLike.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type AgentLike = {
|
|
2
|
+
id?: string;
|
|
3
|
+
tools?: Record<string, any>;
|
|
4
|
+
generate: (args: {
|
|
5
|
+
options?: any;
|
|
6
|
+
abortSignal?: AbortSignal;
|
|
7
|
+
prompt: string;
|
|
8
|
+
timeout?: { totalMs: number } | undefined;
|
|
9
|
+
onStdout?: (text: string) => void;
|
|
10
|
+
onStderr?: (text: string) => void;
|
|
11
|
+
outputSchema?: import("zod").ZodObject<any>;
|
|
12
|
+
}) => Promise<any>;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export type InferRow<TTable> = TTable extends { $inferSelect: infer R }
|
|
4
|
+
? R
|
|
5
|
+
: never;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Infer the output type from either a Zod schema or a Drizzle table.
|
|
9
|
+
*/
|
|
10
|
+
export type InferOutputEntry<T> = T extends z.ZodTypeAny
|
|
11
|
+
? z.infer<T>
|
|
12
|
+
: T extends { $inferSelect: any }
|
|
13
|
+
? InferRow<T>
|
|
14
|
+
: never;
|
|
15
|
+
|
|
16
|
+
export type OutputAccessor<Schema> = {
|
|
17
|
+
<K extends keyof Schema & string>(table: K): Array<InferOutputEntry<Schema[K]>>;
|
|
18
|
+
} & {
|
|
19
|
+
[K in keyof Schema & string]: Array<InferOutputEntry<Schema[K]>>;
|
|
20
|
+
};
|
package/src/OutputKey.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type OutputKey = { nodeId: string; iteration?: number };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SmithersEvent } from "./SmithersEvent";
|
|
2
|
+
|
|
3
|
+
export type RunOptions = {
|
|
4
|
+
runId?: string;
|
|
5
|
+
input: Record<string, unknown>;
|
|
6
|
+
maxConcurrency?: number;
|
|
7
|
+
onProgress?: (e: SmithersEvent) => void;
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
resume?: boolean;
|
|
10
|
+
workflowPath?: string;
|
|
11
|
+
rootDir?: string;
|
|
12
|
+
logDir?: string | null;
|
|
13
|
+
allowNetwork?: boolean;
|
|
14
|
+
maxOutputBytes?: number;
|
|
15
|
+
toolTimeoutMs?: number;
|
|
16
|
+
};
|
package/src/RunResult.ts
ADDED
package/src/RunStatus.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { OutputKey } from "./OutputKey";
|
|
2
|
+
import type { OutputAccessor, InferOutputEntry } from "./OutputAccessor";
|
|
3
|
+
|
|
4
|
+
export interface SmithersCtx<Schema> {
|
|
5
|
+
runId: string;
|
|
6
|
+
iteration: number;
|
|
7
|
+
iterations?: Record<string, number>;
|
|
8
|
+
input: Schema extends { input: infer T } ? T : Record<string, unknown>;
|
|
9
|
+
outputs: OutputAccessor<Schema>;
|
|
10
|
+
|
|
11
|
+
output<K extends keyof Schema & string>(
|
|
12
|
+
table: K,
|
|
13
|
+
key: OutputKey,
|
|
14
|
+
): InferOutputEntry<Schema[K]>;
|
|
15
|
+
|
|
16
|
+
outputMaybe<K extends keyof Schema & string>(
|
|
17
|
+
table: K,
|
|
18
|
+
key: OutputKey,
|
|
19
|
+
): InferOutputEntry<Schema[K]> | undefined;
|
|
20
|
+
|
|
21
|
+
latest<K extends keyof Schema & string>(
|
|
22
|
+
table: K,
|
|
23
|
+
nodeId: string,
|
|
24
|
+
): InferOutputEntry<Schema[K]> | undefined;
|
|
25
|
+
|
|
26
|
+
latestArray(value: unknown, schema: import("zod").ZodType): any[];
|
|
27
|
+
|
|
28
|
+
iterationCount(table: any, nodeId: string): number;
|
|
29
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { RunStatus } from "./RunStatus";
|
|
2
|
+
|
|
3
|
+
export type SmithersEvent =
|
|
4
|
+
| { type: "RunStarted"; runId: string; timestampMs: number }
|
|
5
|
+
| {
|
|
6
|
+
type: "RunStatusChanged";
|
|
7
|
+
runId: string;
|
|
8
|
+
status: RunStatus;
|
|
9
|
+
timestampMs: number;
|
|
10
|
+
}
|
|
11
|
+
| { type: "RunFinished"; runId: string; timestampMs: number }
|
|
12
|
+
| { type: "RunFailed"; runId: string; error: unknown; timestampMs: number }
|
|
13
|
+
| { type: "RunCancelled"; runId: string; timestampMs: number }
|
|
14
|
+
| {
|
|
15
|
+
type: "FrameCommitted";
|
|
16
|
+
runId: string;
|
|
17
|
+
frameNo: number;
|
|
18
|
+
xmlHash: string;
|
|
19
|
+
timestampMs: number;
|
|
20
|
+
}
|
|
21
|
+
| {
|
|
22
|
+
type: "NodePending";
|
|
23
|
+
runId: string;
|
|
24
|
+
nodeId: string;
|
|
25
|
+
iteration: number;
|
|
26
|
+
timestampMs: number;
|
|
27
|
+
}
|
|
28
|
+
| {
|
|
29
|
+
type: "NodeStarted";
|
|
30
|
+
runId: string;
|
|
31
|
+
nodeId: string;
|
|
32
|
+
iteration: number;
|
|
33
|
+
attempt: number;
|
|
34
|
+
timestampMs: number;
|
|
35
|
+
}
|
|
36
|
+
| {
|
|
37
|
+
type: "NodeFinished";
|
|
38
|
+
runId: string;
|
|
39
|
+
nodeId: string;
|
|
40
|
+
iteration: number;
|
|
41
|
+
attempt: number;
|
|
42
|
+
timestampMs: number;
|
|
43
|
+
}
|
|
44
|
+
| {
|
|
45
|
+
type: "NodeFailed";
|
|
46
|
+
runId: string;
|
|
47
|
+
nodeId: string;
|
|
48
|
+
iteration: number;
|
|
49
|
+
attempt: number;
|
|
50
|
+
error: unknown;
|
|
51
|
+
timestampMs: number;
|
|
52
|
+
}
|
|
53
|
+
| {
|
|
54
|
+
type: "NodeCancelled";
|
|
55
|
+
runId: string;
|
|
56
|
+
nodeId: string;
|
|
57
|
+
iteration: number;
|
|
58
|
+
attempt?: number;
|
|
59
|
+
reason?: string;
|
|
60
|
+
timestampMs: number;
|
|
61
|
+
}
|
|
62
|
+
| {
|
|
63
|
+
type: "NodeSkipped";
|
|
64
|
+
runId: string;
|
|
65
|
+
nodeId: string;
|
|
66
|
+
iteration: number;
|
|
67
|
+
timestampMs: number;
|
|
68
|
+
}
|
|
69
|
+
| {
|
|
70
|
+
type: "NodeRetrying";
|
|
71
|
+
runId: string;
|
|
72
|
+
nodeId: string;
|
|
73
|
+
iteration: number;
|
|
74
|
+
attempt: number;
|
|
75
|
+
timestampMs: number;
|
|
76
|
+
}
|
|
77
|
+
| {
|
|
78
|
+
type: "NodeWaitingApproval";
|
|
79
|
+
runId: string;
|
|
80
|
+
nodeId: string;
|
|
81
|
+
iteration: number;
|
|
82
|
+
timestampMs: number;
|
|
83
|
+
}
|
|
84
|
+
| {
|
|
85
|
+
type: "ApprovalRequested";
|
|
86
|
+
runId: string;
|
|
87
|
+
nodeId: string;
|
|
88
|
+
iteration: number;
|
|
89
|
+
timestampMs: number;
|
|
90
|
+
}
|
|
91
|
+
| {
|
|
92
|
+
type: "ApprovalGranted";
|
|
93
|
+
runId: string;
|
|
94
|
+
nodeId: string;
|
|
95
|
+
iteration: number;
|
|
96
|
+
timestampMs: number;
|
|
97
|
+
}
|
|
98
|
+
| {
|
|
99
|
+
type: "ApprovalDenied";
|
|
100
|
+
runId: string;
|
|
101
|
+
nodeId: string;
|
|
102
|
+
iteration: number;
|
|
103
|
+
timestampMs: number;
|
|
104
|
+
}
|
|
105
|
+
| {
|
|
106
|
+
type: "ToolCallStarted";
|
|
107
|
+
runId: string;
|
|
108
|
+
nodeId: string;
|
|
109
|
+
iteration: number;
|
|
110
|
+
attempt: number;
|
|
111
|
+
toolName: string;
|
|
112
|
+
seq: number;
|
|
113
|
+
timestampMs: number;
|
|
114
|
+
}
|
|
115
|
+
| {
|
|
116
|
+
type: "ToolCallFinished";
|
|
117
|
+
runId: string;
|
|
118
|
+
nodeId: string;
|
|
119
|
+
iteration: number;
|
|
120
|
+
attempt: number;
|
|
121
|
+
toolName: string;
|
|
122
|
+
seq: number;
|
|
123
|
+
status: "success" | "error";
|
|
124
|
+
timestampMs: number;
|
|
125
|
+
}
|
|
126
|
+
| {
|
|
127
|
+
type: "NodeOutput";
|
|
128
|
+
runId: string;
|
|
129
|
+
nodeId: string;
|
|
130
|
+
iteration: number;
|
|
131
|
+
attempt: number;
|
|
132
|
+
text: string;
|
|
133
|
+
stream: "stdout" | "stderr";
|
|
134
|
+
timestampMs: number;
|
|
135
|
+
}
|
|
136
|
+
| {
|
|
137
|
+
type: "RevertStarted";
|
|
138
|
+
runId: string;
|
|
139
|
+
nodeId: string;
|
|
140
|
+
iteration: number;
|
|
141
|
+
attempt: number;
|
|
142
|
+
jjPointer: string;
|
|
143
|
+
timestampMs: number;
|
|
144
|
+
}
|
|
145
|
+
| {
|
|
146
|
+
type: "RevertFinished";
|
|
147
|
+
runId: string;
|
|
148
|
+
nodeId: string;
|
|
149
|
+
iteration: number;
|
|
150
|
+
attempt: number;
|
|
151
|
+
jjPointer: string;
|
|
152
|
+
success: boolean;
|
|
153
|
+
error?: string;
|
|
154
|
+
timestampMs: number;
|
|
155
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import type { SmithersCtx } from "./SmithersCtx";
|
|
3
|
+
import type { SmithersWorkflowOptions } from "./SmithersWorkflowOptions";
|
|
4
|
+
import type { SchemaRegistryEntry } from "./SchemaRegistryEntry";
|
|
5
|
+
|
|
6
|
+
export type SmithersWorkflow<Schema> = {
|
|
7
|
+
db: unknown;
|
|
8
|
+
build: (ctx: SmithersCtx<Schema>) => React.ReactElement;
|
|
9
|
+
opts: SmithersWorkflowOptions;
|
|
10
|
+
schemaRegistry?: Map<string, SchemaRegistryEntry>;
|
|
11
|
+
/** Reverse lookup: ZodObject reference → schema key name */
|
|
12
|
+
zodToKeyName?: Map<import("zod").ZodObject<any>, string>;
|
|
13
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AgentLike } from "./AgentLike";
|
|
2
|
+
|
|
3
|
+
export type TaskDescriptor = {
|
|
4
|
+
nodeId: string;
|
|
5
|
+
ordinal: number;
|
|
6
|
+
iteration: number;
|
|
7
|
+
ralphId?: string;
|
|
8
|
+
worktreeId?: string;
|
|
9
|
+
worktreePath?: string;
|
|
10
|
+
outputTable: any | null;
|
|
11
|
+
outputTableName: string;
|
|
12
|
+
outputSchema?: import("zod").ZodObject<any>;
|
|
13
|
+
parallelGroupId?: string;
|
|
14
|
+
parallelMaxConcurrency?: number;
|
|
15
|
+
needsApproval: boolean;
|
|
16
|
+
skipIf: boolean;
|
|
17
|
+
retries: number;
|
|
18
|
+
timeoutMs: number | null;
|
|
19
|
+
continueOnFail: boolean;
|
|
20
|
+
agent?: AgentLike;
|
|
21
|
+
/** Fallback agent used on retry when the primary agent fails (e.g. rate-limited). */
|
|
22
|
+
fallbackAgent?: AgentLike;
|
|
23
|
+
prompt?: string;
|
|
24
|
+
staticPayload?: unknown;
|
|
25
|
+
computeFn?: () => unknown | Promise<unknown>;
|
|
26
|
+
label?: string;
|
|
27
|
+
meta?: Record<string, unknown>;
|
|
28
|
+
};
|
package/src/XmlNode.ts
ADDED