@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.
Files changed (49) hide show
  1. package/dist/entrypoint.js +5 -3
  2. package/dist/index.cjs +5 -3
  3. package/dist/index.js +5 -3
  4. package/docs/entities/agents/horton.md +89 -0
  5. package/docs/entities/agents/worker.md +102 -0
  6. package/docs/entities/patterns/blackboard.md +111 -0
  7. package/docs/entities/patterns/dispatcher.md +77 -0
  8. package/docs/entities/patterns/manager-worker.md +127 -0
  9. package/docs/entities/patterns/map-reduce.md +81 -0
  10. package/docs/entities/patterns/pipeline.md +101 -0
  11. package/docs/entities/patterns/reactive-observers.md +125 -0
  12. package/docs/examples/mega-draw.md +106 -0
  13. package/docs/examples/playground.md +46 -0
  14. package/docs/index.md +208 -0
  15. package/docs/quickstart.md +201 -0
  16. package/docs/reference/agent-config.md +82 -0
  17. package/docs/reference/agent-tool.md +58 -0
  18. package/docs/reference/built-in-collections.md +334 -0
  19. package/docs/reference/cli.md +238 -0
  20. package/docs/reference/entity-definition.md +57 -0
  21. package/docs/reference/entity-handle.md +63 -0
  22. package/docs/reference/entity-registry.md +73 -0
  23. package/docs/reference/handler-context.md +108 -0
  24. package/docs/reference/runtime-handler.md +136 -0
  25. package/docs/reference/shared-state-handle.md +74 -0
  26. package/docs/reference/state-collection-proxy.md +41 -0
  27. package/docs/reference/wake-event.md +132 -0
  28. package/docs/usage/app-setup.md +165 -0
  29. package/docs/usage/clients-and-react.md +191 -0
  30. package/docs/usage/configuring-the-agent.md +136 -0
  31. package/docs/usage/context-composition.md +204 -0
  32. package/docs/usage/defining-entities.md +181 -0
  33. package/docs/usage/defining-tools.md +229 -0
  34. package/docs/usage/embedded-builtins.md +180 -0
  35. package/docs/usage/managing-state.md +93 -0
  36. package/docs/usage/overview.md +284 -0
  37. package/docs/usage/programmatic-runtime-client.md +216 -0
  38. package/docs/usage/shared-state.md +169 -0
  39. package/docs/usage/spawning-and-coordinating.md +165 -0
  40. package/docs/usage/testing.md +76 -0
  41. package/docs/usage/waking-entities.md +148 -0
  42. package/docs/usage/writing-handlers.md +267 -0
  43. package/package.json +2 -1
  44. package/skills/quickstart/scaffold/package.json +16 -3
  45. package/skills/quickstart/scaffold/tsconfig.json +8 -3
  46. package/skills/quickstart/scaffold/vite.config.ts +21 -0
  47. package/skills/quickstart/scaffold-ui/index.html +12 -0
  48. package/skills/quickstart/scaffold-ui/main.tsx +235 -0
  49. 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 |