@ 电动轴/ 代理运行时
耐用实体操作员运行时间 持久流。 。 。 .
每个实体都拥有一个只有附件的流. 每一刻,跑步时间都会实现
以输入 TanStack DB 收藏,构建一个 HandlerContext,以及
运行您的实体的单 handler(ctx, wake) 进入点.
没有分开的 setup() 或者说 loader() 状态.
安装
pnpm add @electric-ax/agents-runtime
同伴依赖 : @tanstack/db >= 0.5.33
快速启动
import http from 'node:http'
import { z } from 'zod'
import { createRuntimeHandler, defineEntity } from '@electric-ax/agents-runtime'
defineEntity(`assistant`, {
description: `Simple durable chat assistant`,
state: {
status: {
schema: z.object({
key: z.string(),
value: z.enum([`idle`, `working`]),
}),
type: `status`,
primaryKey: `key`,
},
},
async handler(ctx) {
if (!ctx.db.collections.status.get(`current`)) {
ctx.db.actions.status_insert({
row: { key: `current`, value: `idle` },
})
}
ctx.useAgent({
systemPrompt: `You are a helpful assistant.`,
model: `claude-sonnet-4-5-20250929`,
tools: [...ctx.electricTools],
})
await ctx.agent.run()
},
})
const runtime = createRuntimeHandler({
baseUrl: `http://127.0.0.1:4437`,
serveEndpoint: `http://127.0.0.1:3000/webhook`,
})
await runtime.registerTypes()
const server = http.createServer(async (req, res) => {
if (req.url === `/webhook` && req.method === `POST`) {
await runtime.onEnter(req, res)
return
}
res.writeHead(404)
res.end()
})
server.listen(3000, `127.0.0.1`)
实体模式
实体申报 defineEntity(name, definition)数字 :
import { z } from 'zod'
import { defineEntity } from '@electric-ax/agents-runtime'
defineEntity(`pr-reviewer`, {
description: `Code review agent`,
creationSchema: z.object({
repo: z.string(),
prNumber: z.number().int(),
}),
inboxSchemas: {
'pr-opened': z.object({
diff: z.string(),
baseBranch: z.string().optional(),
}),
},
state: {
reviewStatus: {
schema: z.object({
key: z.string(),
status: z.enum([`pending`, `reviewing`, `done`]),
}),
type: `review_status`,
primaryKey: `key`,
},
},
async handler(ctx, wake) {
if (ctx.firstWake && !ctx.db.collections.reviewStatus.get(`current`)) {
ctx.db.actions.reviewStatus_insert({
row: { key: `current`, status: `reviewing` },
})
}
if (wake.type === `inbox`) {
ctx.useAgent({
systemPrompt: `Review pull requests carefully and concisely.`,
model: `claude-sonnet-4-5-20250929`,
tools: [...ctx.electricTools],
})
await ctx.agent.run()
ctx.db.actions.reviewStatus_update({
key: `current`,
updater: (draft) => {
draft.status = `done`
},
})
}
},
})
处理器连接
handler(ctx, wake) 接收 :
ctx.firstWake—— 说true只有第一次醒来,实体ctx.entityUrl编号 :ctx.entityType——身份元数据ctx.args- 不可改变的有效产卵群ctx.db——实体已实现的StreatDB被打出ctx.db.collections.<name>(TanStack DB 收藏)用于读取和 自动生成的写入动作ctx.db.actions.<name>_{insert,update,delete}ctx.electricTools——内置运行时间工具通过代理
状态
每一件被宣布为 definition.state 自动生成
<name>_insert, (中文(简体) ), <name>_update,以及 <name>_delete 行动
ctx.db.actions读取 ctx.db.collections.<name>数字 :
ctx.db.actions.counts_insert({ row: { key: `main`, value: 0 } })
ctx.db.actions.counts_update({
key: `main`,
updater: (draft) => {
draft.value++
},
})
ctx.db.actions.counts_delete({ key: `main` })
ctx.db.collections.counts.get(`main`)
ctx.db.collections.counts.toArray
探员
ctx.useAgent({
systemPrompt: `You are a helpful assistant.`,
model: `claude-sonnet-4-5-20250929`,
tools: [...ctx.electricTools, myTool],
})
await ctx.agent.run()
agent.run() 从实现的时间表组装实体的背景
处理提醒触发消息。 如果你不打电话 useAgent(), (中文(简体) ),
运行时间不会运行 LLM 为那个钟.
闪烁、观察和发送
const child = await ctx.spawn(
`researcher`,
`r-1`,
{ topic: `durability` },
{ wake: { on: `runFinished`, includeResponse: true } }
)
// Return from this wake. Continue when the child completion wake arrives.
const observed = await ctx.observe(entity(`/researcher/r-2`), {
wake: { on: `runFinished`, includeResponse: true },
})
const status = observed.status()
ctx.send(`/other-entity/id`, { text: `hello` }, { type: `message` })
共享状态
const boardSchema = {
findings: {
schema: z.object({
key: z.string(),
domain: z.string(),
finding: z.string(),
}),
type: `finding`,
primaryKey: `key`,
},
}
if (ctx.firstWake) {
ctx.mkdb(`board-1`, boardSchema)
}
const board = await ctx.observe(db(`board-1`, boardSchema))
board.findings.insert({
key: `f-1`,
domain: `security`,
finding: `XSS found`,
})
mkdb 创建后置流( 如果已存在的话 ) . observe(db(...)) 返回任意提示读取和写入的控点.
运行时处理器
createRuntimeHandler() 创建电子代理在
实体被唤醒.
const runtime = createRuntimeHandler({
baseUrl: `http://127.0.0.1:4437`,
serveEndpoint: `http://127.0.0.1:3000/webhook`,
})
await runtime.registerTypes()
主要方法:
runtime.registerTypes()——在服务器中注册所有实体类型runtime.onEnter(req, res)——用于webhook发送的节点HTTP适配器runtime.handleRequest(request)取自处理器runtime.drainWakes()等待在飞机上醒来后安顿下来
实体信号
可以通过服务器生命周期终点控制代理:
POST /_electric/entities/:type/:instanceId/signal
JSON的尸体是 { "signal": "SIGINT", "reason": "...", "payload": ... }。 。 。 .
同名API被CLI曝光:
electric agents signal /horton/onboarding SIGINT --reason "stop current run"
electric agents kill /horton/onboarding
信号行为 :
SIGINT中止活动处理器引用并保持实体状态不变.- 非代理处理者可以通过
ctx.signal并传递给可以工作,例如fetch或子进程. SIGSTOP暂停实体。 新信件再次唤醒它,但已经待办的工作在被暂停时不被处理.SIGCONT恢复已暂停的实体.SIGHUP, (中文(简体) ),SIGTERM,以及SIGUSR交付给ctx.onSignal(...)当当前唤醒活动时 .SIGTERM向闲置或暂停的实体过渡stopped; 运行的实体移动到stopping直到清理结束.SIGKILL立即将实体移动到killed,未注册者醒来,关闭实体流.
内设特工
前一个 registerChatAgent 编号 : registerResearcherAgent 编号 : registerCoderAgent 编号 : registerOracleAgent 帮助者已被移除 。 内地特工现在住在 @electric-ax/agents (霍顿+工人). 在您的运行时间注册, 使用 createBuiltinAgentHandler 从 @electric-ax/agents。 。 。 .
时间线助手
运行时间也会导出 UI 层所用的时长帮助 :
createEntityIncludesQuery(db)getEntityState(runs, inbox)buildSections(runs, inbox)timelineToMessages(db)
useChat(db) 使用基于查询的 IncludesRun 编号 : IncludesInboxMessage
阵列,以及 timelineToMessages(db) 从相同的形状建立 LLM 消息
数据.
测试
老公众 createTestAdapter API 已被删除 .
关于该repo内部的集成测试,参见:
许可证
阿帕奇 - 2.0