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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ coverage/
# Cache
.cache/
*.tsbuildinfo

# Generated API docs (npm run docs:build)
docs/api/
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ The SDK requires:

## Quick Start

> For a verified, copy-paste-runnable walkthrough of the current API
> (`createAgent()`, the static `AgentExecutor.execute()`, spec files), see
> [docs/quick-start.md](docs/quick-start.md).

### 1. Build an Agent

```typescript
Expand Down Expand Up @@ -114,6 +118,14 @@ for await (const chunk of stream) {
}
```

## Documentation

- [Installation](docs/installation.md) - requirements, peer/provider packages, installing from a local build
- [Quick Start](docs/quick-start.md) - runnable, verified snippets: `createAgent()`, tools, `AgentBuilder` + `AgentExecutor`, spec files
- [Configuration](docs/configuration.md) - agent spec files, provider credentials, execution options, CLI flags
- [Deployment](docs/deployment.md) - `loushy build` targets: Node server, Docker, Cloudflare Workers
- [API Overview](docs/api-overview.md) - the main exports; `npm run docs:build` generates the full TypeDoc reference

## Core Concepts

### Agents
Expand Down Expand Up @@ -305,6 +317,11 @@ const context = memory.getContext(); // Recent conversation

## Examples

Runnable example agents (support bot, research assistant, workflow router,
doc Q&A, Slack notifier, tracing) live in [examples/](examples/README.md) -
see [the examples index](examples/README.md) for what each one does and how
to run it.

### Example 1: Simple Chatbot

