@ai-setting/roy-agent-core 1.7.57 → 1.7.59

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.
@@ -57,9 +57,10 @@
57
57
  * 都不得向上抛,绝不污染原始错误传播链
58
58
  * - **有硬超时**:兜底 reply 在 5s 内一定 settle(超时 kill 子进程)。
59
59
  * 否则 lark-cli 卡住会把 `QueryExecutor` 的 rethrow 拖死 → 事件队列堵死
60
- * - **复用现有机制**:spawn 参数形态与 `LarkCliNotificationChannel` /
61
- * `event-source-handlers.sendLarkMessage` 保持一致(`--profile` 前置、
62
- * messageId → `+messages-reply`、chatId → `+messages-send`)
60
+ * - **复用现有机制**:spawn 参数形态与 `event-source-handlers.sendLarkMessage`
61
+ * / `lark-card-patch-notifier.buildSendCardArgs` / `telemetry-notice.buildLarkSendArgs`
62
+ * 保持一致(`--profile` 前置、messageId → `+messages-reply`、
63
+ * chatId → `+messages-send`、**两支都显式 `--as bot`**)
63
64
  * - **去重**:同一 triggerEvent(messageId)只回一次;chat-only 通道走
64
65
  * 5 分钟窗口,**不会**把整个群永久封锁;发送失败回滚去重标记
65
66
  * - **无 channel 不崩但也不静默**:本地 REPL / `act` 没有飞书上下文 → debug;
@@ -5,7 +5,7 @@
5
5
  * 支持通过 handleRule.plugins 加载外部 Plugin
6
6
  */
7
7
  import { ChildProcess } from "child_process";
8
- import type { EventSourceConfig, EventSourceHandler, EventSourceInstance, EventSourceStatus, EventSourceEventHandler, EventMetadata, ReplyChannel, RecommendedAction } from "./types";
8
+ import type { EventSourceConfig, EventSourceHandler, EventSourceInstance, EventSourceStatus, EventSourceEventHandler, EventMetadata, ReplyChannel, RecommendedAction, RuleNoticeInfo } from "./types";
9
9
  import { timerHandler } from "./timer-handler";
10
10
  import { bountyIMHandler } from "./bounty-im-handler";
11
11
  import { bountyCliHandler } from "./bounty-cli-handler";
@@ -469,6 +469,16 @@ export declare class LarkCliInstance implements EventSourceInstance {
469
469
  */
470
470
  private sendLarkMessage;
471
471
  }
472
+ /**
473
+ * 渲染「系统回执」小节 —— 告诉主 agent:群里**已经**看到了什么。
474
+ *
475
+ * 三种状态必须分开,否则 agent 会二选一地犯两种错:
476
+ * - 已送达却把「已超时」再讲一遍 → 重复刷屏
477
+ * - 未送达却以为发了 → 用户永远不知道任务结束了(正是 #4561 的现场)
478
+ *
479
+ * @param headline 事件标题句(未送达时要求 agent 自己讲清这个事实)
480
+ */
481
+ export declare function renderRuleNoticeSection(ruleNotice: RuleNoticeInfo | undefined, headline: string): string;
472
482
  /**
473
483
  * LarkCli Handler
474
484
  */
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * 导出所有 EventSource 相关的类型、类和函数
5
5
  */
6
- export type { EventSourceStatus, EventSourceType, EventSourceConfig, EventMetadata, RecommendedAction, ReplyChannel, EventSourceEventPayload, EventSourceEvent, EventSourceEventHandler, EventSourceStatusChangeEvent, EventSourcePersistenceConfig, EventSourceInstance, EventSourceHandler, EventSourceInitHook, EventSourceComponentInterface, EventSourceAgentHandlerConfig, } from "./types";
6
+ export type { EventSourceStatus, EventSourceType, EventSourceConfig, EventMetadata, RecommendedAction, ReplyChannel, EventSourceEventPayload, EventSourceEvent, EventSourceEventHandler, EventSourceStatusChangeEvent, EventSourcePersistenceConfig, EventSourceInstance, EventSourceHandler, EventSourceInitHook, EventSourceComponentInterface, EventSourceAgentHandlerConfig, NotifyChannelInfo, RuleNoticeInfo, } from "./types";
7
7
  export { EventSourceInitHooks } from "./types";
8
8
  export { BUILT_IN_EVENT_SOURCE_TYPES, BUILT_IN_EVENT_SOURCE_TYPE_LIST } from "./types";
