@ai-setting/roy-agent-core 1.7.66 → 1.7.68

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 (42) hide show
  1. package/dist/env/agent/agent-component.d.ts +29 -0
  2. package/dist/env/agent/agent-component.js +1 -1
  3. package/dist/env/agent/index.js +1 -1
  4. package/dist/env/agent/types.d.ts +16 -0
  5. package/dist/env/environment.d.ts +13 -0
  6. package/dist/env/event-source/event-source-handlers.js +6 -5
  7. package/dist/env/event-source/index.js +7 -6
  8. package/dist/env/event-source/timer-handler.d.ts +93 -1
  9. package/dist/env/index.js +12 -11
  10. package/dist/env/prompt/index.js +3 -2
  11. package/dist/env/prompt/prompts-index.d.ts +2 -7
  12. package/dist/env/task/delegate/delegate-tool.d.ts +21 -1
  13. package/dist/env/task/delegate/delegate-tool.js +3 -2
  14. package/dist/env/task/delegate/index.js +3 -2
  15. package/dist/env/task/index.js +5 -4
  16. package/dist/env/task/mcp/index.js +2 -1
  17. package/dist/env/task/plugins/__e2e__/laya-completion-e2e.d.ts +16 -0
  18. package/dist/env/task/plugins/index.js +4 -3
  19. package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +77 -0
  20. package/dist/env/task/plugins/lark-card-patch-notifier.js +7 -1
  21. package/dist/env/task/plugins/laya-completion-plugin.d.ts +133 -0
  22. package/dist/env/task/plugins/laya-port.d.ts +23 -0
  23. package/dist/env/task/task-component.d.ts +43 -0
  24. package/dist/env/task/tools/completion-copy.d.ts +27 -0
  25. package/dist/env/task/tools/index.js +2 -1
  26. package/dist/env/workflow/plugins/index.js +2 -2
  27. package/dist/index.js +13 -12
  28. package/dist/shared/@ai-setting/{roy-agent-core-8nksrtnf.js → roy-agent-core-19na22cc.js} +203 -18
  29. package/dist/shared/@ai-setting/{roy-agent-core-nr2m4xw1.js → roy-agent-core-1k83dq3a.js} +13 -3
  30. package/dist/shared/@ai-setting/roy-agent-core-43r4bxey.js +1083 -0
  31. package/dist/shared/@ai-setting/roy-agent-core-6m4bw8jy.js +6 -0
  32. package/dist/shared/@ai-setting/{roy-agent-core-0ys85x46.js → roy-agent-core-8bjj3d1b.js} +1 -1
  33. package/dist/shared/@ai-setting/{roy-agent-core-2wxbx36p.js → roy-agent-core-dnjm17sa.js} +21 -2
  34. package/dist/shared/@ai-setting/{roy-agent-core-t0ktaevq.js → roy-agent-core-h5vmff8w.js} +60 -4
  35. package/dist/shared/@ai-setting/{roy-agent-core-j1kksatb.js → roy-agent-core-hfr6bzts.js} +67 -10
  36. package/dist/shared/@ai-setting/{roy-agent-core-v8bz7d8x.js → roy-agent-core-keyb45z3.js} +26 -11
  37. package/dist/shared/@ai-setting/{roy-agent-core-vs5gxgwh.js → roy-agent-core-ma35jyd9.js} +14 -3
  38. package/dist/shared/@ai-setting/{roy-agent-core-zk2jg0v7.js → roy-agent-core-tadj19vp.js} +106 -22
  39. package/dist/shared/@ai-setting/{roy-agent-core-cvm1xgmg.js → roy-agent-core-wksgtqf9.js} +1 -1
  40. package/dist/shared/@ai-setting/{roy-agent-core-4882qsjx.js → roy-agent-core-x0ddc2cr.js} +1 -1
  41. package/package.json +1 -1
  42. package/dist/shared/@ai-setting/roy-agent-core-8mjnb7xc.js +0 -654
@@ -0,0 +1,6 @@
1
+ // src/env/task/tools/completion-copy.ts
2
+ var TASK_COMPLETE_ITEM_BY_ITEM = "按子项逐条验收";
3
+ var TASK_COMPLETION_HINT_ANCHOR = "TASK_COMPLETION_HINT";
4
+ var TASK_COMPLETE_VERIFY_DUTY = "核实义务";
5
+
6
+ export { TASK_COMPLETE_ITEM_BY_ITEM, TASK_COMPLETION_HINT_ANCHOR, TASK_COMPLETE_VERIFY_DUTY };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  LarkCardPatchNotifier,
3
3
  formatDurationMs
4
- } from "./roy-agent-core-zk2jg0v7.js";
4
+ } from "./roy-agent-core-tadj19vp.js";
5
5
  import {
6
6
  WorkflowHookPoints,
7
7
  init_workflow_hook_points
@@ -10,7 +10,7 @@ import {
10
10
  } from "./roy-agent-core-1pg0fepg.js";
11
11
  import {
12
12
  builtInPrompts
13
- } from "./roy-agent-core-vs5gxgwh.js";
13
+ } from "./roy-agent-core-ma35jyd9.js";
14
14
  import {
15
15
  DEFAULT_SUBAGENT_PROMPT
16
16
  } from "./roy-agent-core-07fvy74y.js";
