From 23423281b41acae2041fdb11c6db6974af955e8e Mon Sep 17 00:00:00 2001 From: Ali Mohammad Date: Sun, 4 Oct 2026 11:57:55 +0300 Subject: [PATCH] feat!: move flows, integrations and utilities out of the package root (A1) The root exported 825 names; flows, the credentialed Jira/GitHub/Slack/email tools, encryption, StorageService and the validators were not agent functionality a new user needs there. They move to subpaths, nothing is removed: - './flows' now also re-exports the flow types (they leave './types') - new './integrations' re-exports email/jira/github/slack built-ins (they leave the root and './tools') - new './utils' holds crypto, security option types, StorageService, StorageServiceApprovalStore, LocalStorageCheckpointStore and the validators; the root keeps only utils/errors and utils/errorCodes - root exports drop './flows', './storage' (barrel deleted) and the two StorageService stores move with it Root exports: 825 -> 727 (98 fewer; isCreateAgentResult was added to the flows barrel after the audit's count of 97). New src/publicSurface.test.ts / .test-d.ts pin the split; later wave-4 tickets extend them. Closes #236 --- CHANGELOG.md | 3 + apps/agent-forge/server/buildAgent.ts | 2 +- apps/agent-forge/server/logEntries.ts | 3 +- apps/agent-forge/server/runRegistry.ts | 4 +- .../src/graph/__tests__/graphToFlow.test.ts | 3 +- apps/agent-forge/src/graph/graphToFlow.ts | 2 +- docs/api-overview.md | 8 +- docs/approvals.md | 3 +- docs/durable-execution.md | 3 +- docs/errors.md | 2 +- docs/flows.md | 2 +- docs/installation.md | 24 +++ docs/tools.md | 2 +- docs/utilities.md | 10 +- llms-full.txt | 54 +++-- llms.txt | 2 +- package.json | 10 + scripts/verify-docs-snippets.ts | 2 +- src/core/AgentBuilder.ts | 3 +- src/execution/ApprovalGate.ts | 2 +- src/execution/checkpoint.ts | 2 +- src/execution/genAiSemconv.test.ts | 2 +- src/execution/index.ts | 37 +++- src/flows/FlowBuilder.test.ts | 2 +- src/flows/FlowBuilder.ts | 2 +- src/flows/FlowExecutor.test.ts | 5 +- src/flows/FlowExecutor.ts | 2 +- src/flows/flowTypes.test-d.ts | 2 +- src/flows/index.ts | 2 + src/flows/inputs.ts | 2 +- src/flows/validators.ts | 2 +- src/index.ts | 13 +- src/integrations/index.ts | 12 ++ src/providers/importGraph.test.ts | 2 +- src/publicSurface.test-d.ts | 200 ++++++++++++++++++ src/publicSurface.test.ts | 86 ++++++++ src/security/index.ts | 7 +- src/spec/specToAgent.ts | 7 +- src/storage/index.ts | 11 - src/tools/built-in/index.ts | 6 +- src/types/index.ts | 2 +- src/utils/index.ts | 20 +- tsup.config.ts | 2 + 43 files changed, 489 insertions(+), 83 deletions(-) create mode 100644 src/integrations/index.ts create mode 100644 src/publicSurface.test-d.ts create mode 100644 src/publicSurface.test.ts delete mode 100644 src/storage/index.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fe9ae9d..fe1f8f13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 --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. diff --git a/apps/agent-forge/server/buildAgent.ts b/apps/agent-forge/server/buildAgent.ts index f73c2914..7c524869 100644 --- a/apps/agent-forge/server/buildAgent.ts +++ b/apps/agent-forge/server/buildAgent.ts @@ -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'; diff --git a/apps/agent-forge/server/logEntries.ts b/apps/agent-forge/server/logEntries.ts index 6de1a3a2..d8e97a1c 100644 --- a/apps/agent-forge/server/logEntries.ts +++ b/apps/agent-forge/server/logEntries.ts @@ -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. */ diff --git a/apps/agent-forge/server/runRegistry.ts b/apps/agent-forge/server/runRegistry.ts index bed4c7a1..32a748b2 100644 --- a/apps/agent-forge/server/runRegistry.ts +++ b/apps/agent-forge/server/runRegistry.ts @@ -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, @@ -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'; @@ -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; diff --git a/apps/agent-forge/src/graph/__tests__/graphToFlow.test.ts b/apps/agent-forge/src/graph/__tests__/graphToFlow.test.ts index b4d13b6b..ed34344c 100644 --- a/apps/agent-forge/src/graph/__tests__/graphToFlow.test.ts +++ b/apps/agent-forge/src/graph/__tests__/graphToFlow.test.ts @@ -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'; diff --git a/apps/agent-forge/src/graph/graphToFlow.ts b/apps/agent-forge/src/graph/graphToFlow.ts index 0bbe5372..c423f2c1 100644 --- a/apps/agent-forge/src/graph/graphToFlow.ts +++ b/apps/agent-forge/src/graph/graphToFlow.ts @@ -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'; /** diff --git a/docs/api-overview.md b/docs/api-overview.md index 2c2da0b6..c3e72c6f 100644 --- a/docs/api-overview.md +++ b/docs/api-overview.md @@ -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)). | @@ -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). @@ -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 diff --git a/docs/approvals.md b/docs/approvals.md index af280988..08b66cff 100644 --- a/docs/approvals.md +++ b/docs/approvals.md @@ -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); diff --git a/docs/durable-execution.md b/docs/durable-execution.md index 25f9dae0..d2c34494 100644 --- a/docs/durable-execution.md +++ b/docs/durable-execution.md @@ -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); diff --git a/docs/errors.md b/docs/errors.md index b00b1a25..81c9e4ac 100644 --- a/docs/errors.md +++ b/docs/errors.md @@ -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. diff --git a/docs/flows.md b/docs/flows.md index e6123e11..f0f21a30 100644 --- a/docs/flows.md +++ b/docs/flows.md @@ -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', diff --git a/docs/installation.md b/docs/installation.md index 3cde8143..bed7aca6 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -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 diff --git a/docs/tools.md b/docs/tools.md index b10778a6..1715f025 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -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 `__`; 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). | diff --git a/docs/utilities.md b/docs/utilities.md index a27782fc..99f4b42b 100644 --- a/docs/utilities.md +++ b/docs/utilities.md @@ -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 @@ -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'; diff --git a/llms-full.txt b/llms-full.txt index c143920e..23c3c195 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -442,6 +442,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 @@ -920,7 +944,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](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/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](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/approvals.md), [Durable execution](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/durable-execution.md)). | +| `StorageServiceApprovalStore`, `LocalStorageCheckpointStore` (from `/utils`) | File-backed approval and checkpoint stores over a `StorageService` (see [Approvals](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/approvals.md), [Durable execution](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/durable-execution.md)). | | `SqliteStore` (from `/sqlite`) | Sessions, checkpoints and approvals in one SQLite file (see [Sessions](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/sessions.md#choosing-a-store)). | | `fileStore(dir)` | Sessions, checkpoints and approvals as plain JSON files under `dir` (see [Sessions](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/sessions.md#choosing-a-store)). | | `KVStore`, `KVCheckpointStore` (from `/kv`) | Stores on a Cloudflare Workers KV binding, for a hand-written Worker (see [Deployment](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/deployment.md)). | @@ -1187,7 +1211,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](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/flows.md). +- `FlowBuilder` / `FlowExecutor` (`@lousho/build-ai-agent/flows`) - multi-step + workflow graphs; see [Flows](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/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](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/triggers.md). @@ -1213,7 +1238,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](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/hooks.md). -- `EncryptionUtils`, `sha256`, `StorageService` - supporting utilities; see +- `EncryptionUtils`, `sha256`, `StorageService` + (`@lousho/build-ai-agent/utils`) - supporting utilities; see [Utilities](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/utilities.md). ## Deployment @@ -2422,7 +2448,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); @@ -5953,7 +5980,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); @@ -6353,7 +6381,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. @@ -7486,7 +7514,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', @@ -14441,7 +14469,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](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/approvals.md#asking-the-user-a-question). | | `createFsTools()`, `createShellTool()` | File system and shell tools for coding agents; see [Workspace tools](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/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 `__`; see [Use MCP servers in an agent](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/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](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/openapi-tools.md). | | `loadMcpTools(client, name)` | Every tool of a connected MCP server; see [MCP tools](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/mcp.md#tools-from-a-client-you-connected-yourself). | @@ -15039,14 +15067,14 @@ Never paste an API key. Source: docs/utilities.md -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 @@ -15067,7 +15095,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'; diff --git a/llms.txt b/llms.txt index 651de225..3efcd032 100644 --- a/llms.txt +++ b/llms.txt @@ -57,7 +57,7 @@ - [Tools](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/tools.md): A tool is a typed function the model can call. - [Triggers](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/triggers.md): A trigger adapter turns one wake-up mechanism into an agent run: an inbound webhook, a clock, or a Slack event. - [Troubleshooting](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/troubleshooting.md): This page starts from what you see and points to the cause, the fix and the page that explains it. -- [Utilities](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/utilities.md): Supporting helpers exported from the package root: encryption and hashing, and file storage for attachments. +- [Utilities](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/utilities.md): Supporting helpers exported from the `@lousho/build-ai-agent/utils` subpath: encryption and hashing, and file storage for attachments. - [Vue](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/vue.md): `useLoushoAgent()` for Vue 3 is the React hook as a composable: the same sources, the same state and the same actions, over the same typed event stream. - [Workspace tools](https://github.com/LinuxDevil/agent-sdk/blob/main/docs/workspace-tools.md): Workspace tools give an agent a file system and a shell, so you can build coding agents in the style of Claude Code. diff --git a/package.json b/package.json index 4be8a503..de3673e3 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,16 @@ "import": "./dist/flows/index.mjs", "require": "./dist/flows/index.js" }, + "./integrations": { + "types": "./dist/integrations/index.d.ts", + "import": "./dist/integrations/index.mjs", + "require": "./dist/integrations/index.js" + }, + "./utils": { + "types": "./dist/utils/index.d.ts", + "import": "./dist/utils/index.mjs", + "require": "./dist/utils/index.js" + }, "./mcp": { "types": "./dist/tools/mcp/index.d.ts", "import": "./dist/tools/mcp/index.mjs", diff --git a/scripts/verify-docs-snippets.ts b/scripts/verify-docs-snippets.ts index c37ff111..88f8bf10 100644 --- a/scripts/verify-docs-snippets.ts +++ b/scripts/verify-docs-snippets.ts @@ -63,7 +63,7 @@ const PLACEHOLDERS: Record = { provider: `${SDK}.LLMProvider`, registry: `${SDK}.ToolRegistry`, toolRegistry: `${SDK}.ToolRegistry`, - storage: `${SDK}.StorageService`, + storage: `import('@lousho/build-ai-agent/utils').StorageService`, approvalStore: `${SDK}.ApprovalStore`, checkpointStore: `${SDK}.CheckpointStore`, mcpClient: "import('@modelcontextprotocol/sdk/client/index.js').Client", diff --git a/src/core/AgentBuilder.ts b/src/core/AgentBuilder.ts index a10f7473..4c321940 100644 --- a/src/core/AgentBuilder.ts +++ b/src/core/AgentBuilder.ts @@ -1,4 +1,5 @@ -import { AgentConfig, AgentSettings, AgentType, ToolConfiguration, AgentFlow } from '../types'; +import { AgentConfig, AgentSettings, AgentType, ToolConfiguration } from '../types'; +import { AgentFlow } from '../types/flow'; import { validateAgentConfig, validateAgentTools } from '../agent-types'; import { newId } from '../utils/id'; import type { DefinedTool } from '../tools/defineTool'; diff --git a/src/execution/ApprovalGate.ts b/src/execution/ApprovalGate.ts index 5eca160c..24dae788 100644 --- a/src/execution/ApprovalGate.ts +++ b/src/execution/ApprovalGate.ts @@ -5,7 +5,7 @@ import { Message, ToolCall } from '../providers'; import { AgentConfig } from '../types'; -import { StorageService, readJSONAttachmentLocked } from '../storage'; +import { StorageService, readJSONAttachmentLocked } from '../storage/StorageService'; import type { RunUsage } from '../models/usage'; import type { AgentFingerprint } from './agentFingerprint'; import type { Principal } from '../auth/types'; diff --git a/src/execution/checkpoint.ts b/src/execution/checkpoint.ts index b36d5fcc..54d20325 100644 --- a/src/execution/checkpoint.ts +++ b/src/execution/checkpoint.ts @@ -4,7 +4,7 @@ */ import type { Message } from '../providers'; -import { readJSONAttachmentLocked, type StorageService } from '../storage'; +import { readJSONAttachmentLocked, type StorageService } from '../storage/StorageService'; import type { StepUsage } from '../models/usage'; import type { CheckpointUsage } from './runUsage'; import type { AgentFingerprint } from './agentFingerprint'; diff --git a/src/execution/genAiSemconv.test.ts b/src/execution/genAiSemconv.test.ts index ab689142..4079363e 100644 --- a/src/execution/genAiSemconv.test.ts +++ b/src/execution/genAiSemconv.test.ts @@ -6,7 +6,7 @@ import { CAPTURE_CONTENT_ENV } from './semconv'; import { FlowExecutor } from '../flows/FlowExecutor'; import { ToolRegistry, defineTool } from '../tools'; import { AgentBuilder } from '../core'; -import { AgentFlow } from '../types'; +import { AgentFlow } from '../types/flow'; import { mockModel } from '../testing'; /** In-memory exporter: remembers every span, in start order, in its final state. */ diff --git a/src/execution/index.ts b/src/execution/index.ts index bdae5b88..a5855ac9 100644 --- a/src/execution/index.ts +++ b/src/execution/index.ts @@ -6,10 +6,43 @@ export * from './AgentExecutor'; export * from './DelegationTool'; export * from './errors'; -export * from './ApprovalGate'; +// A1: StorageServiceApprovalStore moved to '@lousho/build-ai-agent/utils'. +export { + ASK_QUESTION_TOOL_NAME, + describeApproval, + type ApprovalDecision, + type ApprovalKind, + type ApprovalQuestion, + type ApprovalSignIn, + type ApprovalStore, + type ExecutionSnapshot, + type PausedBackgroundTask, + type PendingApproval, + type ResolvedApproval, + type SubagentSuspension, + type SuspendedBackgroundTasks, +} from './ApprovalGate'; export { InMemoryApprovalStore } from './InMemoryApprovalStore'; export * from './resume'; -export * from './checkpoint'; +// A1: LocalStorageCheckpointStore moved to '@lousho/build-ai-agent/utils'. +export { + appendToRing, + DEFAULT_CHECKPOINT_HISTORY_LIMIT, + getCheckpointHistory, + newestFirst, + resolveHistoryLimit, + RUN_CONFIG_KEY, + toHistoryEntry, + type Checkpoint, + type CheckpointDeleteOptions, + type CheckpointHistoryEntry, + type CheckpointHistoryOptions, + type CheckpointStatus, + type CheckpointStore, + type ForkOptions, + type ForkPatch, + type ForkResult, +} from './checkpoint'; export * from './tracing'; export * from './semconv'; export * from './logger'; diff --git a/src/flows/FlowBuilder.test.ts b/src/flows/FlowBuilder.test.ts index 4fbcb093..5aa5f9c2 100644 --- a/src/flows/FlowBuilder.test.ts +++ b/src/flows/FlowBuilder.test.ts @@ -3,7 +3,7 @@ import { FlowBuilder } from './FlowBuilder'; import { createAgent } from '../createAgent'; import { mockModel } from '../testing'; import { SDKError } from '../execution/errors'; -import type { FlowAgentDefinition } from '../types'; +import type { FlowAgentDefinition } from '../types/flow'; describe('FlowBuilder', () => { describe('basic building', () => { diff --git a/src/flows/FlowBuilder.ts b/src/flows/FlowBuilder.ts index f92db6d4..23d905df 100644 --- a/src/flows/FlowBuilder.ts +++ b/src/flows/FlowBuilder.ts @@ -1,5 +1,5 @@ import { newId } from '../utils/id'; -import { EditorStep, AgentFlow, FlowAgentDefinition, FlowInputVariable } from '../types'; +import { EditorStep, AgentFlow, FlowAgentDefinition, FlowInputVariable } from '../types/flow'; import { SDKError } from '../execution/errors'; import { isCreateAgentResult } from './validators'; diff --git a/src/flows/FlowExecutor.test.ts b/src/flows/FlowExecutor.test.ts index 427e3f3d..24927211 100644 --- a/src/flows/FlowExecutor.test.ts +++ b/src/flows/FlowExecutor.test.ts @@ -3,10 +3,11 @@ */ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import type { ToolDescriptor, EditorStep } from '../types'; +import type { ToolDescriptor } from '../types'; +import type { EditorStep, AgentFlow } from '../types/flow'; import type { FlowExecutionEvent } from './FlowExecutor'; import { FlowExecutor, FlowExecutionContext } from './FlowExecutor'; -import { AgentFlow, AgentConfig } from '../types'; +import { AgentConfig } from '../types'; import { MockLLMProvider } from '../providers/mock'; import { ToolRegistry } from '../tools'; import { SandboxAdapter } from '../security/sandbox'; diff --git a/src/flows/FlowExecutor.ts b/src/flows/FlowExecutor.ts index f0b77a90..e7fcfb4a 100644 --- a/src/flows/FlowExecutor.ts +++ b/src/flows/FlowExecutor.ts @@ -20,7 +20,7 @@ import { SetVariableNode, ThrowNode, ToolCallNode, -} from '../types'; +} from '../types/flow'; import { AgentConfig } from '../types'; import { SandboxAdapter, NoopSandbox } from '../security/sandboxCore'; import { executeToolWithSandboxGuard } from '../execution/sandboxGuard'; diff --git a/src/flows/flowTypes.test-d.ts b/src/flows/flowTypes.test-d.ts index 4ff2537c..e734a6c4 100644 --- a/src/flows/flowTypes.test-d.ts +++ b/src/flows/flowTypes.test-d.ts @@ -1,5 +1,5 @@ import { describe, it, expectTypeOf } from 'vitest'; -import type { EditorStep } from '../types'; +import type { EditorStep } from '../types/flow'; describe('EditorStep covers every node kind FlowExecutor runs', () => { it('accepts executor-side nodes without a cast', () => { diff --git a/src/flows/index.ts b/src/flows/index.ts index e3960a34..52461f86 100644 --- a/src/flows/index.ts +++ b/src/flows/index.ts @@ -2,3 +2,5 @@ export * from './FlowBuilder'; export * from './FlowExecutor'; export * from './inputs'; export * from './validators'; +// A1: the flow types moved out of the root/`./types` into this subpath with the flow engine. +export * from '../types/flow'; diff --git a/src/flows/inputs.ts b/src/flows/inputs.ts index 9754544f..5cd97d02 100644 --- a/src/flows/inputs.ts +++ b/src/flows/inputs.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { FlowInputVariable, FlowInputType } from '../types'; +import { FlowInputVariable, FlowInputType } from '../types/flow'; /** * Extract variable names from a string in the format @variableName diff --git a/src/flows/validators.ts b/src/flows/validators.ts index c3af837d..6e141ac5 100644 --- a/src/flows/validators.ts +++ b/src/flows/validators.ts @@ -1,4 +1,4 @@ -import { AgentFlow, FlowAgentDefinition } from '../types'; +import { AgentFlow, FlowAgentDefinition } from '../types/flow'; /** * Whether `value` is a `createAgent()` result (a `SimpleAgent`): a live agent diff --git a/src/index.ts b/src/index.ts index 7b2bc12f..95d0ad13 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,8 +20,8 @@ export * from './agent-types'; // Tools export * from './tools'; -// Flows -export * from './flows'; +// Flows moved to '@lousho/build-ai-agent/flows' (A1). + // Providers @@ -36,8 +36,7 @@ export * from './evals'; // Security export * from './security'; -// Storage -export * from './storage'; +// Storage moved to '@lousho/build-ai-agent/utils' (A1). // Token estimation and model registry (LOU-W1) @@ -46,8 +45,10 @@ export * from './models'; // Context compaction (LOU-W2) export * from './context'; -// Utils -export * from './utils'; +// Utils: the error helpers stay on the root; encryption, StorageService and +// the validators moved to '@lousho/build-ai-agent/utils' (A1). +export * from './utils/errors'; +export * from './utils/errorCodes'; // createAgent() convenience API (LOU-H1) export * from './createAgent'; diff --git a/src/integrations/index.ts b/src/integrations/index.ts new file mode 100644 index 00000000..44914a6e --- /dev/null +++ b/src/integrations/index.ts @@ -0,0 +1,12 @@ +/** + * Integrations (A1) + * The credentialed third-party tools - Jira, GitHub, Slack and email. They + * used to ship on the package root (and `./tools`); import them from + * `@lousho/build-ai-agent/integrations`. The files themselves still live in + * `src/tools/built-in/`; this barrel only re-exports them. + */ + +export * from '../tools/built-in/email'; +export * from '../tools/built-in/jira'; +export * from '../tools/built-in/github'; +export * from '../tools/built-in/slack'; diff --git a/src/providers/importGraph.test.ts b/src/providers/importGraph.test.ts index 79dbfcb5..f5581471 100644 --- a/src/providers/importGraph.test.ts +++ b/src/providers/importGraph.test.ts @@ -27,7 +27,7 @@ const NEVER_LOADED_AT_IMPORT = [ 'node:sqlite', // LOU-W5: only the lazily-loaded /sqlite subpath may use it, and only when a store is constructed ]; -const ENTRY_POINTS = ['index', 'core/index', 'tools/index', 'tools/mcp/index', 'flows/index', 'testing/index', 'storage/sqlite/index', 'svelte/index']; +const ENTRY_POINTS = ['index', 'core/index', 'tools/index', 'tools/mcp/index', 'flows/index', 'integrations/index', 'utils/index', 'testing/index', 'storage/sqlite/index', 'svelte/index']; /** LOU-P2, LOU-P3: the react and vue entries import their own framework and not the others (`svelte/index` is in ENTRY_POINTS: it loads none). */ const OTHER_FRAMEWORKS: Record = { diff --git a/src/publicSurface.test-d.ts b/src/publicSurface.test-d.ts new file mode 100644 index 00000000..abba3ff2 --- /dev/null +++ b/src/publicSurface.test-d.ts @@ -0,0 +1,200 @@ +/** + * A1 (wave 4): the type half of the public-surface split. Each moved or + * removed type name must be absent from `import('./index')` (the + * `@ts-expect-error` pins that) and, for a move, present on its subpath. + * Later tickets (A2a, A2b, A2c, A3, A5) append the names they remove or + * move here and in publicSurface.test.ts. + */ +import { describe, it, expectTypeOf } from 'vitest'; + +describe('public surface (A1): moved types are off the root, on their subpath', () => { + it('flow types moved to ./flows', () => { + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T1 = import('./index').AgentFlow; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T2 = import('./index').EditorStep; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T3 = import('./index').FlowExecutionEvent; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T4 = import('./index').FlowExecutionEventOf; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T5 = import('./index').FlowExecutionResult; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T6 = import('./index').FlowExecutionContext; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T7 = import('./index').FlowChunkEvent; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T8 = import('./index').FlowInputVariable; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T9 = import('./index').FlowInputType; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T10 = import('./index').FlowAgentDefinition; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T11 = import('./index').FlowToolSetting; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T12 = import('./index').FlowExecutionMode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T13 = import('./index').FlowOutputMode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T14 = import('./index').EditorShapeStep; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T15 = import('./index').RuntimeStep; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T16 = import('./index').StepNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T17 = import('./index').SequenceNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T18 = import('./index').ParallelNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T19 = import('./index').OneOfNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T20 = import('./index').ForEachNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T21 = import('./index').EvaluatorNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T22 = import('./index').BestOfAllNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T23 = import('./index').ToolNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T24 = import('./index').UIComponentNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T25 = import('./index').ConditionNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T26 = import('./index').LoopNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T27 = import('./index').OneOfOption; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T28 = import('./index').OneOfOptionsNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T29 = import('./index').ForEachItemsNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T30 = import('./index').ExpressionEvaluatorNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T31 = import('./index').LLMCallNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T32 = import('./index').ToolCallNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T33 = import('./index').SetVariableNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T34 = import('./index').ReturnNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T35 = import('./index').EndNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T36 = import('./index').ThrowNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T37 = import('./index').FlowDefinitionNode; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T38 = import('./index').FlowExecutionEventType; + // @ts-expect-error - moved to '@lousho/build-ai-agent/flows' (A1) + type _T39 = import('./index').FlowExecutionEventDataMap; + + // Present on './flows': + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf>().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + }); + + it('integration types moved to ./integrations', () => { + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T1 = import('./index').EmailToolOptions; + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T2 = import('./index').JiraConfig; + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T3 = import('./index').JiraTicket; + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T4 = import('./index').JiraComment; + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T5 = import('./index').JiraTransition; + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T6 = import('./index').GitHubConfig; + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T7 = import('./index').GitHubFile; + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T8 = import('./index').GitHubSearchResult; + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T9 = import('./index').GitHubPullRequest; + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T10 = import('./index').GitHubBranch; + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T11 = import('./index').SlackBlock; + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T12 = import('./index').SlackAlertPayload; + // @ts-expect-error - moved to '@lousho/build-ai-agent/integrations' (A1) + type _T13 = import('./index').SlackToolOptions; + + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + }); + + it('utility types moved to ./utils', () => { + // @ts-expect-error - moved to '@lousho/build-ai-agent/utils' (A1) + type _T1 = import('./index').EncryptionConfig; + // @ts-expect-error - moved to '@lousho/build-ai-agent/utils' (A1) + type _T2 = import('./index').DTOEncryptionSettings; + // @ts-expect-error - moved to '@lousho/build-ai-agent/utils' (A1) + type _T3 = import('./index').AuthorizationContext; + // @ts-expect-error - moved to '@lousho/build-ai-agent/utils' (A1) + type _T4 = import('./index').FileSystemAdapter; + // @ts-expect-error - moved to '@lousho/build-ai-agent/utils' (A1) + type _T5 = import('./index').PathAdapter; + // @ts-expect-error - moved to '@lousho/build-ai-agent/utils' (A1) + type _T6 = import('./index').IStorageService; + // @ts-expect-error - moved to '@lousho/build-ai-agent/utils' (A1) + type _T7 = import('./index').StorageConfig; + + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + expectTypeOf().not.toBeNever(); + }); +}); diff --git a/src/publicSurface.test.ts b/src/publicSurface.test.ts new file mode 100644 index 00000000..d240b95d --- /dev/null +++ b/src/publicSurface.test.ts @@ -0,0 +1,86 @@ +/** + * A1 (wave 4): pins the public-surface split across the package entries. + * Every name a breaking-change ticket moves or removes is asserted absent + * from the root (and, for a move, present on its subpath). Later tickets + * (A2a, A2b, A2c, A3, A5) append the names they remove or move here and in + * publicSurface.test-d.ts. + */ +import { describe, it, expect } from 'vitest'; +import * as root from './index'; +import * as flows from './flows'; +import * as integrations from './integrations'; +import * as utils from './utils'; + +type Entry = Record; + +/** [module, specifier, names] - each name must be on the module and OFF the root. */ +const MOVED: Array<[mod: Entry, specifier: string, names: string[]]> = [ + [ + flows as Entry, + '@lousho/build-ai-agent/flows', + [ + 'FlowBuilder', + 'FlowExecutor', + 'FlowChunkType', + 'extractVariableNames', + 'replaceVariablesInString', + 'injectVariables', + 'applyInputTransformation', + 'createDynamicZodSchemaForInputs', + 'validateFlowInput', + 'INPUT_TYPE_LABELS', + 'isCreateAgentResult', + 'validateFlow', + 'validateAgentDefinition', + ], + ], + [ + integrations as Entry, + '@lousho/build-ai-agent/integrations', + [ + 'createEmailTool', + 'createJiraTools', + 'JiraTools', + 'createGitHubTools', + 'GitHubTools', + 'createSlackTool', + 'slackTool', + 'postSlackAlert', + 'postSlackAlertViaSandbox', + 'buildSlackAlertPayload', + 'SLACK_WEBHOOK_URL_ENV_KEY', + ], + ], + [ + utils as Entry, + '@lousho/build-ai-agent/utils', + [ + 'EncryptionUtils', + 'DTOEncryptionFilter', + 'DecryptionError', + 'generatePassword', + 'sha256', + 'StorageService', + 'StorageServiceApprovalStore', + 'LocalStorageCheckpointStore', + 'validateWithSchema', + 'safeValidate', + 'isValidEmail', + 'isValidUrl', + 'isValidJson', + 'sanitizeString', + 'hasRequiredKeys', + ], + ], +]; + +describe('public surface (A1): moved values are on their subpath, off the root', () => { + for (const [mod, specifier, names] of MOVED) { + for (const name of names) { + it(`${name}: on ${specifier}, not on the root`, () => { + expect(mod, `${specifier} should export ${name}`).toHaveProperty(name); + expect(root, `the root should no longer export ${name}`).not.toHaveProperty(name); + }); + } + } +}); diff --git a/src/security/index.ts b/src/security/index.ts index c31509de..9623d6de 100644 --- a/src/security/index.ts +++ b/src/security/index.ts @@ -4,11 +4,8 @@ * Provides cryptographic utilities, sandboxing, and security-related functions */ -// Types -export * from './types'; - -// Crypto utilities -export * from './crypto'; +// A1: './types' and './crypto' moved to '@lousho/build-ai-agent/utils' +// (src/utils/index.ts re-exports them). // Sandboxing (LOU-F4/F5/F6) export * from './sandbox'; diff --git a/src/spec/specToAgent.ts b/src/spec/specToAgent.ts index 1088f803..2ee67662 100644 --- a/src/spec/specToAgent.ts +++ b/src/spec/specToAgent.ts @@ -65,9 +65,10 @@ export function resolveSpecTool(name: string): ToolDescriptor { if (CREDENTIALED_TOOLS.has(name)) { throw new ConfigurationError( - `specToAgent: tool '${name}' needs credentials (see src/tools/built-in/${name}.ts's ` + - `create${name === 'github' ? 'GitHub' : 'Jira'}Tools(config)) that an AgentSpec has no ` + - `field for. Build this agent with createAgent() directly and pass the configured tool instead.`, + `specToAgent: tool '${name}' needs credentials: build it with ` + + `create${name === 'github' ? 'GitHub' : 'Jira'}Tools(config) from ` + + `'@lousho/build-ai-agent/integrations' and pass it to createAgent(); an AgentSpec has no ` + + `field for credentials.`, 'tools', 'LOUSHO_TOOL_NEEDS_CREDENTIALS' ); diff --git a/src/storage/index.ts b/src/storage/index.ts deleted file mode 100644 index 861b837d..00000000 --- a/src/storage/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Storage module - * - * Provides file storage services with locking mechanism for concurrent access - */ - -// Types -export * from './types'; - -// Storage service -export * from './StorageService'; diff --git a/src/tools/built-in/index.ts b/src/tools/built-in/index.ts index b5b103c3..98304400 100644 --- a/src/tools/built-in/index.ts +++ b/src/tools/built-in/index.ts @@ -7,9 +7,7 @@ export * from './currentDate'; export * from './dayName'; export * from './http'; export { webFetchTool, createWebFetchTool, type WebFetchToolOptions, type WebFetchResult } from './webFetch'; -export * from './email'; -export * from './jira'; -export * from './github'; -export * from './slack'; +// A1: the credentialed integrations (email, jira, github, slack) moved to +// '@lousho/build-ai-agent/integrations' (src/integrations/index.ts). export * from './todo'; export { askQuestionTool, type AskQuestionInput, type AskQuestionResult } from './askQuestion'; diff --git a/src/types/index.ts b/src/types/index.ts index 81c63354..47f44d91 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,5 @@ export * from './common'; export * from './agent'; export * from './tool'; -export * from './flow'; +// A1: './flow' moved to '@lousho/build-ai-agent/flows'. export * from './repository'; diff --git a/src/utils/index.ts b/src/utils/index.ts index d45a2d5d..ad4e9ea9 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,10 +1,22 @@ /** - * Utility functions for the Open Agents Builder SDK + * Utilities (A1) + * The `@lousho/build-ai-agent/utils` subpath: encryption, the StorageService + * and the stores built on it, and the small validators. These used to ship + * on the package root; the error helpers (`errors`, `errorCodes`) stayed on + * the root and are NOT re-exported here. */ -// Error handling -export * from './errors'; +// Encryption utilities (src/security/crypto.ts) and their option types. +export * from '../security/crypto'; +export type { EncryptionConfig, DTOEncryptionSettings, AuthorizationContext } from '../security/types'; + +// File storage service and its contract. +export * from '../storage/StorageService'; +export * from '../storage/types'; + +// The two stores built on StorageService. +export { StorageServiceApprovalStore } from '../execution/ApprovalGate'; +export { LocalStorageCheckpointStore } from '../execution/checkpoint'; // Validators export * from './validators'; -export * from './errorCodes'; diff --git a/tsup.config.ts b/tsup.config.ts index be1fb8ef..7cf9f2b5 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -24,6 +24,8 @@ export default defineConfig({ 'tools/index': 'src/tools/index.ts', 'tools/mcp/index': 'src/tools/mcp/index.ts', 'flows/index': 'src/flows/index.ts', + 'integrations/index': 'src/integrations/index.ts', + 'utils/index': 'src/utils/index.ts', 'types/index': 'src/types/index.ts', 'testing/index': 'src/testing/index.ts', 'cli/dev': 'src/cli/dev.ts',