From dcdcefa9d01a7d07cb6a239952a318a6e5467629 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Date: Sun, 15 Nov 2026 00:21:05 +0300 Subject: [PATCH] docs: sync unscoped package names (lousho, lousho-studio, create-lousho) Re-run scripts/sync-sdk-docs.mjs against LinuxDevil/lousho main (ae91375a, #1166): the @linuxdevil/* scope was dropped before it was ever published, so every page now references the unscoped npm names. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- acp.mdx | 8 +-- agent-directories.mdx | 24 ++++----- agent-forge.mdx | 32 ++++++------ agent-spec.mdx | 14 +++--- ai-sdk-ui.mdx | 4 +- api-overview.mdx | 38 +++++++------- approvals.mdx | 40 +++++++-------- auth.mdx | 46 ++++++++--------- build-a-coding-agent.mdx | 18 +++---- changelog.mdx | 40 ++++++++------- channels.mdx | 20 ++++---- claude-projects.mdx | 8 +-- cli.mdx | 94 +++++++++++++++++------------------ cloudflare-workers.mdx | 30 +++++------ code-mode.mdx | 2 +- compaction.mdx | 20 ++++---- configuration.mdx | 22 ++++---- connectors.mdx | 6 +-- decisions.mdx | 8 +-- deployment.mdx | 12 ++--- durable-execution.mdx | 44 ++++++++-------- errors.mdx | 12 ++--- evals.mdx | 36 +++++++------- executor-api.mdx | 4 +- flows.mdx | 34 ++++++------- guardrails.mdx | 12 ++--- handoffs.mdx | 12 ++--- hooks.mdx | 36 +++++++------- hosted-tools.mdx | 12 ++--- improve.mdx | 22 ++++---- installation.mdx | 72 +++++++++++++-------------- mcp-integration.mdx | 28 +++++------ memory.mdx | 32 ++++++------ migrating-from-ai-sdk.mdx | 10 ++-- migrating-to-create-agent.mdx | 12 ++--- models-and-cost.mdx | 8 +-- nextjs.mdx | 12 ++--- oauth.mdx | 18 +++---- observability.mdx | 38 +++++++------- openapi-tools.mdx | 12 ++--- pause-and-resume.mdx | 18 +++---- permission-modes.mdx | 10 ++-- prompting-techniques.mdx | 4 +- providers.mdx | 36 +++++++------- queue-and-steer.mdx | 8 +-- quickstart.mdx | 36 +++++++------- react.mdx | 16 +++--- reasoning.mdx | 2 +- registry.mdx | 2 +- runs.mdx | 8 +-- schedules.mdx | 10 ++-- self-healing.mdx | 10 ++-- sessions.mdx | 28 +++++------ skills.mdx | 4 +- stream-events.mdx | 2 +- streaming.mdx | 22 ++++---- structured-output.mdx | 28 +++++------ sub-agents.mdx | 30 +++++------ svelte.mdx | 8 +-- testing.mdx | 28 +++++------ tool-search.mdx | 6 +-- tools.mdx | 18 +++---- triggers.mdx | 10 ++-- troubleshooting.mdx | 8 +-- upgrading.mdx | 52 +++++++++---------- utilities.mdx | 6 +-- vue.mdx | 8 +-- workspace-tools.mdx | 34 ++++++------- 68 files changed, 705 insertions(+), 699 deletions(-) diff --git a/acp.mdx b/acp.mdx index 936234c..afd0a97 100644 --- a/acp.mdx +++ b/acp.mdx @@ -14,9 +14,9 @@ it, watch its tool calls and approve them from the editor's agent panel. ## The command ```bash -npx @linuxdevil/lousho acp agent.yaml # a spec file -npx @linuxdevil/lousho acp ./my-agent --model openai/gpt-4o # an agent directory, on another model -npx @linuxdevil/lousho acp src/agent.ts # a .ts/.js module (createAgent() options or an agent) +npx lousho acp agent.yaml # a spec file +npx lousho acp ./my-agent --model openai/gpt-4o # an agent directory, on another model +npx lousho acp src/agent.ts # a .ts/.js module (createAgent() options or an agent) ``` ```text @@ -122,7 +122,7 @@ It resolves once `input` ends, after aborting any running turn. ```ts import * as readline from 'node:readline'; -import { createAgent, serveAcp } from '@linuxdevil/lousho'; +import { createAgent, serveAcp } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', instructions: 'You are a coding assistant.' }); diff --git a/agent-directories.mdx b/agent-directories.mdx index f21a211..542975d 100644 --- a/agent-directories.mdx +++ b/agent-directories.mdx @@ -14,7 +14,7 @@ code at any time without a rewrite. > implied; the files run with your process's full permissions. ```ts -import { loadAgentDir } from '@linuxdevil/lousho'; +import { loadAgentDir } from 'lousho'; const agent = await loadAgentDir('./my-agent'); const { text } = await agent.send('Hello!'); @@ -111,7 +111,7 @@ rule decides. ```ts // hooks.ts — the default export is a hook or a list of them -import type { AgentHook } from '@linuxdevil/lousho'; +import type { AgentHook } from 'lousho'; const audit: AgentHook = { name: 'audit', postToolCall(ctx) { @@ -123,7 +123,7 @@ export default audit; ```ts // approve.ts — the default export decides every `ask`ed call -import type { ApproveToolCall } from '@linuxdevil/lousho'; +import type { ApproveToolCall } from 'lousho'; const approve: ApproveToolCall = ({ args }) => !String(args.path ?? '').endsWith('.test.js'); export default approve; ``` @@ -148,7 +148,7 @@ that exports no tool at all is an error, as are two tools with the same name ```ts // tools/send_email.ts import { z } from 'zod'; -import { defineTool } from '@linuxdevil/lousho'; +import { defineTool } from 'lousho'; export default defineTool({ name: 'send_email', @@ -239,7 +239,7 @@ internal helper of the generated bundle); with your own server, use ```ts import { createServer } from 'node:http'; -import { createAgent, mountChannels, resolveAgentDir } from '@linuxdevil/lousho'; +import { createAgent, mountChannels, resolveAgentDir } from 'lousho'; // channels/support.ts: export default webhookChannel({ secret: process.env.HOOK_SECRET ?? '' }) const { config, channels } = await resolveAgentDir('./my-agent'); @@ -262,7 +262,7 @@ naming the file. A `memory` override passed to `loadAgentDir(dir, { overrides }) is merged with the directory's slots by name: the override wins a name clash. ```ts -import { defineMemory, fileMemory, resolveAgentDir } from '@linuxdevil/lousho'; +import { defineMemory, fileMemory, resolveAgentDir } from 'lousho'; // memory/notes.ts: export default { scope: 'global', provider: fileMemory({ dir: './.lousho/memory' }) } const { manifest } = await resolveAgentDir('./my-agent', { @@ -276,7 +276,7 @@ A relative `dir` in a `memory/*.ts` file resolves against the process working di ```ts // memory/notes.ts import { fileURLToPath } from 'node:url'; -import { fileMemory } from '@linuxdevil/lousho'; +import { fileMemory } from 'lousho'; export default { scope: 'global', @@ -300,7 +300,7 @@ The path follows the module: a copied or bundled build keeps its memory next to handy for tests and tooling: ```ts -import { resolveAgentDir, createAgent } from '@linuxdevil/lousho'; +import { resolveAgentDir, createAgent } from 'lousho'; const { config, manifest } = await resolveAgentDir('./my-agent'); console.log(manifest.tools, manifest.skills, manifest.subagents); @@ -320,8 +320,8 @@ declares (`guardrails`, `onEvent`, `retry`, `reasoning`, `redactContent`, `output`, ...). Use it to swap the model in tests: ```ts -import { loadAgentDir } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { loadAgentDir } from 'lousho'; +import { mockModel } from 'lousho/testing'; const agent = await loadAgentDir('./my-agent', { provider: mockModel(['Hi there.']) }); ``` @@ -352,7 +352,7 @@ says so. Compile the directory first, or use `.js`/`.mjs` tools with an ## Run it with `lousho dev` ```bash -npx @linuxdevil/lousho dev ./my-agent +npx lousho dev ./my-agent ``` Serves the chat UI and `POST /chat` for the directory and reloads it when @@ -371,7 +371,7 @@ dev](/schedules#in-lousho-dev)). ## Deploy it with `lousho build` ```bash -npx @linuxdevil/lousho build ./my-agent --target=node-server # or docker +npx lousho build ./my-agent --target=node-server # or docker ``` The agent directory is the unit of deployment: the built server loads it with diff --git a/agent-forge.mdx b/agent-forge.mdx index 90b8d44..f95ebd9 100644 --- a/agent-forge.mdx +++ b/agent-forge.mdx @@ -4,7 +4,7 @@ description: "Lousho Studio is a local web app for building, testing and watchin --- {/* Generated from docs/agent-forge.md in LinuxDevil/lousho by scripts/sync-sdk-docs.mjs. Do not edit here. */} -It is a separate npm package, `@linuxdevil/studio` — the SDK (`@linuxdevil/lousho`) +It is a separate npm package, `lousho-studio` — the SDK (`lousho`) does not contain it. One Studio server shows every agent you have: the spec-v2 graph agents saved in your project folders, code agents Studio finds by scanning a workspace, and agents running anywhere that report to the observe collector. @@ -23,15 +23,15 @@ publishing](/forge-lifecycle). Any of these works: ```bash -npx @linuxdevil/studio +npx lousho-studio ``` ```bash -npm i -g @linuxdevil/studio +npm i -g lousho-studio lousho-studio ``` -Or, inside a project that already has `@linuxdevil/lousho` installed, the SDK's +Or, inside a project that already has `lousho` installed, the SDK's launcher finds the installed Studio and opens this folder as a workspace: ```bash @@ -41,14 +41,14 @@ lousho studio When no Studio is installed the launcher says so instead of bundling one: ``` -Studio is a separate install: npx @linuxdevil/studio +Studio is a separate install: npx lousho-studio ``` `lousho studio` accepts `--port` and `--host` and passes them through; run `lousho studio --help` for its options. `--prod` and `--dev` only apply inside the SDK's own repository checkout (see [Dev mode](#dev-mode)). -`lousho-studio` (and `npx @linuxdevil/studio`) take the same flags: +`lousho-studio` (and `npx lousho-studio`) take the same flags: | Flag | Default | Meaning | | --- | --- | --- | @@ -122,7 +122,7 @@ The rest are grouped under **Elsewhere**. For each workspace Studio picks how agent processes run: - **Bridge** — the normal case. Studio spawns `lousho bridge` from the SDK - installed in that project (`@linuxdevil/lousho`), so runs use the project's + installed in that project (`lousho`), so runs use the project's own SDK version. This matters because a project's `{ file }` tools and skills import that project's dependencies, which only resolve inside the project. @@ -135,10 +135,10 @@ Each non-default state shows a banner at the top of the workspace's pages: | Banner | What to do | | --- | --- | -| `This project has no @linuxdevil/lousho installed. Runs use Studio's built-in SDK .` | Runs still work on Studio's SDK. Install `@linuxdevil/lousho` in the project to run its own version. | +| `This project has no lousho installed. Runs use Studio's built-in SDK .` | Runs still work on Studio's SDK. Install `lousho` in the project to run its own version. | | `This SDK checkout is not built, so runs use Studio's built-in SDK . Run npm run build to use it.` | Inside the SDK repository: run `npm run build` in the checkout. | -| `Update @linuxdevil/lousho in this project to or later to edit agents here.` | The project's SDK is older than this Studio understands. The workspace is read-only: update `@linuxdevil/lousho` in the project to edit. | -| `This project's @linuxdevil/lousho is newer than this Studio understands. Update Studio to edit agents here.` | The project has a newer SDK than this Studio was built for. Update Studio (`npm i -g @linuxdevil/studio@latest`) to edit. | +| `Update lousho in this project to or later to edit agents here.` | The project's SDK is older than this Studio understands. The workspace is read-only: update `lousho` in the project to edit. | +| `This project's lousho is newer than this Studio understands. Update Studio to edit agents here.` | The project has a newer SDK than this Studio was built for. Update Studio (`npm i -g lousho-studio@latest`) to edit. | | `This project's agent process keeps stopping. Check the Studio terminal for its output, then restart it.` | The bridge process crashed repeatedly. Its output is in the terminal that started Studio; use **Restart** on the banner to try again. | While a workspace is read-only, writes under its agents (saves, drafts, runs, @@ -181,7 +181,7 @@ shows up in Studio once it reports to a collector Studio can read. Reporting is pure SDK configuration; nothing in the agent is Studio-specific: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; // LOUSHO_OBSERVE, LOUSHO_OBSERVE_TOKEN and LOUSHO_OBSERVE_PROJECT are read // from the environment - no Studio-specific code. @@ -203,12 +203,12 @@ screens. ## Updating -Studio (`@linuxdevil/studio`) and the SDK (`@linuxdevil/lousho`) are separate +Studio (`lousho-studio`) and the SDK (`lousho`) are separate packages and update independently: ```bash -npm i -g @linuxdevil/studio@latest # Studio itself -npm i @linuxdevil/lousho@latest # inside each project, its SDK +npm i -g lousho-studio@latest # Studio itself +npm i lousho@latest # inside each project, its SDK ``` A workspace's agents edit only when the project's SDK is at least @@ -270,7 +270,7 @@ skipped with a warning in the server log. ### Production vs. dev mode -An installed Studio (`lousho-studio`, `npx @linuxdevil/studio`, or +An installed Studio (`lousho-studio`, `npx lousho-studio`, or `lousho studio` finding it) is always production mode: a single Express server serves both the REST/WebSocket API *and* the pre-built client (React app) as static files, on one port. There is no separate Vite dev server and @@ -755,7 +755,7 @@ and deploy cards; see [Settings, secrets and deploy wiring](#settings-secrets-an `.lousho/agents//traces//.jsonl` under the workspace folder (see [Local traces](/observability#local-traces)): - `npx @linuxdevil/lousho traces --dir .lousho/agents//traces` shows the same + `npx lousho traces --dir .lousho/agents//traces` shows the same runs in the terminal, and deleting the agent's folder deletes its traces. The tab shows what the spans hold: model and tool names, token counts, cost, durations and error messages, and in the inspector's Input, diff --git a/agent-spec.mdx b/agent-spec.mdx index 793645a..c619491 100644 --- a/agent-spec.mdx +++ b/agent-spec.mdx @@ -78,8 +78,8 @@ A minimal spec is four fields. This snippet parses one and runs it on the built-in `mock` provider, so it works offline with no API key: ```ts -import { createMockProvider, LLMProviderRegistry, specV2ToAgent } from '@linuxdevil/lousho'; -import { parseAgentSpecV2 } from '@linuxdevil/lousho/spec'; +import { createMockProvider, LLMProviderRegistry, specV2ToAgent } from 'lousho'; +import { parseAgentSpecV2 } from 'lousho/spec'; // 'mock' is a registered provider name; scripting its reply keeps the output deterministic. LLMProviderRegistry.register('mock', () => createMockProvider({ responses: ['Hello!'] })); @@ -278,8 +278,8 @@ for when you want to build the agent yourself. v1's `specToAgent()` stays separate: it is synchronous and takes a v1 spec, not v2 data. ```ts -import { compileSpec, specV2ToAgent, type SpecHost } from '@linuxdevil/lousho'; -import { parseAgentSpecV2 } from '@linuxdevil/lousho/spec'; +import { compileSpec, specV2ToAgent, type SpecHost } from 'lousho'; +import { parseAgentSpecV2 } from 'lousho/spec'; const host: SpecHost = { baseDir: process.cwd() }; const spec = parseAgentSpecV2({ version: 2, name: 'Greeter', model: 'mock/greeter', instructions: 'Say hello.' }); @@ -307,14 +307,14 @@ and v1's `agentSpecSchema`. Node-only helpers (`specV2ToAgent`, stay on the package root. ```ts -import { agentSpecV2JsonSchema } from '@linuxdevil/lousho/spec'; +import { agentSpecV2JsonSchema } from 'lousho/spec'; const schema = agentSpecV2JsonSchema(); // the draft-07 JSON Schema of a spec, for forms and editors console.log(schema.title); // 'AgentSpec v2' ``` ```ts -import { parseAgentSpecV2, specToCode } from '@linuxdevil/lousho/spec'; +import { parseAgentSpecV2, specToCode } from 'lousho/spec'; const spec = parseAgentSpecV2({ version: 2, name: 'Greeter', model: 'mock/greeter', instructions: 'Say hello.' }); console.log(specToCode(spec)); // the same spec as a readable createAgent({...}) module @@ -329,7 +329,7 @@ is not re-validated - run it through `parseAgentSpecV2()` or use version and always returns a validated `AgentSpecV2`. ```ts -import { loadSpecV2, upgradeSpec } from '@linuxdevil/lousho'; +import { loadSpecV2, upgradeSpec } from 'lousho'; const v1 = { name: 'support-bot', diff --git a/ai-sdk-ui.mdx b/ai-sdk-ui.mdx index 6dc1396..20dee65 100644 --- a/ai-sdk-ui.mdx +++ b/ai-sdk-ui.mdx @@ -22,7 +22,7 @@ The `vendor/` model prefix chooses the provider; with OpenRouter use `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ```ts -import { createAgent, fromUIMessages, memoryStore, toUIMessageStreamResponse, type UIMessageLike } from '@linuxdevil/lousho'; +import { createAgent, fromUIMessages, memoryStore, toUIMessageStreamResponse, type UIMessageLike } from 'lousho'; // The store must outlive the request: without one, every `agent.session({ id })` // gets a fresh in-memory transcript and the next turn would start empty. @@ -159,5 +159,5 @@ kept: a `system` message (or any other role, or an entry that is not a message) is dropped, so a caller cannot add instructions to the agent's prompt. Set instructions on the agent (`createAgent({ instructions })`), not in the request. -A dedicated `@linuxdevil/lousho/ai-sdk-ui` subpath may follow; for now the +A dedicated `lousho/ai-sdk-ui` subpath may follow; for now the functions come from the package root. diff --git a/api-overview.mdx b/api-overview.mdx index 84159f6..3052a56 100644 --- a/api-overview.mdx +++ b/api-overview.mdx @@ -45,9 +45,9 @@ How the pieces fit: | Export | Description | | ----------------------------- | --------------------------------------------------------------------------- | | `createAgent(config)` | Zero-config `{ send(message) }` agent from a `model` string or provider (+ instructions, tools). | -| `AgentBuilder` (from `@linuxdevil/lousho/executor`) | Fluent builder for an `AgentConfig` (`AgentBuilder.create().setName(...)...build()`). Advanced: see [the executor API](/executor-api). | -| `AgentExecutor.execute(opts)` (from `@linuxdevil/lousho/executor`) | Static executor: runs an agent (LLM + tool-calling loop) and resolves to an `ExecutionResult`. Advanced: see [the executor API](/executor-api). | -| `resumeAfterApproval()` (from `@linuxdevil/lousho/executor`) | Resume an execution paused for human approval. Advanced: see [the executor API](/executor-api). | +| `AgentBuilder` (from `lousho/executor`) | Fluent builder for an `AgentConfig` (`AgentBuilder.create().setName(...)...build()`). Advanced: see [the executor API](/executor-api). | +| `AgentExecutor.execute(opts)` (from `lousho/executor`) | Static executor: runs an agent (LLM + tool-calling loop) and resolves to an `ExecutionResult`. Advanced: see [the executor API](/executor-api). | +| `resumeAfterApproval()` (from `lousho/executor`) | Resume an execution paused for human approval. Advanced: see [the executor API](/executor-api). | | `InMemoryApprovalStore` | Process-local `ApprovalStore`; the default store of `createAgent()` agents. | | `StorageServiceApprovalStore`, `LocalStorageCheckpointStore` (from `/utils`) | File-backed approval and checkpoint stores over a `StorageService` (see [Approvals](/approvals), [Durable execution](/durable-execution)). | | `SqliteStore` (from `/sqlite`) | Sessions, checkpoints and approvals in one SQLite file (see [Sessions](/sessions#choosing-a-store)). | @@ -75,8 +75,8 @@ resolves with the task prompt as `input` and the lead run's `sessionId`, `metadata` and `principal`. ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const refund = defineTool({ name: 'refund', description: 'Refunds an order', input: z.object({ orderId: z.string() }), execute: async () => 'ok' }); @@ -105,14 +105,14 @@ when it is created. ### UI bindings -`@linuxdevil/lousho/react` exports `useLoushoAgent(source, options?)`, a +`lousho/react` exports `useLoushoAgent(source, options?)`, a React hook that runs an agent in process (`{ agent, sessionId? }`) or over HTTP (`{ url }`) and returns `messages`, `status`, `pendingApproval`, `send()`, `stop()`, `approve()` and `reject()`. Its framework-neutral parts, `reduceAgentEvents()` and `parseEventStream()`, are exported too. See -[React](/react). `@linuxdevil/lousho/vue` exports the same +[React](/react). `lousho/vue` exports the same `useLoushoAgent()` as a Vue 3 composable, with the state as refs. See -[Vue](/vue). `@linuxdevil/lousho/svelte` exports `loushoAgent()`, the +[Vue](/vue). `lousho/svelte` exports `loushoAgent()`, the same state and actions as a Svelte store (`$agent`). See [Svelte](/svelte). ### Sub-agents @@ -210,7 +210,7 @@ See [Providers](/providers) for how a model string is resolved and which model r ## Testing -Exported from `@linuxdevil/lousho/testing` (see [Testing agents](/testing)). +Exported from `lousho/testing` (see [Testing agents](/testing)). | Export | Description | | --------------------- | ---------------------------------------------------------------------------------------- | @@ -223,18 +223,18 @@ Exported from `@linuxdevil/lousho/testing` (see [Testing agents](/testing)). | -------------------------------------------- | --------------------------------------------- | | `defineTool({ name, description, input, execute, ... })` | Define a tool; `execute`/`needsApproval` args are inferred from the zod `input`. Accepted by `createAgent({ tools: [...] })`, `ToolRegistry.register(tool)` and `AgentBuilder.addTool(tool)`. | | `ToolInput`, `ToolOutput` | Argument and result types of a defined tool. | -| `ToolRegistry` (from `@linuxdevil/lousho/tools` or `@linuxdevil/lousho/executor`) | Holds the tools an agent config refers to (advanced: `register(tool)` or `register(name, descriptor)`). | +| `ToolRegistry` (from `lousho/tools` or `lousho/executor`) | Holds the tools an agent config refers to (advanced: `register(tool)` or `register(name, descriptor)`). | | `httpTool`, `currentDateTool`, `dayNameTool` | Built-in tools. | | `createFsTools(fs, options?)`, `createShellTool(shell, options?)` | Workspace tools (`read_file`, `write_file`, `edit_file`, `list_dir`, `glob`, `grep`, `shell`) over an `FsProvider` / `ShellProvider`. See [Workspace tools](/workspace-tools). | | `NodeWorkspace`, `MemoryWorkspace`, `SandboxShell` | Workspace providers: a real directory (paths confined to `root`, minimal shell env), an in-memory tree with a scripted `exec` for tests, and a `ShellProvider` over a `SandboxAdapter` (Docker). | | `createTodoTools({ store?, onChange? })` | `todo_write` / `todo_read` tools (plus `getTodos()`) so agents can plan and track multi-step work; see [Todo tools](/tools#todo-tools). | | `connectMcp(servers, options?)` | Connect MCP servers from config (stdio or HTTP) and load their tools; see [Share servers with `connectMcp()`](/mcp-integration#share-servers-with-connectmcp). | -| `loadMcpTools(client, connectionName)` | Load a connected MCP server's tools as `ToolDescriptor`s. Available from the package root, `@linuxdevil/lousho/tools`, and `@linuxdevil/lousho/mcp`. | +| `loadMcpTools(client, connectionName)` | Load a connected MCP server's tools as `ToolDescriptor`s. Available from the package root, `lousho/tools`, and `lousho/mcp`. | See [Tools](/tools) for a guide to defining and registering tools. ```ts -import { defineTool } from '@linuxdevil/lousho'; +import { defineTool } from 'lousho'; import { z } from 'zod'; const sendEmail = defineTool({ @@ -260,7 +260,7 @@ execute }`) is legacy, still built for compatibility, and used only for descriptors that do not set `inputSchema` / `execute`. ```ts -import { loadMcpTools } from '@linuxdevil/lousho/mcp'; +import { loadMcpTools } from 'lousho/mcp'; const tools = await loadMcpTools(mcpClient, 'my-server'); ``` @@ -287,7 +287,7 @@ warning naming the server, the tool and the reason, and `onSkip` to collect what was left out: ```ts -import { loadMcpTools, type SkippedMcpTool } from '@linuxdevil/lousho/mcp'; +import { loadMcpTools, type SkippedMcpTool } from 'lousho/mcp'; const skipped: SkippedMcpTool[] = []; const tools = await loadMcpTools(mcpClient, 'my-server', { @@ -316,13 +316,13 @@ Token estimates, the model price table, and the usage and cost of a run: see [Mo ## Flows, evals, observability and security -- `FlowBuilder` / `FlowExecutor` (`@linuxdevil/lousho/flows`) - multi-step +- `FlowBuilder` / `FlowExecutor` (`lousho/flows`) - multi-step workflow graphs; see [Flows](/flows). -- `TriggerAdapter` and `TriggerRegistry` (`@linuxdevil/lousho/triggers`) - +- `TriggerAdapter` and `TriggerRegistry` (`lousho/triggers`) - the extension point for your own wake-up mechanism, plus `verifySlackSignature()`, `parseCronExpression()` and the `WebhookAuth` helpers; see [Triggers](/triggers). - `jwt()`, `oidc()`, `basic()`, `apiToken()`, `anonymous()` and `routeAuth()` - (`@linuxdevil/lousho/auth`) - route auth as an ordered list; the caller + (`lousho/auth`) - route auth as an ordered list; the caller it accepts reaches the run as `principal`; see [Route auth and principals](/auth). - `defineEval()`, scorers such as `exactMatch`, `toolCallOrder` and `budget`, checks such as `includes` and `atLeast`, and `llmJudge()` - agent evals run under vitest @@ -330,7 +330,7 @@ Token estimates, the model price table, and the usage and cost of a run: see [Mo - `withSpan()` and `TraceExporter` - tracing for `AgentExecutor.execute()`. `TraceExporter` is a bring-your-own-exporter interface (no exporter ships by default); for real OpenTelemetry spans, import - `createOtelTraceExporter()` from the `@linuxdevil/lousho/otel` + `createOtelTraceExporter()` from the `lousho/otel` subpath (requires the optional peer dependency `@opentelemetry/api`) instead of hand-rolling the OTel bridge - see `examples/tracing/run-otel.ts`. Spans follow the OpenTelemetry GenAI @@ -344,7 +344,7 @@ Token estimates, the model price table, and the usage and cost of a run: see [Mo `GenerateHookContext` - hooks that observe, deny, rewrite or redact tool calls and model calls. See [Hooks](/hooks). - `EncryptionUtils`, `sha256`, `StorageService` - (`@linuxdevil/lousho/utils`) - supporting utilities; see + (`lousho/utils`) - supporting utilities; see [Utilities](/utilities). ## Deployment diff --git a/approvals.mdx b/approvals.mdx index 56a58f5..5be8007 100644 --- a/approvals.mdx +++ b/approvals.mdx @@ -13,7 +13,7 @@ See also: [Pause, restart, resume](/pause-and-resume), a short end-to-end tutori > Snippets on this page leave some names to you: `provider` is any `LLMProvider` (offline: `createMockProvider()`); `agent` is a `createAgent()` result; `input` is the user's text, a `string`; `emailTool` is a tool built with `defineTool()`; `storage` is a `StorageService`; `toolRegistry` is a `ToolRegistry`. ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; +import { createAgent, defineTool } from 'lousho'; import { z } from 'zod'; const sendEmail = defineTool({ @@ -39,7 +39,7 @@ if (paused.finishReason === 'awaiting-approval') { arguments, typed from the tool's zod `input`: ```ts -import { defineTool } from '@linuxdevil/lousho'; +import { defineTool } from 'lousho'; import { z } from 'zod'; const sendEmail = defineTool({ @@ -92,7 +92,7 @@ error. `allow(tools)`, `deny(tools, reason?)` and `ask(tools)` build the common rules. ```ts -import { allow, ask, createAgent, defineTool, deny, type PermissionRule } from '@linuxdevil/lousho'; +import { allow, ask, createAgent, defineTool, deny, type PermissionRule } from 'lousho'; import { z } from 'zod'; const shell = defineTool({ @@ -171,7 +171,7 @@ session, each `send()` is its own transcript. Compacting away that message makes the tool ask again. ```ts -import { createAgent, defineTool, once } from '@linuxdevil/lousho'; +import { createAgent, defineTool, once } from 'lousho'; import { z } from 'zod'; const askOnce = once(); @@ -237,8 +237,8 @@ restart, give the agent a durable store, such as the SQLite one (see [Choosing a store](/sessions#choosing-a-store)): ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { SqliteStore } from '@linuxdevil/lousho/sqlite'; +import { createAgent } from 'lousho'; +import { SqliteStore } from 'lousho/sqlite'; const store = new SqliteStore('./.lousho/agent.db'); const agent = createAgent({ provider, tools: [emailTool], store }); // or approvalStore: store.approvals @@ -272,7 +272,7 @@ The step's calls that need no approval do not wait: they run when the step does, before the pause. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini' /* , tools: [payInvoice, archiveThread] */ }); const paused = await agent.send('Pay the invoice and archive the thread'); @@ -314,7 +314,7 @@ decided. `approved: false`. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', tools: [emailTool] }); const support = agent.session({ id: 'support-42' }); @@ -341,7 +341,7 @@ approval. There is no option on `resolve()` that ends the run. To stop that: model gets the reason and answers: ```ts -import { createAgent, type AgentHook } from '@linuxdevil/lousho'; +import { createAgent, type AgentHook } from 'lousho'; const noRefile: AgentHook = { name: 'no-refile', @@ -372,7 +372,7 @@ is only on the audit entry (`onPermissionDecision`). An expired pause resolved with `approved: true` does not run the tool. ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; +import { createAgent, defineTool } from 'lousho'; import { z } from 'zod'; const sendEmail = defineTool({ @@ -439,7 +439,7 @@ Its argument's `principal` is whose call it is, and tool's `ctx.approval.by` (see [auth](/auth#who-approved)). ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const trusted = createAgent({ provider, @@ -457,10 +457,10 @@ score, a timeout, a model error, arguments too long to judge — defers to a human exactly like `'defer'`. It never denies a call on its own. ```ts -import { ask, createAgent, createAutoApprove, modelAutoApprover } from '@linuxdevil/lousho'; +import { ask, createAgent, createAutoApprove, modelAutoApprover } from 'lousho'; declare const issueTool: any; -declare const cheapProvider: import('@linuxdevil/lousho').LLMProvider; +declare const cheapProvider: import('lousho').LLMProvider; const agent = createAgent({ provider, @@ -495,11 +495,11 @@ permissions: ``` ```ts -import { modelAutoApprover, specV2ToAgent } from '@linuxdevil/lousho'; +import { modelAutoApprover, specV2ToAgent } from 'lousho'; -declare const spec: import('@linuxdevil/lousho').AgentSpecV2; +declare const spec: import('lousho').AgentSpecV2; declare const baseDir: string; -declare const cheapProvider: import('@linuxdevil/lousho').LLMProvider; +declare const cheapProvider: import('lousho').LLMProvider; const agent = await specV2ToAgent(spec, { baseDir, @@ -526,7 +526,7 @@ is what `resolve()` resolves with. A continuation that pauses again ends with continues in that session, and `run.done` comes once the transcript is saved. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ provider, tools: [emailTool] }); const paused = await agent.send('Email Sam the report'); @@ -563,7 +563,7 @@ approval: in sessions, in durable stores and across a restart. in tests and scripted agents. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ instructions: 'Plan the trip with the user.', provider, askQuestion: true }); @@ -600,8 +600,8 @@ tool. Resume later, after a real restart if you like, with `resumeAfterApproval()`: ```ts -import { AgentExecutor, resumeAfterApproval } from '@linuxdevil/lousho/executor'; -import { StorageServiceApprovalStore } from '@linuxdevil/lousho/utils'; +import { AgentExecutor, resumeAfterApproval } from 'lousho/executor'; +import { StorageServiceApprovalStore } from 'lousho/utils'; const approvalStore = new StorageServiceApprovalStore(storage); diff --git a/auth.mdx b/auth.mdx index eb88a5c..b9e3d32 100644 --- a/auth.mdx +++ b/auth.mdx @@ -4,7 +4,7 @@ title: "Route auth and principals" {/* Generated from docs/auth.md in LinuxDevil/lousho by scripts/sync-sdk-docs.mjs. Do not edit here. */} An agent served over HTTP needs to know two things about each request: may it -call at all, and who is it. `@linuxdevil/lousho/auth` answers both. You +call at all, and who is it. `lousho/auth` answers both. You give a route an ordered list of auth entries (`jwt()`, `oidc()`, `basic()`, `apiToken()`, `anonymous()` or your own function); the first entry that accepts the request returns a **principal**, and that principal goes into the @@ -12,9 +12,9 @@ run, where your `model` / `instructions` / `tools` functions and memory scopes can read it. ```ts -import { createAgent, createRouteHandler } from '@linuxdevil/lousho'; -import { apiToken, jwt } from '@linuxdevil/lousho/auth'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, createRouteHandler } from 'lousho'; +import { apiToken, jwt } from 'lousho/auth'; +import { mockModel } from 'lousho/testing'; const agent = createAgent({ provider: mockModel(['Hello!']), @@ -86,7 +86,7 @@ exactly one key source: | `jwksUrl` | a JWKS endpoint (https, or http on localhost) | `RS256`, `ES256` by default; RSA and EC keys only | ```ts -import { jwt } from '@linuxdevil/lousho/auth'; +import { jwt } from 'lousho/auth'; const fromOurAuthServer = jwt({ jwksUrl: 'https://auth.example.com/.well-known/jwks.json', @@ -126,7 +126,7 @@ or http on `localhost` / `127.0.0.1` / `[::1]` — the same rule as `jwt()`'s `jwksUrl`). Algorithms default to `RS256` and `ES256`; HMAC is not allowed. ```ts -import { oidc } from '@linuxdevil/lousho/auth'; +import { oidc } from 'lousho/auth'; const google = oidc({ issuer: 'https://accounts.google.com', audience: process.env.GOOGLE_CLIENT_ID ?? 'client-id' }); ``` @@ -142,7 +142,7 @@ carries `WWW-Authenticate: Basic realm="", charset="UTF-8"` so browsers show their login prompt. ```ts -import { basic } from '@linuxdevil/lousho/auth'; +import { basic } from 'lousho/auth'; const operators = basic({ users: { ops: process.env.OPS_PASSWORD! }, realm: 'agent' }); // Or check against your own store (compare in constant time there): @@ -171,7 +171,7 @@ Any function of the `Request` is an entry. Declare the challenges it answers a `401` with, or it advertises `Bearer`: ```ts -import { AuthError, type AuthFn } from '@linuxdevil/lousho/auth'; +import { AuthError, type AuthFn } from 'lousho/auth'; const tenantKey: AuthFn = async (request) => { const key = request.headers.get('x-tenant-key'); @@ -231,7 +231,7 @@ In an [agent directory](/agent-directories), put the list in `auth.ts` (or ```ts // my-agent/auth.ts -import { apiToken, oidc } from '@linuxdevil/lousho/auth'; +import { apiToken, oidc } from 'lousho/auth'; export default [ oidc({ issuer: 'https://login.example.com', audience: 'agent-api' }), @@ -261,8 +261,8 @@ The principal is on the run context next to `sessionId` and `metadata`. The `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ```ts -import { createAgent, defineMemory, inMemoryMemory, type Principal } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineMemory, inMemoryMemory, type Principal } from 'lousho'; +import { mockModel } from 'lousho/testing'; const notes = defineMemory({ name: 'notes', @@ -346,7 +346,7 @@ none in any of these places. ### In a tool ```ts -import { defineTool } from '@linuxdevil/lousho'; +import { defineTool } from 'lousho'; import { z } from 'zod'; export const myOrders = defineTool({ @@ -367,8 +367,8 @@ from the request body or the model, and a tool cannot change it. ### Approval policies and permission rules ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const deploy = defineTool({ @@ -396,8 +396,8 @@ A decision can name who made it. The approved tool sees the decider as `ctx.approval.by`, next to `ctx.approval.note`: ```ts -import { createAgent, defineTool, type Principal } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool, type Principal } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const sendEmail = defineTool({ @@ -467,8 +467,8 @@ see the example under [Who may decide an approval](#who-may-decide-an-approval)) caller keeps the check stateless: ```ts -import { createAgent, createRouteHandler, memoryStore } from '@linuxdevil/lousho'; -import { jwt } from '@linuxdevil/lousho/auth'; +import { createAgent, createRouteHandler, memoryStore } from 'lousho'; +import { jwt } from 'lousho/auth'; const agent = createAgent({ model: 'openai/gpt-4o-mini', store: memoryStore() }); // The user id a session id starts with: everything before its last `-`. A prefix match would let @@ -524,7 +524,7 @@ Behind a reverse proxy: rewrites `Host`, list the upstream name the proxy sends, not the public one. ```ts -import { createAgent, createRouteHandler } from '@linuxdevil/lousho'; +import { createAgent, createRouteHandler } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini' }); @@ -554,8 +554,8 @@ customer can no longer approve their own refund, and a supervisor can read any session (to see what they are approving) and decide in it: ```ts -import { createAgent, createRouteHandler, memoryStore } from '@linuxdevil/lousho'; -import { jwt } from '@linuxdevil/lousho/auth'; +import { createAgent, createRouteHandler, memoryStore } from 'lousho'; +import { jwt } from 'lousho/auth'; const agent = createAgent({ model: 'openai/gpt-4o-mini', store: memoryStore() }); const isSupervisor = (principal?: { claims?: Readonly> }) => principal?.claims?.role === 'supervisor'; @@ -577,8 +577,8 @@ claims, a manager decides them, and nobody approves their own: ```ts // my-agent/auth.ts -import { apiToken } from '@linuxdevil/lousho/auth'; -import type { ApprovalAccessRequest, SessionAccessRequest } from '@linuxdevil/lousho'; +import { apiToken } from 'lousho/auth'; +import type { ApprovalAccessRequest, SessionAccessRequest } from 'lousho'; export default [ apiToken(process.env.ALICE_TOKEN!, { id: 'alice' }), diff --git a/build-a-coding-agent.mdx b/build-a-coding-agent.mdx index b28c5db..47503b7 100644 --- a/build-a-coding-agent.mdx +++ b/build-a-coding-agent.mdx @@ -20,7 +20,7 @@ file system and a shell for it, and `createFsTools()` and `createShellTool()` turn it into tools. ```ts -import { NodeWorkspace, createFsTools, createShellTool } from '@linuxdevil/lousho'; +import { NodeWorkspace, createFsTools, createShellTool } from 'lousho'; const workspace = new NodeWorkspace({ root: process.cwd() }); const tools = [...createFsTools(workspace), createShellTool(workspace)]; @@ -45,7 +45,7 @@ per-tool map. For the shell, pass a predicate over the command string and gate only the commands you consider risky. ```ts -import { NodeWorkspace, createFsTools, createShellTool } from '@linuxdevil/lousho'; +import { NodeWorkspace, createFsTools, createShellTool } from 'lousho'; const DESTRUCTIVE = /\b(rm|mv|chmod|chown|git\s+(push|reset|clean|checkout))\b|>/; @@ -77,7 +77,7 @@ written and `tool.start` announces each tool call (see [Streaming](/streaming)). ```ts -import { NodeWorkspace, createAgent, createFsTools, createShellTool } from '@linuxdevil/lousho'; +import { NodeWorkspace, createAgent, createFsTools, createShellTool } from 'lousho'; const workspace = new NodeWorkspace({ root: process.cwd() }); const agent = createAgent({ @@ -114,7 +114,7 @@ the rest of the work. That run can pause again, so loop until there is no ```ts import { createInterface } from 'node:readline/promises'; -import { createAgent, type AgentRun } from '@linuxdevil/lousho'; +import { createAgent, type AgentRun } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', instructions: 'You are a coding agent.' }); const rl = createInterface({ input: process.stdin, output: process.stdout }); @@ -169,7 +169,7 @@ same pause handling. // coding.ts // Run: OPENAI_API_KEY=... npx tsx coding.ts "Fix the failing test" import { createInterface } from 'node:readline/promises'; -import { NodeWorkspace, createAgent, createFsTools, createShellTool, type AgentRun } from '@linuxdevil/lousho'; +import { NodeWorkspace, createAgent, createFsTools, createShellTool, type AgentRun } from 'lousho'; const DESTRUCTIVE = /\b(rm|mv|chmod|chown|git\s+(push|reset|clean|checkout))\b|>/; @@ -253,8 +253,8 @@ memory and takes a scripted `exec` for the shell, and `mockModel` plays the model. Assert on `workspace.snapshot()` and `workspace.commands`. ```ts -import { createAgent, createFsTools, createShellTool, MemoryWorkspace } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, createFsTools, createShellTool, MemoryWorkspace } from 'lousho'; +import { mockModel } from 'lousho/testing'; const workspace = new MemoryWorkspace({ files: { 'math.js': 'exports.add = (a, b) => a - b;\n' }, @@ -281,8 +281,8 @@ To test the approval path, gate `write_file` the way the real agent does. runs the write and finishes the turn. ```ts -import { createAgent, createFsTools, MemoryWorkspace } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, createFsTools, MemoryWorkspace } from 'lousho'; +import { mockModel } from 'lousho/testing'; const workspace = new MemoryWorkspace({ files: { 'notes.txt': 'old\n' } }); const agent = createAgent({ diff --git a/changelog.mdx b/changelog.mdx index 0853ee6..a1fc745 100644 --- a/changelog.mdx +++ b/changelog.mdx @@ -6,13 +6,19 @@ description: "Every notable change to lousho, newest first." ## [Unreleased] -- **Breaking:** the package is renamed from `lousho` to `@linuxdevil/lousho`. Update imports from `'lousho'` / `'lousho/…'` to `'@linuxdevil/lousho'` / `'@linuxdevil/lousho/…'` and dependencies from `"lousho"` to `"@linuxdevil/lousho"`. The `lousho` binary name is unchanged, so `npx @linuxdevil/lousho …` and `lousho …` commands keep working. -- Docs: `docs/agent-forge.md` is retitled **Lousho Studio** and now documents installing the separate `@linuxdevil/studio` package, workspaces, which SDK runs a workspace's agents (with each banner), the collector, and reporting deployed agents via `LOUSHO_OBSERVE_PROJECT`. -- **Breaking:** Studio is now the separate package `@linuxdevil/studio`; the SDK tarball no longer contains `apps/agent-forge`. `lousho studio` finds an installed `@linuxdevil/studio` (local, next to the SDK, or global) and opens the current folder as a workspace, or prints `Studio is a separate install: npx @linuxdevil/studio` when it is missing. Inside the SDK checkout it keeps its dev/prod modes (`--prod`/`--dev`). Install Studio with `npm i -g @linuxdevil/studio` (or `npx @linuxdevil/studio`). +- The npm packages ship unscoped: `lousho` (SDK), `lousho-studio` (Studio) and `create-lousho` (scaffold). The `@linuxdevil/*` scoped names from alpha.118/119 are dropped — they were never published to npm, so `npm install lousho` continues the existing package line. + +## [1.0.0-alpha.119] - 2026-11-14 + +## [1.0.0-alpha.118] - 2026-11-14 + +- **Breaking:** the npm package is published unscoped as `lousho` (the `@linuxdevil/lousho` scoped name from this cycle was never published and is dropped). Studio ships separately as `lousho-studio`. Update dependencies from `"@linuxdevil/lousho"` to `"lousho"` and `"@linuxdevil/studio"` to `"lousho-studio"`. The `lousho` binary name is unchanged, so `npx lousho …` and `lousho …` commands keep working. +- Docs: `docs/agent-forge.md` is retitled **Lousho Studio** and now documents installing the separate `lousho-studio` package, workspaces, which SDK runs a workspace's agents (with each banner), the collector, and reporting deployed agents via `LOUSHO_OBSERVE_PROJECT`. +- **Breaking:** Studio is now the separate package `lousho-studio`; the SDK tarball no longer contains `apps/agent-forge`. `lousho studio` finds an installed `lousho-studio` (local, next to the SDK, or global) and opens the current folder as a workspace, or prints `Studio is a separate install: npx lousho-studio` when it is missing. Inside the SDK checkout it keeps its dev/prod modes (`--prod`/`--dev`). Install Studio with `npm i -g lousho-studio` (or `npx lousho-studio`). - Studio: the Code tab gains "Eject to project" (`POST /agents/:id/eject`), which writes the agent-directory form (`specToAgentDir`) to `agents//` in the workspace after a confirm dialog, with an explicit overwrite step when the folder already exists. -- New `@linuxdevil/lousho/observe` subpath: `startObserveDaemon`, `probeHealth`, `isObserveDaemon`, `defaultDbPath`, `OBSERVE_DEFAULT_PORT` and the daemon option/retention defaults, so a host process (Studio) can run and probe the collector through public exports. -- Install docs use the public npm packages again (`npm install @linuxdevil/lousho`, `npm create lousho`). -- `@linuxdevil/lousho/improve` (experimental): `improveAgent()` proposes tested edits to an agent's instructions and skills, offline, gated against the current harness and held-out cases. It applies nothing. See [Improving an agent](/improve). +- New `lousho/observe` subpath: `startObserveDaemon`, `probeHealth`, `isObserveDaemon`, `defaultDbPath`, `OBSERVE_DEFAULT_PORT` and the daemon option/retention defaults, so a host process (Studio) can run and probe the collector through public exports. +- Install docs use the public npm packages again (`npm install lousho`, `npm create lousho`). +- `lousho/improve` (experimental): `improveAgent()` proposes tested edits to an agent's instructions and skills, offline, gated against the current harness and held-out cases. It applies nothing. See [Improving an agent](/improve). - `lousho bridge`: a new command that speaks Studio's bridge protocol (NDJSON on stdin/stdout, protocol 1) so Lousho Studio can build and run a project's agents with that project's own SDK and dependencies. It answers `hello`, `catalog`, `compile` and `shutdown`, and runs agents: `run`/`chat` start a run or chat turn, `steer`/`abort` control it, `resolveApproval`/`answerQuestion` resume a paused run, and every `AgentEvent`, trace span, steer join and run settle arrives as a pushed event (flows are refused with `LOUSHO_HOST_UNSUPPORTED`). - Model strings resolve any official AI SDK provider: `mistral/...`, `groq/...`, `xai/...`, `bedrock/...`, `vertex/...`, `azure/...` and 13 more load `@ai-sdk/` on the first call, and `ai-sdk//` loads any other `@ai-sdk/*` package. The package reads its own credentials. See [Providers](/providers#any-ai-sdk-vendor-by-name). @@ -188,8 +194,8 @@ description: "Every notable change to lousho, newest first." ## [1.0.0-alpha.32] - 2026-11-07 -- Install docs use the public npm packages again (`npm install @linuxdevil/lousho`, `npm create lousho`). -- `@linuxdevil/lousho/improve` (experimental): `improveAgent()` proposes tested edits to an agent's instructions and skills, offline, gated against the current harness and held-out cases. It applies nothing. See [Improving an agent](/improve). +- Install docs use the public npm packages again (`npm install lousho`, `npm create lousho`). +- `lousho/improve` (experimental): `improveAgent()` proposes tested edits to an agent's instructions and skills, offline, gated against the current harness and held-out cases. It applies nothing. See [Improving an agent](/improve). - `agent.recover({ retryWithDiagnosis: true })` gives a run that reached `maxAttempts` one fork told why it failed, before quarantining it. `createOutputCheck()` is a new hook that runs your check after a tool succeeds and tells the model what it found. A fork no longer inherits `lastError` and `attempts` from its source. - **Behavior change:** `agent.recover()` no longer resumes a failing run forever. It backs off exponentially from the last failure (up to 15 minutes) and stops after `maxAttempts` failures in a row (default 10), calling the new `onQuarantined(run)` once. `agent.resume()` still runs a quarantined run. Pass a larger `maxAttempts` to keep retrying longer. - `lousho observe failures` also lists runs whose process died and scores below `--min-score` (default 0.5). @@ -200,7 +206,7 @@ description: "Every notable change to lousho, newest first." ## [1.0.0-alpha.31] - 2026-11-01 -- **Behavior change:** the package is renamed from `@lousho/build-ai-agent` to `@linuxdevil/lousho`, and the scaffolder from `create-lousho-agent` to `create-lousho` (`npm create lousho`). Update imports from `@lousho/build-ai-agent` to `@linuxdevil/lousho` (subpaths such as `@linuxdevil/lousho/testing` keep their names). Install with `npm install @linuxdevil/lousho` or `npm create lousho` (no token needed). The packages are also mirrored to GitHub Packages as `@linuxdevil/lousho` and `@linuxdevil/create-lousho` for the repository's Packages tab. +- **Behavior change:** the package is renamed from `@lousho/build-ai-agent` to `lousho`, and the scaffolder from `create-lousho-agent` to `create-lousho` (`npm create lousho`). Update imports from `@lousho/build-ai-agent` to `lousho` (subpaths such as `lousho/testing` keep their names). Install with `npm install lousho` or `npm create lousho` (no token needed). The packages are also mirrored to GitHub Packages as `lousho` and `create-lousho` for the repository's Packages tab. ## [1.0.0-alpha.30] - 2026-10-31 @@ -342,7 +348,7 @@ opt-out. - A decision on an approval whose winning decision is still streaming is a `409` (`LOUSHO_APPROVAL_CONFLICT`), as docs/auth.md says, instead of a `404`. - The session routes' `400` bodies carry a `code`: `LOUSHO_REQUEST_INVALID` (invalid JSON, missing or empty fields), `LOUSHO_APPROVAL_DECISION_INVALID` (an approval body with neither `approved` nor `answer`), `LOUSHO_SESSION_ID_INVALID`. - `lousho chat` prints a failed turn with its error code, the provider's failure category and HTTP status, and the fix (`error: User not found. (auth-failure, HTTP 401)` then `[LOUSHO_PROVIDER_REQUEST_FAILED] ...`), as docs/cli.md says, instead of the bare provider message. The terminal chat of a project scaffolded by `lousho init` / `create-lousho-agent` does the same. -- A `.env` saved with a UTF-8 byte order mark (PowerShell 5.1's `Set-Content -Encoding utf8`) no longer loses its first variable: the `@linuxdevil/lousho` commands and the scaffolded `src/index.ts` drop the mark before parsing. Variables already set in the environment still win. +- A `.env` saved with a UTF-8 byte order mark (PowerShell 5.1's `Set-Content -Encoding utf8`) no longer loses its first variable: the `lousho` commands and the scaffolded `src/index.ts` drop the mark before parsing. Variables already set in the environment still win. - `lousho doctor src/agent.ts` loads a `.ts`/`.js` agent module the way `lousho dev` does instead of failing with `LOUSHO_SPEC_UNSUPPORTED_FORMAT`, and a missing key for the module's provider is a failure. A scaffolded project's `npm run doctor` now runs `lousho doctor src/agent.ts`, so it fails when the agent's key is missing. - `lousho doctor --ping` checks OpenRouter at `/api/v1/key`, which needs the key (its `/models` answers without one, so a wrong key passed); a key the provider rejects is now a failure, and a ping of a custom `OPENAI_BASE_URL` says it shows reachability only. - `lousho doctor` fix lines use PowerShell syntax (`$env:NAME=""`) on Windows. @@ -553,7 +559,7 @@ create-lousho-agent 0.1.2-alpha.5. - A run that re-pauses on several approvals after a resume persists every pending approval id in its checkpoint: `markAwaitingApproval()` wrote only `approvalId`, so durable discovery (`pausedApprovalIds`, `agent.approvals.list()`) named 1 of N approvals; it now carries the group's `approvalIds` like `markGroupPending()` does. - The `inbox-triage` registry kit loads under `lousho dev`, `chat`, `acp`, `mcp` and `doctor`: its relative imports carry the `.ts` extension Node's type stripping needs (only bundlers resolved them). A test now installs every registry kit and loads it in a plain `node` process, as the CLI does. An extensionless relative import in an agent directory gets a hint naming the specifier to write (`'./lib/store.ts'`), and on a Node without built-in type stripping (before 22.18) the `.ts` load error says Node 22.19+ is required. - `OPENAI_BASE_URL` (and `ANTHROPIC_BASE_URL`) is used with `@ai-sdk/openai` 1.x (`@ai-sdk/anthropic` 1.x), the line that pairs with `ai` 4: the provider passes it when no `baseURL` is configured. Before, `model: 'openai/...'` called api.openai.com while `lousho doctor --ping` reported the custom endpoint. -- `PropagatingToolError`, `McpToolError` and `WorkspaceError` pass `instanceof` across the ESM and CJS copies of the SDK, so a `.cjs` tool's "do not retry" error stops the run under the ESM `@linuxdevil/lousho` CLI instead of being handed to the model (and retried). +- `PropagatingToolError`, `McpToolError` and `WorkspaceError` pass `instanceof` across the ESM and CJS copies of the SDK, so a `.cjs` tool's "do not retry" error stops the run under the ESM `lousho` CLI instead of being handed to the model (and retried). - `lousho dev` re-evaluates the local helper files an edited tool or agent module imports (the `?t=` cache-bust is passed on to relative imports), instead of logging "reloaded" while serving the stale helper; editor temp files no longer show in the "changed:" list. - `lousho eval`: a case that vitest fails after the case finished (it ran past its time limit) is reported as that case failing, not as a pass plus an extra `vitest` row; a run that records no case at all (a mistyped `--tag`) exits 1 like a glob that matches nothing. - The yarn scaffold's `yarn test` works: the templates list `vite`, vitest's peer, which yarn 1 does not install. The README's "Next 3 commands" lists three, and the tools template's README counts its three tools. @@ -660,7 +666,7 @@ See the 1.0.0-alpha.19 section, which covers everything from 1.0.0-alpha.15 thro - Permission modes: `createAgent({ permissionMode })`, `send()` / `stream()` `{ permissionMode }`, `agent.session({ permissionMode })` and `session.setPermissionMode()` put an agent in `'plan'` (only read-only tools run; everything else is refused with `kind: 'denied'`, also when an `allow` rule matched, and a run that starts in plan mode gets one system-prompt paragraph saying so), `'acceptEdits'` (file edits that would ask run without asking) or `'dontAsk'` (a call that would ask is refused; nothing pauses). The modes are presets applied after hooks, permission rules, tool guardrails and `needsApproval`, so no mode turns a deny into a run. A function mode, and a session's mode, are read at every tool call, so a switch applies from the next call, also mid-turn and in a paused turn continued by `agent.approvals.resolve()`. Sub-agents run under the lead's mode unless it is `'default'`; a remote sub-agent is refused in plan mode. Audit: under a mode other than `'default'` every call is audited, `PermissionDecisionEntry.mode` says when the mode changed the outcome, and the new `onPermissionModeChange` records each switch. New: `PermissionMode` and `PermissionModeChange` types, `defineTool({ editsFiles })` (`metadata.editsFiles`, set on `write_file` and `edit_file`), `readOnlyHint: true` on `load_skill`, `recall_`, `agent_status` and `agent_await`. The mode is not saved in checkpoints. Example: `examples/plan-mode`. See [Permission modes](/permission-modes). - Semantic recall: a memory slot can recall by meaning. New `EmbeddingProvider` (`{ id, embed(texts) }`) and `aiSdkEmbedder(model, { id?, maxBatch? })`, which wraps any AI SDK embedding model through `embedMany` from the installed `ai` (4, 6 or 7; `ai` is loaded on first use, no new dependency), and two providers that rank by cosine similarity: `inMemoryVectorMemory({ embedder, maxItems?, minScore? })` from the root and `sqliteVectorMemory(store, options)` from `@lousho/build-ai-agent/sqlite` (a new `memory_vectors` table, added to an existing database file on open; `memory_items` and `sqliteMemory()` are unchanged). With a query, `list()` returns the scope key's items by score (floor `minScore`, default 0.2; score in `metadata.score`); without one it lists the newest first. Items embedded by another embedder id are skipped when ranking until `provider.reindex(scopeKey?)` re-embeds them. A `MemoryProvider` may set `ranking: 'relevance'`, which makes the `recall_` tool say "by meaning, most relevant first". `hashEmbedder({ dimensions? })` from `@lousho/build-ai-agent/testing` is a deterministic offline embedder for tests. `describeMemoryProviderContract` takes `{ query: 'filter' | 'rank' }`. docs/memory.md has a new `## Semantic recall` section at the end. - `teamsChannel({ appId, appPassword, tenantId?, name?, fetch?, approvers?, onError? })`: an agent behind an Azure Bot in Microsoft Teams, mounted like the other channels (`POST /teams`). The inbound Bot Framework token is verified with the auth module (`oidc()`: RS256, the key set of the fixed Bot Framework OpenID metadata, issuer `https://api.botframework.com`, audience `appId`, 5-minute tolerance) before the body is parsed, and its `serviceurl` claim must equal the activity's `serviceUrl`; replies go only to that service URL (https). Personal chats always reach the agent, group chats and channels only on an `@mention` (stripped from the text); one session per conversation; Markdown replies split at 25,000 characters; tool approvals are Adaptive Cards with Approve and Deny buttons (`approvers` as on the other channels; the card updates to "Approved by..." and names its conversation, so a copied or replayed reference decides nothing); `ask_question` is answered by the next message, also after a restart with durable stores. No `node:*` import. Telegram and GitHub now set the sender as the run's `principal` too (`authenticator: 'telegram'` with the user id, `'github'` with the login), as Slack and Discord do. See docs/channels.md#microsoft-teams. -- Cloudflare Worker target builds agent directories (config, instructions, TypeScript tools, skills): `npx @linuxdevil/lousho build./my-agent --target=cloudflare-worker`. The build reads the directory on Node and generates `agent.module.ts` (static imports of `tools/*` and of an `agent.ts` config; `instructions.md`, a JSON/YAML config and the skills embedded as JSON), and the Worker builds the agent with the rules of `resolveAgentDir()` (the config check and the tool-export rules now live in Node-free modules both use). The config's `model` must name a Worker provider, or `agent.ts` sets a `provider` instance. `subagents/`, `schedules/`, `channels/`, `memory/`, `projectInstructions` and an unsupported provider are rejected with `LOUSHO_DEPLOY_FAILED`, naming what to remove. In a Worker build, an agent file's `@lousho/build-ai-agent` import resolves to a Worker-safe subset (`defineTool`, `defineSkill`, the Worker providers, `fromAiSdk`, errors,...); another name fails the build with the list. The leak check now names the file that imported a Node builtin (`"node:fs" imported by my-agent/tools/files.ts`) instead of failing with esbuild's "Could not resolve". Tools run in the Worker's isolate with its rights: see docs/cloudflare-workers.md#build-and-deploy. +- Cloudflare Worker target builds agent directories (config, instructions, TypeScript tools, skills): `npx lousho build./my-agent --target=cloudflare-worker`. The build reads the directory on Node and generates `agent.module.ts` (static imports of `tools/*` and of an `agent.ts` config; `instructions.md`, a JSON/YAML config and the skills embedded as JSON), and the Worker builds the agent with the rules of `resolveAgentDir()` (the config check and the tool-export rules now live in Node-free modules both use). The config's `model` must name a Worker provider, or `agent.ts` sets a `provider` instance. `subagents/`, `schedules/`, `channels/`, `memory/`, `projectInstructions` and an unsupported provider are rejected with `LOUSHO_DEPLOY_FAILED`, naming what to remove. In a Worker build, an agent file's `@lousho/build-ai-agent` import resolves to a Worker-safe subset (`defineTool`, `defineSkill`, the Worker providers, `fromAiSdk`, errors,...); another name fails the build with the list. The leak check now names the file that imported a Node builtin (`"node:fs" imported by my-agent/tools/files.ts`) instead of failing with esbuild's "Could not resolve". Tools run in the Worker's isolate with its rights: see docs/cloudflare-workers.md#build-and-deploy. - Workspace rewind: `createFsTools(fs, { checkpoints })` with a new `WorkspaceCheckpoints` backs up every file `write_file` and `edit_file` change (content and, where the provider has the new optional `FsProvider.getMode` / `chmod`, permission bits), grouped by session and turn. `checkpoints.list({ sessionId })` shows the changed paths per turn; `checkpoints.rewind(toTurn, { sessionId, dryRun, force })` restores every file changed in that turn or later, deletes files the agent created, and skips files changed since by something else (`'changed-since'`), files over `maxFileBytes` (`'too-large'`) and paths that are no longer files. Every path is checked before anything is written, and restores go through the provider, so `NodeWorkspace` keeps them inside its root. Retention: the newest `maxTurns` turns per session (default 20), the old content only for a turn's first write of a path, rewound turns dropped, `clear({ sessionId })`. Stores: `MemoryWorkspaceCheckpointStore` (default) and `FileWorkspaceCheckpointStore(dir)` (one JSON file per session, atomic writes). `NodeWorkspace` gains `getMode()` and `chmod()`. Without `checkpoints` the tools behave as before. See [Workspace tools](/workspace-tools#the-tools). - Route auth and principals: a new subpath, `@lousho/build-ai-agent/auth`, with `jwt()`, `oidc()`, `basic()`, `apiToken()`, `anonymous()` and `routeAuth()`. A route takes an ordered list of entries: the first one that returns a `Principal` accepts the request, `null` skips to the next, `AuthError(401 | 403)` stops; when every entry skips the answer is a generic `401` with one `WWW-Authenticate` header per challenge (`Bearer`, `Basic realm="..."`), and the body never says which check failed. JWTs are verified with Web Crypto only (no new dependency, no `node:` import, so the helpers run on Workers and edge routes): `alg: none`, algorithms outside the configured list, a `crit` header and algorithm confusion (an HS token checked against a public key) are refused; `exp` is required, `nbf` / `iat` honored, `iss` / `aud` checked, with a clock tolerance of 60 s (at most 300); JWKS key sets are cached for 10 minutes and refetched at most once per 30 s, and a key-set URL is never taken from a token. `oidc()` uses the configured issuer's discovery document and requires it to name the same issuer. `basic()` and `apiToken()` compare in constant time. `createRouteHandler({ auth })` and `createDeployedServer(agent, { auth })` take the list (a token string and a boolean function work as before); the node server appends `apiToken(LOUSHO_API_TOKEN)` when that variable is set; an agent directory's `auth.ts` is bundled by `lousho build --target=node-server` / `docker` and guards the built server (`resolveAgentDir()` returns it as `auth`, the manifest has `auth: true`). The accepted principal reaches the run: `send()` / `stream()` / session turns take `principal`, and `RunConfigContext` (the `model` / `instructions` / `tools` functions) and `MemoryScopeContext` (memory scopes) carry it; Slack and Discord channels set the sender as the principal. `Principal` is also exported from the root. Bad helper options throw the new error code `LOUSHO_AUTH_CONFIG_INVALID` when the helper is created. `createRouteHandler()` without `auth` logs one warning when `NODE_ENV` is `production`. Route auth does not check session ownership, the principal does not reach tools or approval policies yet, and the Cloudflare Worker target keeps the `LOUSHO_API_TOKEN` token only. See [Route auth and principals](/auth). - OAuth token storage: `AgentStore` gains a fourth optional part, `tokens` (`OAuthTokenStore`: `get` / `set` / `delete` / `list` per provider and credential owner, single-use pending sign-ins with `putPending` / `takePending`, and `getClient` / `setClient` for dynamically registered clients). Owners are `{ owner: 'app' }` or `{ owner: 'user', principalId, issuer? }`; `tokenStoreKey(provider, owner)` is the stable, percent-encoded record key (`|app`, `|user||`). `memoryStore()` keeps tokens in process memory; `fileStore(dir)`, `SqliteStore` and `KVStore` encrypt every record with AES-256-GCM (Web Crypto, a fresh 12-byte IV per write, the record key as additional authenticated data) under a 32-byte key the application supplies as `tokenKey` or `LOUSHO_TOKEN_KEY`; there is no default key. Several keys (newest first, or comma-separated in the variable) rotate: writes use the first, reads try each. New error codes `LOUSHO_TOKEN_KEY_MISSING` (first write, or a read of an existing record, without a key) and `LOUSHO_TOKEN_DECRYPT_FAILED` (wrong key or a changed record; names the provider, never the token). `list()` returns metadata only. `generateTokenKey()` makes a key. SQLite: schema migration 4 adds the `oauth_tokens` and `oauth_pending` tables, and `prune()` also deletes expired pending sign-ins (its result gains `oauthPending`). KV: keys under `oauth/`, pending sign-ins written with an `expirationTtl`; `KVBinding` gains an optional `list()` (used only by `tokens.list()`), and `KVListOptions` / `KVListResult` are exported from `/kv`. The generated Cloudflare Worker passes its `LOUSHO_TOKEN_KEY` secret to `KVStore`. Nothing signs in yet; the sign-in flow follows. New page [OAuth](/oauth); new `## OAuth` section at the end of docs/errors.md. @@ -676,7 +682,7 @@ See the 1.0.0-alpha.19 section, which covers everything from 1.0.0-alpha.15 thro - `web_fetch` built-in tool: `webFetchTool` / `createWebFetchTool(options)`, and `web-fetch` in spec files. It `GET`s one public web page and returns `{ url, finalUrl, status, contentType, content, truncated }`: HTML converted to text by a small built-in converter (scripts and styles dropped, link URLs kept, entities decoded), JSON and `text/*` as sent, a note for other types; a 4xx/5xx is returned with its body. Loopback, private and link-local destinations are refused on every hop with the connection pinned to the checked address; redirects (10), bytes read (2 MiB), characters returned (50 000) and time (30 s) are capped; `allowedHosts` / `blockedHosts` are checked before DNS and `allowPrivate` exempts named hosts. Node only. See docs/tools.md#built-in-tools. - `src/security/privateAddress.ts` (internal): `isPrivateAddress()` and `pinnedLookup()`, shared by the credential broker, `http_request` and `web_fetch`. The credential broker uses it instead of its own list, so it also refuses `192.0.0.0/24`, `198.18.0.0/15`, NAT64 and 6to4 addresses. - Slack and Discord: a pending `ask_question` survives a restart. Given durable stores for sessions, checkpoints and approvals, the next message in the Slack thread (or the next `/ask` in the Discord channel) after a restart is still the answer: the turn continues, the reply is posted, and the question, the answer and the reply are appended to the session transcript (before, the answer became a new turn and the question was orphaned). A message in a conversation that waits on a tool approval still does not decide it. New `ChannelContext.pendingQuestion(sessionKey)` for custom channels: the id of the `ask_question` that key's session waits on, also one asked before a restart (it binds the approval to the session, so the continuation is recorded); handed out once until that answer has run. A channel's answer and click continuations now run one at a time with the session's turns. Supporting additions: `Checkpoint.approvalKind`, `PendingTurn.approvalKind` (`session.pending()`) and `SessionAwaitingApprovalError.approvalKind` are `'question'` for a turn paused on an `ask_question`. Slack thread replies that answer a pending question no longer need a saved transcript first (a first turn paused on a question with checkpoints has none yet). See docs/channels.md. -- Local traces and `lousho traces`: `createAgent({ exporter, captureContent })` traces every run of the agent (`send()`, `stream()`, session turns, `agent.resume()` and runs continued by `agent.approvals.resolve()`; sub-agents join the lead's trace). The new subpath `@lousho/build-ai-agent/traces` exports `fileTraceExporter({ dir })`, which writes each run as JSON Lines to `//.jsonl` (default `.lousho/traces`; one finished span per line; a write error warns once and never fails the run), and `listTraces()` / `readTrace()` to read them. `npx @linuxdevil/lousho traces [--dir D] [--limit N] [--json]` lists recent runs with duration, model and tool calls, tokens and cost; `npx @linuxdevil/lousho traces [--json] [--content]` prints one run as a span tree with duration bars. Projects made by `lousho init` now ignore `.lousho/`. See docs/observability.md#local-traces. +- Local traces and `lousho traces`: `createAgent({ exporter, captureContent })` traces every run of the agent (`send()`, `stream()`, session turns, `agent.resume()` and runs continued by `agent.approvals.resolve()`; sub-agents join the lead's trace). The new subpath `@lousho/build-ai-agent/traces` exports `fileTraceExporter({ dir })`, which writes each run as JSON Lines to `//.jsonl` (default `.lousho/traces`; one finished span per line; a write error warns once and never fails the run), and `listTraces()` / `readTrace()` to read them. `npx lousho traces [--dir D] [--limit N] [--json]` lists recent runs with duration, model and tool calls, tokens and cost; `npx lousho traces [--json] [--content]` prints one run as a span tree with duration bars. Projects made by `lousho init` now ignore `.lousho/`. See docs/observability.md#local-traces. - `openApiTools(document, options)`: an OpenAPI 3.0 / 3.1 document (object, JSON or YAML string, or https URL) becomes one typed tool per operation, exported from the root and `./tools`. Names come from `operationId` (or `_`); the input has path, query, header and cookie parameters plus an `application/json` `body`, with `$ref`s resolved; every HTTP response is returned as `{ status, statusText, body }`. GET, HEAD and OPTIONS run and other methods ask for approval by default (`approval`), with read-only / destructive annotations. Options: `baseUrl`, `include`, `exclude`, `prefix`, `headers`, `bearerToken`, `providedArguments` (removed from the model's schema), `timeoutMs`, `maxResponseChars`, `fetch`. The base URL must be https (http only for loopback), credentials never reach events or the transcript, and redirects are followed only on the base origin. Swagger 2.0, multipart bodies and remote `$ref`s are refused. `jsonSchemaToZod(schema, root?)` takes an optional root for `$ref` resolution. New page docs/openapi-tools.md. - Built-in OpenAI, Anthropic and OpenRouter providers send PDF file parts on `ai` 6 and 7 (Anthropic also `text/plain`); `ai` 4 and Ollama keep the text note. A provider subclass opts more media types in with `fileMediaTypes()` (`acceptsFileParts = true` still sends every type). The text-note warning is now once per provider and media type and names the type and the `ai` major. Added `npm run test:live` (`vitest.live.config.ts`) for `*.live.test.ts` files, which the default suite excludes. See docs/providers.md#multimodal-input. - Principals in tools, approval policies and permissions: the run's principal reaches a tool's `execute(args, ctx)` as `ctx.principal`, `needsApproval` functions (`ApprovalCheckContext.principal`), permission rules' `when(args, ctx)` (`PermissionContext.principal`), `preToolCall` / `postToolCall` hooks (`ToolCallHookContext.principal`), `onPermissionDecision`'s new second argument (`PermissionAuditContext`), pending approvals (`PendingApproval.principal`, seen by `approve` and by a channel's `approvers` function as `request.principal`) and in-process sub-agents (`task`, background tasks, `createDelegateTool()`; `remoteAgent()` does not forward it). New `ExecuteOptions.principal`, set by `createAgent()` for `send()`, `stream()`, session turns and channel turns. It is saved in checkpoints (`Checkpoint.principal`) and approval snapshots (`ExecutionSnapshot.principal`), so a static or dynamic run resumed after an approval or a crash acts for the same caller. `agent.approvals.resolve()` / `answer()` / `streamResolve()` / `streamAnswer()` take `{ principal }` (new `ResolveApprovalOptions`) for who decides, and the approved tool sees it as `ctx.approval.by`; `resumeAfterApproval()` takes it as `executeOptions.approver`. The approvals route (`POST /chat/:sessionId/approvals/:id`) passes the caller route auth accepted, and `mountChannels()` the clicking user (`{ id, type: 'user', authenticator: }`) or, for a question answered by a message, its sender. `agent.fork()` keeps the source run's principal; `session.fork()` copies only the conversation. The principal is not added to events, spans or trace files. See [Principals in tools and approvals](/auth#principals-in-tools-and-approvals). @@ -685,7 +691,7 @@ See the 1.0.0-alpha.19 section, which covers everything from 1.0.0-alpha.15 thro - `SlackTriggerAdapter`, `CronTriggerAdapter` and `WebhookTriggerAdapter` (`@lousho/build-ai-agent/triggers`) are deprecated and will be removed in a future major version. Use `slackChannel()` for Slack (sessions per thread, approval buttons), `defineSchedule()` with `startSchedules()` for cron (or `schedules/` in an agent directory, or cron triggers in a spec file), and `webhookChannel()` for webhooks, each mounted with `mountChannels()`. `verifySlackSignature()`, the `WebhookAuth` helpers, `parseCronExpression()`, `TriggerAdapter` and `TriggerRegistry` are not deprecated. Their options types (`SlackTriggerAdapterOptions`, `CronTriggerAdapterOptions` with `CronIntervalOptions` and `CronExpressionOptions`, `WebhookTriggerAdapterOptions`) and `WebhookTriggerHandle` carry `@deprecated` too. Nothing changes at run time. See docs/triggers.md. ### Fixed -- `@linuxdevil/lousho` CLI surface: `lousho dev`/`lousho chat --traces` now writes traces for a `.ts`/`.js` module that exports an already-built `createAgent()` agent - the module's export could not take the flag's exporter after the fact, so nothing was written. `createAgent()` remembers the config it was built with (readable via the internal `createAgentConfigOf()`), and the loader rebuilds the agent with the flag's exporter (the module's own `exporter` is replaced while the flag is set; a module exporting an agent not made by `createAgent()` prints a warning and keeps its own). A spec path that does not exist (e.g. `lousho chat missing.yaml`) now fails with the coded `LOUSHO_SPEC_NOT_FOUND` instead of a raw ENOENT. `lousho add --dry-run` no longer fails when a target file already exists; it prints the file list with an "(exists - a real install needs --overwrite)" marker and exits 0 (the symlink checks still run). Top-level `lousho --help` lists the implemented flags again: `--no-schedules`/`--traces` on `dev` and `chat`, `eval`'s record/replay/drift/`--config` flags, `init`'s `--sdk-path`, and `build`'s positional form. +- `lousho` CLI surface: `lousho dev`/`lousho chat --traces` now writes traces for a `.ts`/`.js` module that exports an already-built `createAgent()` agent - the module's export could not take the flag's exporter after the fact, so nothing was written. `createAgent()` remembers the config it was built with (readable via the internal `createAgentConfigOf()`), and the loader rebuilds the agent with the flag's exporter (the module's own `exporter` is replaced while the flag is set; a module exporting an agent not made by `createAgent()` prints a warning and keeps its own). A spec path that does not exist (e.g. `lousho chat missing.yaml`) now fails with the coded `LOUSHO_SPEC_NOT_FOUND` instead of a raw ENOENT. `lousho add --dry-run` no longer fails when a target file already exists; it prints the file list with an "(exists - a real install needs --overwrite)" marker and exits 0 (the symlink checks still run). Top-level `lousho --help` lists the implemented flags again: `--no-schedules`/`--traces` on `dev` and `chat`, `eval`'s record/replay/drift/`--config` flags, `init`'s `--sdk-path`, and `build`'s positional form. - A cassette failure keeps its type through `agent.send()`: a replay mismatch (or a missing cassette raised by the provider-interception seam that `lousho eval` installs) was compacted into a generic `CompactedLLMProviderError`, losing the cassette path, the call number and the re-record hint. `CassetteMismatchError` is now an `SDKError` with code `LOUSHO_CASSETTE_INVALID` (docs/errors.md already claimed cassette errors carry it), and the run loop rethrows cassette errors untouched instead of compacting them, so `send()` rejects with the `CassetteMismatchError` itself (`.cassette`, `.callNumber`). `setProviderInterceptor()` and the `ProviderInterceptor` type are now exported from `@lousho/build-ai-agent/testing`, the public entry for the model-boundary seam docs/evals.md describes. - A tool call approved with `agent.approvals.resolve()` (or `resumeAfterApproval()`) now has its `execute_tool` span. It ran before the continued run started, outside any span, so OpenTelemetry and `fileTraceExporter()` showed the continued run's `invoke_agent` and `chat` spans but not the approved tool. The continued run's `invoke_agent` span now opens first and the approved call runs inside it: its `execute_tool` span is a child of it (a `run_code` call's inner calls and a sub-agent the tool starts are children of the tool span), with the same `captureContent` / `redactContent` rules as any tool span, and no span is made for a rejected call. A pause that happens again (sign-in) leaves the `invoke_agent` span with the tool span and no `chat`. - A tool that returns `undefined` (or an async generator that yields nothing) no longer writes a `tool` message with `content: undefined` into the transcript. Its result is now `null` everywhere: the transcript and the next model request carry `"null"`, and `tool.done`, approval resumes, `run_code` and flows see `null`. Providers, session stores and checkpoints get a valid string. @@ -779,7 +785,7 @@ See the 1.0.0-alpha.19 section, which covers everything from 1.0.0-alpha.15 thro ### Added - Remote sub-agent approvals go through the lead run: a `remoteAgent()` task whose remote run pauses for a tool approval now pauses the lead run the way a local sub-agent does (it used to fail with `LOUSHO_SESSION_AWAITING_APPROVAL`): the lead's pending approval (`agent.approvals.list()`, channel buttons, the dev chat, ACP permission requests) has the remote tool's name and input and `subagentPath: []`, and a remote `ask_question` arrives as a question. Deciding it on the lead (`resolve`, `streamResolve`, `answer`) posts the decision to the remote `POST /chat/:sessionId/approvals/:id` and the continuation's final answer is the `task` result; a further pause pauses the lead again. The lead's approval snapshot stores the remote session id, the remote approval id, the `taskId` and the agent name (never the token), so a fresh lead process on the same store can decide it. Failures while deciding (401, other non-2xx including the remote's 404 for an approval no longer pending, network) are the `task` call's structured tool error with `LOUSHO_REMOTE_UNAUTHORIZED` / `LOUSHO_REMOTE_REQUEST_FAILED`. A remote agent with an `output` schema now returns its object as JSON with the footer (the V4.2 shape), read from `run.done`'s `object`. `RemoteSubagent.run()` takes `pausable` and `decision` (type `RemoteRunOptions`); the internal session client gains `resolveRemoteApproval()` and `SessionTurnSummary.object`. Only a lead run without an approval store keeps the old `LOUSHO_SESSION_AWAITING_APPROVAL` error. See docs/sub-agents.md#remote-approvals. - Structured output for sessions and sub-agents, typed for either zod major: `createAgent({ output })` and `ExecuteOptions.output` accept a zod 3 schema, a zod 4 schema (`zod/v4` on zod 3.25, or zod 4) or a Standard Schema that can produce JSON Schema, and `result.object` is inferred from each without a cast (`InferSchemaOutput`, as `defineTool`). `AgentSession` is generic (`AgentSession`): `agent.session().send()` and `.stream()` results carry the typed `object`. A sub-agent with its own `output` returns its validated object as JSON (then the `taskId` footer) as the `task` and `agent_await` result; its `output-invalid` finish is a structured tool error. Sub-agents do not inherit the lead's `output`; `remoteAgent()` returns text only. The exported spec schemas (`agentSpecSchema`, ...) are typed as `SpecSchema` / `SpecObjectSchema`, so the published `schema-*.d.ts` no longer depends on zod 3 generics (`skipLibCheck: false` projects on zod 4). The Ollama missing-peer note now says `ollama-ai-provider-v2` needs zod 4 (`npm install zod@^4.0.0`). See docs/structured-output.md. -- Publish readiness: `npm run pack-smoke` (scripts/pack-smoke.ts, a CI job) packs the SDK and `create-lousho-agent`, checks the tarball (no `.env`, tests or secret-looking strings; entry count and size caps), runs `npm publish --dry-run` for both (nothing is published), installs the tarballs plus peers from the registry into a fresh project and verifies ESM and CJS loads of every `exports` entry, a mock-model agent turn, the `@linuxdevil/lousho` bin (`--help`, `doctor`) and `tsc` with `moduleResolution` bundler and node16. +- Publish readiness: `npm run pack-smoke` (scripts/pack-smoke.ts, a CI job) packs the SDK and `create-lousho-agent`, checks the tarball (no `.env`, tests or secret-looking strings; entry count and size caps), runs `npm publish --dry-run` for both (nothing is published), installs the tarballs plus peers from the registry into a fresh project and verifies ESM and CJS loads of every `exports` entry, a mock-model agent turn, the `lousho` bin (`--help`, `doctor`) and `tsc` with `moduleResolution` bundler and node16. - One event system: `AgentEvent` listeners for callers who do not iterate a run. `createAgent({ onEvent: (event: AgentEvent) => void })` and the new `ExecuteOptions.onAgentEvent` (also taken by `AgentExecutor.stream()` and `resumeAfterApproval()`) are called synchronously with every `AgentEvent` of the run, on `send()` / `execute()` as on `stream()`, sub-agents' events included: the same events in the same order as the stream yields (a non-streamed run generates each model step whole, so its text is one `text.delta` per step). A run with a listener also reports hook events, permission decisions, budgets and guardrails to it when it is not streamed. A non-streamed `resumeAfterApproval()` with a listener now reports the decided call (`run.start`, `tool.start`, `tool.done`) like a streamed one. See docs/streaming.md#listening-without-iterating. - Reasoning: new `reasoning` option on `createAgent()`, `send()` / `stream()` (per call, overriding the agent's), `ExecuteOptions` and `GenerateOptions`: `'none' | 'minimal' | 'low' | 'medium' | 'high'` or `{ effort?, budgetTokens?, summary?: 'auto' | 'none', force? }` (types `ReasoningOption`, `ReasoningEffort`, `ReasoningSettings`). The built-in providers send it through `providerOptions` on `ai` 4.3 and 6/7: OpenAI `reasoningEffort` (+ `reasoningSummary`), Anthropic `thinking: { type: 'enabled', budgetTokens }` (effort table 1024 / 2048 / 8192 / 24576 tokens), Ollama `think` (`ollama-ai-provider-v2` only; ignored with one warning on `ai` 4), and OpenRouter's unified `reasoning` body field (`effort` or `max_tokens`). It is only sent to model families known to accept it (`force: true` overrides); `'none'` sends nothing. New events `reasoning.start`, `reasoning.delta` (`text`) and `reasoning.done` (`text`, `tokens?`) in the `AgentEvent` union, emitted inside the step before its first text or tool call, from `ai` v4 `reasoning` parts and v6/v7 `reasoning-start/delta/end`; `ExecutionResult.reasoning` holds the run's reasoning text and `GenerateResult.reasoning` a call's blocks (new type `ReasoningBlock`); new stream chunk types `reasoning-delta` / `reasoning-end` (`StreamChunk.reasoning`). Reasoning is not written to the transcript as text; an assistant tool-call turn keeps Anthropic's signed or redacted thinking blocks in the new `Message.reasoning`, which the Anthropic provider sends back unmodified (it survives checkpoints and resumes). The UI reducer keeps `UIMessage.reasoning`, `toUIMessageStream()` emits `reasoning-start` / `reasoning-delta` / `reasoning-end`, `lousho acp` sends `agent_thought_chunk`, `lousho chat` prints reasoning dimmed, and `chat` spans carry `lousho.usage.reasoning_tokens`. Cassettes record the reasoning chunks. See docs/reasoning.md. - Tools know their session: `ToolExecutionContext.sessionId` is now set whenever the run has one: `AgentExecutor.execute({ sessionId })`, `send({ sessionId })`, every `agent.session()` turn (the session id; a checkpointed turn's own `.turn-`, as hooks see it), approval resumes (the paused run's), and sub-agents (`/`). A session turn's run now carries the session id even without a checkpoint store (no checkpoint is written), so hooks and the `invoke_agent` span's conversation id see it too. @@ -810,7 +816,7 @@ See the 1.0.0-alpha.19 section, which covers everything from 1.0.0-alpha.15 thro - Svelte store: new `@lousho/build-ai-agent/svelte` subpath with `loushoAgent(source, options?)`. It returns a Svelte store (`subscribe(run) => unsubscribe`, so `$agent` works in Svelte 4 and 5) whose value is the same state as the React hook and the Vue composable (`messages`, `status`, `pendingApproval`, `error`, `usage`, `lastEvent`), plus `send()`, `stop()`, `approve()`, `reject()`, `answer()` and `reset()`. The run in flight is aborted by `stop()` and when the last subscriber unsubscribes. The store contract is implemented by hand: the entry imports nothing from `svelte`, so there is no new peer dependency. See docs/svelte.md. - Per-run dynamic config: `createAgent()`'s `model`, `instructions` / `prompt` and `tools` each also take a function of the run, `(ctx) => value | Promise` (new types `PerRun` and `RunConfigContext` = `{ sessionId?, input, metadata? }`), resolved once at the start of each run and of each session turn, before the first model call. `fallbackModels`, `retry`, `projectInstructions`, memory, MCP tools, sub-agents, permissions, approvals and guardrails apply to the resolved values; a dynamic agent used as a sub-agent resolves with the task prompt as `input`. `session.send()` / `session.stream()` gain the `metadata` option (`send()` / `stream()` already had it, and now pass it to these functions too). A function that throws fails the run with the new `LOUSHO_CONFIG_RESOLVER_FAILED` (`field` names the option, `cause` is the error); a session keeps its transcript. A run paused for approval stores its `ctx` and resolved model in the approval snapshot (`agent.metadata.loushoRunConfig`): resuming it uses that model and re-resolves the tools with the same `ctx`; a crash resume from a checkpoint resolves with `input: []`. Static options behave and type as before. `SessionRunner` / `SessionStreamRunner` take an optional fourth `call` argument (new type `SessionTurnCall`). See docs/api-overview.md#dynamic-config. - Vue composable: new `@lousho/build-ai-agent/vue` subpath with `useLoushoAgent(source, options?)` for Vue 3. It takes the same sources and options as the React hook (either may be a `ref`, read when a turn starts) and returns `messages`, `status`, `pendingApproval`, `error`, `usage` and `lastEvent` as read-only refs, plus `send()`, `stop()`, `approve()`, `reject()`, `answer()` and a new `reset()` (aborts, forgets the in-process session, clears the chat). The run in flight is aborted by `stop()` and when the owning scope is disposed (`onScopeDispose`: component unmount, `effectScope().stop()`). `vue` (`^3`) is an optional peer dependency, loaded by no other entry. To share the logic, the reducer and the stream parser moved from `src/react/` to a framework-neutral `src/ui/`, together with the run logic extracted from the React hook (`createAgentRunner()`); the `./react` exports are unchanged, and the reducer gains an additive `ui.reset` action. See docs/vue.md. -- One flag parser for the `@linuxdevil/lousho` CLI: every command (`dev`, `chat`, `eval`, `build`, `mcp`, `doctor`, `studio`; `init` already did) parses its flags with `node:util` `parseArgs` in strict mode through `src/cli/args.ts`. `--flag value` and `--flag=value` work everywhere, `--` ends the flags, `-h` / `--help` prints the usage and exits 0, and an unknown flag, a flag without its value or an extra argument fails with `LOUSHO_CONFIG_INVALID` and the command's usage as the hint. **Behaviour change (migration):** `dev`, `studio`, `mcp`, `build` and `doctor` used to ignore unknown flags, and a flag missing its value could take the next flag or a prefix match (`--portx 1` read as `--port`); they now fail with exit 1 and the usage line, so remove typos from scripts. A value that starts with `-` must be written `--flag=-x` (a bare `--flag -x` is a missing value); `--port` must be an integer from 0 to 65535 for `dev`, `studio` and `mcp`; `dev`, `mcp` and `doctor` reject a second path; `studio --prod --dev` is rejected instead of preferring `--prod`. `lousho dev` and `lousho studio` parsing moved from `bin/lousho.js` into `src/cli/dev.ts` / `src/cli/studio.ts` (`parseDevArgs`, `runDev`, `parseStudioArgs`, `runStudio`). See docs/cli.md. +- One flag parser for the `lousho` CLI: every command (`dev`, `chat`, `eval`, `build`, `mcp`, `doctor`, `studio`; `init` already did) parses its flags with `node:util` `parseArgs` in strict mode through `src/cli/args.ts`. `--flag value` and `--flag=value` work everywhere, `--` ends the flags, `-h` / `--help` prints the usage and exits 0, and an unknown flag, a flag without its value or an extra argument fails with `LOUSHO_CONFIG_INVALID` and the command's usage as the hint. **Behaviour change (migration):** `dev`, `studio`, `mcp`, `build` and `doctor` used to ignore unknown flags, and a flag missing its value could take the next flag or a prefix match (`--portx 1` read as `--port`); they now fail with exit 1 and the usage line, so remove typos from scripts. A value that starts with `-` must be written `--flag=-x` (a bare `--flag -x` is a missing value); `--port` must be an integer from 0 to 65535 for `dev`, `studio` and `mcp`; `dev`, `mcp` and `doctor` reject a second path; `studio --prod --dev` is rejected instead of preferring `--prod`. `lousho dev` and `lousho studio` parsing moved from `bin/lousho.js` into `src/cli/dev.ts` / `src/cli/studio.ts` (`parseDevArgs`, `runDev`, `parseStudioArgs`, `runStudio`). See docs/cli.md. - `sqliteMemory(store, { maxItems? })` (exported from `@lousho/build-ai-agent/sqlite`) keeps `defineMemory` slots in the agent's `SqliteStore` file: a new `memory_items` table (migration 3, one JSON array per scope key, with the same serialization, `maxItems` and query rules as `fileMemory`). An existing database gains the table on open and keeps its sessions and checkpoints. The memory provider contract tests now run against the in-memory, file and SQLite providers. See docs/memory.md#sqlite. - Slack channel: `slackChannel({ signingSecret, botToken, name?, fetch? })` (new export, with types `SlackChannelOptions`, `SlackChannelEvent`, `SlackThread`) serves a Slack app's Events API and interactivity requests through `mountChannels()`. It verifies the `v0` request signature with Web Crypto (no `node:*` import; the timestamp and header checks are shared with `verifySlackSignature()`), answers the `url_verification` challenge, and acknowledges every request before the turn runs. Each Slack thread is one session (team + channel + thread root `ts`): a mention starts or continues it, and later messages in a thread the bot was mentioned in continue it. Retries (`X-Slack-Retry-Num`) and bot messages are skipped. Replies go to the thread with `chat.postMessage` over `fetch`; a tool approval is posted as Approve / Deny buttons whose click resumes the session, and an `ask_question` as text answered by the next thread message. The channel contract gains two additive hooks for this: `parse(req, respond)` may call `respond` to answer the request before the turn runs, and may return `{ decision }` (new type `ChannelDecision`; `ChannelApprovalDecision` now lives in `defineChannel.ts`, same public export) to resolve a pause instead of starting a turn. See docs/channels.md#slack. - Remote sub-agents: `remoteAgent({ url, auth?, name?, description?, headers?, fetch? })` (new export, with types `RemoteAgentOptions` and `RemoteSubagent`) makes a deployed agent usable wherever a local sub-agent is: in `createAgent({ subagents })` records and `SubagentCatalog.resolve()`, delegated to by the `task` tool and by `background: true` tasks. Each task opens a fresh session on the remote agent over `POST /chat` (bearer `auth`: a string or a function, called per task), reads the SSE stream to the end and returns the remote agent's final text; the lead run's abort signal aborts the request. Failures (network, 401 or other non-2xx, malformed stream, remote run error) are structured tool errors with the new code `LOUSHO_REMOTE_AGENT_FAILED`, and the token never appears in errors or events; a remote run that pauses for approval fails with `LOUSHO_SESSION_AWAITING_APPROVAL` naming the remote session (proxying approvals is a follow-up). Worker-safe (`fetch` only). See docs/sub-agents.md#remote-sub-agents. @@ -897,7 +903,7 @@ See the 1.0.0-alpha.19 section, which covers everything from 1.0.0-alpha.15 thro - README revamped into a short front page; the details it dropped moved to `docs/` (new pages: tools, approvals, providers, cli, flows, guardrails, utilities). ### Renamed -- The project is now **lousho** (it was `loushy`), before the first npm release, so nothing was ever published under the old name. Everything that carried the name changed with it, and this changelog uses the new names throughout, including in older entries: the package `@lousho/build-ai-agent` (was `@loushy/build-ai-agent`), the `@linuxdevil/lousho` CLI (was `loushy`), `create-lousho-agent` (was `create-loushy-agent`), the exports `useLoushoAgent`, `loushoAgent`, `LoushoAgentSource`, `LoushoUIMessageChunk` and the other `Lousho*` types, every `LOUSHO_*` error code and environment variable (`LOUSHO_MODEL`, `LOUSHO_API_TOKEN`, `LOUSHO_STORE`, ...), the `.lousho/` directory, the `lousho.*` span attributes and the `data-lousho-approval` stream part. Migration for a checkout that used the old name: replace `loushy` with `@linuxdevil/lousho` (keeping the case) in imports, scripts, environment variables and config, and rename an existing `.loushy/` directory to `.lousho/`. +- The project is now **lousho** (it was `loushy`), before the first npm release, so nothing was ever published under the old name. Everything that carried the name changed with it, and this changelog uses the new names throughout, including in older entries: the package `@lousho/build-ai-agent` (was `@loushy/build-ai-agent`), the `lousho` CLI (was `loushy`), `create-lousho-agent` (was `create-loushy-agent`), the exports `useLoushoAgent`, `loushoAgent`, `LoushoAgentSource`, `LoushoUIMessageChunk` and the other `Lousho*` types, every `LOUSHO_*` error code and environment variable (`LOUSHO_MODEL`, `LOUSHO_API_TOKEN`, `LOUSHO_STORE`, ...), the `.lousho/` directory, the `lousho.*` span attributes and the `data-lousho-approval` stream part. Migration for a checkout that used the old name: replace `loushy` with `lousho` (keeping the case) in imports, scripts, environment variables and config, and rename an existing `.loushy/` directory to `.lousho/`. ### Published - `@lousho/build-ai-agent` and `create-lousho-agent` are on npm. The README and the installation, quick start, CLI and Agent Forge docs no longer say the package is unpublished; "Installing before the first release" is now "Installing from a local build" (docs/installation.md#installing-from-a-local-build) and covers trying an unreleased commit. The hint printed after a failed `lousho init` install no longer mentions a 404; it points to `--sdk-path`. The docs site moved to https://lousho.com. diff --git a/channels.mdx b/channels.mdx index f69d67b..fa3a26e 100644 --- a/channels.mdx +++ b/channels.mdx @@ -46,7 +46,7 @@ const channels = mountFetchChannels(agent, [slack], { waitUntil: (turn) => void ```ts import * as http from 'node:http'; -import { createAgent, createMockProvider, httpChannel, mountChannels } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider, httpChannel, mountChannels } from 'lousho'; const agent = createAgent({ instructions: 'You are helpful.', provider: createMockProvider() }); const channels = mountChannels(agent, [ @@ -121,7 +121,7 @@ Discord, Telegram, Teams, GitHub) keeps its own answer and tells the user the request failed in the conversation. ```ts -import { createAgent, defineChannel, mountChannels } from '@linuxdevil/lousho'; +import { createAgent, defineChannel, mountChannels } from 'lousho'; const agent = createAgent({ instructions: 'You answer text messages.', provider }); @@ -205,7 +205,7 @@ dropped silently once the surface was acknowledged. `resolveApproval()` throws `LOUSHO_APPROVAL_CONFLICT` for an approval another request is deciding. ```ts -import { createAgent, defineChannel, mountChannels } from '@linuxdevil/lousho'; +import { createAgent, defineChannel, mountChannels } from 'lousho'; const agent = createAgent({ instructions: 'You are helpful.', provider, askQuestion: true }); @@ -250,7 +250,7 @@ the verified request (the alerting system that sent the hook, an integration user); `body` is the parsed JSON body, `undefined` when it is not JSON. ```ts -import { createAgent, httpChannel, mountChannels, webhookChannel } from '@linuxdevil/lousho'; +import { createAgent, httpChannel, mountChannels, webhookChannel } from 'lousho'; const agent = createAgent({ instructions: 'You are helpful.', provider }); @@ -343,7 +343,7 @@ Set up the app at [api.slack.com/apps](https://api.slack.com/apps): ```ts import * as http from 'node:http'; -import { createAgent, mountChannels, slackChannel } from '@linuxdevil/lousho'; +import { createAgent, mountChannels, slackChannel } from 'lousho'; const agent = createAgent({ instructions: 'You are a helpful Slack bot.', provider }); @@ -418,7 +418,7 @@ curl -X POST "https://discord.com/api/v10/applications/$APP_ID/guilds/$GUILD_ID/ ```ts import * as http from 'node:http'; -import { createAgent, discordChannel, mountChannels } from '@linuxdevil/lousho'; +import { createAgent, discordChannel, mountChannels } from 'lousho'; const agent = createAgent({ instructions: 'You are a helpful Discord bot.', provider }); @@ -502,7 +502,7 @@ curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" -H "Co ```ts import * as http from 'node:http'; -import { createAgent, mountChannels, telegramChannel } from '@linuxdevil/lousho'; +import { createAgent, mountChannels, telegramChannel } from 'lousho'; const agent = createAgent({ instructions: 'You are a helpful Telegram bot.', provider }); @@ -554,7 +554,7 @@ so it also runs on Workers. them somewhere shared and durable, e.g. Redis `SET NX EX`: ```ts - import { githubChannel, type DeliveryStore } from '@linuxdevil/lousho'; + import { githubChannel, type DeliveryStore } from 'lousho'; const seenIds = new Set(); // stands in for Redis or a database table const deliveries: DeliveryStore = { seen: (id) => seenIds.has(id) || (seenIds.add(id), false) }; @@ -655,7 +655,7 @@ agent from answering its own comments. ```ts import * as http from 'node:http'; -import { createAgent, githubChannel, mountChannels } from '@linuxdevil/lousho'; +import { createAgent, githubChannel, mountChannels } from 'lousho'; const agent = createAgent({ instructions: 'You answer questions about this repository.', provider }); @@ -751,7 +751,7 @@ Set up the bot: ```ts import * as http from 'node:http'; -import { createAgent, mountChannels, teamsChannel } from '@linuxdevil/lousho'; +import { createAgent, mountChannels, teamsChannel } from 'lousho'; const agent = createAgent({ instructions: 'You help the team with questions.', provider }); diff --git a/claude-projects.mdx b/claude-projects.mdx index 57cdc98..a2eaad5 100644 --- a/claude-projects.mdx +++ b/claude-projects.mdx @@ -10,8 +10,8 @@ into `createAgent()`, so the same directory drives both harnesses. Nothing is copied or renamed; Lousho reads the files where they already are. ```ts -import { createAgent, claudeProject } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, claudeProject } from 'lousho'; +import { mockModel } from 'lousho/testing'; const provider = mockModel([{ text: 'ok' }]); @@ -68,8 +68,8 @@ task-shaped notes, hooks can pause on `scratchpad` writes like any tool. ## Options ```ts -import { claudeProject } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { claudeProject } from 'lousho'; +import { mockModel } from 'lousho/testing'; const provider = mockModel([{ text: 'ok' }]); diff --git a/cli.mdx b/cli.mdx index d3b6506..61401a4 100644 --- a/cli.mdx +++ b/cli.mdx @@ -3,10 +3,10 @@ title: "CLI" --- {/* Generated from docs/cli.md in LinuxDevil/lousho by scripts/sync-sdk-docs.mjs. Do not edit here. */} -Installing the package also installs the `@linuxdevil/lousho` command. It scaffolds +Installing the package also installs the `lousho` command. It scaffolds projects, checks your setup, runs an agent locally, serves it over MCP, runs evals, shows saved traces, builds a deployable artifact and opens Lousho Studio. -Run it with `npx @linuxdevil/lousho ` inside a project that has the SDK installed. +Run it with `npx lousho ` inside a project that has the SDK installed. | Command | What it does | Details | | ------- | ------------ | ------- | @@ -20,7 +20,7 @@ Run it with `npx @linuxdevil/lousho ` inside a project that has the SDK | `lousho eval [globs...]` | Run `*.eval.ts` files under vitest; print a summary and write JUnit/JSON reports. | [Evals](/evals#lousho-eval) | | `lousho traces [id]` | List the recent runs saved by `fileTraceExporter()` (duration, model and tool calls, tokens, cost), or print one run as a span tree. | [Observability](/observability#local-traces) | | `lousho build --target= --agent=` | Build a deployable Node server, Docker image or Cloudflare Worker. | [Deployment](/deployment) | -| `lousho studio` | Open Lousho Studio (the separate `@linuxdevil/studio` package) on this project. | [Lousho Studio](/agent-forge) | +| `lousho studio` | Open Lousho Studio (the separate `lousho-studio` package) on this project. | [Lousho Studio](/agent-forge) | | `lousho bridge` | Speak Studio's bridge protocol on stdio; started by Studio, not run by hand. | [Below](#lousho-bridge) | | `lousho workspace ` | Create, inspect, validate and run a workspace from a folder or a running service. | [Workspaces](https://github.com/LinuxDevil/lousho/blob/main/docs/workspaces.md) | | `lousho observe ` | Run the local collector every agent reports to when `LOUSHO_OBSERVE` is set: `start`, `status`, `stop`, `rebuild-rollups`, `prune`, `export`, `failures`, `score`, `token`. | [Below](#lousho-observe) | @@ -84,18 +84,18 @@ not the shared `LOUSHO_CONFIG_INVALID` path. The only short flags besides instead of the generated one; globs then narrow that config's files as vitest filters. -`npm create @linuxdevil/lousho my-agent` runs `lousho init` with the same arguments. +`npm create lousho my-agent` runs `lousho init` with the same arguments. To scaffold against an unreleased build of the SDK, pass `--sdk-path` (see [Installing from a local build](/installation#installing-from-a-local-build)). ## `lousho dev` ```bash -npx @linuxdevil/lousho dev agent.yaml # a spec file -npx @linuxdevil/lousho dev ./my-agent # an agent directory -npx @linuxdevil/lousho dev src/agent.ts # a TypeScript (or JavaScript) module -npx @linuxdevil/lousho dev agent.yaml --port 4000 --host 0.0.0.0 -npx @linuxdevil/lousho dev ./my-agent --no-schedules # do not fire cron schedules or a spec's cron triggers +npx lousho dev agent.yaml # a spec file +npx lousho dev ./my-agent # an agent directory +npx lousho dev src/agent.ts # a TypeScript (or JavaScript) module +npx lousho dev agent.yaml --port 4000 --host 0.0.0.0 +npx lousho dev ./my-agent --no-schedules # do not fire cron schedules or a spec's cron triggers ``` For an agent directory, `channels/` are mounted under `/channels` and @@ -134,7 +134,7 @@ plus a free-text field. The same endpoints work from `curl` or your own page: `--traces` (or `--traces=`) writes every run to local trace files with `fileTraceExporter()`, in `.lousho/traces` unless you name a directory, so -`npx @linuxdevil/lousho traces` shows what the agent did. It is off by default because the +`npx lousho traces` shows what the agent did. It is off by default because the files hold prompt, tool argument and tool result content, and the flag prints the directory it writes to. It applies to spec files, agent directories and modules alike (a module's own `exporter` is replaced while the flag is set). @@ -197,9 +197,9 @@ Directories and modules are your code and run with your permissions; only run ## `lousho chat` ```bash -npx @linuxdevil/lousho chat agent.yaml # a spec file -npx @linuxdevil/lousho chat ./my-agent --model openai/gpt-4o # an agent directory, on another model -npx @linuxdevil/lousho chat src/agent.ts --store sqlite:.lousho/chat.db --session support +npx lousho chat agent.yaml # a spec file +npx lousho chat ./my-agent --model openai/gpt-4o # an agent directory, on another model +npx lousho chat src/agent.ts --store sqlite:.lousho/chat.db --session support ``` A terminal REPL: each line you type is one turn of a session. `` is @@ -258,9 +258,9 @@ scripted lines and a `mockModel` agent and no terminal. ## `lousho build` ```bash -npx @linuxdevil/lousho build --target=node-server --agent=agent.yaml # or docker / cloudflare-worker -npx @linuxdevil/lousho build ./my-agent --target=node-server # an agent directory (node-server, docker) -npx @linuxdevil/lousho build src/agent.ts --target=node-server # an agent module (node-server, docker) +npx lousho build --target=node-server --agent=agent.yaml # or docker / cloudflare-worker +npx lousho build ./my-agent --target=node-server # an agent directory (node-server, docker) +npx lousho build src/agent.ts --target=node-server # an agent module (node-server, docker) ``` Writes the artifact to `--out` (default `.lousho/build/`) and prints @@ -271,15 +271,15 @@ Workers limits are in [Deployment](/deployment). ## `lousho studio` ```bash -npx @linuxdevil/lousho studio # prints http://127.0.0.1:4750/?token= -npx @linuxdevil/lousho studio --port 5000 +npx lousho studio # prints http://127.0.0.1:4750/?token= +npx lousho studio --port 5000 ``` Opens Lousho Studio on this project. Studio is a separate package -(`@linuxdevil/studio`): install it with `npm i -g @linuxdevil/studio` (or run -`npx @linuxdevil/studio`); this command finds it and opens the current folder +(`lousho-studio`): install it with `npm i -g lousho-studio` (or run +`npx lousho-studio`); this command finds it and opens the current folder as a workspace. When no Studio is installed it prints -`Studio is a separate install: npx @linuxdevil/studio` and exits. +`Studio is a separate install: npx lousho-studio` and exits. Options: `--port N` and `--host H` pass through to Studio (defaults 4750 and `127.0.0.1`). `--prod` and `--dev` only apply inside the SDK's own repository @@ -294,22 +294,22 @@ lousho bridge Speaks Studio's bridge protocol (NDJSON) on stdin/stdout so Studio can build and run agents with this project's own SDK. Studio starts it for each -workspace that has `@linuxdevil/lousho` installed; you do not run it +workspace that has `lousho` installed; you do not run it yourself. ## `lousho observe` ```bash -npx @linuxdevil/lousho observe start # collector on http://127.0.0.1:7777 -npx @linuxdevil/lousho observe status # health + fleet counts -npx @linuxdevil/lousho observe stop # .pid + SIGTERM (checks the daemon serves --db) -npx @linuxdevil/lousho observe rebuild-rollups # recompute insight aggregates -npx @linuxdevil/lousho observe prune --older-than 30d # delete finished runs older than 30 days, then VACUUM -npx @linuxdevil/lousho observe export --since 7d --out runs.jsonl # traces as JSONL, one per line -npx @linuxdevil/lousho observe failures --since 7d # recurring tool and model failures, by agent -npx @linuxdevil/lousho observe failures --traces .lousho/traces --json # the same from trace files, no database -npx @linuxdevil/lousho observe score --agent support # continuous score, pass rate and trajectory gate state -npx @linuxdevil/lousho observe token create --project myapp # bearer key for non-loopback auth +npx lousho observe start # collector on http://127.0.0.1:7777 +npx lousho observe status # health + fleet counts +npx lousho observe stop # .pid + SIGTERM (checks the daemon serves --db) +npx lousho observe rebuild-rollups # recompute insight aggregates +npx lousho observe prune --older-than 30d # delete finished runs older than 30 days, then VACUUM +npx lousho observe export --since 7d --out runs.jsonl # traces as JSONL, one per line +npx lousho observe failures --since 7d # recurring tool and model failures, by agent +npx lousho observe failures --traces .lousho/traces --json # the same from trace files, no database +npx lousho observe score --agent support # continuous score, pass rate and trajectory gate state +npx lousho observe token create --project myapp # bearer key for non-loopback auth ``` Runs the local collector every agent reports to when `LOUSHO_OBSERVE` is @@ -360,11 +360,11 @@ no daemon needs to be running. See [Export](/observability#export). ## `lousho prompt` ```bash -npx @linuxdevil/lousho prompt push prompt.md --name mira # publishes a new version -npx @linuxdevil/lousho prompt list -npx @linuxdevil/lousho prompt get mira@latest -npx @linuxdevil/lousho prompt label mira prod 3 # mira@prod -> 3 (was 2) -npx @linuxdevil/lousho prompt get mira@prod +npx lousho prompt push prompt.md --name mira # publishes a new version +npx lousho prompt list +npx lousho prompt get mira@latest +npx lousho prompt label mira prod 3 # mira@prod -> 3 (was 2) +npx lousho prompt get mira@prod ``` Publishes and reads prompt versions in the observe daemon's registry. Agents @@ -388,7 +388,7 @@ resolve them with `createAgent({ prompt: 'mira@latest' })` (or a label, Experimental, like the [`improveAgent()`](/improve) it runs. ```bash -npx @linuxdevil/lousho improve ./my-agent --check ./check.mjs --since 7d --out proposal.md +npx lousho improve ./my-agent --check ./check.mjs --since 7d --out proposal.md ``` ```js @@ -487,9 +487,9 @@ under `.lousho/revisions/` and recorded in the hash-chained `.lousho/audit.jsonl`. Three subcommands read and undo that history: ```bash -npx @linuxdevil/lousho improve revisions ./my-agent -npx @linuxdevil/lousho improve rollback ./my-agent --by ali --reason "answers got worse" -npx @linuxdevil/lousho improve audit ./my-agent --verify +npx lousho improve revisions ./my-agent +npx lousho improve rollback ./my-agent --by ali --reason "answers got worse" +npx lousho improve audit ./my-agent --verify ``` - `revisions [--json]` lists the saved revisions, newest first, with their @@ -519,9 +519,9 @@ In manual mode a proposal that passed the gate waits in `.lousho/proposals/.json` until a person decides: ```bash -npx @linuxdevil/lousho improve pending ./my-agent -npx @linuxdevil/lousho improve approve ./my-agent 3f9c0a1b2d4e --by ali --check ./check.mjs -npx @linuxdevil/lousho improve reject ./my-agent 3f9c0a1b2d4e --by ali --reason "too broad" +npx lousho improve pending ./my-agent +npx lousho improve approve ./my-agent 3f9c0a1b2d4e --by ali --check ./check.mjs +npx lousho improve reject ./my-agent 3f9c0a1b2d4e --by ali --reason "too broad" ``` - `pending [--show ] [--json]` lists the queue, newest first, marking @@ -558,9 +558,9 @@ npx @linuxdevil/lousho improve reject ./my-agent 3f9c0a1b2d4e --by ali --reason `--watch` keeps the loop running on an agent directory: ```bash -npx @linuxdevil/lousho improve ./my-agent --check ./check.mjs --watch --interval 15m -npx @linuxdevil/lousho improve pause ./my-agent --reason "release freeze" --by ali -npx @linuxdevil/lousho improve resume ./my-agent --by ali +npx lousho improve ./my-agent --check ./check.mjs --watch --interval 15m +npx lousho improve pause ./my-agent --reason "release freeze" --by ali +npx lousho improve resume ./my-agent --by ali ``` Each tick checks the kill switch (`.lousho/improve.stop`, `LOUSHO_IMPROVE=off` diff --git a/cloudflare-workers.mdx b/cloudflare-workers.mdx index e26619a..00827d0 100644 --- a/cloudflare-workers.mdx +++ b/cloudflare-workers.mdx @@ -3,7 +3,7 @@ title: "Cloudflare Workers" --- {/* Generated from docs/cloudflare-workers.md in LinuxDevil/lousho by scripts/sync-sdk-docs.mjs. Do not edit here. */} -`npx @linuxdevil/lousho build --target=cloudflare-worker --agent=agent.yaml` generates a module Worker (`worker.ts`), a `wrangler.toml` and a bundle, `dist/worker.js`, that the build checks for `node:` imports. It needs `tsup` and `wrangler` installed, and it takes an [agent spec file](/configuration) or an [agent directory](/agent-directories) (`npx @linuxdevil/lousho build ./my-agent --target=cloudflare-worker`). The Worker serves the same [HTTP API](/deployment#http-api) as the other targets. This target has the tightest limits of the three, so they come first. +`npx lousho build --target=cloudflare-worker --agent=agent.yaml` generates a module Worker (`worker.ts`), a `wrangler.toml` and a bundle, `dist/worker.js`, that the build checks for `node:` imports. It needs `tsup` and `wrangler` installed, and it takes an [agent spec file](/configuration) or an [agent directory](/agent-directories) (`npx lousho build ./my-agent --target=cloudflare-worker`). The Worker serves the same [HTTP API](/deployment#http-api) as the other targets. This target has the tightest limits of the three, so they come first. ## What works and what does not @@ -14,7 +14,7 @@ title: "Cloudflare Workers" | [Agent directories](/agent-directories) | yes: config, `instructions.md`, `tools/`, `skills/`, `subagents/`, `schedules/` (Worker cron rules), `channels/` (mounted under `/channels`), `memory/` (`kvMemory()`, or `inMemoryMemory()` per isolate) and `projectInstructions` (embedded at build time) | yes, all of it | | Providers | `mock`, `openai`, `anthropic`, `openrouter` | `mock`, `openai`, `anthropic`, `ollama`, `openrouter` | | Built-in tools | `current-date`, `day-name`, `http` (listed host names only, from the `LOUSHO_HTTP_ALLOW` binding) | all, including `http` and `web-fetch` | -| Tools written in TypeScript | yes, in an agent directory: no Node builtins, and from `@linuxdevil/lousho` only the names listed under [Agent directories on a Worker](#build-and-deploy) | yes, in an agent directory | +| Tools written in TypeScript | yes, in an agent directory: no Node builtins, and from `lousho` only the names listed under [Agent directories on a Worker](#build-and-deploy) | yes, in an agent directory | | Sandboxed tool execution | no (the build swaps the sandbox for a shim that fails when used) | yes | | MCP servers over stdio | no (the child process cannot start; the shim fails when used) | yes | | [Code mode](/code-mode) (`codeMode`) | no (the build swaps QuickJS for a shim that fails the run at its start) | yes | @@ -107,7 +107,7 @@ Worker builds the agent with the same rules as `resolveAgentDir()` (config keys, tool exports, duplicate tool names): ```bash -npx @linuxdevil/lousho build ./my-agent --target=cloudflare-worker +npx lousho build ./my-agent --target=cloudflare-worker cd .lousho/build/cloudflare-worker && npx wrangler deploy ``` @@ -213,8 +213,8 @@ binding typed as `KVBinding` (the `get`/`put`/`delete` part of Cloudflare's from the `/worker` subpath - see [Writing your own Worker](#writing-your-own-worker): ```ts -import { createAgent } from '@linuxdevil/lousho/worker'; -import { KVStore, type KVBinding } from '@linuxdevil/lousho/kv'; +import { createAgent } from 'lousho/worker'; +import { KVStore, type KVBinding } from 'lousho/kv'; interface Env { AGENT_KV: KVBinding; @@ -277,7 +277,7 @@ new call, which gets a new approval id, so a tool that must act once per invoice also needs a key from its own arguments (the invoice number here): ```ts -import { defineTool } from '@linuxdevil/lousho'; +import { defineTool } from 'lousho'; import { z } from 'zod'; // `payments` stands in for your payment client. @@ -319,7 +319,7 @@ day-of-week (`LOUSHO_SCHEDULE_INVALID`). In a Worker you write yourself, wire an agent defined in code with `handleScheduled(agent, schedules, controller, ctx)` (exported from -`@linuxdevil/lousho/worker` — the `@linuxdevil/lousho/deploy-runtime-worker` +`lousho/worker` — the `lousho/deploy-runtime-worker` specifier in the generated code is a virtual one that only `lousho build`'s bundler resolves; do not import it yourself). It runs the schedules whose `cron` equals `controller.cron` inside `ctx.waitUntil()` and never throws; list @@ -328,9 +328,9 @@ the same expressions under `[triggers] crons` yourself. Give the agent a store isolate's memory and is gone when it is recycled: ```ts -import { createAgent, createMockProvider, defineSchedule, handleScheduled } from '@linuxdevil/lousho/worker'; -import type { ScheduledContext, ScheduledController } from '@linuxdevil/lousho/worker'; -import { KVStore, type KVBinding } from '@linuxdevil/lousho/kv'; +import { createAgent, createMockProvider, defineSchedule, handleScheduled } from 'lousho/worker'; +import type { ScheduledContext, ScheduledController } from 'lousho/worker'; +import { KVStore, type KVBinding } from 'lousho/kv'; interface Env { AGENT_CHECKPOINTS: KVBinding; @@ -352,7 +352,7 @@ export default { ## Writing your own Worker -Import from `@linuxdevil/lousho/worker`, not the package root. The root entry +Import from `lousho/worker`, not the package root. The root entry also exports file stores, the agent-directory loader, MCP over stdio, sandboxes and other Node-only modules, so bundling it for `platform: 'browser'` fails with `Could not resolve "async_hooks"` and friends. `/worker` is `createAgent` plus the @@ -364,8 +364,8 @@ import { createAgent, createMockProvider, defineSchedule, defineTool, handleScheduled, memoryStore, serveFetch, OpenAIProvider, AnthropicProvider, OpenRouterProvider, LLMProviderRegistry, SDKError, /* ... */ -} from '@linuxdevil/lousho/worker'; -import { KVStore } from '@linuxdevil/lousho/kv'; // also re-exported from /worker +} from 'lousho/worker'; +import { KVStore } from 'lousho/kv'; // also re-exported from /worker ``` The exports: `createAgent` (and its config types), `memoryStore`, the KV stores @@ -438,8 +438,8 @@ read-modify-write is not atomic. ## Bundle size and Node builtins The KV-backed stores (`KVStore`, `KVCheckpointStore` and `CHECKPOINT_KV_BINDING`, -exported from `@linuxdevil/lousho/kv`) have no `node:*` references anywhere -in their dependency graph, and neither does the `@linuxdevil/lousho/worker` +exported from `lousho/kv`) have no `node:*` references anywhere +in their dependency graph, and neither does the `lousho/worker` entry a hand-written Worker bundles (its build is checked the same way; see [Writing your own Worker](#writing-your-own-worker)). The Worker runs the spec as a `createAgent()` agent, whose Node-only imports (project instructions, the file session store, guardrail diff --git a/code-mode.mdx b/code-mode.mdx index 80c780e..6ac85bc 100644 --- a/code-mode.mdx +++ b/code-mode.mdx @@ -38,7 +38,7 @@ npm install quickjs-emscripten@^0.32.0 ``` ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; +import { createAgent, defineTool } from 'lousho'; import { z } from 'zod'; const prices: Record = { apple: 1.2, pear: 0.8, plum: 2 }; diff --git a/compaction.mdx b/compaction.mdx index e22020a..9c18fb5 100644 --- a/compaction.mdx +++ b/compaction.mdx @@ -21,7 +21,7 @@ OpenRouter use `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const simple = createAgent({ model: 'openai/gpt-4o', compaction: true }); @@ -87,7 +87,7 @@ listeners (`createAgent({ onEvent })`, `onAgentEvent`) or is streamed. a cheap summarizer model: ```ts -import { createAgent, createCompactionHook, twoPhaseStrategy } from '@linuxdevil/lousho'; +import { createAgent, createCompactionHook, twoPhaseStrategy } from 'lousho'; const agent = createAgent({ provider, @@ -160,7 +160,7 @@ model's loaded context length (`loaded_context_length` from prints the model's default). ```ts -import { createAgent, registerModel } from '@linuxdevil/lousho'; +import { createAgent, registerModel } from 'lousho'; // Either on the agent... const agent = createAgent({ @@ -186,8 +186,8 @@ Registering the hook in a `HookRegistry` is for code that calls the executor directly (see [the executor API](/executor-api)): ```ts -import { AgentExecutor } from '@linuxdevil/lousho/executor'; -import { HookRegistry, createCompactionHook, twoPhaseStrategy } from '@linuxdevil/lousho'; +import { AgentExecutor } from 'lousho/executor'; +import { HookRegistry, createCompactionHook, twoPhaseStrategy } from 'lousho'; const hooks = new HookRegistry(); hooks.register( @@ -273,7 +273,7 @@ message; a pinned tool result keeps its whole assistant turn. Pin what the agent must always see in full, such as the task statement or a key document: ```ts -import { pinMessage, isPinned, type Message } from '@linuxdevil/lousho'; +import { pinMessage, isPinned, type Message } from 'lousho'; const messages: Message[] = [ { role: 'system', content: 'You are a research assistant.' }, @@ -291,7 +291,7 @@ of the conversation, and resolves to a new array (its input is not modified). Use it to shrink a stored transcript before you continue it: ```ts -import { compactMessages, summarizeStrategy, type Message } from '@linuxdevil/lousho'; +import { compactMessages, summarizeStrategy, type Message } from 'lousho'; declare const history: Message[]; @@ -325,8 +325,8 @@ durable turn is unfinished. Listeners added with `session.on()` get checkpoint and checkpoint history, and emits `context.cleared`. ```ts -import { createAgent, twoPhaseStrategy } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, twoPhaseStrategy } from 'lousho'; +import { mockModel } from 'lousho/testing'; const agent = createAgent({ provider: mockModel(['ok']) }); const session = agent.session({ compaction: { strategy: twoPhaseStrategy({ model: 'openai/gpt-4o-mini' }) } }); @@ -351,7 +351,7 @@ unchanged when there is nothing to do: the hook then leaves the run alone and does not call `onCompaction`. ```ts -import { createCompactionHook, isPinned, type CompactionStrategy } from '@linuxdevil/lousho'; +import { createCompactionHook, isPinned, type CompactionStrategy } from 'lousho'; // Keep the system prompt, pinned messages and the last 20 messages. const keepRecent: CompactionStrategy = { diff --git a/configuration.mdx b/configuration.mdx index 3f1f410..2e84194 100644 --- a/configuration.mdx +++ b/configuration.mdx @@ -145,7 +145,7 @@ mcpServers: ``` ```ts -import { agentSpecSchema, specToAgent, type McpServerSpec } from '@linuxdevil/lousho'; +import { agentSpecSchema, specToAgent, type McpServerSpec } from 'lousho'; const filesystem: McpServerSpec = { command: 'npx', args: ['-y', '@modelcontextprotocol/server-filesystem'] }; const spec = agentSpecSchema.parse({ @@ -188,7 +188,7 @@ other models. The `vendor/` model prefix chooses the provider; with OpenRouter use `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', @@ -230,7 +230,7 @@ To build the same thing by hand, or for providers you construct yourself, anywhere a provider is accepted: ```ts -import { createAgent, resolveProvider, withFallback, withRetry } from '@linuxdevil/lousho'; +import { createAgent, resolveProvider, withFallback, withRetry } from 'lousho'; const provider = withFallback( [ @@ -301,7 +301,7 @@ flow waves and eval suites cannot burst unbounded model calls. Every limit is optional: ```ts -import { withRateLimit, resolveProvider } from '@linuxdevil/lousho'; +import { withRateLimit, resolveProvider } from 'lousho'; const provider = withRateLimit(resolveProvider('openai/gpt-4o-mini'), { requestsPerMinute: 60, // calls started per rolling minute @@ -331,7 +331,7 @@ its own provider is queued too). Give several agents the same `createRateLimiter()` to share it across them. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const researcher = createAgent({ model: 'openai/gpt-4o-mini', instructions: 'You research.', description: 'Finds sources' }); createAgent({ model: 'openai/gpt-4o-mini', subagents: { researcher }, rateLimit: { maxConcurrent: 2 } }); @@ -410,7 +410,7 @@ A `send()` or `stream()` call can pass its own, which win key by key over the agent's: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', @@ -445,7 +445,7 @@ Besides `modelSettings`, a `send()` or `stream()` call can set its own instructions for that run: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', instructions: 'You triage support tickets.' }); await agent.send('Summarize the ticket.', { maxSteps: 2, instructions: 'Answer in French.' }); @@ -463,7 +463,7 @@ keeps `model` / `provider` and `instructions` / `prompt` mutually exclusive, so TypeScript rejects spreading it next to a literal that sets one of them. ```ts -import { createAgent, type CreateAgentBase } from '@linuxdevil/lousho'; +import { createAgent, type CreateAgentBase } from 'lousho'; const shared: CreateAgentBase = { maxSteps: 8, modelSettings: { temperature: 0 } }; @@ -522,7 +522,7 @@ with a checkpoint store it is checkpointed as finished, like `'max-steps'`. (`LOUSHO_BUDGET_EXCEEDED`, with the same `budget`) instead. ```ts -import { BudgetExceededError, createAgent } from '@linuxdevil/lousho'; +import { BudgetExceededError, createAgent } from 'lousho'; const agent = createAgent({ provider, @@ -551,7 +551,7 @@ continued from its store keeps its budget. Both apply together; the first limit reached stops the turn. A turn that was aborted is not counted. ```ts -import { createAgent, memoryStore } from '@linuxdevil/lousho'; +import { createAgent, memoryStore } from 'lousho'; const agent = createAgent({ provider, store: memoryStore(), limits: { maxTokens: 20_000 } }); const session = agent.session({ id: 'user-42', limits: { maxCostUsd: 1 } }); @@ -565,7 +565,7 @@ Many repositories keep guidance for coding agents in an `AGENTS.md` (or `CLAUDE.md`) file. `createAgent` can append it to the agent's instructions: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ instructions: 'You review pull requests.', diff --git a/connectors.mdx b/connectors.mdx index 5ef10e2..4e9aa3d 100644 --- a/connectors.mdx +++ b/connectors.mdx @@ -9,7 +9,7 @@ connector is an **MCP server**: add it to `mcpServers` and its tools join the agent's registry namespaced as `__`. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openrouter/openai/gpt-4o-mini', @@ -50,8 +50,8 @@ into the SDK's approval gate: a `slack.post_message` pauses the run until approved, `docs.get_document` does not. Tune it per server: ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent } from 'lousho'; +import { mockModel } from 'lousho/testing'; const provider = mockModel([{ text: 'ok' }]); diff --git a/decisions.mdx b/decisions.mdx index 0e3f126..bebc6cc 100644 --- a/decisions.mdx +++ b/decisions.mdx @@ -8,7 +8,7 @@ title: "Decisions" Public beta: `gpt-6-luna` is the only model the endpoint serves, and the surface may still change. The endpoint is not part of chat completions, so it does not go through `createAgent()` providers or OpenRouter — it needs `OPENAI_API_KEY` (or `apiKey`/`baseURL` for a compatible endpoint). ```ts -import { decide } from '@linuxdevil/lousho'; +import { decide } from 'lousho'; const { answers } = await decide({ input: 'I was charged twice for my order.', @@ -35,7 +35,7 @@ if (route.type === 'choice' && route.confidence >= 0.7) { ## Question types ```ts -import { decide, type DecisionQuestion } from '@linuxdevil/lousho'; +import { decide, type DecisionQuestion } from 'lousho'; const questions: DecisionQuestion[] = [ // predicate: probability 0-1 that a condition holds @@ -72,7 +72,7 @@ const questions: DecisionQuestion[] = [ Independent questions share one `input` and one request: ```ts -import { decide, type DecisionQuestion } from '@linuxdevil/lousho'; +import { decide, type DecisionQuestion } from 'lousho'; const questions: DecisionQuestion[] = [ { type: 'predicate', name: 'is_spam', instructions: 'Is this spam?' }, @@ -94,7 +94,7 @@ const byName = Object.fromEntries(answers.map((a) => [a.name, a])); ## Options ```ts -import { decide } from '@linuxdevil/lousho'; +import { decide } from 'lousho'; const result = await decide({ input: '...', diff --git a/deployment.mdx b/deployment.mdx index cc28ba6..99c6b67 100644 --- a/deployment.mdx +++ b/deployment.mdx @@ -7,7 +7,7 @@ title: "Deployment" into a deployable artifact for one target platform: ```bash -npx @linuxdevil/lousho build --target= --agent=agent.yaml [--out=] +npx lousho build --target= --agent=agent.yaml [--out=] ``` It runs the target's adapter through three steps - **scaffold** (write the @@ -33,7 +33,7 @@ All three targets also take an [agent directory](/agent-directories) in place of a spec file (the path may be positional or `--agent`): ```bash -npx @linuxdevil/lousho build ./my-agent --target=node-server +npx lousho build ./my-agent --target=node-server node .lousho/build/node-server/dist/server.js ``` @@ -44,7 +44,7 @@ directory's [schedules](/schedules) when it listens, mounts its routes keep the bearer token), and logs `schedules: ...; channels: ...`. The directory's code files are pre-bundled with the build's tsup step into `dist/agent/**.js` (one ESM build sharing one copy of the SDK, so a `from -'@linuxdevil/lousho'` import in a tool is the SDK the server runs), and +'lousho'` import in a tool is the SDK the server runs), and only the files the server reads as data are copied: `instructions.md`, `instructions/`, `skills/`, a JSON/YAML config, and the same in each `subagents//`. Nothing else in the directory reaches `dist/`: not `.env` @@ -65,7 +65,7 @@ only a Worker-safe part of `lousho` ([Cloudflare Workers](/cloudflare-workers#build-and-deploy) has the details): ```bash -npx @linuxdevil/lousho build ./my-agent --target=cloudflare-worker +npx lousho build ./my-agent --target=cloudflare-worker ``` ## Agent modules @@ -76,7 +76,7 @@ same file `lousho dev src/agent.ts` serves, and what the minimal and tools scaffolds have (their `npm run build` runs this): ```bash -npx @linuxdevil/lousho build src/agent.ts --target=node-server +npx lousho build src/agent.ts --target=node-server node .lousho/build/node-server/dist/server.js ``` @@ -294,7 +294,7 @@ signal handler: stop accepting, let the requests in flight finish, then call ```ts import { createServer } from 'node:http'; -import { createAgent, mountChannels } from '@linuxdevil/lousho'; +import { createAgent, mountChannels } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini' }); const handler = mountChannels(agent, []); diff --git a/durable-execution.mdx b/durable-execution.mdx index f717311..6eaa83d 100644 --- a/durable-execution.mdx +++ b/durable-execution.mdx @@ -13,8 +13,8 @@ and any `ApprovalStore` work the same way. See also: [Pause, restart, resume](/pause-and-resume), a short end-to-end tutorial. ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { SqliteStore } from '@linuxdevil/lousho/sqlite'; +import { createAgent } from 'lousho'; +import { SqliteStore } from 'lousho/sqlite'; const agent = createAgent({ provider, store: new SqliteStore('./.lousho/jobs.db') }); @@ -107,7 +107,7 @@ session (the session already has its system prompt). To end a session and start over under the same id, call `store.checkpoints.delete(sessionId)`. ```ts -import { createAgent, memoryStore, SessionAwaitingApprovalError } from '@linuxdevil/lousho'; +import { createAgent, memoryStore, SessionAwaitingApprovalError } from 'lousho'; const agent = createAgent({ provider, store: memoryStore() }); @@ -139,7 +139,7 @@ underneath): 3. calls the model once every call of the turn has exactly one result. ```ts -import { createAgent, memoryStore } from '@linuxdevil/lousho'; +import { createAgent, memoryStore } from 'lousho'; const agent = createAgent({ provider, store: memoryStore() }); @@ -163,7 +163,7 @@ finishes it. `agent.pending()` lists those runs across every session and `send(msg, { sessionId })` id, so a restarted process can find them: ```ts -import { createAgent, fileStore } from '@linuxdevil/lousho'; +import { createAgent, fileStore } from 'lousho'; const agent = createAgent({ provider, store: fileStore('./.lousho') }); @@ -228,8 +228,8 @@ without durable writes) is listed as `'in-progress'` with `lastError.code` to the code that serves traffic: ```ts -import { createAgent, defineTool, memoryStore, PropagatingToolError } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool, memoryStore, PropagatingToolError } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; let charges = 0; @@ -439,7 +439,7 @@ model calls). To run a job once, derive the `sessionId` from the delivery id and look at the checkpoint before sending: ```ts -import { createAgent, memoryStore } from '@linuxdevil/lousho'; +import { createAgent, memoryStore } from 'lousho'; const store = memoryStore(); const agent = createAgent({ provider, store }); @@ -477,7 +477,7 @@ same call is re-run on resume) is passed to `execute`, so it works as an idempotency key against a re-run of one call: ```ts -import { defineTool } from '@linuxdevil/lousho'; +import { defineTool } from 'lousho'; import { z } from 'zod'; declare const payments: { charge(amount: number, opts: { idempotencyKey: string }): Promise<{ id: string }> }; @@ -513,7 +513,7 @@ one invoice) needs an idempotency key from your own data, such as the claim id in the arguments, checked or passed on to the system you write to: ```ts -import { defineTool } from '@linuxdevil/lousho'; +import { defineTool } from 'lousho'; import { z } from 'zod'; declare const claims: { file(claim: { claimId: string }, opts: { idempotencyKey: string }): Promise<{ id: string }> }; @@ -543,14 +543,14 @@ Which stores keep one: | `SqliteStore` | yes (`checkpoint_history` table) | `new SqliteStore(path, { historyLimit })` | | `LocalStorageCheckpointStore` | yes | `new LocalStorageCheckpointStore(storage, { historyLimit })` | | `fileStore(dir)` (JSON files) | yes (`checkpoint-history/.json`) | `fileStore(dir, { historyLimit })` | -| `KVCheckpointStore` / `KVStore` (Cloudflare Workers KV, from `@linuxdevil/lousho/kv`) | yes (see below) | `new KVStore(kv, { historyLimit })` | +| `KVCheckpointStore` / `KVStore` (Cloudflare Workers KV, from `lousho/kv`) | yes (see below) | `new KVStore(kv, { historyLimit })` | | Agent Forge's `FileCheckpointStore` | yes | `new FileCheckpointStore(dir, { historyLimit })` | | a custom `CheckpointStore` | only if it implements `history()` | - | For example, with the in-memory store: ```ts -import { getCheckpointHistory, memoryStore } from '@linuxdevil/lousho'; +import { getCheckpointHistory, memoryStore } from 'lousho'; const { checkpoints } = memoryStore({ historyLimit: 20 }); // default 50, 0 keeps none // new SqliteStore(path, { historyLimit }), new LocalStorageCheckpointStore(storage, { historyLimit }) @@ -605,7 +605,7 @@ saves the result as the `'in-progress'` checkpoint of the new session over fork like any unfinished run with `agent.resume(fork.sessionId)`: ```ts -import { createAgent, compareTrajectories, memoryStore } from '@linuxdevil/lousho'; +import { createAgent, compareTrajectories, memoryStore } from 'lousho'; const store = memoryStore(); const assistant = createAgent({ provider, store }); @@ -686,8 +686,8 @@ it and it has no result yet, or the human approved it) always rejects with whatever the option. ```ts -import { createAgent, memoryStore } from '@linuxdevil/lousho'; -import type { LLMProvider } from '@linuxdevil/lousho'; +import { createAgent, memoryStore } from 'lousho'; +import type { LLMProvider } from 'lousho'; declare const provider: LLMProvider; @@ -728,8 +728,8 @@ call it corresponds to. `agent.send(message, { sessionId })`: ```ts -import { AgentExecutor } from '@linuxdevil/lousho/executor'; -import { LocalStorageCheckpointStore } from '@linuxdevil/lousho/utils'; +import { AgentExecutor } from 'lousho/executor'; +import { LocalStorageCheckpointStore } from 'lousho/utils'; const checkpoints = new LocalStorageCheckpointStore(storage); @@ -743,8 +743,8 @@ await AgentExecutor.execute({ agent, input: 'Make it 3 people', provider, sessio It throws `SessionAwaitingApprovalError` on a paused session, as `send()` does: ```ts -import { AgentExecutor } from '@linuxdevil/lousho/executor'; -import { SessionAwaitingApprovalError, memoryStore } from '@linuxdevil/lousho'; +import { AgentExecutor } from 'lousho/executor'; +import { SessionAwaitingApprovalError, memoryStore } from 'lousho'; const { checkpoints } = memoryStore(); @@ -761,7 +761,7 @@ The exported `resumeAfterApproval()` function is what `agent.approvals.resolve()` calls: ```ts -import { AgentExecutor, resumeAfterApproval } from '@linuxdevil/lousho/executor'; +import { AgentExecutor, resumeAfterApproval } from 'lousho/executor'; const paused = await AgentExecutor.execute({ agent, input, provider, toolRegistry, approvalStore, sessionId: 'chat-42', checkpointStore, @@ -785,8 +785,8 @@ if (paused.finishReason === 'awaiting-approval') { fork's `sessionId` is `agent.resume(fork.sessionId)`: ```ts -import { AgentExecutor } from '@linuxdevil/lousho/executor'; -import { compareTrajectories, memoryStore } from '@linuxdevil/lousho'; +import { AgentExecutor } from 'lousho/executor'; +import { compareTrajectories, memoryStore } from 'lousho'; const { checkpoints } = memoryStore(); await AgentExecutor.execute({ agent, provider, toolRegistry, input: 'Plan my trip', sessionId: 'trip', checkpointStore: checkpoints }); diff --git a/errors.mdx b/errors.mdx index 8ba3545..3b23b4e 100644 --- a/errors.mdx +++ b/errors.mdx @@ -31,7 +31,7 @@ serialising the error is unchanged). It is absent when the run failed before it started. ```ts -import { createAgent, type RunUsage } from '@linuxdevil/lousho'; +import { createAgent, type RunUsage } from 'lousho'; try { await createAgent({ provider }).send('Go'); @@ -52,7 +52,7 @@ on purpose: `PropagatingToolError` and its subclasses (a tool throws one to stop the run), `WorkspaceError` (a workspace operation refused or failed - the model sees it as a tool error), `McpToolError` (a loaded MCP tool whose server reports `isError`; also a tool error), `AuthError` (an -`@linuxdevil/lousho/auth` entry rejects the request with its 401 or 403), +`lousho/auth` entry rejects the request with its 401 or 403), `CronExpressionError` (`parseCronExpression()`, which `defineSchedule()` wraps as `LOUSHO_SCHEDULE_INVALID`) and `DecryptionError` (`EncryptionUtils`, `DTOEncryptionFilter`). @@ -63,7 +63,7 @@ server reports `isError`; also a tool error), `AuthError` (an and `.cassette` / `.callNumber` fields. ```ts -import { createAgent, SDKError } from '@linuxdevil/lousho'; +import { createAgent, SDKError } from 'lousho'; try { await createAgent({ provider, instructions: 'Be brief.' }).send('hi'); @@ -400,7 +400,7 @@ where the agent is being built. agent spec has no field for. **Fix:** build the agent with `createAgent()` and pass the configured tool, e.g. -`createGitHubTools(config)` from `@linuxdevil/lousho/integrations`. +`createGitHubTools(config)` from `lousho/integrations`. **Example:** `tools: [github]` in a spec. @@ -1257,7 +1257,7 @@ dataset and add examples to it (`POST /api/v1/datasets`, `POST **Means:** a record/replay cassette is missing, is not valid JSON or does not match the recorded request. The message names the file. A mismatch inside a run reaches `send()` as the `CassetteMismatchError` itself (from -`@linuxdevil/lousho/testing`, with `.cassette` and `.callNumber`) - it is +`lousho/testing`, with `.cassette` and `.callNumber`) - it is a test-fixture failure, so it is never compacted into a `CompactedLLMProviderError`. @@ -1560,7 +1560,7 @@ keep reaching the run that holds the id. ### LOUSHO_AUTH_CONFIG_INVALID -**Means:** a route auth helper from `@linuxdevil/lousho/auth` (`jwt()`, +**Means:** a route auth helper from `lousho/auth` (`jwt()`, `oidc()`, `basic()`, `apiToken()`) got options it cannot verify with, and refused them when it was created, not at the first request. The message names the option: no key source or two of them, an HMAC `secret` shorter than 32 diff --git a/evals.mdx b/evals.mdx index 2e1d33e..97d55b9 100644 --- a/evals.mdx +++ b/evals.mdx @@ -17,7 +17,7 @@ prints a summary and writes JUnit and JSON reports. ```bash npm install --save-dev vitest -npx @linuxdevil/lousho eval +npx lousho eval ``` ## Writing a trajectory eval @@ -31,8 +31,8 @@ for the [judge evals](#judge-evals) below. ```ts // refund.eval.ts import { z } from 'zod'; -import { createAgent, defineEval, defineTool, includes } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineEval, defineTool, includes } from 'lousho'; +import { mockModel } from 'lousho/testing'; const lookupOrder = defineTool({ name: 'lookup_order', @@ -114,7 +114,7 @@ it. Pass `{ subagent: 'researcher' }` to look at one sub-agent (and the sub-agents under it) only: ```ts -import { createAgent, defineEval } from '@linuxdevil/lousho'; +import { createAgent, defineEval } from 'lousho'; const researcher = createAgent({ model: 'openai/gpt-4o-mini', instructions: 'You research.', description: 'Finds sources' }); const agent = createAgent({ model: 'openai/gpt-4o-mini', subagents: { researcher } }); @@ -144,7 +144,7 @@ case `n` times; the case passes when all `n` runs pass, or when at least `passAt: k` of them do: ```ts -import { createAgent, defineEval } from '@linuxdevil/lousho'; +import { createAgent, defineEval } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', instructions: 'You handle refunds.' }); @@ -170,8 +170,8 @@ any object; its `label` (or `name`, or its `input`) names it. ```ts import { z } from 'zod'; -import { createAgent, defineEval, defineTool } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineEval, defineTool } from 'lousho'; +import { mockModel } from 'lousho/testing'; const lookupOrder = defineTool({ name: 'lookup_order', @@ -223,7 +223,7 @@ curl -X POST "$LOUSHO_OBSERVE/api/v1/datasets/refunds/examples" -H 'content-type ``` ```ts -import { createAgent, defineEval, includes } from '@linuxdevil/lousho'; +import { createAgent, defineEval, includes } from 'lousho'; defineEval({ name: 'refunds', @@ -261,8 +261,8 @@ it. ```ts // tone.judge.eval.ts: run with `lousho eval --judge` -import { createAgent, defineEval, atLeast } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineEval, atLeast } from 'lousho'; +import { mockModel } from 'lousho/testing'; defineEval({ name: 'tone', @@ -341,7 +341,7 @@ model. `--timeout ms` sets the limit for every case (`0` turns it off). With One eval can set its own limit, which wins over both: ```ts -import { createAgent, defineEval } from '@linuxdevil/lousho'; +import { createAgent, defineEval } from 'lousho'; defineEval({ name: 'research report', @@ -382,7 +382,7 @@ jobs: - uses: actions/setup-node@v4 with: { node-version: 22 } - run: npm ci - - run: npx @linuxdevil/lousho eval --junit reports/evals.xml --json reports/evals.json + - run: npx lousho eval --junit reports/evals.xml --json reports/evals.json - uses: actions/upload-artifact@v4 if: always() with: @@ -423,9 +423,9 @@ between: it records each case once against the real provider through Your eval files do not change. ```bash -npx @linuxdevil/lousho eval --record # real provider: writes the cassettes, commit them -npx @linuxdevil/lousho eval --replay # every case from its cassette, no network, no key -npx @linuxdevil/lousho eval --drift # re-record and diff each case's trajectory +npx lousho eval --record # real provider: writes the cassettes, commit them +npx lousho eval --replay # every case from its cassette, no network, no key +npx lousho eval --drift # re-record and diff each case's trajectory ``` - **`--record`** runs every case with its agent's real provider and writes one @@ -478,7 +478,7 @@ a real judge only in `*.judge.eval.ts` files. How it works: the run loop sends every model call (`generate` or `stream`, from a plain run, a streamed run, a sub-agent or an approval resume) through one provider-interception seam, `setProviderInterceptor()` (exported from -`@linuxdevil/lousho/testing`; `src/providers/interception.ts` in the +`lousho/testing`; `src/providers/interception.ts` in the source). Nothing is installed by default, so the seam is free. While a case runs under one of these modes, `lousho eval` answers it with the provider wrapped by `recordReplay()` for that case's cassette. You can use @@ -496,7 +496,7 @@ The same eval file that gates CI in-process can smoke-test a deployed agent (the URL and every case runs against the deployment instead of the in-process agent: ```bash -npx @linuxdevil/lousho eval --url https://agent.example.com --token "$DEPLOY_TOKEN" # or LOUSHO_EVAL_TOKEN +npx lousho eval --url https://agent.example.com --token "$DEPLOY_TOKEN" # or LOUSHO_EVAL_TOKEN ``` Each case gets its own remote session (`POST /chat { sessionId, input }`, one @@ -522,7 +522,7 @@ and tools the behaviour you want to check. In code, pass a `target` (this overrides `agent`; `--url` overrides both): ```ts -import { defineEval, remoteTarget } from '@linuxdevil/lousho'; +import { defineEval, remoteTarget } from 'lousho'; defineEval({ name: 'deployed refund flow', diff --git a/executor-api.mdx b/executor-api.mdx index 5ed748f..a4c3ebe 100644 --- a/executor-api.mdx +++ b/executor-api.mdx @@ -19,8 +19,8 @@ take: `temperature` and `maxTokens` (`exporter`, `captureContent` and import { AgentBuilder, AgentExecutor, -} from '@linuxdevil/lousho/executor'; -import { createMockProvider } from '@linuxdevil/lousho'; +} from 'lousho/executor'; +import { createMockProvider } from 'lousho'; const agent = AgentBuilder.create() .setName('Customer Support Agent') diff --git a/flows.mdx b/flows.mdx index 42eb429..064ee5c 100644 --- a/flows.mdx +++ b/flows.mdx @@ -9,8 +9,8 @@ Describe the graph with `FlowBuilder` and run it with the static `FlowExecutor.execute(flow, context, onEvent?)`. ```ts -import { FlowBuilder, FlowExecutor, type EditorStep } from '@linuxdevil/lousho/flows'; -import { resolveProvider, type AgentConfig } from '@linuxdevil/lousho'; +import { FlowBuilder, FlowExecutor, type EditorStep } from 'lousho/flows'; +import { resolveProvider, type AgentConfig } from 'lousho'; // A flow calls an LLMProvider directly (reads OPENAI_API_KEY). const provider = resolveProvider('openai/gpt-4o-mini'); @@ -112,7 +112,7 @@ its tool runs: never run unapproved. ```ts -import { FlowExecutor, type AgentFlow } from '@linuxdevil/lousho/flows'; +import { FlowExecutor, type AgentFlow } from 'lousho/flows'; declare const flow: AgentFlow; @@ -137,7 +137,7 @@ set stays as before: `'{{missing}}'` is `''`, and `'$USD'` (no variable named `USD`) is the literal string `'$USD'`. ```ts -import type { EditorStep } from '@linuxdevil/lousho/flows'; +import type { EditorStep } from 'lousho/flows'; // With variables { count: 5, tags: ['a', 'b'] }, the tool gets { count: 5, tags: ['a', 'b'], note: 'count: 5' }. const step: EditorStep = { @@ -159,7 +159,7 @@ once it is aborted, no further step starts and the flow fails with the signal's to the model request, and a `toolCall` hands it to the tool as `ctx.abortSignal`. ```ts -import { FlowExecutor, type AgentFlow } from '@linuxdevil/lousho/flows'; +import { FlowExecutor, type AgentFlow } from 'lousho/flows'; declare const flow: AgentFlow; @@ -217,7 +217,7 @@ Invalid values (`maxAttempts` < 1 or not an integer, a negative `backoffMs`, a `timeoutMs` <= 0) fail the step with `LOUSHO_FLOW_INVALID`. ```ts -import type { EditorStep } from '@linuxdevil/lousho/flows'; +import type { EditorStep } from 'lousho/flows'; const fetchInvoice: EditorStep = { type: 'toolCall', @@ -251,8 +251,8 @@ from the saved variables. `result.usage` and `result.steps` include the earlier attempts. ```ts -import { FlowExecutor, type AgentFlow } from '@linuxdevil/lousho/flows'; -import { memoryStore } from '@linuxdevil/lousho'; +import { FlowExecutor, type AgentFlow } from 'lousho/flows'; +import { memoryStore } from 'lousho'; declare const flow: AgentFlow; const checkpointStore = memoryStore().checkpoints; @@ -321,8 +321,8 @@ are cancelled. Steps they completed are kept, and the rest run again on resume. To continue, pass the decision to `resume()`: ```ts -import { FlowExecutor, type AgentFlow } from '@linuxdevil/lousho/flows'; -import { memoryStore } from '@linuxdevil/lousho'; +import { FlowExecutor, type AgentFlow } from 'lousho/flows'; +import { memoryStore } from 'lousho'; declare const flow: AgentFlow; const checkpointStore = memoryStore().checkpoints; @@ -369,7 +369,7 @@ asked, and the context's `approve` callback decides, exactly as for a node's `id`, or its place in the flow). ```ts -import type { EditorStep } from '@linuxdevil/lousho/flows'; +import type { EditorStep } from 'lousho/flows'; const steps: EditorStep = { type: 'sequence', @@ -390,7 +390,7 @@ const steps: EditorStep = { the decision: ```ts -import { FlowExecutor, type AgentFlow } from '@linuxdevil/lousho/flows'; +import { FlowExecutor, type AgentFlow } from 'lousho/flows'; declare const flow: AgentFlow; @@ -414,8 +414,8 @@ An `agent` step calls a `createAgent()` agent registered in the context's guardrails and approvals — and the step stores its reply text: ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { FlowExecutor, type AgentFlow } from '@linuxdevil/lousho/flows'; +import { createAgent } from 'lousho'; +import { FlowExecutor, type AgentFlow } from 'lousho/flows'; const writer = createAgent({ name: 'writer', model: 'openai/gpt-4o-mini', instructions: 'Draft a reply.' }); @@ -460,9 +460,9 @@ typed value rather than text: ```ts import { z } from 'zod'; -import { createAgent } from '@linuxdevil/lousho'; -import { FlowExecutor, type AgentFlow } from '@linuxdevil/lousho/flows'; -import { ToolRegistry } from '@linuxdevil/lousho/tools'; +import { createAgent } from 'lousho'; +import { FlowExecutor, type AgentFlow } from 'lousho/flows'; +import { ToolRegistry } from 'lousho/tools'; const researcher = createAgent({ name: 'researcher', model: 'openai/gpt-4o-mini', instructions: 'Answer with three facts.' }); diff --git a/guardrails.mdx b/guardrails.mdx index 3ed3358..5e9d876 100644 --- a/guardrails.mdx +++ b/guardrails.mdx @@ -50,7 +50,7 @@ OpenRouter use `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ```ts -import { createAgent, moderationGuardrail, piiGuardrail } from '@linuxdevil/lousho'; +import { createAgent, moderationGuardrail, piiGuardrail } from 'lousho'; const agent = createAgent({ provider, @@ -90,7 +90,7 @@ import { maxLengthGuardrail, regexGuardrail, type IoGuardrail, -} from '@linuxdevil/lousho'; +} from 'lousho'; const noProdWrites: IoGuardrail = { name: 'no-prod-writes', @@ -136,7 +136,7 @@ try { The last four are a starter set for checks most production agents need: ```ts -import { createAgent, piiGuardrail, promptInjectionGuardrail, secretsGuardrail } from '@linuxdevil/lousho'; +import { createAgent, piiGuardrail, promptInjectionGuardrail, secretsGuardrail } from 'lousho'; const agent = createAgent({ provider, @@ -184,7 +184,7 @@ proposed patch and rolls the results up into one verdict. The agent's patch before a pull request is opened: ```ts -import { runPatchChecks, secretScanCheck, createDiffSizeCheck, createCommandCheck } from '@linuxdevil/lousho'; +import { runPatchChecks, secretScanCheck, createDiffSizeCheck, createCommandCheck } from 'lousho'; const verdict = await runPatchChecks( { diff: patch }, @@ -224,8 +224,8 @@ function. The executor then calls `sandboxExecute` with the run's #### Advanced: the executor API ```ts -import { AgentExecutor } from '@linuxdevil/lousho/executor'; -import { SubprocessSandbox } from '@linuxdevil/lousho'; +import { AgentExecutor } from 'lousho/executor'; +import { SubprocessSandbox } from 'lousho'; // Route a flagged tool (requiresSandbox + sandboxExecute) through a real, // Docker-backed sandbox instead of the in-process NoopSandbox default diff --git a/handoffs.mdx b/handoffs.mdx index 4af8c43..ade11f3 100644 --- a/handoffs.mdx +++ b/handoffs.mdx @@ -19,7 +19,7 @@ description to decide when to hand off), then pass them to the triage agent as use `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; +import { createAgent, defineTool } from 'lousho'; import { z } from 'zod'; const lookupCharges = defineTool({ @@ -70,7 +70,7 @@ name with one of the agent's tools. Wrap a target in `handoff()` to configure it: ```ts -import { createAgent, handoff, handoffFilters } from '@linuxdevil/lousho'; +import { createAgent, handoff, handoffFilters } from 'lousho'; import { z } from 'zod'; const refunds = createAgent({ name: 'refunds', description: 'Issues refunds', model: 'openai/gpt-4o-mini' }); @@ -122,7 +122,7 @@ the middle of a conversation. Two filters are built in: - `handoffFilters.lastUserMessage`: keeps only the last user message. ```ts -import { handoffFilters, type HandoffInputData } from '@linuxdevil/lousho'; +import { handoffFilters, type HandoffInputData } from 'lousho'; // The last four messages, without tool traffic. const recent = (data: HandoffInputData) => handoffFilters.removeToolCalls(data).slice(-4); @@ -169,7 +169,7 @@ In a session the active agent is the one the transcript last handed off to `session.send()` / `session.stream()` runs that agent: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const billing = createAgent({ name: 'billing', description: 'Handles charges and refunds', model: 'openai/gpt-4o-mini' }); const triage = createAgent({ name: 'triage', model: 'openai/gpt-4o-mini', handoffs: [billing] }); @@ -187,7 +187,7 @@ its targets, give the target an array and add the lead to it afterwards (the array is read at every run): ```ts -import { createAgent, handoff, type Handoff } from '@linuxdevil/lousho'; +import { createAgent, handoff, type Handoff } from 'lousho'; const billingHandoffs: Handoff[] = []; const billing = createAgent({ @@ -237,7 +237,7 @@ A handoff is reported between the handoff call's `tool.start` / `tool.done` and the target's first `step.start`: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const billing = createAgent({ name: 'billing', description: 'Handles charges and refunds', model: 'openai/gpt-4o-mini' }); const triage = createAgent({ name: 'triage', model: 'openai/gpt-4o-mini', handoffs: [billing] }); diff --git a/hooks.mdx b/hooks.mdx index 217386e..1e06679 100644 --- a/hooks.mdx +++ b/hooks.mdx @@ -12,8 +12,8 @@ Hooks are code you write, not policy and not a human decision: for rules that al Pass hooks to `createAgent({ hooks })`. This one logs every tool call and what it returned: ```ts -import { createAgent, defineTool, type AgentHook } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool, type AgentHook } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const auditHook: AgentHook = { @@ -84,8 +84,8 @@ that throws still rejects the run, as before. Sub-agents inherit the lead agent's hooks and their outcomes. ```ts -import { createAgent, type AgentHook } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, type AgentHook } from 'lousho'; +import { mockModel } from 'lousho/testing'; const provider = mockModel(['Done.']); @@ -127,8 +127,8 @@ getting anywhere. It's off unless you add it: `LOUSHO_RUN_NO_PROGRESS`, which `agent.recover()` won't retry. ```ts -import { createAgent, createProgressGuard, defineTool } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, createProgressGuard, defineTool } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const lookup = defineTool({ @@ -166,8 +166,8 @@ sees it as `outputCheckFailed` on the result and can fix the output. The check is your code, so make it independent of what the model wrote. ```ts -import { createAgent, createOutputCheck, defineTool } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, createOutputCheck, defineTool } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const files = new Map(); @@ -197,8 +197,8 @@ await agent.send('Write the report'); ### Deny a tool by name ```ts -import { createAgent, defineTool, type AgentHook } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool, type AgentHook } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const shell = defineTool({ @@ -230,8 +230,8 @@ console.log(result.text); Return `{ input }` to run the tool with different arguments. They are validated against the tool's schema again. ```ts -import { createAgent, defineTool, type AgentHook } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool, type AgentHook } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const search = defineTool({ @@ -264,8 +264,8 @@ await agent.send('Search for hooks'); `postToolCall` receives the settled result and may return `{ result }` to replace what the model sees. The tool itself ran unchanged. ```ts -import { createAgent, defineTool, type AgentHook } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool, type AgentHook } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const readLog = defineTool({ @@ -299,8 +299,8 @@ console.log(toolMessage?.role); // 'tool': its content is the redacted text `preGenerate` sees the live `ctx.messages` and `ctx.request`. Push a message to add context, or change a request field. ```ts -import { createAgent, type AgentHook } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, type AgentHook } from 'lousho'; +import { mockModel } from 'lousho/testing'; const addDate: AgentHook = { name: 'add-date', @@ -338,7 +338,7 @@ refused with a tool error instead of running. See [Approvals](/approvals) for th `createAgent({ hooks })` takes a plain array. Code that uses the executor API ([The executor API](/executor-api)) passes a `HookRegistry`: an ordered collection with `register`, `registerMany`, `get`, `has`, `list`, `unregister`, `clear` and `size()` — `size` is a method (`hooks.size()`), not a `Map`-style property. Registering a second hook with the same `name` replaces the first and logs a warning. ```ts -import { HookRegistry, type AgentHook } from '@linuxdevil/lousho/hooks'; +import { HookRegistry, type AgentHook } from 'lousho/hooks'; const redactPii: AgentHook = { name: 'redact-pii', @@ -352,4 +352,4 @@ hooks.register(redactPii); console.log(hooks.list().map((hook) => hook.name)); // [ 'redact-pii' ] ``` -`HookRegistry`, `AgentHook` and the context types are available from the package root and from the `@linuxdevil/lousho/hooks` subpath. Hooks authored in the Agent Forge canvas editor are compiled into a `HookRegistry` and run sandboxed rather than in the host process: see [Agent Forge](/agent-forge#hooks). +`HookRegistry`, `AgentHook` and the context types are available from the package root and from the `lousho/hooks` subpath. Hooks authored in the Agent Forge canvas editor are compiled into a `HookRegistry` and run sandboxed rather than in the host process: see [Agent Forge](/agent-forge#hooks). diff --git a/hosted-tools.mdx b/hosted-tools.mdx index 4007ad4..dfdd8f1 100644 --- a/hosted-tools.mdx +++ b/hosted-tools.mdx @@ -9,7 +9,7 @@ web search, code interpreter and file search are the common ones. Put them in each call in its events, keeps it on the transcript and counts it in usage. ```ts -import { createAgent, webSearch, defineTool } from '@linuxdevil/lousho'; +import { createAgent, webSearch, defineTool } from 'lousho'; import { z } from 'zod'; const saveNote = defineTool({ @@ -112,7 +112,7 @@ providers and with any AI SDK model wrapped by [`fromAiSdk()`](/providers#any-ai-sdk-model-fromaisdk): ```ts -import { createAgent, fromAiSdk, hostedTool } from '@linuxdevil/lousho'; +import { createAgent, fromAiSdk, hostedTool } from 'lousho'; import { openai } from '@ai-sdk/openai'; const agent = createAgent({ @@ -129,7 +129,7 @@ A hosted call is reported like a tool call, with `executedBy: 'provider'` on `tool.start`, `tool.done` and `tool.error`: ```ts -import { createAgent, webSearch } from '@linuxdevil/lousho'; +import { createAgent, webSearch } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', tools: [webSearch()] }); @@ -183,7 +183,7 @@ without a human, leave the tool out of `tools`, or choose tools per run with a function: ```ts -import { createAgent, webSearch } from '@linuxdevil/lousho'; +import { createAgent, webSearch } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', @@ -220,8 +220,8 @@ hosted tools; a resuming agent with a different set reports request, so an agent with hosted tools runs offline: ```ts -import { createAgent, webSearch } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, webSearch } from 'lousho'; +import { mockModel } from 'lousho/testing'; const model = mockModel([ { diff --git a/improve.mdx b/improve.mdx index 6f683d8..b799331 100644 --- a/improve.mdx +++ b/improve.mdx @@ -32,9 +32,9 @@ It's experimental: its results so far come from a small benchmark. For how this fits with recovering failed runs while the agent serves traffic, see [Self-healing and self-improvement](/self-healing). ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; -import { formatImproveResult, improveAgent, renderTranscript, type Harness } from '@linuxdevil/lousho/improve'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool } from 'lousho'; +import { formatImproveResult, improveAgent, renderTranscript, type Harness } from 'lousho/improve'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const search = defineTool({ @@ -218,7 +218,7 @@ files. your own function, keep its `cached` field so the run is counted as cached. ```ts -import { cachedProbe, improveAgent, type Harness, type ImproveOptions } from '@linuxdevil/lousho/improve'; +import { cachedProbe, improveAgent, type Harness, type ImproveOptions } from 'lousho/improve'; declare const harness: Harness; declare const probe: ImproveOptions['probe']; @@ -301,7 +301,7 @@ gate changes: it judges the harness, not the reasons. A `CaseCheck` returns `true`, `false`, `null` when it can't score the answer, or `{ passed, failure }`. Return `failure` on a failed answer to name the check it missed, as a custom `probe` does: each editor reads it in that run's record, after `Result: failed`, on one line with forged untrusted markers removed. A check must be synchronous; any other return value throws a `ConfigurationError`. Without it, a failed run whose answer looks right gives the editors nothing to fix. Name the real check; a wrong name is worse than none. A `failure` on a passed answer is dropped. ```ts -import type { CaseCheck } from '@linuxdevil/lousho/improve'; +import type { CaseCheck } from 'lousho/improve'; declare function failedTests(input: string, answer: string): string[]; @@ -316,7 +316,7 @@ const check: CaseCheck = (input, answer) => { With `retryWithDiagnosis`, [`agent.recover()`](/self-healing#recover-a-run-now) forks a run that kept failing into a `.heal` session told why it failed. When that fork passes, you hold a pair on the same input: the failed run and a retry that passed. `casesFromRecoveries(db, filter)` finds those pairs in your local database and returns the failed runs as cases, each with a `recovery`: the retry's transcript, rendered with `renderTranscript`, the diagnosis it was given, and its final answer. A fork counts only when it carries the diagnosis and finished with no recorded error, no answer cut short and no low score. That isn't your check, so score each `recovery.answer` with your `CaseCheck` and keep only the ones that pass, then pass them on as `recoveries`: ```ts -import { casesFromRecoveries, improveAgent, type CaseCheck, type Harness, type ImproveOptions, type SqlDatabase } from '@linuxdevil/lousho/improve'; +import { casesFromRecoveries, improveAgent, type CaseCheck, type Harness, type ImproveOptions, type SqlDatabase } from 'lousho/improve'; declare const db: SqlDatabase; declare const harness: Harness; @@ -351,7 +351,7 @@ A library keeps lessons from earlier attempts, so later editors can read them. E `libraryFiles(dir, domain)` returns up to 20 active lessons as files under `context/library/`, highest-voted first, then newest. Each file is labelled as data with the lesson between untrusted markers, and you pass them to `improveAgent` as `library`. Entries are checked again when read, so an entry file edited by hand that no longer passes is skipped. `voteEntry(dir, id, 'helped' | 'hurt')` counts an outcome, and retires a lesson for good once its hurt votes outnumber its helped votes by two. Votes are appended to `votes.jsonl` next to the entries, so votes cast at once aren't lost. `improveAgent` accepts only files shaped like `libraryFiles` output, named `context/library/.md`. ```ts -import { improveAgent, libraryFiles, libraryHome, type Harness, type ImproveOptions } from '@linuxdevil/lousho/improve'; +import { improveAgent, libraryFiles, libraryHome, type Harness, type ImproveOptions } from 'lousho/improve'; declare const harness: Harness; declare const probe: ImproveOptions['probe']; @@ -367,7 +367,7 @@ const result = await improveAgent({ harness, cases: ['stuck'], probe, editor: { Once a cycle is decided, `reflect` turns it into at most one lesson. A cycle is decided when its edit was kept, rejected, or applied and then rolled back. A rejected or rolled-back edit teaches as much as a kept one. The reflection session reads four files and nothing else: the review card, the diff, the case verdicts and the outcome. It never sees a raw transcript, and the text that models wrote reaches it between untrusted markers. It replies with an `` block (cue, lesson, evidence, confidence) or `NO_ENTRY`, and the lesson goes through `addEntry` with the cycle's failed checks, so a refused lesson is reported and never stored. `reflect` never throws. A failure comes back as `error` for you to record, and `tokens` is what it spent, to count toward the cycle's budget, which `maxTokens` caps. ```ts -import { libraryHome, reflect, voteOnOutcome, type ImproveResult } from '@linuxdevil/lousho/improve'; +import { libraryHome, reflect, voteOnOutcome, type ImproveResult } from 'lousho/improve'; declare const result: ImproveResult; @@ -391,7 +391,7 @@ The session never sees a held-out case's id: each held-out case appears as `held `settleCanary` reads the trial; call it yourself on a timer when you don't use the watcher. After `sessions` candidate sessions it promotes when the share of flagged sessions is 0 or at least 0.05 below the base's rate over its last 20 runs, and no failure class appeared that the base didn't have. It retires early once a promotion is ruled out, and when the directory changed, the loop is paused, the opt-in is removed or `monitor.giveUpDays` pass. A promote goes through `applyProposal`, which checks the proposal again. A canary started in auto mode is queued for a person instead when `improve.json` has since switched to manual. In manual mode, `startCanary` refuses without `approvedBy`, and a canary without an approver serves no one. ```ts -import { loadImproveSettings, settleCanary, startCanary, type Harness, type ImproveResult, type SqlDatabase } from '@linuxdevil/lousho/improve'; +import { loadImproveSettings, settleCanary, startCanary, type Harness, type ImproveResult, type SqlDatabase } from 'lousho/improve'; declare const db: SqlDatabase; declare const result: ImproveResult; @@ -474,7 +474,7 @@ See [The monitor](#the-monitor). under a harness; `lousho improve` builds it for you. ```ts -import { healOnce, loadImproveSettings, type CaseCheck, type HealOptions, type SqlDatabase } from '@linuxdevil/lousho/improve'; +import { healOnce, loadImproveSettings, type CaseCheck, type HealOptions, type SqlDatabase } from 'lousho/improve'; declare const db: SqlDatabase; declare const check: CaseCheck; @@ -596,7 +596,7 @@ tick as a [schedule](/schedules) for an agent that already uses `schedules/`: ```ts -import { improveSchedule, loadImproveSettings, watchAgent, type CaseCheck, type HealOptions, type SqlDatabase } from '@linuxdevil/lousho/improve'; +import { improveSchedule, loadImproveSettings, watchAgent, type CaseCheck, type HealOptions, type SqlDatabase } from 'lousho/improve'; declare const db: SqlDatabase; declare const check: CaseCheck; diff --git a/installation.mdx b/installation.mdx index b33acce..46d632f 100644 --- a/installation.mdx +++ b/installation.mdx @@ -12,11 +12,11 @@ title: "Installation" ## Install the package ```bash -npm install @linuxdevil/lousho ai zod +npm install lousho ai zod # or -pnpm add @linuxdevil/lousho ai zod +pnpm add lousho ai zod # or -yarn add @linuxdevil/lousho ai zod +yarn add lousho ai zod ``` `ai` (the Vercel AI SDK: `^4.3.19`, `^6.0.0` or `^7.0.0`) and `zod` @@ -45,7 +45,7 @@ that pairs with your `ai` major. Install the pair from one row: For example, on the current `ai` major: ```bash -npm install @linuxdevil/lousho ai@^7.0.0 zod @ai-sdk/openai@^4.0.0 +npm install lousho ai@^7.0.0 zod @ai-sdk/openai@^4.0.0 ``` **Ollama on `ai` 6/7 needs zod 4.** `ollama-ai-provider-v2` (the Ollama package @@ -63,7 +63,7 @@ with zod 4, and `ai` 7 with zod 3 and with zod 4. The two zod 4 entries on `ai` 6 and 7 also install `ollama-ai-provider-v2` (3.x and 4.x) and run the Ollama provider against it, so Ollama on `ai` 6/7 is tested, not assumed. -Peers are loaded on demand: importing `@linuxdevil/lousho` (or any of +Peers are loaded on demand: importing `lousho` (or any of its sub-entries) never loads a provider package, so you only need to install the ones you use. Each provider package is loaded the first time that provider makes a call; if it is missing, that call fails with a @@ -77,7 +77,7 @@ npm install @ai-sdk/openai@^0.0.42 ``` ```ts -import { MissingPeerDependencyError, resolveProvider } from '@linuxdevil/lousho'; +import { MissingPeerDependencyError, resolveProvider } from 'lousho'; const provider = resolveProvider('openai/gpt-4o-mini'); // needs OPENAI_API_KEY; does not load the peer try { @@ -99,9 +99,9 @@ the `lousho init` questions or code mode installs none of them: | --------------------------- | --------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------ | | `dockerode` | `^5.0.1` | Docker sandboxing: `SubprocessSandbox` (and `lousho doctor`'s Docker ping) | `npm install dockerode@^5.0.1` | | `@modelcontextprotocol/sdk` | `^1.31.0` | MCP: `createAgent({ mcpServers })` / `connectMcp()`, `serveMcp()` and `lousho mcp` | `npm install @modelcontextprotocol/sdk@^1.31.0` | -| `@opentelemetry/api` | `^1.9.1` | OpenTelemetry tracing: `createOtelTraceExporter()` from `@linuxdevil/lousho/otel` | `npm install @opentelemetry/api@^1.9.1` | -| `react` | `^18`, `^19` | The React bindings (`@linuxdevil/lousho/react`) | `npm install react` | -| `vue` | `^3` | The Vue bindings (`@linuxdevil/lousho/vue`) | `npm install vue@^3` | +| `@opentelemetry/api` | `^1.9.1` | OpenTelemetry tracing: `createOtelTraceExporter()` from `lousho/otel` | `npm install @opentelemetry/api@^1.9.1` | +| `react` | `^18`, `^19` | The React bindings (`lousho/react`) | `npm install react` | +| `vue` | `^3` | The Vue bindings (`lousho/vue`) | `npm install vue@^3` | | `tsup` | `^8.0.0` | `lousho build`, which bundles the agent for each target | `npm install --save-dev tsup@^8.0.0` | | `prompts` | `^2.4.2` | The interactive questions of `lousho init` (`lousho init --yes` and flags need nothing) | `npm install prompts@^2.4.2` | | `quickjs-emscripten` | `^0.32.0` | [Code mode](/code-mode): `createAgent({ codeMode })` runs `run_code` scripts in its QuickJS isolate | `npm install quickjs-emscripten@^0.32.0` | @@ -114,7 +114,7 @@ that names the feature and the exact command, for example: The optional package 'dockerode' is not installed, but Docker sandboxing (SubprocessSandbox) needs it. Run: npm install dockerode@^5.0.1 ``` -`npm create @linuxdevil/lousho` installs `prompts` itself, so scaffolding with it +`npm create lousho` installs `prompts` itself, so scaffolding with it needs nothing extra. `lousho doctor` lists every optional peer, what it enables and whether it is installed (a missing `dockerode` is an error only when the agent spec uses a sandboxed tool). @@ -153,25 +153,25 @@ The entries `exports` declares, and what each one holds: | Import specifier | What it holds | | ---------------- | ------------- | -| `@linuxdevil/lousho` | The root: `createAgent()`, `defineTool()`, `defineChannel()`, agents' public types, events, stores (`memoryStore()`, `fileStore()`), hooks, guardrails, sessions, skills, memory, sub-agents, evals, spec files, deployment types | -| `@linuxdevil/lousho/executor` | The executor API (advanced - prefer `createAgent()`): `AgentExecutor`, `AgentBuilder`, `resumeAfterApproval()`, `streamResumeAfterApproval()`, `resumeRequest()`, `ToolRegistry`, `globalToolRegistry` | -| `@linuxdevil/lousho/tools` | The tools surface: `defineTool()`, `ToolRegistry`, the built-in tools, MCP/OpenAPI helpers | -| `@linuxdevil/lousho/flows` | `FlowBuilder`, `FlowExecutor`, `validateFlow()` and every flow type (`AgentFlow`, `EditorStep`, `FlowExecutionEvent`, ...) | -| `@linuxdevil/lousho/integrations` | The credentialed third-party tools: `createJiraTools()`, `createGitHubTools()`, `createSlackTool()` / `slackTool`, `postSlackAlert()`, `createEmailTool()` and their types | -| `@linuxdevil/lousho/utils` | `EncryptionUtils`, `sha256()`, `generatePassword()`, `StorageService`, `StorageServiceApprovalStore`, `LocalStorageCheckpointStore` and the validators (`validateWithSchema`, `isValidEmail`, ...) | -| `@linuxdevil/lousho/mcp` | MCP client and server helpers: `connectMcp()`, `loadMcpTools()`, `serveMcp()` | -| `@linuxdevil/lousho/types` | The public types without the runtime | -| `@linuxdevil/lousho/testing` | `mockModel()`, `recordReplay()` and the offline-test helpers | -| `@linuxdevil/lousho/otel` | `createOtelTraceExporter()` (needs the optional `@opentelemetry/api` peer) | -| `@linuxdevil/lousho/hooks` | The hooks surface: `HookRegistry`, hook types | -| `@linuxdevil/lousho/sqlite` | `SqliteStore` (sessions, checkpoints, approvals and tokens in one SQLite file; needs Node's `node:sqlite`) | -| `@linuxdevil/lousho/auth` | Route auth: `routeAuth()`, `jwt()`, `oidc()`, `basic()`, `apiToken()`, `anonymous()` | -| `@linuxdevil/lousho/kv` | `KVStore`, `KVCheckpointStore` for a hand-written Cloudflare Worker | -| `@linuxdevil/lousho/traces` | `fileTraceExporter()`, `listTraces()`, `readTrace()` (what `lousho traces` reads) | -| `@linuxdevil/lousho/triggers` | `TriggerAdapter`, `TriggerRegistry`, `verifySlackSignature`, `parseCronExpression` | -| `@linuxdevil/lousho/react` | `useLoushoAgent()` and the React helpers (needs `react`) | -| `@linuxdevil/lousho/vue` | `useLoushoAgent()` as a Vue composable (needs `vue`) | -| `@linuxdevil/lousho/svelte` | `loushoAgent()` as a Svelte store | +| `lousho` | The root: `createAgent()`, `defineTool()`, `defineChannel()`, agents' public types, events, stores (`memoryStore()`, `fileStore()`), hooks, guardrails, sessions, skills, memory, sub-agents, evals, spec files, deployment types | +| `lousho/executor` | The executor API (advanced - prefer `createAgent()`): `AgentExecutor`, `AgentBuilder`, `resumeAfterApproval()`, `streamResumeAfterApproval()`, `resumeRequest()`, `ToolRegistry`, `globalToolRegistry` | +| `lousho/tools` | The tools surface: `defineTool()`, `ToolRegistry`, the built-in tools, MCP/OpenAPI helpers | +| `lousho/flows` | `FlowBuilder`, `FlowExecutor`, `validateFlow()` and every flow type (`AgentFlow`, `EditorStep`, `FlowExecutionEvent`, ...) | +| `lousho/integrations` | The credentialed third-party tools: `createJiraTools()`, `createGitHubTools()`, `createSlackTool()` / `slackTool`, `postSlackAlert()`, `createEmailTool()` and their types | +| `lousho/utils` | `EncryptionUtils`, `sha256()`, `generatePassword()`, `StorageService`, `StorageServiceApprovalStore`, `LocalStorageCheckpointStore` and the validators (`validateWithSchema`, `isValidEmail`, ...) | +| `lousho/mcp` | MCP client and server helpers: `connectMcp()`, `loadMcpTools()`, `serveMcp()` | +| `lousho/types` | The public types without the runtime | +| `lousho/testing` | `mockModel()`, `recordReplay()` and the offline-test helpers | +| `lousho/otel` | `createOtelTraceExporter()` (needs the optional `@opentelemetry/api` peer) | +| `lousho/hooks` | The hooks surface: `HookRegistry`, hook types | +| `lousho/sqlite` | `SqliteStore` (sessions, checkpoints, approvals and tokens in one SQLite file; needs Node's `node:sqlite`) | +| `lousho/auth` | Route auth: `routeAuth()`, `jwt()`, `oidc()`, `basic()`, `apiToken()`, `anonymous()` | +| `lousho/kv` | `KVStore`, `KVCheckpointStore` for a hand-written Cloudflare Worker | +| `lousho/traces` | `fileTraceExporter()`, `listTraces()`, `readTrace()` (what `lousho traces` reads) | +| `lousho/triggers` | `TriggerAdapter`, `TriggerRegistry`, `verifySlackSignature`, `parseCronExpression` | +| `lousho/react` | `useLoushoAgent()` and the React helpers (needs `react`) | +| `lousho/vue` | `useLoushoAgent()` as a Vue composable (needs `vue`) | +| `lousho/svelte` | `loushoAgent()` as a Svelte store | ### TypeScript settings @@ -241,8 +241,8 @@ one-line `CLAUDE.md` importing it) telling coding agents the project's files, scripts and conventions. It then installs the dependencies and runs `git init`. ```bash -npm create @linuxdevil/lousho my-agent # or: npx @linuxdevil/lousho init my-agent -npx @linuxdevil/lousho init my-agent --yes --provider anthropic --template tools --no-install +npm create lousho my-agent # or: npx lousho init my-agent +npx lousho init my-agent --yes --provider anthropic --template tools --no-install ``` | Flag | Meaning | @@ -258,9 +258,9 @@ npx @linuxdevil/lousho init my-agent --yes --provider anthropic --template tools `create-lousho` (`packages/create-lousho`) is a thin wrapper that runs `lousho init` with the same arguments. -## The `@linuxdevil/lousho` CLI +## The `lousho` CLI -Installing the package also installs the `@linuxdevil/lousho` command: `init`, `doctor`, +Installing the package also installs the `lousho` command: `init`, `doctor`, `dev`, `chat`, `acp`, `add`, `mcp`, `eval`, `traces`, `build`, `studio`, `observe`, `bridge` and more. See [CLI](/cli) for what each one does and its flags. Building needs `tsup`, @@ -269,15 +269,15 @@ an optional peer (see the table above): `npm install --save-dev tsup`. ### Studio (optional) The visual dashboard, Lousho Studio, is a separate package, -`@linuxdevil/studio` - it is not part of this tarball. Install it with -`npm i -g @linuxdevil/studio` (or run `npx @linuxdevil/studio`), or just run +`lousho-studio` - it is not part of this tarball. Install it with +`npm i -g lousho-studio` (or run `npx lousho-studio`), or just run `lousho studio` inside a project that has the SDK: the launcher finds an installed Studio and opens the folder as a workspace. See [Lousho Studio](/agent-forge). ## Troubleshooting: lousho doctor -Run `npx @linuxdevil/lousho doctor` right after installing. It prints one line per check +Run `npx lousho doctor` right after installing. It prints one line per check with a status (`ok`, `warn`, `FAIL`), what it found, and, for anything that is not ok, the command to run or the setting to change: diff --git a/mcp-integration.mdx b/mcp-integration.mdx index beabc2a..32a40e6 100644 --- a/mcp-integration.mdx +++ b/mcp-integration.mdx @@ -6,7 +6,7 @@ sidebarTitle: "MCP" MCP works in two directions. Your agent can call the tools of MCP servers, or your agent can be an MCP server that Claude Code, Cursor and other MCP clients -call. Both live in the `@linuxdevil/lousho/mcp` subpath, and +call. Both live in the `lousho/mcp` subpath, and `@modelcontextprotocol/sdk` is an optional peer: install it to use either direction. @@ -15,7 +15,7 @@ npm install @modelcontextprotocol/sdk ``` `connectMcp`, `loadMcpTools` and `serveMcp` are exported from -`@linuxdevil/lousho/mcp`; the package root and `@linuxdevil/lousho/tools` +`lousho/mcp`; the package root and `lousho/tools` export them too. `createAgent({ mcpServers })` needs no import from the subpath. ## Use MCP servers in an agent @@ -30,7 +30,7 @@ or `ready()`, reconnects. Without `mcpServers`, `ready()` and `close()` do nothi `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', @@ -126,8 +126,8 @@ own tools and MCP tools can sit in one list. (Spreading into one record, `tools: { ...mcp.tools, my_tool: myTool }`, works too.) ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { connectMcp } from '@linuxdevil/lousho/mcp'; +import { createAgent } from 'lousho'; +import { connectMcp } from 'lousho/mcp'; const mcp = await connectMcp( { files: { command: 'npx', args: ['-y', '@modelcontextprotocol/server-filesystem', '.'] } }, @@ -204,7 +204,7 @@ in `loadMcpTools(client, name, { approval })`: `allow`, `deny` or `ask`. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', @@ -228,15 +228,15 @@ its tools with `loadMcpTools()`, then pass the result to `createAgent()` (or register it on a `ToolRegistry`): ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { loadMcpTools } from '@linuxdevil/lousho/mcp'; +import { createAgent } from 'lousho'; +import { loadMcpTools } from 'lousho/mcp'; const tools = await loadMcpTools(mcpClient, 'my-server'); const agent = createAgent({ instructions: '...', provider, tools }); ``` `loadMcpTools` is also available from the package root and from -`@linuxdevil/lousho/tools`. +`lousho/tools`. ## Serve an agent over MCP @@ -245,8 +245,8 @@ optionally, some of its tools) as an MCP server, so Claude Code, Cursor and other MCP clients can call it. ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; -import { serveMcp } from '@linuxdevil/lousho/mcp'; +import { createAgent, defineTool } from 'lousho'; +import { serveMcp } from 'lousho/mcp'; import { z } from 'zod'; const searchDocs = defineTool({ @@ -311,7 +311,7 @@ needs approval is never advertised read-only. The built-in `read_file`, `list_di `glob`, `grep`, `todo_read`, `current_date` and `day_name` tools are read-only. ```ts -import { defineTool } from '@linuxdevil/lousho'; +import { defineTool } from 'lousho'; import { z } from 'zod'; const lookupOrder = defineTool({ @@ -328,8 +328,8 @@ const lookupOrder = defineTool({ `lousho mcp` serves an agent spec file (stdio by default): ```sh -npx @linuxdevil/lousho mcp agent.yaml -npx @linuxdevil/lousho mcp agent.yaml --http --port 3920 --host 127.0.0.1 +npx lousho mcp agent.yaml +npx lousho mcp agent.yaml --http --port 3920 --host 127.0.0.1 ``` To use it from an MCP client, add it to the client's MCP config (for example diff --git a/memory.mdx b/memory.mdx index 234c137..3979d9e 100644 --- a/memory.mdx +++ b/memory.mdx @@ -39,7 +39,7 @@ The `vendor/` model prefix chooses the provider; with OpenRouter use `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ```ts -import { createAgent, defineMemory, fileMemory } from '@linuxdevil/lousho'; +import { createAgent, defineMemory, fileMemory } from 'lousho'; const preferences = defineMemory({ name: 'preferences', @@ -87,8 +87,8 @@ A scope function sees the run's `sessionId` and the `metadata` passed to sessions: ```ts -import { createAgent, defineMemory, inMemoryMemory } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineMemory, inMemoryMemory } from 'lousho'; +import { mockModel } from 'lousho/testing'; const userFacts = defineMemory({ name: 'user_facts', @@ -131,7 +131,7 @@ every start: `add()` dedupes on text, so nothing is copied twice. Each copy keeps `text` and `metadata` and gets a new `id` and `createdAt`. ```ts -import { defineMemory, fileMemory, migrateMemoryKeys } from '@linuxdevil/lousho'; +import { defineMemory, fileMemory, migrateMemoryKeys } from 'lousho'; const provider = fileMemory({ dir: './.lousho/memory' }); const notes = defineMemory({ name: 'notes', scope: 'global', provider }); @@ -169,7 +169,7 @@ the count. ```ts import { z } from 'zod'; -import { defineMemory, inMemoryMemory } from '@linuxdevil/lousho'; +import { defineMemory, inMemoryMemory } from 'lousho'; const relationship = defineMemory({ name: 'relationship', @@ -200,7 +200,7 @@ adding a second version. The item keeps its id, becomes the newest, and ```ts import { z } from 'zod'; -import { defineMemory, inMemoryMemory } from '@linuxdevil/lousho'; +import { defineMemory, inMemoryMemory } from 'lousho'; const settings = defineMemory({ name: 'settings', @@ -286,8 +286,8 @@ by meaning instead of by keyword, use `sqliteVectorMemory()`: see [Semantic recall](#semantic-recall). ```ts -import { createAgent, defineMemory } from '@linuxdevil/lousho'; -import { SqliteStore, sqliteMemory } from '@linuxdevil/lousho/sqlite'; +import { createAgent, defineMemory } from 'lousho'; +import { SqliteStore, sqliteMemory } from 'lousho/sqlite'; const store = new SqliteStore('./.lousho/agent.db'); const notes = defineMemory({ name: 'notes', scope: 'global', provider: sqliteMemory(store) }); @@ -297,7 +297,7 @@ const agent = createAgent({ model: 'openai/gpt-4o-mini', store, memory: [notes] For a hosted store or your own ranking, implement `MemoryProvider`: ```ts -import type { MemoryItem, MemoryProvider } from '@linuxdevil/lousho'; +import type { MemoryItem, MemoryProvider } from 'lousho'; const items = new Map(); const myProvider: MemoryProvider = { @@ -326,8 +326,8 @@ With [`mockModel`](/testing), assert on the system prompt of the first request and drive the tools with scripted tool calls: ```ts -import { createAgent, defineMemory, inMemoryMemory, memoryKey } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineMemory, inMemoryMemory, memoryKey } from 'lousho'; +import { mockModel } from 'lousho/testing'; const store = inMemoryMemory(); const notes = defineMemory({ name: 'notes', scope: 'global', provider: store }); @@ -369,7 +369,7 @@ package that pairs with your `ai` major, as for language models). It adds no dependency: `ai` is already a peer, and the provider package is yours. ```ts -import { createAgent, defineMemory, aiSdkEmbedder, inMemoryVectorMemory } from '@linuxdevil/lousho'; +import { createAgent, defineMemory, aiSdkEmbedder, inMemoryVectorMemory } from 'lousho'; import { createOpenAI } from '@ai-sdk/openai'; const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY }); @@ -391,7 +391,7 @@ Two providers rank by meaning, with the same options: - `inMemoryVectorMemory({ embedder, maxItems?, minScore? })` keeps items in this process. - `sqliteVectorMemory(store, { embedder, maxItems?, minScore? })` (from - `@linuxdevil/lousho/sqlite`) keeps them in the agent's SQLite file, in a + `lousho/sqlite`) keeps them in the agent's SQLite file, in a `memory_vectors` table that an existing file gains when it is opened. Vectors survive restarts. @@ -415,14 +415,14 @@ re-embeds them in batches and returns how many it changed. `reindex(providerKey)` does one key only — for a slot's key, pass `memoryKey(slot, ctx)`. -`hashEmbedder()` from `@linuxdevil/lousho/testing` is a deterministic +`hashEmbedder()` from `lousho/testing` is a deterministic offline embedder for tests: a hashed bag of word stems, so texts that share words are close. It is not a semantic model and does not know that "vegetarian" and "eat" are related. ```ts -import { inMemoryVectorMemory } from '@linuxdevil/lousho'; -import { hashEmbedder } from '@linuxdevil/lousho/testing'; +import { inMemoryVectorMemory } from 'lousho'; +import { hashEmbedder } from 'lousho/testing'; const memory = inMemoryVectorMemory({ embedder: hashEmbedder() }); await memory.add('global', { text: 'Sam is vegetarian' }); diff --git a/migrating-from-ai-sdk.mdx b/migrating-from-ai-sdk.mdx index 63c3e33..a1e32c5 100644 --- a/migrating-from-ai-sdk.mdx +++ b/migrating-from-ai-sdk.mdx @@ -45,7 +45,7 @@ you already configured (keys, base URL, provider options) works as it is: ```ts import { createOpenAI } from '@ai-sdk/openai'; -import { createAgent, fromAiSdk } from '@linuxdevil/lousho'; +import { createAgent, fromAiSdk } from 'lousho'; const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY }); const agent = createAgent({ provider: fromAiSdk(openai.chat('gpt-4o-mini')), instructions: 'You are helpful.' }); @@ -63,7 +63,7 @@ See [Providers](/providers#any-ai-sdk-model-fromaisdk) for the options and which ```ts import { tool } from 'ai'; import { z } from 'zod'; -import { createAgent, fromAiSdk, fromAiSdkTools } from '@linuxdevil/lousho'; +import { createAgent, fromAiSdk, fromAiSdkTools } from 'lousho'; const weather = tool({ description: 'Get the weather in a city', @@ -89,8 +89,8 @@ and a plain object works too. This one runs offline: ```ts import { z } from 'zod'; -import { createAgent, fromAiSdkTools } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, fromAiSdkTools } from 'lousho'; +import { mockModel } from 'lousho/testing'; const tools = fromAiSdkTools({ weather: { @@ -132,7 +132,7 @@ A `useChat` front end keeps working: the route returns the agent's run as a UI m stream. ```ts -import { createAgent, fromUIMessages, memoryStore, toUIMessageStreamResponse, type UIMessageLike } from '@linuxdevil/lousho'; +import { createAgent, fromUIMessages, memoryStore, toUIMessageStreamResponse, type UIMessageLike } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', instructions: 'You are helpful.', store: memoryStore() }); diff --git a/migrating-to-create-agent.mdx b/migrating-to-create-agent.mdx index f932591..984d868 100644 --- a/migrating-to-create-agent.mdx +++ b/migrating-to-create-agent.mdx @@ -6,7 +6,7 @@ sidebarTitle: "Migrating to createAgent()" This page is for code that uses `AgentBuilder`, `AgentExecutor`, `ToolRegistry` and `resumeAfterApproval()`: the lower-level API the SDK started with. Those exports now -live at `@linuxdevil/lousho/executor` and are not going away; if you stay on +live at `lousho/executor` and are not going away; if you stay on them, see [The executor API](/executor-api). The reason to move is that one `createAgent()` call wires tools, stores, approvals, sessions, retries and compaction, where the executor makes you assemble each of them and pass them to every @@ -28,9 +28,9 @@ import { AgentExecutor, ToolRegistry, resumeAfterApproval, -} from '@linuxdevil/lousho/executor'; -import { InMemoryApprovalStore, defineTool } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +} from 'lousho/executor'; +import { InMemoryApprovalStore, defineTool } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const sendEmail = defineTool({ @@ -72,8 +72,8 @@ console.log(result.text); // 'Sent the report to Sam.' ``` ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const sendEmail = defineTool({ diff --git a/models-and-cost.mdx b/models-and-cost.mdx index d4640d5..30e2b8f 100644 --- a/models-and-cost.mdx +++ b/models-and-cost.mdx @@ -10,7 +10,7 @@ sidebarTitle: "Models and cost" Dependency-free helpers for budgeting and context decisions. ```ts -import { estimateTokens, estimateCost, getModelInfo, registerModel } from '@linuxdevil/lousho'; +import { estimateTokens, estimateCost, getModelInfo, registerModel } from 'lousho'; // A custom or self-hosted model: add it (or override a built-in) before use. registerModel({ @@ -40,7 +40,7 @@ The built-in context windows and prices are a dated snapshot (see the retrieval Every `ExecutionResult` (and `agent.send()` result) carries `usage`, the running total of the whole run: ```ts -import { createAgent, formatUsage } from '@linuxdevil/lousho'; +import { createAgent, formatUsage } from 'lousho'; const result = await createAgent({ provider, instructions: 'You compare cities.' }).send('Compare 3 cities'); @@ -65,7 +65,7 @@ console.log(formatUsage(result.usage)); // 1,234 in / 567 out tokens · $0.0042 Prices come from the model registry above, so to get a cost for a custom or fine-tuned model, register it under the id you pass as the model: ```ts -import { registerModel } from '@linuxdevil/lousho'; +import { registerModel } from 'lousho'; registerModel({ id: 'ft:gpt-4o-mini:acme', @@ -86,7 +86,7 @@ Anthropic caches a request prefix only up to a `cache_control` breakpoint that t ```ts import { readFileSync } from 'node:fs'; -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const longPolicy = readFileSync('./policy.md', 'utf8'); // a long, stable system prompt const agent = createAgent({ model: 'openrouter/anthropic/claude-haiku-4.5', instructions: longPolicy }); // promptCaching: 'auto' diff --git a/nextjs.mdx b/nextjs.mdx index 5d7d0f4..a45ef0e 100644 --- a/nextjs.mdx +++ b/nextjs.mdx @@ -17,7 +17,7 @@ prefix chooses the provider; with OpenRouter use `openrouter//` ```ts // app/api/agent/[[...path]]/route.ts -import { createAgent, createRouteHandler, memoryStore } from '@linuxdevil/lousho'; +import { createAgent, createRouteHandler, memoryStore } from 'lousho'; // `store` is what lets a session survive across requests: without one every // `agent.session({ id })` the handler opens gets a fresh in-memory transcript, @@ -40,7 +40,7 @@ and spend your model budget. Pass a bearer token, or a function that checks your own session or cookie: ```ts -import { createAgent, createRouteHandler } from '@linuxdevil/lousho'; +import { createAgent, createRouteHandler } from 'lousho'; declare function isSignedIn(request: Request): Promise; @@ -93,8 +93,8 @@ customer's session (`GET /chat/:id`, to show what they are approving) and to decide in it; by default the session belongs to the customer alone: ```ts -import { createAgent, createRouteHandler, memoryStore } from '@linuxdevil/lousho'; -import { jwt } from '@linuxdevil/lousho/auth'; +import { createAgent, createRouteHandler, memoryStore } from 'lousho'; +import { jwt } from 'lousho/auth'; const agent = createAgent({ model: 'openai/gpt-4o-mini', store: memoryStore() }); const isSupervisor = (principal?: { claims?: Readonly> }) => principal?.claims?.role === 'supervisor'; @@ -135,7 +135,7 @@ see [Route auth and principals](/auth). ```tsx 'use client'; -import { useLoushoAgent } from '@linuxdevil/lousho/react'; +import { useLoushoAgent } from 'lousho/react'; export function Chat() { const agent = useLoushoAgent({ url: '/api/agent', headers: { Authorization: `Bearer ${token}` } }, { approvalsUrl: '/api/agent/approvals' }); @@ -162,7 +162,7 @@ const { messages, sendMessage } = useChat({ transport: new DefaultChatTransport( The same handler, mounted where the framework wants it: ```ts -import { createAgent, createRouteHandler } from '@linuxdevil/lousho'; +import { createAgent, createRouteHandler } from 'lousho'; const { handler } = createRouteHandler(createAgent({ model: 'openai/gpt-4o-mini' }), { basePath: '/agent', auth: 'secret' }); diff --git a/oauth.mdx b/oauth.mdx index 2ffe22e..9fae594 100644 --- a/oauth.mdx +++ b/oauth.mdx @@ -41,7 +41,7 @@ token, an operator who knows which authenticator the old tokens belong to moves them once: ```ts -import { fileStore } from '@linuxdevil/lousho'; +import { fileStore } from 'lousho'; const store = fileStore('./.lousho', { tokenKey: process.env.LOUSHO_TOKEN_KEY }); const legacy = { owner: 'user', principalId: 'U04ABCDEF' } as const; @@ -63,7 +63,7 @@ key unambiguous: a principal id containing `|` cannot collide with another owner. ```ts -import { tokenStoreKey } from '@linuxdevil/lousho'; +import { tokenStoreKey } from 'lousho'; tokenStoreKey('github', { owner: 'app' }); // 'github|app' tokenStoreKey('github', { owner: 'user', principalId: 'a|b', issuer: 'https://id.example.com' }); @@ -75,7 +75,7 @@ tokenStoreKey('github', { owner: 'user', principalId: 'a|b', issuer: 'https://id `AgentStore.tokens` is an `OAuthTokenStore`: ```ts -import { memoryStore } from '@linuxdevil/lousho'; +import { memoryStore } from 'lousho'; const { tokens } = memoryStore(); const alice = { owner: 'user', principalId: 'alice', issuer: 'https://id.example.com' } as const; @@ -118,7 +118,7 @@ node -e "console.log(Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toS ``` ```ts -import { createAgent, fileStore, generateTokenKey } from '@linuxdevil/lousho'; +import { createAgent, fileStore, generateTokenKey } from 'lousho'; console.log(generateTokenKey()); // store it as a secret, e.g. LOUSHO_TOKEN_KEY @@ -174,7 +174,7 @@ Define the provider once, at module level next to the tools that use it, with `defineOAuthProvider()`. A tool asks for a token with `ctx.getToken(provider)`: ```ts -import { createAgent, defineOAuthProvider, defineTool, memoryStore } from '@linuxdevil/lousho'; +import { createAgent, defineOAuthProvider, defineTool, memoryStore } from 'lousho'; import { z } from 'zod'; export const github = defineOAuthProvider({ @@ -239,7 +239,7 @@ the agent's credential for every user: without the token the call fails with The operator signs the app in once with `agent.oauth.signInUrl(provider)`: ```ts -import { createAgent, defineOAuthProvider, fileStore } from '@linuxdevil/lousho'; +import { createAgent, defineOAuthProvider, fileStore } from 'lousho'; const slackBot = defineOAuthProvider({ name: 'slack_bot', @@ -322,7 +322,7 @@ link continues the run by approving the pause. Serving your own route, do the same: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ provider }); @@ -396,8 +396,8 @@ token store (encrypted by the persistent stores) under the provider name one. ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { SqliteStore } from '@linuxdevil/lousho/sqlite'; +import { createAgent } from 'lousho'; +import { SqliteStore } from 'lousho/sqlite'; const agent = createAgent({ provider, diff --git a/observability.mdx b/observability.mdx index a7e9a7a..0a15257 100644 --- a/observability.mdx +++ b/observability.mdx @@ -23,14 +23,14 @@ mapping. Pass an `exporter` to `createAgent()`; every run of the agent (`send()`, `stream()`, session turns, `resume()` and runs continued by `agent.approvals.resolve()`) is traced, sub-agents included. To keep traces -on disk and browse them with `npx @linuxdevil/lousho traces`, use `fileTraceExporter()` +on disk and browse them with `npx lousho traces`, use `fileTraceExporter()` (see [Local traces](#local-traces)). The `vendor/` model prefix chooses the provider; with OpenRouter use `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { fileTraceExporter } from '@linuxdevil/lousho/traces'; +import { createAgent } from 'lousho'; +import { fileTraceExporter } from 'lousho/traces'; const agent = createAgent({ model: 'openai/gpt-4o-mini', @@ -48,8 +48,8 @@ reported once per exporter with `console.warn` and otherwise ignored. With the executor, pass the exporter to `AgentExecutor.execute()`: ```ts -import { AgentExecutor } from '@linuxdevil/lousho/executor'; -import { type TraceExporter } from '@linuxdevil/lousho'; +import { AgentExecutor } from 'lousho/executor'; +import { type TraceExporter } from 'lousho'; const exporter: TraceExporter = { onSpanStart: (span) => console.log('[start]', span.kind, span.name), @@ -60,7 +60,7 @@ await AgentExecutor.execute({ agent, input, provider, toolRegistry, exporter }); ``` For real OpenTelemetry spans, import `createOtelTraceExporter()` from -`@linuxdevil/lousho/otel` (needs the optional peer dependency +`lousho/otel` (needs the optional peer dependency `@opentelemetry/api` and a registered `TracerProvider`). It carries span kind and error status over to OpenTelemetry. See `examples/tracing` (`npm run example:tracing:console`, `npm run example:tracing:otel`, which also prints metrics). @@ -88,7 +88,7 @@ To put several runs in one trace, pass a span id of yours as `parentSpanId`; the run's `invoke_agent` span becomes its child, and its cost rolls up into it: ```ts -import { withSpan, type SimpleAgent, type TraceExporter } from '@linuxdevil/lousho'; +import { withSpan, type SimpleAgent, type TraceExporter } from 'lousho'; declare const exporter: TraceExporter; declare const planner: SimpleAgent; @@ -255,7 +255,7 @@ duration metric, only the model and tool calls inside them. ```ts import { metrics } from '@opentelemetry/api'; import { ConsoleMetricExporter, MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; -import { createOtelTraceExporter } from '@linuxdevil/lousho/otel'; +import { createOtelTraceExporter } from 'lousho/otel'; // Swap ConsoleMetricExporter for an OTLP exporter to reach any backend. metrics.setGlobalMeterProvider( @@ -325,7 +325,7 @@ piece of plain text (before JSON encoding) and returns what to record; if it throws, that text is recorded as `[redacted]` and the run carries on: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; createAgent({ model: 'openai/gpt-4o-mini', @@ -341,8 +341,8 @@ createAgent({ With the executor, opt in per run: ```ts -import { AgentExecutor } from '@linuxdevil/lousho/executor'; -import { type TraceExporter } from '@linuxdevil/lousho'; +import { AgentExecutor } from 'lousho/executor'; +import { type TraceExporter } from 'lousho'; declare const exporter: TraceExporter; @@ -398,13 +398,13 @@ governs only the `gen_ai.*` content attributes. ## Local traces -`fileTraceExporter()` from `@linuxdevil/lousho/traces` writes each run to a +`fileTraceExporter()` from `lousho/traces` writes each run to a file, and `lousho traces` shows the files in the terminal. No OpenTelemetry backend, no extra package. ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; -import { fileTraceExporter } from '@linuxdevil/lousho/traces'; +import { createAgent, defineTool } from 'lousho'; +import { fileTraceExporter } from 'lousho/traces'; import { z } from 'zod'; const getWeather = defineTool({ @@ -424,10 +424,10 @@ const agent = createAgent({ Then, from the project directory: ```bash -npx @linuxdevil/lousho traces # the 20 most recent runs, newest first -npx @linuxdevil/lousho traces --limit 50 --json -npx @linuxdevil/lousho traces 3f2a # one run as a tree; a unique id prefix is enough -npx @linuxdevil/lousho traces 3f2a --content # also the captured messages and tool arguments +npx lousho traces # the 20 most recent runs, newest first +npx lousho traces --limit 50 --json +npx lousho traces 3f2a # one run as a tree; a unique id prefix is enough +npx lousho traces 3f2a --content # also the captured messages and tool arguments ``` ``` @@ -519,7 +519,7 @@ check comes first. One SQLite file (default `~/.lousho/observe.db`, WAL mode) holds the fleet's observations, sessions, eval scores, prompt versions and shared context. -`npx @linuxdevil/studio` renders it (see [Lousho Studio](/agent-forge#screens)): +`npx lousho-studio` renders it (see [Lousho Studio](/agent-forge#screens)): Traces and Trace (trace trees with latency waterfalls), Threads, Monitoring (a live fleet monitor with heartbeat-derived status — a crashed agent's open sessions flip to `interrupted` — over per-agent charts of runs, p95 latency, diff --git a/openapi-tools.mdx b/openapi-tools.mdx index 854e9cf..032aa2a 100644 --- a/openapi-tools.mdx +++ b/openapi-tools.mdx @@ -9,15 +9,15 @@ the API without a hand-written `defineTool()` per endpoint and without the raw [`http` tool](/tools#built-in-tools). Operations that change data ask for [approval](/approvals) by default. -`openApiTools` is exported from `@linuxdevil/lousho` and -`@linuxdevil/lousho/tools`. It needs no extra package. The `vendor/` model +`openApiTools` is exported from `lousho` and +`lousho/tools`. It needs no extra package. The `vendor/` model prefix chooses the provider; with OpenRouter use `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ## Quick start ```ts -import { createAgent, openApiTools } from '@linuxdevil/lousho'; +import { createAgent, openApiTools } from 'lousho'; const tools = await openApiTools('https://api.example.com/openapi.json', { include: ['getOrder', 'refundOrder'], @@ -78,7 +78,7 @@ destructiveHint: false` on GET, HEAD and OPTIONS; `readOnlyHint: false` on the rest, with `destructiveHint: true` for DELETE. Anything that reads annotations treats these tools the way it treats MCP tools. ```ts -import { openApiTools } from '@linuxdevil/lousho'; +import { openApiTools } from 'lousho'; declare const spec: object; @@ -96,7 +96,7 @@ never sees it) and added to the request. The value can be a function, called per request with the operation and the tool call id. ```ts -import { openApiTools } from '@linuxdevil/lousho'; +import { openApiTools } from 'lousho'; declare const spec: object; declare const currentTenant: () => string; @@ -167,7 +167,7 @@ An API with hundreds of operations is hundreds of tools. Give the agent the ones it needs with `include` or `exclude`, or with a predicate on `op.tags`: ```ts -import { openApiTools } from '@linuxdevil/lousho'; +import { openApiTools } from 'lousho'; declare const spec: object; diff --git a/pause-and-resume.mdx b/pause-and-resume.mdx index b838bec..3a74da3 100644 --- a/pause-and-resume.mdx +++ b/pause-and-resume.mdx @@ -13,7 +13,7 @@ section links to the page that has the full detail. Set `needsApproval` on the tool. The run pauses before the tool executes: ```ts -import { defineTool } from '@linuxdevil/lousho'; +import { defineTool } from 'lousho'; import { z } from 'zod'; export const refund = defineTool({ @@ -41,9 +41,9 @@ shared by several processes on one host) or `fileStore(dir)` (plain JSON files). `finishReason: 'awaiting-approval'` and the id to decide: ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; -import { SqliteStore } from '@linuxdevil/lousho/sqlite'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool } from 'lousho'; +import { SqliteStore } from 'lousho/sqlite'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const refund = defineTool({ @@ -78,9 +78,9 @@ run left in the store. `agent.approvals.resolve()` runs the call (or rejects it) continues the run, and resolves with the result: ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; -import { SqliteStore } from '@linuxdevil/lousho/sqlite'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool } from 'lousho'; +import { SqliteStore } from 'lousho/sqlite'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const refund = defineTool({ @@ -135,8 +135,8 @@ This snippet stands in for the crash with a tool that throws step), then recovers: ```ts -import { createAgent, defineTool, memoryStore, PropagatingToolError } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool, memoryStore, PropagatingToolError } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; let charges = 0; diff --git a/permission-modes.mdx b/permission-modes.mdx index 0861132..c04e061 100644 --- a/permission-modes.mdx +++ b/permission-modes.mdx @@ -9,8 +9,8 @@ pause (`dontAsk`). Switch it in the middle of a session, the way you would work with a coding agent: plan first, then let it edit. ```ts -import { createAgent, createFsTools, MemoryWorkspace } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, createFsTools, MemoryWorkspace } from 'lousho'; +import { mockModel } from 'lousho/testing'; const workspace = new MemoryWorkspace({ files: { 'notes.md': '# Notes\n' } }); const agent = createAgent({ @@ -76,7 +76,7 @@ says nothing is treated as one with side effects and is refused. Declare a custom read-only tool like this: ```ts -import { defineTool } from '@linuxdevil/lousho'; +import { defineTool } from 'lousho'; import { z } from 'zod'; const lookUpOrder = defineTool({ @@ -98,7 +98,7 @@ The hint is a promise you make about your tool: plan mode trusts it. alone, so a tool of your own named `write_file` is not approved silently. ```ts -import { defineTool } from '@linuxdevil/lousho'; +import { defineTool } from 'lousho'; import { z } from 'zod'; const appendLine = defineTool({ @@ -143,7 +143,7 @@ Switching is always an explicit call to `setPermissionMode()`. Each switch is reported to `onPermissionModeChange` with `{ sessionId, from, to, at }`: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ provider, diff --git a/prompting-techniques.mdx b/prompting-techniques.mdx index d20adb1..9f454de 100644 --- a/prompting-techniques.mdx +++ b/prompting-techniques.mdx @@ -62,8 +62,8 @@ The four operations, and where each lives in the SDK: | **Isolate** | Run work in a clean context so detail doesn't flood the lead | Sub-agents (`task` calls see only their prompt), code mode (`run_code` returns one value), the `inputFilter` boundary | ```ts -import { createAgent, loadSkills } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, loadSkills } from 'lousho'; +import { mockModel } from 'lousho/testing'; const provider = mockModel([{ text: 'done' }]); const researchAgent = createAgent({ provider, instructions: 'You research one slice.' }); diff --git a/providers.mdx b/providers.mdx index b05ba95..02b27ab 100644 --- a/providers.mdx +++ b/providers.mdx @@ -12,7 +12,7 @@ OpenRouter use `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ```ts -import { resolveProvider, LLMProviderRegistry } from '@linuxdevil/lousho'; +import { resolveProvider, LLMProviderRegistry } from 'lousho'; // The convenient way — reads the credential from the environment const openai = resolveProvider('openai/gpt-4o-mini'); // OPENAI_API_KEY @@ -42,7 +42,7 @@ the vendor's package (`^1.0.0` with `ai` 4, `^3.0.0` with `ai` 6, `^4.0.0` with (`MISTRAL_API_KEY`, `GROQ_API_KEY`, the AWS chain for Bedrock, and so on). ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; createAgent({ model: 'mistral/mistral-large-latest' }); // @ai-sdk/mistral createAgent({ model: 'bedrock/anthropic.claude-sonnet' }); // @ai-sdk/amazon-bedrock @@ -92,7 +92,7 @@ it anyway: present. With none set it throws an error listing the fixes. ```ts -import { createAgent, createMockProvider, resolveProvider } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider, resolveProvider } from 'lousho'; const fromString = createAgent({ model: 'anthropic/claude-sonnet-5-5' }); const fromInstance = createAgent({ provider: resolveProvider('openai/gpt-4o-mini'), model: 'gpt-4o' }); @@ -110,7 +110,7 @@ error, 5xx) twice by default, and with `fallbackModels` moves on to the next model when the call still fails: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', @@ -153,7 +153,7 @@ Parts sent through `createAgent()`: ```ts import { readFileSync } from 'node:fs'; -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ instructions: 'Describe images briefly.', model: 'openai/gpt-4o-mini' }); @@ -169,7 +169,7 @@ console.log(answer.text); A `Message[]` is passed through as it is; `textOf()` returns the text parts of a message: ```ts -import { createAgent, textOf, type Message } from '@linuxdevil/lousho'; +import { createAgent, textOf, type Message } from 'lousho'; const agent = createAgent({ instructions: 'Describe images briefly.', model: 'openai/gpt-4o-mini' }); const messages: Message[] = [ @@ -228,8 +228,8 @@ console.log(textOf(messages[0]), '->', result.text); `AgentExecutor.execute()` takes the same messages as its `input` (see [the executor API](/executor-api)): ```ts -import { AgentBuilder, AgentExecutor } from '@linuxdevil/lousho/executor'; -import { resolveProvider, type Message } from '@linuxdevil/lousho'; +import { AgentBuilder, AgentExecutor } from 'lousho/executor'; +import { resolveProvider, type Message } from 'lousho'; const agent = AgentBuilder.create().setName('vision').setPrompt('Describe images briefly.').build(); const input: Message[] = [ @@ -291,7 +291,7 @@ text is not retried, so it fails after one `timeout`. To fail sooner, lower `timeout` or `retry.maxRetries`. ```ts -import { OpenAIProvider } from '@linuxdevil/lousho'; +import { OpenAIProvider } from 'lousho'; const provider = new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY!, timeout: 60_000 }); ``` @@ -305,7 +305,7 @@ Pass `store: true` to have OpenAI store them (for its dashboard logs or evals). `store` is ignored with `api: 'chat'`. ```ts -import { OpenAIProvider } from '@linuxdevil/lousho'; +import { OpenAIProvider } from 'lousho'; const provider = new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY!, store: true }); ``` @@ -322,7 +322,7 @@ instead. Install `undici` and pass its `Agent` as the dispatcher: ```ts import { Agent } from 'undici'; -import { createAgent, OpenAIProvider } from '@linuxdevil/lousho'; +import { createAgent, OpenAIProvider } from 'lousho'; // Wait up to 15 minutes for the response headers and between body chunks. const dispatcher = new Agent({ headersTimeout: 15 * 60_000, bodyTimeout: 15 * 60_000 }); @@ -350,7 +350,7 @@ your function, so a failure you return from it is classified as it would be from the real network: ```ts -import { OpenRouterProvider } from '@linuxdevil/lousho'; +import { OpenRouterProvider } from 'lousho'; let failures = 1; const provider = new OpenRouterProvider({ @@ -368,7 +368,7 @@ into an `LLMProvider`, so you do not write an adapter: ```ts import { createOpenAI } from '@ai-sdk/openai'; -import { createAgent, fromAiSdk } from '@linuxdevil/lousho'; +import { createAgent, fromAiSdk } from 'lousho'; const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY }); const agent = createAgent({ @@ -419,7 +419,7 @@ npm install @earendil-works/pi-ai@^1.0.3 ``` ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; // OpenRouter's `openai/gpt-4o-mini`, through pi's openrouter adapter: const agent = createAgent({ model: 'pi/openrouter/openai/gpt-4o-mini' }); @@ -465,7 +465,7 @@ wire format. Point `OpenAIProvider` at the server's `/v1` URL and pick the API it implements with `api`: ```ts -import { createAgent, OpenAIProvider } from '@linuxdevil/lousho'; +import { createAgent, OpenAIProvider } from 'lousho'; const agent = createAgent({ provider: new OpenAIProvider({ @@ -518,14 +518,14 @@ Ollama's `/v1` need). ```ts import { createOpenAI } from '@ai-sdk/openai'; -import { createAgent, fromAiSdk } from '@linuxdevil/lousho'; +import { createAgent, fromAiSdk } from 'lousho'; const local = createOpenAI({ baseURL: 'http://localhost:8080/v1', apiKey: 'any' }); const agent = createAgent({ provider: fromAiSdk(local.chat('qwen3-8b')), instructions: 'You are a helpful assistant.' }); ``` -`npx @linuxdevil/lousho doctor` shows `OPENAI_BASE_URL` when it is set, and -`npx @linuxdevil/lousho doctor --ping` calls `GET /models` on each configured +`npx lousho doctor` shows `OPENAI_BASE_URL` when it is set, and +`npx lousho doctor --ping` calls `GET /models` on each configured provider to check that the server answers. ## Vercel AI SDK versions diff --git a/queue-and-steer.mdx b/queue-and-steer.mdx index 8597d6e..a96a453 100644 --- a/queue-and-steer.mdx +++ b/queue-and-steer.mdx @@ -19,7 +19,7 @@ reply gets one more step, so the model answers it in the same run. The `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini' }); const run = agent.stream('Plan a weekend in Rome.'); @@ -66,8 +66,8 @@ Without a stream, pass an `InputQueue` to `AgentExecutor.execute()` (see [the executor API](/executor-api)): ```ts -import { AgentExecutor } from '@linuxdevil/lousho/executor'; -import { InputQueue } from '@linuxdevil/lousho'; +import { AgentExecutor } from 'lousho/executor'; +import { InputQueue } from 'lousho'; const inputQueue = new InputQueue(); const pending = AgentExecutor.execute({ agent, provider, input: 'Plan my trip.', inputQueue }); @@ -94,7 +94,7 @@ the user changes their mind while the agent is still thinking: - `applied` is `false` once the run has finished: send the input as a new turn. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini' }); const run = agent.stream('Plan a weekend in Rome.'); diff --git a/quickstart.mdx b/quickstart.mdx index d5bf697..59eb3a4 100644 --- a/quickstart.mdx +++ b/quickstart.mdx @@ -21,19 +21,19 @@ npm test # offline tests: no API key needed To try a locally packed `create-lousho` (`npm pack`), pass the tarball's absolute path with `--package`: `npx --package=/abs/path/create-lousho-x.y.z.tgz create-lousho my-agent`. A bare `npx ../x.tgz` fails on Windows. -`npx @linuxdevil/lousho init my-agent` is equivalent (pass `--provider` +`npx lousho init my-agent` is equivalent (pass `--provider` and `--template`, since it runs without the interactive prompts). Inside a -project that already has the SDK installed, `npx @linuxdevil/lousho init` works too: the -`lousho` command comes from the SDK package, so a bare `npx @linuxdevil/lousho` outside such +project that already has the SDK installed, `npx lousho init` works too: the +`lousho` command comes from the SDK package, so a bare `npx lousho` outside such a project finds nothing to run. Without arguments, `init` asks for the -directory, provider and template; `npx @linuxdevil/lousho init --help` +directory, provider and template; `npx lousho init --help` lists every flag. Prefer to add the SDK to an existing project? Install it by hand (see [Installation](/installation)): ```bash -npm install @linuxdevil/lousho ai@^7.0.0 zod +npm install lousho ai@^7.0.0 zod npm install @ai-sdk/openai@^4.0.0 @ai-sdk/anthropic@^4.0.0 ``` @@ -50,7 +50,7 @@ providers). The `vendor/` model prefix chooses the provider; with OpenRouter use `openrouter//` (e.g. `openrouter/openai/gpt-4.1-mini`). ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4.1-mini', instructions: 'You are a helpful assistant.' }); const { text } = await agent.send('Hello!'); @@ -73,7 +73,7 @@ whenever the user message mentions a tool's name, so this snippet exercises a real tool round trip without an LLM. ```ts -import { createAgent, createMockProvider, defineTool } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider, defineTool } from 'lousho'; import { z } from 'zod'; const currentDate = defineTool({ @@ -106,7 +106,7 @@ it for typed events (model text as it arrives, tool calls, step boundaries), and `await run.result` for the same result `send()` returns. ```ts -import { createAgent, createMockProvider } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider } from 'lousho'; const agent = createAgent({ instructions: 'You are a helpful assistant.', @@ -132,7 +132,7 @@ sending the events to a browser. keeps the transcript and passes it to the model on each `send()`. ```ts -import { createAgent, createMockProvider } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider } from 'lousho'; const agent = createAgent({ instructions: 'You are a helpful assistant.', @@ -154,12 +154,12 @@ Pass a store to keep sessions across restarts; see [Sessions](/sessions). Flag a tool `needsApproval` and the run pauses before calling it. `send()` resolves with `finishReason: 'awaiting-approval'` and an `approvalId`; your code (or a human) then approves or rejects the call and the run continues. Here -`mockModel` from `@linuxdevil/lousho/testing` scripts the model's turns +`mockModel` from `lousho/testing` scripts the model's turns exactly, so the tool call is certain. ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const sendEmail = defineTool({ @@ -197,8 +197,8 @@ request the agent made, so you can assert on what the model was sent as well as on the result. No network, no key. ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; let sent: string | undefined; @@ -245,7 +245,7 @@ the mock provider otherwise - the same pattern the runnable [examples](https://github.com/LinuxDevil/lousho/blob/main/examples/README.md) use. ```ts -import { createAgent, createMockProvider, resolveProvider } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider, resolveProvider } from 'lousho'; const provider = process.env.OPENAI_API_KEY ? resolveProvider('openai/gpt-4.1-mini') @@ -268,7 +268,7 @@ into a live agent with `specToAgent()`. The same shape can be written as a YAML or JSON file and loaded with `loadSpec()`. ```ts -import { specToAgent, agentSpecSchema } from '@linuxdevil/lousho'; +import { specToAgent, agentSpecSchema } from 'lousho'; const spec = agentSpecSchema.parse({ name: 'support-bot', @@ -298,8 +298,8 @@ the same spec runs in the local dev server (chat UI at `/`, `POST /chat`, hot reload on save) and builds into a deployable server: ```bash -npx @linuxdevil/lousho dev agent.yaml -npx @linuxdevil/lousho build --target=node-server --agent=agent.yaml +npx lousho dev agent.yaml +npx lousho build --target=node-server --agent=agent.yaml ``` ## Advanced: the executor API diff --git a/react.mdx b/react.mdx index f43534c..ef7e434 100644 --- a/react.mdx +++ b/react.mdx @@ -8,11 +8,11 @@ title: "React" render-ready state: messages with their text and tool calls, a `status` for the composer, and the tool call waiting for approval, if any. -It lives in the `@linuxdevil/lousho/react` subpath. `react` (18 or 19) is +It lives in the `lousho/react` subpath. `react` (18 or 19) is an optional peer dependency: install it in the app that uses the hook. ```tsx -import { useLoushoAgent } from '@linuxdevil/lousho/react'; +import { useLoushoAgent } from 'lousho/react'; export function Chat() { const agent = useLoushoAgent({ url: '/api/agent' }); @@ -123,8 +123,8 @@ model prefix chooses the provider; with OpenRouter use ```ts import { createServer, type IncomingMessage } from 'node:http'; -import { createAgent } from '@linuxdevil/lousho'; -import type { ApprovalOutcome } from '@linuxdevil/lousho/react'; +import { createAgent } from 'lousho'; +import type { ApprovalOutcome } from 'lousho/react'; const agent = createAgent({ model: 'openai/gpt-4o-mini' }); @@ -173,8 +173,8 @@ gets a fresh in-memory transcript and the next turn forgets the first (see The hook takes the agent itself, too. Called from a custom hook: ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { useLoushoAgent } from '@linuxdevil/lousho/react'; +import { createAgent } from 'lousho'; +import { useLoushoAgent } from 'lousho/react'; const agent = createAgent({ model: 'openai/gpt-4o-mini' }); @@ -194,7 +194,7 @@ event)`, a pure reducer over `AgentEvent`s and a few local actions exported from the same subpath, for a custom binding or a non-React client: ```ts -import { initialAgentUIState, parseEventStream, reduceAgentEvents } from '@linuxdevil/lousho/react'; +import { initialAgentUIState, parseEventStream, reduceAgentEvents } from 'lousho/react'; let state = reduceAgentEvents(initialAgentUIState, { type: 'ui.send', input: 'Weather in Paris?' }); const response = await fetch('http://localhost:3000/api/agent', { @@ -222,7 +222,7 @@ chat and the plan. The list carries across turns and is cleared by nothing but a new hook (it is not restored when the page reconnects to an existing session). ```tsx -import { useLoushoAgent, useTodos } from '@linuxdevil/lousho/react'; +import { useLoushoAgent, useTodos } from 'lousho/react'; export function Plan() { const agent = useLoushoAgent({ url: '/api/agent' }); diff --git a/reasoning.mdx b/reasoning.mdx index d62b9f9..8ecc50b 100644 --- a/reasoning.mdx +++ b/reasoning.mdx @@ -10,7 +10,7 @@ as `reasoning.*` events in `agent.stream()`, and as `result.reasoning`. The `openrouter//` (e.g. `openrouter/anthropic/claude-sonnet-4.5`). ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'anthropic/claude-sonnet-4-5', diff --git a/registry.mdx b/registry.mdx index 885591a..78ab1f4 100644 --- a/registry.mdx +++ b/registry.mdx @@ -91,7 +91,7 @@ Each item document is: "files": [ { "path": "tools/web-search.ts", - "content": "import { defineTool } from '@linuxdevil/lousho';\nimport { z } from 'zod';\n\nexport default defineTool({\n name: 'web-search',\n description: 'Search the web',\n input: z.object({ query: z.string() }),\n needsApproval: true,\n execute: async ({ query }) => {\n const response = await fetch(`https://api.example.com/search?q=${encodeURIComponent(query)}`, {\n headers: { authorization: `Bearer ${process.env.SEARCH_KEY}` },\n });\n return response.json();\n },\n});\n" + "content": "import { defineTool } from 'lousho';\nimport { z } from 'zod';\n\nexport default defineTool({\n name: 'web-search',\n description: 'Search the web',\n input: z.object({ query: z.string() }),\n needsApproval: true,\n execute: async ({ query }) => {\n const response = await fetch(`https://api.example.com/search?q=${encodeURIComponent(query)}`, {\n headers: { authorization: `Bearer ${process.env.SEARCH_KEY}` },\n });\n return response.json();\n },\n});\n" } ], "permissions": { diff --git a/runs.mdx b/runs.mdx index ddf68a7..e8c733b 100644 --- a/runs.mdx +++ b/runs.mdx @@ -28,7 +28,7 @@ same reason is on `run.done` in [streaming](/streaming) (and on `finish`, the deprecated `onEvent` counterpart). ```ts -import { createAgent, createMockProvider } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider } from 'lousho'; const agent = createAgent({ instructions: 'You are helpful.', provider: createMockProvider(), maxSteps: 3 }); const result = await agent.send('Research this thoroughly'); if (result.finishReason === 'max-steps') console.warn(`Gave up after ${result.steps} steps`); @@ -41,7 +41,7 @@ Pass an `AbortSignal` to stop a run: `agent.send(input, { signal })`, `resumeAfterApproval(..., { signal })`. ```ts -import { createAgent, createMockProvider } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider } from 'lousho'; const agent = createAgent({ instructions: 'You are helpful.', provider: createMockProvider() }); const controller = new AbortController(); setTimeout(() => controller.abort(), 5_000); // e.g. from a Stop button @@ -52,7 +52,7 @@ console.log(result.finishReason); // 'aborted' if it was cancelled, else 'stop' For a time limit, use `AbortSignal.timeout(ms)`: ```ts -import { createAgent, createMockProvider } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider } from 'lousho'; const agent = createAgent({ instructions: 'You are helpful.', provider: createMockProvider() }); const result = await agent.send('Summarize this', { signal: AbortSignal.timeout(30_000) }); ``` @@ -104,7 +104,7 @@ for strictly sequential execution, e.g. when your tools share state that is not safe to touch concurrently. ```ts -import { createAgent, createMockProvider, defineTool } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider, defineTool } from 'lousho'; import { z } from 'zod'; const getWeather = defineTool({ diff --git a/schedules.mdx b/schedules.mdx index 996a9c3..34bf839 100644 --- a/schedules.mdx +++ b/schedules.mdx @@ -15,7 +15,7 @@ Give a cron expression and exactly one of `prompt` (text sent to the agent as a new turn) or `run` (your own function). ```ts -import { createAgent, createMockProvider, defineSchedule, startSchedules } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider, defineSchedule, startSchedules } from 'lousho'; const agent = createAgent({ instructions: 'You write reports.', provider: createMockProvider() }); @@ -61,7 +61,7 @@ server or a test exits normally. A script that does nothing but run schedules would therefore exit straight away; pass `keepAlive: true` for that: ```ts -import { createAgent, createMockProvider, defineSchedule, startSchedules } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider, defineSchedule, startSchedules } from 'lousho'; const agent = createAgent({ instructions: 'You write reports.', provider: createMockProvider() }); const running = startSchedules(agent, [defineSchedule({ cron: '@hourly', prompt: 'Check the queue.' })], { keepAlive: true }); @@ -87,7 +87,7 @@ claim is best effort across edge locations (see [Durable execution](/durable-execution#run-leases)). ```ts -import { createAgent, createMockProvider, defineSchedule, fireSchedule } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider, defineSchedule, fireSchedule } from 'lousho'; const agent = createAgent({ instructions: 'You write reports.', provider: createMockProvider() }); const report = defineSchedule({ name: 'report', cron: '0 9 * * *', prompt: 'Run the daily report.' }); @@ -108,7 +108,7 @@ they show an empty transcript for a schedule id. Read a fire's report from the checkpoint store instead, or continue it with `agent.fork(id)`: ```ts -import { createAgent, createMockProvider, defineSchedule, fireSchedule, memoryStore } from '@linuxdevil/lousho'; +import { createAgent, createMockProvider, defineSchedule, fireSchedule, memoryStore } from 'lousho'; const store = memoryStore(); const agent = createAgent({ instructions: 'You write reports.', provider: createMockProvider(), store }); @@ -134,7 +134,7 @@ my-agent/ `schedules/` loads exactly as before. ```ts -import { createAgent, resolveAgentDir, startSchedules } from '@linuxdevil/lousho'; +import { createAgent, resolveAgentDir, startSchedules } from 'lousho'; const { config, schedules } = await resolveAgentDir('./my-agent'); const agent = createAgent(config); diff --git a/self-healing.mdx b/self-healing.mdx index 74475bd..87ce134 100644 --- a/self-healing.mdx +++ b/self-healing.mdx @@ -26,8 +26,8 @@ each interrupted run from its last checkpoint, so completed steps don't run again. ```ts -import { createAgent, createProgressGuard } from '@linuxdevil/lousho'; -import { SqliteStore } from '@linuxdevil/lousho/sqlite'; +import { createAgent, createProgressGuard } from 'lousho'; +import { SqliteStore } from 'lousho/sqlite'; const agent = createAgent({ provider, @@ -91,7 +91,7 @@ recorded as not retryable, so `agent.recover()` won't resume the run. Each hint, denial and stop is streamed as a `heal.action` event: ```ts -import { createAgent, createProgressGuard } from '@linuxdevil/lousho'; +import { createAgent, createProgressGuard } from 'lousho'; const agent = createAgent({ provider, hooks: [createProgressGuard()] }); @@ -173,8 +173,8 @@ better instructions and skills using failing cases you give it, and returns a proposal without writing anything. ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { formatImproveResult, improveAgent, renderTranscript } from '@linuxdevil/lousho/improve'; +import { createAgent } from 'lousho'; +import { formatImproveResult, improveAgent, renderTranscript } from 'lousho/improve'; const result = await improveAgent({ harness: { instructions: 'You answer from the docs.', skills: {} }, diff --git a/sessions.mdx b/sessions.mdx index 07015a6..8bb38a8 100644 --- a/sessions.mdx +++ b/sessions.mdx @@ -12,7 +12,7 @@ See also: [Pause, restart, resume](/pause-and-resume), a short end-to-end tutori > Snippets on this page leave some names to you: `provider` is any `LLMProvider` (offline: `createMockProvider()`). ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const chat = createAgent({ instructions: 'Be brief.', provider }); @@ -74,7 +74,7 @@ The `vendor/` model prefix chooses the provider; with OpenRouter use `openrouter//` (e.g. `openrouter/openai/gpt-4o-mini`). ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini' }); const session = agent.session({ id: 'user-42', turnPolicy: 'queue' }); @@ -126,7 +126,7 @@ returns the same `AgentRun` (typed events plus a `result` promise, see it. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const chat = createAgent({ instructions: 'Be brief.', provider }); const session = chat.session(); @@ -195,8 +195,8 @@ crash, a failed checkpoint write or a `PropagatingToolError` can then be finished later, in another process: ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { SqliteStore } from '@linuxdevil/lousho/sqlite'; +import { createAgent } from 'lousho'; +import { SqliteStore } from 'lousho/sqlite'; const agent = createAgent({ provider, store: new SqliteStore('./.lousho/agent.db') }); @@ -289,7 +289,7 @@ console.log(finished?.text, await agent.session({ id: 'user-42' }).pending()); / A `SessionStore` keeps transcripts between calls: ```ts -import type { Message } from '@linuxdevil/lousho'; +import type { Message } from 'lousho'; interface SessionStore { load(id: string): Promise; @@ -320,7 +320,7 @@ writer in another process can still slip between, losing one turn. files on Windows and macOS too. ```ts -import { createAgent, FileSessionStore } from '@linuxdevil/lousho'; +import { createAgent, FileSessionStore } from 'lousho'; const agent = createAgent({ provider }); const store = new FileSessionStore('./.lousho/sessions'); @@ -353,7 +353,7 @@ implementation by where the process runs: | In memory (`memoryStore()`) | `MemorySessionStore` | in memory | `InMemoryApprovalStore` | Tests, scripts, one process that never restarts | | Files (`fileStore(dir)`) | `/sessions/.json` | `/checkpoints/`, `/checkpoint-history/` | `/approvals/.json` | One machine, you want plain inspectable files | | SQLite | `store.sessions` | `store.checkpoints` | `store.approvals` | A Node server: one durable, transactional file, shared by several processes on one host (see [Sharing a SQLite file](#sharing-a-sqlite-file)) | -| Cloudflare KV (`KVStore` from `@linuxdevil/lousho/kv`) | `store.sessions` | `store.checkpoints` | `store.approvals` | Workers deployments (see [Deployment](/deployment)) | +| Cloudflare KV (`KVStore` from `lousho/kv`) | `store.sessions` | `store.checkpoints` | `store.approvals` | Workers deployments (see [Deployment](/deployment)) | Each one is an `AgentStore` part: pass them together as `createAgent({ store: { sessions, checkpoints, approvals } })`. `memoryStore()`, @@ -367,7 +367,7 @@ another writer or reader has open fails for a moment with `EPERM`, `EACCES` or file still end with one of them (the last rename wins): ```ts -import { createAgent, fileStore } from '@linuxdevil/lousho'; +import { createAgent, fileStore } from 'lousho'; const agent = createAgent({ provider, store: fileStore('./.lousho') }); // ./.lousho/sessions/user-42.json, ./.lousho/checkpoints/..., ./.lousho/approvals/... @@ -404,7 +404,7 @@ session's transcript), and so are pending approvals and OAuth tokens. It resolves with the counts, `{ sessions, checkpoints, checkpointHistory, oauthPending }`: ```ts -import { fileStore } from '@linuxdevil/lousho'; +import { fileStore } from 'lousho'; const store = fileStore('./.lousho'); await store.prune({ olderThanMs: 30 * 24 * 60 * 60 * 1000 }); // a month @@ -419,8 +419,8 @@ generated Worker uses `KVCheckpointStore`, see [Deployment](/deployment)). re-exported from the root entry, so importing the SDK never loads it): ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { SqliteStore } from '@linuxdevil/lousho/sqlite'; +import { createAgent } from 'lousho'; +import { SqliteStore } from 'lousho/sqlite'; const store = new SqliteStore('./.lousho/agent.db'); // or ':memory:' const agent = createAgent({ provider, store }); // transcripts, per-step checkpoints and approvals @@ -485,8 +485,8 @@ starts a new session from any of them, so you can try another direction without losing the original conversation: ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent, defineTool } from 'lousho'; +import { mockModel } from 'lousho/testing'; import { z } from 'zod'; const searchFlights = defineTool({ diff --git a/skills.mdx b/skills.mdx index aefb343..101dad3 100644 --- a/skills.mdx +++ b/skills.mdx @@ -25,7 +25,7 @@ can correct itself. ## Skills in code ```ts -import { createAgent, defineSkill } from '@linuxdevil/lousho'; +import { createAgent, defineSkill } from 'lousho'; const agent = createAgent({ instructions: 'You are a release engineer.', @@ -94,7 +94,7 @@ Use the imperative mood and link the PR. (both paths are listed). ```ts -import { createAgent, loadSkills } from '@linuxdevil/lousho'; +import { createAgent, loadSkills } from 'lousho'; const agent = createAgent({ instructions: 'You are a release engineer.', diff --git a/stream-events.mdx b/stream-events.mdx index d41ac25..d7ad672 100644 --- a/stream-events.mdx +++ b/stream-events.mdx @@ -127,7 +127,7 @@ payload. Each event type is exported too (`TextDeltaEvent`, `ToolDoneEvent`, `RunDoneEvent`, ...), and `AgentEventOf<'tool.done'>` picks one by name. ```ts -import { isAgentEvent, isToolEvent, type AgentEvent } from '@linuxdevil/lousho'; +import { isAgentEvent, isToolEvent, type AgentEvent } from 'lousho'; function render(event: AgentEvent): string { switch (event.type) { diff --git a/streaming.mdx b/streaming.mdx index 6f2fe20..43d185e 100644 --- a/streaming.mdx +++ b/streaming.mdx @@ -14,7 +14,7 @@ provider; with OpenRouter use `openrouter//` (e.g. > Snippets on this page leave some names to you: `provider` is any `LLMProvider` (offline: `createMockProvider()`); `agent` is a `createAgent()` result; `input` is the user's text, a `string`; `approvalStore` is an `ApprovalStore`; `toolRegistry` is a `ToolRegistry`. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini' }); @@ -75,7 +75,7 @@ interface AgentRun extends AsyncIterable { throw synchronously from `stream()`. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini' }); @@ -102,7 +102,7 @@ Output guardrails still check only the final reply of a `send()`, as without a listener. Sub-agents' events arrive tagged with `subagent`. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', @@ -153,7 +153,7 @@ save, so the transcript is complete when the loop ends. An aborted or failed run, or one you stop reading early, is not saved. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini' }); const session = agent.session({ id: 'user-42' }); @@ -181,7 +181,7 @@ process, since everything is read from the approval store. A model chosen per run (`model` as a function) is the one the paused run used. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ provider /* , a tool with needsApproval */ }); const paused = await agent.send('Delete the old reports.'); @@ -200,7 +200,7 @@ of `resumeAfterApproval()` (see [the executor API](/executor-api)) and returns the same `AgentRun`. ```ts -import { AgentExecutor, streamResumeAfterApproval } from '@linuxdevil/lousho/executor'; +import { AgentExecutor, streamResumeAfterApproval } from 'lousho/executor'; const paused = await AgentExecutor.execute({ agent, input, provider, toolRegistry, approvalStore }); const run = streamResumeAfterApproval({ id: paused.approvalId!, approved: true }, approvalStore, toolRegistry, provider); @@ -234,7 +234,7 @@ for approval is reported once, by the top-level `approval.requested` (which carries the sub-agent's call). ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const researcher = createAgent({ provider, instructions: 'You research.', description: 'Finds sources' }); const lead = createAgent({ provider, instructions: 'You coordinate.', subagents: { researcher } }); @@ -275,7 +275,7 @@ step was running) and `run.done` (`'aborted'`), and `result` resolves with `finishReason: 'aborted'`. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini' }); const run = agent.stream('Write a long story'); @@ -293,7 +293,7 @@ To add input to a run that is still going, or to redirect it, see [Queued input ## Example: terminal ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; +import { createAgent, defineTool } from 'lousho'; import { z } from 'zod'; const getWeather = defineTool({ @@ -332,7 +332,7 @@ saved even when the client leaves (see [Next.js](/nextjs#routes)). ```ts import { createServer } from 'node:http'; -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini' }); @@ -375,7 +375,7 @@ with `{ sessionId, input }` streams `agent.session({ id }).stream(input)` as needs approval ends the stream with `approval.requested`: ```ts -import { AgentExecutor } from '@linuxdevil/lousho/executor'; +import { AgentExecutor } from 'lousho/executor'; const run = AgentExecutor.stream({ agent, input, provider, toolRegistry, approvalStore }); for await (const event of run) { diff --git a/structured-output.mdx b/structured-output.mdx index e49f937..df2e743 100644 --- a/structured-output.mdx +++ b/structured-output.mdx @@ -11,7 +11,7 @@ the provider; with OpenRouter use `openrouter//` (e.g. ```ts import { z } from 'zod'; -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', @@ -39,7 +39,7 @@ itself) or any other Standard Schema that can produce JSON Schema: ```ts import { z } from 'zod/v4'; -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', output: z.object({ city: z.string() }) }); const { object } = await agent.send('Where is the Eiffel Tower?'); @@ -57,7 +57,7 @@ the tool take the data and keep `output` for a summary or status. ```ts import { z } from 'zod'; -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; declare function postComment(comment: { file: string; body: string }): Promise; @@ -80,7 +80,7 @@ from the data (a claim id), because a rerun of the whole flow extracts again: ```ts import { z } from 'zod'; -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; declare function askReviewer(claim: { claimId: string; amountCents: number }): Promise; declare const claims: { file(claim: { claimId: string; amountCents: number }, opts: { idempotencyKey: string }): Promise }; @@ -109,7 +109,7 @@ turn. There is no per-call `output` override: the schema is the agent's. ```ts import { z } from 'zod'; -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o-mini', output: z.object({ city: z.string(), tempC: z.number() }) }); const session = agent.session(); @@ -139,7 +139,7 @@ read from the `object` of the remote stream's `run.done` event. ```ts import { z } from 'zod'; -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const reporter = createAgent({ model: 'openai/gpt-4o-mini', @@ -164,7 +164,7 @@ and `result.object` has no key for it. A field that accepts `null` itself ```ts import { z } from 'zod'; -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openrouter/openai/gpt-4o-mini', @@ -244,7 +244,7 @@ here; set `'final'` to keep it off those calls): ```ts import { z } from 'zod'; -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const agent = createAgent({ model: 'openai/gpt-4o', @@ -281,7 +281,7 @@ where a failure is yours to report: ```ts import { z } from 'zod'; -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const Invoice = z.object({ subtotal: z.number(), tax: z.number(), total: z.number() }); const agent = createAgent({ model: 'openai/gpt-4o-mini', output: Invoice }); @@ -333,7 +333,7 @@ progress, and `result.object` for the answer. The UI bindings keep it as `partialObject`. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; import { z } from 'zod'; const planner = createAgent({ model: 'openai/gpt-4o-mini', output: z.object({ days: z.array(z.string()) }) }); @@ -350,8 +350,8 @@ schema, or use `createAgent()` for the inferred type. ```ts import { z } from 'zod'; -import { AgentBuilder, AgentExecutor } from '@linuxdevil/lousho/executor'; -import { createMockProvider } from '@linuxdevil/lousho'; +import { AgentBuilder, AgentExecutor } from 'lousho/executor'; +import { createMockProvider } from 'lousho'; const Ticket = z.object({ title: z.string(), priority: z.enum(['low', 'high']) }); const agent = AgentBuilder.create().setName('triage').setPrompt('You triage bug reports.').build(); @@ -365,8 +365,8 @@ With `mockModel`, script the JSON text the model would write: ```ts import { z } from 'zod'; -import { createAgent } from '@linuxdevil/lousho'; -import { mockModel } from '@linuxdevil/lousho/testing'; +import { createAgent } from 'lousho'; +import { mockModel } from 'lousho/testing'; const agent = createAgent({ provider: mockModel(['{"city":"Paris","tempC":21}']), output: z.object({ city: z.string(), tempC: z.number() }) }); const { object } = await agent.send('Weather in Paris?'); diff --git a/sub-agents.mdx b/sub-agents.mdx index c395d90..4373f33 100644 --- a/sub-agents.mdx +++ b/sub-agents.mdx @@ -24,7 +24,7 @@ Give each sub-agent a `description` (the lead model reads it to choose), then pass them to the lead as `subagents`: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const researcher = createAgent({ provider, @@ -112,8 +112,8 @@ needs no code. A lead that keeps its work across turns or restarts needs a store and a session: ```ts -import { createAgent } from '@linuxdevil/lousho'; -import { SqliteStore } from '@linuxdevil/lousho/sqlite'; +import { createAgent } from 'lousho'; +import { SqliteStore } from 'lousho/sqlite'; const store = new SqliteStore('./.lousho/agent.db'); const researcher = createAgent({ provider, instructions: 'You research.', description: 'Finds and summarizes sources' }); @@ -161,7 +161,7 @@ sometimes pass one sub-agent's `taskId` to another and waste a turn on the error; this removes the option. ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const analyst = createAgent({ provider, instructions: 'You analyze.', description: 'Analyzes data' }); const lead = createAgent({ provider, subagents: { analyst }, subagentOptions: { continuation: false } }); @@ -197,7 +197,7 @@ At most `maxConcurrent` background sub-agents of one lead run run at once as slots free. Set it with `subagentOptions` on `createAgent()`: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const researcher = createAgent({ provider, instructions: 'You research.', description: 'Finds and summarizes sources' }); @@ -238,7 +238,7 @@ events arrive before the lead's `run.done`. The result lists every background task of the run with its final status: ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const researcher = createAgent({ provider, instructions: 'You research.', description: 'Finds and summarizes sources' }); const lead = createAgent({ provider, instructions: 'You coordinate research.', subagents: { researcher } }); @@ -290,7 +290,7 @@ Limits: cannot be resumed: the model chose not to wait for it. ```ts -import { createAgent, defineTool } from '@linuxdevil/lousho'; +import { createAgent, defineTool } from 'lousho'; import { z } from 'zod'; const shell = defineTool({ @@ -321,7 +321,7 @@ the set can change between runs; `resolve()` is called when the lead picks a name (return `undefined` for an unknown one). ```ts -import { createAgent, type SubagentCatalog } from '@linuxdevil/lousho'; +import { createAgent, type SubagentCatalog } from 'lousho'; const experts = new Map([ ['researcher', createAgent({ provider, instructions: 'You research.' })], @@ -343,7 +343,7 @@ next to local ones, and the lead delegates to it with the same `task` tool, including `background: true`. ```ts -import { createAgent, remoteAgent } from '@linuxdevil/lousho'; +import { createAgent, remoteAgent } from 'lousho'; const lead = createAgent({ provider, @@ -407,7 +407,7 @@ final answer becomes the `task` result; a continuation that pauses again pauses the lead again. ```ts -import type { SimpleAgent } from '@linuxdevil/lousho'; +import type { SimpleAgent } from 'lousho'; declare const lead: SimpleAgent; // the lead agent above const paused = await lead.send('Deploy the docs site'); @@ -441,7 +441,7 @@ use the ready-made Pi adapter. Pi's tools (`read`, `bash`, `edit`, `write`, ...) work on a local directory. ```ts -import { createAgent, piAgent, ask, allow } from '@linuxdevil/lousho'; +import { createAgent, piAgent, ask, allow } from 'lousho'; import { ModelRuntime } from '@earendil-works/pi-coding-agent'; const runtime = await ModelRuntime.create(); // reads ~/.pi/agent auth, env keys @@ -532,7 +532,7 @@ the enclosing sub-agent as `parent` when nested deeper. A hook that should only see the top-level run returns early: ```ts -import { HookRegistry } from '@linuxdevil/lousho'; +import { HookRegistry } from 'lousho'; const hooks = new HookRegistry(); hooks.register({ @@ -568,7 +568,7 @@ sub-agents it runs inside (e.g. `['researcher']`). Approve or reject it with is needed): ```ts -import { createAgent } from '@linuxdevil/lousho'; +import { createAgent } from 'lousho'; const lead = createAgent({ provider, @@ -625,8 +625,8 @@ Resume with `resumeAfterApproval()`, passing the same `subagents` option as the paused run: ```ts -import { AgentExecutor, resumeAfterApproval, ToolRegistry } from '@linuxdevil/lousho/executor'; -import { createAgent } from '@linuxdevil/lousho'; +import { AgentExecutor, resumeAfterApproval, ToolRegistry } from 'lousho/executor'; +import { createAgent } from 'lousho'; const subagents = { researcher: createAgent({ provider, instructions: 'You research.', description: 'Researches' }) }; diff --git a/svelte.mdx b/svelte.mdx index 8942d3f..595fbd0 100644 --- a/svelte.mdx +++ b/svelte.mdx @@ -9,13 +9,13 @@ sources, the same state and the same actions, over the same field means, the two kinds of source, how approvals work and the server side; this page covers what differs. -It lives in the `@linuxdevil/lousho/svelte` subpath. The store implements +It lives in the `lousho/svelte` subpath. The store implements Svelte's store contract (`subscribe(run) => unsubscribe`) by hand, so it works with `$agent` in Svelte 4 and 5 and needs no `svelte` import: there is no peer dependency to install. ```ts -import { loushoAgent } from '@linuxdevil/lousho/svelte'; +import { loushoAgent } from 'lousho/svelte'; // In a component's