@ai-setting/roy-agent-core 1.7.72 → 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.
Files changed (91) hide show
  1. package/dist/env/agent/agent-component.js +6 -6
  2. package/dist/env/agent/agent-registry.d.ts +4 -1
  3. package/dist/env/agent/index.js +6 -6
  4. package/dist/env/error-reply.d.ts +46 -0
  5. package/dist/env/error-reply.js +3 -1
  6. package/dist/env/event-source/event-source-handlers.js +13 -12
  7. package/dist/env/event-source/index.js +14 -13
  8. package/dist/env/index.js +31 -30
  9. package/dist/env/llm/index.js +2 -2
  10. package/dist/env/llm/orphan-toolcall.d.ts +107 -0
  11. package/dist/env/plugin/index.js +3 -3
  12. package/dist/env/prompt/index.js +4 -3
  13. package/dist/env/prompt/prompts-index.d.ts +27 -3
  14. package/dist/env/session/index.js +4 -4
  15. package/dist/env/session/storage/index.js +3 -3
  16. package/dist/env/task/backend/index.js +9 -1
  17. package/dist/env/task/delegate/delegate-tool.d.ts +8 -0
  18. package/dist/env/task/delegate/delegate-tool.js +5 -4
  19. package/dist/env/task/delegate/index.js +5 -4
  20. package/dist/env/task/hooks/contexts.d.ts +12 -1
  21. package/dist/env/task/hooks/task-hook-points.d.ts +8 -3
  22. package/dist/env/task/index.d.ts +1 -0
  23. package/dist/env/task/index.js +17 -7
  24. package/dist/env/task/mcp/index.js +3 -2
  25. package/dist/env/task/plugins/index.d.ts +3 -0
  26. package/dist/env/task/plugins/index.js +33 -9
  27. package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +61 -0
  28. package/dist/env/task/plugins/lark-card-patch-notifier.js +6 -2
  29. package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +42 -0
  30. package/dist/env/task/prompt/index.js +2 -1
  31. package/dist/env/task/prompt/task-discipline.d.ts +9 -3
  32. package/dist/env/task/task-component.d.ts +79 -0
  33. package/dist/env/task/tools/cancel-tool.d.ts +36 -0
  34. package/dist/env/task/tools/completion-copy.d.ts +92 -0
  35. package/dist/env/task/tools/index.d.ts +2 -0
  36. package/dist/env/task/tools/index.js +7 -2
  37. package/dist/env/task/tools/pre-complete-tool.d.ts +32 -0
  38. package/dist/env/task/tools/task-types.d.ts +71 -17
  39. package/dist/env/task/types.d.ts +13 -1
  40. package/dist/env/tool/built-in/compact-session-tool.d.ts +120 -0
  41. package/dist/env/tool/built-in/index.d.ts +2 -0
  42. package/dist/env/tool/built-in/index.js +6 -2
  43. package/dist/env/tool/index.js +4 -4
  44. package/dist/env/workflow/engine/index.js +6 -6
  45. package/dist/env/workflow/index.js +15 -13
  46. package/dist/env/workflow/plugins/index.js +2 -2
  47. package/dist/env/workflow/tools/ask-user-tool.d.ts +88 -0
  48. package/dist/env/workflow/tools/index.js +11 -9
  49. package/dist/env/workflow/tools/run-workflow.d.ts +2 -2
  50. package/dist/env/workflow/types/index.js +2 -2
  51. package/dist/env/workflow/types/workflow-hil.d.ts +25 -1
  52. package/dist/env/workflow/utils/index.js +5 -5
  53. package/dist/index.d.ts +1 -1
  54. package/dist/index.js +43 -33
  55. package/dist/shared/@ai-setting/{roy-agent-core-sm0sf6sb.js → roy-agent-core-0qyrk5k0.js} +2 -2
  56. package/dist/shared/@ai-setting/{roy-agent-core-kmbct9yg.js → roy-agent-core-0snm5hc5.js} +1 -1
  57. package/dist/shared/@ai-setting/{roy-agent-core-yr1mryb8.js → roy-agent-core-1v4g0d4x.js} +1 -1
  58. package/dist/shared/@ai-setting/{roy-agent-core-rxb00gjm.js → roy-agent-core-264qexac.js} +1 -1
  59. package/dist/shared/@ai-setting/roy-agent-core-2akfd64z.js +374 -0
  60. package/dist/shared/@ai-setting/{roy-agent-core-skyc30qx.js → roy-agent-core-3fh1f7s7.js} +2 -2
  61. package/dist/shared/@ai-setting/roy-agent-core-4rtg69cn.js +26 -0
  62. package/dist/shared/@ai-setting/{roy-agent-core-add1zqsh.js → roy-agent-core-6cy43nss.js} +22 -1
  63. package/dist/shared/@ai-setting/{roy-agent-core-vah6g5yx.js → roy-agent-core-6rh8k6ba.js} +5 -5
  64. package/dist/shared/@ai-setting/{roy-agent-core-kpg25s2q.js → roy-agent-core-8ez6mccb.js} +4 -2
  65. package/dist/shared/@ai-setting/{roy-agent-core-yhwxpj12.js → roy-agent-core-9vxh6f8h.js} +233 -16
  66. package/dist/shared/@ai-setting/{roy-agent-core-7qvw65jt.js → roy-agent-core-aa2j7bk9.js} +11 -8
  67. package/dist/shared/@ai-setting/{roy-agent-core-sjr1kmfz.js → roy-agent-core-acj8ag08.js} +1 -1
  68. package/dist/shared/@ai-setting/{roy-agent-core-mds8tqd0.js → roy-agent-core-cgtq6427.js} +1 -1
  69. package/dist/shared/@ai-setting/{roy-agent-core-34jz9mgh.js → roy-agent-core-cmprygrp.js} +1 -1
  70. package/dist/shared/@ai-setting/{roy-agent-core-wr7mv7k3.js → roy-agent-core-cxy143m4.js} +1 -1
  71. package/dist/shared/@ai-setting/{roy-agent-core-pakhchy7.js → roy-agent-core-ddyk1rc0.js} +1 -1
  72. package/dist/shared/@ai-setting/{roy-agent-core-attpry6g.js → roy-agent-core-dqxec05w.js} +4 -3
  73. package/dist/shared/@ai-setting/{roy-agent-core-vm8h5zyj.js → roy-agent-core-ed233k5z.js} +1 -1
  74. package/dist/shared/@ai-setting/{roy-agent-core-jrj30vq3.js → roy-agent-core-er070zjq.js} +1 -1
  75. package/dist/shared/@ai-setting/{roy-agent-core-40rv283c.js → roy-agent-core-fghc2m8z.js} +152 -2
  76. package/dist/shared/@ai-setting/{roy-agent-core-5gw0qa8m.js → roy-agent-core-ghnagzrh.js} +557 -92
  77. package/dist/shared/@ai-setting/{roy-agent-core-4jjwewvw.js → roy-agent-core-m46fe3x4.js} +41 -30
  78. package/dist/shared/@ai-setting/{roy-agent-core-e25xkv53.js → roy-agent-core-mkc3zshb.js} +3 -1
  79. package/dist/shared/@ai-setting/{roy-agent-core-hfyfavp3.js → roy-agent-core-mww0mnd9.js} +231 -27
  80. package/dist/shared/@ai-setting/{roy-agent-core-46rv4d7d.js → roy-agent-core-nmx9934r.js} +92 -2
  81. package/dist/shared/@ai-setting/{roy-agent-core-1w9y7mxj.js → roy-agent-core-p3zv1mx5.js} +1 -1
  82. package/dist/shared/@ai-setting/{roy-agent-core-jgb3bb42.js → roy-agent-core-q5r75vev.js} +17 -8
  83. package/dist/shared/@ai-setting/{roy-agent-core-y12kyxwa.js → roy-agent-core-t3rrb959.js} +33 -9
  84. package/dist/shared/@ai-setting/{roy-agent-core-dt104qa1.js → roy-agent-core-v85n42ve.js} +1 -1
  85. package/dist/shared/@ai-setting/{roy-agent-core-gr48c89p.js → roy-agent-core-v87njmq7.js} +3 -3
  86. package/dist/shared/@ai-setting/{roy-agent-core-7sj9xs9c.js → roy-agent-core-vffccgf6.js} +252 -25
  87. package/dist/shared/@ai-setting/{roy-agent-core-2gzt2q8c.js → roy-agent-core-z43aj03k.js} +134 -29
  88. package/dist/shared/@ai-setting/roy-agent-core-zycadbpx.js +662 -0
  89. package/package.json +1 -1
  90. package/dist/shared/@ai-setting/roy-agent-core-1p6syv85.js +0 -1083
  91. package/dist/shared/@ai-setting/roy-agent-core-58804s3j.js +0 -155