```typescript
Expand Down Expand Up @@ -406,7 +423,7 @@ const result = await executor.execute({ input: 'Product ideas' });
- **renderTemplate** - Template rendering
- **validateTokenQuotas** - Quota validation

For detailed API documentation, see the [TypeScript definitions](./dist/index.d.ts).
For detailed API documentation, see the [API Overview](docs/api-overview.md), or run `npm run docs:build` to generate the full TypeDoc reference in `docs/api/`.

## Architecture

Expand Down
36 changes: 28 additions & 8 deletions bin/loushy.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@

const path = require('node:path');

async function main() {
const [command, ...rest] = process.argv.slice(2);

if (command !== 'dev') {
console.error(`loushy: unknown command '${command || ''}'. Usage: loushy dev <config.yaml|config.json> [--port N]`);
process.exitCode = 1;
return;
}
const USAGE = [
'Usage:',
' loushy dev <config.yaml|config.json> [--port N] [--host H]',
' loushy build --target=<name> --agent=<path> [--out=<dir>]',
].join('\n');

async function runDev(rest) {
const configPath = rest.find((arg) => !arg.startsWith('--'));
const portFlag = rest.find((arg) => arg.startsWith('--port'));
const port = portFlag ? Number(portFlag.split('=')[1] || rest[rest.indexOf(portFlag) + 1]) : 3737;
Expand All @@ -35,4 +33,26 @@ async function main() {
}
}

async function runBuildCommand(rest) {
// Flag parsing (--target/--agent/--out) lives in src/cli/build.ts's
// parseBuildArgs(), which follows the same `--flag=value` / `--flag value`
// convention as runDev() above.
const { runBuild } = require(path.join(__dirname, '..', 'dist', 'cli', 'build.js'));
process.exitCode = await runBuild(rest);
}

async function main() {
const [command, ...rest] = process.argv.slice(2);

switch (command) {
case 'dev':
return runDev(rest);
case 'build':
return runBuildCommand(rest);
default:
console.error(`loushy: unknown command '${command || ''}'.\n${USAGE}`);
process.exitCode = 1;
}
}

main();
61 changes: 61 additions & 0 deletions docs/api-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# API Overview

The package root (`@loushy/build-ai-agent`) exports everything below. For
the complete, generated API reference (every export, signature and doc
comment), build the TypeDoc site:

```bash
npm run docs:build # writes docs/api/index.html
```

## Building and running agents

| Export | Description |
| ----------------------------- | --------------------------------------------------------------------------- |
| `createAgent(config)` | Zero-config `{ send(message) }` agent from a prompt + provider (+ tools). |
| `AgentBuilder` | Fluent builder for an `AgentConfig` (`AgentBuilder.create().setName(...)...build()`). |
| `AgentExecutor.execute(opts)` | Static executor: runs an agent (LLM + tool-calling loop) and resolves to an `ExecutionResult`. |
| `AgentType` | Agent type enum (e.g. `AgentType.SmartAssistant`). |
| `resumeAfterApproval()` | Resume an execution paused for human approval. |
| `createDelegateTool()` | Wrap a child agent as a tool for multi-agent delegation. |

## Declarative specs

| Export | Description |
| ------------------ | -------------------------------------------------------------- |
| `loadSpec(path)` | Load and validate a `.yaml`/`.yml`/`.json` `AgentSpec` file. |
| `agentSpecSchema` | The zod schema for `AgentSpec`. |
| `specToAgent(spec)` | Turn an `AgentSpec` into a live `createAgent()` agent. |

## Providers

| Export | Description |
| ------------------------------- | ------------------------------------------------------------ |
| `resolveProvider('p/model')` | Build a real provider from env-var credentials. |
| `LLMProviderRegistry` | Registry of provider factories (`create`, `register`, `has`). |
| `OpenAIProvider`, `AnthropicProvider`, `OllamaProvider`, `OpenRouterProvider` | Provider classes. |
| `createMockProvider()`, `MockLLMProvider` | Deterministic mock provider for tests and demos. |

## Tools

| Export | Description |
| -------------------------------------------- | --------------------------------------------- |
| `ToolRegistry` | Holds the tools an agent config refers to. |
| `httpTool`, `currentDateTool`, `dayNameTool` | Built-in tools. |

## Flows, evals, observability and security

- `FlowBuilder` / `FlowExecutor` - multi-step workflow graphs.
- `defineEval()`, scorers such as `exactMatch` and `toolCallOrder`, and
`llmJudge()` - agent evals run under vitest.
- `withSpan()` and `TraceExporter` - tracing for `AgentExecutor.execute()`.
- `NoopSandbox` / `SubprocessSandbox` - sandboxing for tools that opt in via
`requiresSandbox`; guardrails such as `createCommandGuardrail()` and
`secretScanGuardrail`.
- `EncryptionUtils`, `sha256`, `StorageService`, `renderTemplate`,
`MemoryManager` - supporting utilities.

## Deployment

- `DeploymentAdapter`, `registerAdapter()`, `getAdapter()`, `listAdapters()` -
the adapter registry behind `loushy build` (see [Deployment](./deployment.md)).
106 changes: 106 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Configuration

## Agent spec files (`AgentSpec`)

A declarative agent is a YAML (`.yaml`/`.yml`) or JSON (`.json`) file,
validated with zod by `loadSpec()` (`src/spec/schema.ts`). It is the format
`loushy dev` serves and `loushy build` deploys.

| Field | Type | Required | Description |
| ---------------- | ---------- | -------- | ------------------------------------------------------- |
| `name` | `string` | yes | Agent name. |
| `prompt` | `string` | yes | System prompt. |
| `provider.type` | `string` | yes | `openai`, `anthropic`, `ollama`, `openrouter` or `mock`. |
| `provider.model` | `string` | yes | Model id passed to the provider as its default model. |
| `tools` | `string[]` | no | Built-in tool names (see below). |

A missing or invalid field fails with an error naming the exact field, e.g.
`'prompt': Required`.

```yaml
name: support-bot
prompt: You are a friendly support agent.
provider:
type: openai
model: gpt-4o-mini
tools:
- current-date
- http
```

### Tools a spec can reference

| Name | Tool |
| -------------- | ---------------------------------------------------- |
| `http` | `httpTool` - HTTP requests |
| `current-date` | `currentDateTool` - current date/time (ISO, UTC) |
| `day-name` | `dayNameTool` - day of the week |

`github` and `jira` need credentials a spec has no field for; referencing
them throws an error telling you to build the agent with `createAgent()` and
pass a configured tool instead.

## Provider credentials

Real providers are resolved by `resolveProvider('<provider>/<model>')`
(also used for spec files), which reads the credential from the environment:

| Provider | Environment variable |
| ------------ | --------------------- |
| `openai` | `OPENAI_API_KEY` |
| `anthropic` | `ANTHROPIC_API_KEY` |
| `openrouter` | `OPENROUTER_API_KEY` |
| `ollama` | `OLLAMA_BASE_URL` |

The `mock` provider needs no credentials and returns canned responses; it is
what the examples and the Quick Start use by default.

## `createAgent()` options

| Option | Description |
| ---------- | ------------------------------------------------------------------ |
| `prompt` | System prompt (required). |
| `provider` | An `LLMProvider` instance (required). |
| `tools` | `Record<string, ToolDescriptor>`, keyed by the name the agent uses. |
| `name` | Agent name (default `'agent'`). |
| `maxSteps` | Passed through to `AgentExecutor.execute()`. |

## `AgentExecutor.execute()` options

`AgentExecutor` is static: call `AgentExecutor.execute(options)`. Only
`agent`, `input` and `provider` are required. Commonly used options:

| Option | Description |
| --------------------------------------- | --------------------------------------------------------------- |
| `agent` | An `AgentConfig`, usually built with `AgentBuilder`. |
| `input` | A user message string, or a `Message[]` conversation. |
| `provider` | The `LLMProvider` to generate with. |
| `toolRegistry` | A `ToolRegistry` holding the tools the agent config refers to. |
| `maxSteps` | Upper bound on LLM/tool steps. |
| `temperature`, `maxTokens` | Generation parameters. |
| `onEvent` | Callback for execution events (`start`, `tool-call`, `finish`, ...). |
| `approvalStore`, `sessionId` | Human-in-the-loop approvals (see `resumeAfterApproval()`). |
| `checkpointStore` | Persist/resume execution checkpoints. |
| `exporter` | A `TraceExporter` for tracing spans. |
| `onLLMRequest`, `onLLMResponse`, `onToolCall`, `onToolResult` | Observability hooks. |

It resolves to an `ExecutionResult`: `{ text, messages, toolCalls, usage,
finishReason, steps, approvalId? }`.

## CLI

### `loushy dev <spec> [--port N] [--host H]`

Local dev server for a spec file: `GET /` chat UI, `GET /health`,
`POST /chat` (`{ "message": "..." }`, 1MB body limit). Reloads the agent
whenever the spec file changes and keeps the last good config if an edit is
invalid.

- `--port` - default `3737`.
- `--host` - default `127.0.0.1` (localhost only). Pass e.g. `--host=0.0.0.0`
to opt in to LAN access.

### `loushy build --target=<target> --agent=<spec> [--out=<dir>]`

Builds a deployable artifact; see [Deployment](./deployment.md).
`--out` defaults to `.loushy/build/<target>`.
87 changes: 87 additions & 0 deletions docs/deployment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Deployment

`loushy build` turns an agent spec file (see [Configuration](./configuration.md))
into a deployable artifact for one target platform:

```bash
npx loushy build --target=<target> --agent=agent.yaml [--out=<dir>]
```

It runs the target's adapter through three steps - **scaffold** (write the
entrypoint and platform files into `--out`, default `.loushy/build/<target>`),
**build** (bundle with `tsup`) and **describe** (print the command to run or
deploy the result). `tsup` must be installed (`npm install --save-dev tsup`).

| Target | Output | Printed command |
| ------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------- |
| `node-server` | `server.ts`, `agent.config.js`, `package.json`, `dist/server.js` | `node dist/server.js` |
| `docker` | everything `node-server` writes, plus `Dockerfile` | `docker build -t loushy-agent . && docker run -p 3000:3000 loushy-agent` |
| `cloudflare-worker` | `worker.ts`, `agent.config.js`, `wrangler.toml`, `dist/worker.js` | `wrangler deploy` |

Every target serves the same HTTP API as `loushy dev`:

- `GET /health` - `200 ok`
- `POST /chat` - JSON `{ "message": "..." }` in, the agent's
`ExecutionResult` (from `AgentExecutor.execute()`) out. Bodies over 1MB
are rejected with `413`.

## `node-server`

`dist/server.js` is a single self-contained bundle (the SDK and its
dependencies are included), so it runs without `npm install`:

```bash
cd .loushy/build/node-server
node dist/server.js # http://127.0.0.1:3000
node dist/server.js --port=8080 --host=0.0.0.0
```

Like `loushy dev`, it binds to `127.0.0.1` unless you opt in to another
interface with `--host=<h>` (or `HOST=<h>`); the port comes from `--port`,
`PORT`, or defaults to `3000`. Provider credentials are read from the same
environment variables as everywhere else (`OPENAI_API_KEY`, ...).

## `docker`

Reuses the `node-server` scaffold and bundle and adds a `Dockerfile` based
on `node:20-slim` that copies `dist/` and runs `node dist/server.js` on port
3000. The image sets `HOST=0.0.0.0` - inside a container the server has to
listen on all interfaces for `docker run -p` to reach it. Pass provider
credentials at run time:

```bash
cd .loushy/build/docker
docker build -t loushy-agent .
docker run -p 3000:3000 -e OPENAI_API_KEY=... loushy-agent
```

## `cloudflare-worker`

Generates a module Worker (`export default { fetch }`) and bundles it as a
browser-platform ES module; the build fails if any `node:` import ends up in
`dist/worker.js`. `wrangler.toml` points `main` at `dist/worker.js` with
`no_bundle = true`, so exactly the verified bundle is uploaded:

```bash
cd .loushy/build/cloudflare-worker
npx wrangler dev # local workerd runtime
npx wrangler deploy # requires a Cloudflare account (`wrangler login`)
```

Workers have no Node.js builtins, so this target currently supports:

- the `mock` provider (real provider SDKs are not bundled into the Worker
yet - use `node-server` or `docker` for OpenAI/Anthropic/Ollama/OpenRouter);
- the `current-date` and `day-name` tools (`http` depends on Node networking
modules).

`loushy build` rejects a spec that uses anything else, with an error naming
the unsupported provider or tool. Provider API keys, once real providers are
supported, are read from Worker bindings named `<TYPE>_API_KEY`
(`wrangler secret put OPENAI_API_KEY`).

## Custom targets

Targets are `DeploymentAdapter` objects (`scaffold`, `build`, `describe`)
registered by name with `registerAdapter()`; both are exported from the
package root.
Loading
Loading