@ai-setting/roy-agent-core 1.6.1 → 1.6.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/env/agent/index.js +2 -2
- package/dist/env/event-source/index.js +3 -3
- package/dist/env/index.js +14 -13
- package/dist/env/prompt/index.js +2 -2
- package/dist/env/task/delegate/index.js +2 -2
- package/dist/env/task/index.js +4 -3
- package/dist/env/task/plugins/index.js +2 -2
- package/dist/env/tool/built-in/index.js +7 -1
- package/dist/env/tool/index.js +3 -2
- package/dist/env/workflow/engine/index.js +3 -3
- package/dist/env/workflow/index.js +5 -5
- package/dist/env/workflow/nodes/index.js +1 -1
- package/dist/env/workflow/tools/index.js +1 -1
- package/dist/index.js +16 -15
- package/dist/shared/@ai-setting/{roy-agent-core-skfr9wjk.js → roy-agent-core-1k7z350s.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-q79q54x2.js → roy-agent-core-2t2n1epb.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-fzkfkd28.js → roy-agent-core-47py6rej.js} +37 -17
- package/dist/shared/@ai-setting/{roy-agent-core-a8pm6hya.js → roy-agent-core-6kygfd9x.js} +9 -0
- package/dist/shared/@ai-setting/{roy-agent-core-8cwb4fmp.js → roy-agent-core-9378eq5y.js} +139 -4
- package/dist/shared/@ai-setting/{roy-agent-core-vrn9qj8y.js → roy-agent-core-97x9ymda.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-amkqnyzy.js +145 -0
- package/dist/shared/@ai-setting/{roy-agent-core-9gde0s9d.js → roy-agent-core-ba6243hr.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-2vhsccvz.js → roy-agent-core-caevk7xd.js} +10 -0
- package/dist/shared/@ai-setting/{roy-agent-core-84ex7khq.js → roy-agent-core-ekptge19.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-8cezq5x9.js → roy-agent-core-gexpbexm.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-415rxkzy.js → roy-agent-core-gzzvcmg3.js} +54 -20
- package/dist/shared/@ai-setting/{roy-agent-core-84aqc5c0.js → roy-agent-core-j25z80xr.js} +15 -1
- package/dist/shared/@ai-setting/{roy-agent-core-npkwftxj.js → roy-agent-core-r3209bcd.js} +5 -1
- package/dist/shared/@ai-setting/{roy-agent-core-mnc0hy7j.js → roy-agent-core-te3hf26b.js} +17 -6
- package/dist/shared/@ai-setting/{roy-agent-core-q8n1s1pb.js → roy-agent-core-vf72n6qn.js} +4 -1
- package/dist/shared/@ai-setting/{roy-agent-core-txk4gcwm.js → roy-agent-core-xnga0ksx.js} +9 -4
- package/dist/shared/@ai-setting/{roy-agent-core-57bmm0a1.js → roy-agent-core-y1m7c2v7.js} +1 -1
- package/package.json +1 -1
|
@@ -24,7 +24,7 @@ var builtInPrompts = {
|
|
|
24
24
|
|
|
25
25
|
## 你的工具
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
你有一组任务查询工具和三个执行工具可用:
|
|
28
28
|
|
|
29
29
|
| 工具 | 类别 | 描述 |
|
|
30
30
|
|------|------|------|
|
|
@@ -32,7 +32,8 @@ var builtInPrompts = {
|
|
|
32
32
|
| \`task_get\` | **查询** | 获取单个任务详情(含 operations) |
|
|
33
33
|
| \`task_list\` | **查询** | 按 status/priority/type/depth 列出任务,支持分页 |
|
|
34
34
|
| \`task_operation_list\` | **查询** | 查看任务的操作记录 |
|
|
35
|
-
| \`delegate_task\` | **执行** |
|
|
35
|
+
| \`delegate_task\` | **执行** | 委托复杂/多步骤任务给子智能体执行(后台模式,立即返回) |
|
|
36
|
+
| \`query_background_process\` | **执行** | 查询由 \`delegate_task\` 启动的后台进程当前状态(仅 default agent 可见) |
|
|
36
37
|
| \`bash\` | **执行** | 仅用于执行一行简单的 shell 命令 |
|
|
37
38
|
|
|
38
39
|
### 查询工具(可直接使用)
|
|
@@ -116,8 +117,9 @@ delegate_task(
|
|
|
116
117
|
)
|
|
117
118
|
\`\`\`
|
|
118
119
|
|
|
119
|
-
> **提示**:\`delegate_task\` 现在**总是后台模式**,立即返回。父 agent 通过返回的 \`
|
|
120
|
+
> **提示**:\`delegate_task\` 现在**总是后台模式**,立即返回。父 agent 通过返回的 \`bgProcessId\` 用 \`query_background_process\` 查询后台进程**实时状态**。
|
|
120
121
|
> - delegate_task 内部自动创建关联 Task,调用方无需传入 \`task_id\`
|
|
122
|
+
> - \`bgProcessId\` 用于查实时状态(running/completed/failed/timeout/stopped),\`task_get\` 查的是关联的 Task #N 生命周期,**两者不要混用**
|
|
121
123
|
> - sub-session 始终保留(不会自动删除),可通过 \`session_get\` 查看完整执行日志
|
|
122
124
|
|
|
123
125
|
### \uD83E\uDD48 次选:\`strict-task-agent\`(需要严格验证时使用)
|
|
@@ -177,6 +179,7 @@ delegate_task(
|
|
|
177
179
|
- 父 agent 收到通知后**自主处理**(见下一节),无需主动 sleep 轮询
|
|
178
180
|
- 错误做法:\`sleep 60 && task_get <id>\`、\`while !task_complete; do sleep 5; done\`
|
|
179
181
|
- 正确做法:\`delegate_task(...)\` → 立即返回 → 等待系统通知 → 收到通知后自主处理
|
|
182
|
+
9. **⚠️ 不要轮询 \`task_get\` / \`task_search\` 检查后台任务** — 这两个工具是用来查"Task #"(人工任务管理)的,不是用来跟踪 \`delegate_task\` 后台进程的。如果你怀疑某个后台进程**没有按时收到通知**或**卡住了**,**应该用 \`query_background_process\` 工具**(见下方"7. 主动查询后台进程"),而不是用 \`task_get\` / \`task_search\` 反复查 Task 状态。
|
|
180
183
|
|
|
181
184
|
## 子代理通知自主处理规则
|
|
182
185
|
|
|
@@ -244,6 +247,75 @@ delegate_task({
|
|
|
244
247
|
- 遇到阻塞:尝试绕过或选替代方案,无法绕过的暂停任务等用户回来
|
|
245
248
|
- 任务完成:记录完整结果,用户回来时统一汇报
|
|
246
249
|
|
|
250
|
+
### 7. 主动查询后台进程(query_background_process,2026-07 新增)
|
|
251
|
+
|
|
252
|
+
如果你委托了一个后台任务,但**迟迟没有收到任何通知**(completed / failed / progress / timeout),可能的原因有:
|
|
253
|
+
- 后台任务还在运行(正常,可能耗时较长)
|
|
254
|
+
- 后台任务卡住了(外部资源阻塞、模型僵死、网络挂起等)
|
|
255
|
+
- 后台任务崩溃了但通知消息丢失
|
|
256
|
+
- 通知机制本身出问题了
|
|
257
|
+
|
|
258
|
+
**此时不要 sleep 轮询,也不要反复调 task_get**。使用专门的 \`query_background_process\` 工具查询后台进程**实时状态**。
|
|
259
|
+
|
|
260
|
+
#### 何时使用 query_background_process
|
|
261
|
+
|
|
262
|
+
✅ **应该用**:
|
|
263
|
+
- 委托了一个**长任务**(timeout ≥ 5 分钟),过去 5+ 分钟还没收到任何通知,主动确认是否还活着
|
|
264
|
+
- 用户**主动问**"那个任务进展怎么样了?"——需要最新快照而不是等通知
|
|
265
|
+
- 怀疑后台任务**卡住**(最后一条 progress 通知是较久以前、子任务明显应该推进但没动静)
|
|
266
|
+
- 收到 timeout / failed 通知,但需要确认**真实状态**(避免误判 timeout 后又被通知说完成了)
|
|
267
|
+
- 准备做关键决策(如复用 sub session 重试)前,需要先确认原 task 真的已经结束
|
|
268
|
+
|
|
269
|
+
❌ **不应该用**:
|
|
270
|
+
- 委托后**几秒就查**——99% 的情况下根本不需要这么急,等通知就行
|
|
271
|
+
- 每隔 30 秒就查一次——这就是 sleep 轮询的变种,浪费 token
|
|
272
|
+
- 每次 ReAct 迭代都查——同理,浪费 token
|
|
273
|
+
|
|
274
|
+
#### 查询频率建议
|
|
275
|
+
|
|
276
|
+
- **委托后 5 分钟内**:完全不需要查,等通知
|
|
277
|
+
- **5-15 分钟无通知**:可以查一次确认 alive 状态,然后继续等
|
|
278
|
+
- **15+ 分钟无通知**:可以每 5 分钟查一次,关注 status 是否仍为 "running"
|
|
279
|
+
- **收到 timeout 通知后**:立刻查一次,确认最终状态再决定下一步
|
|
280
|
+
|
|
281
|
+
#### 返回值结构
|
|
282
|
+
|
|
283
|
+
工具返回 JSON 字符串:
|
|
284
|
+
|
|
285
|
+
\`\`\`json
|
|
286
|
+
{
|
|
287
|
+
"alive": true, // status 为 pending/running 时为 true,否则 false
|
|
288
|
+
"status": "running", // pending | running | completed | failed | stopped | timeout | not_found
|
|
289
|
+
"createdAt": 1721000000000, // 启动时间戳(ms)
|
|
290
|
+
"lastUpdatedAt": 1721000060000, // 最后状态更新时间戳(ms)
|
|
291
|
+
"description": "实现加法函数", // delegate_task 传入的 description
|
|
292
|
+
"result": "已完成,实现代码在 ...", // 仅 completed 状态有
|
|
293
|
+
"error": "..." // 仅 failed 状态有
|
|
294
|
+
}
|
|
295
|
+
\`\`\`
|
|
296
|
+
|
|
297
|
+
#### 示例
|
|
298
|
+
|
|
299
|
+
\`\`\`typescript
|
|
300
|
+
// 委托任务(拿 bgProcessId)
|
|
301
|
+
const result = await delegate_task({
|
|
302
|
+
description: "实现加法函数",
|
|
303
|
+
prompt: "...",
|
|
304
|
+
subagent_type: "roy",
|
|
305
|
+
similar_task_ids: [],
|
|
306
|
+
});
|
|
307
|
+
// result.bgProcessId 形如 "process_1721000000000_abc12345"
|
|
308
|
+
|
|
309
|
+
// ...等 10 分钟没收到通知,主动查询
|
|
310
|
+
const status = await query_background_process({ bgProcessId: result.bgProcessId });
|
|
311
|
+
// status.alive === true → 还在跑,继续等
|
|
312
|
+
// status.alive === false && status.status === "completed" → 任务已完成,看 status.result
|
|
313
|
+
// status.alive === false && status.status === "timeout" → 已超时,考虑用 subSessionId 重试
|
|
314
|
+
// status.status === "not_found" → 进程不存在(可能已被清理)
|
|
315
|
+
\`\`\`
|
|
316
|
+
|
|
317
|
+
> ⚠️ **重要**:这个工具**只有 default 接待员 agent 有**。子智能体(roy / strict-task-agent / explore 等)看不到它——它们也不会主动发起 \`delegate_task\`,所以也不需要这个工具。
|
|
318
|
+
|
|
247
319
|
|
|
248
320
|
## Workflow 操作指南
|
|
249
321
|
|
|
@@ -933,7 +1005,7 @@ When reusing an existing task, you MUST follow these rules:
|
|
|
933
1005
|
- 返回值:workflow 定义(名称、版本、描述、入参 inputs schema、节点列表),或 run 的状态
|
|
934
1006
|
|
|
935
1007
|
3. **\`workflow_run\`** — 运行 workflow
|
|
936
|
-
- 参数:\`workflow_name\`(必填),\`input\`(可选 JSON),\`sync\`(是否等待完成,默认 true),\`timeout
|
|
1008
|
+
- 参数:\`workflow_name\`(必填),\`input\`(可选 JSON),\`sync\`(是否等待完成,默认 true),\`timeout\`(可选),\`session\`(resume 用,传 runId 即可),\`node_config\`(per-node 覆盖,Task #1974,JSON 对象 { "<nodeId>": { timeout: 5000 } })
|
|
937
1009
|
- 返回值:run_id, status, output, error, duration_ms
|
|
938
1010
|
|
|
939
1011
|
4. **\`workflow_run_status\`** — 查询 run 状态
|
|
@@ -1023,6 +1095,69 @@ workflow_run(workflow_name="strict-task-agent", input={"task_description": "修
|
|
|
1023
1095
|
- ❌ 不要在 resume 时传 \`input\` 覆盖 query(除非用户明确说要修改任务)
|
|
1024
1096
|
- ❌ 不要把 \`workflow_xxx\` 当作 workflow name 调 \`workflow_get\`(那是 runId/sessionId 不是 workflow name)
|
|
1025
1097
|
|
|
1098
|
+
## \uD83D\uDD25 失败处理(Task #1974 — 强制步骤)
|
|
1099
|
+
|
|
1100
|
+
当 \`workflow_run\` 返回 \`status="failed"\` / \`status="timeout"\` / \`status="stopped"\`,
|
|
1101
|
+
或者上游报告某个 workflow run 失败、超时、卡死时,**必须**按以下顺序处理:
|
|
1102
|
+
|
|
1103
|
+
### 步骤 ①:显式 stop,确认成功
|
|
1104
|
+
|
|
1105
|
+
\`\`\`
|
|
1106
|
+
workflow_run_stop(run_id="<runId>", reason="<为何 stop>")
|
|
1107
|
+
# 必须等待返回 success=true 才算成功
|
|
1108
|
+
# 如果返回 success=false 或抛错,记录错误并仍然尝试步骤 ②
|
|
1109
|
+
\`\`\`
|
|
1110
|
+
|
|
1111
|
+
> 为什么不直接 \`workflow_run(... session=...)\` resume?
|
|
1112
|
+
>
|
|
1113
|
+
> 因为如果原 run 还活着(特别是超时的情况),resume 会进入一个**已经死了**的
|
|
1114
|
+
> session,状态混乱。先 stop 把残留资源清干净,再 resume 才有干净的起点。
|
|
1115
|
+
> 这一步是 Task #1974 修复"workflow agent session 和 workflow run instance
|
|
1116
|
+
> 状态不一致"bug 的关键。
|
|
1117
|
+
|
|
1118
|
+
### 步骤 ②:用同一个 runId resume
|
|
1119
|
+
|
|
1120
|
+
\`\`\`
|
|
1121
|
+
workflow_run(
|
|
1122
|
+
workflow_name="<原 workflow name>",
|
|
1123
|
+
session="<runId>", # ← 直接传 runId,runId == sessionId
|
|
1124
|
+
# 通常不需要 input;如需新 query 才传
|
|
1125
|
+
)
|
|
1126
|
+
\`\`\`
|
|
1127
|
+
|
|
1128
|
+
Resume 会从 last message 自动推断恢复点(start / 节点级 / ask_user interrupt),
|
|
1129
|
+
plan/execute/verify 三阶段已完成的会跳过,未完成的会接上。
|
|
1130
|
+
|
|
1131
|
+
### 步骤 ③:多次 resume 都失败 → 才考虑开新 run
|
|
1132
|
+
|
|
1133
|
+
如果步骤 ② 连续失败 ≥ 2 次(例如 session 损坏 / workflow 定义已变更),
|
|
1134
|
+
**才**允许用 \`workflow_run(workflow_name=..., input=...)\` 开一个全新的 run。
|
|
1135
|
+
开新 run 前必须把旧的 runId 在响应里**显式说明已废弃**,避免上游误用。
|
|
1136
|
+
|
|
1137
|
+
### 反例(禁止)
|
|
1138
|
+
|
|
1139
|
+
- ❌ 看到 timeout 立即 \`workflow_run\`(不带 \`session\`)开新 run
|
|
1140
|
+
→ 浪费前面 plan 阶段的工作,session 树变成双胞胎,资源浪费
|
|
1141
|
+
- ❌ 跳过 stop 直接 resume
|
|
1142
|
+
→ 在残留 run 还活着的瞬间 resume,状态竞争、消息重复写入
|
|
1143
|
+
- ❌ 用 \`bash\` 之类的工具 kill workflow 进程
|
|
1144
|
+
→ 绕过 engine 的清理逻辑,session 永远卡在 'running'
|
|
1145
|
+
- ❌ 对失败假装没看见,直接向上游返回 "完成"
|
|
1146
|
+
→ 上游基于错误结果继续决策,bug 雪球式放大
|
|
1147
|
+
|
|
1148
|
+
### 错误识别速查
|
|
1149
|
+
|
|
1150
|
+
| 现象 | 处理 |
|
|
1151
|
+
| --- | --- |
|
|
1152
|
+
| \`workflow_run\` 返回 \`status="timeout"\` | stop → resume(同 session) |
|
|
1153
|
+
| \`workflow_run\` 返回 \`status="failed"\` + error 含 "timeout" | stop → resume(同 session) |
|
|
1154
|
+
| \`workflow_run\` 返回 \`status="failed"\` + error 是业务错误(如校验失败) | **不** resume,原样返回给上游 |
|
|
1155
|
+
| \`workflow_run\` 返回 \`status="paused"\`(ask_user 中断) | 用 \`session\` resume + 用户 response |
|
|
1156
|
+
| \`workflow_run\` 返回 \`status="running"\` 但实际卡死很久 | 先 \`workflow_run_status\` 确认;仍是 running 则 stop → resume |
|
|
1157
|
+
| \`workflow_run\` 抛异常(非 status=failed,是 throw) | stop → resume;resume 仍失败则开新 run |
|
|
1158
|
+
|
|
1159
|
+
记住:**stop + resume 是默认动作**,不是例外。
|
|
1160
|
+
|
|
1026
1161
|
### 严格约束
|
|
1027
1162
|
|
|
1028
1163
|
- ✅ **只**通过 \`workflow_run\` 工具执行 workflow
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// src/env/tool/built-in/query-background-process-tool.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var QueryBackgroundProcessParameters = z.object({
|
|
4
|
+
bgProcessId: z.string().min(1, "bgProcessId must not be empty").describe("The background process ID returned by delegate_task " + "(format: process_<timestamp>_<random>).")
|
|
5
|
+
});
|
|
6
|
+
function backgroundTaskToOutput(task) {
|
|
7
|
+
return {
|
|
8
|
+
alive: task.status === "pending" || task.status === "running",
|
|
9
|
+
status: task.status,
|
|
10
|
+
createdAt: task.createdAt,
|
|
11
|
+
lastUpdatedAt: task.lastUpdatedAt ?? task.completedAt ?? task.startedAt ?? task.createdAt,
|
|
12
|
+
description: task.description,
|
|
13
|
+
...task.result !== undefined ? { result: task.result } : {},
|
|
14
|
+
...task.error !== undefined ? { error: task.error } : {}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function notFoundOutput(bgProcessId) {
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
return {
|
|
20
|
+
alive: false,
|
|
21
|
+
status: "not_found",
|
|
22
|
+
createdAt: now,
|
|
23
|
+
lastUpdatedAt: now,
|
|
24
|
+
description: `No background process found with id '${bgProcessId}'.`
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
var queryBackgroundProcessTool = {
|
|
28
|
+
name: "query_background_process",
|
|
29
|
+
description: `Query the status of a background process previously launched via delegate_task.
|
|
30
|
+
|
|
31
|
+
` + `Use this tool when:
|
|
32
|
+
` + ` - You delegated a long-running task but have NOT received any notification after a reasonable wait (5+ minutes), and want to confirm whether the process is still alive.
|
|
33
|
+
` + ` - The user asks about the progress of a delegated task and you need a current snapshot.
|
|
34
|
+
` + ` - You suspect a delegated task got stuck (no progress messages, no completion notification).
|
|
35
|
+
|
|
36
|
+
` + `DO NOT poll this tool on every turn — it is meant for diagnostic checks at a reasonable cadence (e.g. every few minutes, or when you genuinely suspect the task is stuck).
|
|
37
|
+
|
|
38
|
+
` + `Returns a JSON object with:
|
|
39
|
+
` + ` - alive: boolean — true if status is 'pending' or 'running'
|
|
40
|
+
` + ` - status: 'pending' | 'running' | 'completed' | 'failed' | 'stopped' | 'timeout' | 'not_found'
|
|
41
|
+
` + ` - createdAt, lastUpdatedAt: timestamps (ms)
|
|
42
|
+
` + ` - description: the task description you passed to delegate_task
|
|
43
|
+
` + ` - result: present when status is 'completed'
|
|
44
|
+
` + ` - error: present when status is 'failed'
|
|
45
|
+
|
|
46
|
+
` + "Only the default receptionist agent has this tool. Sub-agents (roy, strict-task-agent, etc.) cannot see it because they do not need to query background tasks they themselves launched.",
|
|
47
|
+
parameters: QueryBackgroundProcessParameters,
|
|
48
|
+
permission: {
|
|
49
|
+
level: "safe",
|
|
50
|
+
requireConfirmation: false
|
|
51
|
+
},
|
|
52
|
+
metadata: {
|
|
53
|
+
category: "task",
|
|
54
|
+
tags: ["background", "delegate", "query", "status", "diagnostic"],
|
|
55
|
+
version: "1.0.0",
|
|
56
|
+
allowedAgentTypes: ["default"]
|
|
57
|
+
},
|
|
58
|
+
execute: async (args, ctx) => createQueryBackgroundProcessTool(null).execute(args, ctx)
|
|
59
|
+
};
|
|
60
|
+
function createQueryBackgroundProcessTool(manager = null) {
|
|
61
|
+
return {
|
|
62
|
+
name: "query_background_process",
|
|
63
|
+
description: `Query the status of a background process previously launched via delegate_task.
|
|
64
|
+
|
|
65
|
+
` + `Use this tool when:
|
|
66
|
+
` + ` - You delegated a long-running task but have NOT received any notification after a reasonable wait (5+ minutes), and want to confirm whether the process is still alive.
|
|
67
|
+
` + ` - The user asks about the progress of a delegated task and you need a current snapshot.
|
|
68
|
+
` + ` - You suspect a delegated task got stuck (no progress messages, no completion notification).
|
|
69
|
+
|
|
70
|
+
` + `DO NOT poll this tool on every turn — it is meant for diagnostic checks at a reasonable cadence (e.g. every few minutes, or when you genuinely suspect the task is stuck).
|
|
71
|
+
|
|
72
|
+
` + `Returns a JSON object with:
|
|
73
|
+
` + ` - alive: boolean — true if status is 'pending' or 'running'
|
|
74
|
+
` + ` - status: 'pending' | 'running' | 'completed' | 'failed' | 'stopped' | 'timeout' | 'not_found'
|
|
75
|
+
` + ` - createdAt, lastUpdatedAt: timestamps (ms)
|
|
76
|
+
` + ` - description: the task description you passed to delegate_task
|
|
77
|
+
` + ` - result: present when status is 'completed'
|
|
78
|
+
` + ` - error: present when status is 'failed'
|
|
79
|
+
|
|
80
|
+
` + "Only the default receptionist agent has this tool. Sub-agents (roy, strict-task-agent, etc.) cannot see it because they do not need to query background tasks they themselves launched.",
|
|
81
|
+
parameters: QueryBackgroundProcessParameters,
|
|
82
|
+
permission: {
|
|
83
|
+
level: "safe",
|
|
84
|
+
requireConfirmation: false
|
|
85
|
+
},
|
|
86
|
+
metadata: {
|
|
87
|
+
category: "task",
|
|
88
|
+
tags: ["background", "delegate", "query", "status", "diagnostic"],
|
|
89
|
+
version: "1.0.0",
|
|
90
|
+
allowedAgentTypes: ["default"]
|
|
91
|
+
},
|
|
92
|
+
execute: async (args, ctx) => {
|
|
93
|
+
const startTime = Date.now();
|
|
94
|
+
const params = QueryBackgroundProcessParameters.parse(args);
|
|
95
|
+
const { bgProcessId } = params;
|
|
96
|
+
if (!manager) {
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
output: "",
|
|
100
|
+
error: "query_background_process is not initialized: no BackgroundTaskManager " + "has been bound. TaskComponent should call " + "createQueryBackgroundProcessTool(manager) and register the tool " + "during startup.",
|
|
101
|
+
metadata: { execution_time_ms: Date.now() - startTime }
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
let task;
|
|
105
|
+
try {
|
|
106
|
+
task = manager.getTask(bgProcessId);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
output: "",
|
|
111
|
+
error: `failed to query background task: ${err.message}`,
|
|
112
|
+
metadata: { execution_time_ms: Date.now() - startTime }
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (task) {
|
|
116
|
+
const currentSessionId = ctx.session_id;
|
|
117
|
+
if (!currentSessionId || task.parentSessionId !== currentSessionId) {
|
|
118
|
+
return {
|
|
119
|
+
success: true,
|
|
120
|
+
output: JSON.stringify(notFoundOutput(bgProcessId), null, 2),
|
|
121
|
+
metadata: {
|
|
122
|
+
execution_time_ms: Date.now() - startTime,
|
|
123
|
+
bgProcessId,
|
|
124
|
+
status: "not_found",
|
|
125
|
+
alive: false
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const output = task ? backgroundTaskToOutput(task) : notFoundOutput(bgProcessId);
|
|
131
|
+
return {
|
|
132
|
+
success: true,
|
|
133
|
+
output: JSON.stringify(output, null, 2),
|
|
134
|
+
metadata: {
|
|
135
|
+
execution_time_ms: Date.now() - startTime,
|
|
136
|
+
bgProcessId,
|
|
137
|
+
status: output.status,
|
|
138
|
+
alive: output.alive
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export { queryBackgroundProcessTool, createQueryBackgroundProcessTool };
|
|
@@ -103,7 +103,7 @@ var init_tool_node = __esm(() => {
|
|
|
103
103
|
nodeId: context.nodeId
|
|
104
104
|
}
|
|
105
105
|
};
|
|
106
|
-
const toolTimeoutMs = getToolTimeoutMs(input,
|
|
106
|
+
const toolTimeoutMs = getToolTimeoutMs(input, config);
|
|
107
107
|
const executePromise = tool.execute(input, toolContext);
|
|
108
108
|
let timeoutHandle;
|
|
109
109
|
const timeoutPromise = new Promise((_, reject) => {
|
|
@@ -28,6 +28,16 @@ function inferNextNode(messages, definition) {
|
|
|
28
28
|
};
|
|
29
29
|
case "workflow.node.end":
|
|
30
30
|
case "workflow.node.result":
|
|
31
|
+
if (metadata?.success === false) {
|
|
32
|
+
const failedNodeId = getWorkflowNodeIdFromMetadata(metadata);
|
|
33
|
+
if (failedNodeId) {
|
|
34
|
+
return {
|
|
35
|
+
type: "resume_node",
|
|
36
|
+
nodeId: failedNodeId,
|
|
37
|
+
agentSessionId: metadata.agentSessionId
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
31
41
|
return getNextNodes(getWorkflowNodeIdFromMetadata(metadata), definition);
|
|
32
42
|
default:
|
|
33
43
|
const lastStart = findLastStartMessage(messages);
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
WorkflowEngine,
|
|
7
7
|
exports_engine,
|
|
8
8
|
init_engine
|
|
9
|
-
} from "./roy-agent-core-
|
|
9
|
+
} from "./roy-agent-core-gzzvcmg3.js";
|
|
10
10
|
import {
|
|
11
11
|
askUserTool,
|
|
12
12
|
createRunWorkflowTool,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
createWorkflowSearchTool,
|
|
19
19
|
createWorkflowTagListTool,
|
|
20
20
|
createWorkflowValidateTool
|
|
21
|
-
} from "./roy-agent-core-
|
|
21
|
+
} from "./roy-agent-core-xnga0ksx.js";
|
|
22
22
|
import {
|
|
23
23
|
WorkflowService
|
|
24
24
|
} from "./roy-agent-core-b75jtybq.js";
|
|
@@ -141,7 +141,7 @@ class WorkflowComponent extends BaseComponent {
|
|
|
141
141
|
if (!agentRunner && this._workflowEnv) {
|
|
142
142
|
const agentComponent = this._workflowEnv.getComponent("agent");
|
|
143
143
|
if (agentComponent) {
|
|
144
|
-
const { AgentComponentAdapter } = await import("./roy-agent-core-
|
|
144
|
+
const { AgentComponentAdapter } = await import("./roy-agent-core-y1m7c2v7.js");
|
|
145
145
|
agentRunner = new AgentComponentAdapter(agentComponent, {}, this.sessionComponent);
|
|
146
146
|
}
|
|
147
147
|
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
AgentComponentAdapter,
|
|
9
9
|
init_agent_component_adapter
|
|
10
|
-
} from "./roy-agent-core-
|
|
10
|
+
} from "./roy-agent-core-vf72n6qn.js";
|
|
11
11
|
import {
|
|
12
12
|
TemplateResolver,
|
|
13
13
|
init_template_resolver
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
init_skill_node,
|
|
20
20
|
init_tool_node,
|
|
21
21
|
init_workflow_node
|
|
22
|
-
} from "./roy-agent-core-
|
|
22
|
+
} from "./roy-agent-core-ba6243hr.js";
|
|
23
23
|
import {
|
|
24
24
|
WorkflowError,
|
|
25
25
|
createWorkflowEvent,
|
|
@@ -416,14 +416,17 @@ var init_executor = __esm(() => {
|
|
|
416
416
|
await this.writeNodeStart(context.sessionId, nodeId, nodeType, context.input, agentSessionId);
|
|
417
417
|
const abortController = new AbortController;
|
|
418
418
|
this.abortControllers.set(nodeId, abortController);
|
|
419
|
-
|
|
419
|
+
context.abortSignal ??= abortController.signal;
|
|
420
|
+
const ctxCfg = context.config;
|
|
421
|
+
const cfgTimeout = ctxCfg && typeof ctxCfg.timeout === "number" && ctxCfg.timeout > 0 ? ctxCfg.timeout : undefined;
|
|
422
|
+
const timeout = definition.timeout ?? cfgTimeout ?? this.options.globalTimeout ?? undefined;
|
|
420
423
|
const retryConfig = definition.retry ?? this.options.globalRetry;
|
|
421
424
|
try {
|
|
422
425
|
let result;
|
|
423
426
|
if (retryConfig && retryConfig.max_attempts > 1) {
|
|
424
|
-
result = await this.executeWithRetry(definition, context, abortController
|
|
427
|
+
result = await this.executeWithRetry(definition, context, abortController, timeout);
|
|
425
428
|
} else {
|
|
426
|
-
result = await this.executeWithTimeout(definition, context, abortController
|
|
429
|
+
result = await this.executeWithTimeout(definition, context, abortController, timeout);
|
|
427
430
|
}
|
|
428
431
|
if (result.error instanceof AskUserError) {
|
|
429
432
|
const interruptEvent = createNodeInterruptEvent(context.runId, nodeId, nodeType, result.error.query, result.error.agentSessionId || agentSessionId);
|
|
@@ -455,7 +458,7 @@ var init_executor = __esm(() => {
|
|
|
455
458
|
this.abortControllers.delete(nodeId);
|
|
456
459
|
}
|
|
457
460
|
}
|
|
458
|
-
async executeWithTimeout(definition, context,
|
|
461
|
+
async executeWithTimeout(definition, context, abortController, timeout) {
|
|
459
462
|
const startTime = Date.now();
|
|
460
463
|
const nodeContext = {
|
|
461
464
|
runId: context.runId,
|
|
@@ -464,9 +467,13 @@ var init_executor = __esm(() => {
|
|
|
464
467
|
debug: context.debug
|
|
465
468
|
};
|
|
466
469
|
const node = this.nodeRegistry.createNode(definition, nodeContext);
|
|
470
|
+
const signal = abortController.signal;
|
|
467
471
|
if (timeout) {
|
|
468
472
|
const timeoutPromise = new Promise((_, reject) => {
|
|
469
473
|
const handle = setTimeout(() => {
|
|
474
|
+
if (!abortController.signal.aborted) {
|
|
475
|
+
abortController.abort(new Error(`Node execution timeout: ${timeout}ms`));
|
|
476
|
+
}
|
|
470
477
|
reject(new Error(`Node execution timeout: ${timeout}ms`));
|
|
471
478
|
}, timeout);
|
|
472
479
|
signal.addEventListener("abort", () => clearTimeout(handle));
|
|
@@ -510,10 +517,11 @@ var init_executor = __esm(() => {
|
|
|
510
517
|
}
|
|
511
518
|
}
|
|
512
519
|
}
|
|
513
|
-
async executeWithRetry(definition, context,
|
|
520
|
+
async executeWithRetry(definition, context, abortController, timeout) {
|
|
514
521
|
const retryConfig = definition.retry;
|
|
515
522
|
let lastError;
|
|
516
523
|
let delay = retryConfig.initial_delay;
|
|
524
|
+
const signal = abortController.signal;
|
|
517
525
|
for (let attempt = 1;attempt <= retryConfig.max_attempts; attempt++) {
|
|
518
526
|
if (signal.aborted) {
|
|
519
527
|
return {
|
|
@@ -524,8 +532,25 @@ var init_executor = __esm(() => {
|
|
|
524
532
|
}
|
|
525
533
|
const attemptAbort = new AbortController;
|
|
526
534
|
const combinedSignal = this.combineAbortSignals(signal, attemptAbort.signal);
|
|
535
|
+
const combinedController = new AbortController;
|
|
536
|
+
combinedSignal.addEventListener("abort", () => {
|
|
537
|
+
if (!combinedController.signal.aborted) {
|
|
538
|
+
combinedController.abort(combinedSignal.reason);
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
const attemptAbortController = {
|
|
542
|
+
get signal() {
|
|
543
|
+
return combinedSignal;
|
|
544
|
+
},
|
|
545
|
+
abort: (reason) => {
|
|
546
|
+
attemptAbort.abort(reason);
|
|
547
|
+
if (!combinedController.signal.aborted) {
|
|
548
|
+
combinedController.abort(reason);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
};
|
|
527
552
|
try {
|
|
528
|
-
const result = await this.executeWithTimeout(definition, context,
|
|
553
|
+
const result = await this.executeWithTimeout(definition, context, attemptAbortController, timeout);
|
|
529
554
|
if (!result.error) {
|
|
530
555
|
return result;
|
|
531
556
|
}
|
|
@@ -991,6 +1016,8 @@ var init_agent_node = __esm(() => {
|
|
|
991
1016
|
const agentName = config.agent_name || undefined;
|
|
992
1017
|
const options = config.options || {};
|
|
993
1018
|
const outputConfig = parseAgentOutputConfig(config);
|
|
1019
|
+
const flatTimeout = typeof config.timeout === "number" && config.timeout > 0 ? config.timeout : undefined;
|
|
1020
|
+
const resolvedTimeout = flatTimeout ?? options.timeout;
|
|
994
1021
|
let resolvedPrompt;
|
|
995
1022
|
if (context.resolvedConfig && context.templateResolver) {
|
|
996
1023
|
resolvedPrompt = config.prompt || "";
|
|
@@ -1044,7 +1071,7 @@ var init_agent_node = __esm(() => {
|
|
|
1044
1071
|
type: resolvedAgentType,
|
|
1045
1072
|
prompt: resolvedPrompt,
|
|
1046
1073
|
options: {
|
|
1047
|
-
timeout:
|
|
1074
|
+
timeout: resolvedTimeout,
|
|
1048
1075
|
model: effectiveModel,
|
|
1049
1076
|
allowedTools: effectiveAllowedTools,
|
|
1050
1077
|
deniedTools: effectiveDeniedTools,
|
|
@@ -1058,7 +1085,12 @@ var init_agent_node = __esm(() => {
|
|
|
1058
1085
|
userQuery,
|
|
1059
1086
|
agentSessionId
|
|
1060
1087
|
};
|
|
1061
|
-
|
|
1088
|
+
let result;
|
|
1089
|
+
if (context.abortSignal) {
|
|
1090
|
+
result = await this.agentRunner.run(agentConfig, resumeOptions, context.abortSignal);
|
|
1091
|
+
} else {
|
|
1092
|
+
result = await this.agentRunner.run(agentConfig, resumeOptions);
|
|
1093
|
+
}
|
|
1062
1094
|
const duration = Date.now() - startTime;
|
|
1063
1095
|
return {
|
|
1064
1096
|
output: buildAgentNodeOutput(result, outputConfig),
|
|
@@ -1433,9 +1465,6 @@ var init_engine = __esm(() => {
|
|
|
1433
1465
|
if (metadata.status === "completed") {
|
|
1434
1466
|
return { runId: this.getRunIdFromSessionId(sessionId), status: "completed" };
|
|
1435
1467
|
}
|
|
1436
|
-
if (metadata.status === "failed") {
|
|
1437
|
-
return { runId: this.getRunIdFromSessionId(sessionId), status: "failed" };
|
|
1438
|
-
}
|
|
1439
1468
|
if (options?.input && this.sessionComponent) {
|
|
1440
1469
|
const hasNewInput = typeof options.input === "string" ? options.input !== undefined && options.input !== "" : Object.keys(options.input).length > 0;
|
|
1441
1470
|
if (hasNewInput) {
|
|
@@ -1457,7 +1486,7 @@ var init_engine = __esm(() => {
|
|
|
1457
1486
|
extractResumeInfo,
|
|
1458
1487
|
enrichResumeInfoWithAgentSessions,
|
|
1459
1488
|
enrichResumePointWithAgentSessions
|
|
1460
|
-
} = await import("./roy-agent-core-
|
|
1489
|
+
} = await import("./roy-agent-core-caevk7xd.js");
|
|
1461
1490
|
const entry = workflowDef.entry;
|
|
1462
1491
|
const entryNode = Array.isArray(entry) ? entry[0] : entry;
|
|
1463
1492
|
const agentSessions = metadata.agentSessions ?? [];
|
|
@@ -1496,7 +1525,7 @@ var init_engine = __esm(() => {
|
|
|
1496
1525
|
persistedInput = rest;
|
|
1497
1526
|
}
|
|
1498
1527
|
}
|
|
1499
|
-
const { stripEphemeralResumeInput } = await import("./roy-agent-core-
|
|
1528
|
+
const { stripEphemeralResumeInput } = await import("./roy-agent-core-caevk7xd.js");
|
|
1500
1529
|
const { workflowInput: runtimeInput } = stripEphemeralResumeInput(rawRuntimeInput);
|
|
1501
1530
|
const defaultsFromSchema = {};
|
|
1502
1531
|
if (definition.inputs) {
|
|
@@ -1515,7 +1544,8 @@ var init_engine = __esm(() => {
|
|
|
1515
1544
|
strict: options?.strict ?? definition.config?.strict ?? false,
|
|
1516
1545
|
maxIterations: options?.maxIterations ?? definition.config?.maxIterations ?? 100,
|
|
1517
1546
|
loopEnabled,
|
|
1518
|
-
workflowInput
|
|
1547
|
+
workflowInput,
|
|
1548
|
+
nodeConfig: options?.nodeConfig ?? {}
|
|
1519
1549
|
};
|
|
1520
1550
|
const abortController = new AbortController;
|
|
1521
1551
|
let resolveCompleted;
|
|
@@ -1691,6 +1721,8 @@ var init_engine = __esm(() => {
|
|
|
1691
1721
|
status: "paused",
|
|
1692
1722
|
pendingNodeId: event.node_id,
|
|
1693
1723
|
pauseReason: "ask_user",
|
|
1724
|
+
pendingAskUserQuery: event.query,
|
|
1725
|
+
pendingAskUserAgentSessionId: event.agent_session_id,
|
|
1694
1726
|
agentSessions
|
|
1695
1727
|
}
|
|
1696
1728
|
});
|
|
@@ -1712,7 +1744,7 @@ var init_engine = __esm(() => {
|
|
|
1712
1744
|
async scheduleAndExecute(sessionState, options) {
|
|
1713
1745
|
const { scheduler, executor, eventBus, abortController, nodeOutputs } = sessionState;
|
|
1714
1746
|
const { input: globalInput, pendingNodeId, agentSessionId, restoredOutputs } = options || {};
|
|
1715
|
-
const { stripEphemeralResumeInput } = await import("./roy-agent-core-
|
|
1747
|
+
const { stripEphemeralResumeInput } = await import("./roy-agent-core-caevk7xd.js");
|
|
1716
1748
|
const { workflowInput: inputToPersist, userQuery: resumeUserQuery } = stripEphemeralResumeInput(globalInput);
|
|
1717
1749
|
if (inputToPersist) {
|
|
1718
1750
|
sessionState.config.workflowInput = {
|
|
@@ -1854,7 +1886,7 @@ var init_engine = __esm(() => {
|
|
|
1854
1886
|
if (!agentSessionId && this.sessionComponent) {
|
|
1855
1887
|
const session = await this.sessionComponent.get(sessionId);
|
|
1856
1888
|
const agentSessions = session?.metadata?.agentSessions;
|
|
1857
|
-
const { resolveAgentSessionId } = await import("./roy-agent-core-
|
|
1889
|
+
const { resolveAgentSessionId } = await import("./roy-agent-core-caevk7xd.js");
|
|
1858
1890
|
agentSessionId = resolveAgentSessionId(nodeId, undefined, agentSessions);
|
|
1859
1891
|
}
|
|
1860
1892
|
await sessionState.executor.writeNodeResume(sessionId, nodeId, nodeDef.type, options?.userQuery ?? "", agentSessionId);
|
|
@@ -1996,6 +2028,8 @@ var init_engine = __esm(() => {
|
|
|
1996
2028
|
const askUser = (query) => {
|
|
1997
2029
|
throw new AskUserError(runId, sessionState.sessionId, nodeId, nodeDef.type, query);
|
|
1998
2030
|
};
|
|
2031
|
+
const nodeConfigOverride = sessionState.config.nodeConfig?.[nodeId];
|
|
2032
|
+
const effectiveConfig = nodeConfigOverride ? { ...nodeDef.config ?? {}, ...nodeConfigOverride } : nodeDef.config ?? {};
|
|
1999
2033
|
const context = {
|
|
2000
2034
|
runId,
|
|
2001
2035
|
sessionId: sessionState.sessionId,
|
|
@@ -2003,7 +2037,7 @@ var init_engine = __esm(() => {
|
|
|
2003
2037
|
nodeId,
|
|
2004
2038
|
input,
|
|
2005
2039
|
previousOutputs: sessionState.nodeOutputs,
|
|
2006
|
-
config:
|
|
2040
|
+
config: effectiveConfig,
|
|
2007
2041
|
debug: sessionState.config.debug,
|
|
2008
2042
|
eventBus: sessionState.eventBus,
|
|
2009
2043
|
nodeOutputs: sessionState.nodeOutputs,
|
|
@@ -2015,7 +2049,7 @@ var init_engine = __esm(() => {
|
|
|
2015
2049
|
},
|
|
2016
2050
|
workflowInput,
|
|
2017
2051
|
templateResolver,
|
|
2018
|
-
resolvedConfig: templateResolver.resolveValue(
|
|
2052
|
+
resolvedConfig: templateResolver.resolveValue(effectiveConfig)
|
|
2019
2053
|
};
|
|
2020
2054
|
return context;
|
|
2021
2055
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createQueryBackgroundProcessTool
|
|
3
|
+
} from "./roy-agent-core-amkqnyzy.js";
|
|
1
4
|
import {
|
|
2
5
|
TaskEntityEventTypes
|
|
3
6
|
} from "./roy-agent-core-8gxth0eh.js";
|
|
@@ -5,7 +8,7 @@ import {
|
|
|
5
8
|
BackgroundTaskManager,
|
|
6
9
|
createDelegateTool,
|
|
7
10
|
createStopTool
|
|
8
|
-
} from "./roy-agent-core-
|
|
11
|
+
} from "./roy-agent-core-te3hf26b.js";
|
|
9
12
|
import {
|
|
10
13
|
SQLiteTaskStore,
|
|
11
14
|
getDefaultTaskDbPath
|
|
@@ -314,6 +317,8 @@ class TaskComponent extends BaseComponent {
|
|
|
314
317
|
this.toolComponent = this.env?.getComponent("tool");
|
|
315
318
|
if (this.toolComponent) {
|
|
316
319
|
await this.registerTools();
|
|
320
|
+
this.toolComponent.register(createQueryBackgroundProcessTool(this.backgroundTaskManager), "built-in");
|
|
321
|
+
logger2.debug("[TaskComponent] Injected BackgroundTaskManager into query_background_process tool (factory pattern)");
|
|
317
322
|
} else {
|
|
318
323
|
logger2.warn("[TaskComponent] ToolComponent not found, skipping tool registration");
|
|
319
324
|
}
|
|
@@ -323,8 +328,17 @@ class TaskComponent extends BaseComponent {
|
|
|
323
328
|
this.configWatcher();
|
|
324
329
|
this.configWatcher = undefined;
|
|
325
330
|
}
|
|
331
|
+
if (this.toolComponent) {
|
|
332
|
+
try {
|
|
333
|
+
this.toolComponent.register(createQueryBackgroundProcessTool(null), "built-in");
|
|
334
|
+
logger2.debug("[TaskComponent] Reset query_background_process tool to no-manager default (cleanup)");
|
|
335
|
+
} catch (err) {
|
|
336
|
+
logger2.warn(`[TaskComponent] Failed to reset query_background_process tool: ${err.message}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
326
339
|
if (this.backgroundTaskManager) {
|
|
327
340
|
this.backgroundTaskManager.dispose();
|
|
341
|
+
this.backgroundTaskManager = undefined;
|
|
328
342
|
}
|
|
329
343
|
if (this.store) {
|
|
330
344
|
await this.store.close();
|