@@ -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
- TASK_DISCIPLINE_GOALS
10
- } from "./roy-agent-core-4jjwewvw.js";
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,13 @@ 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
- deliverable_storage_path: z.string().optional().describe("交付物的寻址地址(路径 / URL / 分支名等)。" + "调用本工具前,请先读取任务的 goal(description)和 delivery(goals_and_expected_deliverables)栏位," + "建议验证目标和交付物是否符合预期,然后将实际交付物的寻址地址填入此参数。")
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)。")
112
+ });
113
+ var CancelTaskToolSchema = withLegacyTaskId({
114
+ reason: z.string().optional().describe("取消原因(可选)。会写入该任务的 operation 记录,便于事后追溯验收结论。")
105
115
  });
106
116
  var BatchArchiveToolSchema = z.object({
107
117
  task_ids: z.array(z.number()).describe("要归档的任务 ID 列表"),
@@ -113,7 +123,7 @@ var BatchDeleteToolSchema = z.object({
113
123
  var TreeTasksToolSchema = z.object({
114
124
  depth: z.number().optional().default(2).describe("树的展开深度。默认 2 (根 + 一层子任务 + 二层孙任务)。" + "0 = 只返回根任务,不展开任何子节点。" + "-1 或负数 = 无限层级(全部展开)。"),
115
125
  parent_task_id: z.number().optional().describe("只看指定父任务的子树。不传则从所有根任务(forest)开始。"),
116
- 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 一致)。"),
117
127
  priority: z.enum(["low", "medium", "high"]).optional().describe("按优先级过滤。"),
118
128
  type: z.enum(["normal", "cycle", "longterm"]).optional().describe("按任务类型过滤。"),
119
129
  include_archived: z.boolean().optional().default(false).describe("是否包含已归档任务(默认 false,与 task_list 一致)。")
@@ -699,7 +709,10 @@ function batchArchiveTaskTool(taskComponent) {
699
709
  let archivedCount = 0;
700
710
  for (const taskId of taskIds) {
701
711
  try {
702
- const task = await taskComponent.updateTask(taskId, { status: "archived" });
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
+ });
703
716
  if (task) {
704
717
  await taskComponent.createOperation({
705
718
  taskId,
@@ -828,6 +841,22 @@ function updateTaskTool(taskComponent) {
828
841
  };
829
842
  }
830
843
  }
844
+ const callerAgentType = ctx.metadata?.agentType ?? undefined;
845
+ const callerAgentName = ctx.metadata?.agentName ?? undefined;
846
+ if (params.status === "completed" && callerAgentType === "sub") {
847
+ const agentComponent = taskComponent.env?.getComponent?.("agent");
848
+ const callerAgent = callerAgentName && agentComponent?.getAgent ? agentComponent.getAgent(callerAgentName) : undefined;
849
+ const cfg = callerAgent?.config;
850
+ const hasCompletePermission = cfg ? Array.isArray(cfg.allowedTools) && cfg.allowedTools.includes("task_complete") || !Array.isArray(cfg.allowedTools) && !(Array.isArray(cfg.deniedTools) && cfg.deniedTools.includes("task_complete")) : false;
851
+ if (!hasCompletePermission) {
852
+ return {
853
+ success: false,
854
+ output: "",
855
+ error: `Cannot set status="completed" without acceptance permission (Task #5007 — 验收权分离). ` + `执行方只能推进进度:用 task_update(progress=100) 表示"我这边干完了",` + `再 task_operation_create 记录结论,然后结束返回;` + `任务终态必须由验收方(default agent / 主智能体)调用 task_complete 落定。`,
856
+ metadata: { execution_time_ms: 0 }
857
+ };
858
+ }
859
+ }
831
860
  const task = await taskComponent.updateTask(params.task_id, {
832
861
  title: params.title,
833
862
  description: params.description,
@@ -921,7 +950,11 @@ init_env_context();
921
950
  init_logger();
922
951
  var logger4 = createLogger("task:tools:complete");
923
952
  var TASK_COMPLETE_DESCRIPTION = `
924
- Mark a task as completed (progress=100, status=completed). Creates an operation record.
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).
925
958
 
926
959
  **Before calling this tool**, you MUST:
927
960
 
@@ -949,21 +982,27 @@ Mark a task as completed (progress=100, status=completed). Creates an operation
949
982
  (milestone_type=decision) — the reason **must** land in the
950
983
  operation record (必须落到操作记录里), not merely a line in
951
984
  your final report (不能只在报告里带一句).
952
- 3. **Provide \`deliverable_storage_path\`** parameter with the deliverable's address:
953
- - Feishu doc URL (e.g., https://my.feishu.cn/docx/abc)
954
- - Local file path (e.g., /tmp/report.md)
955
- - Git branch or worktree path (e.g., feature/my-feature)
956
- - GitHub commit URL or PR URL
957
- - Cloud drive URL (e.g., https://my.feishu.cn/file/abc)
958
- - Or any other locator that points to where the work product lives
959
-
960
- 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.
961
990
 
962
991
  **Restrictions**:
963
992
  - Cannot complete cycle-type tasks (they are periodic). Use \`task_update\` to reset progress instead.
964
993
  - Marks the task as 100% complete; use \`task_update\` to record partial progress first if needed.
965
994
 
966
- **Backward compatibility**: \`deliverable_storage_path\` is optional. Existing callers that omit it continue to work as before.
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")\`.
967
1006
  `.trim();
968
1007
  function completeTaskTool(taskComponent) {
969
1008
  return {
@@ -991,7 +1030,18 @@ function completeTaskTool(taskComponent) {
991
1030
  metadata: { execution_time_ms: 0 }
992
1031
  };
993
1032
  }
994
- const task = await taskComponent.completeTask(params.task_id, sessionId, params.deliverable_storage_path);
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);
995
1045
  if (!task) {
996
1046
  return {
997
1047
  success: false,
@@ -1001,11 +1051,188 @@ function completeTaskTool(taskComponent) {
1001
1051
  };
1002
1052
  }
1003
1053
  setCurrentTaskId(undefined);
1004
- logger4.info(`[completeTask] Trace: Task #${params.task_id} completed, deliverable=${params.deliverable_storage_path || "(none)"}, currentTaskId cleared from EnvContext`);
1054
+ logger4.info(`[completeTask] Trace: Task #${params.task_id} completed (acceptance committed), currentTaskId cleared from EnvContext`);
1055
+ return {
1056
+ success: true,
1057
+ output: `Task completed: #${task.id} - ${task.title}`,
1058
+ metadata: { execution_time_ms: 0, task }
1059
+ };
1060
+ } catch (error) {
1061
+ return {
1062
+ success: false,
1063
+ output: "",
1064
+ error: error instanceof Error ? error.message : String(error),
1065
+ metadata: { execution_time_ms: 0 }
1066
+ };
1067
+ }
1068
+ }
1069
+ };
1070
+ }
1071
+ // src/env/task/tools/cancel-tool.ts
1072
+ init_logger();
1073
+ var logger5 = createLogger("tool:cancel-task");
1074
+ var NON_CANCELLABLE_STATUSES = new Set(["completed", "cancelled", "archived"]);
1075
+ var TASK_CANCEL_DESCRIPTION = `
1076
+ Cancel a task (task.status = "cancelled"). This is the **acceptance-side** cancel.
1077
+
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.
1087
+
1088
+ **What it does NOT do**:
1089
+ - It does **NOT** stop a running background process — that is \`stop_task\`
1090
+ (which takes a bg-process id from \`delegate_task\`). If the sub-agent is still
1091
+ running, call \`stop_task\` **first**, then \`task_cancel\` to close the record.
1092
+
1093
+ **Side effects**:
1094
+ - Sets \`status = "cancelled"\` and fires the task terminal-state hooks
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"\`.
1099
+ - Writes an operation record so the cancellation reason stays auditable.
1100
+ - A cancelled task can be reopened later with \`task_update(status="active")\`.
1101
+
1102
+ **Parameters**:
1103
+ - \`task_id\` (required): the task to cancel
1104
+ - \`reason\` (optional): why it is being cancelled — recorded in the operation trail
1105
+ `.trim();
1106
+ function cancelTaskTool(taskComponent) {
1107
+ return {
1108
+ name: "task_cancel",
1109
+ description: TASK_CANCEL_DESCRIPTION,
1110
+ parameters: CancelTaskToolSchema,
1111
+ execute: async (args, ctx) => {
1112
+ const params = CancelTaskToolSchema.parse(args);
1113
+ const sessionId = ctx.session_id || "unknown";
1114
+ try {
1115
+ const current = await taskComponent.getTask(params.task_id);
1116
+ if (!current) {
1117
+ return {
1118
+ success: false,
1119
+ output: "",
1120
+ error: `Task not found: ${params.task_id}`,
1121
+ metadata: { execution_time_ms: 0 }
1122
+ };
1123
+ }
1124
+ if (NON_CANCELLABLE_STATUSES.has(current.status)) {
1125
+ return {
1126
+ success: false,
1127
+ output: "",
1128
+ error: `Cannot cancel task #${params.task_id}: it is already in terminal status "${current.status}".`,
1129
+ metadata: { execution_time_ms: 0, task_id: params.task_id }
1130
+ };
1131
+ }
1132
+ const task = await taskComponent.updateTask(params.task_id, {
1133
+ status: "cancelled",
1134
+ current_status: params.reason ? `${TASK_CANCELLED_STATUS_WORD}:${params.reason}` : TASK_CANCELLED_STATUS_WORD
1135
+ });
1136
+ if (!task) {
1137
+ return {
1138
+ success: false,
1139
+ output: "",
1140
+ error: `Task not found: ${params.task_id}`,
1141
+ metadata: { execution_time_ms: 0 }
1142
+ };
1143
+ }
1144
+ await taskComponent.createOperation({
1145
+ taskId: params.task_id,
1146
+ sessionId,
1147
+ milestoneType: "decision",
1148
+ milestoneTitle: `Task cancelled: #${params.task_id}`,
1149
+ milestoneDescription: params.reason ? `Cancelled by acceptance side. Reason: ${params.reason}` : "Cancelled by acceptance side (no reason given)."
1150
+ });
1151
+ logger5.info(`[cancelTask] Task #${params.task_id} cancelled (previous status: ${current.status})`);
1152
+ return {
1153
+ success: true,
1154
+ output: `Task cancelled: #${params.task_id} - ${task.title}`,
1155
+ metadata: { execution_time_ms: 0, task }
1156
+ };
1157
+ } catch (error) {
1158
+ return {
1159
+ success: false,
1160
+ output: "",
1161
+ error: error instanceof Error ? error.message : String(error),
1162
+ metadata: { execution_time_ms: 0 }
1163
+ };
1164
+ }
1165
+ }
1166
+ };
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
+ }
1005
1232
  return {
1006
1233
  success: true,
1007
- output: params.deliverable_storage_path ? `Task completed: #${task.id} - ${task.title}
1008
- Deliverable (context + operation): ${params.deliverable_storage_path}` : `Task completed: #${task.id} - ${task.title}
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}
1009
1236
  Deliverable: (no path provided)`,
1010
1237
  metadata: { execution_time_ms: 0, task }
1011
1238
  };
@@ -1158,4 +1385,4 @@ function treeTasksTool(taskComponent) {
1158
1385
  }
1159
1386
  };
1160
1387
  }
1161
- export { createTaskTool, getTaskTool, listTasksTool, searchTasksTool, batchArchiveTaskTool, batchDeleteTaskTool, updateTaskTool, deleteTaskTool, completeTaskTool, treeTasksTool };
1388
+ export { createTaskTool, getTaskTool, listTasksTool, searchTasksTool, batchArchiveTaskTool, batchDeleteTaskTool, updateTaskTool, deleteTaskTool, completeTaskTool, cancelTaskTool, preCompleteTaskTool, treeTasksTool };
@@ -1,28 +1,32 @@
1
1
  import {
2
+ compactSessionTool,
3
+ createCompactSessionTool,
2
4
  createQueryBackgroundProcessTool
3
- } from "./roy-agent-core-58804s3j.js";
5
+ } from "./roy-agent-core-2akfd64z.js";
4
6
  import {
5
7
  createStopTool
6
8
  } from "./roy-agent-core-jm136jzh.js";
7
9
  import {
8
10
  BackgroundTaskManager,
9
11
  createDelegateTool
10
- } from "./roy-agent-core-7qvw65jt.js";
12
+ } from "./roy-agent-core-aa2j7bk9.js";
11
13
  import {
12
14
  TaskEntityEventTypes
13
15
  } from "./roy-agent-core-8gxth0eh.js";
14
16
  import {
15
17
  batchArchiveTaskTool,
16
18
  batchDeleteTaskTool,
19
+ cancelTaskTool,
17
20
  completeTaskTool,
18
21
  createTaskTool,
19
22
  deleteTaskTool,
20
23
  getTaskTool,
21
24
  listTasksTool,
25
+ preCompleteTaskTool,
22
26
  searchTasksTool,
23
27
  treeTasksTool,
24
28
  updateTaskTool
25
- } from "./roy-agent-core-7sj9xs9c.js";
29
+ } from "./roy-agent-core-vffccgf6.js";
26
30
  import {
27
31
  createOperationTool,
28
32
  deleteOperationTool,
@@ -37,6 +41,16 @@ import {
37
41
  import {
38
42
  TaskHookPoints
39
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";
40
54
  import {
41
55
  envKeyToConfigKey
42
56
  } from "./roy-agent-core-7hh0brvs.js";
@@ -84,16 +98,6 @@ var TASK_CONFIG_REGISTRATION = {
84
98
 
85
99
  // src/env/task/task-component.ts
86
100
  init_global_hook_manager();
87
-
88
- // src/env/task/hooks/contexts.ts
89
- var TASK_COMPLETE_TERMINAL_STATUSES = [
90
- "completed",
91
- "failed",
92
- "cancelled",
93
- "timeout"
94
- ];
95
-
96
- // src/env/task/task-component.ts
97
101
  init_env_context();
98
102
 
99
103
  // src/env/task/tag-service.ts
@@ -184,6 +188,17 @@ class DefaultTagService {
184
188
  // src/env/task/task-component.ts
185
189
  init_decorator();
186
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
+ }
187
202
  var DELIVERABLE_OPERATION_PREFIX = "Deliverable: ";
188
203
  function formatDeliverableForOperation(deliverableStoragePath) {
189
204
  if (!deliverableStoragePath)
@@ -336,6 +351,9 @@ class TaskComponent extends BaseComponent {
336
351
  await this.registerTools();
337
352
  this.toolComponent.register(createQueryBackgroundProcessTool(this.backgroundTaskManager), "built-in");
338
353
  logger2.debug("[TaskComponent] Injected BackgroundTaskManager into query_background_process tool (factory pattern)");
354
+ const sessionComponent = this.env?.getComponent("session");
355
+ this.toolComponent.register(createCompactSessionTool(this.backgroundTaskManager, sessionComponent ?? null), "built-in");
356
+ logger2.debug("[TaskComponent] Registered compact_session tool (factory pattern)");
339
357
  } else {
340
358
  logger2.warn("[TaskComponent] ToolComponent not found, skipping tool registration");
341
359
  }
@@ -352,6 +370,12 @@ class TaskComponent extends BaseComponent {
352
370
  } catch (err) {
353
371
  logger2.warn(`[TaskComponent] Failed to reset query_background_process tool: ${err.message}`);
354
372
  }
373
+ try {
374
+ this.toolComponent.register(compactSessionTool, "built-in");
375
+ logger2.debug("[TaskComponent] Reset compact_session tool to no-deps default (cleanup)");
376
+ } catch (err) {
377
+ logger2.warn(`[TaskComponent] Failed to reset compact_session tool: ${err.message}`);
378
+ }
355
379
  }
356
380
  if (this.backgroundTaskManager) {
357
381
  await this.backgroundTaskManager.dispose(this.skipAbortRunningTasksOnDispose ? {
@@ -378,7 +402,9 @@ class TaskComponent extends BaseComponent {
378
402
  batchDeleteTaskTool(this),
379
403
  updateTaskTool(this),
380
404
  deleteTaskTool(this),
381
- completeTaskTool(this)
405
+ completeTaskTool(this),
406
+ cancelTaskTool(this),
407
+ preCompleteTaskTool(this)
382
408
  ];
383
409
  const operationToolsList = [
384
410
  createOperationTool(this),
@@ -452,6 +478,31 @@ class TaskComponent extends BaseComponent {
452
478
  const operations = await this.store.listOperations({ taskId: id });
453
479
  return { ...task, operations, task };
454
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
+ }
455
506
  async updateTask(id, options) {
456
507
  const updateCtx = {
457
508
  id,
@@ -459,7 +510,13 @@ class TaskComponent extends BaseComponent {
459
510
  tagService: this.tagService
460
511
  };
461
512
  await this.executeHook(TaskHookPoints.BEFORE_UPDATE, updateCtx);
462
- const task = await this.store.updateTask(id, updateCtx.options);
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);
463
520
  if (task) {
464
521
  const resultCtx = {
465
522
  task,
@@ -469,18 +526,6 @@ class TaskComponent extends BaseComponent {
469
526
  await this.executeHook(TaskHookPoints.AFTER_UPDATE, resultCtx);
470
527
  this.publishEvent(TaskEntityEventTypes.UPDATED, { task, changes: options, sessionId: "" }, "");
471
528
  logger2.info(`[TaskComponent] Task updated: ${id}`);
472
- if (options.status && TASK_COMPLETE_TERMINAL_STATUSES.includes(options.status)) {
473
- const env = getEnvContextOrEmpty();
474
- const completeCtx = {
475
- task,
476
- changes: options,
477
- terminalStatus: options.status,
478
- sessionId: env.sessionId ?? "",
479
- tagService: this.tagService
480
- };
481
- await this.executeHook(TaskHookPoints.AFTER_COMPLETE, completeCtx);
482
- logger2.info(`[TaskComponent] Task completed (AFTER_COMPLETE fired): ${id} -> ${options.status}`);
483
- }
484
529
  }
485
530
  return task;
486
531
  }
@@ -513,9 +558,13 @@ class TaskComponent extends BaseComponent {
513
558
  if (existing && existing.type === "cycle") {
514
559
  throw new Error(`Cannot complete cycle task: #${id} - ${existing.title}. Cycle tasks are not meant to be completed.`);
515
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
+ }
516
564
  const updateOptions = {
517
565
  status: "completed",
518
- progress: 100
566
+ progress: 100,
567
+ current_status: deliverableStoragePath ? `${TASK_COMPLETED_STATUS_WORD}(${TASK_DELIVERABLE_RECORDED}:${deliverableStoragePath})` : TASK_COMPLETED_STATUS_WORD
519
568
  };
520
569
  if (deliverableStoragePath) {
521
570
  let existingContext = {};
@@ -549,6 +598,59 @@ class TaskComponent extends BaseComponent {
549
598
  async updateTaskContext(id, context) {
550
599
  return this.store.updateTask(id, { context });
551
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
+ }
552
654
  async createOperation(options) {
553
655
  const createCtx = {
554
656
  options: { ...options }
@@ -617,12 +719,15 @@ __legacyDecorateClassTS([
617
719
  __legacyDecorateClassTS([
618
720
  TracedAs("task.component.completeTask", { recordParams: true, recordResult: true, log: true })
619
721
  ], TaskComponent.prototype, "completeTask", null);
722
+ __legacyDecorateClassTS([
723
+ TracedAs("task.component.preCompleteTask", { recordParams: true, recordResult: true, log: true })
724
+ ], TaskComponent.prototype, "preCompleteTask", null);
620
725
  __legacyDecorateClassTS([
621
726
  TracedAs("task.component.createOperation", { recordParams: true, recordResult: true, log: true })
622
727
  ], TaskComponent.prototype, "createOperation", null);
623
728
  // src/env/task/types.ts
624
729
  import { z } from "zod";
625
- var TaskStatusEnum = z.enum(["todo", "active", "completed", "paused", "cancelled", "archived"]);
730
+ var TaskStatusEnum = z.enum(["todo", "active", "to_validate", "completed", "paused", "cancelled", "archived"]);
626
731
  var TaskPriorityEnum = z.enum(["low", "medium", "high"]);
627
732
  var TaskTypeEnum = z.enum(["normal", "cycle", "longterm"]);
628
733
  var ActionTypeEnum = z.enum([