npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mhingston5/lasso

v0.2.1

Published

Lasso is a local-first workflow compiler for pi-duroxide.

Readme

Lasso

Table of contents


Lasso goes from intent to executable workflow — and repairs the harness while it runs.

Intent
  → Environment discovery (tools, resources, constraints)
  → Memory query (past patterns, what worked/failed)
  → Graph synthesis (planner + capabilities)
  → Failure prediction (auth, tool, network, resource)
  → Risk assessment (probability × impact, threshold filtering)
  → Policy synthesis (mutations: add verification, retry, approval)
  → Compilation (validate → lower → optimize → execute)
  → Per-node harnesses (guardrails, verification hooks)
  → Runtime adaptation (trace → synthesize → continueAsNew)

What is Lasso?

Lasso is a runtime harness synthesizer built on pi-duroxide. It synthesizes deterministic scaffolding around non-deterministic parts — predicting failures, assessing risks, and generating per-node guardrails before execution. It's a TypeScript package that plugs into pi via the pi field in package.json. When installed, it:

  1. Boots pi-duroxide (the durable workflow runtime)
  2. Registers 5 slash commands (/lasso:plan, /lasso:run, etc.)
  3. Exports a library API for programmatic use

There are two ways to use it:

| Mode | How | When | | --- | --- | --- | | Chat mode | Slash commands inside pi's coding agent UI | Interactive workflow planning and execution | | Library mode | import { compileHarnessSpec } from "lasso" | Building custom tooling, CI pipelines, or other extensions |

Quick start

Install

# From this repository
pi install .

# Or from npm (once published)
pi install @mhingston5/lasso

Chat mode (inside pi)

# 1. Plan a workflow from a freeform brief
/lasso:plan Validate that the bug fix in fix.patch works against main

# 2. Run it (paste the JSON output from step 1)
/lasso:run {"workflow":"patch-validation","input":{...}}

# 3. Inspect what happened
/lasso:inspect

Library mode (TypeScript)

import { compileHarnessSpec, mutateHarness, classifyFailure } from "lasso";

const compiled = compileHarnessSpec(spec);
const signature = classifyFailure(error, { nodeId: "deploy" });

Safety: Lasso checks out refs, applies patches, and merges branches in the target repo. Use a throwaway clone or disposable worktree, not your primary checkout.


Why Lasso exists

pi-duroxide gives you a durable workflow runtime. That is the right layer when you already know what workflow you want to run.

Lasso sits one level higher. It:

  1. discovers the execution environment (available tools, auth, constraints)
  2. synthesizes a workflow graph from intent
  3. predicts failures before they happen
  4. mutates the harness to prevent them
  5. compiles into a replay-safe durable workflow
  6. repairs the harness at runtime based on observed failures
  7. remembers what worked across sessions

Use Lasso when you want workflow automation that is:

  • more reusable than an ad hoc prompt
  • more inspectable than hidden agent logic
  • safer to validate before execution
  • adaptive — it repairs itself when things go wrong
  • aware of its environment — it knows what tools are available

What Lasso does

Lasso takes a declarative HarnessSpec, validates it, lowers it to CIR, optimizes it, and compiles it into a replay-safe workflow that runs on pi-duroxide.

Out of the box, it ships with:

  • Two bundled workflowspatch-validation and pr-review-merge
  • Slash commands/lasso:plan, /lasso:replan, /lasso:compile, /lasso:run, /lasso:inspect
  • A library API — for programmatic use from TypeScript

How it works

The generation pipeline

Intent (brief or skill markdown)
  ↓
parsePromptOrSkill() → IntentIR
  ↓
buildTaskGraph() → TaskGraph
  ↓
analyzeRisks() → RiskModel
  ↓
generateFailureModes() → FailureMode[] + Risk[]
  ↓
assessRisks() → RiskAssessment (overallScore, threshold filtering)
  ↓
synthesizePolicy() → PolicyBundle
  ↓
synthesizeHarness() → HarnessSpec (with per-node guardrails & verification hooks)
  ↓
