Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,7 @@ docs/api/
# defaultRegistry.test.ts's temp agent directories (inside the repo so the
# installed items' package imports resolve like a real project's)
.lousho-default-registry-*/
# coding-harness/kit.test.ts's installed-kit directories (same reason)
.coding-kit-*/
# Local agent worktrees and session state
.claude/
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

This section lists what is on `main` and not yet on npm.

### Added
- Agent directories: new `agent.*` config keys `permissionMode`, `permissions` (a serializable rule form: `tool` is a name, a list or - in a code config - a `RegExp`; `when` narrows the rule with a record of argument names to regular expressions, or a predicate in a code config; `action` is `allow` / `ask` / `deny`), `compaction`, `limits`, plus `hooks` and `approve`, each a path (relative to the directory) to a file default-exporting the hook(s) / the approver function, or the value inline in a code config. `instructions/<family>.md` appends to `instructions.md` when the resolved model id contains `<family>` (e.g. `openai`, `anthropic`). `lousho build --target=node-server` (and `--target=docker`) bundles a root `hooks.*` / `approve.*` file next to the config and resolves the compiled `.js` sibling; the Cloudflare Worker target carries `permissionMode`, `permissions`, `compaction` and `limits` over and accepts `hooks` / `approve` from a code config (a file path is refused, a Worker cannot import it). See [Agent directories](docs/agent-directories.md).
- Registry: a new item `type` `kit` is a whole agent directory - its `files` may sit at any (still safe) relative path - so `lousho add <kit> --dir <target>` installs `agent.*`, `instructions.md`, tools, skills and sub-agents in one item, under one permission manifest, and records it in `lousho-registry.json` like any other item. The default registry's first kit is `coding-kit`, a port of the `examples/coding-harness` baseline: workspace tools with checkpoint rewind, an allow-listed shell, permission rules, a loop guard and output cap as hooks, a compaction setting, a `fix-failing-test` skill and a read-only `explorer` sub-agent (manifest: `exec`, `fs-write`; install with `lousho add coding-kit --dir <dir> --yes --allow exec,fs-write`). See [Registry](docs/registry.md).
- The `pi` provider (H2): `createAgent({ model: 'pi/<pi-provider>/<model>' })` routes calls through `@earendil-works/pi-ai` (`1.0.3`, a new optional peer - `npm install @earendil-works/pi-ai@1.0.3`), the engine `pi-coding-agent` runs on, so pi's whole catalog (OpenRouter, Anthropic, OpenAI, Google, ...) is usable natively; e.g. `pi/openrouter/openai/gpt-4o-mini` is OpenRouter's `openai/gpt-4o-mini` (nested slashes are kept: the segment after `pi/` is the pi provider, the rest the model id). Messages (including reasoning blocks, tool calls/results and image parts), tool `parameters` (raw JSON Schema, zod 3/4 or Standard Schema), streamed chunks and usage/cost map to the same surface the `ai`-SDK providers expose, and pi's own retries are pinned off so `retry`/`fallbackModels` stay the only retry layer. Credentials are per nested provider: pi reads each one's own env var (`pi/openrouter/...` reads `OPENROUTER_API_KEY`), and a missing one fails the call with `LOUSHO_PROVIDER_MISSING_API_KEY` naming it; a missing package fails with `MissingPeerDependencyError` naming `npm install @earendil-works/pi-ai@1.0.3`. New export `PiProvider` (+ `PiProviderConfig`, whose `models` injects custom/faux catalog entries). `pi` is Node-only: the `cloudflare-worker` target refuses `pi/...` specs. See docs/providers.md#the-pi-provider.
- `piAgent()` (Build Harness 3, "pi-coder"): a Pi coding-agent session (`@earendil-works/pi-coding-agent`, a new optional peer) usable in-process as a `createAgent({ subagents })` entry - `subagents: { coder: piAgent({ cwd, model, description, permissions }) }`. Each `task` call is one Pi session under the task's `sessionId`, so `taskId` resumes and approval resumes reopen the same JSONL transcript even across a restart. `permissions` (the same `PermissionRule`s as `createAgent()`) gate every Pi tool call through a `tool_call` extension handler: `deny` refuses it, `ask` pauses the lead run durably for approval when the caller can pause and refuses it otherwise; approving re-issues that exact call once (Pi has no run-blocked-call API, so a decision prompt asks the model to repeat it and the gate lets that one call through), rejecting sends the note as the next turn. Pi usage rolls into the lead's `result.usage`, and aborts propagate to the Pi session. Node-only: the package is loaded lazily, so a consumer without it still typechecks and `run()` fails with `LOUSHO_PEER_MISSING`. Also new: `defineRemoteSubagent(impl)` wraps any `RemoteSubagent` implementation (same contract as `remoteAgent()`), and `SubagentApprovalPause` is exported for such adapters.
- Agent directories: `"engine": "pi"` in a `subagents/<name>/` config makes that directory a `piAgent()` coding sub-agent instead of a nested `createAgent()` one - it lands in the parent's `subagents` map (the `task` tool) and its sessions run in the parent's directory, the workspace both share. Its `model` is the usual `pi/<provider>/<model>` id and its `permissions` gate the Pi session's own tool calls; the remaining config keys are ignored (Pi carries its own prompt and tools). `loadAgentDir()`'s new `piAgent` override injects `PiAgentOptions` into every such sub-agent (a faux `modelRuntime` in tests, a `sessionDir` for durable approvals); the directory always wins `cwd`, `name` and `description`. Cloudflare Worker targets reject `engine` - Pi needs the Node runtime. The default registry's second kit, `coding-pi`, is `coding-kit` on the full Pi stack: pi-provider lead, dir-declared pi coder, same safety rails (install `--allow exec,fs-write,network,env`). See [Agent directories](docs/agent-directories.md#sub-agents).