@@ -1832,14 +1832,33 @@ ${reuseSection.markdown}`;
1832
1832
  async forceStopAll() {
1833
1833
  return this.forceStopAllAcrossParents();
1834
1834
  }
1835
- async dispose() {
1835
+ async dispose(options) {
1836
1836
  for (const [, timer] of this.progressTimers) {
1837
1837
  try {
1838
1838
  clearInterval(timer);
1839
1839
  } catch {}
1840
1840
  }
1841
1841
  this.progressTimers.clear();
1842
+ const skipAbortRunningTasks = options?.skipAbortRunningTasks === true;
1843
+ const waitForRunningTasks = options?.waitForRunningTasks === true;
1844
+ if (waitForRunningTasks && skipAbortRunningTasks) {
1845
+ const waitStart = Date.now();
1846
+ const WAIT_TIMEOUT_MS = 10 * 60 * 1000;
1847
+ while (Date.now() - waitStart < WAIT_TIMEOUT_MS) {
1848
+ let running = 0;
1849
+ for (const [, t] of this.tasks) {
1850
+ if (t.status === "running" || t.status === "pending")
1851
+ running++;
1852
+ }
1853
+ if (running === 0)
1854
+ break;
1855
+ logger3.info(`[BackgroundTaskManager] dispose waiting for ${running} running task(s)`);
1856
+ await new Promise((resolve) => setTimeout(resolve, 200));
1857
+ }
1858
+ }
1842
1859
  for (const [, controller] of this.abortControllers) {
1860
+ if (skipAbortRunningTasks)
1861
+ break;
1843
1862
  try {
1844
1863
  controller.abort();
1845
1864
  } catch {}
@@ -2,6 +2,11 @@ import {
2
2
  NEW_TASK_ID_HASH_PLACEHOLDER,
3
3
  NEW_TASK_ID_PLACEHOLDER
4
4
  } from "./roy-agent-core-tr3ep81v.js";
5
+ import {
6
+ TASK_COMPLETE_ITEM_BY_ITEM,
7
+ TASK_COMPLETE_VERIFY_DUTY,
8
+ TASK_COMPLETION_HINT_ANCHOR
9
+ } from "./roy-agent-core-6m4bw8jy.js";
5
10
  import {
6
11
  getEnvContextOrEmpty,
7
12
  init_env_context,
@@ -38,7 +43,7 @@ var CreateTaskToolSchema = z.object({
38
43
  description: z.string().optional().describe("Task description"),
39
44
  priority: z.enum(["low", "medium", "high"]).optional().describe("Task priority"),
40
45
  type: z.enum(["normal", "cycle", "longterm"]).optional().default("normal").describe("Task type. normal: 常规任务,需要跟踪完成. cycle: 周期任务(如每周发周报),无需 complete,定期执行. longterm: 长期任务/愿景目标(如提升公司影响力),难以一次性完成,持续跟进"),
41
- goals_and_expected_deliverables: z.string().optional().describe("Goals and expected deliverables"),
46
+ goals_and_expected_deliverables: z.string().optional().describe("Goals and expected deliverables. " + "⚠️ 写成可逐条验收的编号子项(1. … 2. … 3. …),每条是一个可判定的交付/动作" + "(文件断言:文件存在/大小/内容;进程断言:命令 exit code/stdout;" + "测试断言:pass/fail 计数;状态断言:版本号/HTTP 200/状态字段)。" + "避免一句话模糊目标(如「做好 X」「完成调研」)——" + "任务完成时 completion hook 会按这些子项逐条对照操作记录验收。"),
42
47
  due_date: z.string().optional().describe("Due date (ISO format)"),
43
48
  tags: z.array(z.string()).optional().describe("Task tags (for search, stored in task.tags JSON field)"),
44
49
  project_path: z.string().default("unknown").describe("Project root path for task positioning. " + "Used to locate the project space for this task. " + "Use 'unknown' if unknown. (Defaults to 'unknown'.)"),
@@ -84,7 +89,7 @@ var UpdateTaskToolSchema = withLegacyTaskId({
84
89
  type: z.enum(["normal", "cycle", "longterm"]).optional().describe("Task type"),
85
90
  progress: z.number().min(0).max(100).optional(),
86
91
  current_status: z.string().optional(),
87
- goals_and_expected_deliverables: z.string().optional(),
92
+ goals_and_expected_deliverables: z.string().optional().describe("Goals and expected deliverables. " + "⚠️ 追加/修改时同样写成可逐条验收的编号子项(1. … 2. … 3. …)," + "每条是一个可判定的交付/动作(文件断言:文件存在/大小/内容;" + "进程断言:命令 exit code/stdout;测试断言:pass/fail 计数;" + "状态断言:版本号/HTTP 200/状态字段)。" + "避免一句话模糊目标(如「做好 X」「完成调研」)——" + "任务完成时 completion hook 会按这些子项逐条对照操作记录验收。" + "(Task #4833 — 与 task_create 的 goals 规则同源)"),
88
93
  due_date: z.string().optional(),
89
94
  tags: z.array(z.string()).optional(),
90
95
  project_path: z.string().optional().describe("Project path, identifies which project this task belongs to"),
@@ -316,6 +321,37 @@ function createTaskTool(taskComponent) {
316
321
  - Same-theme tasks cluster under one parent
317
322
  - Long-tail/stranded tasks should be archived or merged
318
323
 
324
+ ## ⚠️ Goals 子项拆分规则 (Task #4791)
325
+
326
+ \`goals_and_expected_deliverables\` **MUST** be written as
327
+ **numbered sub-items** (编号子项) that can each be verified
328
+ one by one (逐条验收) — for \`type=normal\` tasks:
329
+
330
+ 1. 实现 X(含文件路径)
331
+ 2. 测试 Y 覆盖 Z 场景(pass/fail 计数)
332
+ 3. 文档 W 落盘到 <path>
333
+
334
+ ⚠️ For \`type=longterm\` / \`type=cycle\` tasks there is no
335
+ per-item completion acceptance(不走逐条 complete 验收), so the
336
+ MUST above does not apply; for those, it is **strongly recommended**
337
+ (建议) to split goals into staged, individually checkable sub-items
338
+ (阶段性可判子项) instead of one vague sentence.
339
+
340
+ Why: when the task is completed, the completion hook
341
+ (task:after.complete) judges the whole goal against the
342
+ operation-record chain, using each sub-item as fine-grained
343
+ evidence (逐条对照的依据). Vague one-line goals (一句话模糊目标,
344
+ 如「做好 X」「完成调研」) give the judge nothing to check and
345
+ make a completion hint much more likely.
346
+
347
+ Reference the four atomic assertion forms (Task #4768
348
+ 四原子形态) when writing each sub-item:
349
+
350
+ - 文件断言 (file assertion): file exists / size / content grep
351
+ - 进程断言 (process assertion): command exit code / stdout match
352
+ - 测试断言 (test assertion): pass/fail counts, no NEW fail
353
+ - 状态断言 (state assertion): version number / HTTP 200 / status field
354
+
319
355
  ## ⚠️ Spatial + Temporal Context (Task #2325)
320
356
 
321
357
  A task MUST carry spatio-temporal context so it survives task-handoff
@@ -917,8 +953,28 @@ Mark a task as completed (progress=100, status=completed). Creates an operation
917
953
 
918
954
  1. Read the task's \`goal\` (description) and \`delivery\` (goals_and_expected_deliverables) fields.
919
955
  - Use \`task_get(task_id=<id>)\` to fetch the full task record.
920
- 2. **Validate** whether the goal and delivery match what you actually delivered.
921
- - If mismatch, either: (a) extend the task via \`task_update\`, or (b) honestly note the discrepancy in your final report.
956
+ 2. **Validate whether each goal sub-item was achieved** (${TASK_COMPLETE_ITEM_BY_ITEM}).
957
+ - Goals should be numbered sub-items (编号子项, see task_create rules). Check
958
+ each sub-item against the operation-record chain (操作记录链) and the
959
+ deliverable evidence (context.deliverable_storage_path).
960
+ - The completion hook (task:after.complete) judges the whole goal vs.
961
+ deliverables; numbered sub-items give it finer-grained evidence to
962
+ reason with (逐条对照的证据粒度). If it detects that the goal or
963
+ deliverables appear unmet, the task context will carry a
964
+ \`[${TASK_COMPLETION_HINT_ANCHOR}]\`.
965
+ - **If you receive a [${TASK_COMPLETION_HINT_ANCHOR}]**, you MUST verify it
966
+ (${TASK_COMPLETE_VERIFY_DUTY}): re-read the task via \`task_get\`, then do exactly one of:
967
+ (a) 确认未完成 → \`task_update(task_id=<id>, status=todo)\` reopen and continue; or
968
+ (b) 确认已完成 → \`task_operation_create\` (milestone_type=decision,
969
+ milestone_title=<一行结论>) state why the sub-items are actually
970
+ met (说明理由).
971
+ Never ignore it (不允许无视).
972
+ - If the goal and delivery mismatch **without** a hint, either:
973
+ (a) extend the task via \`task_update\`, or
974
+ (b) record the discrepancy via \`task_operation_create\`
975
+ (milestone_type=decision) — the reason **must** land in the
976
+ operation record (必须落到操作记录里), not merely a line in
977
+ your final report (不能只在报告里带一句).
922
978
  3. **Provide \`deliverable_storage_path\`** parameter with the deliverable's address:
923
979
  - Feishu doc URL (e.g., https://my.feishu.cn/docx/abc)
924
980
  - Local file path (e.g., /tmp/report.md)
@@ -68,6 +68,8 @@ import {
68
68
 
69
69
  // src/env/agent/agent-component.ts
70
70
  init_logger();
71
+ import fs from "fs/promises";
72
+ import path from "path";
71
73
  import { z } from "zod";
72
74
 
73
75
  // src/env/task/plugins/max-iteration-reminder-plugin.ts
@@ -600,6 +602,7 @@ class AgentComponent extends BaseComponent {
600
602
  doomLoopCaches = new Map;
601
603
  fallbackState = new Map;
602
604
  configComponent;
605
+ skipAbortRunningTasksOnStop = false;
603
606
  configWatcher;
604
607
  messageConverter = new SessionMessageConverter;
605
608
  _constructorConfig;
@@ -622,6 +625,7 @@ class AgentComponent extends BaseComponent {
622
625
  throw new Error("ConfigComponent is required for AgentComponent initialization");
623
626
  }
624
627
  this.configComponent = options.configComponent;
628
+ this.skipAbortRunningTasksOnStop = options.skipAbortRunningTasksOnStop === true;
625
629
  await this.registerConfig(options);
626
630
  await this.initRegistry(options.configComponent);
627
631
  this.setStatus("running");
@@ -794,13 +798,19 @@ class AgentComponent extends BaseComponent {
794
798
  }
795
799
  async onStop() {
796
800
  logger2.info("[AgentComponent] Stopping and cleaning up resources...");
797
- for (const [name, agent] of this.agents) {
798
- if (agent.status === "running" || agent.status === "idle") {
799
- this.abort(name);
800
- logger2.debug(`[AgentComponent] Aborted agent: ${name}`);
801
+ const skipAbort = this.skipAbortRunningTasksOnStop === true;
802
+ if (!skipAbort) {
803
+ for (const [name, agent] of this.agents) {
804
+ if (agent.status === "running" || agent.status === "idle") {
805
+ this.abort(name);
806
+ logger2.debug(`[AgentComponent] Aborted agent: ${name}`);
807
+ }
801
808
  }
809
+ } else {
810
+ logger2.info("[AgentComponent] skipAbortRunningTasksOnStop=true — keeping running agent runs alive");
802
811
  }
803
812
  this.abortControllers.clear();
813
+ this.aborted.clear();
804
814
  this.doomLoopCaches.clear();
805
815
  if (this.configWatcher) {
806
816
  this.configWatcher();
@@ -1603,12 +1613,18 @@ class AgentComponent extends BaseComponent {
1603
1613
  const session = await sessionComponent.get(sessionId);
1604
1614
  const existingSnapshot = session?.metadata?.cacheSnapshot;
1605
1615
  if (existingSnapshot && typeof existingSnapshot === "object" && existingSnapshot.systemPrompt) {
1606
- logger2.debug(`[AgentComponent._run] Reusing cacheSnapshot for session ${sessionId} (snapshotAt=${existingSnapshot.snapshotAt})`);
1607
- return {
1608
- snapshot: existingSnapshot,
1609
- systemPromptOverride: existingSnapshot.systemPrompt
1610
- };
1616
+ const stale = await this._isMemorySnapshotStale(existingSnapshot);
1617
+ if (stale) {
1618
+ logger2.info(`[AgentComponent._run] cacheSnapshot for session ${sessionId} is stale (${stale}); recreating`);
1619
+ } else {
1620
+ logger2.debug(`[AgentComponent._run] Reusing cacheSnapshot for session ${sessionId} (snapshotAt=${existingSnapshot.snapshotAt})`);
1621
+ return {
1622
+ snapshot: existingSnapshot,
1623
+ systemPromptOverride: existingSnapshot.systemPrompt
1624
+ };
1625
+ }
1611
1626
  }
1627
+ const memoryStat = await this._collectMemoryStat();
1612
1628
  let resolvedSystemPrompt = initialSystemPrompt;
1613
1629
  if (resolvedSystemPrompt && resolvedSystemPrompt.includes("{{memory}}")) {
1614
1630
  try {
@@ -1627,7 +1643,8 @@ class AgentComponent extends BaseComponent {
1627
1643
  const snapshot = {
1628
1644
  systemPrompt: resolvedSystemPrompt ?? agentConfigSystemPrompt ?? "",
1629
1645
  additionInfo: effectiveContext.additionInfo || "",
1630
- snapshotAt: Date.now()
1646
+ snapshotAt: Date.now(),
1647
+ memoryStat
1631
1648
  };
1632
1649
  try {
1633
1650
  await sessionComponent.update(sessionId, {
@@ -1646,6 +1663,46 @@ class AgentComponent extends BaseComponent {
1646
1663
  return { snapshot: null, systemPromptOverride: undefined };
1647
1664
  }
1648
1665
  }
1666
+ async _isMemorySnapshotStale(snapshot) {
1667
+ const stat = snapshot.memoryStat;
1668
+ if (!stat || typeof stat !== "object") {
1669
+ return "no-memoryStat (legacy snapshot)";
1670
+ }
1671
+ const current = await this._collectMemoryStat();
1672
+ if (current.globalMtime !== stat.globalMtime) {
1673
+ return `global memory.md mtime changed (${stat.globalMtime} -> ${current.globalMtime})`;
1674
+ }
1675
+ if (current.projectMtime !== stat.projectMtime) {
1676
+ return `project memory.md mtime changed (${stat.projectMtime} -> ${current.projectMtime})`;
1677
+ }
1678
+ return null;
1679
+ }
1680
+ async _collectMemoryStat() {
1681
+ const empty = { globalMtime: 0, projectMtime: 0 };
1682
+ try {
1683
+ const memoryComponent = this.env?.getComponent?.("memory");
1684
+ if (!memoryComponent?.getMemoryPaths || !memoryComponent.getMemoryFile) {
1685
+ return empty;
1686
+ }
1687
+ const memoryFile = memoryComponent.getMemoryFile();
1688
+ const paths = memoryComponent.getMemoryPaths();
1689
+ const result = { globalMtime: 0, projectMtime: 0 };
1690
+ for (const p of paths) {
1691
+ const filePath = path.join(p.path, memoryFile);
1692
+ try {
1693
+ const st = await fs.stat(filePath);
1694
+ if (p.type === "global") {
1695
+ result.globalMtime = st.mtimeMs;
1696
+ } else if (p.type === "project") {
1697
+ result.projectMtime = st.mtimeMs;
1698
+ }
1699
+ } catch {}
1700
+ }
1701
+ return result;
1702
+ } catch {
1703
+ return empty;
1704
+ }
1705
+ }
1649
1706
  async _fallbackReplaceMemory(systemPrompt) {
1650
1707
  if (!systemPrompt.includes("{{memory}}"))
1651
1708
  return systemPrompt;
@@ -10,7 +10,7 @@ import {
10
10
  } from "./roy-agent-core-1pg0fepg.js";
11
11
  import {
12
12
  WORKFLOW_EXTRACT_PROMPT
13
- } from "./roy-agent-core-vs5gxgwh.js";
13
+ } from "./roy-agent-core-ma35jyd9.js";
14
14
  import {
15
15
  assertNoLeakage,
16
16
  buildHintText,
@@ -24,7 +24,7 @@ import {
24
24
  import {
25
25
  LarkCardPatchNotifier,
26
26
  humanizeValue
27
- } from "./roy-agent-core-zk2jg0v7.js";
27
+ } from "./roy-agent-core-tadj19vp.js";
28
28
  import {
29
29
  validateWorkflowDefinition
30
30
  } from "./roy-agent-core-yr1mryb8.js";
@@ -929,6 +929,7 @@ function truncateSnippet(text) {
929
929
  var DEFAULT_TIMEOUT_MS = 2000;
930
930
  var DEFAULT_MAX_STRIKES = 3;
931
931
  var DEFAULT_MODEL = "task-decision";
932
+ var DEFAULT_GOAL_MODEL = "task-completion";
932
933
  var SYSTEMONE_PATH = "/v1/systemone";
933
934
 
934
935
  class LayaDecisionPort {
@@ -1058,19 +1059,32 @@ function isTimeoutError(err) {
1058
1059
  }
1059
1060
  return false;
1060
1061
  }
1062
+ var LAYA_DECISION_PROVIDER = "laya-decision";
1063
+ var LAYA_GOAL_DECISION_PROVIDER = "laya-goal-decision";
1064
+ function providerNameForPurpose(purpose) {
1065
+ return purpose === "goal" ? LAYA_GOAL_DECISION_PROVIDER : LAYA_DECISION_PROVIDER;
1066
+ }
1061
1067
  async function loadLayaAuth(authPath = `${process.env.HOME ?? "~"}/.roy-agent/auth.json`) {
1068
+ return loadLayaAuthForPurpose(authPath, "create");
1069
+ }
1070
+ async function loadLayaAuthForPurpose(authPath = `${process.env.HOME ?? "~"}/.roy-agent/auth.json`, purpose = "create") {
1062
1071
  try {
1063
1072
  const fs = await import("fs/promises");
1064
1073
  const raw = await fs.readFile(authPath, "utf-8");
1065
1074
  const parsed = JSON.parse(raw);
1066
- const p = parsed.providers?.["laya-decision"];
1067
- if (!p?.baseURL || !p?.apiKey)
1068
- return null;
1069
- return {
1070
- baseURL: p.baseURL,
1071
- apiKey: p.apiKey,
1072
- model: p.model ?? DEFAULT_MODEL
1073
- };
1075
+ const primary = providerNameForPurpose(purpose);
1076
+ const candidates = purpose === "goal" ? [primary, LAYA_DECISION_PROVIDER] : [primary];
1077
+ for (const name of candidates) {
1078
+ const p = parsed.providers?.[name];
1079
+ if (p?.baseURL && p?.apiKey) {
1080
+ return {
1081
+ baseURL: p.baseURL,
1082
+ apiKey: p.apiKey,
1083
+ model: p.model ?? (name === LAYA_GOAL_DECISION_PROVIDER ? DEFAULT_GOAL_MODEL : DEFAULT_MODEL)
1084
+ };
1085
+ }
1086
+ }
1087
+ return null;
1074
1088
  } catch {
1075
1089
  return null;
1076
1090
  }
@@ -2162,6 +2176,7 @@ class LarkCliTaskNotifyHook {
2162
2176
  status,
2163
2177
  meta: this.buildTaskMeta(task),
2164
2178
  finalStep: task.current_status || undefined,
2179
+ expandTimeline: rawStatus === "archived" ? true : undefined,
2165
2180
  ...rawStatus === "archived" ? { pausedMilestoneTitle: "Task 已归档" } : {}
2166
2181
  });
2167
2182
  if (isPaused) {
@@ -2937,4 +2952,4 @@ __legacyDecorateClassTS([
2937
2952
  function createPerTaskGroupChatPlugin(config = {}) {
2938
2953
  return new PerTaskGroupChatPlugin(config);
2939
2954
  }
2940
- export { createAutoTaskPlugin, LAYA_REUSE_THRESHOLD, LayaDecisionPlugin, createLayaDecisionPlugin, WORKFLOW_EXTRACTOR_PROMPT, formatExtractorInput, formatExtractorInputFromDescription, parseExtractorOutput, TaskTagPlugin, extractPerTaskChatId, extractPerTaskChatName, NotifyType, LarkCliTaskNotifyHook, createLarkCliTaskNotifyHook, PerTaskGroupChatPlugin, createPerTaskGroupChatPlugin };
2955
+ export { createAutoTaskPlugin, pickGateConfidence, LayaDecisionPort, loadLayaAuthForPurpose, LAYA_REUSE_THRESHOLD, LayaDecisionPlugin, createLayaDecisionPlugin, WORKFLOW_EXTRACTOR_PROMPT, formatExtractorInput, formatExtractorInputFromDescription, parseExtractorOutput, TaskTagPlugin, extractPerTaskChatId, extractPerTaskChatName, NotifyType, LarkCliTaskNotifyHook, createLarkCliTaskNotifyHook, PerTaskGroupChatPlugin, createPerTaskGroupChatPlugin };
@@ -1,3 +1,9 @@
1
+ import {
2
+ TASK_COMPLETE_ITEM_BY_ITEM,
3
+ TASK_COMPLETE_VERIFY_DUTY,
4
+ TASK_COMPLETION_HINT_ANCHOR
5
+ } from "./roy-agent-core-6m4bw8jy.js";
6
+
1
7
  // src/env/prompt/prompts-index.ts
2
8
  var builtInPrompts = {
3
9
  default: `你是 {{agent_name}} 接待智能体(Receptionist Agent)—— {{agent_name}} 系统的智能入口。