compileHarnessSpec() → CompiledWorkflow → pi-duroxide

The adaptation loop

Workflow executes
  ↓
Execution trace captured (timestamps, I/O snapshots, failures)
  ↓
synthesizeFromTrace(trace, currentSpec, env) → HarnessSynthesisResult
  → classifies repeated failures, slow nodes, cost spikes
  → derives mutations
  ↓
mutateHarness(spec, mutations) → new spec
  ↓
prepareRuntimeReplan() → continue_as_new / needs_operator_input / stop
  ↓
New version with repaired harness

The feedback loop

compileHarnessSpec()
  ↓
analyzeCompiledWorkflow()
  → CostEstimate (LLM calls, duration, USD)
  → RiskAssessment (cost, failure, quality, complexity)
  → HarnessMutation[] (executable, with triggers)
  ↓
mutateHarness(spec, mutations)
  → replace expensive models
  → add retry policies
  → add verification hooks
  ↓
Recompile with improvements

Slash commands

| Command | Use it when | What it does | | --- | --- | --- | | /lasso:plan <brief> | You have an English brief and want a draft request | Returns a draft JSON request or a clarification result | | /lasso:replan <JSON> | You have a previous request plus a real outcome | Returns a revised draft, needs_operator_input, or stop | | /lasso:compile <input> | You want to inspect what Lasso will register | Compiles and stores the artifact in memory | | /lasso:run <input> | You want to execute a workflow locally | Compiles, registers, and starts the workflow | | /lasso:inspect [name] | You want to see compiled spec, CIR, and runtime state | Shows the latest or named compiled workflow |

/lasso:plan

Deterministic, draft-only. Classifies a brief into patch-validation, pr-review-merge, or custom and returns a draft JSON envelope you can pass to /lasso:compile or /lasso:run. Does not compile, register, or run anything.

For the two bundled families, it extracts structured fields with strict validation. For custom families (via skill markdown with an explicit workflow name), it builds a sequential graph from parsed steps.

/lasso:replan

Deterministic, draft-only. Accepts the original request plus an observedOutcome and returns one of:

  1. a revised draft request
  2. needs_operator_input — human must provide new facts
  3. stop — auto-retrying would be wrong

Custom compile/run input shapes

/lasso:compile and /lasso:run accept four input forms:

  1. Bundled workflow request JSON{ "workflow": "patch-validation", "input": {...} }
  2. Raw HarnessSpec JSON — the full spec
  3. Envelope with spec or specPath{ "spec": {...}, "input": {...} } or { "specPath": "/path/to/spec.json" }
  4. Direct path/tmp/custom-spec.json

Bundled workflows

Both operate entirely against a local repository or worktree.

patch-validation

Validates a candidate fix against a known-bad baseline:

  1. Check out baselineRef and run reproduceCommands to confirm the bug
  2. Apply the candidate from candidateSource
  3. Re-run reproduceCommands — expect them to pass
  4. Run verificationCommands as a broader regression check
  5. Optionally route to human approval

Terminal outcomes: validated-fix, not-reproduced, apply-failed, candidate-failed, rejected

pr-review-merge

Local rehearsal of a review-and-merge flow:

  1. Inspect the repo
  2. Run verification commands
  3. Generate an LLM review summary
  4. Route through human approval
  5. Perform local merge
  6. Re-run verification after merge

Request examples

patch-validation

{
  "workflow": "patch-validation",
  "input": {
    "repoPath": "/absolute/path/to/disposable-worktree",
    "baselineRef": "main",
    "candidateSource": { "kind": "patchFile", "value": "/path/to/fix.patch" },
    "reproduceCommands": ["npm test -- --grep 'the broken test'"],
    "verificationCommands": ["npm test"],
    "reviewInstructions": "Approve if the patch applies cleanly and verification passes.",
    "approvalRequired": false
  }
}

pr-review-merge

{
  "workflow": "pr-review-merge",
  "input": {
    "repoPath": "/absolute/path/to/disposable-worktree",
    "sourceBranch": "feature/pr-change",
    "targetBranch": "main",
    "reviewInstructions": "Approve only if verification passes and the diff looks safe.",
    "verificationCommands": ["node -e \"process.exit(0)\""]
  }
}