## [1.0.0-rc.0] - 2026-10-04

The first 1.0 release candidate. Upgrading from a `1.0.0-alpha.*` release?
Expand Down
36 changes: 29 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ required. It is for developers who need an agent to keep working when a run
pauses for a human or the process restarts, and who want to test it like the
rest of their code.

Three things set it apart:
Four things set it apart:

- **Durable sessions and approvals on any host.** Sessions, checkpoints and
approval pauses live in pluggable stores (memory, files, one SQLite file, or
Expand All @@ -26,6 +26,10 @@ Three things set it apart:
ships the same agent spec to a Node server, Docker or a Worker; runs emit
OpenTelemetry GenAI spans to any exporter, and every result reports token
usage and USD cost.
- **Coding agents that ship.** Agents are plain directories (`agent.json`,
`instructions.md`, `tools/`, `subagents/`), registries install them as kits,
and `piAgent()` delegates real coding work to a Pi coding agent with durable
approvals - one `lousho add coding-pi` is a working coding harness.

[Quickstart](#quickstart) · [Features](#features) · [Documentation](#documentation) ·
[Examples](#examples) · [Docs](https://lousho.com)
Expand Down Expand Up @@ -66,7 +70,7 @@ console.log(text);
```

`model` is a `provider/model` string (`openai`, `anthropic`, `openrouter`,
`ollama`) and the key comes from the provider's usual variable
`ollama`, `pi`) and the key comes from the provider's usual variable
(`OPENAI_API_KEY`, ...). Leave it out to pick the provider from the
environment, or pass `provider:` with your own or a mock provider. See
[Quick Start](docs/quick-start.md) for runnable, offline versions and
Expand All @@ -88,11 +92,11 @@ agent can be an `agent.yaml` spec served with `npx lousho dev agent.yaml`
- **Next.js and Fetch frameworks**: `createRouteHandler(agent)` serves the session API from an App Router, SvelteKit, Hono or Bun route. [Next.js](docs/nextjs.md)
- **Durable execution**: `createAgent({ store })` checkpoints every step, and `agent.resume()` finishes a crashed or interrupted run without redoing finished tools (it throws `SessionAwaitingApprovalError` on an approval-paused run, which continues via `agent.approvals.resolve()`). [Durable execution](docs/durable-execution.md)
- **Cancellation, usage and cost**: pass an `AbortSignal`; every result carries token usage and USD cost for priced models. [Runs](docs/runs.md), [Models and cost](docs/models-and-cost.md)
- **Providers**: OpenAI, Anthropic, OpenRouter, Ollama or a mock, with `withRetry()` and `withFallback()`. [Providers](docs/providers.md)
- **Sub-agents**: `subagents: { researcher, writer }` gives the lead a `task` tool plus `agent_status`, `agent_await` and `agent_cancel` for background tasks; sub-agents run in parallel. [Sub-agents](docs/sub-agents.md)
- **Providers**: OpenAI, Anthropic, OpenRouter, Ollama or a mock, with `withRetry()` and `withFallback()`; `pi/<provider>/<model>` routes through `@earendil-works/pi-ai`, opening Pi's whole catalog (OpenRouter, Anthropic, OpenAI, Google, ...) in one spec space. [Providers](docs/providers.md)
- **Sub-agents**: `subagents: { researcher, writer }` gives the lead a `task` tool plus `agent_status`, `agent_await` and `agent_cancel` for background tasks; sub-agents run in parallel. `remoteAgent()` delegates to a deployed agent over HTTP, `piAgent()` to a Pi coding agent in process - both durable-resumable - and `defineRemoteSubagent()` covers your own backend. [Sub-agents](docs/sub-agents.md)
- **Handoffs**: `handoffs: [billing, support]` lets a triage agent hand the conversation to a specialist, which answers the user and keeps the session. [Handoffs](docs/handoffs.md)
- **Skills and AGENTS.md**: `loadSkills()` loads instructions on demand; `projectInstructions` appends your `AGENTS.md`. [Skills](docs/skills.md), [Project instructions](docs/configuration.md#project-instructions)
- **Agent directories**: `loadAgentDir('./my-agent')` builds an agent from `instructions.md`, `tools/` and `skills/`. [Agent directories](docs/agent-directories.md)
- **Agent directories**: `loadAgentDir('./my-agent')` builds an agent from `agent.json`, `instructions.md`, `tools/` and `skills/` - config covers models, permission rules and modes, hooks, approvers, compaction, limits and per-family instruction tails; `subagents/<name>/` with `"engine": "pi"` is a Pi coding sub-agent sharing the workspace. [Agent directories](docs/agent-directories.md)
- **Compaction**: `createAgent({ compaction })` prunes old tool results, then summarizes old turns, before the context window fills. [Context compaction](docs/compaction.md)
- **MCP client and server**: `createAgent({ mcpServers })` (or `connectMcp()`) connects stdio and HTTP MCP servers from config; `serveMcp()` / `lousho mcp` exposes your agent. [MCP](docs/mcp.md)
- **Workspace tools**: file system and shell tools for coding agents, confined to a root, shell approval-gated. [Workspace tools](docs/workspace-tools.md)
Expand All @@ -103,7 +107,7 @@ agent can be an `agent.yaml` spec served with `npx lousho dev agent.yaml`
- **Testing and evals**: `mockModel`, `recordReplay` cassettes, `defineEval()` trajectory assertions, `lousho eval` with `--record` / `--replay` cassettes and `--drift` trajectory diffs. [Testing](docs/testing.md), [Evals](docs/evals.md)
- **CLI**: `init`, `doctor`, `dev`, `chat`, `acp`, `add`, `mcp`, `eval`, `traces`, `build` and `studio`. [CLI](docs/cli.md)
- **Editors (ACP)**: `lousho acp ./my-agent` serves your agent to Zed and other Agent Client Protocol editors, with tool calls and permission prompts. [ACP](docs/acp.md)
- **Registry**: `lousho add <name>` copies a tool, skill, channel, schedule or memory slot into your agent directory from the default registry (or one you point at with `--registry <url-or-path>`), after showing its permissions. [Registry](docs/registry.md)
- **Registry**: `lousho add <name>` copies a tool, skill, channel, schedule, memory slot or **kit** - a whole agent directory - into yours from the default registry (or one you point at with `--registry <url-or-path>`), after showing its permissions. The built-in `coding-kit` and `coding-pi` kits are production-shaped coding harnesses. [Registry](docs/registry.md)
- **Agent Forge**: `lousho studio` opens a visual canvas, run debugger and chat with approval cards. [Agent Forge](docs/agent-forge.md)

## Usage
Expand Down Expand Up @@ -171,6 +175,20 @@ await agent.resume('user-42'); // after a crash: finishes the interrupted turn w
const { text } = await agent.session({ id: 'user-42' }).send('What is my name?'); // same id, same conversation
```

### A coding agent from the registry

```bash
lousho add coding-pi --dir ./my-agent --yes --allow exec,fs-write,network,env
OPENROUTER_API_KEY=... lousho dev ./my-agent
```

`coding-pi` is a whole agent directory: workspace file tools with checkpoint
rewind, an allow-listed shell, permission rules (`rm` denied, writes to
`*.test.*` refused), a loop guard, a cost cap, a read-only explorer and a Pi
coding sub-agent the lead delegates to through `task`. `coding-kit` is the
same rails on the standard providers. Both were validated live over
OpenRouter; see [the harness validation report](docs/plan/harness-validation-report.md).

## Documentation

| Page | What it covers |
Expand Down Expand Up @@ -250,6 +268,7 @@ Most examples run offline with a mock provider; see the
| Example | What it shows |
| ------- | ------------- |
| [ops-pipeline](examples/ops-pipeline) | Flagship: monitor alert, Slack "Fix it" button, human approval, fixer agent, patch-check-gated GitHub PR (`npm run pipeline:demo`) |
| [coding-harness](examples/coding-harness) | A production-shaped coding agent: checkpoints, permission rules, loop guard, the `coding-kit` and `coding-pi` registry kits, live OpenRouter runs |
| [agent-dir](examples/agent-dir) | An agent defined as a directory and loaded with `loadAgentDir()` |
| [support-bot](examples/support-bot) | A minimal customer-support agent |
| [research-assistant](examples/research-assistant) | A research agent with the built-in `http` tool |
Expand All @@ -268,7 +287,7 @@ Most examples run offline with a mock provider; see the
| `lousho dev <spec>` | Local chat UI and `POST /chat` with hot reload |
| `lousho chat <path>` | Terminal REPL: streamed replies, tool calls, approvals and questions |
| `lousho acp <path>` | Serve the agent to Zed and other Agent Client Protocol editors |
| `lousho add <name> [--registry <url-or-path>]` | Copy a tool, skill, channel, schedule or memory slot from the default registry (or a given one) into an agent directory |
| `lousho add <name> [--registry <url-or-path>]` | Copy a tool, skill, channel, schedule, memory slot or kit (a whole agent directory) from the default registry (or a given one) into an agent directory |
| `lousho mcp <spec>` | Serve the agent as an MCP server (stdio or HTTP) |
| `lousho eval [globs]` | Run `*.eval.ts` files; JUnit and JSON reports |
| `lousho traces [id]` | List saved runs, or print one as a span tree |
Expand All @@ -294,6 +313,9 @@ notes. Known gaps:
accepts (`application/pdf` for OpenAI and OpenRouter, `application/pdf` and
`text/plain` for Anthropic, on `ai` 6/7); other types - and every file part
on `ai` 4 - are replaced by a text note ([Providers](docs/providers.md)).
- The `pi` provider and `piAgent()` are Node-only (they run Pi's engine);
`lousho build --target=cloudflare-worker` refuses `pi/...` model specs and
`engine: 'pi'` sub-agent directories.
- The Cloudflare Worker target has a limited provider and tool set, and builds
agent directories without sub-agents, schedules, channels or memory slots
([Cloudflare Workers](docs/cloudflare-workers.md)).
Expand Down
17 changes: 9 additions & 8 deletions api/executor.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1862,6 +1862,7 @@ interface ProviderUsage {
cachedInputTokens?: number;
// (undocumented)
completionTokens: number;
costUsd?: number;
// (undocumented)
promptTokens: number;
reasoningTokens?: number;
Expand Down Expand Up @@ -2867,14 +2868,14 @@ interface Usage {

// Warnings were encountered during analysis:
//
// dist/createAgent-ERC_wJgQ.d.ts:712:9 - (ae-forgotten-export) The symbol "PiiType" needs to be exported by the entry point index.d.ts
// dist/createAgent-ERC_wJgQ.d.ts:729:5 - (ae-forgotten-export) The symbol "ModerationCategory" needs to be exported by the entry point index.d.ts
// dist/createAgent-ERC_wJgQ.d.ts:1073:9 - (ae-forgotten-export) The symbol "CompactedProviderErrorCategory" needs to be exported by the entry point index.d.ts
// dist/index-MPvXfVX9.d.ts:34:5 - (ae-forgotten-export) The symbol "SchemaIssue" needs to be exported by the entry point index.d.ts
// dist/index-MPvXfVX9.d.ts:45:9 - (ae-forgotten-export) The symbol "StandardResult" needs to be exported by the entry point index.d.ts
// dist/index-MPvXfVX9.d.ts:1853:9 - (ae-forgotten-export) The symbol "McpToolAnnotations" needs to be exported by the entry point index.d.ts
// dist/index-MPvXfVX9.d.ts:1889:5 - (ae-forgotten-export) The symbol "ApprovalCheckContext" needs to be exported by the entry point index.d.ts
// dist/index-MPvXfVX9.d.ts:1889:5 - (ae-forgotten-export) The symbol "ApprovalOutcome" needs to be exported by the entry point index.d.ts
// dist/createAgent-DyyUScMn.d.ts:712:9 - (ae-forgotten-export) The symbol "PiiType" needs to be exported by the entry point index.d.ts
// dist/createAgent-DyyUScMn.d.ts:729:5 - (ae-forgotten-export) The symbol "ModerationCategory" needs to be exported by the entry point index.d.ts
// dist/createAgent-DyyUScMn.d.ts:1073:9 - (ae-forgotten-export) The symbol "CompactedProviderErrorCategory" needs to be exported by the entry point index.d.ts
// dist/index-Cu54_KAW.d.ts:34:5 - (ae-forgotten-export) The symbol "SchemaIssue" needs to be exported by the entry point index.d.ts
// dist/index-Cu54_KAW.d.ts:45:9 - (ae-forgotten-export) The symbol "StandardResult" needs to be exported by the entry point index.d.ts
// dist/index-Cu54_KAW.d.ts:1860:9 - (ae-forgotten-export) The symbol "McpToolAnnotations" needs to be exported by the entry point index.d.ts
// dist/index-Cu54_KAW.d.ts:1896:5 - (ae-forgotten-export) The symbol "ApprovalCheckContext" needs to be exported by the entry point index.d.ts
// dist/index-Cu54_KAW.d.ts:1896:5 - (ae-forgotten-export) The symbol "ApprovalOutcome" needs to be exported by the entry point index.d.ts

// (No @packageDocumentation comment for this package)

Expand Down
11 changes: 6 additions & 5 deletions api/flows.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,7 @@ interface ProviderUsage {
cachedInputTokens?: number;
// (undocumented)
completionTokens: number;
costUsd?: number;
// (undocumented)
promptTokens: number;
reasoningTokens?: number;
Expand Down Expand Up @@ -1252,11 +1253,11 @@ export function validateFlowInput(input: Record<string, unknown>, variables: Flo
// Warnings were encountered during analysis:
//
// dist/flows/index.d.ts:156:9 - (ae-forgotten-export) The symbol "ProviderUsage" needs to be exported by the entry point index.d.ts
// dist/index-MPvXfVX9.d.ts:34:5 - (ae-forgotten-export) The symbol "SchemaIssue" needs to be exported by the entry point index.d.ts
// dist/index-MPvXfVX9.d.ts:45:9 - (ae-forgotten-export) The symbol "StandardResult" needs to be exported by the entry point index.d.ts
// dist/index-MPvXfVX9.d.ts:1853:9 - (ae-forgotten-export) The symbol "McpToolAnnotations" needs to be exported by the entry point index.d.ts
// dist/index-MPvXfVX9.d.ts:1889:5 - (ae-forgotten-export) The symbol "ApprovalCheckContext" needs to be exported by the entry point index.d.ts
// dist/index-MPvXfVX9.d.ts:1889:5 - (ae-forgotten-export) The symbol "ApprovalOutcome" needs to be exported by the entry point index.d.ts
// dist/index-Cu54_KAW.d.ts:34:5 - (ae-forgotten-export) The symbol "SchemaIssue" needs to be exported by the entry point index.d.ts
// dist/index-Cu54_KAW.d.ts:45:9 - (ae-forgotten-export) The symbol "StandardResult" needs to be exported by the entry point index.d.ts
// dist/index-Cu54_KAW.d.ts:1860:9 - (ae-forgotten-export) The symbol "McpToolAnnotations" needs to be exported by the entry point index.d.ts
// dist/index-Cu54_KAW.d.ts:1896:5 - (ae-forgotten-export) The symbol "ApprovalCheckContext" needs to be exported by the entry point index.d.ts
// dist/index-Cu54_KAW.d.ts:1896:5 - (ae-forgotten-export) The symbol "ApprovalOutcome" needs to be exported by the entry point index.d.ts

// (No @packageDocumentation comment for this package)

Expand Down
Loading
Loading