@osolmaz/pi-workflows 0.1.0 → 0.3.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 +107 -26
- package/dist/builtins/monitor.workflow.d.ts +71 -0
- package/dist/builtins/monitor.workflow.js +234 -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 +212 -0
- package/dist/controllers/sqlite.js +1009 -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 +93 -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 +14 -1
- 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 -89
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/recorder.d.ts +91 -0
- package/dist/extension/recorder.js +537 -0
- package/dist/extension/recorder.js.map +1 -0
- package/dist/extension/session-events.d.ts +134 -0
- package/dist/extension/session-events.js +60 -0
- package/dist/extension/session-events.js.map +1 -0
- package/dist/extension/widget.js +25 -24
- package/dist/extension/widget.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 +49 -0
- package/dist/host/runner.js +350 -0
- package/dist/host/runner.js.map +1 -0
- package/dist/render/canvas.d.ts +1 -1
- package/dist/render/canvas.js +5 -0
- package/dist/render/canvas.js.map +1 -1
- package/dist/render/graph-render.d.ts +5 -0
- package/dist/render/graph-render.js +211 -48
- package/dist/render/graph-render.js.map +1 -1
- 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/viewer/render.js +19 -3
- package/dist/viewer/render.js.map +1 -1
- package/dist/viewer/session-reducer.d.ts +45 -0
- package/dist/viewer/session-reducer.js +266 -0
- package/dist/viewer/session-reducer.js.map +1 -0
- package/dist/workflows/artifacts.d.ts +40 -0
- package/dist/workflows/artifacts.js +155 -0
- package/dist/workflows/artifacts.js.map +1 -0
- package/dist/workflows/engine.d.ts +38 -0
- package/dist/workflows/engine.js +281 -20
- 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 +3 -2
- package/dist/workflows/index.js +2 -1
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/loader.d.ts +5 -3
- package/dist/workflows/loader.js +10 -1
- package/dist/workflows/loader.js.map +1 -1
- package/dist/workflows/schema.js +1 -1
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/store.d.ts +98 -10
- package/dist/workflows/store.js +921 -46
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/types.d.ts +136 -3
- package/docs/CONTROLLERS.md +215 -0
- package/docs/development.md +51 -24
- package/docs/live-replay-protocol.md +155 -0
- 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/piw-viewer-experience-implementation-plan.md +674 -0
- package/docs/plans/replayable-run-bundles-implementation-plan.md +65 -0
- package/docs/plans/session-event-replay-implementation-plan.md +494 -0
- package/docs/plans/tui-viewer-implementation-plan.md +64 -0
- package/docs/run-bundles.md +359 -55
- package/docs/session-event-journal.md +470 -0
- package/docs/tui-viewer.md +218 -0
- package/docs/workflows.md +131 -11
- package/examples/controllers/pull-request.controller.ts +215 -0
- package/package.json +11 -2
- package/src/builtins/monitor.workflow.ts +278 -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 +1427 -0
- package/src/controllers/store.ts +160 -0
- package/src/controllers/types.ts +183 -0
- package/src/controllers/workflow-engine-scheduler.ts +145 -0
- package/src/controllers/workflows.ts +152 -0
- package/src/extension/controller-host.ts +163 -0
- package/src/extension/executor.ts +29 -2
- package/src/extension/index.ts +1315 -109
- package/src/extension/recorder.ts +662 -0
- package/src/extension/session-events.ts +119 -0
- package/src/extension/widget.ts +26 -24
- 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 +406 -0
- package/src/render/canvas.ts +19 -1
- package/src/render/graph-render.ts +277 -44
- package/src/viewer/cli.ts +167 -21
- package/src/viewer/render.ts +21 -3
- package/src/viewer/session-reducer.ts +347 -0
- package/src/workflows/artifacts.ts +188 -0
- package/src/workflows/engine.ts +365 -19
- package/src/workflows/errors.ts +45 -0
- package/src/workflows/graph.ts +0 -5
- package/src/workflows/index.ts +15 -0
- package/src/workflows/loader.ts +13 -3
- package/src/workflows/schema.ts +1 -1
- package/src/workflows/store.ts +1157 -48
- package/src/workflows/types.ts +151 -3
package/README.md
CHANGED
|
@@ -29,8 +29,16 @@ Or try the npm package without installing it:
|
|
|
29
29
|
pi -e npm:@osolmaz/pi-workflows
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
Install the interactive terminal viewer separately from crates.io. The crate
|
|
33
|
+
is named `pi-workflows`; its command is `piw`:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cargo install pi-workflows
|
|
37
|
+
piw
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The npm package also includes the simpler `pi-workflows` snapshot viewer. To
|
|
41
|
+
link that command from a clone, run `npm install && npm run build && npm link`,
|
|
34
42
|
or run it in place with `npx tsx src/viewer/cli.ts`.
|
|
35
43
|
|
|
36
44
|
## Quick start
|
|
@@ -63,16 +71,16 @@ Then, from any pi conversation:
|
|
|
63
71
|
```
|
|
64
72
|
|
|
65
73
|
`/workflow` with no arguments lists discovered workflows. `/workflow pause`
|
|
66
|
-
lets the current step finish and then holds the run before the next node
|
|
67
|
-
useful when you want to interject in the conversation mid-workflow
|
|
68
|
-
|
|
74
|
+
lets the current step finish and then holds the run before the next node. This
|
|
75
|
+
is useful when you want to interject in the conversation mid-workflow.
|
|
76
|
+
`/workflow resume` continues it. Pressing escape to interrupt a turn
|
|
69
77
|
pauses the workflow automatically, so the run never nudges the model while
|
|
70
78
|
you have taken the conversation back; `/workflow resume` re-delivers the
|
|
71
79
|
pending step prompt. `/workflow cancel` stops the active run; if the last run
|
|
72
80
|
already ended (for example parked at a checkpoint), it clears the leftover
|
|
73
81
|
widget instead. Trailing text becomes `{ task: "..." }`; pass arbitrary input
|
|
74
|
-
with `--input-json {"key": "value"}`. The names `cancel`, `list`, `pause`,
|
|
75
|
-
|
|
82
|
+
with `--input-json {"key": "value"}`. The names `answer`, `cancel`, `list`, `pause`, `resume`, and `status` are
|
|
83
|
+
reserved and rejected as workflow names.
|
|
76
84
|
|
|
77
85
|
While a run is on screen, the footer status bar shows a compact
|
|
78
86
|
`wf <name> [status] <node>` indicator alongside the widget.
|
|
@@ -82,6 +90,26 @@ structured run ends to request one normal, human-readable assistant response.
|
|
|
82
90
|
Workflows without it remain silent after their final structured output, which
|
|
83
91
|
keeps shell-only and machine-consumed workflows model-free.
|
|
84
92
|
|
|
93
|
+
## Agent-managed workflows
|
|
94
|
+
|
|
95
|
+
The model can use the same `workflow` tool to list, start, inspect, pause,
|
|
96
|
+
resume, cancel, and answer workflows. Step contracts use the tool's `submit`
|
|
97
|
+
action. Slash commands and model actions share one lifecycle implementation.
|
|
98
|
+
|
|
99
|
+
Pi Workflows includes a `monitor` workflow for plain-language requests such as:
|
|
100
|
+
|
|
101
|
+
> Monitor PR 123 every 30 minutes. Report failed checks. Stop when it is merged or closed.
|
|
102
|
+
|
|
103
|
+
The monitor checks immediately, reports only the states requested by the user,
|
|
104
|
+
waits with a normal shell action, and loops until its stop condition or check
|
|
105
|
+
limit. Its input supports `task`, `everyMinutes`, `reportWhen`, `stopWhen`, and
|
|
106
|
+
`maxChecks`. Project and global workflows can replace the built-in `monitor`
|
|
107
|
+
by using the same file name.
|
|
108
|
+
|
|
109
|
+
A monitor occupies the session's one active workflow slot. If its Pi runner
|
|
110
|
+
stops during the shell wait, the run parks and repeats that wait node when a
|
|
111
|
+
runner resumes it.
|
|
112
|
+
|
|
85
113
|
Because the workflow runs in your current conversation, you can have a long
|
|
86
114
|
discussion first and then trigger a workflow that builds on it. The
|
|
87
115
|
`elegant-solution` example does exactly that. It asks the model for the most
|
|
@@ -105,30 +133,37 @@ pi-workflows view --once # print a snapshot and exit (good for scripts)
|
|
|
105
133
|
```
|
|
106
134
|
|
|
107
135
|
The run detail view draws the workflow as a boxed graph, like the acpx replay
|
|
108
|
-
viewer
|
|
109
|
-
|
|
110
|
-
|
|
136
|
+
viewer. Every card has a centered step-name header and a divider above its
|
|
137
|
+
structured metadata. Border characters keep the graph background, the body
|
|
138
|
+
surface begins inside the border, and the header interior uses a separate
|
|
139
|
+
surface. Node type, status, attempts, and timing use compact symbol rows; start
|
|
140
|
+
and terminal markers sit outside the card. Node types have distinct
|
|
141
|
+
semantic colors, active cards use a heavy border, branches carry their case
|
|
142
|
+
labels, the taken path is highlighted, and loops route through a gutter on the
|
|
143
|
+
right back into their target from above. `←/→` scrubs
|
|
111
144
|
backwards and forwards through the recorded steps and re-derives every node's
|
|
112
145
|
status as of that step, with the selected step's full output shown below;
|
|
113
146
|
scrubbing to the end snaps back to following the run live.
|
|
114
147
|
|
|
148
|
+
The Rust `piw` viewer under `tui/` adds a Catppuccin interface, selectable
|
|
149
|
+
themes, centered active-node following, draggable browser and inspector sizes,
|
|
150
|
+
detailed trace and conversation inspection, temporal replay, and reconnecting
|
|
151
|
+
remote viewing. Full cards have one fixed graph-wide size, so streaming,
|
|
152
|
+
selection, timer ticks, and replay never move nodes or edges. Live conversation
|
|
153
|
+
capture shows text, thinking, tool calls, and tool execution as they happen,
|
|
154
|
+
then reconciles settled messages to verbatim Pi entries. See
|
|
155
|
+
[the piw guide](docs/tui-viewer.md).
|
|
156
|
+
|
|
115
157
|
```
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
┗━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
126
|
-
┌─ clean ─┘ └─ issues ─┐ │
|
|
127
|
-
▼ ▼ │
|
|
128
|
-
┌───────────┐ ┌───────────────┐ │
|
|
129
|
-
│ · done │ │ ✓ fix [agent] │ │
|
|
130
|
-
└───────────┘ └───────────────┘ │
|
|
131
|
-
└──────────┘
|
|
158
|
+
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
159
|
+
┃ review ┃
|
|
160
|
+
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
161
|
+
┃ ● agent ◐ running ┃
|
|
162
|
+
┃ ↻ 2 ◷ 12s ┃
|
|
163
|
+
┃ ◇ clean ┃
|
|
164
|
+
┃ ◇ issues_found ┃
|
|
165
|
+
┃ … reviewing implementation ┃
|
|
166
|
+
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
132
167
|
```
|
|
133
168
|
|
|
134
169
|
Inside pi, a widget above the editor shows the same boxed graph while a
|
|
@@ -153,6 +188,48 @@ the model to pick from a fixed set of choices and validates the answer, and
|
|
|
153
188
|
See [docs/workflows.md](docs/workflows.md) for the full authoring reference
|
|
154
189
|
and [docs/run-bundles.md](docs/run-bundles.md) for the on-disk run format.
|
|
155
190
|
|
|
191
|
+
## Controllers
|
|
192
|
+
|
|
193
|
+
Controllers keep long-running automation aligned with current external state. They store desired state in `spec`, report observed state through conditions and `status`, and reconcile a deduplicated resource key whenever an event or retry makes it ready.
|
|
194
|
+
|
|
195
|
+
Put `*.controller.ts` files in `.pi/controllers/` or `~/.pi/agent/controllers/`. Import the API from `@osolmaz/pi-workflows/controllers`:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { conditionTrue, defineController } from "@osolmaz/pi-workflows/controllers";
|
|
199
|
+
|
|
200
|
+
export default defineController({
|
|
201
|
+
name: "example",
|
|
202
|
+
initialStatus: () => ({ phase: "new" }),
|
|
203
|
+
reconcile: (ctx, resource) =>
|
|
204
|
+
ctx.settled({
|
|
205
|
+
controllerStatus: { phase: "done" },
|
|
206
|
+
conditions: [conditionTrue("Ready", "Complete")],
|
|
207
|
+
}),
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Apply and inspect resources from Pi:
|
|
212
|
+
|
|
213
|
+
```text
|
|
214
|
+
/controller apply example item-1 {"enabled":true}
|
|
215
|
+
/controller get example item-1
|
|
216
|
+
/controller reconcile example item-1
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The standalone CLI provides read-only views with `pi-workflows controllers` and `pi-workflows controller <controller> <key>`. See [docs/CONTROLLERS.md](docs/CONTROLLERS.md) for reconciliation, queue, effect, and child workflow semantics.
|
|
220
|
+
|
|
221
|
+
## Always-on workflows
|
|
222
|
+
|
|
223
|
+
Runs do not depend on the Pi window. Every `/workflow` run is claimed through a durable queue, so closing Pi mid-run **parks** the run instead of cancelling it, and any runner can **resume** it later at the node it stopped on. Reopening Pi resumes parked runs itself and catches the session up through the shared event feed; a checkpointed run waits durably until you answer it with `/workflow answer <json>`, which continues the graph in a linked run.
|
|
224
|
+
|
|
225
|
+
For runs that must continue while Pi is closed, keep the standalone host running:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
pi-workflows host --project /path/to/project
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The host claims parked runs and reconciles controllers without a Pi session. Conversation nodes execute in headless `pi --mode rpc` children that expose the same `workflow` tool contract. It is a foreground process — stop it with Ctrl-C; a crashed host's leftovers are reaped by the next one. See [docs/workflows.md](docs/workflows.md#durable-runs-parking-and-resume) for the model and [docs/run-bundles.md](docs/run-bundles.md) for the on-disk rules.
|
|
232
|
+
|
|
156
233
|
## Examples
|
|
157
234
|
|
|
158
235
|
The [examples/workflows/](examples/workflows/) directory mirrors the acpx
|
|
@@ -177,6 +254,10 @@ example set. Copy any of them into `.pi/workflows/` to use them:
|
|
|
177
254
|
result plateaus or a diverse generation all fails, then conclusions are
|
|
178
255
|
written before the winner is promoted out of the loop directory.
|
|
179
256
|
|
|
257
|
+
The controller example at `examples/controllers/pull-request.controller.ts`
|
|
258
|
+
shows child repair work and check polling. It also uses expected-head guards
|
|
259
|
+
and recoverable merge effects.
|
|
260
|
+
|
|
180
261
|
## License
|
|
181
262
|
|
|
182
263
|
[MIT](LICENSE)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
name: string;
|
|
3
|
+
title: ({ input }: {
|
|
4
|
+
input: unknown;
|
|
5
|
+
workflowName: string;
|
|
6
|
+
}) => string;
|
|
7
|
+
presentationPrompt: ({ finalOutput }: import("../workflows/types.js").WorkflowPresentationContext) => string | undefined;
|
|
8
|
+
startAt: string;
|
|
9
|
+
maxSteps: number;
|
|
10
|
+
nodes: {
|
|
11
|
+
prepare: import("../workflows/types.js").ComputeNodeDefinition;
|
|
12
|
+
guard: import("../workflows/types.js").ComputeNodeDefinition;
|
|
13
|
+
continue_guard: import("../workflows/types.js").ComputeNodeDefinition;
|
|
14
|
+
check: import("../workflows/types.js").AgentNodeDefinition;
|
|
15
|
+
report_continue: import("../workflows/types.js").AgentNodeDefinition;
|
|
16
|
+
report_stop: import("../workflows/types.js").AgentNodeDefinition;
|
|
17
|
+
sleep: import("../workflows/types.js").ShellActionNodeDefinition;
|
|
18
|
+
finish: import("../workflows/types.js").ComputeNodeDefinition;
|
|
19
|
+
};
|
|
20
|
+
edges: ({
|
|
21
|
+
from: string;
|
|
22
|
+
to: string;
|
|
23
|
+
switch?: never;
|
|
24
|
+
} | {
|
|
25
|
+
to?: never;
|
|
26
|
+
from: string;
|
|
27
|
+
switch: {
|
|
28
|
+
on: string;
|
|
29
|
+
cases: {
|
|
30
|
+
check: string;
|
|
31
|
+
stop: string;
|
|
32
|
+
continue_quiet?: never;
|
|
33
|
+
continue_report?: never;
|
|
34
|
+
stop_quiet?: never;
|
|
35
|
+
stop_report?: never;
|
|
36
|
+
sleep?: never;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
} | {
|
|
40
|
+
to?: never;
|
|
41
|
+
from: string;
|
|
42
|
+
switch: {
|
|
43
|
+
on: string;
|
|
44
|
+
cases: {
|
|
45
|
+
check?: never;
|
|
46
|
+
continue_quiet: string;
|
|
47
|
+
continue_report: string;
|
|
48
|
+
stop_quiet: string;
|
|
49
|
+
stop_report: string;
|
|
50
|
+
sleep?: never;
|
|
51
|
+
stop?: never;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
} | {
|
|
55
|
+
to?: never;
|
|
56
|
+
from: string;
|
|
57
|
+
switch: {
|
|
58
|
+
on: string;
|
|
59
|
+
cases: {
|
|
60
|
+
check?: never;
|
|
61
|
+
continue_quiet?: never;
|
|
62
|
+
continue_report?: never;
|
|
63
|
+
stop_quiet?: never;
|
|
64
|
+
stop_report?: never;
|
|
65
|
+
sleep: string;
|
|
66
|
+
stop: string;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
})[];
|
|
70
|
+
};
|
|
71
|
+
export default _default;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { agent, compute, defineWorkflow, shell } from "../workflows/index.js";
|
|
2
|
+
const MIN_INTERVAL_MINUTES = 1;
|
|
3
|
+
const MAX_INTERVAL_MINUTES = 24 * 60;
|
|
4
|
+
const DEFAULT_MAX_CHECKS = 1_000;
|
|
5
|
+
const MAX_CHECKS = 1_000;
|
|
6
|
+
const MAX_OBSERVATION_CHARS = 8_000;
|
|
7
|
+
const MAX_REPORT_CHARS = 4_000;
|
|
8
|
+
const MAX_REASON_CHARS = 2_000;
|
|
9
|
+
const SLEEP_TIMEOUT_MARGIN_MS = 60_000;
|
|
10
|
+
const NODE_TIMEOUT_MARGIN_MS = 2 * 60_000;
|
|
11
|
+
const MONITOR_ROUTES = new Set([
|
|
12
|
+
"continue_quiet",
|
|
13
|
+
"continue_report",
|
|
14
|
+
"stop_quiet",
|
|
15
|
+
"stop_report",
|
|
16
|
+
]);
|
|
17
|
+
function requireRecord(value, label) {
|
|
18
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
19
|
+
throw new Error(`${label} must be an object`);
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
function requireBoundedString(value, label, maxChars) {
|
|
24
|
+
if (typeof value !== "string") {
|
|
25
|
+
throw new Error(`${label} must be a string`);
|
|
26
|
+
}
|
|
27
|
+
const trimmed = value.trim();
|
|
28
|
+
if (trimmed.length === 0) {
|
|
29
|
+
throw new Error(`${label} must not be empty`);
|
|
30
|
+
}
|
|
31
|
+
if (trimmed.length > maxChars) {
|
|
32
|
+
throw new Error(`${label} must be at most ${maxChars} characters`);
|
|
33
|
+
}
|
|
34
|
+
return trimmed;
|
|
35
|
+
}
|
|
36
|
+
function prepareInput(input) {
|
|
37
|
+
const value = requireRecord(input, "monitor input");
|
|
38
|
+
const task = requireBoundedString(value.task, "task", 8_000);
|
|
39
|
+
if (typeof value.everyMinutes !== "number" ||
|
|
40
|
+
!Number.isInteger(value.everyMinutes) ||
|
|
41
|
+
value.everyMinutes < MIN_INTERVAL_MINUTES ||
|
|
42
|
+
value.everyMinutes > MAX_INTERVAL_MINUTES) {
|
|
43
|
+
throw new Error(`everyMinutes must be an integer from ${MIN_INTERVAL_MINUTES} through ${MAX_INTERVAL_MINUTES}`);
|
|
44
|
+
}
|
|
45
|
+
const maxChecks = value.maxChecks ?? DEFAULT_MAX_CHECKS;
|
|
46
|
+
if (!Number.isInteger(maxChecks) || maxChecks <= 0 || maxChecks > MAX_CHECKS) {
|
|
47
|
+
throw new Error(`maxChecks must be an integer from 1 through ${MAX_CHECKS}`);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
task,
|
|
51
|
+
everyMinutes: value.everyMinutes,
|
|
52
|
+
reportWhen: value.reportWhen === undefined
|
|
53
|
+
? "The observed state changes materially or needs the user's attention."
|
|
54
|
+
: requireBoundedString(value.reportWhen, "reportWhen", 4_000),
|
|
55
|
+
stopWhen: value.stopWhen === undefined
|
|
56
|
+
? "The user cancels the monitor or it reaches its maximum check count."
|
|
57
|
+
: requireBoundedString(value.stopWhen, "stopWhen", 4_000),
|
|
58
|
+
maxChecks,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function configFrom(outputs) {
|
|
62
|
+
return outputs.prepare;
|
|
63
|
+
}
|
|
64
|
+
function completedChecks(context) {
|
|
65
|
+
return context.state.steps.filter((step) => step.nodeId === "check" && step.outcome === "ok")
|
|
66
|
+
.length;
|
|
67
|
+
}
|
|
68
|
+
function validateCheck(output) {
|
|
69
|
+
const value = requireRecord(output, "monitor check output");
|
|
70
|
+
if (typeof value.route !== "string" || !MONITOR_ROUTES.has(value.route)) {
|
|
71
|
+
throw new Error(`route must be one of ${[...MONITOR_ROUTES].join(", ")}`);
|
|
72
|
+
}
|
|
73
|
+
const route = value.route;
|
|
74
|
+
const observation = requireBoundedString(value.observation, "observation", MAX_OBSERVATION_CHARS);
|
|
75
|
+
const reason = requireBoundedString(value.reason, "reason", MAX_REASON_CHARS);
|
|
76
|
+
const reports = route === "continue_report" || route === "stop_report";
|
|
77
|
+
const report = value.report === undefined
|
|
78
|
+
? undefined
|
|
79
|
+
: requireBoundedString(value.report, "report", MAX_REPORT_CHARS);
|
|
80
|
+
if (reports && report === undefined) {
|
|
81
|
+
throw new Error(`route ${route} requires a report`);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
route,
|
|
85
|
+
observation,
|
|
86
|
+
...(report !== undefined ? { report } : {}),
|
|
87
|
+
reason,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function validateReportAck(output) {
|
|
91
|
+
const value = requireRecord(output, "report acknowledgement");
|
|
92
|
+
if (value.reported !== true) {
|
|
93
|
+
throw new Error("report acknowledgement must set reported to true");
|
|
94
|
+
}
|
|
95
|
+
return { reported: true };
|
|
96
|
+
}
|
|
97
|
+
function reportPrompt(outputs) {
|
|
98
|
+
const check = outputs.check;
|
|
99
|
+
return [
|
|
100
|
+
"Write one concise normal assistant message to the user with this monitoring update:",
|
|
101
|
+
check.report ?? check.observation,
|
|
102
|
+
"Do not add unrelated detail.",
|
|
103
|
+
"After writing the update, submit the acknowledgement required by the workflow step contract.",
|
|
104
|
+
].join("\n\n");
|
|
105
|
+
}
|
|
106
|
+
export default defineWorkflow({
|
|
107
|
+
name: "monitor",
|
|
108
|
+
title: ({ input }) => {
|
|
109
|
+
try {
|
|
110
|
+
const task = prepareInput(input).task;
|
|
111
|
+
return `monitor: ${task.slice(0, 80)}`;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return "monitor";
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
presentationPrompt: ({ finalOutput }) => {
|
|
118
|
+
const result = requireRecord(finalOutput, "monitor result");
|
|
119
|
+
if (result.reported === true) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
return `Tell the user concisely why this monitor stopped: ${String(result.reason ?? "monitor ended")}`;
|
|
123
|
+
},
|
|
124
|
+
startAt: "prepare",
|
|
125
|
+
maxSteps: 5_010,
|
|
126
|
+
nodes: {
|
|
127
|
+
prepare: compute({
|
|
128
|
+
run: ({ input }) => prepareInput(input),
|
|
129
|
+
}),
|
|
130
|
+
guard: compute({
|
|
131
|
+
run: (context) => {
|
|
132
|
+
const config = configFrom(context.outputs);
|
|
133
|
+
const checks = completedChecks(context);
|
|
134
|
+
return checks >= config.maxChecks
|
|
135
|
+
? { route: "stop", checks, reason: `Reached the ${config.maxChecks}-check limit.` }
|
|
136
|
+
: { route: "check", checks };
|
|
137
|
+
},
|
|
138
|
+
}),
|
|
139
|
+
continue_guard: compute({
|
|
140
|
+
run: (context) => {
|
|
141
|
+
const config = configFrom(context.outputs);
|
|
142
|
+
const checks = completedChecks(context);
|
|
143
|
+
return checks >= config.maxChecks
|
|
144
|
+
? { route: "stop", checks, reason: `Reached the ${config.maxChecks}-check limit.` }
|
|
145
|
+
: { route: "sleep", checks };
|
|
146
|
+
},
|
|
147
|
+
}),
|
|
148
|
+
check: agent({
|
|
149
|
+
statusDetail: "checking monitored target",
|
|
150
|
+
prompt: (context) => {
|
|
151
|
+
const config = configFrom(context.outputs);
|
|
152
|
+
const previous = context.outputs.check;
|
|
153
|
+
const checkNumber = completedChecks(context) + 1;
|
|
154
|
+
return [
|
|
155
|
+
`Perform monitoring check ${checkNumber} of at most ${config.maxChecks}.`,
|
|
156
|
+
`Task: ${config.task}`,
|
|
157
|
+
`Report when: ${config.reportWhen}`,
|
|
158
|
+
`Stop when: ${config.stopWhen}`,
|
|
159
|
+
previous === undefined
|
|
160
|
+
? "There is no previous observation. Report the initial state only when the report condition calls for it."
|
|
161
|
+
: `Previous accepted observation: ${previous.observation}`,
|
|
162
|
+
"Use available tools to inspect the current state. Observe only unless the task explicitly authorizes a mutation.",
|
|
163
|
+
"Choose continue_quiet, continue_report, stop_quiet, or stop_report. A report route requires concise report text.",
|
|
164
|
+
].join("\n\n");
|
|
165
|
+
},
|
|
166
|
+
expectedOutput: '{ "route": "continue_quiet" | "continue_report" | "stop_quiet" | "stop_report", "observation": "current factual state", "report": "required for report routes", "reason": "short reason" }',
|
|
167
|
+
validate: (output) => validateCheck(output),
|
|
168
|
+
}),
|
|
169
|
+
report_continue: agent({
|
|
170
|
+
statusDetail: "reporting monitor update",
|
|
171
|
+
prompt: ({ outputs }) => reportPrompt(outputs),
|
|
172
|
+
expectedOutput: '{ "reported": true }',
|
|
173
|
+
validate: (output) => validateReportAck(output),
|
|
174
|
+
}),
|
|
175
|
+
report_stop: agent({
|
|
176
|
+
statusDetail: "reporting final monitor update",
|
|
177
|
+
prompt: ({ outputs }) => reportPrompt(outputs),
|
|
178
|
+
expectedOutput: '{ "reported": true }',
|
|
179
|
+
validate: (output) => validateReportAck(output),
|
|
180
|
+
}),
|
|
181
|
+
sleep: shell({
|
|
182
|
+
statusDetail: "waiting for next monitor check",
|
|
183
|
+
timeoutMs: MAX_INTERVAL_MINUTES * 60_000 + NODE_TIMEOUT_MARGIN_MS,
|
|
184
|
+
exec: ({ outputs }) => {
|
|
185
|
+
const config = configFrom(outputs);
|
|
186
|
+
const sleepMs = config.everyMinutes * 60_000;
|
|
187
|
+
return {
|
|
188
|
+
command: process.execPath,
|
|
189
|
+
args: ["-e", "setTimeout(() => {}, Number(process.argv[1]))", String(sleepMs)],
|
|
190
|
+
timeoutMs: sleepMs + SLEEP_TIMEOUT_MARGIN_MS,
|
|
191
|
+
maxOutputChars: 1_024,
|
|
192
|
+
};
|
|
193
|
+
},
|
|
194
|
+
parse: (_result, { outputs }) => ({ waitedMinutes: configFrom(outputs).everyMinutes }),
|
|
195
|
+
}),
|
|
196
|
+
finish: compute({
|
|
197
|
+
run: ({ outputs }) => {
|
|
198
|
+
const check = outputs.check;
|
|
199
|
+
const guard = outputs.guard;
|
|
200
|
+
const continueGuard = outputs.continue_guard;
|
|
201
|
+
return {
|
|
202
|
+
reason: continueGuard?.reason ?? guard?.reason ?? check?.reason ?? "Monitor finished.",
|
|
203
|
+
observation: check?.observation ?? null,
|
|
204
|
+
reported: outputs.report_stop !== undefined ||
|
|
205
|
+
(check !== undefined && check.route === "stop_report"),
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
},
|
|
210
|
+
edges: [
|
|
211
|
+
{ from: "prepare", to: "guard" },
|
|
212
|
+
{ from: "guard", switch: { on: "$.route", cases: { check: "check", stop: "finish" } } },
|
|
213
|
+
{
|
|
214
|
+
from: "check",
|
|
215
|
+
switch: {
|
|
216
|
+
on: "$.route",
|
|
217
|
+
cases: {
|
|
218
|
+
continue_quiet: "continue_guard",
|
|
219
|
+
continue_report: "report_continue",
|
|
220
|
+
stop_quiet: "finish",
|
|
221
|
+
stop_report: "report_stop",
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{ from: "report_continue", to: "continue_guard" },
|
|
226
|
+
{ from: "report_stop", to: "finish" },
|
|
227
|
+
{
|
|
228
|
+
from: "continue_guard",
|
|
229
|
+
switch: { on: "$.route", cases: { sleep: "sleep", stop: "finish" } },
|
|
230
|
+
},
|
|
231
|
+
{ from: "sleep", to: "guard" },
|
|
232
|
+
],
|
|
233
|
+
});
|
|
234
|
+
//# sourceMappingURL=monitor.workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitor.workflow.js","sourceRoot":"","sources":["../../src/builtins/monitor.workflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ControllerCondition, ControllerConditionInput, ControllerResourceStatus, ControllerStatusPatch } from "./types.js";
|
|
2
|
+
export declare function conditionTrue(type: string, reason: string, message?: string): ControllerConditionInput;
|
|
3
|
+
export declare function conditionFalse(type: string, reason: string, message?: string): ControllerConditionInput;
|
|
4
|
+
export declare function conditionUnknown(type: string, reason: string, message?: string): ControllerConditionInput;
|
|
5
|
+
export declare function applyStatusPatch<TStatus>(current: ControllerResourceStatus<TStatus>, patch: ControllerStatusPatch<TStatus> | undefined, generation: number, now: string): ControllerResourceStatus<TStatus>;
|
|
6
|
+
export declare function mergeConditions(current: ControllerCondition[], updates: ControllerConditionInput[], generation: number, now: string): ControllerCondition[];
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export function conditionTrue(type, reason, message) {
|
|
2
|
+
return condition(type, true, reason, message);
|
|
3
|
+
}
|
|
4
|
+
export function conditionFalse(type, reason, message) {
|
|
5
|
+
return condition(type, false, reason, message);
|
|
6
|
+
}
|
|
7
|
+
export function conditionUnknown(type, reason, message) {
|
|
8
|
+
return condition(type, "unknown", reason, message);
|
|
9
|
+
}
|
|
10
|
+
export function applyStatusPatch(current, patch, generation, now) {
|
|
11
|
+
return {
|
|
12
|
+
observedGeneration: generation,
|
|
13
|
+
conditions: mergeConditions(current.conditions, patch?.conditions ?? [], generation, now),
|
|
14
|
+
controllerStatus: patch !== undefined && Object.hasOwn(patch, "controllerStatus")
|
|
15
|
+
? patch.controllerStatus
|
|
16
|
+
: current.controllerStatus,
|
|
17
|
+
...(patch?.workflowRun === null
|
|
18
|
+
? {}
|
|
19
|
+
: patch?.workflowRun !== undefined
|
|
20
|
+
? { workflowRun: patch.workflowRun }
|
|
21
|
+
: current.workflowRun !== undefined
|
|
22
|
+
? { workflowRun: current.workflowRun }
|
|
23
|
+
: {}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function mergeConditions(current, updates, generation, now) {
|
|
27
|
+
const byType = new Map(current.map((item) => [item.type, item]));
|
|
28
|
+
const seen = new Set();
|
|
29
|
+
for (const update of updates) {
|
|
30
|
+
validateCondition(update);
|
|
31
|
+
if (seen.has(update.type)) {
|
|
32
|
+
throw new Error(`Condition ${JSON.stringify(update.type)} was updated more than once`);
|
|
33
|
+
}
|
|
34
|
+
seen.add(update.type);
|
|
35
|
+
const previous = byType.get(update.type);
|
|
36
|
+
const next = {
|
|
37
|
+
...update,
|
|
38
|
+
observedGeneration: generation,
|
|
39
|
+
lastTransitionTime: previous !== undefined && previous.status === update.status
|
|
40
|
+
? previous.lastTransitionTime
|
|
41
|
+
: now,
|
|
42
|
+
};
|
|
43
|
+
byType.set(update.type, next);
|
|
44
|
+
}
|
|
45
|
+
return [...byType.values()];
|
|
46
|
+
}
|
|
47
|
+
function condition(type, status, reason, message) {
|
|
48
|
+
const value = {
|
|
49
|
+
type,
|
|
50
|
+
status,
|
|
51
|
+
reason,
|
|
52
|
+
...(message !== undefined ? { message } : {}),
|
|
53
|
+
};
|
|
54
|
+
validateCondition(value);
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
function validateCondition(value) {
|
|
58
|
+
if (value.type.trim().length === 0) {
|
|
59
|
+
throw new Error("Condition type must not be empty");
|
|
60
|
+
}
|
|
61
|
+
if (value.reason.trim().length === 0) {
|
|
62
|
+
throw new Error("Condition reason must not be empty");
|
|
63
|
+
}
|
|
64
|
+
if (value.status !== true && value.status !== false && value.status !== "unknown") {
|
|
65
|
+
throw new Error(`Invalid condition status for ${JSON.stringify(value.type)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=conditions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditions.js","sourceRoot":"","sources":["../../src/controllers/conditions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AnyControllerDefinition, ControllerDefinition, ControllerResource } from "./types.js";
|
|
2
|
+
export declare function defineController<TSpec, TStatus>(definition: ControllerDefinition<TSpec, TStatus>): ControllerDefinition<TSpec, TStatus>;
|
|
3
|
+
export declare function isControllerDefinition(value: unknown): value is AnyControllerDefinition;
|
|
4
|
+
export declare function assertValidControllerDefinition<TSpec, TStatus>(definition: ControllerDefinition<TSpec, TStatus>): void;
|
|
5
|
+
export declare function asAnyControllerDefinition<TSpec, TStatus>(definition: ControllerDefinition<TSpec, TStatus>): AnyControllerDefinition;
|
|
6
|
+
export declare function asTypedResource<TSpec, TStatus>(resource: ControllerResource): ControllerResource<TSpec, TStatus>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const CONTROLLER_DEFINITION_BRAND = Symbol.for("pi-workflows.controller-definition");
|
|
2
|
+
const CONTROLLER_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
3
|
+
export function defineController(definition) {
|
|
4
|
+
assertValidControllerDefinition(definition);
|
|
5
|
+
if (isControllerDefinition(definition)) {
|
|
6
|
+
return definition;
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(definition, CONTROLLER_DEFINITION_BRAND, {
|
|
9
|
+
value: true,
|
|
10
|
+
enumerable: false,
|
|
11
|
+
configurable: false,
|
|
12
|
+
writable: false,
|
|
13
|
+
});
|
|
14
|
+
return definition;
|
|
15
|
+
}
|
|
16
|
+
export function isControllerDefinition(value) {
|
|
17
|
+
return (value !== null &&
|
|
18
|
+
typeof value === "object" &&
|
|
19
|
+
value[CONTROLLER_DEFINITION_BRAND] === true);
|
|
20
|
+
}
|
|
21
|
+
export function assertValidControllerDefinition(definition) {
|
|
22
|
+
if (definition === null || typeof definition !== "object") {
|
|
23
|
+
throw new Error("Invalid controller definition: expected an object");
|
|
24
|
+
}
|
|
25
|
+
if (!CONTROLLER_NAME_PATTERN.test(definition.name)) {
|
|
26
|
+
throw new Error(`Invalid controller definition: name ${JSON.stringify(definition.name)} must match ${CONTROLLER_NAME_PATTERN.source}`);
|
|
27
|
+
}
|
|
28
|
+
if (typeof definition.initialStatus !== "function") {
|
|
29
|
+
throw new Error("Invalid controller definition: initialStatus must be a function");
|
|
30
|
+
}
|
|
31
|
+
if (typeof definition.reconcile !== "function") {
|
|
32
|
+
throw new Error("Invalid controller definition: reconcile must be a function");
|
|
33
|
+
}
|
|
34
|
+
if (definition.timeoutMs !== undefined &&
|
|
35
|
+
(!Number.isSafeInteger(definition.timeoutMs) || definition.timeoutMs <= 0)) {
|
|
36
|
+
throw new Error("Invalid controller definition: timeoutMs must be a positive safe integer");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function asAnyControllerDefinition(definition) {
|
|
40
|
+
return definition;
|
|
41
|
+
}
|
|
42
|
+
export function asTypedResource(resource) {
|
|
43
|
+
return resource;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=definition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definition.js","sourceRoot":"","sources":["../../src/controllers/definition.ts"],"names":[],"mappings":"AAEA,MAAM,2BAA2B,GAAG,MAAM,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;AACrF,MAAM,uBAAuB,GAAG,mBAAmB,CAAC;AAEpD,MAAM,UAAU,gBAAgB,CAC9B,UAAgD;IAEhD,+BAA+B,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,sBAAsB,CAAC,UAAU,CAAC,EAAE,CAAC;QACvC,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,2BAA2B,EAAE;QAC7D,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,KAAK;QACjB,YAAY,EAAE,KAAK;QACnB,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;IACH,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,OAAO,CACL,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,KAAK,QAAQ;QACxB,KAAsC,CAAC,2BAA2B,CAAC,KAAK,IAAI,CAC9E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC7C,UAAgD;IAEhD,IAAI,UAAU,KAAK,IAAI,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,uCAAuC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,eAAe,uBAAuB,CAAC,MAAM,EAAE,CACtH,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,UAAU,CAAC,aAAa,KAAK,UAAU,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,OAAO,UAAU,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IACD,IACE,UAAU,CAAC,SAAS,KAAK,SAAS;QAClC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,UAAU,CAAC,SAAS,IAAI,CAAC,CAAC,EAC1E,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC9F,CAAC;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,UAAgD;IAEhD,OAAO,UAA+D,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,QAA4B;IAE5B,OAAO,QAA8C,CAAC;AACxD,CAAC"}
|