9
9
  export { isBuiltInEventSourceType, isValidEventSourceType, getDefaultConfigForType, validateEventSourceConfig, } from "./types";
@@ -11,7 +11,7 @@ import {
11
11
  larkCliHandler,
12
12
  resolveBountyIMSystemPrompt,
13
13
  timerHandler
14
- } from "../../shared/@ai-setting/roy-agent-core-jw5ar4p0.js";
14
+ } from "../../shared/@ai-setting/roy-agent-core-6q5m1qk5.js";
15
15
  import {
16
16
  BUILT_IN_EVENT_SOURCE_TYPES,
17
17
  BUILT_IN_EVENT_SOURCE_TYPE_LIST,
@@ -580,6 +580,55 @@ export interface ReplyContext {
580
580
  /** lark-cli profile(用于 im +messages-reply --profile) */
581
581
  profile?: string;
582
582
  }
583
+ /**
584
+ * 触发渠道信息(渠道无关的最小集合)。
585
+ *
586
+ * 由 CLI EventHandler 从 trigger event + handler.extractReplyContext 解析后
587
+ * 传给 buildNotifyHandlePrompt,让 prompt 能按**真实渠道**渲染:
588
+ * - 渠道类型(lark-cli / bounty-im / timer / ...)
589
+ * - 群 ID / 群聊还是私聊
590
+ * - 触发消息 ID、发送者、profile
591
+ *
592
+ * ★ 为什么不让每个 handler 自己从 triggerEvent 里抠字段:
593
+ * bounty-im 用 from/to 邮箱、lark 用 chatId/messageId,字段名完全不同;
594
+ * 统一在这里归一化,handler 只负责渲染「怎么发」。
595
+ */
596
+ export interface NotifyChannelInfo {
597
+ /** EventSource 类型(lark-cli / bounty-im / timer / ...) */
598
+ esType: string;
599
+ /** 回复目标会话 ID(飞书为 oc_xxx) */
600
+ chatId?: string;
601
+ /** 群聊 / 私聊 / 未知(飞书 trigger event metadata.chatType) */
602
+ chatType?: "group" | "p2p" | "unknown";
603
+ /** 触发消息 ID(飞书为 om_xxx);reply 这条可保持话题线索 */
604
+ messageId?: string;
605
+ /** 触发者 ID(飞书为 ou_xxx) */
606
+ senderId?: string;
607
+ /** 多 appId 场景的 lark-cli profile */
608
+ profile?: string;
609
+ /** 回复方式:reply=回复触发消息 / send=新消息 / none=无可用通道 */
610
+ replyKind?: "reply" | "send" | "none";
611
+ }
612
+ /**
613
+ * 规则化回执(rule-based direct notice)的投递结果。
614
+ *
615
+ * 终态事件(completed / timeout)在进 lane 之前,会先用 telemetry 同一套
616
+ * 发送器直发一条**保底**消息到触发群;这里把「发没发出去」如实带给主 agent,
617
+ * 避免 prompt 谎称「已发送」而 agent 又重复/遗漏关键信息。
618
+ */
619
+ export interface RuleNoticeInfo {
620
+ /** 是否尝试过直发(false = 该事件类型不在规则回执白名单) */
621
+ attempted: boolean;
622
+ /** 是否确实送到群里 */
623
+ delivered: boolean;
624
+ chatId?: string;
625
+ /** 实际发出的文案(送达时带,便于 prompt 里告知「回执已经说了什么」) */
626
+ text?: string;
627
+ /** 送达时刻(epoch ms) */
628
+ sentAt?: number;
629
+ /** 失败原因(未送达时带) */
630
+ error?: string;
631
+ }
583
632
  /**
584
633
  * buildNotifyHandlePrompt 输入
585
634
  */
@@ -591,6 +640,12 @@ export interface BuildNotifyHandlePromptInput {
591
640
  description?: string;
592
641
  result?: string;
593
642
  error?: string;
643
+ /**
644
+ * 子 session ID —— 超时 / 失败后如需**续跑**,主 agent 可直接
645
+ * `delegate_task(existingSubSessionId=<此值>, ...)` 让子代理继承历史继续推进,
646
+ * 不必从零重来。
647
+ */
648
+ subSessionId?: string;
594
649
  };
595
650
  /** 触发整个 delegate_task 的原始 event(从 EventStore 反查) */
596
651
  triggerEvent: PersistedEvent;
@@ -605,6 +660,19 @@ export interface BuildNotifyHandlePromptInput {
605
660
  * 让主 agent 拿到的回复命令带上正确的 `--profile`。
606
661
  */
607
662
  profile?: string;
663
+ /**
664
+ * 触发渠道信息(渠道无关,由调用方归一化后传入)。
665
+ *
666
+ * 缺省时 handler 自行从 triggerEvent 反查(向后兼容)。
667
+ */
668
+ channel?: NotifyChannelInfo;
669
+ /**
670
+ * 规则化回执的投递结果。
671
+ *
672
+ * 缺省 = 本事件不直发回执(如 failed / stopped / ask_user),
673
+ * prompt 需要引导 agent 把结果完整讲清楚。
674
+ */
675
+ ruleNotice?: RuleNoticeInfo;
608
676
  }
609
677
  /**
610
678
  * buildNotifyHandlePrompt 输出
@@ -16,7 +16,7 @@ export type { Task, TaskOperation, TaskStatus, TaskPriority, ActionType, CreateT
16
16
  export { McpComponent, } from "./mcp";
17
17
  export type { McpComponentConfig, McpComponentOptions, McpPath, McpServerConfig, McpServerStatus, DiscoveredMcpServer, } from "./mcp";
18
18
  export { EventSourceComponent, larkCliHandler, builtInHandlers, } from "./event-source/event-source-component";
19
- export type { EventSourceConfig, EventSourceStatus, EventSourceType, EventSourceEvent, EventSourceEventPayload, EventSourceEventHandler, EventSourceStatusChangeEvent, EventSourcePersistenceConfig, EventSourceComponentInterface, EventSourceHandler, EventSourceInstance, EventSourceAgentHandlerConfig, BUILT_IN_EVENT_SOURCE_TYPES, } from "./event-source/types";
19
+ export type { EventSourceConfig, EventSourceStatus, EventSourceType, EventSourceEvent, EventSourceEventPayload, EventSourceEventHandler, EventSourceStatusChangeEvent, EventSourcePersistenceConfig, EventSourceComponentInterface, EventSourceHandler, EventSourceInstance, EventSourceAgentHandlerConfig, NotifyChannelInfo, RuleNoticeInfo, BUILT_IN_EVENT_SOURCE_TYPES, } from "./event-source/types";
20
20
  export { EventSourceInitHooks, isValidEventSourceType, isBuiltInEventSourceType, getDefaultConfigForType, validateEventSourceConfig, } from "./event-source/types";
21
21
  export { WorkflowComponent, } from "./workflow/workflow-component";
22
22
  export type { WorkflowComponentConfig, } from "./workflow/workflow-component";
package/dist/env/index.js CHANGED
@@ -3,19 +3,19 @@ import {
3
3
  } from "../shared/@ai-setting/roy-agent-core-84rs0c8d.js";
4
4
  import {
5
5
  TaskComponent
6
- } from "../shared/@ai-setting/roy-agent-core-ewcc5qdt.js";
6
+ } from "../shared/@ai-setting/roy-agent-core-991pk4ss.js";
7
7
  import"../shared/@ai-setting/roy-agent-core-amkqnyzy.js";
8
8
  import"../shared/@ai-setting/roy-agent-core-7q4wdze2.js";
9
9
  import"../shared/@ai-setting/roy-agent-core-vh0qzb24.js";
10
10
  import"../shared/@ai-setting/roy-agent-core-rn57psp0.js";
11
11
  import"../shared/@ai-setting/roy-agent-core-8gxth0eh.js";
12
- import"../shared/@ai-setting/roy-agent-core-1sfbs57v.js";
12
+ import"../shared/@ai-setting/roy-agent-core-6bradb0j.js";
13
13
  import"../shared/@ai-setting/roy-agent-core-14bq77wr.js";
14
14
  import {
15
15
  EventSourceComponent,
16
16
  builtInHandlers,
17
17
  larkCliHandler
18
- } from "../shared/@ai-setting/roy-agent-core-jw5ar4p0.js";
18
+ } from "../shared/@ai-setting/roy-agent-core-6q5m1qk5.js";
19
19
  import {
20
20
  EventSourceInitHooks,
21
21
  getDefaultConfigForType,
@@ -86,7 +86,9 @@ ${options.content}`;
86
86
  "--chat-id",
87
87
  chatId,
88
88
  "--markdown",
89
- normalizedContent
89
+ normalizedContent,
90
+ "--as",
91
+ "bot"
90
92
  ];
91
93
  }
92
94
  if (profile) {
@@ -19,10 +19,19 @@
19
19
  *
20
20
  * ## spawn lark-cli
21
21
  *
22
- * - messageId 走 `lark-cli im +messages-reply --message-id <id> --markdown <md>`
23
- * - chatId 走 `lark-cli im +messages-send --chat-id <id> --markdown <md>`
22
+ * - messageId 走 `lark-cli im +messages-reply --message-id <id> --markdown <md> --as bot`
23
+ * - chatId 走 `lark-cli im +messages-send --chat-id <id> --markdown <md> --as bot`
24
24
  * - profile 不为 undefined 时在 args 头部插入 `--profile <name>`
25
25
  *
26
+ * ⚠️ **两支都必须显式 `--as bot`**(Task #4557)。chatId 分支历史上漏了,
27
+ * 不带 flag 时 lark-cli 用默认身份 = **user(真人账号)** 发出 →
28
+ * `lark-cli event consume im.message.receive_v1` 只看「是不是 user 发的」→
29
+ * 自己的通知被同一个 consumer 回灌成「用户消息」→ 又跑一次完整 agent run。
30
+ * Task #4552 在同类路径实测回灌率 **5/11 = 45%**,补 flag 后 **0/5 = 0%**
31
+ * (证据:本机 E2E 报告 `reports/4552-a1-real-e2e.md` §6(`reports/` 被 .gitignore 忽略,未入库))。
32
+ * 本 channel 虽 `@deprecated` 但仍是 opt-in 可启用实现,所以同样必须修。
33
+ * 失败即 reject → 由 `send()` 层 swallow + warn,**绝不降级成 user**。
34
+ *
26
35
  * ## 失败处理
27
36
  *
28
37
  * - spawn 失败 / exit code != 0 → swallow(log warn,notification 失败不污染 Plugin 主流程)
@@ -49,6 +49,20 @@ export interface BackgroundTaskStartedPayload {
49
49
  parentSessionId: string;
50
50
  description: string;
51
51
  subagentType: string;
52
+ /**
53
+ * ★ Task #4552 (A1):任务启动时刻(epoch ms)。
54
+ *
55
+ * 消费端(CLI EventHandler)把 telemetry 降级成一条纯 IM 进展提示后,
56
+ * 文案要回答「跑了多久」,只能从这里读。缺失时文案降级(不渲染「已跑」)。
57
+ */
58
+ startedAt?: number;
59
+ /**
60
+ * ★ Task #4552 (A1):本任务被允许的运行时长(毫秒)。
61
+ *
62
+ * 让用户看到「预计 17:23 超时(timeout 5h)」,避免以为「早该有结论了」——
63
+ * 这正是 Task #4552 报障的认知错位(用户 16:40 以为超时,实际 17:23 才超)。
64
+ */
65
+ timeoutMs?: number;
52
66
  }
