@electric-ax/agents 0.2.3 → 0.3.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/dist/entrypoint.js +474 -737
- package/dist/index.cjs +470 -733
- package/dist/index.d.cts +68 -35
- package/dist/index.d.ts +69 -36
- package/dist/index.js +489 -751
- package/docs/entities/agents/horton.md +12 -12
- package/docs/entities/agents/worker.md +18 -18
- package/docs/entities/patterns/blackboard.md +6 -6
- package/docs/entities/patterns/dispatcher.md +1 -1
- package/docs/entities/patterns/manager-worker.md +1 -1
- package/docs/entities/patterns/map-reduce.md +1 -1
- package/docs/entities/patterns/pipeline.md +1 -1
- package/docs/entities/patterns/reactive-observers.md +2 -2
- package/docs/examples/playground.md +42 -26
- package/docs/index.md +25 -23
- package/docs/quickstart.md +12 -12
- package/docs/reference/agent-config.md +20 -12
- package/docs/reference/agent-tool.md +1 -1
- package/docs/reference/built-in-collections.md +21 -21
- package/docs/reference/cli.md +39 -30
- package/docs/reference/entity-definition.md +9 -9
- package/docs/reference/entity-handle.md +2 -2
- package/docs/reference/entity-registry.md +1 -1
- package/docs/reference/handler-context.md +34 -18
- package/docs/reference/mcp-registry.md +189 -0
- package/docs/reference/mcp-server-config.md +226 -0
- package/docs/reference/runtime-handler.md +25 -23
- package/docs/reference/shared-state-handle.md +7 -7
- package/docs/reference/state-collection-proxy.md +1 -1
- package/docs/reference/wake-event.md +23 -23
- package/docs/usage/app-setup.md +24 -23
- package/docs/usage/clients-and-react.md +40 -36
- package/docs/usage/configuring-the-agent.md +25 -19
- package/docs/usage/context-composition.md +12 -12
- package/docs/usage/defining-entities.md +36 -36
- package/docs/usage/defining-tools.md +45 -45
- package/docs/usage/embedded-builtins.md +54 -43
- package/docs/usage/managing-state.md +12 -12
- package/docs/usage/mcp-servers.md +354 -0
- package/docs/usage/overview.md +50 -45
- package/docs/usage/programmatic-runtime-client.md +51 -48
- package/docs/usage/shared-state.md +32 -32
- package/docs/usage/spawning-and-coordinating.md +9 -9
- package/docs/usage/testing.md +14 -14
- package/docs/usage/waking-entities.md +13 -13
- package/docs/usage/writing-handlers.md +52 -26
- package/package.json +9 -4
- package/scripts/sync-docs.mjs +42 -0
- package/docs/examples/mega-draw.md +0 -106
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Defining tools
|
|
3
|
-
titleTemplate:
|
|
3
|
+
titleTemplate: "... - Electric Agents"
|
|
4
4
|
description: >-
|
|
5
5
|
Create stateless, stateful, and handler-scoped tools for the LLM agent loop.
|
|
6
6
|
outline: [2, 3]
|
|
@@ -33,7 +33,7 @@ The return type:
|
|
|
33
33
|
|
|
34
34
|
```ts
|
|
35
35
|
interface AgentToolResult<T = any> {
|
|
36
|
-
content: Array<{ type:
|
|
36
|
+
content: Array<{ type: "text"; text: string }>
|
|
37
37
|
details: T
|
|
38
38
|
}
|
|
39
39
|
```
|
|
@@ -43,11 +43,11 @@ interface AgentToolResult<T = any> {
|
|
|
43
43
|
Defined using [TypeBox](https://github.com/sinclairzx81/typebox) (`@sinclair/typebox`). The schema is used for LLM function calling and argument validation.
|
|
44
44
|
|
|
45
45
|
```ts
|
|
46
|
-
import { Type } from
|
|
46
|
+
import { Type } from "@sinclair/typebox"
|
|
47
47
|
|
|
48
48
|
parameters: Type.Object({
|
|
49
|
-
expression: Type.String({ description:
|
|
50
|
-
precision: Type.Optional(Type.Number({ description:
|
|
49
|
+
expression: Type.String({ description: "Math expression to evaluate" }),
|
|
50
|
+
precision: Type.Optional(Type.Number({ description: "Decimal places" })),
|
|
51
51
|
})
|
|
52
52
|
```
|
|
53
53
|
|
|
@@ -56,21 +56,21 @@ parameters: Type.Object({
|
|
|
56
56
|
Pure functions with no side effects beyond what they compute. Define directly as an `AgentTool` object.
|
|
57
57
|
|
|
58
58
|
```ts
|
|
59
|
-
import { Type } from
|
|
60
|
-
import type { AgentTool } from
|
|
59
|
+
import { Type } from "@sinclair/typebox"
|
|
60
|
+
import type { AgentTool } from "@electric-ax/agents-runtime"
|
|
61
61
|
|
|
62
62
|
const calculatorTool: AgentTool = {
|
|
63
|
-
name:
|
|
64
|
-
label:
|
|
65
|
-
description:
|
|
63
|
+
name: "calculator",
|
|
64
|
+
label: "Calculator",
|
|
65
|
+
description: "Evaluate mathematical expressions.",
|
|
66
66
|
parameters: Type.Object({
|
|
67
|
-
expression: Type.String({ description:
|
|
67
|
+
expression: Type.String({ description: "The expression to evaluate" }),
|
|
68
68
|
}),
|
|
69
69
|
execute: async (_toolCallId, params) => {
|
|
70
70
|
const { expression } = params as { expression: string }
|
|
71
71
|
const result = evaluate(expression)
|
|
72
72
|
return {
|
|
73
|
-
content: [{ type:
|
|
73
|
+
content: [{ type: "text", text: String(result) }],
|
|
74
74
|
details: {},
|
|
75
75
|
}
|
|
76
76
|
},
|
|
@@ -82,20 +82,20 @@ const calculatorTool: AgentTool = {
|
|
|
82
82
|
Use a factory function that receives the `HandlerContext`. The state persists across wakes -- it is backed by the entity's durable stream. Reads go through `ctx.db.collections` and writes go through `ctx.db.actions`.
|
|
83
83
|
|
|
84
84
|
```ts
|
|
85
|
-
import { Type } from
|
|
86
|
-
import type { AgentTool, HandlerContext } from
|
|
85
|
+
import { Type } from "@sinclair/typebox"
|
|
86
|
+
import type { AgentTool, HandlerContext } from "@electric-ax/agents-runtime"
|
|
87
87
|
|
|
88
88
|
function createMemoryStoreTool(ctx: HandlerContext): AgentTool {
|
|
89
89
|
return {
|
|
90
|
-
name:
|
|
91
|
-
label:
|
|
92
|
-
description:
|
|
90
|
+
name: "memory_store",
|
|
91
|
+
label: "Memory Store",
|
|
92
|
+
description: "Persistent key-value store.",
|
|
93
93
|
parameters: Type.Object({
|
|
94
94
|
operation: Type.Union([
|
|
95
|
-
Type.Literal(
|
|
96
|
-
Type.Literal(
|
|
97
|
-
Type.Literal(
|
|
98
|
-
Type.Literal(
|
|
95
|
+
Type.Literal("get"),
|
|
96
|
+
Type.Literal("set"),
|
|
97
|
+
Type.Literal("delete"),
|
|
98
|
+
Type.Literal("list"),
|
|
99
99
|
]),
|
|
100
100
|
key: Type.Optional(Type.String()),
|
|
101
101
|
value: Type.Optional(Type.String()),
|
|
@@ -106,7 +106,7 @@ function createMemoryStoreTool(ctx: HandlerContext): AgentTool {
|
|
|
106
106
|
key?: string
|
|
107
107
|
value?: string
|
|
108
108
|
}
|
|
109
|
-
if (operation ===
|
|
109
|
+
if (operation === "set") {
|
|
110
110
|
const existing = ctx.db.collections.kv?.get(key!)
|
|
111
111
|
if (existing) {
|
|
112
112
|
ctx.db.actions.kv_update({
|
|
@@ -119,27 +119,27 @@ function createMemoryStoreTool(ctx: HandlerContext): AgentTool {
|
|
|
119
119
|
ctx.db.actions.kv_insert({ row: { key: key!, value: value! } })
|
|
120
120
|
}
|
|
121
121
|
return {
|
|
122
|
-
content: [{ type:
|
|
122
|
+
content: [{ type: "text", text: `Stored "${key}"` }],
|
|
123
123
|
details: {},
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
if (operation ===
|
|
126
|
+
if (operation === "get") {
|
|
127
127
|
const entry = ctx.db.collections.kv?.get(key!)
|
|
128
128
|
const text = entry ? entry.value : `No value found for "${key}"`
|
|
129
|
-
return { content: [{ type:
|
|
129
|
+
return { content: [{ type: "text", text }], details: {} }
|
|
130
130
|
}
|
|
131
|
-
if (operation ===
|
|
131
|
+
if (operation === "delete") {
|
|
132
132
|
ctx.db.actions.kv_delete({ key: key! })
|
|
133
133
|
return {
|
|
134
|
-
content: [{ type:
|
|
134
|
+
content: [{ type: "text", text: `Deleted "${key}"` }],
|
|
135
135
|
details: {},
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
// list
|
|
139
139
|
const entries = ctx.db.collections.kv?.toArray ?? []
|
|
140
|
-
const text = entries.map((e) => `${e.key}: ${e.value}`).join(
|
|
140
|
+
const text = entries.map((e) => `${e.key}: ${e.value}`).join("\n")
|
|
141
141
|
return {
|
|
142
|
-
content: [{ type:
|
|
142
|
+
content: [{ type: "text", text: text || "(empty)" }],
|
|
143
143
|
details: {},
|
|
144
144
|
}
|
|
145
145
|
},
|
|
@@ -162,18 +162,18 @@ The entity state API:
|
|
|
162
162
|
Use a factory that receives the `HandlerContext`. These tools can spawn entities, observe streams, send messages, and use any other `ctx` primitive.
|
|
163
163
|
|
|
164
164
|
```ts
|
|
165
|
-
import { Type } from
|
|
166
|
-
import type { AgentTool, HandlerContext } from
|
|
165
|
+
import { Type } from "@sinclair/typebox"
|
|
166
|
+
import type { AgentTool, HandlerContext } from "@electric-ax/agents-runtime"
|
|
167
167
|
|
|
168
168
|
function createDispatchTool(ctx: HandlerContext): AgentTool {
|
|
169
169
|
return {
|
|
170
|
-
name:
|
|
171
|
-
label:
|
|
172
|
-
description:
|
|
170
|
+
name: "dispatch",
|
|
171
|
+
label: "Dispatch",
|
|
172
|
+
description: "Spawn a child agent and wait for its response.",
|
|
173
173
|
parameters: Type.Object({
|
|
174
|
-
type: Type.String({ description:
|
|
175
|
-
systemPrompt: Type.String({ description:
|
|
176
|
-
task: Type.String({ description:
|
|
174
|
+
type: Type.String({ description: "Entity type to spawn" }),
|
|
175
|
+
systemPrompt: Type.String({ description: "System prompt for the child" }),
|
|
176
|
+
task: Type.String({ description: "Task to send to the child" }),
|
|
177
177
|
}),
|
|
178
178
|
execute: async (_, params) => {
|
|
179
179
|
const { type, systemPrompt, task } = params as {
|
|
@@ -187,12 +187,12 @@ function createDispatchTool(ctx: HandlerContext): AgentTool {
|
|
|
187
187
|
{ systemPrompt },
|
|
188
188
|
{
|
|
189
189
|
initialMessage: task,
|
|
190
|
-
wake:
|
|
190
|
+
wake: "runFinished",
|
|
191
191
|
}
|
|
192
192
|
)
|
|
193
|
-
const text = (await child.text()).join(
|
|
193
|
+
const text = (await child.text()).join("\n\n")
|
|
194
194
|
return {
|
|
195
|
-
content: [{ type:
|
|
195
|
+
content: [{ type: "text", text }],
|
|
196
196
|
details: {},
|
|
197
197
|
}
|
|
198
198
|
},
|
|
@@ -207,18 +207,18 @@ function createDispatchTool(ctx: HandlerContext): AgentTool {
|
|
|
207
207
|
Tools are constructed in the handler and passed to `useAgent`. Include `ctx.electricTools` when your runtime host provides runtime-level tools that the LLM should be able to call:
|
|
208
208
|
|
|
209
209
|
```ts
|
|
210
|
-
registry.define(
|
|
211
|
-
description:
|
|
210
|
+
registry.define("assistant", {
|
|
211
|
+
description: "An assistant with memory and delegation",
|
|
212
212
|
state: {
|
|
213
|
-
kv: { primaryKey:
|
|
213
|
+
kv: { primaryKey: "key" },
|
|
214
214
|
},
|
|
215
215
|
async handler(ctx) {
|
|
216
216
|
const memoryTool = createMemoryStoreTool(ctx)
|
|
217
217
|
const dispatchTool = createDispatchTool(ctx)
|
|
218
218
|
|
|
219
219
|
ctx.useAgent({
|
|
220
|
-
systemPrompt:
|
|
221
|
-
model:
|
|
220
|
+
systemPrompt: "You are a helpful assistant with persistent memory.",
|
|
221
|
+
model: "claude-sonnet-4-5-20250929",
|
|
222
222
|
tools: [...ctx.electricTools, memoryTool, dispatchTool, calculatorTool],
|
|
223
223
|
})
|
|
224
224
|
await ctx.agent.run()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Embedded built-ins
|
|
3
|
-
titleTemplate:
|
|
3
|
+
titleTemplate: "... - Electric Agents"
|
|
4
4
|
description: >-
|
|
5
5
|
Embed the built-in Horton and worker runtime in your own process using
|
|
6
6
|
@electric-ax/agents, BuiltinAgentsServer, or the entrypoint helpers.
|
|
@@ -16,10 +16,10 @@ The CLI commands `electric agents start-builtin` and `electric agents quickstart
|
|
|
16
16
|
`BuiltinAgentsServer` starts an HTTP webhook server, registers `horton` and `worker`, and forwards Electric Agents webhook wakes to the built-in handler.
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
|
-
import { BuiltinAgentsServer } from
|
|
19
|
+
import { BuiltinAgentsServer } from "@electric-ax/agents"
|
|
20
20
|
|
|
21
21
|
const server = new BuiltinAgentsServer({
|
|
22
|
-
agentServerUrl:
|
|
22
|
+
agentServerUrl: "http://localhost:4437",
|
|
23
23
|
port: 4448,
|
|
24
24
|
workingDirectory: process.cwd(),
|
|
25
25
|
})
|
|
@@ -36,9 +36,9 @@ await server.stop()
|
|
|
36
36
|
### Options
|
|
37
37
|
|
|
38
38
|
```ts
|
|
39
|
-
import type { RuntimeRouterConfig } from
|
|
39
|
+
import type { RuntimeRouterConfig } from "@electric-ax/agents-runtime"
|
|
40
40
|
|
|
41
|
-
type CreateElectricTools = RuntimeRouterConfig[
|
|
41
|
+
type CreateElectricTools = RuntimeRouterConfig["createElectricTools"]
|
|
42
42
|
|
|
43
43
|
interface BuiltinAgentsServerOptions {
|
|
44
44
|
agentServerUrl: string
|
|
@@ -49,19 +49,30 @@ interface BuiltinAgentsServerOptions {
|
|
|
49
49
|
mockStreamFn?: StreamFn
|
|
50
50
|
webhookPath?: string
|
|
51
51
|
createElectricTools?: CreateElectricTools
|
|
52
|
+
// MCP integration
|
|
53
|
+
extraMcpServers?: ReadonlyArray<McpServerConfig>
|
|
54
|
+
loadProjectMcpConfig?: boolean
|
|
55
|
+
mcpOAuthRedirectBase?: string
|
|
56
|
+
openAuthorizeUrl?: (url: string, server: string) => void
|
|
57
|
+
onConfigError?: (error: unknown) => void
|
|
52
58
|
}
|
|
53
59
|
```
|
|
54
60
|
|
|
55
|
-
| Field
|
|
56
|
-
|
|
|
57
|
-
| `agentServerUrl`
|
|
58
|
-
| `baseUrl`
|
|
59
|
-
| `port`
|
|
60
|
-
| `host`
|
|
61
|
-
| `workingDirectory`
|
|
62
|
-
| `mockStreamFn`
|
|
63
|
-
| `webhookPath`
|
|
64
|
-
| `createElectricTools`
|
|
61
|
+
| Field | Description |
|
|
62
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
63
|
+
| `agentServerUrl` | Electric Agents coordinator server URL. |
|
|
64
|
+
| `baseUrl` | Public base URL used when registering the webhook. Defaults to local URL. |
|
|
65
|
+
| `port` | Local webhook server port. |
|
|
66
|
+
| `host` | Bind host. Defaults to `127.0.0.1`. |
|
|
67
|
+
| `workingDirectory` | Directory used by Horton and worker file tools. Defaults to `process.cwd()`. |
|
|
68
|
+
| `mockStreamFn` | Optional test stream function. Lets you run without `ANTHROPIC_API_KEY`. |
|
|
69
|
+
| `webhookPath` | Webhook path. Defaults to `/_electric/builtin-agent-handler`. |
|
|
70
|
+
| `createElectricTools` | Optional factory for extra tools injected into built-in agent handlers. |
|
|
71
|
+
| `extraMcpServers` | MCP servers contributed by the embedder. On name conflict with `mcp.json`, `mcp.json` wins. `authorizationCode` servers are auto-wired with `keychainPersistence`. |
|
|
72
|
+
| `loadProjectMcpConfig` | Load `<workingDirectory>/mcp.json` (and watch it). Off by default — stdio MCP servers can spawn local commands, so the embedder must opt in. The Electron desktop and `electric-ax` CLI opt in. |
|
|
73
|
+
| `mcpOAuthRedirectBase` | Base for OAuth redirect URIs (full URI is `<base>/oauth/callback/<server-name>`). MUST be stable across restarts so DCR client info stays valid; required when listening on `port: 0`. The runtime never listens at this URI — the embedder intercepts the redirect. |
|
|
74
|
+
| `openAuthorizeUrl` | Hook invoked when an `authorizationCode` MCP server first needs user consent. Receives the SDK-generated authorize URL. The desktop opens it in a sandboxed `BrowserWindow`; headless embedders can read the URL from the `authenticating` envelope of `addServer` and surface it themselves. |
|
|
75
|
+
| `onConfigError` | Invoked when applying an MCP config (initial boot or watcher reload) fails. Errors are always logged; this hook is for surfacing them programmatically. |
|
|
65
76
|
|
|
66
77
|
Without `mockStreamFn`, `ANTHROPIC_API_KEY` must be present before the built-in handler starts.
|
|
67
78
|
|
|
@@ -73,16 +84,16 @@ Use `createBuiltinAgentHandler()` when you already have an HTTP server and only
|
|
|
73
84
|
import {
|
|
74
85
|
createBuiltinAgentHandler,
|
|
75
86
|
registerBuiltinAgentTypes,
|
|
76
|
-
} from
|
|
87
|
+
} from "@electric-ax/agents"
|
|
77
88
|
|
|
78
89
|
const bootstrap = await createBuiltinAgentHandler({
|
|
79
|
-
agentServerUrl:
|
|
80
|
-
serveEndpoint:
|
|
90
|
+
agentServerUrl: "http://localhost:4437",
|
|
91
|
+
serveEndpoint: "https://example.com/_electric/builtin-agent-handler",
|
|
81
92
|
workingDirectory: process.cwd(),
|
|
82
93
|
})
|
|
83
94
|
|
|
84
95
|
if (!bootstrap) {
|
|
85
|
-
throw new Error(
|
|
96
|
+
throw new Error("ANTHROPIC_API_KEY is required for built-in agents")
|
|
86
97
|
}
|
|
87
98
|
|
|
88
99
|
await registerBuiltinAgentTypes(bootstrap)
|
|
@@ -108,27 +119,27 @@ interface AgentHandlerResult {
|
|
|
108
119
|
Both `BuiltinAgentsServer` and `createBuiltinAgentHandler()` accept `createElectricTools`. The factory receives the same context shape as `RuntimeRouterConfig.createElectricTools` and can add host-specific tools to Horton.
|
|
109
120
|
|
|
110
121
|
```ts
|
|
111
|
-
import { Type } from
|
|
122
|
+
import { Type } from "@sinclair/typebox"
|
|
112
123
|
|
|
113
124
|
const server = new BuiltinAgentsServer({
|
|
114
|
-
agentServerUrl:
|
|
125
|
+
agentServerUrl: "http://localhost:4437",
|
|
115
126
|
port: 4448,
|
|
116
127
|
createElectricTools: ({ entityUrl, upsertCronSchedule }) => [
|
|
117
128
|
{
|
|
118
|
-
name:
|
|
119
|
-
label:
|
|
120
|
-
description:
|
|
129
|
+
name: "schedule_daily_summary",
|
|
130
|
+
label: "Schedule daily summary",
|
|
131
|
+
description: "Schedule a daily summary wake for this entity.",
|
|
121
132
|
parameters: Type.Object({
|
|
122
133
|
hour: Type.Number(),
|
|
123
134
|
}),
|
|
124
135
|
execute: async (_id, params) => {
|
|
125
136
|
const { hour } = params as { hour: number }
|
|
126
137
|
await upsertCronSchedule({
|
|
127
|
-
id:
|
|
138
|
+
id: "daily-summary",
|
|
128
139
|
expression: `0 ${hour} * * *`,
|
|
129
140
|
payload: `Run daily summary for ${entityUrl}`,
|
|
130
141
|
})
|
|
131
|
-
return { content: [{ type:
|
|
142
|
+
return { content: [{ type: "text", text: "Scheduled." }], details: {} }
|
|
132
143
|
},
|
|
133
144
|
},
|
|
134
145
|
],
|
|
@@ -143,7 +154,7 @@ const server = new BuiltinAgentsServer({
|
|
|
143
154
|
import {
|
|
144
155
|
resolveBuiltinAgentsEntrypointOptions,
|
|
145
156
|
runBuiltinAgentsEntrypoint,
|
|
146
|
-
} from
|
|
157
|
+
} from "@electric-ax/agents"
|
|
147
158
|
|
|
148
159
|
const options = resolveBuiltinAgentsEntrypointOptions(process.env)
|
|
149
160
|
const { server, url } = await runBuiltinAgentsEntrypoint()
|
|
@@ -154,27 +165,27 @@ await server.stop()
|
|
|
154
165
|
|
|
155
166
|
Environment variables:
|
|
156
167
|
|
|
157
|
-
| Variable
|
|
158
|
-
|
|
|
159
|
-
| `ELECTRIC_AGENTS_SERVER_URL`
|
|
160
|
-
| `ELECTRIC_AGENTS_BUILTIN_BASE_URL`
|
|
161
|
-
| `ELECTRIC_AGENTS_BUILTIN_HOST`
|
|
162
|
-
| `ELECTRIC_AGENTS_BUILTIN_PORT`
|
|
163
|
-
| `ELECTRIC_AGENTS_WORKING_DIRECTORY` | Working directory for file tools.
|
|
168
|
+
| Variable | Description |
|
|
169
|
+
| -------------------------------- | ----------------------------------------------------- |
|
|
170
|
+
| `ELECTRIC_AGENTS_SERVER_URL` | Required coordinator server URL. |
|
|
171
|
+
| `ELECTRIC_AGENTS_BUILTIN_BASE_URL` | Public webhook base URL for the built-in server. |
|
|
172
|
+
| `ELECTRIC_AGENTS_BUILTIN_HOST` | Bind host. |
|
|
173
|
+
| `ELECTRIC_AGENTS_BUILTIN_PORT` | Built-in server port. Defaults to `4448`. |
|
|
174
|
+
| `ELECTRIC_AGENTS_WORKING_DIRECTORY` | Working directory for file tools. |
|
|
164
175
|
|
|
165
176
|
## Built-in Agent APIs
|
|
166
177
|
|
|
167
178
|
The built-in agent exports are also available if you want to compose your own runtime:
|
|
168
179
|
|
|
169
|
-
| Export
|
|
170
|
-
|
|
|
171
|
-
| `registerHorton()`
|
|
172
|
-
| `registerWorker()`
|
|
173
|
-
| `HORTON_MODEL`
|
|
180
|
+
| Export | Purpose |
|
|
181
|
+
| ------------------------- | --------------------------------------------------- |
|
|
182
|
+
| `registerHorton()` | Register the `horton` type on an `EntityRegistry`. |
|
|
183
|
+
| `registerWorker()` | Register the `worker` type on an `EntityRegistry`. |
|
|
184
|
+
| `HORTON_MODEL` | Default model id used by Horton and worker. |
|
|
174
185
|
| `buildHortonSystemPrompt()` | Build Horton's system prompt for a working directory. |
|
|
175
|
-
| `createHortonTools()`
|
|
176
|
-
| `createSpawnWorkerTool()`
|
|
177
|
-
| `WORKER_TOOL_NAMES`
|
|
178
|
-
| `createHortonDocsSupport()` | Create Horton's docs knowledge-base support.
|
|
186
|
+
| `createHortonTools()` | Create Horton's base shell/file/search/worker tools. |
|
|
187
|
+
| `createSpawnWorkerTool()` | Create the `spawn_worker` tool for another agent. |
|
|
188
|
+
| `WORKER_TOOL_NAMES` | Valid primitive tool names for workers. |
|
|
189
|
+
| `createHortonDocsSupport()` | Create Horton's docs knowledge-base support. |
|
|
179
190
|
|
|
180
191
|
For the behavior of `horton` and `worker`, see [Horton](../entities/agents/horton) and [Worker](../entities/agents/worker).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Managing state
|
|
3
|
-
titleTemplate:
|
|
3
|
+
titleTemplate: "... - Electric Agents"
|
|
4
4
|
description: >-
|
|
5
5
|
Declare and manage persistent entity state using custom collections with typed CRUD operations.
|
|
6
6
|
outline: [2, 3]
|
|
@@ -15,16 +15,16 @@ Entities can declare custom persistent collections. The convenience API is `ctx.
|
|
|
15
15
|
Define collections in the `state` field of the entity definition:
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
|
-
registry.define(
|
|
18
|
+
registry.define("my-entity", {
|
|
19
19
|
state: {
|
|
20
|
-
status: { primaryKey:
|
|
20
|
+
status: { primaryKey: "key" },
|
|
21
21
|
items: {
|
|
22
22
|
schema: z.object({
|
|
23
23
|
key: z.string(),
|
|
24
24
|
name: z.string(),
|
|
25
25
|
count: z.number(),
|
|
26
26
|
}),
|
|
27
|
-
primaryKey:
|
|
27
|
+
primaryKey: "key",
|
|
28
28
|
},
|
|
29
29
|
},
|
|
30
30
|
async handler(ctx) {
|
|
@@ -59,33 +59,33 @@ Write helpers return a Transaction. Reads query the underlying TanStack DB colle
|
|
|
59
59
|
|
|
60
60
|
```ts
|
|
61
61
|
// Convenience API
|
|
62
|
-
ctx.state.items.insert({ key:
|
|
63
|
-
const itemViaState = ctx.state.items.get(
|
|
62
|
+
ctx.state.items.insert({ key: "item-1", name: "Widget", count: 5 })
|
|
63
|
+
const itemViaState = ctx.state.items.get("item-1")
|
|
64
64
|
const allViaState = ctx.state.items.toArray
|
|
65
|
-
ctx.state.items.update(
|
|
65
|
+
ctx.state.items.update("item-1", (draft) => {
|
|
66
66
|
draft.count += 1
|
|
67
67
|
})
|
|
68
|
-
ctx.state.items.delete(
|
|
68
|
+
ctx.state.items.delete("item-1")
|
|
69
69
|
|
|
70
70
|
// Lower-level insert
|
|
71
71
|
ctx.db.actions.items_insert({
|
|
72
|
-
row: { key:
|
|
72
|
+
row: { key: "item-1", name: "Widget", count: 5 },
|
|
73
73
|
})
|
|
74
74
|
|
|
75
75
|
// Lower-level read
|
|
76
|
-
const item = ctx.db.collections.items?.get(
|
|
76
|
+
const item = ctx.db.collections.items?.get("item-1")
|
|
77
77
|
const all = ctx.db.collections.items?.toArray
|
|
78
78
|
|
|
79
79
|
// Lower-level update (Immer-style draft)
|
|
80
80
|
ctx.db.actions.items_update({
|
|
81
|
-
key:
|
|
81
|
+
key: "item-1",
|
|
82
82
|
updater: (draft) => {
|
|
83
83
|
draft.count += 1
|
|
84
84
|
},
|
|
85
85
|
})
|
|
86
86
|
|
|
87
87
|
// Lower-level delete
|
|
88
|
-
ctx.db.actions.items_delete({ key:
|
|
88
|
+
ctx.db.actions.items_delete({ key: "item-1" })
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
## Built-in collections
|