replan

{
  "workflow": "patch-validation",
  "originalRequest": {
    "workflow": "patch-validation",
    "input": { "..." : "..." }
  },
  "observedOutcome": {
    "terminalNodeId": "validated-fix",
    "notes": ["prod hotfix"]
  }
}

For aborted attempts: { "aborted": true, "abortReason": "retry-exhaustion" }

Custom HarnessSpec compile

{
  "name": "custom-echo",
  "graph": {
    "entryNodeId": "echo",
    "nodes": [
      { "id": "echo", "kind": "tool", "tool": "bash", "args": ["-lc", "echo hello"] }
    ],
    "edges": []
  }
}

Custom workflows

Use /lasso:compile and /lasso:run with any HarnessSpec, or use Lasso as a library:

import { validateHarnessSpec, lowerHarnessSpecToCir, compileHarnessSpec } from "lasso";

validateHarnessSpec(spec);       // structural validation
lowerHarnessSpecToCir(spec);     // inspect lowered IR
compileHarnessSpec(spec);        // produce replay-safe workflow

Arbitrary workflow families are supported. The planner accepts custom families via skill markdown with an explicit workflow name.


HarnessSpec reference

Canonical sources: src/spec/types.ts, src/spec/schema.ts, src/spec/validate.ts

Top-level shape

{
  "name": "workflow-name",
  "graph": { "entryNodeId": "start", "nodes": [], "edges": [] },
  "executionPolicy": {},
  "humanPolicy": {},
  "observabilityPolicy": {}
}

| Field | Required | Type | Notes | | --- | --- | --- | --- | | name | Yes | string | Unique workflow name | | graph | Yes | object | Contains entryNodeId, nodes, edges | | executionPolicy | No | object | Global execution settings | | humanPolicy | No | object | Human interaction defaults | | observabilityPolicy | No | object | Trace / metrics / logging |

All top-level objects are strict. Unknown fields are rejected.

Node kinds

| Kind | Key fields | Maps to | | --- | --- | --- | | tool | tool, args, env, cwd | ctx.pi.tool() | | llm | provider, model, prompt, system | ctx.pi.llm() | | human | prompt, interactionType, options | ctx.waitForEvent() | | condition | condition, thenNodeId, elseNodeId | Branch evaluation | | merge | waitFor, strategy | Fork-join synchronization | | subworkflow | specRef, inputs | ctx.scheduleSubOrchestration() |

Per-node fields (available on all node kinds via BaseNode):

| Field | Type | Notes | | --- | --- | --- | | guardrails | NodeGuardrails | Per-node limits (timeout, retries, cost, constraints) | | verificationHooks | VerificationHook[] | Inline checks that run after this node completes |

Validation rules

  1. Node IDs must be unique
  2. entryNodeId must exist
  3. Every edge from/to must reference an existing node
  4. condition.thenNodeId and condition.elseNodeId must exist
  5. merge.waitFor must not be empty
  6. human nodes with interactionType: "choice" must have options
  7. Unreachable nodes are rejected
  8. retryPolicy only on tool, llm, subworkflow
  9. Verification rules cannot reference missing nodes
  10. Circular verification dependencies are rejected

Library API

Compiler

import { compileHarnessSpec, type CompiledHarnessWorkflow } from "lasso";

const compiled = compileHarnessSpec(spec);
// compiled.name, compiled.spec, compiled.cir, compiled.optimizations
// compiled.register(pi) — registers with pi-duroxide

Pipeline: validate → lower → optimize → validate CIR → build generator.

Compiler feedback

Analyzes compiled workflows and emits executable mutations (not just advisory suggestions):

import { analyzeCompiledWorkflow, mutateHarness } from "lasso";

const analysis = analyzeCompiledWorkflow(compiled);
// analysis.cost — LLM calls, duration, USD estimate
// analysis.risk — cost, failure, quality, complexity
// analysis.mutations — executable HarnessMutation[] with triggers