@@ -125,7 +131,7 @@ var builtInPrompts = {
125
131
  - ✅ 功能开发、bug 修复、重构、文档生成
126
132
  - ✅ 网络搜索和研究
127
133
 
128
- **roy** 的提示词中内置了任务生命周期——它知道如何使用 \`task_create\`、\`task_update\`、\`task_operation_create\` 和 \`task_complete\` 来跟踪每项工作。
134
+ **roy** 的提示词中内置了任务生命周期——它知道如何使用 \`task_create\`、\`task_update\`、\`task_operation_create\` 和 \`task_complete\` 来跟踪每项工作。task_complete 时会${TASK_COMPLETE_VERIFY_DUTY}:按子项逐条验收(${TASK_COMPLETE_ITEM_BY_ITEM}),若收到 [${TASK_COMPLETION_HINT_ANCHOR}] 必须核实并二选一(重开 / 落 decision 说明理由)。
129
135
 
130
136
  推荐使用:
131
137
  \`\`\`
@@ -893,8 +899,13 @@ Follow this lifecycle for every task:
893
899
  ┌─────────────────────────────────────────────────────────────────┐
894
900
  │ 6. COMPLETE (task_complete) │
895
901
  │ - Mark task as completed (progress=100%, status=completed) │
896
- │ - Verify all goals_and_expected_deliverables are met │
897
- │ - Create completion operation record │
902
+ │ - ${TASK_COMPLETE_ITEM_BY_ITEM}: check each goal sub-item │
903
+ │ against the operation-record chain + deliverable evidence │
904
+ │ - If a [${TASK_COMPLETION_HINT_ANCHOR}] appears in task │
905
+ │ context, you MUST ${TASK_COMPLETE_VERIFY_DUTY}: reopen │
906
+ │ (task_update status=todo) or record why it is met │
907
+ │ (task_operation_create decision). Never ignore it. │
908
+ │ - Create completion operation record │
898
909
  └─────────────────────────────────────────────────────────────────┘
899
910
  \`\`\`
900
911
 
@@ -148,6 +148,33 @@ var CONTEXT_SKIP_KEYS = new Set([
148
148
  "larkPerTaskChatTaskId",
149
149
  "larkPerTaskChatCreatedAt"
150
150
  ]);
151
+ function isTerminalStatusForTimeline(status) {
152
+ return status === "completed" || status === "failed" || status === "cancelled" || status === "deleted";
153
+ }
154
+ function isTerminalStatusForWorkflowTimeline(status) {
155
+ return status !== "running";
156
+ }
157
+ function appendHr(elements) {
158
+ if (elements.length > 0 && elements[elements.length - 1].tag === "hr")
159
+ return;
160
+ elements.push({ tag: "hr" });
161
+ }
162
+ function buildCollapsiblePanel(opts) {
163
+ const panel = {
164
+ tag: "collapsible_panel",
165
+ expanded: opts.expanded ?? false,
166
+ header: {
167
+ title: {
168
+ tag: "markdown",
169
+ content: sanitizeInlineText(opts.title, 120)
170
+ }
171
+ },
172
+ elements: opts.elements
173
+ };
174
+ if (opts.elementId)
175
+ panel.element_id = opts.elementId;
176
+ return panel;
177
+ }
151
178
  function renderTaskInfoLines(meta) {
152
179
  const lines = [];
153
180
  if (meta.id !== undefined)
@@ -274,20 +301,51 @@ function buildTaskCard(opts) {
274
301
  tag: "markdown",
275
302
  content: `[${label}](${link})`
276
303
  });
277
- elements.push({ tag: "hr" });
304
+ appendHr(elements);
278
305
  }
279
306
  const infoLines = renderTaskInfoLines(opts.meta);
280
307
  if (infoLines.length > 0) {
281
- elements.push({ tag: "markdown", content: infoLines.join(`
282
- `) });
283
- elements.push({ tag: "hr" });
308
+ elements.push(buildCollapsiblePanel({
309
+ title: "\uD83D\uDCCB 任务详情",
310
+ elementId: "task_detail",
311
+ elements: [{ tag: "markdown", content: infoLines.join(`
312
+ `) }]
313
+ }));
314
+ appendHr(elements);
284
315
  }
285
- elements.push({ tag: "markdown", content: `## \uD83C\uDFAF 里程碑序列` });
286
- elements.push({ tag: "markdown", content: renderMilestones(milestones) });
287
- elements.push({ tag: "hr" });
288
- elements.push({ tag: "markdown", content: `## \uD83D\uDD27 过程信息` });
289
- elements.push({ tag: "markdown", content: renderProcessSteps(processSteps) });
290
- elements.push({ tag: "hr" });
316
+ const timelineElements = [];
317
+ if (milestones.length > 0) {
318
+ timelineElements.push({
319
+ tag: "markdown",
320
+ content: `## \uD83C\uDFAF 里程碑序列`
321
+ });
322
+ timelineElements.push({
323
+ tag: "markdown",
324
+ content: renderMilestones(milestones)
325
+ });
326
+ }
327
+ if (processSteps.length > 0) {
328
+ if (timelineElements.length > 0)
329
+ timelineElements.push({ tag: "hr" });
330
+ timelineElements.push({
331
+ tag: "markdown",
332
+ content: `## \uD83D\uDD27 过程信息`
333
+ });
334
+ timelineElements.push({
335
+ tag: "markdown",
336
+ content: renderProcessSteps(processSteps)
337
+ });
338
+ }
339
+ if (timelineElements.length > 0) {
340
+ const timelineTitle = `⏱️ 执行动态(${milestones.length} 里程碑 / ${processSteps.length} 过程)`;
341
+ elements.push(buildCollapsiblePanel({
342
+ title: timelineTitle,
343
+ elementId: "task_timeline",
344
+ expanded: opts.expandTimeline ?? isTerminalStatusForTimeline(status),
345
+ elements: timelineElements
346
+ }));
347
+ }
348
+ appendHr(elements);
291
349
  const effectiveProgress = status === "completed" ? 100 : opts.progress;
292
350
  const isTerminalStatus = status !== "running";
293
351
  const terminalProgressText = status === "deleted" ? "任务已取消(删除),记录已归档" : status === "cancelled" ? "任务已取消" : status === "stopped" ? "任务已停止(可恢复)" : status === "failed" ? "任务失败" : "任务已完成";
@@ -402,21 +460,45 @@ function buildWorkflowCard(opts) {
402
460
  error: opts.error
403
461
  });
