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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ dashboard — any provider, any deploy target, no lock-in.
- **Human-in-the-loop** — flag a tool `needsApproval` and pause execution until a human approves or rejects it, then `resumeAfterApproval()` from any process
- **Durable execution** — pass a `sessionId` + `checkpointStore` and a crash mid-conversation resumes instead of restarting
- **Cancellation** — pass an `AbortSignal` (`agent.send(input, { signal })`) to stop a run; it resolves with `finishReason: 'aborted'` and the transcript so far, and the signal reaches the provider, tools and delegated agents
- **Skills** — `defineSkill()` / `loadSkills('./skills')`: only each skill's name and description sit in the system prompt; the model loads the full markdown on demand through an auto-registered `load_skill` tool
- **Multi-agent delegation** — wrap a child agent as a tool with `createDelegateTool()`, with a `maxDepth` guard against delegation loops
- **Pre/post hooks** — a `HookRegistry` of `AgentHook`s that can inspect or mutate a tool call/LLM generate step, or throw to abort it, run sandboxed by Agent Forge's hook editor
- **Guardrails** — fail-closed, concurrently-run checks (secret scan, diff size, test/lint commands) that gate a fixer agent's patch before it's used
Expand Down Expand Up @@ -197,7 +196,6 @@ console.log(result.usage.totalTokens, result.finishReason, result.steps);
- [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
- [Testing](docs/testing.md) - unit-test agents deterministically with the scripted `mockModel`
- [Skills](docs/skills.md) - on-demand instructions: `defineSkill()`, `loadSkills()`, how they save context
- [Agent Forge](docs/agent-forge.md) - the visual dashboard (`loushy studio`): quickstart, first-agent walkthrough, hook authoring
- Full guides site: [linuxdevil.github.io/agent-sdk-docs](https://linuxdevil.github.io/agent-sdk-docs/)

Expand Down
50 changes: 43 additions & 7 deletions docs/api-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,6 @@ npm run docs:build # writes docs/api/index.html
| `resumeAfterApproval()` | Resume an execution paused for human approval. |
| `createDelegateTool()` | Wrap a child agent as a tool for multi-agent delegation. |

### Skills

Pass `skills: [defineSkill({ name, description, content }), ...(await loadSkills(dir))]` to
`createAgent()` or `AgentExecutor.execute()`: only names and descriptions go in
the system prompt and the model loads bodies through an auto-registered
`load_skill` tool. See [Skills](./skills.md).

### Cancellation

Pass an `AbortSignal` to stop a run: `agent.send(input, { signal })`,
Expand Down Expand Up @@ -132,6 +125,49 @@ import { loadMcpTools } from '@loushy/build-ai-agent/mcp';
const tools = await loadMcpTools(mcpClient, 'my-server');
```

**Schema support.** Each tool's JSON Schema `inputSchema` is converted to a
zod schema, and the model's arguments are validated against it before the
server is called. The converter handles `type` (including arrays such as
`["string", "null"]`), `enum` with mixed types, `const`, `anyOf` / `oneOf`
(a union; `anyOf: [X, { type: "null" }]` becomes `X.nullable()`), `allOf`
(merge / intersection), local `$ref` into `$defs` / `definitions`,
`properties` / `required`, `additionalProperties` (boolean or schema), `items`,
`default`, and the constraints `minimum`, `maximum`, `exclusiveMinimum`,
`exclusiveMaximum`, `minLength`, `maxLength`, `pattern`, `minItems` and
`maxItems`. Where JSON Schema is ambiguous the conversion is permissive:
unknown keywords, empty schemas, tuple `items` and unresolvable refs become
`z.any()`, a recursive `$ref` is expanded once and `z.any()` is used for the
inner occurrence, an invalid `pattern` regex is skipped, and objects keep
extra properties unless `additionalProperties` is `false`. The converter never
throws on schema content.

**Skipped tools.** If one tool still cannot be converted, only that tool is
skipped; the rest of the server's tools load. Pass a `logger` to receive a
warning naming the server, the tool and the reason, and `onSkip` to collect
what was left out:

```ts
import { loadMcpTools, type SkippedMcpTool } from '@loushy/build-ai-agent/mcp';

const skipped: SkippedMcpTool[] = [];
const tools = await loadMcpTools(mcpClient, 'my-server', {
logger: console,
onSkip: (tool) => skipped.push(tool), // { name, reason }
});
```

**Results.** An MCP result with `isError: true` is a normal tool failure: the
model receives `{ "error": "McpToolError", "toolName": "...", "message": "..." }`
where `message` is the server's text content. Successful results are
JSON-serializable: if the server returns `structuredContent` it is the result
object; otherwise the result is `{ text, content }`, where `text` joins all
text parts and `content` keeps every part in order (`text`, `image`, `audio`,
`resource`, `resource_link`; an unrecognised part type is kept as
`{ type: 'unknown', raw }`). When `structuredContent` arrives together with
non-text parts the result is `{ structuredContent, text, content }` with
`content` holding only the non-text parts, so images, audio and resources are
never dropped.

### Tool errors

A tool call can fail in two ways. In both, the run continues and the model
Expand Down
106 changes: 0 additions & 106 deletions docs/skills.md

This file was deleted.

11 changes: 0 additions & 11 deletions src/createAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { LLMProvider } from './providers/llm';
import { ToolRegistry } from './tools/ToolRegistry';
import { ToolDescriptor } from './types';
import type { DefinedTool } from './tools/defineTool';
import type { Skill } from './skills/defineSkill';

/**
* Configuration for createAgent(). Tools are keyed by the name the agent
Expand All @@ -42,15 +41,6 @@ export interface CreateAgentConfig {
* createAgent({ prompt: '...', provider, tools: [sendEmail] });
*/
tools?: readonly DefinedTool[] | Record<string, ToolDescriptor>;
/**
* Optional skills (LOU-Y2): only name + description go in the system
* prompt; the model loads a skill's full content with the auto-registered
* `load_skill` tool. Build them with `defineSkill()` or `loadSkills()`.
*
* @example
* createAgent({ prompt: '...', provider, skills: await loadSkills('./skills') });
*/
skills?: readonly Skill[];
/** Optional agent name; defaults to 'agent'. */
name?: string;
/** Optional maxSteps passed through to AgentExecutor.execute(). */
Expand Down Expand Up @@ -101,7 +91,6 @@ export function createAgent(config: CreateAgentConfig): SimpleAgent {
input: message,
provider: config.provider,
toolRegistry,
skills: config.skills,
maxSteps: config.maxSteps,
signal: options.signal,
});
Expand Down
18 changes: 1 addition & 17 deletions src/execution/AgentExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
* Executes agents with streaming support and tool calling
*/

import type { Skill } from '../skills/defineSkill';
import { withSkills } from '../skills/withSkills';
import { nanoid } from 'nanoid';
import { LLMProvider, Message, ToolCall, GenerateOptions, GenerateResult, ToolDefinition } from '../providers';
import { AgentConfig } from '../types';
Expand Down Expand Up @@ -105,16 +103,6 @@ export interface ExecuteOptions {
input: string | Message[];
provider: LLMProvider;
toolRegistry?: ToolRegistry;
/**
* Skills (LOU-Y2): instructions the model loads on demand. Their names and
* descriptions are appended to the system prompt and a `load_skill` tool is
* registered; bodies only enter the conversation when the model loads them.
* Throws if a tool named `load_skill` is already registered.
*
* @example
* AgentExecutor.execute({ agent, input, provider, skills: [defineSkill({ name, description, content })] });
*/
skills?: readonly Skill[];
streaming?: boolean;
maxSteps?: number;
temperature?: number;
Expand Down Expand Up @@ -343,11 +331,7 @@ export class AgentExecutor {
exporter,
'agent.run',
{ input: typeof input === 'string' ? input : JSON.stringify(input) },
async (agentSpan) =>
this.runAgentLoop(
{ ...options, ...withSkills(options.agent, options.toolRegistry, options.skills) },
agentSpan.id
),
async (agentSpan) => this.runAgentLoop(options, agentSpan.id),
undefined
);
}
Expand Down
3 changes: 0 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ export * from './utils';
// createAgent() convenience API (LOU-H1)
export * from './createAgent';

// Skills: progressive disclosure of instructions (LOU-Y2)
export * from './skills';

// Declarative agent spec file format (LOU-H9)
export * from './spec';

Expand Down
80 changes: 0 additions & 80 deletions src/skills/defineSkill.ts

This file was deleted.

3 changes: 0 additions & 3 deletions src/skills/index.ts

This file was deleted.

Loading
Loading