// Apply mutations directly
const { spec: improvedSpec } = mutateHarness(spec, analysis.mutations);

Each mutation carries a trigger (why it was emitted) and description (human-readable reason):

| Trigger | Mutation | Effect | | --- | --- | --- | | cost_high | replace-node | Swap expensive model for cheaper one | | retry_exhausted | modify-node | Add retry policy with exponential backoff | | verification_failed | add-verification | Add verification hook | | loop_detected | modify-node | Flag adjacent nodes for merge |

Guardrails

Enforce execution limits at runtime. The compiler stops execution when limits are exceeded, throwing a GuardrailExceededError with a descriptive message.

{
  "name": "limited-workflow",
  "executionPolicy": {
    "maxSteps": 25,
    "costLimitUsd": 0.25,
    "timeout": 300000
  },
  "graph": { "..." : "..." }
}

| Field | Type | Enforcement | | --- | --- | --- | | maxSteps | number (positive integer) | Stops after N node executions | | costLimitUsd | number (positive) | Stops when estimated LLM cost exceeds limit | | timeout | number (ms) | Stops after wall-clock time |

Step count resets on continueAsNew (adaptive evolution). Cost accumulates across versions.

Failure mode generation

Before execution, Lasso generates plausible failure modes from the task description and environment. This answers "Where am I likely to fail?" before acting.

import { generateFailureModes } from "lasso";

const generation = generateFailureModes("Deploy my app to staging", env);
// generation.failureModes — array of FailureMode
// generation.riskSummary — "HIGH RISK: auth failures likely (env constraint detected)"

| Task keyword | Generated failure modes | | --- | --- | | deploy | auth expiry, network timeout, config drift | | test | flaky tests, timeout, environment mismatch | | build | dependency failure, disk full, OOM | | merge | conflict, verification failure | | database | connection timeout, migration failure | | api | rate limit, auth expiry, schema mismatch | | file | permission denied, disk full, path not found |

Failure modes are cross-referenced with environment constraints: if auth constraint detected, auth failure probability is boosted. Each mode includes triggers, mitigations, and recovery actions.

generateFailureModes() now returns risks: Risk[] alongside failureModes, converting each failure mode into a quantified risk with probability, impact, and score.

Risk assessment

First-class Risk type with quantitative scoring. Each risk carries probability (0-1), impact (0-1), and a composite score. assessRisks() filters by threshold and returns a structured assessment.

import { generateFailureModes, assessRisks } from "lasso";

const generation = generateFailureModes("Deploy my app to staging", env);
// generation.risks — Risk[] converted from failure modes

const assessment = assessRisks(generation.risks);
// assessment.overallScore — average risk score (0-1)
// assessment.risksAboveThreshold — risks scoring >= highRiskThreshold (default 0.7)
// assessment.highRiskThreshold — the threshold used

// Custom threshold
const strict = assessRisks(generation.risks, { highRiskThreshold: 0.5 });

Risk interface:

| Field | Type | Description | | --- | --- | --- | | id | string | Unique risk identifier | | probability | number (0-1) | Likelihood of occurrence | | impact | number (0-1) | Severity if it occurs | | score | number | probability × impact | | signals | string[] | Triggers or indicators | | mitigations | HarnessMutation[] | Suggested mitigations as executable mutations | | failureClass | FailureClass | Classification (auth, tool, network, etc.) | | description | string | Human-readable description |

Per-node harnesses

Every node in a HarnessSpec can carry its own guardrails and verification hooks. These override global settings and run only during that node's execution.

{
  "id": "deploy",
  "kind": "tool",
  "tool": "bash",
  "args": ["./deploy.sh"],
  "guardrails": {
    "timeoutSeconds": 120,
    "maxRetries": 2,
    "maxCostUsd": 0.10,
    "constraints": ["exit_code == 0"]
  },
  "verificationHooks": [
    {
      "name": "health-check",
      "kind": "tool",
      "check": "curl -sf http://localhost:3000/health",
      "onFail": "block",
      "maxAttempts": 3
    }
  ]
}