53
67
  export interface BackgroundTaskProgressPayload {
54
68
  backgroundTaskId: string;
@@ -58,6 +72,10 @@ export interface BackgroundTaskProgressPayload {
58
72
  progress?: number;
59
73
  description?: string;
60
74
  associatedTaskId?: number;
75
+ /** ★ Task #4552 (A1):见 BackgroundTaskStartedPayload.startedAt */
76
+ startedAt?: number;
77
+ /** ★ Task #4552 (A1):见 BackgroundTaskStartedPayload.timeoutMs */
78
+ timeoutMs?: number;
61
79
  }
62
80
  export interface BackgroundTaskCompletedPayload {
63
81
  backgroundTaskId: string;
@@ -191,6 +209,14 @@ export interface BackgroundTask {
191
209
  /** Timestamp of the latest lifecycle state change or progress notification. */
192
210
  lastUpdatedAt?: number;
193
211
  startedAt?: number;
212
+ /**
213
+ * ★ Task #4552 (A1):本任务被允许的运行时长(毫秒,createTask 时确定)。
214
+ *
215
+ * 存到 task 上而不是每次重算:progress 心跳发生在 20 分钟后,那时
216
+ * `timeout` 参数已不在作用域内,只能靠这里。同时保证 started / progress
217
+ * 两条通知给出同一个超时时刻。
218
+ */
219
+ timeoutMs?: number;
194
220
  completedAt?: number;
195
221
  result?: string;
196
222
  error?: string;
@@ -3,7 +3,7 @@ import {
3
3
  BackgroundTaskManager,
4
4
  createDelegateTool,
5
5
  createStopTool
6
- } from "../../../shared/@ai-setting/roy-agent-core-1sfbs57v.js";
6
+ } from "../../../shared/@ai-setting/roy-agent-core-6bradb0j.js";
7
7
  import"../../../shared/@ai-setting/roy-agent-core-14bq77wr.js";
8
8
  import"../../../shared/@ai-setting/roy-agent-core-2ctyjy62.js";
9
9
  import"../../../shared/@ai-setting/roy-agent-core-1pg0fepg.js";
@@ -5,7 +5,7 @@ import {
5
5
  TaskPriorityEnum,
6
6
  TaskStatusEnum,
7
7
  TaskTypeEnum
8
- } from "../../shared/@ai-setting/roy-agent-core-ewcc5qdt.js";
8
+ } from "../../shared/@ai-setting/roy-agent-core-991pk4ss.js";
9
9
  import"../../shared/@ai-setting/roy-agent-core-amkqnyzy.js";
10
10
  import"../../shared/@ai-setting/roy-agent-core-7q4wdze2.js";
11
11
  import"../../shared/@ai-setting/roy-agent-core-vh0qzb24.js";
@@ -13,7 +13,7 @@ import"../../shared/@ai-setting/roy-agent-core-rn57psp0.js";
13
13
  import {
14
14
  TaskEntityEventTypes
15
15
  } from "../../shared/@ai-setting/roy-agent-core-8gxth0eh.js";
16
- import"../../shared/@ai-setting/roy-agent-core-1sfbs57v.js";
16
+ import"../../shared/@ai-setting/roy-agent-core-6bradb0j.js";
17
17
  import"../../shared/@ai-setting/roy-agent-core-14bq77wr.js";
18
18
  import {
19
19
  SQLiteTaskStore,
package/dist/index.js CHANGED
@@ -63,13 +63,13 @@ import {
63
63
  } from "./shared/@ai-setting/roy-agent-core-84rs0c8d.js";
64
64
  import {
65
65
  TaskComponent
66
- } from "./shared/@ai-setting/roy-agent-core-ewcc5qdt.js";
66
+ } from "./shared/@ai-setting/roy-agent-core-991pk4ss.js";
67
67
  import"./shared/@ai-setting/roy-agent-core-amkqnyzy.js";
68
68
  import"./shared/@ai-setting/roy-agent-core-7q4wdze2.js";
69
69
  import"./shared/@ai-setting/roy-agent-core-vh0qzb24.js";
70
70
  import"./shared/@ai-setting/roy-agent-core-rn57psp0.js";
71
71
  import"./shared/@ai-setting/roy-agent-core-8gxth0eh.js";
72
- import"./shared/@ai-setting/roy-agent-core-1sfbs57v.js";
72
+ import"./shared/@ai-setting/roy-agent-core-6bradb0j.js";
73
73
  import"./shared/@ai-setting/roy-agent-core-14bq77wr.js";
74
74
  import {
75
75
  BountyIMInstance,
@@ -83,7 +83,7 @@ import {
83
83
  larkCliHandler,
84
84
  resolveBountyIMSystemPrompt,
85
85
  timerHandler
86
- } from "./shared/@ai-setting/roy-agent-core-jw5ar4p0.js";
86
+ } from "./shared/@ai-setting/roy-agent-core-6q5m1qk5.js";
87
87
  import {
88
88
  BUILT_IN_EVENT_SOURCE_TYPES,
89
89
  BUILT_IN_EVENT_SOURCE_TYPE_LIST,
@@ -1207,6 +1207,7 @@ class BackgroundTaskManager {
1207
1207
  status: "pending",
1208
1208
  createdAt,
1209
1209
  lastUpdatedAt: createdAt,
1210
+ timeoutMs: timeout || DEFAULT_TIMEOUT,
1210
1211
  abortController,
1211
1212
  similarTaskIds,
1212
1213
  triggerEventId,
@@ -1219,7 +1220,9 @@ class BackgroundTaskManager {
1219
1220
  subSessionId: subSession.id,
1220
1221
  parentSessionId,
1221
1222
  description,
1222
- subagentType
1223
+ subagentType,
1224
+ startedAt: createdAt,
1225
+ timeoutMs: timeout || DEFAULT_TIMEOUT
1223
1226
  };
1224
1227
  this.publishBackgroundEvent(BackgroundTaskEventTypes.STARTED, startedPayload, parentSessionId, triggerEventId, replyChannelSnapshot);
1225
1228
  this.executeTask(taskIdGen, prompt, timeout, parentSessionId).catch((err) => {
@@ -1403,7 +1406,9 @@ class BackgroundTaskManager {
1403
1406
  parentSessionId,
1404
1407
  description: task.description,
1405
1408
  status: task.status,
1406
- associatedTaskId: task.associatedTaskId
1409
+ associatedTaskId: task.associatedTaskId,
1410
+ startedAt: task.createdAt,
1411
+ timeoutMs: task.timeoutMs
1407
1412
  };
1408
1413
  this.publishBackgroundEvent(BackgroundTaskEventTypes.PROGRESS, progressPayload, parentSessionId, task.triggerEventId, task.replyChannel);
1409
1414
  }, PROGRESS_INTERVAL);
@@ -1402,7 +1402,9 @@ function spawnLarkReply(options) {
1402
1402
  "--chat-id",
1403
1403
  chatId,
1404
1404
  "--markdown",
1405
- normalizedContent
1405
+ normalizedContent,
1406
+ "--as",
1407
+ "bot"
1406
1408
  ];
1407
1409
  } else {
1408
1410
  finish(new Error("No target specified for failure reply"));
@@ -2501,7 +2503,6 @@ class LarkCliInstance {
2501
2503
  chatId: replyChannel.chatId,
2502
2504
  userId: replyChannel.params?.userId,
2503
2505
  content: text,
2504
- as: "bot",
2505
2506
  profile
2506
2507
  });
2507
2508
  },
@@ -2510,7 +2511,6 @@ class LarkCliInstance {
2510
2511
  chatId: replyChannel.chatId,
2511
2512
  userId: replyChannel.params?.userId,
2512
2513
  content: markdown,
2513
- as: "bot",
2514
2514
  format: "markdown",
2515
2515
  profile
2516
2516
  });