404
462
  if (infoLines.length > 0) {
405
- elements.push({ tag: "markdown", content: infoLines.join(`
406
- `) });
407
- elements.push({ tag: "hr" });
463
+ elements.push(buildCollapsiblePanel({
464
+ title: "\uD83D\uDCCB 运行详情",
465
+ elementId: "workflow_detail",
466
+ elements: [{ tag: "markdown", content: infoLines.join(`
467
+ `) }]
468
+ }));
469
+ appendHr(elements);
470
+ }
471
+ const timelineElements = [];
472
+ if (milestones.length > 0) {
473
+ timelineElements.push({ tag: "markdown", content: `## \uD83C\uDFAF 里程碑序列` });
474
+ timelineElements.push({
475
+ tag: "markdown",
476
+ content: renderMilestones(milestones)
477
+ });
478
+ }
479
+ if (processSteps.length > 0) {
480
+ if (timelineElements.length > 0)
481
+ timelineElements.push({ tag: "hr" });
482
+ timelineElements.push({ tag: "markdown", content: `## \uD83D\uDD27 过程信息` });
483
+ timelineElements.push({
484
+ tag: "markdown",
485
+ content: renderProcessSteps(processSteps)
486
+ });
487
+ }
488
+ if (timelineElements.length > 0) {
489
+ elements.push(buildCollapsiblePanel({
490
+ title: `⏱️ 执行动态(${milestones.length} 里程碑 / ${processSteps.length} 过程)`,
491
+ elementId: "workflow_timeline",
492
+ expanded: opts.expandTimeline ?? isTerminalStatusForWorkflowTimeline(status),
493
+ elements: timelineElements
494
+ }));
408
495
  }
