@mhingston5/lasso 0.1.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 +707 -0
- package/docs/agent-wrangling.png +0 -0
- package/package.json +26 -0
- package/src/capabilities/matcher.ts +25 -0
- package/src/capabilities/registry.ts +103 -0
- package/src/capabilities/types.ts +15 -0
- package/src/cir/lower.ts +253 -0
- package/src/cir/optimize.ts +251 -0
- package/src/cir/types.ts +131 -0
- package/src/cir/validate.ts +265 -0
- package/src/compiler/compile.ts +601 -0
- package/src/compiler/feedback.ts +471 -0
- package/src/compiler/runtime-helpers.ts +455 -0
- package/src/composition/chain.ts +58 -0
- package/src/composition/conditional.ts +76 -0
- package/src/composition/parallel.ts +75 -0
- package/src/composition/types.ts +105 -0
- package/src/environment/analyzer.ts +56 -0
- package/src/environment/discovery.ts +179 -0
- package/src/environment/types.ts +68 -0
- package/src/failures/classifiers.ts +134 -0
- package/src/failures/generator.ts +421 -0
- package/src/failures/map-reference-failures.ts +23 -0
- package/src/failures/ontology.ts +210 -0
- package/src/failures/recovery.ts +214 -0
- package/src/failures/types.ts +14 -0
- package/src/index.ts +67 -0
- package/src/memory/advisor.ts +132 -0
- package/src/memory/extractor.ts +166 -0
- package/src/memory/store.ts +107 -0
- package/src/memory/types.ts +53 -0
- package/src/metaharness/engine.ts +256 -0
- package/src/metaharness/predictor.ts +168 -0
- package/src/metaharness/types.ts +40 -0
- package/src/mutation/derive.ts +308 -0
- package/src/mutation/diff.ts +52 -0
- package/src/mutation/engine.ts +256 -0
- package/src/mutation/types.ts +84 -0
- package/src/pi/command-input.ts +209 -0
- package/src/pi/commands.ts +351 -0
- package/src/pi/extension.ts +16 -0
- package/src/planner/synthesize.ts +83 -0
- package/src/planner/template-rules.ts +183 -0
- package/src/planner/types.ts +42 -0
- package/src/reference/catalog.ts +128 -0
- package/src/reference/patch-validation-strategies.ts +170 -0
- package/src/reference/patch-validation.ts +174 -0
- package/src/reference/pr-review-merge.ts +155 -0
- package/src/reference/strategies.ts +126 -0
- package/src/reference/types.ts +33 -0
- package/src/replanner/risk-rules.ts +161 -0
- package/src/replanner/runtime.ts +308 -0
- package/src/replanner/synthesize.ts +619 -0
- package/src/replanner/types.ts +73 -0
- package/src/spec/schema.ts +254 -0
- package/src/spec/types.ts +319 -0
- package/src/spec/validate.ts +296 -0
- package/src/state/snapshots.ts +43 -0
- package/src/state/types.ts +12 -0
- package/src/synthesis/graph-builder.ts +267 -0
- package/src/synthesis/harness-builder.ts +113 -0
- package/src/synthesis/intent-ir.ts +63 -0
- package/src/synthesis/policy-builder.ts +320 -0
- package/src/synthesis/risk-analyzer.ts +182 -0
- package/src/synthesis/skill-parser.ts +441 -0
- package/src/verification/engine.ts +230 -0
- package/src/versioning/file-store.ts +103 -0
- package/src/versioning/history.ts +43 -0
- package/src/versioning/store.ts +16 -0
- package/src/versioning/types.ts +31 -0
- package/test/capabilities/matcher.test.ts +67 -0
- package/test/capabilities/registry.test.ts +136 -0
- package/test/capabilities/synthesis.test.ts +264 -0
- package/test/cir/lower.test.ts +417 -0
- package/test/cir/optimize.test.ts +266 -0
- package/test/cir/validate.test.ts +368 -0
- package/test/compiler/adaptive-runtime.test.ts +157 -0
- package/test/compiler/compile.test.ts +1198 -0
- package/test/compiler/feedback.test.ts +784 -0
- package/test/compiler/guardrails.test.ts +191 -0
- package/test/compiler/trace.test.ts +404 -0
- package/test/composition/chain.test.ts +328 -0
- package/test/composition/conditional.test.ts +241 -0
- package/test/composition/parallel.test.ts +215 -0
- package/test/environment/analyzer.test.ts +204 -0
- package/test/environment/discovery.test.ts +149 -0
- package/test/failures/classifiers.test.ts +287 -0
- package/test/failures/generator.test.ts +203 -0
- package/test/failures/ontology.test.ts +439 -0
- package/test/failures/recovery.test.ts +300 -0
- package/test/helpers/createFixtureRepo.ts +84 -0
- package/test/helpers/createPatchValidationFixture.ts +144 -0
- package/test/helpers/runCompiledWorkflow.ts +208 -0
- package/test/memory/advisor.test.ts +332 -0
- package/test/memory/extractor.test.ts +295 -0
- package/test/memory/store.test.ts +244 -0
- package/test/metaharness/engine.test.ts +575 -0
- package/test/metaharness/predictor.test.ts +436 -0
- package/test/mutation/derive-failure.test.ts +209 -0
- package/test/mutation/engine.test.ts +622 -0
- package/test/package-smoke.test.ts +29 -0
- package/test/pi/command-input.test.ts +153 -0
- package/test/pi/commands.test.ts +623 -0
- package/test/planner/classify-template.test.ts +32 -0
- package/test/planner/synthesize.test.ts +901 -0
- package/test/reference/PatchValidation.failures.test.ts +137 -0
- package/test/reference/PatchValidation.test.ts +326 -0
- package/test/reference/PrReviewMerge.failures.test.ts +121 -0
- package/test/reference/PrReviewMerge.test.ts +55 -0
- package/test/reference/catalog-open.test.ts +70 -0
- package/test/replanner/runtime.test.ts +207 -0
- package/test/replanner/synthesize.test.ts +303 -0
- package/test/spec/validate.test.ts +1056 -0
- package/test/state/snapshots.test.ts +264 -0
- package/test/synthesis/custom-workflow.test.ts +264 -0
- package/test/synthesis/graph-builder.test.ts +370 -0
- package/test/synthesis/harness-builder.test.ts +128 -0
- package/test/synthesis/policy-builder.test.ts +149 -0
- package/test/synthesis/risk-analyzer.test.ts +230 -0
- package/test/synthesis/skill-parser.test.ts +796 -0
- package/test/verification/engine.test.ts +509 -0
- package/test/versioning/history.test.ts +144 -0
- package/test/versioning/store.test.ts +254 -0
- package/vitest.config.ts +9 -0
package/README.md
ADDED
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
# Lasso
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="docs/agent-wrangling.png" width="320" alt="Agent Wrangling" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
Lasso is a dynamic harness engine built on `pi-duroxide`. It goes from intent to
|
|
8
|
+
executable workflow — and repairs the harness while it runs.
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
Intent
|
|
12
|
+
→ Environment discovery (tools, resources, constraints)
|
|
13
|
+
→ Memory query (past patterns, what worked/failed)
|
|
14
|
+
→ Graph synthesis (planner + capabilities)
|
|
15
|
+
→ Failure prediction (auth, tool, network, resource)
|
|
16
|
+
→ Policy synthesis (mutations: add verification, retry, approval)
|
|
17
|
+
→ Compilation (validate → lower → optimize → execute)
|
|
18
|
+
→ Runtime adaptation (trace → mutate → continueAsNew)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If `pi-duroxide` is the durable runtime engine, **Lasso is the layer that
|
|
22
|
+
generates, optimizes, and repairs the harnesses that run on it**.
|
|
23
|
+
|
|
24
|
+
## What is Lasso?
|
|
25
|
+
|
|
26
|
+
Lasso is a **pi coding agent extension** — a TypeScript package that plugs into
|
|
27
|
+
pi via the `pi` field in `package.json`. When installed, it:
|
|
28
|
+
|
|
29
|
+
1. Boots `pi-duroxide` (the durable workflow runtime)
|
|
30
|
+
2. Registers 5 slash commands (`/lasso:plan`, `/lasso:run`, etc.)
|
|
31
|
+
3. Exports a library API for programmatic use
|
|
32
|
+
|
|
33
|
+
There are two ways to use it:
|
|
34
|
+
|
|
35
|
+
| Mode | How | When |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| **Chat mode** | Slash commands inside pi's coding agent UI | Interactive workflow planning and execution |
|
|
38
|
+
| **Library mode** | `import { compileHarnessSpec } from "lasso"` | Building custom tooling, CI pipelines, or other extensions |
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
41
|
+
|
|
42
|
+
### Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# From this repository
|
|
46
|
+
pi install .
|
|
47
|
+
|
|
48
|
+
# Or from npm (once published)
|
|
49
|
+
pi install @mhingston5/lasso
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Chat mode (inside pi)
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# 1. Plan a workflow from a freeform brief
|
|
56
|
+
/lasso:plan Validate that the bug fix in fix.patch works against main
|
|
57
|
+
|
|
58
|
+
# 2. Run it (paste the JSON output from step 1)
|
|
59
|
+
/lasso:run {"workflow":"patch-validation","input":{...}}
|
|
60
|
+
|
|
61
|
+
# 3. Inspect what happened
|
|
62
|
+
/lasso:inspect
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Library mode (TypeScript)
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { compileHarnessSpec, mutateHarness, classifyFailure } from "lasso";
|
|
69
|
+
|
|
70
|
+
const compiled = compileHarnessSpec(spec);
|
|
71
|
+
const signature = classifyFailure(error, { nodeId: "deploy" });
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
> **Safety:** Lasso checks out refs, applies patches, and merges branches in the
|
|
75
|
+
> target repo. Use a throwaway clone or disposable worktree, not your primary
|
|
76
|
+
> checkout.
|
|
77
|
+
|
|
78
|
+
## Table of contents
|
|
79
|
+
|
|
80
|
+
- [What is Lasso?](#what-is-lasso)
|
|
81
|
+
- [Why Lasso exists](#why-lasso-exists)
|
|
82
|
+
- [What Lasso does](#what-lasso-does)
|
|
83
|
+
- [How it works](#how-it-works)
|
|
84
|
+
- [Slash commands](#slash-commands)
|
|
85
|
+
- [Bundled workflows](#bundled-workflows)
|
|
86
|
+
- [Request examples](#request-examples)
|
|
87
|
+
- [Custom workflows](#custom-workflows)
|
|
88
|
+
- [HarnessSpec reference](#harnessspec-reference)
|
|
89
|
+
- [Library API](#library-api)
|
|
90
|
+
- [Compiler](#compiler)
|
|
91
|
+
- [Compiler feedback](#compiler-feedback)
|
|
92
|
+
- [Verification engine](#verification-engine)
|
|
93
|
+
- [Compiler optimizations](#compiler-optimizations)
|
|
94
|
+
- [Harness mutations](#harness-mutations)
|
|
95
|
+
- [Guardrails](#guardrails)
|
|
96
|
+
- [Failure mode generation](#failure-mode-generation)
|
|
97
|
+
- [Adaptive runtime](#adaptive-runtime)
|
|
98
|
+
- [Lineage persistence](#lineage-persistence)
|
|
99
|
+
- [Harness memory](#harness-memory)
|
|
100
|
+
- [Environment model](#environment-model)
|
|
101
|
+
- [Failure ontology](#failure-ontology)
|
|
102
|
+
- [Capabilities](#capabilities)
|
|
103
|
+
- [Meta-harness](#meta-harness)
|
|
104
|
+
- [Multi-harness composition](#multi-harness-composition)
|
|
105
|
+
- [How Lasso fits with pi-duroxide](#how-lasso-fits-with-pi-duroxide)
|
|
106
|
+
- [Non-goals](#non-goals)
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Why Lasso exists
|
|
111
|
+
|
|
112
|
+
`pi-duroxide` gives you a durable workflow runtime. That is the right layer when
|
|
113
|
+
you already know what workflow you want to run.
|
|
114
|
+
|
|
115
|
+
Lasso sits one level higher. It:
|
|
116
|
+
|
|
117
|
+
1. **discovers** the execution environment (available tools, auth, constraints)
|
|
118
|
+
2. **synthesizes** a workflow graph from intent
|
|
119
|
+
3. **predicts** failures before they happen
|
|
120
|
+
4. **mutates** the harness to prevent them
|
|
121
|
+
5. **compiles** into a replay-safe durable workflow
|
|
122
|
+
6. **repairs** the harness at runtime based on observed failures
|
|
123
|
+
7. **remembers** what worked across sessions
|
|
124
|
+
|
|
125
|
+
Use Lasso when you want workflow automation that is:
|
|
126
|
+
|
|
127
|
+
- more reusable than an ad hoc prompt
|
|
128
|
+
- more inspectable than hidden agent logic
|
|
129
|
+
- safer to validate before execution
|
|
130
|
+
- adaptive — it repairs itself when things go wrong
|
|
131
|
+
- aware of its environment — it knows what tools are available
|
|
132
|
+
|
|
133
|
+
## What Lasso does
|
|
134
|
+
|
|
135
|
+
Lasso takes a declarative `HarnessSpec`, validates it, lowers it to CIR,
|
|
136
|
+
optimizes it, and compiles it into a replay-safe workflow that runs on
|
|
137
|
+
`pi-duroxide`.
|
|
138
|
+
|
|
139
|
+
Out of the box, it ships with:
|
|
140
|
+
|
|
141
|
+
- **Two bundled workflows** — `patch-validation` and `pr-review-merge`
|
|
142
|
+
- **Slash commands** — `/lasso:plan`, `/lasso:replan`, `/lasso:compile`, `/lasso:run`, `/lasso:inspect`
|
|
143
|
+
- **A library API** — for programmatic use from TypeScript
|
|
144
|
+
|
|
145
|
+
## How it works
|
|
146
|
+
|
|
147
|
+
### The generation pipeline
|
|
148
|
+
|
|
149
|
+
```text
|
|
150
|
+
Intent (brief or skill markdown)
|
|
151
|
+
↓
|
|
152
|
+
parsePromptOrSkill() → IntentIR
|
|
153
|
+
↓
|
|
154
|
+
buildTaskGraph() → TaskGraph
|
|
155
|
+
↓
|
|
156
|
+
analyzeRisks() → RiskModel
|
|
157
|
+
↓
|
|
158
|
+
synthesizePolicy() → PolicyBundle
|
|
159
|
+
↓
|
|
160
|
+
synthesizeHarness() → HarnessSpec
|
|
161
|
+
↓
|
|
162
|
+
compileHarnessSpec() → CompiledWorkflow → pi-duroxide
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### The adaptation loop
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
Workflow executes
|
|
169
|
+
↓
|
|
170
|
+
Execution trace captured (timestamps, I/O snapshots, failures)
|
|
171
|
+
↓
|
|
172
|
+
deriveMutationsFromTrace() → HarnessMutation[]
|
|
173
|
+
↓
|
|
174
|
+
mutateHarness(spec, mutations) → new spec
|
|
175
|
+
↓
|
|
176
|
+
prepareRuntimeReplan() → continue_as_new / needs_operator_input / stop
|
|
177
|
+
↓
|
|
178
|
+
New version with repaired harness
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### The feedback loop
|
|
182
|
+
|
|
183
|
+
```text
|
|
184
|
+
compileHarnessSpec()
|
|
185
|
+
↓
|
|
186
|
+
analyzeCompiledWorkflow()
|
|
187
|
+
→ CostEstimate (LLM calls, duration, USD)
|
|
188
|
+
→ RiskAssessment (cost, failure, quality, complexity)
|
|
189
|
+
→ HarnessMutation[] (executable, with triggers)
|
|
190
|
+
↓
|
|
191
|
+
mutateHarness(spec, mutations)
|
|
192
|
+
→ replace expensive models
|
|
193
|
+
→ add retry policies
|
|
194
|
+
→ add verification hooks
|
|
195
|
+
↓
|
|
196
|
+
Recompile with improvements
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Slash commands
|
|
202
|
+
|
|
203
|
+
| Command | Use it when | What it does |
|
|
204
|
+
| --- | --- | --- |
|
|
205
|
+
| `/lasso:plan <brief>` | You have an English brief and want a draft request | Returns a draft JSON request or a clarification result |
|
|
206
|
+
| `/lasso:replan <JSON>` | You have a previous request plus a real outcome | Returns a revised draft, `needs_operator_input`, or `stop` |
|
|
207
|
+
| `/lasso:compile <input>` | You want to inspect what Lasso will register | Compiles and stores the artifact in memory |
|
|
208
|
+
| `/lasso:run <input>` | You want to execute a workflow locally | Compiles, registers, and starts the workflow |
|
|
209
|
+
| `/lasso:inspect [name]` | You want to see compiled spec, CIR, and runtime state | Shows the latest or named compiled workflow |
|
|
210
|
+
|
|
211
|
+
### `/lasso:plan`
|
|
212
|
+
|
|
213
|
+
Deterministic, draft-only. Classifies a brief into `patch-validation`,
|
|
214
|
+
`pr-review-merge`, or `custom` and returns a draft JSON envelope you can pass to
|
|
215
|
+
`/lasso:compile` or `/lasso:run`. Does not compile, register, or run anything.
|
|
216
|
+
|
|
217
|
+
For the two bundled families, it extracts structured fields with strict
|
|
218
|
+
validation. For custom families (via skill markdown with an explicit `workflow`
|
|
219
|
+
name), it builds a sequential graph from parsed steps.
|
|
220
|
+
|
|
221
|
+
### `/lasso:replan`
|
|
222
|
+
|
|
223
|
+
Deterministic, draft-only. Accepts the original request plus an
|
|
224
|
+
`observedOutcome` and returns one of:
|
|
225
|
+
|
|
226
|
+
1. a revised draft request
|
|
227
|
+
2. `needs_operator_input` — human must provide new facts
|
|
228
|
+
3. `stop` — auto-retrying would be wrong
|
|
229
|
+
|
|
230
|
+
### Custom compile/run input shapes
|
|
231
|
+
|
|
232
|
+
`/lasso:compile` and `/lasso:run` accept four input forms:
|
|
233
|
+
|
|
234
|
+
1. **Bundled workflow request JSON** — `{ "workflow": "patch-validation", "input": {...} }`
|
|
235
|
+
2. **Raw `HarnessSpec` JSON** — the full spec
|
|
236
|
+
3. **Envelope with spec or specPath** — `{ "spec": {...}, "input": {...} }` or `{ "specPath": "/path/to/spec.json" }`
|
|
237
|
+
4. **Direct path** — `/tmp/custom-spec.json`
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Bundled workflows
|
|
242
|
+
|
|
243
|
+
Both operate entirely against a **local** repository or worktree.
|
|
244
|
+
|
|
245
|
+
### `patch-validation`
|
|
246
|
+
|
|
247
|
+
Validates a candidate fix against a known-bad baseline:
|
|
248
|
+
|
|
249
|
+
1. Check out `baselineRef` and run `reproduceCommands` to confirm the bug
|
|
250
|
+
2. Apply the candidate from `candidateSource`
|
|
251
|
+
3. Re-run `reproduceCommands` — expect them to pass
|
|
252
|
+
4. Run `verificationCommands` as a broader regression check
|
|
253
|
+
5. Optionally route to human approval
|
|
254
|
+
|
|
255
|
+
Terminal outcomes: `validated-fix`, `not-reproduced`, `apply-failed`, `candidate-failed`, `rejected`
|
|
256
|
+
|
|
257
|
+
### `pr-review-merge`
|
|
258
|
+
|
|
259
|
+
Local rehearsal of a review-and-merge flow:
|
|
260
|
+
|
|
261
|
+
1. Inspect the repo
|
|
262
|
+
2. Run verification commands
|
|
263
|
+
3. Generate an LLM review summary
|
|
264
|
+
4. Route through human approval
|
|
265
|
+
5. Perform local merge
|
|
266
|
+
6. Re-run verification after merge
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Request examples
|
|
271
|
+
|
|
272
|
+
### `patch-validation`
|
|
273
|
+
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"workflow": "patch-validation",
|
|
277
|
+
"input": {
|
|
278
|
+
"repoPath": "/absolute/path/to/disposable-worktree",
|
|
279
|
+
"baselineRef": "main",
|
|
280
|
+
"candidateSource": { "kind": "patchFile", "value": "/path/to/fix.patch" },
|
|
281
|
+
"reproduceCommands": ["npm test -- --grep 'the broken test'"],
|
|
282
|
+
"verificationCommands": ["npm test"],
|
|
283
|
+
"reviewInstructions": "Approve if the patch applies cleanly and verification passes.",
|
|
284
|
+
"approvalRequired": false
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### `pr-review-merge`
|
|
290
|
+
|
|
291
|
+
```json
|
|
292
|
+
{
|
|
293
|
+
"workflow": "pr-review-merge",
|
|
294
|
+
"input": {
|
|
295
|
+
"repoPath": "/absolute/path/to/disposable-worktree",
|
|
296
|
+
"sourceBranch": "feature/pr-change",
|
|
297
|
+
"targetBranch": "main",
|
|
298
|
+
"reviewInstructions": "Approve only if verification passes and the diff looks safe.",
|
|
299
|
+
"verificationCommands": ["node -e \"process.exit(0)\""]
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### `replan`
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"workflow": "patch-validation",
|
|
309
|
+
"originalRequest": {
|
|
310
|
+
"workflow": "patch-validation",
|
|
311
|
+
"input": { "..." : "..." }
|
|
312
|
+
},
|
|
313
|
+
"observedOutcome": {
|
|
314
|
+
"terminalNodeId": "validated-fix",
|
|
315
|
+
"notes": ["prod hotfix"]
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
For aborted attempts: `{ "aborted": true, "abortReason": "retry-exhaustion" }`
|
|
321
|
+
|
|
322
|
+
### Custom `HarnessSpec` compile
|
|
323
|
+
|
|
324
|
+
```json
|
|
325
|
+
{
|
|
326
|
+
"name": "custom-echo",
|
|
327
|
+
"graph": {
|
|
328
|
+
"entryNodeId": "echo",
|
|
329
|
+
"nodes": [
|
|
330
|
+
{ "id": "echo", "kind": "tool", "tool": "bash", "args": ["-lc", "echo hello"] }
|
|
331
|
+
],
|
|
332
|
+
"edges": []
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Custom workflows
|
|
340
|
+
|
|
341
|
+
Use `/lasso:compile` and `/lasso:run` with any `HarnessSpec`, or use Lasso as
|
|
342
|
+
a library:
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import { validateHarnessSpec, lowerHarnessSpecToCir, compileHarnessSpec } from "lasso";
|
|
346
|
+
|
|
347
|
+
validateHarnessSpec(spec); // structural validation
|
|
348
|
+
lowerHarnessSpecToCir(spec); // inspect lowered IR
|
|
349
|
+
compileHarnessSpec(spec); // produce replay-safe workflow
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Arbitrary workflow families are supported. The planner accepts custom families
|
|
353
|
+
via skill markdown with an explicit `workflow` name.
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## HarnessSpec reference
|
|
358
|
+
|
|
359
|
+
Canonical sources: `src/spec/types.ts`, `src/spec/schema.ts`, `src/spec/validate.ts`
|
|
360
|
+
|
|
361
|
+
### Top-level shape
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
{
|
|
365
|
+
"name": "workflow-name",
|
|
366
|
+
"graph": { "entryNodeId": "start", "nodes": [], "edges": [] },
|
|
367
|
+
"executionPolicy": {},
|
|
368
|
+
"humanPolicy": {},
|
|
369
|
+
"observabilityPolicy": {}
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
| Field | Required | Type | Notes |
|
|
374
|
+
| --- | --- | --- | --- |
|
|
375
|
+
| `name` | Yes | `string` | Unique workflow name |
|
|
376
|
+
| `graph` | Yes | `object` | Contains `entryNodeId`, `nodes`, `edges` |
|
|
377
|
+
| `executionPolicy` | No | `object` | Global execution settings |
|
|
378
|
+
| `humanPolicy` | No | `object` | Human interaction defaults |
|
|
379
|
+
| `observabilityPolicy` | No | `object` | Trace / metrics / logging |
|
|
380
|
+
|
|
381
|
+
All top-level objects are **strict**. Unknown fields are rejected.
|
|
382
|
+
|
|
383
|
+
### Node kinds
|
|
384
|
+
|
|
385
|
+
| Kind | Key fields | Maps to |
|
|
386
|
+
| --- | --- | --- |
|
|
387
|
+
| `tool` | `tool`, `args`, `env`, `cwd` | `ctx.pi.tool()` |
|
|
388
|
+
| `llm` | `provider`, `model`, `prompt`, `system` | `ctx.pi.llm()` |
|
|
389
|
+
| `human` | `prompt`, `interactionType`, `options` | `ctx.waitForEvent()` |
|
|
390
|
+
| `condition` | `condition`, `thenNodeId`, `elseNodeId` | Branch evaluation |
|
|
391
|
+
| `merge` | `waitFor`, `strategy` | Fork-join synchronization |
|
|
392
|
+
| `subworkflow` | `specRef`, `inputs` | `ctx.scheduleSubOrchestration()` |
|
|
393
|
+
|
|
394
|
+
### Validation rules
|
|
395
|
+
|
|
396
|
+
1. Node IDs must be unique
|
|
397
|
+
2. `entryNodeId` must exist
|
|
398
|
+
3. Every edge `from`/`to` must reference an existing node
|
|
399
|
+
4. `condition.thenNodeId` and `condition.elseNodeId` must exist
|
|
400
|
+
5. `merge.waitFor` must not be empty
|
|
401
|
+
6. `human` nodes with `interactionType: "choice"` must have `options`
|
|
402
|
+
7. Unreachable nodes are rejected
|
|
403
|
+
8. `retryPolicy` only on `tool`, `llm`, `subworkflow`
|
|
404
|
+
9. Verification rules cannot reference missing nodes
|
|
405
|
+
10. Circular verification dependencies are rejected
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Library API
|
|
410
|
+
|
|
411
|
+
### Compiler
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
import { compileHarnessSpec, type CompiledHarnessWorkflow } from "lasso";
|
|
415
|
+
|
|
416
|
+
const compiled = compileHarnessSpec(spec);
|
|
417
|
+
// compiled.name, compiled.spec, compiled.cir, compiled.optimizations
|
|
418
|
+
// compiled.register(pi) — registers with pi-duroxide
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Pipeline: validate → lower → optimize → validate CIR → build generator.
|
|
422
|
+
|
|
423
|
+
### Compiler feedback
|
|
424
|
+
|
|
425
|
+
Analyzes compiled workflows and emits **executable mutations** (not just
|
|
426
|
+
advisory suggestions):
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
import { analyzeCompiledWorkflow, mutateHarness } from "lasso";
|
|
430
|
+
|
|
431
|
+
const analysis = analyzeCompiledWorkflow(compiled);
|
|
432
|
+
// analysis.cost — LLM calls, duration, USD estimate
|
|
433
|
+
// analysis.risk — cost, failure, quality, complexity
|
|
434
|
+
// analysis.mutations — executable HarnessMutation[] with triggers
|
|
435
|
+
|
|
436
|
+
// Apply mutations directly
|
|
437
|
+
const { spec: improvedSpec } = mutateHarness(spec, analysis.mutations);
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Each mutation carries a `trigger` (why it was emitted) and `description`
|
|
441
|
+
(human-readable reason):
|
|
442
|
+
|
|
443
|
+
| Trigger | Mutation | Effect |
|
|
444
|
+
| --- | --- | --- |
|
|
445
|
+
| `cost_high` | `replace-node` | Swap expensive model for cheaper one |
|
|
446
|
+
| `retry_exhausted` | `modify-node` | Add retry policy with exponential backoff |
|
|
447
|
+
| `verification_failed` | `add-verification` | Add verification hook |
|
|
448
|
+
| `loop_detected` | `modify-node` | Flag adjacent nodes for merge |
|
|
449
|
+
|
|
450
|
+
### Guardrails
|
|
451
|
+
|
|
452
|
+
Enforce execution limits at runtime. The compiler stops execution when limits
|
|
453
|
+
are exceeded, throwing a `GuardrailExceededError` with a descriptive message.
|
|
454
|
+
|
|
455
|
+
```json
|
|
456
|
+
{
|
|
457
|
+
"name": "limited-workflow",
|
|
458
|
+
"executionPolicy": {
|
|
459
|
+
"maxSteps": 25,
|
|
460
|
+
"costLimitUsd": 0.25,
|
|
461
|
+
"timeout": 300000
|
|
462
|
+
},
|
|
463
|
+
"graph": { "..." : "..." }
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
| Field | Type | Enforcement |
|
|
468
|
+
| --- | --- | --- |
|
|
469
|
+
| `maxSteps` | `number` (positive integer) | Stops after N node executions |
|
|
470
|
+
| `costLimitUsd` | `number` (positive) | Stops when estimated LLM cost exceeds limit |
|
|
471
|
+
| `timeout` | `number` (ms) | Stops after wall-clock time |
|
|
472
|
+
|
|
473
|
+
Step count resets on `continueAsNew` (adaptive evolution). Cost accumulates
|
|
474
|
+
across versions.
|
|
475
|
+
|
|
476
|
+
### Failure mode generation
|
|
477
|
+
|
|
478
|
+
Before execution, Lasso generates plausible failure modes from the task
|
|
479
|
+
description and environment. This answers "Where am I likely to fail?" before
|
|
480
|
+
acting.
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
import { generateFailureModes } from "lasso";
|
|
484
|
+
|
|
485
|
+
const generation = generateFailureModes("Deploy my app to staging", env);
|
|
486
|
+
// generation.failureModes — array of FailureMode
|
|
487
|
+
// generation.riskSummary — "HIGH RISK: auth failures likely (env constraint detected)"
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
| Task keyword | Generated failure modes |
|
|
491
|
+
| --- | --- |
|
|
492
|
+
| `deploy` | auth expiry, network timeout, config drift |
|
|
493
|
+
| `test` | flaky tests, timeout, environment mismatch |
|
|
494
|
+
| `build` | dependency failure, disk full, OOM |
|
|
495
|
+
| `merge` | conflict, verification failure |
|
|
496
|
+
| `database` | connection timeout, migration failure |
|
|
497
|
+
| `api` | rate limit, auth expiry, schema mismatch |
|
|
498
|
+
| `file` | permission denied, disk full, path not found |
|
|
499
|
+
|
|
500
|
+
Failure modes are cross-referenced with environment constraints: if auth
|
|
501
|
+
constraint detected, auth failure probability is boosted. Each mode includes
|
|
502
|
+
triggers, mitigations, and recovery actions.
|
|
503
|
+
|
|
504
|
+
### Verification engine
|
|
505
|
+
|
|
506
|
+
Standalone module with compositional strategies:
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
import { runVerification } from "lasso/verification/engine";
|
|
510
|
+
|
|
511
|
+
// Strategies: "all-must-pass" (default), "first-pass", "any-block"
|
|
512
|
+
const report = yield* runVerification(nodeId, hooks, nodeMap, state, ctx, "first-pass");
|
|
513
|
+
// report.overallStatus — "pass" | "warn" | "block"
|
|
514
|
+
// report.hookResults — per-hook outcome + duration
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### Compiler optimizations
|
|
518
|
+
|
|
519
|
+
Three passes between lowering and CIR validation:
|
|
520
|
+
|
|
521
|
+
1. **Dead-node elimination** — removes unreachable nodes
|
|
522
|
+
2. **Single-branch merge elision** — simplifies single-branch merges
|
|
523
|
+
3. **Tool-node fusion** — merges adjacent `bash`/`sh` nodes
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
import { optimizeCirWorkflow } from "lasso/cir/optimize";
|
|
527
|
+
|
|
528
|
+
const { optimized, passes } = optimizeCirWorkflow(cir);
|
|
529
|
+
// passes — ["dead-node-elimination", "merge-elision", "tool-fusion"]
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### Harness mutations
|
|
533
|
+
|
|
534
|
+
Structural spec modifications from execution traces or compiler feedback:
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
import { deriveMutationsFromTrace, deriveMutationsFromFailure, mutateHarness } from "lasso";
|
|
538
|
+
|
|
539
|
+
// From execution trace
|
|
540
|
+
const mutations = deriveMutationsFromTrace(trace, spec);
|
|
541
|
+
|
|
542
|
+
// From classified failure
|
|
543
|
+
const mutations = deriveMutationsFromFailure(signature, spec, { nodeId: "deploy" });
|
|
544
|
+
|
|
545
|
+
// Apply
|
|
546
|
+
const { spec: newSpec, diff } = mutateHarness(spec, mutations);
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
Mutation types: `add-node`, `remove-node`, `modify-node`, `add-edge`,
|
|
550
|
+
`toggle-approval`, `add-verification`, `replace-node`, `tighten-guardrail`
|
|
551
|
+
|
|
552
|
+
Triggers: `node_failed`, `confidence_low`, `cost_high`, `loop_detected`,
|
|
553
|
+
`retry_exhausted`, `verification_failed`, `tool_missing`, `auth_expired`
|
|
554
|
+
|
|
555
|
+
### Adaptive runtime
|
|
556
|
+
|
|
557
|
+
Reference workflows get automatic version evolution:
|
|
558
|
+
|
|
559
|
+
```typescript
|
|
560
|
+
import { prepareRuntimeReplan, MAX_ADAPTIVE_VERSIONS } from "lasso";
|
|
561
|
+
|
|
562
|
+
const decision = await prepareRuntimeReplan(metadata, input, result);
|
|
563
|
+
// decision.type — "continue_as_new" | "needs_operator_input" | "stop"
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
Capped at 5 versions (`MAX_ADAPTIVE_VERSIONS`). Each version records a
|
|
567
|
+
`HarnessVersion` with full lineage.
|
|
568
|
+
|
|
569
|
+
### Lineage persistence
|
|
570
|
+
|
|
571
|
+
```typescript
|
|
572
|
+
import { FileLineageStore } from "lasso";
|
|
573
|
+
|
|
574
|
+
const store = new FileLineageStore("/path/to/store");
|
|
575
|
+
await store.saveVersion(version);
|
|
576
|
+
await store.saveLineage(entry);
|
|
577
|
+
|
|
578
|
+
const chain = await store.getLineageChain(3);
|
|
579
|
+
const recent = await store.queryLineage({ terminalNodeId: "validated-fix", limit: 10 });
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### Harness memory
|
|
583
|
+
|
|
584
|
+
Tracks patterns across sessions:
|
|
585
|
+
|
|
586
|
+
```typescript
|
|
587
|
+
import { FileMemoryStore, adviseFromMemory } from "lasso";
|
|
588
|
+
|
|
589
|
+
const store = new FileMemoryStore("/path/to/memory");
|
|
590
|
+
const advice = await adviseFromMemory("deploy-staging", store);
|
|
591
|
+
// advice.suggestions — "Previously, auth-check-before-deploy improved success rate"
|
|
592
|
+
// advice.warnings — "Pattern deploy-without-auth failed 6 times"
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
### Environment model
|
|
596
|
+
|
|
597
|
+
Discovers execution environment before generating a harness:
|
|
598
|
+
|
|
599
|
+
```typescript
|
|
600
|
+
import { discoverEnvironment, analyzeEnvironment } from "lasso";
|
|
601
|
+
|
|
602
|
+
const env = await discoverEnvironment("/path/to/repo");
|
|
603
|
+
// env.tools — bash, git, node, etc.
|
|
604
|
+
// env.constraints — auth, network, rate-limit
|
|
605
|
+
// env.repoState — branch, uncommitted changes, remotes
|
|
606
|
+
|
|
607
|
+
const analysis = analyzeEnvironment(env, ["git", "node"]);
|
|
608
|
+
// analysis.readinessScore — 0-100
|
|
609
|
+
// analysis.preparatorySteps — actionable prep steps
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### Failure ontology
|
|
613
|
+
|
|
614
|
+
7 failure classes with evidence and recovery:
|
|
615
|
+
|
|
616
|
+
```typescript
|
|
617
|
+
import { classifyFailure, suggestRecovery } from "lasso";
|
|
618
|
+
|
|
619
|
+
const signature = classifyFailure(error, { nodeId: "deploy" });
|
|
620
|
+
// signature.class — "auth" | "tool" | "resource" | "semantic" | "human" | "environment-drift" | "network" | "unknown"
|
|
621
|
+
// signature.confidence — 0-1
|
|
622
|
+
// signature.suggestedRecovery — actionable steps
|
|
623
|
+
|
|
624
|
+
const recovery = suggestRecovery(signature);
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
### Capabilities
|
|
628
|
+
|
|
629
|
+
Dynamic graph generation from required tools:
|
|
630
|
+
|
|
631
|
+
```typescript
|
|
632
|
+
import { DefaultCapabilityRegistry, planWorkflowRequest } from "lasso";
|
|
633
|
+
|
|
634
|
+
const registry = new DefaultCapabilityRegistry();
|
|
635
|
+
// Pre-registered: bash, git, node, llm-review, human-approval
|
|
636
|
+
|
|
637
|
+
const result = planWorkflowRequest(brief, registry);
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Meta-harness
|
|
641
|
+
|
|
642
|
+
Full generation pipeline — discover, predict, synthesize, compile:
|
|
643
|
+
|
|
644
|
+
```typescript
|
|
645
|
+
import { DefaultMetaHarness, DefaultCapabilityRegistry, FileMemoryStore } from "lasso";
|
|
646
|
+
|
|
647
|
+
const meta = new DefaultMetaHarness({
|
|
648
|
+
capabilityRegistry: new DefaultCapabilityRegistry(),
|
|
649
|
+
memoryStore: new FileMemoryStore("/path/to/memory"),
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
const result = await meta.generateHarness("Deploy my app to staging");
|
|
653
|
+
// result.spec — generated HarnessSpec
|
|
654
|
+
// result.environmentAnalysis — tool/resource availability
|
|
655
|
+
// result.predictedFailures — anticipated failures with confidence
|
|
656
|
+
// result.compilerAnalysis — cost, risk, mutations
|
|
657
|
+
// result.readinessScore — 0-100
|
|
658
|
+
// result.appliedMutations — what was changed
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### Multi-harness composition
|
|
662
|
+
|
|
663
|
+
```typescript
|
|
664
|
+
// Sequential chain
|
|
665
|
+
const chained = meta.composeHarnesses([
|
|
666
|
+
{ name: "research", spec: researchSpec },
|
|
667
|
+
{ name: "plan", spec: planSpec },
|
|
668
|
+
{ name: "execute", spec: executeSpec },
|
|
669
|
+
]);
|
|
670
|
+
|
|
671
|
+
// Parallel execution
|
|
672
|
+
const parallel = meta.composeParallel([verificationSpec, notificationSpec]);
|
|
673
|
+
|
|
674
|
+
// Conditional branching
|
|
675
|
+
const conditional = meta.composeConditional("isProduction", prodSpec, stagingSpec);
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
Node IDs are prefixed with stage names to avoid collisions.
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
682
|
+
## How Lasso fits with pi-duroxide
|
|
683
|
+
|
|
684
|
+
Lasso is distributed as a **pi extension** (`package.json` has a `"pi"` field
|
|
685
|
+
pointing to `./src/index.ts`). When you `pi install` it:
|
|
686
|
+
|
|
687
|
+
1. pi loads `src/index.ts`, which exports a default extension function
|
|
688
|
+
2. That function (`src/pi/extension.ts`) first boots `pi-duroxide`
|
|
689
|
+
3. Then it registers the 5 slash commands with pi's `ExtensionAPI`
|
|
690
|
+
|
|
691
|
+
The layering:
|
|
692
|
+
|
|
693
|
+
- `pi-duroxide` owns workflow lifecycle, replay, timers, events, and runtime registration
|
|
694
|
+
- Lasso owns spec validation, CIR lowering, optimization, compilation, and operator-facing commands
|
|
695
|
+
|
|
696
|
+
In other words: `pi-duroxide` is the durable runtime engine; Lasso is the
|
|
697
|
+
harness generation, optimization, and adaptation layer built on top of it.
|
|
698
|
+
|
|
699
|
+
## Non-goals
|
|
700
|
+
|
|
701
|
+
Lasso does **not** currently aim to provide:
|
|
702
|
+
|
|
703
|
+
- live GitHub or `gh` integration
|
|
704
|
+
- autonomous code authoring or patch generation
|
|
705
|
+
- LLM-backed planning or replanning (all planning is deterministic)
|
|
706
|
+
- automatic compile/run behavior from `/lasso:plan` or `/lasso:replan`
|
|
707
|
+
- arbitrary generated TypeScript
|
|
Binary file
|