NodeGuardrails:

| Field | Type | Description | | --- | --- | --- | | timeoutSeconds | number | Max execution time for this node | | maxRetries | number | Max retries (overrides global retryPolicy) | | maxCostUsd | number | Max LLM cost for this node | | constraints | string[] | Custom expressions that must hold true |

VerificationHook:

| Field | Type | Description | | --- | --- | --- | | name | string | Hook identifier | | kind | "tool" \| "llm" \| "expression" | Type of check | | check | string | Tool name, LLM prompt, or expression | | onFail | "block" \| "warn" \| "retry" | Action on failure | | maxAttempts | number | Max verification attempts (optional) |

Per-node guardrails override global executionPolicy settings. Verification hooks run inline after the node completes, with retry/block/warn semantics.

Trace-based synthesis

synthesizeFromTrace() analyzes an execution trace mid-flight, classifies failures, and derives mutations — wired into the compiler's adaptation loop.

import { DefaultMetaHarness } from "lasso";

const meta = new DefaultMetaHarness(config);

const trace = {
  completedNodes: [
    { nodeId: "build", startedAt: 1, completedAt: 2, costUsd: 0.05 },
  ],
  failedNodes: [
    { nodeId: "deploy", startedAt: 2, failedAt: 3, error: "auth expired", failureClass: "auth", retryCount: 3 },
  ],
  totalCostUsd: 0.15,
  capturedAt: Date.now(),
};

const result = await meta.synthesizeFromTrace(trace, currentSpec, environment);
// result.mutations — HarnessMutation[] derived from trace analysis
// result.spec — mutated HarnessSpec
// result.rationale — human-readable explanation of changes
// result.decision — "continue" | "needs_operator_input" | "stop"

The synthesis classifies:

  • Repeated failures — same node failing across retries → add verification or block
  • Slow nodes — duration spikes → tighten timeout guardrails
  • Cost spikes — LLM cost above expected → swap to cheaper model

This feeds directly into the continueAsNew path, producing a new harness version with repairs applied.

Verification engine

Standalone module with compositional strategies:

import { runVerification } from "lasso/verification/engine";

// Strategies: "all-must-pass" (default), "first-pass", "any-block"
const report = yield* runVerification(nodeId, hooks, nodeMap, state, ctx, "first-pass");
// report.overallStatus — "pass" | "warn" | "block"
// report.hookResults — per-hook outcome + duration

Compiler optimizations

Three passes between lowering and CIR validation:

  1. Dead-node elimination — removes unreachable nodes
  2. Single-branch merge elision — simplifies single-branch merges
  3. Tool-node fusion — merges adjacent bash/sh nodes
import { optimizeCirWorkflow } from "lasso/cir/optimize";

const { optimized, passes } = optimizeCirWorkflow(cir);
// passes — ["dead-node-elimination", "merge-elision", "tool-fusion"]

Harness mutations

Structural spec modifications from execution traces or compiler feedback:

import { deriveMutationsFromTrace, deriveMutationsFromFailure, mutateHarness } from "lasso";

// From execution trace
const mutations = deriveMutationsFromTrace(trace, spec);

// From classified failure
const mutations = deriveMutationsFromFailure(signature, spec, { nodeId: "deploy" });

// Apply
const { spec: newSpec, diff } = mutateHarness(spec, mutations);

Mutation types: add-node, remove-node, modify-node, add-edge, toggle-approval, add-verification, replace-node, tighten-guardrail

Triggers: node_failed, confidence_low, cost_high, loop_detected, retry_exhausted, verification_failed, tool_missing, auth_expired

Adaptive runtime

Reference workflows get automatic version evolution:

import { prepareRuntimeReplan, MAX_ADAPTIVE_VERSIONS } from "lasso";

const decision = await prepareRuntimeReplan(metadata, input, result);
// decision.type — "continue_as_new" | "needs_operator_input" | "stop"

Capped at 5 versions (MAX_ADAPTIVE_VERSIONS). Each version records a HarnessVersion with full lineage.

Lineage persistence