409
- elements.push({ tag: "markdown", content: `## \uD83C\uDFAF 里程碑序列` });
410
- elements.push({ tag: "markdown", content: renderMilestones(milestones) });
411
- elements.push({ tag: "hr" });
412
- elements.push({ tag: "markdown", content: `## \uD83D\uDD27 过程信息` });
413
- elements.push({ tag: "markdown", content: renderProcessSteps(processSteps) });
414
496
  const effectiveProgress = status === "completed" ? 100 : deriveProgress({
415
497
  completedNodes: opts.completedNodes,
416
498
  totalNodes: opts.totalNodes,
417
499
  progress: opts.progress
418
500
  });
419
- elements.push({ tag: "hr" });
501
+ appendHr(elements);
420
502
  elements.push({
421
503
  tag: "markdown",
422
504
  content: effectiveProgress !== undefined ? `**\uD83D\uDCCA 进度**:${renderProgressBar(effectiveProgress)}` : `**\uD83D\uDCCA 进度**:进行中(第 ${updateCount} 次更新,尚未上报百分比)`
@@ -598,7 +680,8 @@ class LarkCardPatchNotifier {
598
680
  milestones: mergeUnique(state?.milestones ?? [], [pauseMilestone], milestoneKey),
599
681
  processSteps: state?.processSteps ?? [],
600
682
  updateCount,
601
- spinner
683
+ spinner,
684
+ expandTimeline: opts.expandTimeline
602
685
  });
603
686
  await this.runLarkCli(this.buildPatchCardArgs({
604
687
  messageId: opts.messageId,
@@ -653,7 +736,8 @@ class LarkCardPatchNotifier {
653
736
  milestones: mergeUnique(state?.milestones ?? [], [finalMilestone], milestoneKey),
654
737
  processSteps: state?.processSteps ?? [],
655
738
  updateCount,
656
- spinner
739
+ spinner,
740
+ expandTimeline: opts.expandTimeline
657
741
  });
658
742
  await this.runLarkCli(this.buildPatchCardArgs({
659
743
  messageId: opts.messageId,
@@ -1167,4 +1251,4 @@ function sanitize(text) {
1167
1251
  return `${trimmed.slice(0, 500)}…`;
1168
1252
  }
1169
1253
 
1170
- export { formatTimestampForDisplay, sanitizeInlineText, formatDateTimeForDisplay, renderProgressBar, resolveTaskTitleForDisplay, humanizeValue, renderTaskInfoLines, renderContextAsLines, renderMilestones, renderProcessSteps, buildTaskCard, buildChatAppLink, DEFAULT_APP_LINK_BASE, formatDurationMs, renderWorkflowInfoLines, buildWorkflowCard, LarkCardPatchNotifier };
1254
+ export { formatTimestampForDisplay, sanitizeInlineText, formatDateTimeForDisplay, renderProgressBar, resolveTaskTitleForDisplay, humanizeValue, isTerminalStatusForTimeline, isTerminalStatusForWorkflowTimeline, buildCollapsiblePanel, renderTaskInfoLines, renderContextAsLines, renderMilestones, renderProcessSteps, buildTaskCard, buildChatAppLink, DEFAULT_APP_LINK_BASE, formatDurationMs, renderWorkflowInfoLines, buildWorkflowCard, LarkCardPatchNotifier };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  builtInPrompts
3
- } from "./roy-agent-core-vs5gxgwh.js";
3
+ } from "./roy-agent-core-ma35jyd9.js";
4
4
  import {
5
5
  getXDGPath
6
6
  } from "./roy-agent-core-qxnbvgwe.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  builtInHandlers
3
- } from "./roy-agent-core-8nksrtnf.js";
3
+ } from "./roy-agent-core-19na22cc.js";
4
4
  import {
5
5
  envKeyToConfigKey
6
6
  } from "./roy-agent-core-7hh0brvs.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-setting/roy-agent-core",
3
- "version": "1.7.66",
3
+ "version": "1.7.68",
4
4
  "type": "module",
5
5
  "description": "Core SDK for roy-agent - Environment, Components, Tools, Sessions, Tasks",
6
6
  "main": "./dist/index.js",