@electric-ax/agents 0.2.1 → 0.2.2
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 +5 -3
- package/dist/index.cjs +5 -3
- package/dist/index.js +5 -3
- package/docs/entities/agents/horton.md +89 -0
- package/docs/entities/agents/worker.md +102 -0
- package/docs/entities/patterns/blackboard.md +111 -0
- package/docs/entities/patterns/dispatcher.md +77 -0
- package/docs/entities/patterns/manager-worker.md +127 -0
- package/docs/entities/patterns/map-reduce.md +81 -0
- package/docs/entities/patterns/pipeline.md +101 -0
- package/docs/entities/patterns/reactive-observers.md +125 -0
- package/docs/examples/mega-draw.md +106 -0
- package/docs/examples/playground.md +46 -0
- package/docs/index.md +208 -0
- package/docs/quickstart.md +201 -0
- package/docs/reference/agent-config.md +82 -0
- package/docs/reference/agent-tool.md +58 -0
- package/docs/reference/built-in-collections.md +334 -0
- package/docs/reference/cli.md +238 -0
- package/docs/reference/entity-definition.md +57 -0
- package/docs/reference/entity-handle.md +63 -0
- package/docs/reference/entity-registry.md +73 -0
- package/docs/reference/handler-context.md +108 -0
- package/docs/reference/runtime-handler.md +136 -0
- package/docs/reference/shared-state-handle.md +74 -0
- package/docs/reference/state-collection-proxy.md +41 -0
- package/docs/reference/wake-event.md +132 -0
- package/docs/usage/app-setup.md +165 -0
- package/docs/usage/clients-and-react.md +191 -0
- package/docs/usage/configuring-the-agent.md +136 -0
- package/docs/usage/context-composition.md +204 -0
- package/docs/usage/defining-entities.md +181 -0
- package/docs/usage/defining-tools.md +229 -0
- package/docs/usage/embedded-builtins.md +180 -0
- package/docs/usage/managing-state.md +93 -0
- package/docs/usage/overview.md +284 -0
- package/docs/usage/programmatic-runtime-client.md +216 -0
- package/docs/usage/shared-state.md +169 -0
- package/docs/usage/spawning-and-coordinating.md +165 -0
- package/docs/usage/testing.md +76 -0
- package/docs/usage/waking-entities.md +148 -0
- package/docs/usage/writing-handlers.md +267 -0
- package/package.json +2 -1
- package/skills/quickstart/scaffold/package.json +16 -3
- package/skills/quickstart/scaffold/tsconfig.json +8 -3
- package/skills/quickstart/scaffold/vite.config.ts +21 -0
- package/skills/quickstart/scaffold-ui/index.html +12 -0
- package/skills/quickstart/scaffold-ui/main.tsx +235 -0
- package/skills/quickstart.md +244 -334
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: RuntimeHandler
|
|
3
|
+
titleTemplate: '... - Electric Agents'
|
|
4
|
+
description: >-
|
|
5
|
+
API reference for RuntimeHandler: webhook handling, type registration, and deployment configuration.
|
|
6
|
+
outline: [2, 3]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# RuntimeHandler
|
|
10
|
+
|
|
11
|
+
Factory functions that create the runtime request router and Node HTTP adapter. The router handles webhook wake delivery from the Electric Agents runtime server and registers entity types on startup.
|
|
12
|
+
|
|
13
|
+
**Source:** `@electric-ax/agents-runtime`
|
|
14
|
+
|
|
15
|
+
## RuntimeRouter
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
interface RuntimeRouter {
|
|
19
|
+
handleRequest(request: Request): Promise<Response | null>
|
|
20
|
+
handleWebhookRequest(request: Request): Promise<Response>
|
|
21
|
+
dispatchWebhookWake(notification: WebhookNotification): void
|
|
22
|
+
drainWakes(): Promise<void>
|
|
23
|
+
waitForSettled(): Promise<void>
|
|
24
|
+
abortWakes(): void
|
|
25
|
+
debugState(): RuntimeDebugState
|
|
26
|
+
readonly typeNames: string[]
|
|
27
|
+
registerTypes(): Promise<void>
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Method | Return Type | Description |
|
|
32
|
+
| ----------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
33
|
+
| `handleRequest(request)` | `Promise<Response \| null>` | Route a fetch `Request`. Returns `null` if the request path does not match `webhookPath`. |
|
|
34
|
+
| `handleWebhookRequest(request)` | `Promise<Response>` | Handle a webhook request directly, without route matching. |
|
|
35
|
+
| `dispatchWebhookWake(notification)` | `void` | Dispatch an already-parsed webhook notification. Runs the wake handler in the background. |
|
|
36
|
+
| `drainWakes()` | `Promise<void>` | Wait for all in-flight wake handlers to settle. Throws if any wake errored. |
|
|
37
|
+
| `waitForSettled()` | `Promise<void>` | Wait for all in-flight wake handlers to settle. |
|
|
38
|
+
| `abortWakes()` | `void` | Abort in-flight wakes so host shutdown can complete quickly. |
|
|
39
|
+
| `debugState()` | `RuntimeDebugState` | Return a runtime-local snapshot for tests and shutdown diagnostics. |
|
|
40
|
+
| `typeNames` | `string[]` | Names of all registered entity types (read-only). |
|
|
41
|
+
| `registerTypes()` | `Promise<void>` | Register all entity types with the Electric Agents runtime server. Uses upsert semantics — safe to call on every startup. |
|
|
42
|
+
|
|
43
|
+
## RuntimeHandler
|
|
44
|
+
|
|
45
|
+
Extends `RuntimeRouter` with a Node HTTP adapter.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
interface RuntimeHandler extends RuntimeRouter {
|
|
49
|
+
onEnter(req: IncomingMessage, res: ServerResponse): Promise<void>
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
| Method | Parameters | Description |
|
|
54
|
+
| ------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
55
|
+
| `onEnter(req, res)` | Node `IncomingMessage`, `ServerResponse` | Node HTTP adapter. Converts the request to a fetch `Request` and delegates to `handleWebhookRequest`. |
|
|
56
|
+
|
|
57
|
+
## RuntimeDebugState
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
interface RuntimeDebugState {
|
|
61
|
+
pendingWakeCount: number
|
|
62
|
+
pendingWakeLabels: string[]
|
|
63
|
+
wakeErrorCount: number
|
|
64
|
+
typeNames: string[]
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
| Field | Type | Description |
|
|
69
|
+
| ------------------- | ---------- | ------------------------------------------------------- |
|
|
70
|
+
| `pendingWakeCount` | `number` | Number of in-flight wake handlers. |
|
|
71
|
+
| `pendingWakeLabels` | `string[]` | Labels identifying each pending wake (for diagnostics). |
|
|
72
|
+
| `wakeErrorCount` | `number` | Number of wake handlers that have errored. |
|
|
73
|
+
| `typeNames` | `string[]` | Names of all registered entity types. |
|
|
74
|
+
|
|
75
|
+
## Factory functions
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
function createRuntimeRouter(config: RuntimeRouterConfig): RuntimeRouter
|
|
79
|
+
|
|
80
|
+
function createRuntimeHandler(config: RuntimeHandlerConfig): RuntimeHandler
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Both factory functions accept the same runtime router configuration.
|
|
84
|
+
|
|
85
|
+
## RuntimeRouterConfig
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
interface RuntimeRouterConfig {
|
|
89
|
+
baseUrl: string
|
|
90
|
+
serveEndpoint?: string
|
|
91
|
+
webhookPath?: string
|
|
92
|
+
registry?: EntityRegistry
|
|
93
|
+
subscriptionPathForType?: (typeName: string) => string
|
|
94
|
+
idleTimeout?: number
|
|
95
|
+
heartbeatInterval?: number
|
|
96
|
+
createElectricTools?: (context: {
|
|
97
|
+
entityUrl: string
|
|
98
|
+
entityType: string
|
|
99
|
+
args: Readonly<Record<string, unknown>>
|
|
100
|
+
db: EntityStreamDBWithActions
|
|
101
|
+
events: Array<ChangeEvent>
|
|
102
|
+
upsertCronSchedule(opts: {
|
|
103
|
+
id: string
|
|
104
|
+
expression: string
|
|
105
|
+
timezone?: string
|
|
106
|
+
payload?: unknown
|
|
107
|
+
debounceMs?: number
|
|
108
|
+
timeoutMs?: number
|
|
109
|
+
}): Promise<{ txid: string }>
|
|
110
|
+
upsertFutureSendSchedule(opts: {
|
|
111
|
+
id: string
|
|
112
|
+
payload: unknown
|
|
113
|
+
targetUrl?: string
|
|
114
|
+
fireAt: string
|
|
115
|
+
from?: string
|
|
116
|
+
messageType?: string
|
|
117
|
+
}): Promise<{ txid: string }>
|
|
118
|
+
deleteSchedule(opts: { id: string }): Promise<{ txid: string }>
|
|
119
|
+
}) => AgentTool[] | Promise<AgentTool[]>
|
|
120
|
+
onWakeError?: (error: Error) => boolean | void
|
|
121
|
+
registrationConcurrency?: number
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
| Field | Type | Default | Description |
|
|
126
|
+
| ------------------------- | ------------------------------------------ | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
|
|
127
|
+
| `baseUrl` | `string` | - | Base URL of the Electric Agents runtime server (e.g. `"http://localhost:4437"`). Required. |
|
|
128
|
+
| `serveEndpoint` | `string` | - | Full webhook callback URL exposed by your app. Used for type registration. |
|
|
129
|
+
| `webhookPath` | `string` | pathname from `serveEndpoint`, or `"/electric-agents"` | Path matched by `handleRequest()`. |
|
|
130
|
+
| `registry` | `EntityRegistry` | default registry | Entity registry for this handler. Falls back to the module-level default registry. |
|
|
131
|
+
| `subscriptionPathForType` | `(typeName: string) => string` | - | Override the webhook subscription path used per entity type registration. |
|
|
132
|
+
| `idleTimeout` | `number` | `20000` | Idle timeout in milliseconds before closing a wake. |
|
|
133
|
+
| `heartbeatInterval` | `number` | `30000` | Heartbeat interval in milliseconds. |
|
|
134
|
+
| `createElectricTools` | `(context) => AgentTool[] \| Promise<...>` | - | Optional tool factory invoked for each wake context before handler execution. Provides extra tools to the agent. |
|
|
135
|
+
| `onWakeError` | `(error: Error) => boolean \| void` | - | Observer for background wake failures. Return `true` to mark the error as handled so it is not rethrown on drain. |
|
|
136
|
+
| `registrationConcurrency` | `number` | `8` | Max number of concurrent entity-type registrations. |
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: SharedStateHandle
|
|
3
|
+
titleTemplate: '... - Electric Agents'
|
|
4
|
+
description: >-
|
|
5
|
+
Type reference for SharedStateHandle: collection proxies and SharedStateSchemaMap interface.
|
|
6
|
+
outline: [2, 3]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# SharedStateHandle
|
|
10
|
+
|
|
11
|
+
Handle for a shared state stream, returned by `ctx.mkdb()` and `await ctx.observe(db(...))`. Provides typed collection proxies keyed by the collection names declared in the schema map.
|
|
12
|
+
|
|
13
|
+
**Source:** `@electric-ax/agents-runtime`
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
type SharedStateHandle<
|
|
17
|
+
TSchema extends SharedStateSchemaMap = SharedStateSchemaMap,
|
|
18
|
+
> = {
|
|
19
|
+
id: string
|
|
20
|
+
} & { [K in keyof TSchema]: StateCollectionProxy }
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Properties
|
|
24
|
+
|
|
25
|
+
| Property | Type | Description |
|
|
26
|
+
| ------------------ | -------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
27
|
+
| `id` | `string` | The shared state stream identifier. |
|
|
28
|
+
| `[collectionName]` | [`StateCollectionProxy`](./state-collection-proxy) | One property per collection defined in the schema. Provides insert/update/delete/get/toArray. |
|
|
29
|
+
|
|
30
|
+
## SharedStateSchemaMap
|
|
31
|
+
|
|
32
|
+
Defines the collections in a shared state stream.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
type SharedStateSchemaMap = Record<string, SharedStateCollectionSchema>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## SharedStateCollectionSchema
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
interface SharedStateCollectionSchema {
|
|
42
|
+
schema?: StandardSchemaV1
|
|
43
|
+
type: string
|
|
44
|
+
primaryKey: string
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| Field | Type | Required | Description |
|
|
49
|
+
| ------------ | ------------------ | -------- | ---------------------------------------------------------------- |
|
|
50
|
+
| `schema` | `StandardSchemaV1` | No | Zod or Standard Schema validator for the row type. |
|
|
51
|
+
| `type` | `string` | Yes | Event type string used in the durable stream (e.g. `"finding"`). |
|
|
52
|
+
| `primaryKey` | `string` | Yes | Primary key field name on the row (must be a string field). |
|
|
53
|
+
|
|
54
|
+
## Example
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { db } from '@electric-ax/agents-runtime'
|
|
58
|
+
|
|
59
|
+
const schema = {
|
|
60
|
+
findings: {
|
|
61
|
+
schema: z.object({
|
|
62
|
+
key: z.string(),
|
|
63
|
+
domain: z.string(),
|
|
64
|
+
finding: z.string(),
|
|
65
|
+
}),
|
|
66
|
+
type: 'finding',
|
|
67
|
+
primaryKey: 'key',
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
ctx.mkdb('research-findings', schema)
|
|
72
|
+
const shared = await ctx.observe(db('research-findings', schema))
|
|
73
|
+
shared.findings.insert({ key: 'f1', domain: 'security', finding: '...' })
|
|
74
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: StateCollectionProxy
|
|
3
|
+
titleTemplate: '... - Electric Agents'
|
|
4
|
+
description: >-
|
|
5
|
+
API reference for StateCollectionProxy: insert, update, delete, get, and toArray operations.
|
|
6
|
+
outline: [2, 3]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# StateCollectionProxy
|
|
10
|
+
|
|
11
|
+
Proxy handle for a state collection. Entity-local state exposes these proxies on `ctx.state.<collection>`, and shared state exposes them on a `SharedStateHandle` returned by `ctx.mkdb()` or `await ctx.observe(db(...))`. Mutations are routed through auto-generated CRUD actions. Reads delegate to the underlying TanStack DB collection.
|
|
12
|
+
|
|
13
|
+
> **Note:** Entity state can also be accessed through the lower-level `ctx.db.actions.<coll>_insert/update/delete` and `ctx.db.collections.<coll>?.get/toArray` APIs. `ctx.state` is the proxy convenience layer over those APIs.
|
|
14
|
+
|
|
15
|
+
**Source:** `@electric-ax/agents-runtime`
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
interface StateCollectionProxy<T extends object = Record<string, unknown>> {
|
|
19
|
+
insert(row: T): unknown
|
|
20
|
+
update(key: string, updater: (draft: T) => void): unknown
|
|
21
|
+
delete(key: string): unknown
|
|
22
|
+
get(key: string): T | undefined
|
|
23
|
+
toArray: T[]
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Members
|
|
28
|
+
|
|
29
|
+
| Member | Parameters | Return Type | Description |
|
|
30
|
+
| ---------------------- | -------------------------------------------- | ---------------- | ----------------------------------------------------------------------- |
|
|
31
|
+
| `insert(row)` | `row: T` | `Transaction` | Insert a new row into the collection. |
|
|
32
|
+
| `update(key, updater)` | `key: string`, `updater: (draft: T) => void` | `Transaction` | Update a row by key. The updater receives an Immer-style mutable draft. |
|
|
33
|
+
| `delete(key)` | `key: string` | `Transaction` | Delete a row by key. |
|
|
34
|
+
| `get(key)` | `key: string` | `T \| undefined` | Read a single row by key. Returns `undefined` if not found. |
|
|
35
|
+
| `toArray` | - | `T[]` | All rows as an array. This is a getter property, not a method. |
|
|
36
|
+
|
|
37
|
+
## Notes
|
|
38
|
+
|
|
39
|
+
- Mutating methods (`insert`, `update`, `delete`) return a Transaction. These are fire-and-forget -- the write is persisted to the backing durable stream asynchronously.
|
|
40
|
+
- The `update` method uses Immer-style drafts. Mutate the draft directly rather than returning a new object.
|
|
41
|
+
- `toArray` is a property, not a method call. Access it without parentheses: `ctx.state.items.toArray` or `shared.items.toArray`.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: WakeEvent
|
|
3
|
+
titleTemplate: '... - Electric Agents'
|
|
4
|
+
description: >-
|
|
5
|
+
Type reference for WakeEvent and Wake configuration: runFinished and change-based wake conditions.
|
|
6
|
+
outline: [2, 3]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# WakeEvent
|
|
10
|
+
|
|
11
|
+
Describes why an entity handler was invoked. Passed as the second argument to the handler function.
|
|
12
|
+
|
|
13
|
+
**Source:** `@electric-ax/agents-runtime`
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
type WakeEvent = {
|
|
17
|
+
source: string
|
|
18
|
+
type: string
|
|
19
|
+
fromOffset: number
|
|
20
|
+
toOffset: number
|
|
21
|
+
eventCount: number
|
|
22
|
+
payload?: unknown
|
|
23
|
+
summary?: string
|
|
24
|
+
fullRef?: string
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Fields
|
|
29
|
+
|
|
30
|
+
| Field | Type | Description |
|
|
31
|
+
| ------------ | --------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
|
+
| `source` | `string` | URL or identifier of the stream that triggered the wake. |
|
|
33
|
+
| `type` | `string` | Wake type. Usually `"message_received"` or `"wake"`; fallback webhook events can use `triggerEvent` or `"message"`. See catalog. |
|
|
34
|
+
| `fromOffset` | `number` | Start offset of new events in the source stream. |
|
|
35
|
+
| `toOffset` | `number` | End offset (exclusive) of new events. |
|
|
36
|
+
| `eventCount` | `number` | Number of new events in this wake. |
|
|
37
|
+
| `payload` | `unknown` | Optional payload data associated with the wake. Shape depends on `type`. |
|
|
38
|
+
| `summary` | `string` | Optional human-readable summary of the wake reason. |
|
|
39
|
+
| `fullRef` | `string` | Optional full reference identifier for the wake source. |
|
|
40
|
+
|
|
41
|
+
## Wake-type catalog
|
|
42
|
+
|
|
43
|
+
Handlers usually see two values for `wake.type`. Direct inbox messages arrive as `"message_received"`. Most non-message triggers are flattened into `"wake"`, with the specifics carried on `wake.payload`. Low-level webhook fallbacks can surface `triggerEvent` directly, or `"message"` when no trigger event is provided.
|
|
44
|
+
|
|
45
|
+
### `"message_received"`
|
|
46
|
+
|
|
47
|
+
An external message landed in the entity's inbox — from `ctx.send()`, the CLI's `electric agents send`, or any direct `/send` HTTP call.
|
|
48
|
+
|
|
49
|
+
| Field | Shape |
|
|
50
|
+
| -------------- | --------------------------------------------------------------------------------- |
|
|
51
|
+
| `wake.source` | The `from` field of the message (sender identifier), or the entity URL if absent. |
|
|
52
|
+
| `wake.payload` | The message payload (any JSON-serialisable value). |
|
|
53
|
+
| `wake.summary` | The `message_type` if the sender set one. |
|
|
54
|
+
|
|
55
|
+
### `"wake"`
|
|
56
|
+
|
|
57
|
+
A synthesised wake for any non-message trigger. `wake.payload` is a `WakeMessage`:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
type WakeMessage = {
|
|
61
|
+
timestamp: string
|
|
62
|
+
source: string
|
|
63
|
+
timeout: boolean
|
|
64
|
+
changes: Array<{
|
|
65
|
+
collection: string
|
|
66
|
+
kind: 'insert' | 'update' | 'delete'
|
|
67
|
+
key: string
|
|
68
|
+
}>
|
|
69
|
+
finished_child?: {
|
|
70
|
+
url: string
|
|
71
|
+
type: string
|
|
72
|
+
run_status: 'completed' | 'failed'
|
|
73
|
+
response?: string
|
|
74
|
+
error?: string
|
|
75
|
+
}
|
|
76
|
+
other_children?: Array<{
|
|
77
|
+
url: string
|
|
78
|
+
type: string
|
|
79
|
+
status: 'spawning' | 'running' | 'idle' | 'stopped'
|
|
80
|
+
}>
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Inspect the payload to distinguish the sub-kind:
|
|
85
|
+
|
|
86
|
+
| Sub-kind | Producer | Payload marker |
|
|
87
|
+
| ------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
88
|
+
| Child finished | `ctx.spawn(..., { wake: 'runFinished' })` when the child completes or fails | `payload.finished_child` is set (with `run_status` and optional `response`) |
|
|
89
|
+
| Observed change | `ctx.observe(..., { wake: { on: 'change' } })` or `observe(db(...))` | `payload.changes` is non-empty |
|
|
90
|
+
| Shared-state change | `await ctx.observe(db(...), { wake: { on: 'change' } })` | `payload.changes` is non-empty, `payload.source` identifies the shared-state stream |
|
|
91
|
+
| Cron fired | A cron schedule entry on the entity's manifest | `payload.source` identifies the schedule; `payload.changes` is empty |
|
|
92
|
+
| Scheduled send | A `future_send` schedule fires | Arrives as `"message_received"` (not `"wake"`) — the schedule produces a message delivery |
|
|
93
|
+
| Timeout | `timeoutMs` on a `change` wake config elapsed with no changes | `payload.timeout === true`, `payload.changes` is empty |
|
|
94
|
+
|
|
95
|
+
For the narrative on how these are produced, see [Waking entities](../usage/waking-entities).
|
|
96
|
+
|
|
97
|
+
## Wake
|
|
98
|
+
|
|
99
|
+
The `Wake` type configures when a parent should be woken in response to a child, observed entity, or shared state change. Used in `ctx.spawn()`, `ctx.observe()`, and `ctx.observe(db(...))` options.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
type Wake =
|
|
103
|
+
| 'runFinished'
|
|
104
|
+
| { on: 'runFinished'; includeResponse?: boolean }
|
|
105
|
+
| {
|
|
106
|
+
on: 'change'
|
|
107
|
+
collections?: string[]
|
|
108
|
+
ops?: ('insert' | 'update' | 'delete')[]
|
|
109
|
+
debounceMs?: number
|
|
110
|
+
timeoutMs?: number
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `'runFinished'`
|
|
115
|
+
|
|
116
|
+
Wake the parent when the child's agent run completes (status changes to `completed` or `failed`). By default, the wake event includes the child's concatenated text response in `finished_child.response`.
|
|
117
|
+
|
|
118
|
+
### `{ on: 'runFinished', includeResponse?: boolean }`
|
|
119
|
+
|
|
120
|
+
Object form of `runFinished` with options. Set `includeResponse: false` to omit the child's text response from the wake event.
|
|
121
|
+
|
|
122
|
+
### `{ on: 'change' }`
|
|
123
|
+
|
|
124
|
+
Wake the parent when changes occur in the observed stream.
|
|
125
|
+
|
|
126
|
+
| Field | Type | Description |
|
|
127
|
+
| ------------- | ---------- | --------------------------------------------------------------------- |
|
|
128
|
+
| `on` | `'change'` | Required discriminant. |
|
|
129
|
+
| `collections` | `string[]` | Optional filter. Only wake on changes to these collections. |
|
|
130
|
+
| `ops` | `string[]` | Optional operation filter: `"insert"`, `"update"`, and/or `"delete"`. |
|
|
131
|
+
| `debounceMs` | `number` | Debounce interval in milliseconds. Batches rapid changes. |
|
|
132
|
+
| `timeoutMs` | `number` | Maximum time to wait before waking, even if no changes occur. |
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: App setup
|
|
3
|
+
titleTemplate: '... - Electric Agents'
|
|
4
|
+
description: >-
|
|
5
|
+
Connect your app to the Electric Agents runtime with createRuntimeHandler, webhooks, and type registration.
|
|
6
|
+
outline: [2, 3]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# App setup
|
|
10
|
+
|
|
11
|
+
The runtime handler connects your app's entity definitions to the Electric Agents runtime server via webhooks.
|
|
12
|
+
|
|
13
|
+
## createRuntimeHandler
|
|
14
|
+
|
|
15
|
+
Creates a runtime with a Node HTTP adapter:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import {
|
|
19
|
+
createEntityRegistry,
|
|
20
|
+
createRuntimeHandler,
|
|
21
|
+
} from '@electric-ax/agents-runtime'
|
|
22
|
+
|
|
23
|
+
const registry = createEntityRegistry()
|
|
24
|
+
// ... register entity types ...
|
|
25
|
+
|
|
26
|
+
const runtime = createRuntimeHandler({
|
|
27
|
+
baseUrl: 'http://localhost:4437',
|
|
28
|
+
serveEndpoint: 'http://localhost:3000/webhook',
|
|
29
|
+
registry,
|
|
30
|
+
})
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
interface RuntimeRouterConfig {
|
|
37
|
+
baseUrl: string // Electric Agents server URL
|
|
38
|
+
serveEndpoint?: string // Webhook callback URL
|
|
39
|
+
webhookPath?: string // Path to match (default: derived from serveEndpoint)
|
|
40
|
+
registry?: EntityRegistry
|
|
41
|
+
subscriptionPathForType?: (typeName: string) => string
|
|
42
|
+
idleTimeout?: number // ms before closing idle wake (default: 20000)
|
|
43
|
+
heartbeatInterval?: number // ms between heartbeats (default: 30000)
|
|
44
|
+
createElectricTools?: (context: {
|
|
45
|
+
entityUrl: string
|
|
46
|
+
entityType: string
|
|
47
|
+
args: Readonly<Record<string, unknown>>
|
|
48
|
+
db: EntityStreamDBWithActions
|
|
49
|
+
events: Array<ChangeEvent>
|
|
50
|
+
upsertCronSchedule(opts: {
|
|
51
|
+
id: string
|
|
52
|
+
expression: string
|
|
53
|
+
timezone?: string
|
|
54
|
+
payload?: unknown
|
|
55
|
+
debounceMs?: number
|
|
56
|
+
timeoutMs?: number
|
|
57
|
+
}): Promise<{ txid: string }>
|
|
58
|
+
upsertFutureSendSchedule(opts: {
|
|
59
|
+
id: string
|
|
60
|
+
payload: unknown
|
|
61
|
+
targetUrl?: string
|
|
62
|
+
fireAt: string
|
|
63
|
+
from?: string
|
|
64
|
+
messageType?: string
|
|
65
|
+
}): Promise<{ txid: string }>
|
|
66
|
+
deleteSchedule(opts: { id: string }): Promise<{ txid: string }>
|
|
67
|
+
}) => AgentTool[] | Promise<AgentTool[]> // factory for extra agent tools
|
|
68
|
+
onWakeError?: (error: Error) => boolean | void // return true to mark handled
|
|
69
|
+
registrationConcurrency?: number // max concurrent type registrations (default: 8)
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## HTTP server
|
|
74
|
+
|
|
75
|
+
Your app needs an HTTP server to receive webhook callbacks from the Electric Agents runtime server. Forward webhook POSTs to the runtime handler:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import http from 'node:http'
|
|
79
|
+
|
|
80
|
+
const server = http.createServer(async (req, res) => {
|
|
81
|
+
if (req.url === '/webhook' && req.method === 'POST') {
|
|
82
|
+
await runtime.onEnter(req, res)
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
res.writeHead(404)
|
|
86
|
+
res.end()
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
server.listen(PORT, async () => {
|
|
90
|
+
await runtime.registerTypes()
|
|
91
|
+
console.log(`${runtime.typeNames.length} types registered`)
|
|
92
|
+
})
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## registerTypes
|
|
96
|
+
|
|
97
|
+
Registers all entity types with the Electric Agents runtime server and creates webhook subscriptions. Uses upsert semantics — re-registering an existing type updates it rather than erroring.
|
|
98
|
+
|
|
99
|
+
Must be called after your app starts listening.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
await runtime.registerTypes()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
This makes two requests per entity type:
|
|
106
|
+
|
|
107
|
+
1. `POST /_electric/entity-types` — registers the type definition and schemas.
|
|
108
|
+
2. `PUT /{type}/**?subscription={type}-handler` — creates a webhook subscription for the type.
|
|
109
|
+
|
|
110
|
+
## RuntimeHandler
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
interface RuntimeHandler {
|
|
114
|
+
onEnter(req: IncomingMessage, res: ServerResponse): Promise<void>
|
|
115
|
+
handleRequest(request: Request): Promise<Response | null>
|
|
116
|
+
handleWebhookRequest(request: Request): Promise<Response>
|
|
117
|
+
dispatchWebhookWake(notification: WebhookNotification): void
|
|
118
|
+
drainWakes(): Promise<void>
|
|
119
|
+
waitForSettled(): Promise<void>
|
|
120
|
+
abortWakes(): void
|
|
121
|
+
debugState(): RuntimeDebugState
|
|
122
|
+
readonly typeNames: string[]
|
|
123
|
+
registerTypes(): Promise<void>
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
interface RuntimeDebugState {
|
|
127
|
+
pendingWakeCount: number
|
|
128
|
+
pendingWakeLabels: string[]
|
|
129
|
+
wakeErrorCount: number
|
|
130
|
+
typeNames: string[]
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Method | Description |
|
|
135
|
+
| ---------------------- | ---------------------------------------------------------------------------------------- |
|
|
136
|
+
| `onEnter` | Node HTTP adapter — reads the request body and delegates to `handleWebhookRequest` |
|
|
137
|
+
| `handleRequest` | Fetch-native router — returns `null` if the path does not match `webhookPath` |
|
|
138
|
+
| `handleWebhookRequest` | Processes a webhook POST directly, without path matching |
|
|
139
|
+
| `dispatchWebhookWake` | Dispatches a pre-parsed notification (fire-and-forget) |
|
|
140
|
+
| `drainWakes` | Waits for all in-flight wake handlers to settle; throws on errors |
|
|
141
|
+
| `waitForSettled` | Waits for all in-flight wakes; throws on errors |
|
|
142
|
+
| `abortWakes` | Cancels all in-flight wake handlers immediately |
|
|
143
|
+
| `debugState` | Returns a snapshot of internal runtime state for diagnostics |
|
|
144
|
+
| `registerTypes` | Registers entity types and webhook subscriptions with the Electric Agents runtime server |
|
|
145
|
+
|
|
146
|
+
## createRuntimeRouter
|
|
147
|
+
|
|
148
|
+
Fetch-native alternative with no Node HTTP dependency:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { createRuntimeRouter } from '@electric-ax/agents-runtime'
|
|
152
|
+
|
|
153
|
+
const router = createRuntimeRouter(config)
|
|
154
|
+
const response = await router.handleRequest(request)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Use this when integrating with non-Node frameworks or edge runtimes.
|
|
158
|
+
|
|
159
|
+
## Environment variables
|
|
160
|
+
|
|
161
|
+
| Variable | Default | Purpose |
|
|
162
|
+
| --------------------- | ----------------------- | ---------------------------------- |
|
|
163
|
+
| `ELECTRIC_AGENTS_URL` | `http://localhost:4437` | Electric Agents runtime server URL |
|
|
164
|
+
| `PORT` | `3000` | Your app's HTTP port |
|
|
165
|
+
| `ANTHROPIC_API_KEY` | — | Claude API key |
|