@agentloop/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +287 -0
- package/dist/index.cjs +1168 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +825 -0
- package/dist/index.d.mts +825 -0
- package/dist/index.mjs +1152 -0
- package/dist/index.mjs.map +1 -0
- package/dist/test.cjs +7 -0
- package/dist/test.cjs.map +1 -0
- package/dist/test.d.cts +5 -0
- package/dist/test.d.mts +5 -0
- package/dist/test.mjs +6 -0
- package/dist/test.mjs.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# @agentloop/core
|
|
2
|
+
|
|
3
|
+
Core library for agentloop — the minimal, composable agent loop for TypeScript.
|
|
4
|
+
|
|
5
|
+
This package provides the agent loop, tools, policies, observers, messages, content types, and all foundational interfaces. It has zero production dependencies.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @agentloop/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { defineAgent, defineTool, definePolicy, defineObserver } from "@agentloop/core";
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
You'll also need a provider package (e.g. `@agentloop/anthropic`) to create a model instance.
|
|
20
|
+
|
|
21
|
+
## Agent
|
|
22
|
+
|
|
23
|
+
Create an agent with `defineAgent` and run it with `.run()` or `.stream()`:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { defineAgent } from "@agentloop/core";
|
|
27
|
+
import { createAnthropic } from "@agentloop/anthropic";
|
|
28
|
+
|
|
29
|
+
const provider = createAnthropic();
|
|
30
|
+
const model = provider.model("claude-sonnet-4-6", { maxTokens: 4096 });
|
|
31
|
+
|
|
32
|
+
const agent = defineAgent({
|
|
33
|
+
name: "assistant",
|
|
34
|
+
model,
|
|
35
|
+
instructions: "You are a helpful assistant.",
|
|
36
|
+
tools: [myTool],
|
|
37
|
+
policies: [myPolicy],
|
|
38
|
+
observers: [myObserver],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Run to completion
|
|
42
|
+
const result = await agent.run("What is 2 + 2?");
|
|
43
|
+
console.log(result.text);
|
|
44
|
+
console.log(result.usage); // { inputTokens, outputTokens, totalTokens }
|
|
45
|
+
console.log(result.steps); // number of model calls
|
|
46
|
+
console.log(result.duration); // wall-clock ms
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Streaming
|
|
50
|
+
|
|
51
|
+
Stream observer events as they happen:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const stream = agent.stream("Write a poem.");
|
|
55
|
+
for await (const event of stream) {
|
|
56
|
+
if (event.type === "textDelta") {
|
|
57
|
+
process.stdout.write(event.text);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const result = await stream.result;
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Structured output
|
|
64
|
+
|
|
65
|
+
Pass an `output` schema to get typed, validated results:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { z } from "zod";
|
|
69
|
+
|
|
70
|
+
const schema = z.object({
|
|
71
|
+
answer: z.string(),
|
|
72
|
+
confidence: z.number(),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const result = await agent.run("What year was TypeScript released?", {
|
|
76
|
+
output: schema,
|
|
77
|
+
});
|
|
78
|
+
console.log(result.object); // { answer: "2012", confidence: 0.95 }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Run options
|
|
82
|
+
|
|
83
|
+
Override agent defaults per-run:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
const result = await agent.run("Hello", {
|
|
87
|
+
transcript: existingMessages, // continue a conversation
|
|
88
|
+
observers: [extraObserver], // additional observers
|
|
89
|
+
policies: [extraPolicy], // additional policies
|
|
90
|
+
output: schema, // structured output schema
|
|
91
|
+
state: { userId: "123" }, // run-scoped state
|
|
92
|
+
signal: abortController.signal, // cooperative cancellation
|
|
93
|
+
maxTokens: 2048, // model config overrides
|
|
94
|
+
temperature: 0.5,
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Agent nesting
|
|
99
|
+
|
|
100
|
+
Convert an agent into a tool for use by another agent:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
const researchTool = researchAgent.asTool({
|
|
104
|
+
name: "research",
|
|
105
|
+
description: "Research a topic.",
|
|
106
|
+
schema: z.object({ topic: z.string() }),
|
|
107
|
+
prompt: (args) => `Research: ${args.topic}`,
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Tools
|
|
112
|
+
|
|
113
|
+
Tools let the model call your functions. Define them with a schema and an execute function:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { defineTool } from "@agentloop/core";
|
|
117
|
+
import { z } from "zod";
|
|
118
|
+
|
|
119
|
+
const getWeather = defineTool({
|
|
120
|
+
name: "get_weather",
|
|
121
|
+
description: "Get weather for a city.",
|
|
122
|
+
schema: z.object({
|
|
123
|
+
city: z.string().describe("The city name"),
|
|
124
|
+
}),
|
|
125
|
+
async execute(args, ctx) {
|
|
126
|
+
ctx.update({ status: "fetching" }); // progress update
|
|
127
|
+
return { city: args.city, temp: 22 };
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The schema can be from Zod, Valibot, ArkType, or any library implementing [Standard Schema v1](https://github.com/standard-schema/standard-schema).
|
|
133
|
+
|
|
134
|
+
### Return types
|
|
135
|
+
|
|
136
|
+
Tools can return a `string`, a `Record<string, unknown>` (auto-wrapped as JSON), or a `Content[]` array.
|
|
137
|
+
|
|
138
|
+
### Lifecycle hooks
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const guarded = defineTool({
|
|
142
|
+
name: "delete_file",
|
|
143
|
+
description: "Delete a file.",
|
|
144
|
+
schema: z.object({ path: z.string() }),
|
|
145
|
+
before(args, ctx) {
|
|
146
|
+
if (args.path.startsWith("/etc")) {
|
|
147
|
+
return { action: "skip", reason: "Protected path" };
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
async execute(args) {
|
|
151
|
+
// ...
|
|
152
|
+
return `Deleted ${args.path}`;
|
|
153
|
+
},
|
|
154
|
+
after(args, output, ctx) {
|
|
155
|
+
// inspect or rewrite output
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Policies
|
|
161
|
+
|
|
162
|
+
Policies control the agent loop at five decision points:
|
|
163
|
+
|
|
164
|
+
| Hook | When | Available actions |
|
|
165
|
+
| --------------- | ---------------------------------- | -------------------------- |
|
|
166
|
+
| `beforeStep` | Before each model call | `stop` |
|
|
167
|
+
| `afterResponse` | After model responds, before tools | `stop`, `replace`, `retry` |
|
|
168
|
+
| `beforeToolRun` | Before each tool execution | `skip`, `stop` |
|
|
169
|
+
| `afterToolRun` | After each tool execution | `rewrite`, `retry`, `stop` |
|
|
170
|
+
| `afterStep` | After complete step | `stop`, `retry`, `inject` |
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { definePolicy } from "@agentloop/core";
|
|
174
|
+
|
|
175
|
+
const tokenBudget = definePolicy({
|
|
176
|
+
name: "token-budget",
|
|
177
|
+
afterResponse(ctx, info) {
|
|
178
|
+
if (info.totalUsage.totalTokens > 100_000) {
|
|
179
|
+
return { action: "stop", reason: "Token budget exceeded" };
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Policies are evaluated in order. The first policy to return an action short-circuits — later policies are skipped.
|
|
186
|
+
|
|
187
|
+
## Observers
|
|
188
|
+
|
|
189
|
+
Observers watch the loop and react to events. They don't affect control flow.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import { defineObserver } from "@agentloop/core";
|
|
193
|
+
|
|
194
|
+
const logger = defineObserver({
|
|
195
|
+
name: "logger",
|
|
196
|
+
onRunStart(event) {
|
|
197
|
+
console.log(`Run started: model=${event.model}`);
|
|
198
|
+
},
|
|
199
|
+
onTextDelta(event) {
|
|
200
|
+
process.stdout.write(event.text);
|
|
201
|
+
},
|
|
202
|
+
onToolRunEnd(event) {
|
|
203
|
+
console.log(`${event.name}: ${event.duration}ms`);
|
|
204
|
+
},
|
|
205
|
+
onRunFinish(event) {
|
|
206
|
+
console.log(`${event.steps} steps, ${event.usage.totalTokens} tokens`);
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Event types
|
|
212
|
+
|
|
213
|
+
Text: `textStart`, `textDelta`, `textStop`
|
|
214
|
+
Thinking: `thinkingStart`, `thinkingDelta`, `thinkingStop`
|
|
215
|
+
Tool calls: `toolCallStart`, `toolCallDelta`, `toolCallStop`
|
|
216
|
+
Tool execution: `toolRunStart`, `toolRunUpdate`, `toolRunEnd`, `toolSkip`
|
|
217
|
+
Steps: `stepStart`, `stepRetry`, `stepFinish`
|
|
218
|
+
Response: `responseFinish`
|
|
219
|
+
Run: `runStart`, `runFinish`
|
|
220
|
+
Errors: `abort`, `error`
|
|
221
|
+
|
|
222
|
+
Use typed `on*` handlers for specific events or a catch-all `handler(event)` for everything.
|
|
223
|
+
|
|
224
|
+
## Messages and content
|
|
225
|
+
|
|
226
|
+
### Messages
|
|
227
|
+
|
|
228
|
+
Four message types: `SystemMessage`, `UserMessage`, `AssistantMessage`, `ToolMessage`.
|
|
229
|
+
|
|
230
|
+
Helper constructors:
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { system, user, assistant } from "@agentloop/core";
|
|
234
|
+
|
|
235
|
+
const messages = [system("You are helpful."), user("Hello!")];
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Content parts
|
|
239
|
+
|
|
240
|
+
Messages contain typed content parts:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import { text, json, blob, url } from "@agentloop/core";
|
|
244
|
+
|
|
245
|
+
// Text content
|
|
246
|
+
text("Hello world");
|
|
247
|
+
|
|
248
|
+
// Structured JSON
|
|
249
|
+
json({ key: "value" });
|
|
250
|
+
|
|
251
|
+
// Binary data (images, audio, etc.)
|
|
252
|
+
blob(uint8Array, "image/png");
|
|
253
|
+
|
|
254
|
+
// External URL
|
|
255
|
+
url("https://example.com/image.png", "image/png");
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Prompt type
|
|
259
|
+
|
|
260
|
+
`Prompt` is a flexible input type — it accepts a `string`, a single `Message`, or a `Message[]` array. Use `normalizePrompt()` to convert any prompt to a `Message[]`.
|
|
261
|
+
|
|
262
|
+
## Model and Provider
|
|
263
|
+
|
|
264
|
+
The `Provider` interface creates `Model` instances. The `Model` interface streams responses:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
interface Provider {
|
|
268
|
+
model(name: string, config?: ModelConfig): Model;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
interface Model {
|
|
272
|
+
name: string;
|
|
273
|
+
stream(options: {
|
|
274
|
+
messages: Message[];
|
|
275
|
+
tools?: ToolDefinition[];
|
|
276
|
+
config?: ModelConfig;
|
|
277
|
+
output?: Schema;
|
|
278
|
+
signal?: AbortSignal;
|
|
279
|
+
}): AsyncIterable<StreamPart>;
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Implement these interfaces to add support for any LLM provider.
|
|
284
|
+
|
|
285
|
+
## License
|
|
286
|
+
|
|
287
|
+
MIT
|