@reinsjs/agent 0.4.3 → 0.5.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/CHANGELOG.md +22 -0
- package/README.md +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +23 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @reinsjs/agent
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 45bfc8d: Stop semantics, no replay of interrupted writes, a stop request in the server, and narrower resume validation.
|
|
8
|
+
|
|
9
|
+
- **Stop is a kind of abort.** `controller.abort(new RunStop())` makes every pause point close the pending tool calls with an error result ("Not executed: the run was stopped by the user…"; calls that had already started get "outcome unknown" or "interrupted"), so the next run asks the model directly instead of re-running what the user stopped. Approval-pending and client-tool calls are closed too. The result is still `paused` / `host` with the note `stopped by the user`. `new RunStop("host")` is for a host closing calls left by an interrupted run: the same closing, but the texts say an earlier run was interrupted instead of blaming the user (`stopTextsOf`). A run whose signal is already aborted when it starts no longer writes `core.tools_bound` or the tool-change note. Other abort reasons keep the old behaviour (pending calls are replayed on resume). An abort that lands while the request is still connecting now ends as `paused(host)` instead of `error`.
|
|
10
|
+
- **Interrupted writes are not replayed.** New `Tool.replayable` (default: only `risk: "low"` tools) and `isReplayable()`. Non-replayable tools get a model-invisible `core.tool_started` event before `execute`; on resume, a started call without a result is closed with "outcome unknown, check the current state before retrying" instead of running again. A signal-aware write tool cut off by an abort gets the same result instead of "Tool execution failed". Tools that do not declare `risk` now write one extra event per call.
|
|
11
|
+
- **Server stop request.** `POST { sessionId, stop: true }` answers JSON `{ status: "stopping" | "idle", sessionId }` without waiting for the run. `SessionAuthzInput.method` gains `"STOP"` — hooks that allow writes only for `"POST"` will refuse stop requests until they accept `"STOP"`.
|
|
12
|
+
- **`asTool`** declares `replayable: true`, resumes a child only when the parent already recorded `tool_started` for that call (a stopped expert's next task is no longer swallowed), and closes a child's leftover pending calls with `RunStop("host")` before starting a new task.
|
|
13
|
+
- **Narrower resume validation.** `SerializedRunState` gains optional `modelDigest` / `pendingToolsDigest` (included in the signature when present). States carrying them only check the model and the definitions of the tools behind the pending calls, so adding an unrelated tool no longer makes paused sessions answer `409 config_mismatch`; older states still compare the whole configuration hash.
|
|
14
|
+
- Lowerings and the AG-UI encoder declare landings for `core.tool_started` (dropped / `CUSTOM`). The TanStack adapter writes `tool_started` in `onBeforeToolCall` and applies the same no-replay rule; stop handling and resume validation stay out of its scope.
|
|
15
|
+
|
|
16
|
+
**Rollback risk:** sessions that contain `core.tool_started` cannot be read by `@reinsjs/core` 0.9 or earlier. Sessions paused inside a sub-agent before upgrading resume the child as a new task after upgrading; let them finish first.
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [45bfc8d]
|
|
21
|
+
- @reinsjs/core@0.10.0
|
|
22
|
+
- @reinsjs/server@0.5.0
|
|
23
|
+
- @reinsjs/ui-agui@0.3.7
|
|
24
|
+
|
|
3
25
|
## 0.4.3
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ const lead = createAgent({
|
|
|
54
54
|
})
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
Whether to call an expert, what to ask, and whether to trust the answer are the parent model's decisions; there is no orchestrator. `asTool` handles what a hand-rolled wrapper gets wrong: when the child pauses for approval the **parent pauses too** (`Interruption { kind: "subagent" }` carries the child's interruptions), the host answers with `decisions: [{ toolCallId, sessionId: childSessionId, approved, by }]`, and the child's tokens count against the parent's budget. `examples/team` in the repository is a complete three-role setup on Postgres.
|
|
57
|
+
Whether to call an expert, what to ask, and whether to trust the answer are the parent model's decisions; there is no orchestrator. `asTool` handles what a hand-rolled wrapper gets wrong: when the child pauses for approval the **parent pauses too** (`Interruption { kind: "subagent" }` carries the child's interruptions), the host answers with `decisions: [{ toolCallId, sessionId: childSessionId, approved, by }]`, and the child's tokens count against the parent's budget. With the default `abort: "linked"`, stopping the parent (`abort(new RunStop())`) stops the child too and closes its pending calls; a later call to the same expert starts a fresh task instead of resuming the stopped one. `examples/team` in the repository is a complete three-role setup on Postgres.
|
|
58
58
|
|
|
59
59
|
## What you get for free
|
|
60
60
|
|
package/dist/index.d.ts
CHANGED
|
@@ -57,8 +57,11 @@ declare function createAgent(options: CreateAgentOptions): Agent;
|
|
|
57
57
|
* childSessionId 写进结果 JSON(模型可见,回放顺着它找子会话)。
|
|
58
58
|
*
|
|
59
59
|
* 子会话 id 缺省 `${父 sessionId}:${toolCallId}`:确定性派生,续跑时不靠内存、不靠宿主传回就能找回子会话。
|
|
60
|
-
*
|
|
61
|
-
*
|
|
60
|
+
* **父日志里本次调用已有 `core.tool_started`** 即父在补跑同一个调用 → 续跑子 run(不带 input,宿主给子会话的结论从 `ctx.decisions`
|
|
61
|
+
* 原样下传,多层嵌套逐层传);循环在本工具返回 `subagentPause` 后必补写这条,所以识别得出。否则当这个专家的新一轮(带 input)——
|
|
62
|
+
* 传自己的 `childSessionId` 就能跟同一个专家多轮对话。新一轮遇到子会话还有遗留 pending(子在等审批时父被停止,子收不到 signal),
|
|
63
|
+
* 先用预中止的 `RunStop("host")` 跑一次子会话收尾(只收尾、不问模型,遗留调用落"之前的 run 中断了"),再带新任务开跑。
|
|
64
|
+
* (2026-11-05 起;之前按"子日志末条是 run_paused"判,停止后同一专家的新任务会被吞掉。)
|
|
62
65
|
* 深度守卫仍靠工具表:专家 agent 的工具表里不放 asTool 类工具。
|
|
63
66
|
*/
|
|
64
67
|
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,8 @@ export * from "@reinsjs/ui-agui";
|
|
|
7
7
|
import {
|
|
8
8
|
createCoreRegistry,
|
|
9
9
|
defineTool,
|
|
10
|
+
pendingToolCalls,
|
|
11
|
+
RunStop,
|
|
10
12
|
readTimeline,
|
|
11
13
|
subagentPause
|
|
12
14
|
} from "@reinsjs/core";
|
|
@@ -33,6 +35,8 @@ function asTool(agent, opts) {
|
|
|
33
35
|
description: opts.description,
|
|
34
36
|
inputSchema: SUBAGENT_TASK_SCHEMA,
|
|
35
37
|
risk: opts.risk ?? "low",
|
|
38
|
+
// 子会话自己能续跑:父中断后补跑同一个调用就是续跑子 run,不能因宿主把 risk 提到 medium / high 就被判成"结果未知"
|
|
39
|
+
replayable: true,
|
|
36
40
|
validate(input) {
|
|
37
41
|
const task = input?.task;
|
|
38
42
|
if (typeof task !== "string" || task.trim() === "") throw new Error("task must be a non-empty string");
|
|
@@ -40,8 +44,18 @@ function asTool(agent, opts) {
|
|
|
40
44
|
},
|
|
41
45
|
async execute(input, ctx) {
|
|
42
46
|
const childSessionId = (opts.childSessionId ?? defaultChildSessionId)(ctx, input);
|
|
43
|
-
const
|
|
44
|
-
|
|
47
|
+
const resuming = await hasStarted(ctx.log, ctx.sessionId, ctx.toolCallId);
|
|
48
|
+
if (!resuming && pendingToolCalls(await readTimeline(log, childSessionId, { registry })).length > 0) {
|
|
49
|
+
const stop = new AbortController();
|
|
50
|
+
stop.abort(new RunStop("host"));
|
|
51
|
+
const settle = agent.run({
|
|
52
|
+
sessionId: childSessionId,
|
|
53
|
+
signal: stop.signal,
|
|
54
|
+
...ctx.principal ? { principal: ctx.principal } : {}
|
|
55
|
+
});
|
|
56
|
+
while (!(await settle.next()).done) {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
45
59
|
const run = agent.run({
|
|
46
60
|
sessionId: childSessionId,
|
|
47
61
|
...resuming ? {} : { input: input.task },
|
|
@@ -104,6 +118,13 @@ function asTool(agent, opts) {
|
|
|
104
118
|
}
|
|
105
119
|
});
|
|
106
120
|
}
|
|
121
|
+
async function hasStarted(parentLog, sessionId, toolCallId) {
|
|
122
|
+
for await (const e of parentLog.read(sessionId)) {
|
|
123
|
+
if (e.type === "core.tool_started" && e.payload.toolCallId === toolCallId)
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
107
128
|
function usageOf(timeline) {
|
|
108
129
|
const usage = { requests: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, toolCalls: 0 };
|
|
109
130
|
for (const e of timeline) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/as-tool.ts","../src/create-agent.ts"],"sourcesContent":["/**\n * reins —— 总包。装上、配自己的模型,就得到一辆有驾驭经验的车(PRD §5.1):\n *\n * import { createAgent, memoryStore } from \"@reinsjs/agent\"\n * import { anthropic } from \"@reinsjs/lowering-pi\"\n * const agent = createAgent({ model: anthropic(\"claude-opus-5\", { apiKey }), tools, store: memoryStore() })\n * export const POST = agent.handler\n *\n * 本包只做一件事:createAgent。其余全部原样再导出 @reinsjs/core、@reinsjs/server、@reinsjs/ui-agui,\n * 用户装一个包就够;降级层(@reinsjs/lowering-pi,带 pi-ai 依赖)单独装,换别的降级层实现不必带着它。\n */\nexport * from \"@reinsjs/core\"\nexport * from \"@reinsjs/server\"\nexport * from \"@reinsjs/ui-agui\"\nexport {\n type AsToolOptions,\n asTool,\n defaultChildSessionId,\n SUBAGENT_TASK_SCHEMA,\n type SubagentOutcome,\n type SubagentTask,\n type SubagentUsage,\n subagentOutcomesOf,\n usageOf,\n} from \"./as-tool.js\"\nexport { type Agent, type CreateAgentOptions, createAgent, type RunOptions } from \"./create-agent.js\"\n","/**\n * asTool:把一个 Agent 包成另一个 Agent 工具表上的 Tool —— 子代理即工具(技术方案 §10.1)。\n *\n * 要不要叫这个专家、叫它做什么、结果信不信,都是父模型在判断(宪法一);库不做编排器,只把\"手写容易做错的两件事\"做对:\n * - **审批冒泡**:子 run 暂停(审批 / 预算 / 中止)时不把状态当 isError 交给父模型,而是返回 `subagentPause`——父 run 不落这条\n * tool_result、整体 paused,宿主拿到 `Interruption(kind=subagent)`,处理子的审批后续跑父 run;父续跑补齐这条 pending 调用时,\n * 本工具凭 childSessionId 续跑子 run。状态全部可序列化,换进程成立。\n * - **预算合算**:子 run 的每条 budget_usage 经 `ctx.spend` 计入父 run 的 tokensSpent,父的 budget 模块按总账拦。\n * 其余三件与手写范式(examples/team/subagent-tool.handwritten.ts)一致:中止传递由使用者自决(abort)、principal 原样下传、\n * childSessionId 写进结果 JSON(模型可见,回放顺着它找子会话)。\n *\n * 子会话 id 缺省 `${父 sessionId}:${toolCallId}`:确定性派生,续跑时不靠内存、不靠宿主传回就能找回子会话。\n * 子日志末条是 run_paused 即续跑(不带 input,宿主给子会话的结论从 `ctx.decisions` 原样下传,多层嵌套逐层传);\n * 否则当这个专家的新一轮(带 input)——传自己的 `childSessionId` 就能跟同一个专家多轮对话。\n * 深度守卫仍靠工具表:专家 agent 的工具表里不放 asTool 类工具。\n */\nimport {\n type BudgetUsagePayload,\n createCoreRegistry,\n defineTool,\n type Event,\n type RunResult,\n readTimeline,\n subagentPause,\n type Tool,\n type ToolContext,\n type ToolResult,\n} from \"@reinsjs/core\"\nimport type { Agent } from \"./create-agent.js\"\n\nexport interface SubagentTask {\n task: string\n}\n\nexport interface AsToolOptions {\n /** 模型看到的工具名,如 ask_analyst */\n name: string\n description: string\n /** 角色名,写进结果 JSON 方便模型与回放辨认 */\n role?: string\n /**\n * 中止传递:linked(缺省)把父的 signal 传给子 run,父停子停;detached 不传,父中止时子做完为止(接力语义)。\n * 循环层保证正在执行的工具跑完、结果落进父日志,父才 paused(host)\n */\n abort?: \"linked\" | \"detached\"\n /** 缺省 low(专家只有只读工具)。专家能改东西就提高,让父侧 approval 模块问人 */\n risk?: \"low\" | \"medium\" | \"high\"\n /** 子会话 id;缺省 `${父 sessionId}:${toolCallId}`。返回已有会话即跟同一个专家继续多轮 */\n childSessionId?: (ctx: ToolContext, input: SubagentTask) => string\n}\n\n/** 子 run 的用量汇总(从子会话时间线算,含续跑之前的部分) */\nexport interface SubagentUsage {\n requests: number\n input: number\n output: number\n cacheRead: number\n cacheWrite: number\n toolCalls: number\n}\n\n/** 写进父 tool_result 的 JSON(模型可见) */\nexport interface SubagentOutcome {\n role?: string\n childSessionId: string\n /** 只会是 done / handoff / error:paused 不落结果而是冒泡 */\n status: RunResult[\"status\"]\n /** 子 run 最后一轮的文本(最后一次工具调用之后的 model_text 拼接) */\n answer?: string\n /** handoff 时的目标会话,error 时的错误 */\n detail?: string\n usage: SubagentUsage\n}\n\nexport const SUBAGENT_TASK_SCHEMA: Record<string, unknown> = {\n type: \"object\",\n properties: {\n task: {\n type: \"string\",\n description:\n \"What you want this expert to do, self-contained: the expert sees none of your conversation.\",\n },\n },\n required: [\"task\"],\n additionalProperties: false,\n}\n\n/** 缺省子会话 id:父会话 + 这次调用,确定性派生 */\nexport function defaultChildSessionId(ctx: ToolContext): string {\n return `${ctx.sessionId}:${ctx.toolCallId}`\n}\n\nexport function asTool(agent: Agent, opts: AsToolOptions): Tool {\n const abort = opts.abort ?? \"linked\"\n const registry = agent.definition.registry ?? createCoreRegistry()\n const log = agent.definition.log\n\n return defineTool<SubagentTask>({\n name: opts.name,\n description: opts.description,\n inputSchema: SUBAGENT_TASK_SCHEMA,\n risk: opts.risk ?? \"low\",\n validate(input) {\n const task = (input as { task?: unknown } | null)?.task\n if (typeof task !== \"string\" || task.trim() === \"\") throw new Error(\"task must be a non-empty string\")\n return { task }\n },\n async execute(input, ctx) {\n const childSessionId = (opts.childSessionId ?? defaultChildSessionId)(ctx, input)\n // 子日志末条是 run_paused → 这是父续跑补齐 pending:续跑子 run,不再追加 input\n const last = (await log.tail(childSessionId, 1))[0]\n const resuming = last?.type === \"core.run_paused\"\n\n const run = agent.run({\n sessionId: childSessionId,\n ...(resuming ? {} : { input: input.task }),\n // 宿主给别的会话的结论原样下传:子循环把 sessionId 等于自己的拿去校验并记事件,其余再转发给它的工具(多层嵌套)\n ...(ctx.decisions && ctx.decisions.length > 0 ? { decisions: ctx.decisions } : {}),\n ...(ctx.principal ? { principal: ctx.principal } : {}),\n ...(abort === \"linked\" && ctx.signal ? { signal: ctx.signal } : {}),\n })\n\n let answer: string[] = []\n let result: RunResult\n while (true) {\n const step = await run.next()\n if (step.done) {\n result = step.value\n break\n }\n const e = step.value\n switch (e.type) {\n case \"core.budget_usage\":\n // 预算合算:子的每次模型请求都记到父 run 的账上\n ctx.spend?.((e.payload as BudgetUsagePayload).tokens)\n break\n case \"core.tool_call\":\n answer = [] // 答案 = 最后一次工具调用之后的文本\n break\n case \"core.model_text\":\n answer.push((e.payload as { text: string }).text)\n break\n }\n }\n\n if (result.status === \"paused\") {\n // 审批冒泡:不落结果,父 run 整体暂停;宿主处理子的中断后续跑父,父补齐这条 pending 时本工具再进来续跑子\n return subagentPause({\n childSessionId,\n reason: result.reason,\n interruptions: result.interruptions,\n state: result.state,\n })\n }\n\n const outcome: SubagentOutcome = {\n ...(opts.role !== undefined ? { role: opts.role } : {}),\n childSessionId,\n status: result.status,\n usage: usageOf(await readTimeline(log, childSessionId, { registry })),\n }\n let isError = false\n switch (result.status) {\n case \"done\":\n outcome.answer = answer.join(\"\\n\")\n break\n case \"handoff\":\n outcome.answer = answer.join(\"\\n\")\n outcome.detail = `expert handed off to session ${result.toSessionId}; its work continues there`\n break\n case \"error\":\n isError = true\n outcome.detail = `expert failed: ${(result.error.payload as { message?: string }).message ?? \"unknown error\"}`\n break\n }\n const res: ToolResult = { content: [{ type: \"text\", text: JSON.stringify(outcome, null, 2) }], isError }\n return res\n },\n })\n}\n\n/** 子会话时间线里的用量合计:每条 budget_usage 一次请求,tool_call 计工具次数 */\nexport function usageOf(timeline: readonly Event[]): SubagentUsage {\n const usage: SubagentUsage = { requests: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, toolCalls: 0 }\n for (const e of timeline) {\n if (e.type === \"core.budget_usage\") {\n const t = (e.payload as BudgetUsagePayload).tokens\n usage.requests += 1\n usage.input += t.input\n usage.output += t.output\n usage.cacheRead += t.cacheRead ?? 0\n usage.cacheWrite += t.cacheWrite ?? 0\n } else if (e.type === \"core.tool_call\") usage.toolCalls += 1\n }\n return usage\n}\n\n/** 父时间线里,凡 name 在 toolNames 里的 tool_result,其 JSON 里的 childSessionId —— 回放 / 导出用 */\nexport function subagentOutcomesOf(\n events: Iterable<{ type: string; payload: unknown }>,\n toolNames: ReadonlySet<string>,\n): SubagentOutcome[] {\n const found: SubagentOutcome[] = []\n for (const e of events) {\n if (e.type !== \"core.tool_result\") continue\n const p = e.payload as { name: string; content: { type: string; text?: string }[] }\n if (!toolNames.has(p.name)) continue\n const text = p.content.find((c) => c.type === \"text\")?.text\n if (!text) continue\n try {\n const parsed = JSON.parse(text) as Partial<SubagentOutcome>\n if (typeof parsed.childSessionId === \"string\") found.push(parsed as SubagentOutcome)\n } catch {\n // 不是 asTool 写的结果(如入参校验失败的 isError 文本),跳过\n }\n }\n return found\n}\n","/**\n * createAgent:把\"模型 + 工具 + 存储 + 脑子\"装成一个 agent 对象,给出两种用法 ——\n * `handler`(Web 标准 (Request) => Response,缺省 AG-UI 编码)和 `run()`(不经 HTTP 直接跑,脚本 / 队列 / 测试用)。\n *\n * 它不引入新概念:AgentDefinition 就是 runLoop 的跨请求配置,handler 就是 createAgentHandler。\n * 这里只负责把 BoundModel 拆成 model + lowering、把 Stores 拆成 log / blobs / memory(runLease 则换成 handler 的登记表)。\n */\nimport {\n type ApprovalDecisionInput,\n type BoundModel,\n type Event,\n type LoopConfig,\n type LoweringDelta,\n type Principal,\n type RunResult,\n runLoop,\n type SerializedRunState,\n type Stores,\n uuidv7,\n} from \"@reinsjs/core\"\nimport {\n type AgentDefinition,\n type AgentHandler,\n createAgentHandler,\n type HandlerOptions,\n leasedRunRegistry,\n} from \"@reinsjs/server\"\nimport { aguiEncoding } from \"@reinsjs/ui-agui\"\n\nexport interface CreateAgentOptions\n extends Omit<AgentDefinition, \"model\" | \"lowering\" | \"log\" | \"blobs\" | \"memory\"> {\n /** `anthropic(\"claude-opus-5\", { apiKey })` 之类工厂的返回值,或自己组的 { model, lowering } */\n model: BoundModel\n /**\n * `memoryStore()`、`sqliteStores(db)`、`await pgStores(client)`(B9)或自己实现的一套接口;只有 log 必需。\n * 带 `runLease` 时 handler 自动用跨进程的租约登记表(D4)——多实例部署同一条会话同时只跑一个 run\n */\n store: Stores\n /** 传输层选项;缺省 AG-UI 编码(`encode: aguiEncoding()`),想推原始事件就传 `encode: () => rawEncoder` */\n handler?: HandlerOptions\n}\n\n/** `agent.run()` 的每次参数:与 HTTP 请求体是同一组字段,多了 signal / onDelta / principal 这些进程内才有的 */\nexport interface RunOptions {\n /** 缺省新建会话;结果的 sessionId 告诉你它是什么 */\n sessionId?: string\n /** 与 `runLoop` 同一类型:进程内可传草稿数组(如 `[system_note(kind: \"host\"), user_message]`);HTTP 请求体不收草稿数组 */\n input?: NonNullable<LoopConfig[\"input\"]>\n resume?: SerializedRunState\n decisions?: readonly ApprovalDecisionInput[]\n principal?: Principal\n signal?: AbortSignal\n onDelta?: (delta: LoweringDelta) => void\n}\n\nexport interface Agent {\n /** 跨请求不变的循环配置;要自己起 runLoop 或接别的传输层时用它 */\n readonly definition: AgentDefinition\n /** Web 标准 handler:POST 起 run、GET 补发,见 @reinsjs/server */\n readonly handler: AgentHandler\n /** 不经 HTTP 直接跑一次:yield 每条刚 append 的事件,返回 RunResult 四态 */\n run(options?: RunOptions): AsyncGenerator<Event, RunResult>\n}\n\nexport function createAgent(options: CreateAgentOptions): Agent {\n const { model, store, handler: handlerOptions, ...rest } = options\n const definition: AgentDefinition = {\n ...rest,\n model: model.model,\n lowering: model.lowering,\n log: store.log,\n ...(store.blobs ? { blobs: store.blobs } : {}),\n ...(store.memory ? { memory: store.memory } : {}),\n }\n // store 带 runLease(如 pgStores)就自动装跨进程的租约登记表(D4),宿主不用记这一步;自己传 handler.runs 则以宿主为准\n const runs =\n store.runLease !== undefined\n ? {\n runs: leasedRunRegistry(store.runLease, {\n ...(handlerOptions?.warn !== undefined ? { warn: handlerOptions.warn } : {}),\n }),\n }\n : {}\n const handler = createAgentHandler(definition, { encode: aguiEncoding(), ...runs, ...handlerOptions })\n return {\n definition,\n handler,\n run(runOptions = {}) {\n const { sessionId, ...perRun } = runOptions\n return runLoop({ ...definition, ...stripUndefined(perRun), sessionId: sessionId ?? uuidv7() })\n },\n }\n}\n\n/** exactOptionalPropertyTypes 下不能把 undefined 传给可选字段,这里把没给的键去掉 */\nfunction stripUndefined<T extends object>(obj: T): T {\n return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)) as T\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/as-tool.ts","../src/create-agent.ts"],"sourcesContent":["/**\n * reins —— 总包。装上、配自己的模型,就得到一辆有驾驭经验的车(PRD §5.1):\n *\n * import { createAgent, memoryStore } from \"@reinsjs/agent\"\n * import { anthropic } from \"@reinsjs/lowering-pi\"\n * const agent = createAgent({ model: anthropic(\"claude-opus-5\", { apiKey }), tools, store: memoryStore() })\n * export const POST = agent.handler\n *\n * 本包只做一件事:createAgent。其余全部原样再导出 @reinsjs/core、@reinsjs/server、@reinsjs/ui-agui,\n * 用户装一个包就够;降级层(@reinsjs/lowering-pi,带 pi-ai 依赖)单独装,换别的降级层实现不必带着它。\n */\nexport * from \"@reinsjs/core\"\nexport * from \"@reinsjs/server\"\nexport * from \"@reinsjs/ui-agui\"\nexport {\n type AsToolOptions,\n asTool,\n defaultChildSessionId,\n SUBAGENT_TASK_SCHEMA,\n type SubagentOutcome,\n type SubagentTask,\n type SubagentUsage,\n subagentOutcomesOf,\n usageOf,\n} from \"./as-tool.js\"\nexport { type Agent, type CreateAgentOptions, createAgent, type RunOptions } from \"./create-agent.js\"\n","/**\n * asTool:把一个 Agent 包成另一个 Agent 工具表上的 Tool —— 子代理即工具(技术方案 §10.1)。\n *\n * 要不要叫这个专家、叫它做什么、结果信不信,都是父模型在判断(宪法一);库不做编排器,只把\"手写容易做错的两件事\"做对:\n * - **审批冒泡**:子 run 暂停(审批 / 预算 / 中止)时不把状态当 isError 交给父模型,而是返回 `subagentPause`——父 run 不落这条\n * tool_result、整体 paused,宿主拿到 `Interruption(kind=subagent)`,处理子的审批后续跑父 run;父续跑补齐这条 pending 调用时,\n * 本工具凭 childSessionId 续跑子 run。状态全部可序列化,换进程成立。\n * - **预算合算**:子 run 的每条 budget_usage 经 `ctx.spend` 计入父 run 的 tokensSpent,父的 budget 模块按总账拦。\n * 其余三件与手写范式(examples/team/subagent-tool.handwritten.ts)一致:中止传递由使用者自决(abort)、principal 原样下传、\n * childSessionId 写进结果 JSON(模型可见,回放顺着它找子会话)。\n *\n * 子会话 id 缺省 `${父 sessionId}:${toolCallId}`:确定性派生,续跑时不靠内存、不靠宿主传回就能找回子会话。\n * **父日志里本次调用已有 `core.tool_started`** 即父在补跑同一个调用 → 续跑子 run(不带 input,宿主给子会话的结论从 `ctx.decisions`\n * 原样下传,多层嵌套逐层传);循环在本工具返回 `subagentPause` 后必补写这条,所以识别得出。否则当这个专家的新一轮(带 input)——\n * 传自己的 `childSessionId` 就能跟同一个专家多轮对话。新一轮遇到子会话还有遗留 pending(子在等审批时父被停止,子收不到 signal),\n * 先用预中止的 `RunStop(\"host\")` 跑一次子会话收尾(只收尾、不问模型,遗留调用落\"之前的 run 中断了\"),再带新任务开跑。\n * (2026-11-05 起;之前按\"子日志末条是 run_paused\"判,停止后同一专家的新任务会被吞掉。)\n * 深度守卫仍靠工具表:专家 agent 的工具表里不放 asTool 类工具。\n */\nimport {\n type BudgetUsagePayload,\n createCoreRegistry,\n defineTool,\n type Event,\n type EventLog,\n pendingToolCalls,\n type RunResult,\n RunStop,\n readTimeline,\n subagentPause,\n type Tool,\n type ToolContext,\n type ToolResult,\n} from \"@reinsjs/core\"\nimport type { Agent } from \"./create-agent.js\"\n\nexport interface SubagentTask {\n task: string\n}\n\nexport interface AsToolOptions {\n /** 模型看到的工具名,如 ask_analyst */\n name: string\n description: string\n /** 角色名,写进结果 JSON 方便模型与回放辨认 */\n role?: string\n /**\n * 中止传递:linked(缺省)把父的 signal 传给子 run,父停子停;detached 不传,父中止时子做完为止(接力语义)。\n * 循环层保证正在执行的工具跑完、结果落进父日志,父才 paused(host)\n */\n abort?: \"linked\" | \"detached\"\n /** 缺省 low(专家只有只读工具)。专家能改东西就提高,让父侧 approval 模块问人 */\n risk?: \"low\" | \"medium\" | \"high\"\n /** 子会话 id;缺省 `${父 sessionId}:${toolCallId}`。返回已有会话即跟同一个专家继续多轮 */\n childSessionId?: (ctx: ToolContext, input: SubagentTask) => string\n}\n\n/** 子 run 的用量汇总(从子会话时间线算,含续跑之前的部分) */\nexport interface SubagentUsage {\n requests: number\n input: number\n output: number\n cacheRead: number\n cacheWrite: number\n toolCalls: number\n}\n\n/** 写进父 tool_result 的 JSON(模型可见) */\nexport interface SubagentOutcome {\n role?: string\n childSessionId: string\n /** 只会是 done / handoff / error:paused 不落结果而是冒泡 */\n status: RunResult[\"status\"]\n /** 子 run 最后一轮的文本(最后一次工具调用之后的 model_text 拼接) */\n answer?: string\n /** handoff 时的目标会话,error 时的错误 */\n detail?: string\n usage: SubagentUsage\n}\n\nexport const SUBAGENT_TASK_SCHEMA: Record<string, unknown> = {\n type: \"object\",\n properties: {\n task: {\n type: \"string\",\n description:\n \"What you want this expert to do, self-contained: the expert sees none of your conversation.\",\n },\n },\n required: [\"task\"],\n additionalProperties: false,\n}\n\n/** 缺省子会话 id:父会话 + 这次调用,确定性派生 */\nexport function defaultChildSessionId(ctx: ToolContext): string {\n return `${ctx.sessionId}:${ctx.toolCallId}`\n}\n\nexport function asTool(agent: Agent, opts: AsToolOptions): Tool {\n const abort = opts.abort ?? \"linked\"\n const registry = agent.definition.registry ?? createCoreRegistry()\n const log = agent.definition.log\n\n return defineTool<SubagentTask>({\n name: opts.name,\n description: opts.description,\n inputSchema: SUBAGENT_TASK_SCHEMA,\n risk: opts.risk ?? \"low\",\n // 子会话自己能续跑:父中断后补跑同一个调用就是续跑子 run,不能因宿主把 risk 提到 medium / high 就被判成\"结果未知\"\n replayable: true,\n validate(input) {\n const task = (input as { task?: unknown } | null)?.task\n if (typeof task !== \"string\" || task.trim() === \"\") throw new Error(\"task must be a non-empty string\")\n return { task }\n },\n async execute(input, ctx) {\n const childSessionId = (opts.childSessionId ?? defaultChildSessionId)(ctx, input)\n // 父日志里本调用已开始动手 → 父在补跑同一个调用:续跑子 run,不再追加 input\n const resuming = await hasStarted(ctx.log, ctx.sessionId, ctx.toolCallId)\n if (!resuming && pendingToolCalls(await readTimeline(log, childSessionId, { registry })).length > 0) {\n // 新一轮,但子会话还有遗留 pending:先按停止收尾,否则带新任务开跑时会先补跑 / 再挂起那些旧调用。\n // 用 RunStop(\"host\"):遗留可能是父进程中途死亡留下的,也可能是用户停止时子在等审批——这里分不清,\n // 子模型读到的是\"之前的 run 中断了\",不说\"用户停止\"\n const stop = new AbortController()\n stop.abort(new RunStop(\"host\"))\n const settle = agent.run({\n sessionId: childSessionId,\n signal: stop.signal,\n ...(ctx.principal ? { principal: ctx.principal } : {}),\n })\n while (!(await settle.next()).done) {}\n }\n\n const run = agent.run({\n sessionId: childSessionId,\n ...(resuming ? {} : { input: input.task }),\n // 宿主给别的会话的结论原样下传:子循环把 sessionId 等于自己的拿去校验并记事件,其余再转发给它的工具(多层嵌套)\n ...(ctx.decisions && ctx.decisions.length > 0 ? { decisions: ctx.decisions } : {}),\n ...(ctx.principal ? { principal: ctx.principal } : {}),\n ...(abort === \"linked\" && ctx.signal ? { signal: ctx.signal } : {}),\n })\n\n let answer: string[] = []\n let result: RunResult\n while (true) {\n const step = await run.next()\n if (step.done) {\n result = step.value\n break\n }\n const e = step.value\n switch (e.type) {\n case \"core.budget_usage\":\n // 预算合算:子的每次模型请求都记到父 run 的账上\n ctx.spend?.((e.payload as BudgetUsagePayload).tokens)\n break\n case \"core.tool_call\":\n answer = [] // 答案 = 最后一次工具调用之后的文本\n break\n case \"core.model_text\":\n answer.push((e.payload as { text: string }).text)\n break\n }\n }\n\n if (result.status === \"paused\") {\n // 审批冒泡:不落结果,父 run 整体暂停;宿主处理子的中断后续跑父,父补齐这条 pending 时本工具再进来续跑子\n return subagentPause({\n childSessionId,\n reason: result.reason,\n interruptions: result.interruptions,\n state: result.state,\n })\n }\n\n const outcome: SubagentOutcome = {\n ...(opts.role !== undefined ? { role: opts.role } : {}),\n childSessionId,\n status: result.status,\n usage: usageOf(await readTimeline(log, childSessionId, { registry })),\n }\n let isError = false\n switch (result.status) {\n case \"done\":\n outcome.answer = answer.join(\"\\n\")\n break\n case \"handoff\":\n outcome.answer = answer.join(\"\\n\")\n outcome.detail = `expert handed off to session ${result.toSessionId}; its work continues there`\n break\n case \"error\":\n isError = true\n outcome.detail = `expert failed: ${(result.error.payload as { message?: string }).message ?? \"unknown error\"}`\n break\n }\n const res: ToolResult = { content: [{ type: \"text\", text: JSON.stringify(outcome, null, 2) }], isError }\n return res\n },\n })\n}\n\n/** 父会话里这次调用是否已有 `core.tool_started`(只看 type 与 toolCallId,不经注册表:父可能有子不认识的 ext.* 事件) */\nasync function hasStarted(parentLog: EventLog, sessionId: string, toolCallId: string): Promise<boolean> {\n for await (const e of parentLog.read(sessionId)) {\n if (e.type === \"core.tool_started\" && (e.payload as { toolCallId?: unknown }).toolCallId === toolCallId)\n return true\n }\n return false\n}\n\n/** 子会话时间线里的用量合计:每条 budget_usage 一次请求,tool_call 计工具次数 */\nexport function usageOf(timeline: readonly Event[]): SubagentUsage {\n const usage: SubagentUsage = { requests: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, toolCalls: 0 }\n for (const e of timeline) {\n if (e.type === \"core.budget_usage\") {\n const t = (e.payload as BudgetUsagePayload).tokens\n usage.requests += 1\n usage.input += t.input\n usage.output += t.output\n usage.cacheRead += t.cacheRead ?? 0\n usage.cacheWrite += t.cacheWrite ?? 0\n } else if (e.type === \"core.tool_call\") usage.toolCalls += 1\n }\n return usage\n}\n\n/** 父时间线里,凡 name 在 toolNames 里的 tool_result,其 JSON 里的 childSessionId —— 回放 / 导出用 */\nexport function subagentOutcomesOf(\n events: Iterable<{ type: string; payload: unknown }>,\n toolNames: ReadonlySet<string>,\n): SubagentOutcome[] {\n const found: SubagentOutcome[] = []\n for (const e of events) {\n if (e.type !== \"core.tool_result\") continue\n const p = e.payload as { name: string; content: { type: string; text?: string }[] }\n if (!toolNames.has(p.name)) continue\n const text = p.content.find((c) => c.type === \"text\")?.text\n if (!text) continue\n try {\n const parsed = JSON.parse(text) as Partial<SubagentOutcome>\n if (typeof parsed.childSessionId === \"string\") found.push(parsed as SubagentOutcome)\n } catch {\n // 不是 asTool 写的结果(如入参校验失败的 isError 文本),跳过\n }\n }\n return found\n}\n","/**\n * createAgent:把\"模型 + 工具 + 存储 + 脑子\"装成一个 agent 对象,给出两种用法 ——\n * `handler`(Web 标准 (Request) => Response,缺省 AG-UI 编码)和 `run()`(不经 HTTP 直接跑,脚本 / 队列 / 测试用)。\n *\n * 它不引入新概念:AgentDefinition 就是 runLoop 的跨请求配置,handler 就是 createAgentHandler。\n * 这里只负责把 BoundModel 拆成 model + lowering、把 Stores 拆成 log / blobs / memory(runLease 则换成 handler 的登记表)。\n */\nimport {\n type ApprovalDecisionInput,\n type BoundModel,\n type Event,\n type LoopConfig,\n type LoweringDelta,\n type Principal,\n type RunResult,\n runLoop,\n type SerializedRunState,\n type Stores,\n uuidv7,\n} from \"@reinsjs/core\"\nimport {\n type AgentDefinition,\n type AgentHandler,\n createAgentHandler,\n type HandlerOptions,\n leasedRunRegistry,\n} from \"@reinsjs/server\"\nimport { aguiEncoding } from \"@reinsjs/ui-agui\"\n\nexport interface CreateAgentOptions\n extends Omit<AgentDefinition, \"model\" | \"lowering\" | \"log\" | \"blobs\" | \"memory\"> {\n /** `anthropic(\"claude-opus-5\", { apiKey })` 之类工厂的返回值,或自己组的 { model, lowering } */\n model: BoundModel\n /**\n * `memoryStore()`、`sqliteStores(db)`、`await pgStores(client)`(B9)或自己实现的一套接口;只有 log 必需。\n * 带 `runLease` 时 handler 自动用跨进程的租约登记表(D4)——多实例部署同一条会话同时只跑一个 run\n */\n store: Stores\n /** 传输层选项;缺省 AG-UI 编码(`encode: aguiEncoding()`),想推原始事件就传 `encode: () => rawEncoder` */\n handler?: HandlerOptions\n}\n\n/** `agent.run()` 的每次参数:与 HTTP 请求体是同一组字段,多了 signal / onDelta / principal 这些进程内才有的 */\nexport interface RunOptions {\n /** 缺省新建会话;结果的 sessionId 告诉你它是什么 */\n sessionId?: string\n /** 与 `runLoop` 同一类型:进程内可传草稿数组(如 `[system_note(kind: \"host\"), user_message]`);HTTP 请求体不收草稿数组 */\n input?: NonNullable<LoopConfig[\"input\"]>\n resume?: SerializedRunState\n decisions?: readonly ApprovalDecisionInput[]\n principal?: Principal\n signal?: AbortSignal\n onDelta?: (delta: LoweringDelta) => void\n}\n\nexport interface Agent {\n /** 跨请求不变的循环配置;要自己起 runLoop 或接别的传输层时用它 */\n readonly definition: AgentDefinition\n /** Web 标准 handler:POST 起 run、GET 补发,见 @reinsjs/server */\n readonly handler: AgentHandler\n /** 不经 HTTP 直接跑一次:yield 每条刚 append 的事件,返回 RunResult 四态 */\n run(options?: RunOptions): AsyncGenerator<Event, RunResult>\n}\n\nexport function createAgent(options: CreateAgentOptions): Agent {\n const { model, store, handler: handlerOptions, ...rest } = options\n const definition: AgentDefinition = {\n ...rest,\n model: model.model,\n lowering: model.lowering,\n log: store.log,\n ...(store.blobs ? { blobs: store.blobs } : {}),\n ...(store.memory ? { memory: store.memory } : {}),\n }\n // store 带 runLease(如 pgStores)就自动装跨进程的租约登记表(D4),宿主不用记这一步;自己传 handler.runs 则以宿主为准\n const runs =\n store.runLease !== undefined\n ? {\n runs: leasedRunRegistry(store.runLease, {\n ...(handlerOptions?.warn !== undefined ? { warn: handlerOptions.warn } : {}),\n }),\n }\n : {}\n const handler = createAgentHandler(definition, { encode: aguiEncoding(), ...runs, ...handlerOptions })\n return {\n definition,\n handler,\n run(runOptions = {}) {\n const { sessionId, ...perRun } = runOptions\n return runLoop({ ...definition, ...stripUndefined(perRun), sessionId: sessionId ?? uuidv7() })\n },\n }\n}\n\n/** exactOptionalPropertyTypes 下不能把 undefined 传给可选字段,这里把没给的键去掉 */\nfunction stripUndefined<T extends object>(obj: T): T {\n return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)) as T\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reinsjs/agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "reins — hand the reins to the model. createAgent() + re-exports of @reinsjs/core, @reinsjs/server and @reinsjs/ui-agui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"CHANGELOG.md"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@reinsjs/core": "0.
|
|
39
|
-
"@reinsjs/server": "0.
|
|
40
|
-
"@reinsjs/ui-agui": "0.3.
|
|
38
|
+
"@reinsjs/core": "0.10.0",
|
|
39
|
+
"@reinsjs/server": "0.5.0",
|
|
40
|
+
"@reinsjs/ui-agui": "0.3.7"
|
|
41
41
|
},
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=22"
|