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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

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

### Breaking
- Flows, the Jira / GitHub / Slack / email tools, encryption, `StorageService` and the validators moved out of the package root (A1). Import them from their subpath: `FlowBuilder`, `FlowExecutor`, `validateFlow` and every flow type (`AgentFlow`, `EditorStep`, `FlowExecutionEvent`, ...) from `@lousho/build-ai-agent/flows`; `createJiraTools`, `createGitHubTools`, `createSlackTool`, `slackTool`, `postSlackAlert`, `createEmailTool` and their types from `@lousho/build-ai-agent/integrations`; `EncryptionUtils`, `DTOEncryptionFilter`, `DecryptionError`, `sha256`, `generatePassword`, `StorageService`, `StorageServiceApprovalStore`, `LocalStorageCheckpointStore` and the validators (`validateWithSchema`, `safeValidate`, `isValidEmail`, ...) from `@lousho/build-ai-agent/utils`. The integrations are no longer in `@lousho/build-ai-agent/tools` and the flow types are no longer in `@lousho/build-ai-agent/types`. Nothing else changed: the classes and functions are the same. For file-backed stores with `createAgent()`, prefer `fileStore(dir)`.

### Security
- `openApiTools()` can refuse private destinations (#291): with the new option `privateAddresses: 'refuse'` (default `'allow'`, unchanged), every operation request and the document fetch, each redirect hop included, fails when its host is or resolves to a loopback, link-local, private or reserved address. Host names go through the pinned DNS lookup `http_request` and `web_fetch` use (one resolution per connection, every address checked, the socket connects to the checked address), so DNS rebinding cannot reach a private address; IP literals in every form `URL` normalizes (decimal, octal, hex, IPv4-mapped IPv6) and `localhost` / `*.localhost` are refused when the tools are made. `allowPrivate` lists hosts that may be private. `'refuse'` cannot be combined with a custom `fetch` (it throws a `ConfigurationError`: a custom fetch's connections cannot be pinned) and needs Node.js (`undici`, `node:dns`); where undici cannot load, making the tools throws a `ConfigurationError`. See docs/openapi-tools.md#security-rules.
- The registry permission manifest is now enforced when an agent directory loads and runs (#272, follow-up to M7a). `loadAgentDir` re-hashes every file the `lousho-registry.json` receipt lists; an item with edited or missing files is reported `unattested` (a warning naming the item and files, and `manifest.registry` on `resolveAgentDir()`, which also gains the new `Attestation`, `RegistryItemStatus` and `RegistryStatus` types) without refusing the load. Every tool a receipt item owns then runs inside the manifest accepted at install: an `exec: true` or `needsApproval: true` item's tools - and any unattested item's tools - always wait for approval (a tool's own `deny` still denies); `fetch` while the tool runs is limited to the declared `network` hosts; `process.env` shows only the declared `env` names; and a `requiresSandbox` tool gets a narrowed adapter (`run()` refused without `exec`, `writeFile()` without `filesystem: "write"`, a command env of the non-secret base plus the declared variables only). The envelope stays as narrow as the last accepted manifest until `lousho add <name> --overwrite` attests the current files. Limits, unchanged: the guards intercept `fetch` and `process.env`, not `http`/`net`/`axios`, the filesystem, module-scope captures or a container's own network policy; the manifest is still not a sandbox. See docs/registry.md#permission-manifest.
Expand Down
2 changes: 1 addition & 1 deletion apps/agent-forge/server/buildAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ import {
resolveSpecProvider,
resolveSpecTool,
type AgentConfig,
type AgentFlow,
type AgentSpec,
type HookRegistry,
type LLMProvider,
type SandboxAdapter,
type ToolDescriptor,
} from '@lousho/build-ai-agent';
import type { AgentFlow } from '@lousho/build-ai-agent/flows';
import { compileHooksFromSpecPolicy } from './compileHooks';
import { isSecretProvider, type SecretsStore } from './secretsStore';

Expand Down
3 changes: 2 additions & 1 deletion apps/agent-forge/server/logEntries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
* `LogEntry` rows the Logs tab renders (LOU-O1). Used by runRegistry.ts.
*/
import { randomUUID } from 'node:crypto';
import type { AgentEvent, AgentEventOf, AgentEventType, FlowExecutionEvent, FlowExecutionEventOf } from '@lousho/build-ai-agent';
import type { AgentEvent, AgentEventOf, AgentEventType } from '@lousho/build-ai-agent';
import type { FlowExecutionEvent, FlowExecutionEventOf } from '@lousho/build-ai-agent/flows';
import type { LogEntry } from '../shared/wireTypes';

/** The event-specific part of a `LogEntry`; id/agentId/timestamp are added by the translators. */
Expand Down
4 changes: 2 additions & 2 deletions apps/agent-forge/server/runRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { fileTraceExporter } from '@lousho/build-ai-agent/traces';
import { agentTraceDir, fanOutExporter } from './traceStore';
import {
AgentExecutor,
FlowExecutor,
ToolRegistry,
type AgentEvent,
type CheckpointStore,
Expand All @@ -31,6 +30,7 @@ import {
type ForkPatch,
type TrajectoryComparison,
} from '@lousho/build-ai-agent';
import { FlowExecutor, type AgentFlow } from '@lousho/build-ai-agent/flows';
import { buildAgentFromSpec, extractFlowFromSpec } from './buildAgent';
import { withAbortSignal, RunAbortedError } from './abortableProvider';
import { FileApprovalStore } from './approvalStore';
Expand Down Expand Up @@ -596,7 +596,7 @@ export class RunManager extends EventEmitter {
*/
private runFlow(
agentId: string,
flow: import('@lousho/build-ai-agent').AgentFlow,
flow: AgentFlow,
deps: {
agent: import('@lousho/build-ai-agent').AgentConfig;
provider: import('@lousho/build-ai-agent').LLMProvider;
Expand Down
3 changes: 2 additions & 1 deletion apps/agent-forge/src/graph/__tests__/graphToFlow.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest';
import { FlowExecutor, MockLLMProvider, type AgentConfig } from '@lousho/build-ai-agent';
import { MockLLMProvider, type AgentConfig } from '@lousho/build-ai-agent';
import { FlowExecutor } from '@lousho/build-ai-agent/flows';
import { graphToFlow, hasRouterNode } from '../graphToFlow';
import { graphToSpec } from '../graphToSpec';
import type { AgentGraphSpec } from '../types';
Expand Down
2 changes: 1 addition & 1 deletion apps/agent-forge/src/graph/graphToFlow.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AgentFlow, EditorStep } from '@lousho/build-ai-agent';
import type { AgentFlow, EditorStep } from '@lousho/build-ai-agent/flows';
import type { AgentGraphEdge, AgentGraphNode, AgentGraphSpec } from './types';

/**
Expand Down
8 changes: 5 additions & 3 deletions docs/api-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ How the pieces fit:
| `AgentType` | Deprecated, no runtime effect: agents need no type. |
| `resumeAfterApproval()` | Resume an execution paused for human approval. Advanced: see [the executor API](./executor-api.md). |
| `InMemoryApprovalStore` | Process-local `ApprovalStore`; the default store of `createAgent()` agents. |
| `StorageServiceApprovalStore`, `LocalStorageCheckpointStore` | File-backed approval and checkpoint stores over a `StorageService` (see [Approvals](./approvals.md), [Durable execution](./durable-execution.md)). |
| `StorageServiceApprovalStore`, `LocalStorageCheckpointStore` (from `/utils`) | File-backed approval and checkpoint stores over a `StorageService` (see [Approvals](./approvals.md), [Durable execution](./durable-execution.md)). |
| `SqliteStore` (from `/sqlite`) | Sessions, checkpoints and approvals in one SQLite file (see [Sessions](./sessions.md#choosing-a-store)). |
| `fileStore(dir)` | Sessions, checkpoints and approvals as plain JSON files under `dir` (see [Sessions](./sessions.md#choosing-a-store)). |
| `KVStore`, `KVCheckpointStore` (from `/kv`) | Stores on a Cloudflare Workers KV binding, for a hand-written Worker (see [Deployment](./deployment.md)). |
Expand Down Expand Up @@ -314,7 +314,8 @@ Token estimates, the model price table, and the usage and cost of a run: see [Mo

## Flows, evals, observability and security

- `FlowBuilder` / `FlowExecutor` - multi-step workflow graphs; see [Flows](./flows.md).
- `FlowBuilder` / `FlowExecutor` (`@lousho/build-ai-agent/flows`) - multi-step
workflow graphs; see [Flows](./flows.md).
- `WebhookTriggerAdapter`, `SlackTriggerAdapter`, `CronTriggerAdapter` and `TriggerRegistry`
(`@lousho/build-ai-agent/triggers`) - wake an agent from a webhook, a Slack
message or a schedule; see [Triggers](./triggers.md).
Expand All @@ -340,7 +341,8 @@ Token estimates, the model price table, and the usage and cost of a run: see [Mo
- `HookRegistry`, `AgentHook`, `HookContext`, `ToolCallHookContext`,
`GenerateHookContext` - hooks that observe, deny, rewrite or redact tool calls
and model calls. See [Hooks](./hooks.md).
- `EncryptionUtils`, `sha256`, `StorageService` - supporting utilities; see
- `EncryptionUtils`, `sha256`, `StorageService`
(`@lousho/build-ai-agent/utils`) - supporting utilities; see
[Utilities](./utilities.md).

## Deployment
Expand Down
3 changes: 2 additions & 1 deletion docs/approvals.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ tool. Resume later, after a real restart if you like, with
`resumeAfterApproval()`:

```ts
import { AgentExecutor, resumeAfterApproval, StorageServiceApprovalStore } from '@lousho/build-ai-agent';
import { AgentExecutor, resumeAfterApproval } from '@lousho/build-ai-agent';
import { StorageServiceApprovalStore } from '@lousho/build-ai-agent/utils';

const approvalStore = new StorageServiceApprovalStore(storage);

Expand Down
3 changes: 2 additions & 1 deletion docs/durable-execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,8 @@ call it corresponds to.
`agent.send(message, { sessionId })`:

```ts
import { AgentExecutor, LocalStorageCheckpointStore } from '@lousho/build-ai-agent';
import { AgentExecutor } from '@lousho/build-ai-agent';
import { LocalStorageCheckpointStore } from '@lousho/build-ai-agent/utils';

const checkpoints = new LocalStorageCheckpointStore(storage);

Expand Down
2 changes: 1 addition & 1 deletion docs/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ of a spec field and would be ignored. Other unknown fields are still ignored.
agent spec has no field for.

**Fix:** build the agent with `createAgent()` and pass the configured tool, e.g.
from `createGitHubTools(config)`.
`createGitHubTools(config)` from `@lousho/build-ai-agent/integrations`.

**Example:** `tools: [github]` in a spec.

Expand Down
2 changes: 1 addition & 1 deletion docs/flows.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Describe the graph with `FlowBuilder` and run it with the static
`FlowExecutor.execute(flow, context, onEvent?)`.

```ts
import { FlowBuilder, FlowExecutor, type EditorStep } from '@lousho/build-ai-agent';
import { FlowBuilder, FlowExecutor, type EditorStep } from '@lousho/build-ai-agent/flows';

// FlowBuilder is a metadata builder: setCode/setName/setInputs/setFlow(...).build().
// EditorStep covers every node type FlowExecutor runs ('sequence', 'llmCall',
Expand Down
24 changes: 24 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,30 @@ hazard), so a class from one is not `===` the other. `instanceof SDKError` and
`instanceof HookRegistry` are safe across the two copies (they check a
`Symbol.for` brand); for other classes, use one module format per process.

The entries `exports` declares, and what each one holds:

| Import specifier | What it holds |
| ---------------- | ------------- |
| `@lousho/build-ai-agent` | 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/build-ai-agent/core` | `AgentBuilder` (the lower-level agent builder; advanced - prefer `createAgent()`) |
| `@lousho/build-ai-agent/tools` | The tools surface: `defineTool()`, `ToolRegistry`, the built-in tools, MCP/OpenAPI helpers |
| `@lousho/build-ai-agent/flows` | `FlowBuilder`, `FlowExecutor`, `validateFlow()` and every flow type (`AgentFlow`, `EditorStep`, `FlowExecutionEvent`, ...) |
| `@lousho/build-ai-agent/integrations` | The credentialed third-party tools: `createJiraTools()`, `createGitHubTools()`, `createSlackTool()` / `slackTool`, `postSlackAlert()`, `createEmailTool()` and their types |
| `@lousho/build-ai-agent/utils` | `EncryptionUtils`, `sha256()`, `generatePassword()`, `StorageService`, `StorageServiceApprovalStore`, `LocalStorageCheckpointStore` and the validators (`validateWithSchema`, `isValidEmail`, ...) |
| `@lousho/build-ai-agent/mcp` | MCP client and server helpers: `connectMcp()`, `loadMcpTools()`, `serveMcp()` |
| `@lousho/build-ai-agent/types` | The public types without the runtime |
| `@lousho/build-ai-agent/testing` | `mockModel()`, `recordReplay()` and the offline-test helpers |
| `@lousho/build-ai-agent/otel` | `createOtelTraceExporter()` (needs the optional `@opentelemetry/api` peer) |
| `@lousho/build-ai-agent/hooks` | The hooks surface: `HookRegistry`, hook types |
| `@lousho/build-ai-agent/sqlite` | `SqliteStore` (sessions, checkpoints, approvals and tokens in one SQLite file; needs Node's `node:sqlite`) |
| `@lousho/build-ai-agent/auth` | Route auth: `routeAuth()`, `jwt()`, `oidc()`, `basic()`, `apiToken()`, `anonymous()` |
| `@lousho/build-ai-agent/kv` | `KVStore`, `KVCheckpointStore` for a hand-written Cloudflare Worker |
| `@lousho/build-ai-agent/traces` | `fileTraceExporter()`, `listTraces()`, `readTrace()` (what `lousho traces` reads) |
| `@lousho/build-ai-agent/triggers` | `WebhookTriggerAdapter`, `SlackTriggerAdapter`, `CronTriggerAdapter`, `TriggerRegistry` |
| `@lousho/build-ai-agent/react` | `useLoushoAgent()` and the React helpers (needs `react`) |
| `@lousho/build-ai-agent/vue` | `useLoushoAgent()` as a Vue composable (needs `vue`) |
| `@lousho/build-ai-agent/svelte` | `loushoAgent()` as a Svelte store |

## Installing from a local build

To try an unreleased commit, build and pack the SDK from a checkout of this
Expand Down
2 changes: 1 addition & 1 deletion docs/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ its kind by carrying a `toolErrorKind` property. An error extending
| `createTodoTools()` | `todo_write` / `todo_read` so an agent can plan multi-step work; see [Todo tools](#todo-tools). |
| `askQuestionTool()`, `createAgent({ askQuestion: true })` | `ask_question`: the agent asks the user something and the run pauses until `agent.approvals.answer()`; see [Asking the user a question](./approvals.md#asking-the-user-a-question). |
| `createFsTools()`, `createShellTool()` | File system and shell tools for coding agents; see [Workspace tools](./workspace-tools.md). |
| `createEmailTool()`, `createSlackTool()`, `createGitHubTools()`, `createJiraTools()` | Integrations that need credentials, so they are built with options. |
| `createEmailTool()`, `createSlackTool()`, `createGitHubTools()`, `createJiraTools()` (from `@lousho/build-ai-agent/integrations`) | Integrations that need credentials, so they are built with options. |
| `createAgent({ mcpServers })`, `connectMcp(servers)` | Every tool of MCP servers given as config (stdio `command` or HTTP `url`), named `<server>__<tool>`; see [Use MCP servers in an agent](./mcp.md#use-mcp-servers-in-an-agent). |
| `openApiTools(document, options)` | One tool per operation of an OpenAPI 3.0 / 3.1 document; mutating operations ask for approval. See [OpenAPI tools](./openapi-tools.md). |
| `loadMcpTools(client, name)` | Every tool of a connected MCP server; see [MCP tools](./mcp.md#tools-from-a-client-you-connected-yourself). |
Expand Down
10 changes: 5 additions & 5 deletions docs/utilities.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Utilities

Supporting helpers exported from the package root: encryption and hashing,
and file storage for attachments. None of them is needed
to build an agent; they are here for the apps around one.
Supporting helpers exported from the `@lousho/build-ai-agent/utils` subpath:
encryption and hashing, and file storage for attachments. None of them is
needed to build an agent; they are here for the apps around one.

## Encryption and hashing

```ts
import { EncryptionUtils, sha256 } from '@lousho/build-ai-agent';
import { EncryptionUtils, sha256 } from '@lousho/build-ai-agent/utils';

const encryption = new EncryptionUtils('your-secret-key');
const encrypted = await encryption.encrypt('sensitive data'); // fresh random salt every call
Expand All @@ -28,7 +28,7 @@ as-is. It is also what `StorageServiceApprovalStore` and
`LocalStorageCheckpointStore` write through.

```ts
import { StorageService } from '@lousho/build-ai-agent';
import { StorageService } from '@lousho/build-ai-agent/utils';
import * as fs from 'node:fs';
import * as path from 'node:path';

Expand Down
Loading
Loading