@ai-setting/roy-agent-core 1.7.75 → 1.7.76
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/agent-component.js +1 -1
- package/dist/env/agent/index.js +1 -1
- package/dist/env/event-source/event-source-handlers.js +5 -4
- package/dist/env/event-source/index.js +6 -5
- package/dist/env/index.js +11 -10
- package/dist/env/prompt/index.js +4 -3
- package/dist/env/prompt/prompts-index.d.ts +11 -9
- package/dist/env/task/backend/index.js +9 -1
- package/dist/env/task/delegate/delegate-tool.js +4 -3
- package/dist/env/task/delegate/index.js +4 -3
- package/dist/env/task/hooks/contexts.d.ts +12 -1
- package/dist/env/task/hooks/task-hook-points.d.ts +8 -3
- package/dist/env/task/index.d.ts +1 -0
- package/dist/env/task/index.js +15 -5
- package/dist/env/task/mcp/index.js +3 -2
- package/dist/env/task/plugins/index.js +4 -3
- package/dist/env/task/prompt/index.js +2 -1
- package/dist/env/task/prompt/task-discipline.d.ts +1 -1
- package/dist/env/task/task-component.d.ts +79 -0
- package/dist/env/task/tools/cancel-tool.d.ts +12 -4
- package/dist/env/task/tools/completion-copy.d.ts +92 -0
- package/dist/env/task/tools/index.d.ts +1 -0
- package/dist/env/task/tools/index.js +5 -2
- package/dist/env/task/tools/pre-complete-tool.d.ts +32 -0
- package/dist/env/task/tools/task-types.d.ts +49 -17
- package/dist/env/task/types.d.ts +13 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +21 -11
- package/dist/shared/@ai-setting/roy-agent-core-4rtg69cn.js +26 -0
- package/dist/shared/@ai-setting/{roy-agent-core-3e6gg17d.js → roy-agent-core-9vxh6f8h.js} +169 -30
- package/dist/shared/@ai-setting/{roy-agent-core-jg65qnwa.js → roy-agent-core-aa2j7bk9.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-29b6hc39.js → roy-agent-core-cmprygrp.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-n9q1encx.js → roy-agent-core-cxy143m4.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-y7x2qzwc.js → roy-agent-core-ghnagzrh.js} +7 -3
- package/dist/shared/@ai-setting/{roy-agent-core-11shp00p.js → roy-agent-core-m46fe3x4.js} +21 -31
- package/dist/shared/@ai-setting/{roy-agent-core-djpq6yb0.js → roy-agent-core-q5r75vev.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-60jbrfye.js → roy-agent-core-t3rrb959.js} +28 -4
- package/dist/shared/@ai-setting/{roy-agent-core-hnyk1by7.js → roy-agent-core-vffccgf6.js} +152 -33
- package/dist/shared/@ai-setting/{roy-agent-core-4gr3k7qg.js → roy-agent-core-z43aj03k.js} +120 -28
- package/dist/shared/@ai-setting/{roy-agent-core-snvh4s2p.js → roy-agent-core-zycadbpx.js} +1 -1
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
createPerTaskGroupChatPlugin,
|
|
11
11
|
extractPerTaskChatId,
|
|
12
12
|
extractPerTaskChatName
|
|
13
|
-
} from "./roy-agent-core-
|
|
13
|
+
} from "./roy-agent-core-ghnagzrh.js";
|
|
14
14
|
import {
|
|
15
15
|
getSharedEventStore
|
|
16
16
|
} from "./roy-agent-core-na18zwj0.js";
|
|
@@ -3327,6 +3327,32 @@ function renderRuleNoticeSection(ruleNotice, headline) {
|
|
|
3327
3327
|
return `❌ **回执未送达**(原因:${ruleNotice.error ?? "unknown"})
|
|
3328
3328
|
` + `→ 用户目前**还不知道**「${headline}」这件事,必须由你在回复里讲清楚,` + `并且不要假装系统已经通知过。`;
|
|
3329
3329
|
}
|
|
3330
|
+
function renderActionSection(notifyType, taskId, scopeWord) {
|
|
3331
|
+
const completeCall = taskId !== "?" ? `task_complete(task_id=${taskId})` : `task_complete(task_id=<用 task_search 找回的任务号>)`;
|
|
3332
|
+
if (notifyType === "task.background.completed") {
|
|
3333
|
+
return `**第一步 —— 验收(⚠️ 不可跳过)**:Task #${taskId} 的最终验收由**你**(default agent)负责 —— sub-agent 只负责干完活并调 task_pre_complete 提交待验证(status=to_validate),**判定权在你**(Task #5073 两阶段验收)。
|
|
3334
|
+
1. \`task_get(task_id=${taskId})\` **核对实际产出**:读 goal / delivery / 操作记录链(交付物在 \`context.deliverable_storage_path\` 和 operation 的 Deliverable 行里),再对照真实产出(文件是否存在、测试是否通过、review 结论是什么)。⚠️ **不接受 sub-agent 的自述作为验收依据** —— 它说"做完了"不算,产出在才算。
|
|
3335
|
+
2. **逐条对照 goals 的编号子项**(文件断言 / 进程断言 / 测试断言 / 状态断言),一条一条核。
|
|
3336
|
+
3. 全部达成 → **必须实际调用** \`${completeCall}\` 把任务落终态(completed)。
|
|
3337
|
+
⚠️ **只回消息不算完成** —— 任务会一直挂在 \`to_validate\`,下游(任务卡片 / goal 检查 / 进度统计)都读不到最终状态。
|
|
3338
|
+
⚠️ 本工具**不带**交付物参数(交付物通道 = task_pre_complete)—— 若发现交付物缺失,重新委托补做,不要在这一步补传。
|
|
3339
|
+
4. 有差距 → 用 \`delegate_task(sub_session_id=...)\` 续跑或重新委托;**不要**因为"验收不通过"就 \`task_cancel\`(\`task_cancel\` 仅限**用户明令**)。
|
|
3340
|
+
|
|
3341
|
+
**第二步 —— 汇报**
|
|
3342
|
+
5. 用 2~5 行总结:这次完成了什么 / 卡在哪(依据上面的输出与子 session)
|
|
3343
|
+
6. 给出 1~3 个下一步选项(继续推进 / 拆小 / 放弃),并**问用户选哪个**
|
|
3344
|
+
7. 用 lark-cli 把回复发到上面的${scopeWord}(见下)`;
|
|
3345
|
+
}
|
|
3346
|
+
return `**第一步 —— 找根因 → 继续推进(不要甩尾放弃)**
|
|
3347
|
+
1. **先找根因**:读上面的输出与子 session,判断这次失败/超时/停止的真实原因(sub-agent 没做完?环境问题?指令不清?超时其实是工具撞硬超时但后台仍在跑?)。
|
|
3348
|
+
2. **继续推进**:能用 \`delegate_task(sub_session_id=...)\` 续跑就续跑(继承子 session 历史,不必从零重来);不能续跑就按根因重新委托。
|
|
3349
|
+
3. ⚠️ **不要**用 \`task_cancel\` 结束这个任务 —— \`task_cancel\` 仅限**用户明令**。确实推不动(外部依赖挂了 / 用户另有安排)就如实告诉用户「卡在哪 + 需要用户做什么决定」,等用户决定。
|
|
3350
|
+
|
|
3351
|
+
**第二步 —— 汇报**
|
|
3352
|
+
4. 用 2~5 行总结:这次完成了什么 / 卡在哪(依据上面的输出与子 session)
|
|
3353
|
+
5. 给出 1~3 个下一步选项(继续推进 / 拆小 / 放弃),并**问用户选哪个**
|
|
3354
|
+
6. 用 lark-cli 把回复发到上面的${scopeWord}(见下)`;
|
|
3355
|
+
}
|
|
3330
3356
|
function extractCommandText(rawEvent) {
|
|
3331
3357
|
const event = rawEvent;
|
|
3332
3358
|
const innerEvent = event.event;
|
|
@@ -3454,9 +3480,7 @@ ${receiptSection}
|
|
|
3454
3480
|
${result}
|
|
3455
3481
|
|
|
3456
3482
|
### \uD83D\uDC49 你要做什么
|
|
3457
|
-
|
|
3458
|
-
2. 给出 1~3 个下一步选项(继续推进 / 拆小 / 放弃),并**问用户选哪个**
|
|
3459
|
-
3. 用 lark-cli 把回复发到上面的${scopeWord}(见下)
|
|
3483
|
+
${renderActionSection(notifyType, taskId, scopeWord)}
|
|
3460
3484
|
|
|
3461
3485
|
### 发送方式(渠道 = lark-cli)
|
|
3462
3486
|
${canReply ? ` ${replyCommand}` : `⚠️ 没有可用的 chatId —— 无法投递到触发会话。请在会话内汇报,并说明「未投递」的原因。`}
|
|
@@ -3,11 +3,16 @@ import {
|
|
|
3
3
|
NEW_TASK_ID_PLACEHOLDER
|
|
4
4
|
} from "./roy-agent-core-tr3ep81v.js";
|
|
5
5
|
import {
|
|
6
|
+
TASK_DISCIPLINE_GOALS
|
|
7
|
+
} from "./roy-agent-core-m46fe3x4.js";
|
|
8
|
+
import {
|
|
9
|
+
TASK_ARCHIVED_STATUS_WORD,
|
|
10
|
+
TASK_CANCELLED_STATUS_WORD,
|
|
6
11
|
TASK_COMPLETE_ITEM_BY_ITEM,
|
|
7
12
|
TASK_COMPLETE_VERIFY_DUTY,
|
|
8
13
|
TASK_COMPLETION_HINT_ANCHOR,
|
|
9
|
-
|
|
10
|
-
} from "./roy-agent-core-
|
|
14
|
+
TERMINAL_NON_ACCEPTABLE_STATUSES
|
|
15
|
+
} from "./roy-agent-core-4rtg69cn.js";
|
|
11
16
|
import {
|
|
12
17
|
getEnvContextOrEmpty,
|
|
13
18
|
init_env_context,
|
|
@@ -63,7 +68,7 @@ var GetTaskToolSchema = withLegacyTaskId({
|
|
|
63
68
|
include_operations: z.boolean().optional().default(true).describe("Include operation records (milestones / process log) with the task. " + "Defaults to true — the task's operation history is part of its detail. " + "Pass false for a lean task-only response.")
|
|
64
69
|
});
|
|
65
70
|
var ListTasksToolSchema = z.object({
|
|
66
|
-
status: z.enum(["todo", "active", "completed", "paused", "cancelled", "archived"]).optional(),
|
|
71
|
+
status: z.enum(["todo", "active", "to_validate", "completed", "paused", "cancelled", "archived"]).optional(),
|
|
67
72
|
priority: z.enum(["low", "medium", "high"]).optional(),
|
|
68
73
|
type: z.enum(["normal", "cycle", "longterm"]).optional().describe("Filter by task type"),
|
|
69
74
|
limit: z.number().optional().default(20),
|
|
@@ -75,7 +80,7 @@ var SearchTasksToolSchema = z.object({
|
|
|
75
80
|
keywords: z.array(z.string()).describe("关键词列表(AND 逻辑,匹配 title/description/goals/tags/operations)"),
|
|
76
81
|
limit: z.number().optional().default(20).describe("返回数量"),
|
|
77
82
|
offset: z.number().optional().default(0).describe("偏移量"),
|
|
78
|
-
status: z.enum(["todo", "active", "completed", "paused", "cancelled", "archived"]).optional().describe("按状态筛选"),
|
|
83
|
+
status: z.enum(["todo", "active", "to_validate", "completed", "paused", "cancelled", "archived"]).optional().describe("按状态筛选"),
|
|
79
84
|
priority: z.enum(["low", "medium", "high"]).optional().describe("按优先级筛选"),
|
|
80
85
|
type: z.enum(["normal", "cycle", "longterm"]).optional().describe("按类型筛选"),
|
|
81
86
|
exclude_task_id: z.number().optional().describe("排除的 task ID"),
|
|
@@ -85,7 +90,7 @@ var SearchTasksToolSchema = z.object({
|
|
|
85
90
|
var UpdateTaskToolSchema = withLegacyTaskId({
|
|
86
91
|
title: z.string().optional(),
|
|
87
92
|
description: z.string().optional(),
|
|
88
|
-
status: z.enum(["todo", "active", "completed", "paused", "cancelled", "archived"]).optional(),
|
|
93
|
+
status: z.enum(["todo", "active", "to_validate", "completed", "paused", "cancelled", "archived"]).optional(),
|
|
89
94
|
priority: z.enum(["low", "medium", "high"]).optional(),
|
|
90
95
|
type: z.enum(["normal", "cycle", "longterm"]).optional().describe("Task type"),
|
|
91
96
|
progress: z.number().min(0).max(100).optional(),
|
|
@@ -100,8 +105,10 @@ var UpdateTaskToolSchema = withLegacyTaskId({
|
|
|
100
105
|
var DeleteTaskToolSchema = z.object({
|
|
101
106
|
task_id: z.number().describe("Task ID")
|
|
102
107
|
});
|
|
103
|
-
var CompleteTaskToolSchema = withLegacyTaskId({
|
|
104
|
-
|
|
108
|
+
var CompleteTaskToolSchema = withLegacyTaskId({});
|
|
109
|
+
var PreCompleteTaskToolSchema = withLegacyTaskId({
|
|
110
|
+
deliverable_storage_path: z.string().optional().describe("交付物的寻址地址(路径 / URL / 分支名等)。" + "会持久化到 task.context.deliverable_storage_path," + "并写入 operation 记录(Deliverable 行),供验收方 task_complete 时逐条对照。"),
|
|
111
|
+
current_status: z.string().optional().describe("本次交付的进展摘要(可选)。" + "会拼进「待验证:」文案,作为任务当前状态展示给验收方(default agent)。")
|
|
105
112
|
});
|
|
106
113
|
var CancelTaskToolSchema = withLegacyTaskId({
|
|
107
114
|
reason: z.string().optional().describe("取消原因(可选)。会写入该任务的 operation 记录,便于事后追溯验收结论。")
|
|
@@ -116,7 +123,7 @@ var BatchDeleteToolSchema = z.object({
|
|
|
116
123
|
var TreeTasksToolSchema = z.object({
|
|
117
124
|
depth: z.number().optional().default(2).describe("树的展开深度。默认 2 (根 + 一层子任务 + 二层孙任务)。" + "0 = 只返回根任务,不展开任何子节点。" + "-1 或负数 = 无限层级(全部展开)。"),
|
|
118
125
|
parent_task_id: z.number().optional().describe("只看指定父任务的子树。不传则从所有根任务(forest)开始。"),
|
|
119
|
-
status: z.enum(["todo", "active", "completed", "paused", "cancelled", "archived"]).optional().describe("按状态过滤(与 task_list 一致)。"),
|
|
126
|
+
status: z.enum(["todo", "active", "to_validate", "completed", "paused", "cancelled", "archived"]).optional().describe("按状态过滤(与 task_list 一致)。"),
|
|
120
127
|
priority: z.enum(["low", "medium", "high"]).optional().describe("按优先级过滤。"),
|
|
121
128
|
type: z.enum(["normal", "cycle", "longterm"]).optional().describe("按任务类型过滤。"),
|
|
122
129
|
include_archived: z.boolean().optional().default(false).describe("是否包含已归档任务(默认 false,与 task_list 一致)。")
|
|
@@ -702,7 +709,10 @@ function batchArchiveTaskTool(taskComponent) {
|
|
|
702
709
|
let archivedCount = 0;
|
|
703
710
|
for (const taskId of taskIds) {
|
|
704
711
|
try {
|
|
705
|
-
const task = await taskComponent.updateTask(taskId, {
|
|
712
|
+
const task = await taskComponent.updateTask(taskId, {
|
|
713
|
+
status: "archived",
|
|
714
|
+
current_status: params.reason ? `${TASK_ARCHIVED_STATUS_WORD}:${params.reason}` : TASK_ARCHIVED_STATUS_WORD
|
|
715
|
+
});
|
|
706
716
|
if (task) {
|
|
707
717
|
await taskComponent.createOperation({
|
|
708
718
|
taskId,
|
|
@@ -940,7 +950,11 @@ init_env_context();
|
|
|
940
950
|
init_logger();
|
|
941
951
|
var logger4 = createLogger("task:tools:complete");
|
|
942
952
|
var TASK_COMPLETE_DESCRIPTION = `
|
|
943
|
-
|
|
953
|
+
Final acceptance of a submitted task: set status=completed, progress=100
|
|
954
|
+
(two-phase acceptance, Task #5073).
|
|
955
|
+
|
|
956
|
+
**Who should call this** — the **acceptance side** (default agent). The
|
|
957
|
+
executor (sub-agent) has NO permission to call it (deniedTools, Task #5007).
|
|
944
958
|
|
|
945
959
|
**Before calling this tool**, you MUST:
|
|
946
960
|
|
|
@@ -968,21 +982,27 @@ Mark a task as completed (progress=100, status=completed). Creates an operation
|
|
|
968
982
|
(milestone_type=decision) — the reason **must** land in the
|
|
969
983
|
operation record (必须落到操作记录里), not merely a line in
|
|
970
984
|
your final report (不能只在报告里带一句).
|
|
971
|
-
3.
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
-
|
|
975
|
-
|
|
976
|
-
- Cloud drive URL (e.g., https://my.feishu.cn/file/abc)
|
|
977
|
-
- Or any other locator that points to where the work product lives
|
|
978
|
-
|
|
979
|
-
The \`deliverable_storage_path\` will be persisted to the task's \`context.deliverable_storage_path\` for future reference and traceability.
|
|
985
|
+
3. Deliverables were already submitted by the executor via
|
|
986
|
+
\`task_pre_complete\` (they live in \`context.deliverable_storage_path\`
|
|
987
|
+
and the operation trail — Task #5073 design: task_complete takes **no**
|
|
988
|
+
deliverable parameter). Cross-check them in step 2; do NOT ask the
|
|
989
|
+
executor to resubmit.
|
|
980
990
|
|
|
981
991
|
**Restrictions**:
|
|
982
992
|
- Cannot complete cycle-type tasks (they are periodic). Use \`task_update\` to reset progress instead.
|
|
983
993
|
- Marks the task as 100% complete; use \`task_update\` to record partial progress first if needed.
|
|
984
994
|
|
|
985
|
-
**
|
|
995
|
+
**Side effects**:
|
|
996
|
+
- Sets \`status = "completed"\`, \`progress = 100\` **and refreshes \`current_status\`**
|
|
997
|
+
to the completed wording. You do **NOT** need a follow-up
|
|
998
|
+
\`task_update(current_status=...)\` call — a task must never keep a stale
|
|
999
|
+
pre-completion progress sentence (e.g. "paused, not committed") that would
|
|
1000
|
+
contradict \`status = "completed"\`.
|
|
1001
|
+
- The goal-check decision model does **NOT** run here — it already ran at
|
|
1002
|
+
\`task_pre_complete\` time (Task #5073 design v2). This tool is the final
|
|
1003
|
+
state commit by the acceptance side.
|
|
1004
|
+
- Cannot complete a task that is already in a terminal state (cancelled /
|
|
1005
|
+
archived) — reopen it first via \`task_update(status="active")\`.
|
|
986
1006
|
`.trim();
|
|
987
1007
|
function completeTaskTool(taskComponent) {
|
|
988
1008
|
return {
|
|
@@ -1010,7 +1030,18 @@ function completeTaskTool(taskComponent) {
|
|
|
1010
1030
|
metadata: { execution_time_ms: 0 }
|
|
1011
1031
|
};
|
|
1012
1032
|
}
|
|
1013
|
-
const
|
|
1033
|
+
const TERMINAL_NON_ACCEPTABLE = new Set([
|
|
1034
|
+
...TERMINAL_NON_ACCEPTABLE_STATUSES
|
|
1035
|
+
]);
|
|
1036
|
+
if (TERMINAL_NON_ACCEPTABLE.has(currentTask.status)) {
|
|
1037
|
+
return {
|
|
1038
|
+
success: false,
|
|
1039
|
+
output: "",
|
|
1040
|
+
error: `Cannot complete task #${params.task_id}: it is already in terminal status "${currentTask.status}". Reopen it first via task_update(status="active") if acceptance should proceed.`,
|
|
1041
|
+
metadata: { execution_time_ms: 0 }
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
const task = await taskComponent.completeTask(params.task_id, sessionId);
|
|
1014
1045
|
if (!task) {
|
|
1015
1046
|
return {
|
|
1016
1047
|
success: false,
|
|
@@ -1020,12 +1051,10 @@ function completeTaskTool(taskComponent) {
|
|
|
1020
1051
|
};
|
|
1021
1052
|
}
|
|
1022
1053
|
setCurrentTaskId(undefined);
|
|
1023
|
-
logger4.info(`[completeTask] Trace: Task #${params.task_id} completed
|
|
1054
|
+
logger4.info(`[completeTask] Trace: Task #${params.task_id} completed (acceptance committed), currentTaskId cleared from EnvContext`);
|
|
1024
1055
|
return {
|
|
1025
1056
|
success: true,
|
|
1026
|
-
output:
|
|
1027
|
-
Deliverable (context + operation): ${params.deliverable_storage_path}` : `Task completed: #${task.id} - ${task.title}
|
|
1028
|
-
Deliverable: (no path provided)`,
|
|
1057
|
+
output: `Task completed: #${task.id} - ${task.title}`,
|
|
1029
1058
|
metadata: { execution_time_ms: 0, task }
|
|
1030
1059
|
};
|
|
1031
1060
|
} catch (error) {
|
|
@@ -1044,13 +1073,17 @@ init_logger();
|
|
|
1044
1073
|
var logger5 = createLogger("tool:cancel-task");
|
|
1045
1074
|
var NON_CANCELLABLE_STATUSES = new Set(["completed", "cancelled", "archived"]);
|
|
1046
1075
|
var TASK_CANCEL_DESCRIPTION = `
|
|
1047
|
-
Cancel a task (task.status = "cancelled"). This is the **acceptance-side** cancel
|
|
1048
|
-
use it when the work is abandoned, the user asked to stop, or acceptance failed.
|
|
1076
|
+
Cancel a task (task.status = "cancelled"). This is the **acceptance-side** cancel.
|
|
1049
1077
|
|
|
1050
|
-
**When to use
|
|
1051
|
-
- The user asks to cancel / stop / drop the task
|
|
1052
|
-
|
|
1053
|
-
|
|
1078
|
+
**When to use** — ONLY when the user explicitly asks (用户明令):
|
|
1079
|
+
- The user asks to cancel / stop / drop the task (用户明令取消 / 停掉 / 放弃这个任务)
|
|
1080
|
+
|
|
1081
|
+
**When NOT to use**:
|
|
1082
|
+
- ❌ A sub-agent failed / timed out / went off-track — that is NOT a cancellation.
|
|
1083
|
+
Find the root cause, propose a fix, and re-delegate (with \`sub_session_id\` to resume).
|
|
1084
|
+
If it truly cannot be moved forward, report the situation to the user and **wait for
|
|
1085
|
+
the user's decision** — do NOT cancel it on your own.
|
|
1086
|
+
- ❌ Acceptance found gaps but re-delegation is still possible — re-delegate instead.
|
|
1054
1087
|
|
|
1055
1088
|
**What it does NOT do**:
|
|
1056
1089
|
- It does **NOT** stop a running background process — that is \`stop_task\`
|
|
@@ -1060,6 +1093,9 @@ use it when the work is abandoned, the user asked to stop, or acceptance failed.
|
|
|
1060
1093
|
**Side effects**:
|
|
1061
1094
|
- Sets \`status = "cancelled"\` and fires the task terminal-state hooks
|
|
1062
1095
|
(notifications / card state machine converge to the cancelled state).
|
|
1096
|
+
- Also refreshes \`current_status\` to the cancelled wording (with your \`reason\`
|
|
1097
|
+
appended when given) — the task no longer keeps a stale pre-cancel progress
|
|
1098
|
+
sentence that would contradict \`status = "cancelled"\`.
|
|
1063
1099
|
- Writes an operation record so the cancellation reason stays auditable.
|
|
1064
1100
|
- A cancelled task can be reopened later with \`task_update(status="active")\`.
|
|
1065
1101
|
|
|
@@ -1094,7 +1130,8 @@ function cancelTaskTool(taskComponent) {
|
|
|
1094
1130
|
};
|
|
1095
1131
|
}
|
|
1096
1132
|
const task = await taskComponent.updateTask(params.task_id, {
|
|
1097
|
-
status: "cancelled"
|
|
1133
|
+
status: "cancelled",
|
|
1134
|
+
current_status: params.reason ? `${TASK_CANCELLED_STATUS_WORD}:${params.reason}` : TASK_CANCELLED_STATUS_WORD
|
|
1098
1135
|
});
|
|
1099
1136
|
if (!task) {
|
|
1100
1137
|
return {
|
|
@@ -1128,6 +1165,88 @@ function cancelTaskTool(taskComponent) {
|
|
|
1128
1165
|
}
|
|
1129
1166
|
};
|
|
1130
1167
|
}
|
|
1168
|
+
// src/env/task/tools/pre-complete-tool.ts
|
|
1169
|
+
init_logger();
|
|
1170
|
+
var logger6 = createLogger("task:tools:pre-complete");
|
|
1171
|
+
var TASK_PRE_COMPLETE_DESCRIPTION = `
|
|
1172
|
+
Submit a task for acceptance (two-phase acceptance, Task #5073).
|
|
1173
|
+
|
|
1174
|
+
**Who should call this** — the **executor** (sub-agent) when it believes
|
|
1175
|
+
the work is done and deliverables are ready.
|
|
1176
|
+
|
|
1177
|
+
**What it does**:
|
|
1178
|
+
- Sets \`status = "to_validate"\` (待验证) — the task enters the
|
|
1179
|
+
acceptance queue. This is **NOT** a terminal state.
|
|
1180
|
+
- Persists \`deliverable_storage_path\` to \`task.context.deliverable_storage_path\`
|
|
1181
|
+
and writes a \`Deliverable: <path>\` line into the operation trail.
|
|
1182
|
+
- Refreshes \`current_status\` to the to-validate wording
|
|
1183
|
+
(with your progress summary appended when given).
|
|
1184
|
+
- Creates an operation record so the submission stays auditable.
|
|
1185
|
+
|
|
1186
|
+
**What it does NOT do**:
|
|
1187
|
+
- It does **NOT** mark the task completed — final acceptance
|
|
1188
|
+
(\`task_complete\`) belongs to the **default agent** (验收权分离, Task #5007).
|
|
1189
|
+
|
|
1190
|
+
**Goal self-check (design v2)**:
|
|
1191
|
+
- Submitting **does** fire the completion goal-check hook once
|
|
1192
|
+
(\`terminalStatus="to_validate"\`) as a self-check gate — the
|
|
1193
|
+
decision model reviews your goals/operations at submission time.
|
|
1194
|
+
If it produces a hint, read it and address the gaps **before**
|
|
1195
|
+
ending your turn. The final acceptance (\`task_complete\`) does
|
|
1196
|
+
**not** re-run the goal check.
|
|
1197
|
+
|
|
1198
|
+
**After calling**:
|
|
1199
|
+
- Push progress to 100 via \`task_update(progress=100)\` if not already,
|
|
1200
|
+
then **end your turn and return the result** to the main agent.
|
|
1201
|
+
- The default agent will pick up the notification, verify the goals
|
|
1202
|
+
item-by-item, and call \`task_complete\` (or re-delegate on gaps).
|
|
1203
|
+
|
|
1204
|
+
**Parameters**:
|
|
1205
|
+
- \`task_id\` (required): the task to submit
|
|
1206
|
+
- \`deliverable_storage_path\` (optional): where the work product lives
|
|
1207
|
+
(file path / Feishu doc URL / git branch / PR URL ...)
|
|
1208
|
+
- \`current_status\` (optional): a one-line progress summary shown to the
|
|
1209
|
+
acceptance side (e.g. "实现完成,测试全绿")
|
|
1210
|
+
|
|
1211
|
+
**Restrictions**:
|
|
1212
|
+
- Cannot pre-complete cycle-type tasks (they are periodic).
|
|
1213
|
+
`.trim();
|
|
1214
|
+
function preCompleteTaskTool(taskComponent) {
|
|
1215
|
+
return {
|
|
1216
|
+
name: "task_pre_complete",
|
|
1217
|
+
description: TASK_PRE_COMPLETE_DESCRIPTION,
|
|
1218
|
+
parameters: PreCompleteTaskToolSchema,
|
|
1219
|
+
execute: async (args, ctx) => {
|
|
1220
|
+
const params = PreCompleteTaskToolSchema.parse(args);
|
|
1221
|
+
const sessionId = ctx.session_id || "unknown";
|
|
1222
|
+
try {
|
|
1223
|
+
const task = await taskComponent.preCompleteTask(params.task_id, sessionId, params.deliverable_storage_path, params.current_status);
|
|
1224
|
+
if (!task) {
|
|
1225
|
+
return {
|
|
1226
|
+
success: false,
|
|
1227
|
+
output: "",
|
|
1228
|
+
error: `Task not found: ${params.task_id}`,
|
|
1229
|
+
metadata: { execution_time_ms: 0 }
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
return {
|
|
1233
|
+
success: true,
|
|
1234
|
+
output: params.deliverable_storage_path ? `Task submitted for acceptance (to_validate): #${task.id} - ${task.title}
|
|
1235
|
+
Deliverable (context + operation): ${params.deliverable_storage_path}` : `Task submitted for acceptance (to_validate): #${task.id} - ${task.title}
|
|
1236
|
+
Deliverable: (no path provided)`,
|
|
1237
|
+
metadata: { execution_time_ms: 0, task }
|
|
1238
|
+
};
|
|
1239
|
+
} catch (error) {
|
|
1240
|
+
return {
|
|
1241
|
+
success: false,
|
|
1242
|
+
output: "",
|
|
1243
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1244
|
+
metadata: { execution_time_ms: 0 }
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1131
1250
|
// src/env/task/tools/task-tree-tool.ts
|
|
1132
1251
|
function compareTasksDesc(a, b) {
|
|
1133
1252
|
if (a.updatedAt < b.updatedAt)
|
|
@@ -1266,4 +1385,4 @@ function treeTasksTool(taskComponent) {
|
|
|
1266
1385
|
}
|
|
1267
1386
|
};
|
|
1268
1387
|
}
|
|
1269
|
-
export { createTaskTool, getTaskTool, listTasksTool, searchTasksTool, batchArchiveTaskTool, batchDeleteTaskTool, updateTaskTool, deleteTaskTool, completeTaskTool, cancelTaskTool, treeTasksTool };
|
|
1388
|
+
export { createTaskTool, getTaskTool, listTasksTool, searchTasksTool, batchArchiveTaskTool, batchDeleteTaskTool, updateTaskTool, deleteTaskTool, completeTaskTool, cancelTaskTool, preCompleteTaskTool, treeTasksTool };
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
BackgroundTaskManager,
|
|
11
11
|
createDelegateTool
|
|
12
|
-
} from "./roy-agent-core-
|
|
12
|
+
} from "./roy-agent-core-aa2j7bk9.js";
|
|
13
13
|
import {
|
|
14
14
|
TaskEntityEventTypes
|
|
15
15
|
} from "./roy-agent-core-8gxth0eh.js";
|
|
@@ -22,10 +22,11 @@ import {
|
|
|
22
22
|
deleteTaskTool,
|
|
23
23
|
getTaskTool,
|
|
24
24
|
listTasksTool,
|
|
25
|
+
preCompleteTaskTool,
|
|
25
26
|
searchTasksTool,
|
|
26
27
|
treeTasksTool,
|
|
27
28
|
updateTaskTool
|
|
28
|
-
} from "./roy-agent-core-
|
|
29
|
+
} from "./roy-agent-core-vffccgf6.js";
|
|
29
30
|
import {
|
|
30
31
|
createOperationTool,
|
|
31
32
|
deleteOperationTool,
|
|
@@ -40,6 +41,16 @@ import {
|
|
|
40
41
|
import {
|
|
41
42
|
TaskHookPoints
|
|
42
43
|
} from "./roy-agent-core-1pg0fepg.js";
|
|
44
|
+
import {
|
|
45
|
+
TASK_ARCHIVED_STATUS_WORD,
|
|
46
|
+
TASK_CANCELLED_STATUS_WORD,
|
|
47
|
+
TASK_COMPLETED_STATUS_WORD,
|
|
48
|
+
TASK_DELIVERABLE_RECORDED,
|
|
49
|
+
TASK_REOPENED_STATUS_WORD,
|
|
50
|
+
TASK_TERMINAL_STATUS_WORDS,
|
|
51
|
+
TASK_TO_VALIDATE_STATUS_WORD,
|
|
52
|
+
TERMINAL_NON_ACCEPTABLE_STATUSES
|
|
53
|
+
} from "./roy-agent-core-4rtg69cn.js";
|
|
43
54
|
import {
|
|
44
55
|
envKeyToConfigKey
|
|
45
56
|
} from "./roy-agent-core-7hh0brvs.js";
|
|
@@ -87,16 +98,6 @@ var TASK_CONFIG_REGISTRATION = {
|
|
|
87
98
|
|
|
88
99
|
// src/env/task/task-component.ts
|
|
89
100
|
init_global_hook_manager();
|
|
90
|
-
|
|
91
|
-
// src/env/task/hooks/contexts.ts
|
|
92
|
-
var TASK_COMPLETE_TERMINAL_STATUSES = [
|
|
93
|
-
"completed",
|
|
94
|
-
"failed",
|
|
95
|
-
"cancelled",
|
|
96
|
-
"timeout"
|
|
97
|
-
];
|
|
98
|
-
|
|
99
|
-
// src/env/task/task-component.ts
|
|
100
101
|
init_env_context();
|
|
101
102
|
|
|
102
103
|
// src/env/task/tag-service.ts
|
|
@@ -187,6 +188,17 @@ class DefaultTagService {
|
|
|
187
188
|
// src/env/task/task-component.ts
|
|
188
189
|
init_decorator();
|
|
189
190
|
var logger2 = createLogger("task");
|
|
191
|
+
function isTerminalTaskStatus(status) {
|
|
192
|
+
return status === "completed" || status === "cancelled" || status === "archived";
|
|
193
|
+
}
|
|
194
|
+
function looksLikeSystemTerminalWording(currentStatus) {
|
|
195
|
+
if (!currentStatus)
|
|
196
|
+
return false;
|
|
197
|
+
const trimmed = currentStatus.trim();
|
|
198
|
+
if (!trimmed)
|
|
199
|
+
return false;
|
|
200
|
+
return TASK_TERMINAL_STATUS_WORDS.some((word) => trimmed.startsWith(word));
|
|
201
|
+
}
|
|
190
202
|
var DELIVERABLE_OPERATION_PREFIX = "Deliverable: ";
|
|
191
203
|
function formatDeliverableForOperation(deliverableStoragePath) {
|
|
192
204
|
if (!deliverableStoragePath)
|
|
@@ -391,7 +403,8 @@ class TaskComponent extends BaseComponent {
|
|
|
391
403
|
updateTaskTool(this),
|
|
392
404
|
deleteTaskTool(this),
|
|
393
405
|
completeTaskTool(this),
|
|
394
|
-
cancelTaskTool(this)
|
|
406
|
+
cancelTaskTool(this),
|
|
407
|
+
preCompleteTaskTool(this)
|
|
395
408
|
];
|
|
396
409
|
const operationToolsList = [
|
|
397
410
|
createOperationTool(this),
|
|
@@ -465,6 +478,31 @@ class TaskComponent extends BaseComponent {
|
|
|
465
478
|
const operations = await this.store.listOperations({ taskId: id });
|
|
466
479
|
return { ...task, operations, task };
|
|
467
480
|
}
|
|
481
|
+
withTerminalCurrentStatus(options, previousTask) {
|
|
482
|
+
const status = options.status;
|
|
483
|
+
if (!status)
|
|
484
|
+
return options;
|
|
485
|
+
if (options.current_status !== undefined)
|
|
486
|
+
return options;
|
|
487
|
+
let terminalWord;
|
|
488
|
+
switch (status) {
|
|
489
|
+
case "completed":
|
|
490
|
+
terminalWord = TASK_COMPLETED_STATUS_WORD;
|
|
491
|
+
break;
|
|
492
|
+
case "cancelled":
|
|
493
|
+
terminalWord = TASK_CANCELLED_STATUS_WORD;
|
|
494
|
+
break;
|
|
495
|
+
case "archived":
|
|
496
|
+
terminalWord = TASK_ARCHIVED_STATUS_WORD;
|
|
497
|
+
break;
|
|
498
|
+
default:
|
|
499
|
+
if (previousTask && isTerminalTaskStatus(previousTask.status) && looksLikeSystemTerminalWording(previousTask.current_status)) {
|
|
500
|
+
return { ...options, current_status: TASK_REOPENED_STATUS_WORD };
|
|
501
|
+
}
|
|
502
|
+
return options;
|
|
503
|
+
}
|
|
504
|
+
return { ...options, current_status: terminalWord };
|
|
505
|
+
}
|
|
468
506
|
async updateTask(id, options) {
|
|
469
507
|
const updateCtx = {
|
|
470
508
|
id,
|
|
@@ -472,7 +510,13 @@ class TaskComponent extends BaseComponent {
|
|
|
472
510
|
tagService: this.tagService
|
|
473
511
|
};
|
|
474
512
|
await this.executeHook(TaskHookPoints.BEFORE_UPDATE, updateCtx);
|
|
475
|
-
const
|
|
513
|
+
const requestedStatus = updateCtx.options.status;
|
|
514
|
+
let previousTask;
|
|
515
|
+
if (requestedStatus && !isTerminalTaskStatus(requestedStatus)) {
|
|
516
|
+
previousTask = await this.store.getTask(id);
|
|
517
|
+
}
|
|
518
|
+
const effectiveOptions = this.withTerminalCurrentStatus(updateCtx.options, previousTask);
|
|
519
|
+
const task = await this.store.updateTask(id, effectiveOptions);
|
|
476
520
|
if (task) {
|
|
477
521
|
const resultCtx = {
|
|
478
522
|
task,
|
|
@@ -482,18 +526,6 @@ class TaskComponent extends BaseComponent {
|
|
|
482
526
|
await this.executeHook(TaskHookPoints.AFTER_UPDATE, resultCtx);
|
|
483
527
|
this.publishEvent(TaskEntityEventTypes.UPDATED, { task, changes: options, sessionId: "" }, "");
|
|
484
528
|
logger2.info(`[TaskComponent] Task updated: ${id}`);
|
|
485
|
-
if (options.status && TASK_COMPLETE_TERMINAL_STATUSES.includes(options.status)) {
|
|
486
|
-
const env = getEnvContextOrEmpty();
|
|
487
|
-
const completeCtx = {
|
|
488
|
-
task,
|
|
489
|
-
changes: options,
|
|
490
|
-
terminalStatus: options.status,
|
|
491
|
-
sessionId: env.sessionId ?? "",
|
|
492
|
-
tagService: this.tagService
|
|
493
|
-
};
|
|
494
|
-
await this.executeHook(TaskHookPoints.AFTER_COMPLETE, completeCtx);
|
|
495
|
-
logger2.info(`[TaskComponent] Task completed (AFTER_COMPLETE fired): ${id} -> ${options.status}`);
|
|
496
|
-
}
|
|
497
529
|
}
|
|
498
530
|
return task;
|
|
499
531
|
}
|
|
@@ -526,9 +558,13 @@ class TaskComponent extends BaseComponent {
|
|
|
526
558
|
if (existing && existing.type === "cycle") {
|
|
527
559
|
throw new Error(`Cannot complete cycle task: #${id} - ${existing.title}. Cycle tasks are not meant to be completed.`);
|
|
528
560
|
}
|
|
561
|
+
if (existing && TERMINAL_NON_ACCEPTABLE_STATUSES.includes(existing.status)) {
|
|
562
|
+
throw new Error(`Cannot complete task: #${id} - ${existing.title}. ` + `It is already in terminal status "${existing.status}". ` + `Reopen it first via task_update(status="active") if acceptance should proceed.`);
|
|
563
|
+
}
|
|
529
564
|
const updateOptions = {
|
|
530
565
|
status: "completed",
|
|
531
|
-
progress: 100
|
|
566
|
+
progress: 100,
|
|
567
|
+
current_status: deliverableStoragePath ? `${TASK_COMPLETED_STATUS_WORD}(${TASK_DELIVERABLE_RECORDED}:${deliverableStoragePath})` : TASK_COMPLETED_STATUS_WORD
|
|
532
568
|
};
|
|
533
569
|
if (deliverableStoragePath) {
|
|
534
570
|
let existingContext = {};
|
|
@@ -562,6 +598,59 @@ class TaskComponent extends BaseComponent {
|
|
|
562
598
|
async updateTaskContext(id, context) {
|
|
563
599
|
return this.store.updateTask(id, { context });
|
|
564
600
|
}
|
|
601
|
+
async preCompleteTask(id, sessionId, deliverableStoragePath, progressSummary) {
|
|
602
|
+
const existing = await this.store.getTask(id);
|
|
603
|
+
if (existing && existing.type === "cycle") {
|
|
604
|
+
throw new Error(`Cannot pre-complete cycle task: #${id} - ${existing.title}. Cycle tasks are not meant to be completed or submitted for acceptance.`);
|
|
605
|
+
}
|
|
606
|
+
const toValidateWording = progressSummary ? `${TASK_TO_VALIDATE_STATUS_WORD}:${progressSummary}` : TASK_TO_VALIDATE_STATUS_WORD;
|
|
607
|
+
const updateOptions = {
|
|
608
|
+
status: "to_validate",
|
|
609
|
+
current_status: toValidateWording
|
|
610
|
+
};
|
|
611
|
+
if (deliverableStoragePath) {
|
|
612
|
+
let existingContext = {};
|
|
613
|
+
if (existing?.context) {
|
|
614
|
+
try {
|
|
615
|
+
const parsed = JSON.parse(existing.context);
|
|
616
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
617
|
+
existingContext = parsed;
|
|
618
|
+
}
|
|
619
|
+
} catch {
|
|
620
|
+
logger2.warn(`[TaskComponent] Task #${id} context is not valid JSON, ignoring when merging deliverable`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
existingContext.deliverable_storage_path = deliverableStoragePath;
|
|
624
|
+
updateOptions.context = JSON.stringify(existingContext);
|
|
625
|
+
}
|
|
626
|
+
const task = await this.updateTask(id, updateOptions);
|
|
627
|
+
if (task) {
|
|
628
|
+
const env = getEnvContextOrEmpty();
|
|
629
|
+
const completeCtx = {
|
|
630
|
+
task,
|
|
631
|
+
changes: updateOptions,
|
|
632
|
+
terminalStatus: "to_validate",
|
|
633
|
+
sessionId: env.sessionId ?? sessionId,
|
|
634
|
+
tagService: this.tagService
|
|
635
|
+
};
|
|
636
|
+
await this.executeHook(TaskHookPoints.AFTER_COMPLETE, completeCtx);
|
|
637
|
+
const descParts = [];
|
|
638
|
+
const deliverableLine = formatDeliverableForOperation(deliverableStoragePath);
|
|
639
|
+
if (deliverableLine)
|
|
640
|
+
descParts.push(deliverableLine);
|
|
641
|
+
if (progressSummary)
|
|
642
|
+
descParts.push(progressSummary);
|
|
643
|
+
await this.createOperation({
|
|
644
|
+
taskId: id,
|
|
645
|
+
sessionId,
|
|
646
|
+
milestoneType: "milestone",
|
|
647
|
+
milestoneTitle: `${TASK_TO_VALIDATE_STATUS_WORD}(to_validate):Task #${id} submitted for acceptance`,
|
|
648
|
+
milestoneDescription: descParts.join(" / ") || TASK_TO_VALIDATE_STATUS_WORD
|
|
649
|
+
});
|
|
650
|
+
logger2.info(`[TaskComponent] Task submitted for acceptance (to_validate): ${id}, deliverable=${deliverableStoragePath || "(none)"}`);
|
|
651
|
+
}
|
|
652
|
+
return task;
|
|
653
|
+
}
|
|
565
654
|
async createOperation(options) {
|
|
566
655
|
const createCtx = {
|
|
567
656
|
options: { ...options }
|
|
@@ -630,12 +719,15 @@ __legacyDecorateClassTS([
|
|
|
630
719
|
__legacyDecorateClassTS([
|
|
631
720
|
TracedAs("task.component.completeTask", { recordParams: true, recordResult: true, log: true })
|
|
632
721
|
], TaskComponent.prototype, "completeTask", null);
|
|
722
|
+
__legacyDecorateClassTS([
|
|
723
|
+
TracedAs("task.component.preCompleteTask", { recordParams: true, recordResult: true, log: true })
|
|
724
|
+
], TaskComponent.prototype, "preCompleteTask", null);
|
|
633
725
|
__legacyDecorateClassTS([
|
|
634
726
|
TracedAs("task.component.createOperation", { recordParams: true, recordResult: true, log: true })
|
|
635
727
|
], TaskComponent.prototype, "createOperation", null);
|
|
636
728
|
// src/env/task/types.ts
|
|
637
729
|
import { z } from "zod";
|
|
638
|
-
var TaskStatusEnum = z.enum(["todo", "active", "completed", "paused", "cancelled", "archived"]);
|
|
730
|
+
var TaskStatusEnum = z.enum(["todo", "active", "to_validate", "completed", "paused", "cancelled", "archived"]);
|
|
639
731
|
var TaskPriorityEnum = z.enum(["low", "medium", "high"]);
|
|
640
732
|
var TaskTypeEnum = z.enum(["normal", "cycle", "longterm"]);
|
|
641
733
|
var ActionTypeEnum = z.enum([
|