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.
Files changed (60) hide show
  1. package/README.md +18 -13
  2. package/package.json +1 -1
  3. package/src/AgentLike.ts +14 -0
  4. package/src/GraphSnapshot.ts +9 -0
  5. package/src/OutputAccessor.ts +20 -0
  6. package/src/OutputKey.ts +1 -0
  7. package/src/RunOptions.ts +16 -0
  8. package/src/RunResult.ts +6 -0
  9. package/src/RunStatus.ts +6 -0
  10. package/src/SchemaRegistryEntry.ts +4 -0
  11. package/src/SmithersCtx.ts +29 -0
  12. package/src/SmithersError.ts +5 -0
  13. package/src/SmithersEvent.ts +155 -0
  14. package/src/SmithersWorkflow.ts +13 -0
  15. package/src/SmithersWorkflowOptions.ts +3 -0
  16. package/src/TaskDescriptor.ts +28 -0
  17. package/src/XmlNode.ts +13 -0
  18. package/src/agents/{cli.ts → BaseCliAgent.ts} +79 -624
  19. package/src/agents/ClaudeCodeAgent.ts +166 -0
  20. package/src/agents/CodexAgent.ts +121 -0
  21. package/src/agents/GeminiAgent.ts +82 -0
  22. package/src/agents/KimiAgent.ts +91 -0
  23. package/src/agents/PiAgent.ts +219 -0
  24. package/src/agents/index.ts +12 -0
  25. package/src/camelToSnake.ts +6 -0
  26. package/src/cli/index.ts +1 -1
  27. package/src/components/Branch.ts +14 -0
  28. package/src/components/MergeQueue.ts +22 -0
  29. package/src/components/Parallel.ts +18 -0
  30. package/src/components/Ralph.ts +15 -0
  31. package/src/components/Sequence.ts +11 -0
  32. package/src/{components.ts → components/Task.ts} +21 -61
  33. package/src/components/Workflow.ts +11 -0
  34. package/src/components/Worktree.ts +18 -0
  35. package/src/components/index.ts +23 -0
  36. package/src/context.ts +17 -4
  37. package/src/create.ts +9 -8
  38. package/src/db/output.ts +28 -37
  39. package/src/dom/extract.ts +8 -12
  40. package/src/engine/index.ts +240 -41
  41. package/src/engine/scheduler.ts +2 -1
  42. package/src/events.ts +1 -1
  43. package/src/index.ts +24 -38
  44. package/src/{mdx-components.ts → markdownComponents.ts} +0 -24
  45. package/src/pi-plugin/index.ts +1 -1
  46. package/src/renderMdx.ts +26 -0
  47. package/src/revert.ts +1 -1
  48. package/src/server/index.ts +12 -2
  49. package/src/tools/bash.ts +155 -0
  50. package/src/tools/edit.ts +51 -0
  51. package/src/tools/grep.ts +89 -0
  52. package/src/tools/logToolCall.ts +54 -0
  53. package/src/tools/read.ts +40 -0
  54. package/src/tools/tools.ts +8 -0
  55. package/src/tools/write.ts +43 -0
  56. package/src/unwrapZodType.ts +33 -0
  57. package/src/utils/xml.ts +1 -1
  58. package/src/zodToCreateTableSQL.ts +60 -0
  59. package/src/{zod-to-table.ts → zodToTable.ts} +2 -77
  60. 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, Task, Sequence } from "smithers-orchestrator";
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="analyze" agent={analyzer}>
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="fix" agent={fixer}>
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="analyze"
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="implement" agent={coder}>
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="review" agent={reviewer}>
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="assess" agent={analyst}>
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="plan" agent={architect}>
208
+ <Task id="plan" output={outputs.plan} agent={architect}>
204
209
  Plan implementation
205
210
  </Task>
206
211
  ) : (
207
- <Task id="implement" output="code" agent={coder}>
212
+ <Task id="implement" output={outputs.code} agent={coder}>
208
213
  Quick fix
209
214
  </Task>
210
215
  )}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smithers-orchestrator",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "AI workflow orchestration with JSX",
5
5
  "author": "William Cory",
6
6
  "license": "MIT",
@@ -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,9 @@
1
+ import type { XmlNode } from "./XmlNode";
2
+ import type { TaskDescriptor } from "./TaskDescriptor";
3
+
4
+ export type GraphSnapshot = {
5
+ runId: string;
6
+ frameNo: number;
7
+ xml: XmlNode | null;
8
+ tasks: TaskDescriptor[];
9
+ };
@@ -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
+ };
@@ -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
+ };
@@ -0,0 +1,6 @@
1
+ export type RunResult = {
2
+ runId: string;
3
+ status: "finished" | "failed" | "cancelled" | "waiting-approval";
4
+ output?: unknown;
5
+ error?: unknown;
6
+ };
@@ -0,0 +1,6 @@
1
+ export type RunStatus =
2
+ | "running"
3
+ | "waiting-approval"
4
+ | "finished"
5
+ | "failed"
6
+ | "cancelled";
@@ -0,0 +1,4 @@
1
+ export type SchemaRegistryEntry = {
2
+ table: any;
3
+ zodSchema: import("zod").ZodObject<any>;
4
+ };
@@ -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,5 @@
1
+ export type SmithersError = {
2
+ code: string;
3
+ message: string;
4
+ details?: Record<string, unknown>;
5
+ };
@@ -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,3 @@
1
+ export type SmithersWorkflowOptions = {
2
+ cache?: boolean;
3
+ };
@@ -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
@@ -0,0 +1,13 @@
1
+ export type XmlNode = XmlElement | XmlText;
2
+
3
+ export type XmlElement = {
4
+ kind: "element";
5
+ tag: string;
6
+ props: Record<string, string>;
7
+ children: XmlNode[];
8
+ };
9
+
10
+ export type XmlText = {
11
+ kind: "text";
12
+ text: string;
13
+ };