@@ -2520,7 +2520,6 @@ class LarkCliInstance {
2520
2520
  chatId: replyChannel.chatId,
2521
2521
  userId: replyChannel.params?.userId,
2522
2522
  content: JSON.stringify(content),
2523
- as: "bot",
2524
2523
  profile
2525
2524
  });
2526
2525
  },
@@ -2529,7 +2528,6 @@ class LarkCliInstance {
2529
2528
  await this.sendLarkMessage({
2530
2529
  messageId: replyChannel.messageId,
2531
2530
  content: JSON.stringify(content),
2532
- as: "bot",
2533
2531
  profile
2534
2532
  });
2535
2533
  }
@@ -2538,16 +2536,16 @@ class LarkCliInstance {
2538
2536
  }
2539
2537
  async sendLarkMessage(options) {
2540
2538
  return new Promise((resolve, reject) => {
2541
- const { chatId, userId, messageId, content, as: identity, format = "text", profile } = options;
2539
+ const { chatId, userId, messageId, content, format = "text", profile } = options;
2542
2540
  const normalizedContent = normalizeLarkAtMentions(content);
2543
2541
  const contentFlag = format === "markdown" ? "--markdown" : "--text";
2544
2542
  const args = ["im"];
2545
2543
  if (messageId) {
2546
- args.push("+messages-reply", "--message-id", messageId, "--as", identity, contentFlag, normalizedContent);
2544
+ args.push("+messages-reply", "--message-id", messageId, "--as", "bot", contentFlag, normalizedContent);
2547
2545
  } else if (chatId) {
2548
- args.push("+messages-send", "--chat-id", chatId, "--as", identity, contentFlag, normalizedContent);
2546
+ args.push("+messages-send", "--chat-id", chatId, "--as", "bot", contentFlag, normalizedContent);
2549
2547
  } else if (userId) {
2550
- args.push("+messages-send", "--user-id", userId, "--as", identity, contentFlag, normalizedContent);
2548
+ args.push("+messages-send", "--user-id", userId, "--as", "bot", contentFlag, normalizedContent);
2551
2549
  } else {
2552
2550
  reject(new Error("No target specified for sending message"));
2553
2551
  return;
@@ -2623,6 +2621,24 @@ function describeNotifyEvent(type, taskId, desc) {
2623
2621
  return { headline: `Sub-agent 更新了 Task #${taskId}「${desc}」。`, actionable };
2624
2622
  }
2625
2623
  }
2624
+ function renderRuleNoticeSection(ruleNotice, headline) {
2625
+ if (!ruleNotice || !ruleNotice.attempted) {
2626
+ return `(本事件不直发规则回执)
2627
+ ` + `→ 你的回复要把结果讲**完整**(用户只能从你这条消息知道结论)。`;
2628
+ }
2629
+ if (ruleNotice.delivered) {
2630
+ const when = ruleNotice.sentAt ? new Date(ruleNotice.sentAt).toLocaleTimeString("zh-CN", {
2631
+ hour12: false,
2632
+ hour: "2-digit",
2633
+ minute: "2-digit"
2634
+ }) : "";
2635
+ const sent = ruleNotice.text ? `:「${ruleNotice.text.replace(/\s*\n+\s*/g, " ")}」` : "";
2636
+ return `✅ 系统已于 ${when || "刚才"} 把下面这条发到群里${sent}
2637
+ ` + `→ 你的回复**只发增量**:不要再重复「任务已结束」这类回执原文,` + `直接讲结论/交付 + 下一步选项。`;
2638
+ }
2639
+ return `❌ **回执未送达**(原因:${ruleNotice.error ?? "unknown"})
2640
+ ` + `→ 用户目前**还不知道**「${headline}」这件事,必须由你在回复里讲清楚,` + `并且不要假装系统已经通知过。`;
2641
+ }
2626
2642
  var larkCliHandler = {
2627
2643
  type: "lark-cli",
2628
2644
  validateConfig(config) {
@@ -2685,47 +2701,67 @@ var larkCliHandler = {
2685
2701
  senderOpenId: input.triggerEvent.metadata?.senderId ?? input.triggerEvent.senderId,
2686
2702
  profile: input.profile
2687
2703
  };
2688
- const profileFlag = ctx.profile ? `--profile ${ctx.profile} ` : "";
2704
+ const esType = input.channel?.esType ?? ctx.esType ?? "lark-cli";
2705
+ const chatId = input.channel?.chatId || ctx.chatId;
2706
+ const messageId = input.channel?.messageId || ctx.messageId;
2707
+ const senderId = input.channel?.senderId || ctx.senderOpenId;
2708
+ const chatType = input.channel?.chatType ?? "unknown";
2709
+ const profile = input.channel?.profile ?? ctx.profile;
2710
+ const profileFlag = profile ? `--profile ${profile} ` : "";
2689
2711
  const desc = input.notifyEvent.description ?? "(未命名任务)";
2690
2712
  const result = input.subAgentResult.summary ?? input.notifyEvent.result ?? "";
2691
2713
  const notifyType = input.notifyEvent.type;
2692
2714
  const taskId = input.notifyEvent.taskId ?? "?";
2693
2715
  const { headline, actionable } = describeNotifyEvent(notifyType, taskId, desc);
2716
+ const chatTypeLabel = chatType === "group" ? "群聊" : chatType === "p2p" ? "私聊" : "会话类型未知";
2717
+ const canReply = Boolean(chatId);
2718
+ const receiptSection = renderRuleNoticeSection(input.ruleNotice, headline);
2719
+ const replyCommand = messageId ? `lark-cli ${profileFlag}im +messages-reply --message-id ${messageId} --chat-id ${chatId} --as bot --markdown '<你的回复>'` : `lark-cli ${profileFlag}im +messages-send --chat-id ${chatId} --as bot --markdown '<你的回复>'`;
2720
+ const scopeWord = chatType === "group" ? "群聊" : chatType === "p2p" ? "私聊" : "会话";
2694
2721
  const promptText = actionable ? `
2695
2722
  ## Notify Event: ${notifyType}
2696
2723
 
2697
2724
  ${headline}
2698
2725
 
2699
- ### Trigger event(原始飞书消息,来自 EventStore 反查)
2700
- - ES: lark-cli
2701
- - **chatId: ${ctx.chatId}** ← 关键!必须回复到这里
2702
- - **messageId: ${ctx.messageId}** ← 必须 reply 这条
2703
- - sender: ${ctx.senderOpenId}
2704
- ${ctx.profile ? `- profile: ${ctx.profile} ← 调用 lark-cli 必须带 --profile
2726
+ ### 触发渠道(EventSource)
2727
+ - ES: ${esType}(飞书,${chatTypeLabel})
2728
+ - **chatId: ${chatId || "(缺失)"}** ← 关键!必须发到这里
2729
+ - **messageId: ${messageId || "(缺失)"}** ← ${messageId ? "reply 这条,保持话题线索" : "无触发消息,用 +messages-send"}
2730
+ - sender: ${senderId || "(未知)"}
2731
+ ${input.notifyEvent.subSessionId ? `- 子 session: ${input.notifyEvent.subSessionId} ← 如需续跑:delegate_task(existingSubSessionId=<此值>, prompt=<新要求>)
2732
+ ` : ""}${profile ? `- profile: ${profile} ← 调用 lark-cli 必须带 --profile
2705
2733
  ` : ""}
2734
+ ### 系统回执(rule-based 直发)
2735
+ ${receiptSection}
2736
+
2706
2737
  ### Sub-agent 输出
2707
2738
  ${result}
2708
2739
 
2709
- ### \uD83D\uDC49 你应该这样做
2710
- 用 lark-cli 调 im +messages-reply:
2711
- lark-cli ${profileFlag}im +messages-reply --message-id ${ctx.messageId} --chat-id ${ctx.chatId} --as bot --msg-type text --content '<你的回复>'
2740
+ ### \uD83D\uDC49 你要做什么
2741
+ 1. 用 2~5 行总结:这次完成了什么 / 卡在哪(依据上面的输出与子 session)
2742
+ 2. 给出 1~3 个下一步选项(继续推进 / 拆小 / 放弃),并**问用户选哪个**
2743
+ 3. 用 lark-cli 把回复发到上面的${scopeWord}(见下)
2744
+
2745
+ ### 发送方式(渠道 = lark-cli)
2746
+ ${canReply ? ` ${replyCommand}` : `⚠️ 没有可用的 chatId —— 无法投递到触发会话。请在会话内汇报,并说明「未投递」的原因。`}
2712
2747
 
2713
2748
  ⚠️ 不要 fallback 到 lastReplyChannel 或 metadata.replyChannel — 那个 chatId
2714
2749
  可能跟用户原始消息的 chatId 不一致(参见 #4035/#4038/#4039 bug)。
2750
+ ⚠️ 必须 --as bot(否则会被自己的 lark-cli consumer 当用户消息回灌);不要用 user 身份。
2715
2751
  ` : `
2716
2752
  ## Notify Event: ${notifyType}
2717
2753
 
2718
2754
  ${headline}
2719
2755
 
2720
- ### Trigger event
2721
- - ES: lark-cli
2722
- - chatId: ${ctx.chatId}
2723
- - messageId: ${ctx.messageId}
2756
+ ### 触发渠道(EventSource)
2757
+ - ES: ${esType}(${chatTypeLabel})
2758
+ - chatId: ${chatId || "(缺失)"}
2759
+ - messageId: ${messageId || "(缺失)"}
2724
2760
 
2725
2761
  (这是中间态通知:仅作状态同步,**不要**回复用户,也不要调用 lark-cli 发消息。
2726
2762
  等收到 task.background.completed / failed / timeout / stopped 终态通知后再回复。)
2727
2763
  `;
2728
- const systemHint = actionable ? `回复必须用 lark-cli im +messages-reply(${profileFlag.trim() ? `带 ${profileFlag.trim()};` : ""}` + `chatId=${ctx.chatId} / messageId=${ctx.messageId} 来自 trigger event,不要 fallback。` : `这是 ${notifyType} 中间态通知,不要回复用户;仅等待终态通知。`;
2764
+ const systemHint = actionable ? `回复必须用 lark-cli im +messages-reply(${profileFlag.trim() ? `带 ${profileFlag.trim()};` : ""}` + `chatId=${chatId} / messageId=${messageId} 来自 trigger event(esType=${esType}),不要 fallback。` + (input.ruleNotice?.delivered ? "系统回执已送达,只发增量。" : "") : `这是 ${notifyType} 中间态通知,不要回复用户;仅等待终态通知。`;
2729
2765
  return { promptText, systemHint };
2730
2766
  },
2731
2767
  setRunner(runner) {
@@ -27,7 +27,7 @@ import {
27
27
  BackgroundTaskManager,
28
28
  createDelegateTool,
29
29
  createStopTool
30
- } from "./roy-agent-core-1sfbs57v.js";
30
+ } from "./roy-agent-core-6bradb0j.js";
31
31
  import {
32
32
  SQLiteTaskStore,
33
33
  getDefaultTaskDbPath
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-setting/roy-agent-core",
3
- "version": "1.7.57",
3
+ "version": "1.7.59",
4
4
  "type": "module",
5
5
  "description": "Core SDK for roy-agent - Environment, Components, Tools, Sessions, Tasks",
6
6
  "main": "./dist/index.js",