@osolmaz/pi-workflows 0.2.0 → 0.4.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 +72 -5
- package/dist/builtins/catalog.d.ts +2 -0
- package/dist/builtins/catalog.js +22 -0
- package/dist/builtins/catalog.js.map +1 -0
- package/dist/builtins/monitor.workflow.d.ts +71 -0
- package/dist/builtins/monitor.workflow.js +250 -0
- package/dist/builtins/monitor.workflow.js.map +1 -0
- package/dist/controllers/conditions.d.ts +6 -0
- package/dist/controllers/conditions.js +68 -0
- package/dist/controllers/conditions.js.map +1 -0
- package/dist/controllers/definition.d.ts +6 -0
- package/dist/controllers/definition.js +45 -0
- package/dist/controllers/definition.js.map +1 -0
- package/dist/controllers/effects.d.ts +14 -0
- package/dist/controllers/effects.js +104 -0
- package/dist/controllers/effects.js.map +1 -0
- package/dist/controllers/errors.d.ts +12 -0
- package/dist/controllers/errors.js +25 -0
- package/dist/controllers/errors.js.map +1 -0
- package/dist/controllers/index.d.ts +13 -0
- package/dist/controllers/index.js +13 -0
- package/dist/controllers/index.js.map +1 -0
- package/dist/controllers/json.d.ts +5 -0
- package/dist/controllers/json.js +57 -0
- package/dist/controllers/json.js.map +1 -0
- package/dist/controllers/loader.d.ts +23 -0
- package/dist/controllers/loader.js +74 -0
- package/dist/controllers/loader.js.map +1 -0
- package/dist/controllers/manager.d.ts +58 -0
- package/dist/controllers/manager.js +399 -0
- package/dist/controllers/manager.js.map +1 -0
- package/dist/controllers/results.d.ts +5 -0
- package/dist/controllers/results.js +32 -0
- package/dist/controllers/results.js.map +1 -0
- package/dist/controllers/sqlite.d.ts +235 -0
- package/dist/controllers/sqlite.js +1087 -0
- package/dist/controllers/sqlite.js.map +1 -0
- package/dist/controllers/store.d.ts +112 -0
- package/dist/controllers/store.js +32 -0
- package/dist/controllers/store.js.map +1 -0
- package/dist/controllers/types.d.ts +159 -0
- package/dist/controllers/types.js +2 -0
- package/dist/controllers/types.js.map +1 -0
- package/dist/controllers/workflow-engine-scheduler.d.ts +25 -0
- package/dist/controllers/workflow-engine-scheduler.js +95 -0
- package/dist/controllers/workflow-engine-scheduler.js.map +1 -0
- package/dist/controllers/workflows.d.ts +27 -0
- package/dist/controllers/workflows.js +109 -0
- package/dist/controllers/workflows.js.map +1 -0
- package/dist/extension/controller-host.d.ts +47 -0
- package/dist/extension/controller-host.js +110 -0
- package/dist/extension/controller-host.js.map +1 -0
- package/dist/extension/executor.d.ts +3 -0
- package/dist/extension/executor.js +12 -2
- package/dist/extension/executor.js.map +1 -1
- package/dist/extension/index.d.ts +7 -0
- package/dist/extension/index.js +1118 -106
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/recorder.d.ts +6 -0
- package/dist/extension/recorder.js +20 -8
- package/dist/extension/recorder.js.map +1 -1
- package/dist/extension/workflow-tool.d.ts +28 -0
- package/dist/extension/workflow-tool.js +33 -0
- package/dist/extension/workflow-tool.js.map +1 -0
- package/dist/host/processes.d.ts +24 -0
- package/dist/host/processes.js +114 -0
- package/dist/host/processes.js.map +1 -0
- package/dist/host/rpc-bridge.d.ts +9 -0
- package/dist/host/rpc-bridge.js +39 -0
- package/dist/host/rpc-bridge.js.map +1 -0
- package/dist/host/rpc-executor.d.ts +38 -0
- package/dist/host/rpc-executor.js +254 -0
- package/dist/host/rpc-executor.js.map +1 -0
- package/dist/host/runner.d.ts +50 -0
- package/dist/host/runner.js +379 -0
- package/dist/host/runner.js.map +1 -0
- package/dist/viewer/cli.d.ts +7 -3
- package/dist/viewer/cli.js +150 -19
- package/dist/viewer/cli.js.map +1 -1
- package/dist/workflows/catalog.d.ts +43 -0
- package/dist/workflows/catalog.js +79 -0
- package/dist/workflows/catalog.js.map +1 -0
- package/dist/workflows/engine.d.ts +37 -2
- package/dist/workflows/engine.js +297 -29
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/errors.d.ts +23 -0
- package/dist/workflows/errors.js +38 -0
- package/dist/workflows/errors.js.map +1 -1
- package/dist/workflows/graph.js +0 -5
- package/dist/workflows/graph.js.map +1 -1
- package/dist/workflows/index.d.ts +2 -2
- package/dist/workflows/index.js +1 -1
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/loader.d.ts +19 -15
- package/dist/workflows/loader.js +63 -19
- package/dist/workflows/loader.js.map +1 -1
- package/dist/workflows/migrate-sources.d.ts +41 -0
- package/dist/workflows/migrate-sources.js +129 -0
- package/dist/workflows/migrate-sources.js.map +1 -0
- package/dist/workflows/schema.js +3 -2
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/store.d.ts +50 -6
- package/dist/workflows/store.js +448 -53
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/types.d.ts +27 -3
- package/docs/CONTROLLERS.md +215 -0
- package/docs/development.md +14 -9
- package/docs/plans/2026-08-04-controller-runtime-plan.md +169 -0
- package/docs/plans/2026-08-05-always-on-workflows-plan.md +125 -0
- package/docs/plans/2026-08-10-agent-managed-monitor-workflows-plan.md +184 -0
- package/docs/plans/2026-08-12-coordinated-workflow-timeouts-plan.md +74 -0
- package/docs/plans/2026-08-13-built-in-workflow-catalog-plan.md +97 -0
- package/docs/run-bundles.md +85 -28
- package/docs/workflows.md +161 -17
- package/examples/controllers/pull-request.controller.ts +215 -0
- package/package.json +10 -2
- package/src/builtins/catalog.ts +22 -0
- package/src/builtins/monitor.workflow.ts +302 -0
- package/src/controllers/conditions.ts +110 -0
- package/src/controllers/definition.ts +65 -0
- package/src/controllers/effects.ts +123 -0
- package/src/controllers/errors.ts +27 -0
- package/src/controllers/index.ts +90 -0
- package/src/controllers/json.ts +62 -0
- package/src/controllers/loader.ts +104 -0
- package/src/controllers/manager.ts +533 -0
- package/src/controllers/results.ts +46 -0
- package/src/controllers/sqlite.ts +1545 -0
- package/src/controllers/store.ts +160 -0
- package/src/controllers/types.ts +183 -0
- package/src/controllers/workflow-engine-scheduler.ts +148 -0
- package/src/controllers/workflows.ts +152 -0
- package/src/extension/controller-host.ts +163 -0
- package/src/extension/executor.ts +13 -2
- package/src/extension/index.ts +1325 -130
- package/src/extension/recorder.ts +65 -36
- package/src/extension/workflow-tool.ts +59 -0
- package/src/host/processes.ts +119 -0
- package/src/host/rpc-bridge.ts +44 -0
- package/src/host/rpc-executor.ts +299 -0
- package/src/host/runner.ts +445 -0
- package/src/viewer/cli.ts +167 -21
- package/src/workflows/catalog.ts +135 -0
- package/src/workflows/engine.ts +404 -41
- package/src/workflows/errors.ts +45 -0
- package/src/workflows/graph.ts +0 -5
- package/src/workflows/index.ts +2 -0
- package/src/workflows/loader.ts +76 -22
- package/src/workflows/migrate-sources.ts +167 -0
- package/src/workflows/schema.ts +3 -2
- package/src/workflows/store.ts +557 -48
- package/src/workflows/types.ts +22 -3
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ControllerManager,
|
|
3
|
+
SqliteControllerStore,
|
|
4
|
+
projectControllerStorePath,
|
|
5
|
+
loadDiscoveredControllers,
|
|
6
|
+
type AnyControllerDefinition,
|
|
7
|
+
type ControllerResource,
|
|
8
|
+
type WorkflowSchedulerRequest,
|
|
9
|
+
type WorkflowSchedulerResult,
|
|
10
|
+
} from "../controllers/index.js";
|
|
11
|
+
|
|
12
|
+
export type ParsedControllerArgs =
|
|
13
|
+
| { kind: "list" }
|
|
14
|
+
| { kind: "get"; controller: string; key: string }
|
|
15
|
+
| { kind: "apply"; controller: string; key: string; spec: unknown }
|
|
16
|
+
| { kind: "reconcile"; controller: string; key: string }
|
|
17
|
+
| { kind: "delete"; controller: string; key: string }
|
|
18
|
+
| { kind: "start" }
|
|
19
|
+
| { kind: "stop" };
|
|
20
|
+
|
|
21
|
+
export type PiChildWorkflowStarter = (
|
|
22
|
+
request: WorkflowSchedulerRequest,
|
|
23
|
+
signal: AbortSignal,
|
|
24
|
+
onComplete: (result: WorkflowSchedulerResult) => void,
|
|
25
|
+
) => Promise<WorkflowSchedulerResult>;
|
|
26
|
+
|
|
27
|
+
export function parseControllerArgs(args: string): ParsedControllerArgs {
|
|
28
|
+
const trimmed = args.trim();
|
|
29
|
+
if (trimmed.length === 0 || trimmed === "list") {
|
|
30
|
+
return { kind: "list" };
|
|
31
|
+
}
|
|
32
|
+
if (trimmed === "start" || trimmed === "stop") {
|
|
33
|
+
return { kind: trimmed };
|
|
34
|
+
}
|
|
35
|
+
const apply = trimmed.match(/^apply\s+(\S+)\s+(\S+)\s+([\s\S]+)$/);
|
|
36
|
+
if (apply !== null) {
|
|
37
|
+
let spec: unknown;
|
|
38
|
+
try {
|
|
39
|
+
spec = JSON.parse(apply[3] as string) as unknown;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Invalid controller spec JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
kind: "apply",
|
|
47
|
+
controller: apply[1] as string,
|
|
48
|
+
key: apply[2] as string,
|
|
49
|
+
spec,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const operation = trimmed.match(/^(get|reconcile|delete)\s+(\S+)\s+(\S+)$/);
|
|
53
|
+
if (operation !== null) {
|
|
54
|
+
return {
|
|
55
|
+
kind: operation[1] as "get" | "reconcile" | "delete",
|
|
56
|
+
controller: operation[2] as string,
|
|
57
|
+
key: operation[3] as string,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
throw new Error(
|
|
61
|
+
"Usage: /controller [list|get <controller> <key>|apply <controller> <key> <json>|reconcile <controller> <key>|delete <controller> <key>|start|stop]",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export class PiControllerHost {
|
|
66
|
+
readonly definitions: AnyControllerDefinition[];
|
|
67
|
+
readonly store: SqliteControllerStore;
|
|
68
|
+
readonly manager: ControllerManager;
|
|
69
|
+
private running = false;
|
|
70
|
+
|
|
71
|
+
private constructor(
|
|
72
|
+
definitions: AnyControllerDefinition[],
|
|
73
|
+
store: SqliteControllerStore,
|
|
74
|
+
manager: ControllerManager,
|
|
75
|
+
) {
|
|
76
|
+
this.definitions = definitions;
|
|
77
|
+
this.store = store;
|
|
78
|
+
this.manager = manager;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static async create(options: {
|
|
82
|
+
cwd: string;
|
|
83
|
+
startChild: PiChildWorkflowStarter;
|
|
84
|
+
}): Promise<PiControllerHost | undefined> {
|
|
85
|
+
const definitions = await loadDiscoveredControllers({ cwd: options.cwd });
|
|
86
|
+
if (definitions.length === 0) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
const store = new SqliteControllerStore(projectControllerStorePath(options.cwd));
|
|
90
|
+
const manager = new ControllerManager({
|
|
91
|
+
store,
|
|
92
|
+
controllers: definitions,
|
|
93
|
+
workflowScheduler: { ensure: options.startChild },
|
|
94
|
+
});
|
|
95
|
+
return new PiControllerHost(definitions, store, manager);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
start(): void {
|
|
99
|
+
this.manager.start();
|
|
100
|
+
this.running = true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async stop(): Promise<void> {
|
|
104
|
+
await this.manager.stop();
|
|
105
|
+
this.running = false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async close(): Promise<void> {
|
|
109
|
+
await this.stop();
|
|
110
|
+
this.store.close();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get isRunning(): boolean {
|
|
114
|
+
return this.running;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
list(): string {
|
|
118
|
+
const names = this.definitions.map((item) => item.name).join(", ");
|
|
119
|
+
const resources = this.store.listResources();
|
|
120
|
+
if (resources.length === 0) {
|
|
121
|
+
return `Controllers: ${names}. No resources.`;
|
|
122
|
+
}
|
|
123
|
+
const rows = resources.map((resource) => resourceSummary(resource)).join("; ");
|
|
124
|
+
return `Controllers: ${names}. Resources: ${rows}.`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
get(controller: string, key: string): string {
|
|
128
|
+
const resource = this.store.getResource({ controller, key });
|
|
129
|
+
if (resource === undefined) {
|
|
130
|
+
throw new Error(`Controller resource not found: ${controller}/${key}`);
|
|
131
|
+
}
|
|
132
|
+
return JSON.stringify(
|
|
133
|
+
{
|
|
134
|
+
resource,
|
|
135
|
+
effects: this.store.listEffects(resource.metadata.uid),
|
|
136
|
+
workflows: this.store.listWorkflows(resource.metadata.uid),
|
|
137
|
+
},
|
|
138
|
+
null,
|
|
139
|
+
2,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
apply(controller: string, key: string, spec: unknown): ControllerResource {
|
|
144
|
+
return this.manager.putResourceByName(controller, key, spec);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
reconcile(controller: string, key: string): void {
|
|
148
|
+
this.manager.enqueue({ controller, key });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
delete(controller: string, key: string): void {
|
|
152
|
+
this.manager.requestDeletion({ controller, key });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function resourceSummary(resource: ControllerResource): string {
|
|
157
|
+
const condition =
|
|
158
|
+
resource.status.conditions.find((item) => item.type === "Ready") ??
|
|
159
|
+
resource.status.conditions[0];
|
|
160
|
+
const state =
|
|
161
|
+
condition === undefined ? "unknown" : `${String(condition.status)}:${condition.reason}`;
|
|
162
|
+
return `${resource.metadata.controller}/${resource.metadata.key} generation=${resource.metadata.generation} ready=${state}`;
|
|
163
|
+
}
|
|
@@ -33,6 +33,8 @@ export type ConversationStepExecutorOptions = {
|
|
|
33
33
|
maxNudges?: number;
|
|
34
34
|
/** Conversation linkage hooks, wired to the session recorder. */
|
|
35
35
|
conversation?: ConversationHooks;
|
|
36
|
+
/** Called when the engine aborts a pending agent step. */
|
|
37
|
+
onAbort?: (contract: AgentStepRequest["contract"], reason: unknown) => void;
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
type PendingStep = {
|
|
@@ -61,6 +63,7 @@ export class ConversationStepExecutor implements AgentStepExecutor {
|
|
|
61
63
|
private readonly sendPrompt: (delivery: PromptDelivery) => void;
|
|
62
64
|
private readonly maxNudges: number;
|
|
63
65
|
private readonly conversation: ConversationHooks | undefined;
|
|
66
|
+
private readonly onAbort: ConversationStepExecutorOptions["onAbort"];
|
|
64
67
|
private pending: PendingStep | null = null;
|
|
65
68
|
private streaming = false;
|
|
66
69
|
private heldByUser = false;
|
|
@@ -69,6 +72,7 @@ export class ConversationStepExecutor implements AgentStepExecutor {
|
|
|
69
72
|
this.sendPrompt = options.sendPrompt;
|
|
70
73
|
this.maxNudges = options.maxNudges ?? DEFAULT_MAX_NUDGES;
|
|
71
74
|
this.conversation = options.conversation;
|
|
75
|
+
this.onAbort = options.onAbort;
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
/** Track agent streaming state (wire to agent_start / agent_settled). */
|
|
@@ -121,8 +125,15 @@ export class ConversationStepExecutor implements AgentStepExecutor {
|
|
|
121
125
|
return await new Promise<AgentStepSubmission>((resolve, reject) => {
|
|
122
126
|
const onAbort = () => {
|
|
123
127
|
const reason: unknown = signal.reason ?? new Error("Workflow step aborted");
|
|
128
|
+
if (this.pending?.request !== request) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
124
131
|
this.clearPending();
|
|
125
|
-
|
|
132
|
+
try {
|
|
133
|
+
this.onAbort?.(request.contract, reason);
|
|
134
|
+
} finally {
|
|
135
|
+
reject(reason);
|
|
136
|
+
}
|
|
126
137
|
};
|
|
127
138
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
128
139
|
let markCleared!: () => void;
|
|
@@ -255,7 +266,7 @@ export class ConversationStepExecutor implements AgentStepExecutor {
|
|
|
255
266
|
prompt: [
|
|
256
267
|
`Reminder: workflow step ${JSON.stringify(nodeId)} is still awaiting your output.`,
|
|
257
268
|
"Complete it by calling the `workflow` tool with:",
|
|
258
|
-
`{"step": ${JSON.stringify(nodeId)}, "attempt": ${JSON.stringify(attemptId)}, "output": <your result>}`,
|
|
269
|
+
`{"action": "submit", "step": ${JSON.stringify(nodeId)}, "attempt": ${JSON.stringify(attemptId)}, "output": <your result>}`,
|
|
259
270
|
`Expected output: ${pending.request.contract.expectedOutput ?? "a JSON object with your result"}`,
|
|
260
271
|
].join("\n"),
|
|
261
272
|
streaming: this.streaming,
|