import { FileLineageStore } from "lasso";

const store = new FileLineageStore("/path/to/store");
await store.saveVersion(version);
await store.saveLineage(entry);

const chain = await store.getLineageChain(3);
const recent = await store.queryLineage({ terminalNodeId: "validated-fix", limit: 10 });

Harness memory

Tracks patterns across sessions:

import { FileMemoryStore, adviseFromMemory } from "lasso";

const store = new FileMemoryStore("/path/to/memory");
const advice = await adviseFromMemory("deploy-staging", store);
// advice.suggestions — "Previously, auth-check-before-deploy improved success rate"
// advice.warnings — "Pattern deploy-without-auth failed 6 times"

Environment model

Discovers execution environment before generating a harness:

import { discoverEnvironment, analyzeEnvironment } from "lasso";

const env = await discoverEnvironment("/path/to/repo");
// env.tools — bash, git, node, etc.
// env.constraints — auth, network, rate-limit
// env.repoState — branch, uncommitted changes, remotes

const analysis = analyzeEnvironment(env, ["git", "node"]);
// analysis.readinessScore — 0-100
// analysis.preparatorySteps — actionable prep steps

Failure ontology

7 failure classes with evidence and recovery:

import { classifyFailure, suggestRecovery } from "lasso";

const signature = classifyFailure(error, { nodeId: "deploy" });
// signature.class — "auth" | "tool" | "resource" | "semantic" | "human" | "environment-drift" | "network" | "unknown"
// signature.confidence — 0-1
// signature.suggestedRecovery — actionable steps

const recovery = suggestRecovery(signature);

Capabilities

Dynamic graph generation from required tools:

import { DefaultCapabilityRegistry, planWorkflowRequest } from "lasso";

const registry = new DefaultCapabilityRegistry();
// Pre-registered: bash, git, node, llm-review, human-approval

const result = planWorkflowRequest(brief, registry);

Meta-harness

Full generation pipeline — discover, predict, synthesize, compile:

import { DefaultMetaHarness, DefaultCapabilityRegistry, FileMemoryStore } from "lasso";

const meta = new DefaultMetaHarness({
  capabilityRegistry: new DefaultCapabilityRegistry(),
  memoryStore: new FileMemoryStore("/path/to/memory"),
});

const result = await meta.generateHarness("Deploy my app to staging");
// result.spec — generated HarnessSpec
// result.environmentAnalysis — tool/resource availability
// result.predictedFailures — anticipated failures with confidence
// result.compilerAnalysis — cost, risk, mutations
// result.readinessScore — 0-100
// result.appliedMutations — what was changed

Multi-harness composition

// Sequential chain
const chained = meta.composeHarnesses([
  { name: "research", spec: researchSpec },
  { name: "plan", spec: planSpec },
  { name: "execute", spec: executeSpec },
]);

// Parallel execution
const parallel = meta.composeParallel([verificationSpec, notificationSpec]);

// Conditional branching
const conditional = meta.composeConditional("isProduction", prodSpec, stagingSpec);

Node IDs are prefixed with stage names to avoid collisions.


How Lasso fits with pi-duroxide

Lasso is distributed as a pi extension (package.json has a "pi" field pointing to ./src/index.ts). When you pi install it:

  1. pi loads src/index.ts, which exports a default extension function
  2. That function (src/pi/extension.ts) first boots pi-duroxide
  3. Then it registers the 5 slash commands with pi's ExtensionAPI

The layering:

  • pi-duroxide owns workflow lifecycle, replay, timers, events, and runtime registration
  • Lasso owns spec validation, CIR lowering, optimization, compilation, and operator-facing commands

In other words: pi-duroxide is the durable runtime engine; Lasso is the harness generation, optimization, and adaptation layer built on top of it.

Non-goals

Lasso does not currently aim to provide:

  • live GitHub or gh integration
  • autonomous code authoring or patch generation
  • LLM-backed planning or replanning (all planning is deterministic)
  • automatic compile/run behavior from /lasso:plan or /lasso:replan
  • arbitrary generated TypeScript