@ai-setting/roy-agent-core 1.7.77 → 1.7.85

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 (66) hide show
  1. package/dist/config/config-protection.d.ts +89 -0
  2. package/dist/config/index.d.ts +1 -0
  3. package/dist/config/index.js +16 -0
  4. package/dist/env/agent/agent-component.d.ts +42 -21
  5. package/dist/env/agent/agent-component.js +2 -2
  6. package/dist/env/agent/index.js +2 -2
  7. package/dist/env/agent/types.d.ts +32 -12
  8. package/dist/env/error-reply.d.ts +6 -0
  9. package/dist/env/error-reply.js +1 -1
  10. package/dist/env/event-source/bounty-im-handler.d.ts +8 -0
  11. package/dist/env/event-source/es-command.d.ts +39 -0
  12. package/dist/env/event-source/es-plugins.d.ts +68 -0
  13. package/dist/env/event-source/event-source-component.d.ts +15 -0
  14. package/dist/env/event-source/event-source-handlers.d.ts +68 -0
  15. package/dist/env/event-source/event-source-handlers.js +20 -8
  16. package/dist/env/event-source/index.d.ts +5 -1
  17. package/dist/env/event-source/index.js +57 -8
  18. package/dist/env/event-source/types.d.ts +101 -1
  19. package/dist/env/event-source/wecom-card-action-handler.d.ts +141 -0
  20. package/dist/env/event-source/wecom-card-builder.d.ts +220 -0
  21. package/dist/env/event-source/wecom-cli-handler.d.ts +266 -0
  22. package/dist/env/event-source/wecom-cli-sender.d.ts +88 -0
  23. package/dist/env/event-source/wecom-media.d.ts +130 -0
  24. package/dist/env/event-source/wecom-respond-registry.d.ts +151 -0
  25. package/dist/env/event-source/wecom-ws-bridge.d.ts +213 -0
  26. package/dist/env/index.js +16 -14
  27. package/dist/env/task/delegate/delegate-tool.d.ts +62 -2
  28. package/dist/env/task/delegate/delegate-tool.js +3 -1
  29. package/dist/env/task/delegate/index.js +1 -1
  30. package/dist/env/task/index.js +2 -2
  31. package/dist/env/task/plugins/index.d.ts +2 -0
  32. package/dist/env/task/plugins/index.js +23 -2
  33. package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +41 -2
  34. package/dist/env/task/plugins/lark-card-patch-notifier.js +1 -1
  35. package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +16 -0
  36. package/dist/env/task/plugins/wecom-card-state-store.d.ts +121 -0
  37. package/dist/env/task/plugins/wecom-task-notify-plugin.d.ts +224 -0
  38. package/dist/env/tool/built-in/index.d.ts +2 -0
  39. package/dist/env/tool/built-in/index.js +6 -1
  40. package/dist/env/tool/index.js +3 -2
  41. package/dist/env/workflow/index.js +3 -3
  42. package/dist/env/workflow/plugins/index.js +2 -2
  43. package/dist/env/workflow/tools/index.js +2 -2
  44. package/dist/index.d.ts +1 -1
  45. package/dist/index.js +68 -17
  46. package/dist/shared/@ai-setting/{roy-agent-core-v87njmq7.js → roy-agent-core-0dh8npnz.js} +1 -1
  47. package/dist/shared/@ai-setting/roy-agent-core-2stt4rp5.js +134 -0
  48. package/dist/shared/@ai-setting/{roy-agent-core-mww0mnd9.js → roy-agent-core-7bpj859q.js} +1 -1
  49. package/dist/shared/@ai-setting/{roy-agent-core-w0qjeq2v.js → roy-agent-core-7dd0s3xb.js} +14 -4
  50. package/dist/shared/@ai-setting/{roy-agent-core-rsn44n9y.js → roy-agent-core-b31v3qpv.js} +851 -46
  51. package/dist/shared/@ai-setting/{roy-agent-core-0v27y99n.js → roy-agent-core-begm7ytz.js} +63 -2
  52. package/dist/shared/@ai-setting/{roy-agent-core-aa2j7bk9.js → roy-agent-core-cha0jc7n.js} +40 -26
  53. package/dist/shared/@ai-setting/{roy-agent-core-hd3n57zs.js → roy-agent-core-d1n4jbza.js} +1 -1
  54. package/dist/shared/@ai-setting/{roy-agent-core-9vhq7fvv.js → roy-agent-core-dkrvmjsh.js} +12 -74
  55. package/dist/shared/@ai-setting/{roy-agent-core-99nwgj84.js → roy-agent-core-fzw9xzm1.js} +1 -1
  56. package/dist/shared/@ai-setting/roy-agent-core-m3vjepzz.js +544 -0
  57. package/dist/shared/@ai-setting/{roy-agent-core-ew6qb5yx.js → roy-agent-core-p6ks90tb.js} +1 -1
  58. package/dist/shared/@ai-setting/{roy-agent-core-rhtdspx6.js → roy-agent-core-qk9hd89p.js} +1 -1
  59. package/dist/shared/@ai-setting/{roy-agent-core-6cy43nss.js → roy-agent-core-rykf7c04.js} +49 -9
  60. package/dist/shared/@ai-setting/{roy-agent-core-8ez6mccb.js → roy-agent-core-tk98f92b.js} +4 -0
  61. package/dist/shared/@ai-setting/{roy-agent-core-bxkjk7zx.js → roy-agent-core-vx8zc847.js} +4 -1
  62. package/dist/shared/@ai-setting/roy-agent-core-vyr6svt6.js +31 -0
  63. package/dist/shared/@ai-setting/{roy-agent-core-3gpygqcx.js → roy-agent-core-xf6z96qy.js} +2086 -163
  64. package/package.json +10 -1
  65. /package/dist/shared/@ai-setting/{roy-agent-core-b4x6x4k2.js → roy-agent-core-m1fgtnrc.js} +0 -0
  66. /package/dist/shared/@ai-setting/{roy-agent-core-v85n42ve.js → roy-agent-core-ze5cqpf2.js} +0 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Config protection utilities — ~/.roy-agent 配置事故系统性防护(Task #5194)
3
+ *
4
+ * 背景:2026-11-07/11-08 连续三起 ~/.roy-agent 配置被覆盖/重置事故
5
+ * (测试覆盖 ×2、daily-summary 副本手动覆盖 ×1)。路径级守卫
6
+ * (Task #5181 测试隔离)已封死测试写路径,但内容级覆盖
7
+ * (合法写入者写坏内容)无法靠路径守卫防住。
8
+ *
9
+ * 三道护栏:
10
+ * 1. snapshotConfigDir — 写前快照,保留最近 N=10 份,可回滚
11
+ * 2. appendAuditLog — 写入审计(谁/何时/什么),排查不再考古
12
+ * 3. checkSourceCountDrop — 源数骤降 >30% 检测(fail-safe 拒绝覆盖)
13
+ */
14
+ /** 需要快照保护的关键配置文件 */
15
+ export declare const PROTECTED_CONFIG_FILES: readonly ["config.jsonc", "auth.json", "event-sources.json", "wecom-credentials.json"];
16
+ /** 快照保留数量 */
17
+ export declare const SNAPSHOT_KEEP = 10;
18
+ /** 文件名安全的时间戳(如 2026-11-08T21-37-13-512Z) */
19
+ export declare function snapshotTimestamp(d?: Date): string;
20
+ /**
21
+ * 对 configDir 下的关键配置文件做一次快照。
22
+ *
23
+ * - 只快照存在的文件(缺失跳过,不报错)
24
+ * - configDir 不存在时返回 null(不抛错)
25
+ * - 快照后清理最旧快照,保留最近 SNAPSHOT_KEEP 份
26
+ *
27
+ * @returns 快照目录路径;configDir 不存在时返回 null
28
+ */
29
+ export declare function snapshotConfigDir(configDir: string, now?: Date): string | null;
30
+ /** 列出现有快照目录(按时间戳名排序,旧→新) */
31
+ export declare function listSnapshots(configDir: string): string[];
32
+ /** 单条审计记录 */
33
+ export interface AuditEntry {
34
+ /** ISO 时间戳 */
35
+ ts: string;
36
+ /** 写入进程 pid */
37
+ pid: number;
38
+ /** 被写文件名 */
39
+ file: string;
40
+ /** 动作(如 save / pre-save-snapshot / drop-blocked) */
41
+ action: string;
42
+ /** 补充信息 */
43
+ detail?: string;
44
+ }
45
+ /**
46
+ * 追加一条审计记录到 <configDir>/audit.log(每行一个 JSON)。
47
+ * 失败静默(审计绝不能反过来弄坏配置写入主流程)。
48
+ */
49
+ export declare function appendAuditLog(configDir: string, entry: Omit<AuditEntry, "ts" | "pid">): void;
50
+ /** 读取审计日志(解析失败的行跳过) */
51
+ export declare function readAuditLog(configDir: string): AuditEntry[];
52
+ /** 源数骤降检查结果 */
53
+ export interface SourceDropCheck {
54
+ allowed: boolean;
55
+ reason?: string;
56
+ }
57
+ /**
58
+ * 源数骤降检测(Task #5188 P1 建议)。
59
+ *
60
+ * 规则:previousCount 合法(>0)且 newCount 下降超过 30% ⇒ 拒绝。
61
+ * previousCount <= 0(无盘上配置/读取失败)时放行。
62
+ *
63
+ * @param previousCount 磁盘上现有源数
64
+ * @param newCount 本次要写入的源数
65
+ */
66
+ export declare function checkSourceCountDrop(previousCount: number, newCount: number): SourceDropCheck;
67
+ /**
68
+ * 读取磁盘上 event-sources.json 的源数;文件缺失/损坏返回 -1。
69
+ */
70
+ export declare function readSourceCountFromDisk(configPath: string): number;
71
+ /** utimes helper for tests (exported so tests need no require hack) */
72
+ export declare function setTimestamps(p: string, d: Date): void;
73
+ /**
74
+ * 通用护栏写入包装(Task #5194 opencodego review MAJOR-1)。
75
+ *
76
+ * 背景:护栏原先只接 event-sources.json 一个写入口,
77
+ * config.jsonc / auth.json 的写入路径(fallback-model-command、
78
+ * cli app-config-io)零接线 ⇒ 事故 1/2 的主要目标文件裸奔。
79
+ *
80
+ * 语义:写前快照(可回滚)→ 执行 writeFn → 写后审计。
81
+ * - writeFn 抛错 ⇒ 返回 false 并记 write-failed 审计(错误原样上抛由调用方决定)
82
+ * - 快照/审计失败不阻断写入主流程(护栏绝不能弄坏配置写入)
83
+ *
84
+ * @param configDir 配置目录(如 ~/.roy-agent)
85
+ * @param file 被写文件名(如 config.jsonc)
86
+ * @param writeFn 实际写入动作(同步)
87
+ * @returns true=写入成功;false=写入抛错
88
+ */
89
+ export declare function guardConfigWrite(configDir: string, file: string, writeFn: () => void): boolean;
@@ -2,3 +2,4 @@ export { type ConfigSource, type ConfigSourceType, type ConfigChangeEvent, type
2
2
  export { FileSource, type FileSourceOptions } from "./file-source";
3
3
  export { EnvSource, type EnvSourceOptions } from "./env-source";
4
4
  export { parseJSONC, parseJSONCWithEnv, parseJSONCWithProtocols, substituteEnvVars, substituteProtocolRefs, } from "./config-parser";
5
+ export { guardConfigWrite, snapshotConfigDir, appendAuditLog, readAuditLog, checkSourceCountDrop, readSourceCountFromDisk, PROTECTED_CONFIG_FILES, type AuditEntry, type SourceDropCheck, } from "./config-protection";
@@ -13,6 +13,15 @@ import {
13
13
  substituteEnvVars,
14
14
  substituteProtocolRefs
15
15
  } from "../shared/@ai-setting/roy-agent-core-adg9ghbt.js";
16
+ import {
17
+ PROTECTED_CONFIG_FILES,
18
+ appendAuditLog,
19
+ checkSourceCountDrop,
20
+ guardConfigWrite,
21
+ readAuditLog,
22
+ readSourceCountFromDisk,
23
+ snapshotConfigDir
24
+ } from "../shared/@ai-setting/roy-agent-core-2stt4rp5.js";
16
25
  import"../shared/@ai-setting/roy-agent-core-qxnbvgwe.js";
17
26
  import"../shared/@ai-setting/roy-agent-core-7hh0brvs.js";
18
27
  import"../shared/@ai-setting/roy-agent-core-pgvcf91v.js";
@@ -27,9 +36,16 @@ import"../shared/@ai-setting/roy-agent-core-fs0mn2jk.js";
27
36
  export {
28
37
  substituteProtocolRefs,
29
38
  substituteEnvVars,
39
+ snapshotConfigDir,
40
+ readSourceCountFromDisk,
41
+ readAuditLog,
30
42
  parseJSONCWithProtocols,
31
43
  parseJSONCWithEnv,
32
44
  parseJSONC,
45
+ guardConfigWrite,
46
+ checkSourceCountDrop,
47
+ appendAuditLog,
48
+ PROTECTED_CONFIG_FILES,
33
49
  MemorySource,
34
50
  FileSource,
35
51
  EnvSource,
@@ -240,6 +240,34 @@ export declare class AgentComponent extends BaseComponent {
240
240
  private abortControllers;
241
241
  private defaultTools;
242
242
  private doomLoopCaches;
243
+ /**
244
+ * ★ Task #5224 — session-scope cache snapshot(**内存级**,key = sessionId)。
245
+ *
246
+ * ## 为什么改成内存级
247
+ *
248
+ * 旧实现把快照持久化到 `session.metadata.cacheSnapshot`,带来三个问题:
249
+ *
250
+ * 1. **体积爆炸**:快照 = 整份 systemPrompt(含展开后的 memory),
251
+ * 实测单 session metadata ≈ 102 KB —— `packages/cli/src/commands/shared/chat-session.ts:48`
252
+ * 自述这是本地 `sessions.db` 膨胀到 3.3 GB 的直接原因。
253
+ * 2. **跨进程时间差**:进程重启后复用的旧快照创建于渠道 prompt 引入之前,
254
+ * 且失效检测只看 memory.md mtime → 旧快照被判「新鲜」永久复用
255
+ * → 渠道 prompt 永远进不了 LLM system message(Task #5210/#5217 根因)。
256
+ * 3. **需要一堆补丁兜底**:fix③ 追加 + includes 判重(Task #5223)
257
+ * 都是为了在「持久化 + 复用」模型下把渠道 prompt 塞回去。
258
+ *
259
+ * ## 新语义(用户确认)
260
+ *
261
+ * - 快照是**进程内**的一次性构建产物:session 生命周期内构建一次,之后一直用。
262
+ * - **不持久化** sessions.db ⇒ 进程重启后全部重建(自然拿到最新 memory)。
263
+ * - **渠道 prompt 在构建时注入**(唯一注入点)⇒ 无需任何复用轮补丁。
264
+ * - **不做失效检测**(`_isMemorySnapshotStale` 已删除)⇒ session 内 memory
265
+ * 变更不生效(冻结语义),新 session 自然用最新 memory。
266
+ *
267
+ * 生命周期 = AgentComponent 实例 = 生产环境进程生命周期
268
+ * (`environment.service.ts` 只 `new AgentComponent()` 一次)。
269
+ */
270
+ private _cacheSnapshots;
243
271
  /**
244
272
  * Task #4623 — per-run fallback state.
245
273
  *
@@ -491,36 +519,28 @@ export declare class AgentComponent extends BaseComponent {
491
519
  * 这里只需要构建当前请求的消息,不重复添加
492
520
  */
493
521
  /**
494
- * 【Task #2744】session-scope cache snapshot 加载/创建/持久化
522
+ * 【Task #2744 / #5224】session-scope cache snapshot —— **内存级**构建/复用
495
523
  *
496
- * 设计目标:把 systemPrompt({{memory}} 已替换)和 additionInfo 绑定到 session 生命周期,
497
- * first-run 计算一次并持久化到 session.metadata.cacheSnapshot,后续 _run 直接复用
524
+ * 设计目标:把 systemPrompt({{memory}} 已替换 + **构建时注入的渠道 prompt**)
525
+ * 和 additionInfo 绑定到 session 生命周期,构建一次后一直用
498
526
  * → 字节级一致 → LLM prompt cache 命中。
499
527
  *
528
+ * ★ Task #5224 变更(用户确认的最终设计):
529
+ * - 存储:**内存 Map**(`_cacheSnapshots`,key = sessionId),**不持久化** sessions.db
530
+ * ⇒ 不再把整份 systemPrompt(~102KB)写进 session 行(本地库膨胀主因)
531
+ * - 复用:同 sessionId 直接命中 Map;**不做失效检测**
532
+ * (`_isMemorySnapshotStale` 已删除)⇒ session 内 memory 变更不生效(冻结)
533
+ * - 注入:渠道 prompt 在**构建时**并入快照(唯一注入点)
534
+ * ⇒ 不再需要 fix③ 复用轮追加 + includes 判重补丁
535
+ *
500
536
  * @returns
501
537
  * - snapshot: 冻结的 { systemPrompt, additionInfo, snapshotAt } 快照
502
538
  * - systemPromptOverride: 如果 snapshot 命中,覆盖调用方的 resolvedSystemPrompt
503
539
  * (值是 snapshot.systemPrompt,命中时与 snapshot.systemPrompt 相同)
504
540
  *
505
- * 错误处理:所有 I/O 失败均降级为"返回 null"(调用方走 fallback 路径),
506
- * 不阻塞 run。
541
+ * 错误处理:所有异常均降级为"返回 null"(调用方走 fallback 路径),不阻塞 run。
507
542
  */
508
543
  private _loadOrCreateCacheSnapshot;
509
- /**
510
- * 【Task #4813】B1 mtime 失效检测:快照是否因 memory 文件变化而过期
511
- *
512
- * 返回 null = 快照仍有效(可复用);返回字符串 = 过期原因(用于日志)。
513
- *
514
- * 兼容迁移:旧快照(Task #4813 之前持久化)无 memoryStat 字段 → 视为过期。
515
- */
516
- private _isMemorySnapshotStale;
517
- /**
518
- * 【Task #4813】采集各 memory 文件的 mtime(按 MemoryComponent 配置的
519
- * memoryPaths × memoryFile),供快照失效检测使用。
520
- *
521
- * 文件不存在 → 记为 0(删除后重拍语义);memory component 缺失 → 全 0。
522
- */
523
- private _collectMemoryStat;
524
544
  /**
525
545
  * 【Task #2744】无 sessionId 时的 {{memory}} fallback 替换
526
546
  *
@@ -535,7 +555,8 @@ export declare class AgentComponent extends BaseComponent {
535
555
  * currentTaskId 是否成功注入到 LLM 上下文
536
556
  *
537
557
  * Task #2744 (session-scope cache optimization):
538
- * 如果 ctx._cacheSnapshot 存在(由 AgentComponent._run 在 first-run 时持久化到
558
+ * 如果 ctx._cacheSnapshot 存在(由 AgentComponent._run 在 first-run 时构建到
559
+ * 内存快照 `_cacheSnapshots`,★ Task #5224 起不再持久化到
539
560
  * session.metadata.cacheSnapshot),则返回 snapshot 的 additionInfo,**跨多轮
540
561
  * 保持字节级一致** —— 这是 LLM prompt cache 命中的前提。
541
562
  *
@@ -7,8 +7,8 @@ import {
7
7
  filterToolsByAgent,
8
8
  parseToolList,
9
9
  toLLMMessage
10
- } from "../../shared/@ai-setting/roy-agent-core-9vhq7fvv.js";
11
- import"../../shared/@ai-setting/roy-agent-core-v85n42ve.js";
10
+ } from "../../shared/@ai-setting/roy-agent-core-dkrvmjsh.js";
11
+ import"../../shared/@ai-setting/roy-agent-core-ze5cqpf2.js";
12
12
  import"../../shared/@ai-setting/roy-agent-core-jvv2h3d6.js";
13
13
  import"../../shared/@ai-setting/roy-agent-core-ed233k5z.js";
14
14
  import"../../shared/@ai-setting/roy-agent-core-qxnbvgwe.js";
@@ -4,8 +4,8 @@ import {
4
4
  import {
5
5
  AgentComponent,
6
6
  AgentComponentConfigSchema
7
- } from "../../shared/@ai-setting/roy-agent-core-9vhq7fvv.js";
8
- import"../../shared/@ai-setting/roy-agent-core-v85n42ve.js";
7
+ } from "../../shared/@ai-setting/roy-agent-core-dkrvmjsh.js";
8
+ import"../../shared/@ai-setting/roy-agent-core-ze5cqpf2.js";
9
9
  import"../../shared/@ai-setting/roy-agent-core-jvv2h3d6.js";
10
10
  import"../../shared/@ai-setting/roy-agent-core-ed233k5z.js";
11
11
  import"../../shared/@ai-setting/roy-agent-core-qxnbvgwe.js";
@@ -227,24 +227,28 @@ export interface HookContext {
227
227
  /**
228
228
  * Task #2744: session-scope cache snapshot (internal use)
229
229
  *
230
- * 持久化在 session.metadata.cacheSnapshot,由 AgentComponent._run 在 first-run
231
- * 创建并透传给 buildMessages。buildMessages 使用 snapshot 的 additionInfo(而非
232
- * 每轮重读 ctx.context.additionInfo)以保证 system prompt 字节级一致 → LLM
233
- * prompt cache 命中。
230
+ * ★ Task #5224 — 快照改为**内存级**(`AgentComponent._cacheSnapshots`,
231
+ * key=sessionId),**不再持久化**到 session.metadata.cacheSnapshot。
232
+ * 由 AgentComponent._run 在 first-run 创建并透传给 buildMessages。
233
+ * buildMessages 使用 snapshot 的 additionInfo(而非每轮重读
234
+ * ctx.context.additionInfo)以保证 system prompt 字节级一致 → LLM
235
+ * prompt cache 命中。
234
236
  *
235
- * - systemPrompt: 含 {{memory}} 替换后的最终字符串
237
+ * - systemPrompt: 含 {{memory}} 替换后的最终字符串(**含构建时注入的渠道 prompt**)
236
238
  * - additionInfo: first-run 时的 additionInfo(冻结)
237
- * - snapshotAt: 持久化时间戳
239
+ * - snapshotAt: 构建时间戳
238
240
  *
239
- * 第三方代码不应直接修改此字段。如需强制重建 snapshot,可通过
240
- * sessionComponent.update(sessionId, { metadata: { cacheSnapshot: null } }) 清除。
241
+ * ⚠️ 本字段**仅是运行时透传载体**:它的值只来自
242
+ * `_loadOrCreateCacheSnapshot` 的内存 Map,**既不从 session.metadata 读取,
243
+ * 也不写入 session.metadata** —— 不要把它当成「可持久化到 session 行」的句柄。
244
+ *
245
+ * 第三方代码不应直接修改此字段。快照重建条件:进程重启(内存态全失)
246
+ * 或换用新的 sessionId。
241
247
  */
242
248
  _cacheSnapshot?: {
243
249
  systemPrompt: string;
244
250
  additionInfo: string;
245
251
  snapshotAt: number;
246
- /** 【Task #4813】B1 mtime 失效检测:首拍时各 memory 文件的 mtime(ms) */
247
- memoryStat?: MemorySnapshotStat;
248
252
  };
249
253
  /** 执行上下文(可直接修改属性) */
250
254
  context: AgentContext;
@@ -320,13 +324,29 @@ export type AgentType = "primary" | "sub" | "summary";
320
324
  * - globalMtime: global memory.md 的 mtime(ms);文件不存在记 0
321
325
  * - projectMtime: project memory.md 的 mtime(ms);文件不存在记 0
322
326
  *
323
- * 复用快照前比对当前 mtime,不一致 → 快照过期 → 重拍。
324
- * 旧快照无此字段 → 视为过期(一次性迁移重拍)。
327
+ * ⚠️ 【Task #5224】**已停用**(保留类型仅为向后兼容,不破坏公开导出面):
328
+ * 快照改内存级后,失效检测(`_isMemorySnapshotStale`)被移除 —— session
329
+ * 生命周期内快照冻结,新 session / 进程重启自然重建并拿到最新 memory。
325
330
  */
326
331
  export interface MemorySnapshotStat {
327
332
  globalMtime: number;
328
333
  projectMtime: number;
329
334
  }
335
+ /**
336
+ * ★ Task #5224 — session-scope cache snapshot(内存级)。
337
+ *
338
+ * 构建一次、session 生命周期内冻结使用;不持久化到 sessions.db。
339
+ *
340
+ * - systemPrompt: 含 {{memory}} 替换后的最终字符串,
341
+ * **且含构建时注入的渠道 prompt**(唯一注入点)
342
+ * - additionInfo: 构建时的 additionInfo(冻结 → 字节级一致 → prompt cache 命中)
343
+ * - snapshotAt: 构建时间戳
344
+ */
345
+ export interface CacheSnapshot {
346
+ systemPrompt: string;
347
+ additionInfo: string;
348
+ snapshotAt: number;
349
+ }
330
350
  /**
331
351
  * Agent 状态
332
352
  */
@@ -111,6 +111,12 @@ export interface FailureReplyChannelLike {
111
111
  profile?: string;
112
112
  appId?: string;
113
113
  userId?: string;
114
+ /**
115
+ * Task #5311:渠道自定义参数(event-source ReplyChannel.params 的透传)。
116
+ * 企微通道用 `params.sourceId`(WS fallback 实例定位)与
117
+ * `params.reqId`(WS 被动回帧)。lark 通道不读它。
118
+ */
119
+ params?: Record<string, unknown>;
114
120
  }
115
121
  export interface NotifyReactFailureOptions {
116
122
  /** 触发 React 中断的错误(任意类型,内部会做类型安全处理) */
@@ -8,7 +8,7 @@ import {
8
8
  getFailureRuleIds,
9
9
  humanizeRetryAfter,
10
10
  notifyReactFailure
11
- } from "../shared/@ai-setting/roy-agent-core-6cy43nss.js";
11
+ } from "../shared/@ai-setting/roy-agent-core-rykf7c04.js";
12
12
  import"../shared/@ai-setting/roy-agent-core-ntn32q7j.js";
13
13
  import"../shared/@ai-setting/roy-agent-core-m880qz3y.js";
14
14
  import"../shared/@ai-setting/roy-agent-core-7f7t9wwp.js";
@@ -208,7 +208,15 @@ export declare class BountyIMInstance implements EventSourceInstance {
208
208
  * 优先用 config.systemPrompt,否则用默认 prompt。
209
209
  * 该 prompt 会透传到 EventSourceEvent.metadata.systemPrompt,
210
210
  * 由 run.ts 在 dispatch env event 时注入到 agent system prompt。
211
+ *
212
+ * ★ ES-M1(Task #5144 / 方案 M2):结果缓存为实例字段 ——
213
+ * 构造一次,每条消息事件不再重新 resolve。同一渠道输出
214
+ * 逐字节稳定(resolveBountyIMSystemPrompt 只读 config,
215
+ * 本就确定;缓存进一步杜绝未来混入事件级变量导致的字节漂移,
216
+ * 保护 LLM prompt cache 前缀一致性)。
211
217
  */
218
+ private cachedSystemPrompt?;
219
+ private systemPromptResolved;
212
220
  private getCurrentSystemPrompt;
213
221
  getStatus(): EventSourceStatus;
214
222
  start(): Promise<void>;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @fileoverview Task #5302 — `es` command(eventsource 管理斜杆命令)
3
+ *
4
+ * 用户需求(飞书明令 2026-11-11 10:11):
5
+ * 1. 斜杆命令 `/es add|list|del|mod` 管理 eventsource
6
+ * 2. 飞书 `##command##: es <sub>` 触发(复用 Task #4722 command 子系统)
7
+ * 3. interactive TUI 也要支持(见 packages/cli/src/tui-solid/interactive-slash-commands.ts)
8
+ *
9
+ * ## 数据面
10
+ *
11
+ * 直接读写 `~/.roy-agent/event-sources.json`(EventSourceComponent
12
+ * 的持久化文件,格式 `{version, sources: EventSourceConfig[]}`)。
13
+ *
14
+ * 为什么不走组件实例:command 子系统是**无状态纯函数**(es 前端 /
15
+ * CLI / interactive 三个前端共享),拿不到运行中的 EventSourceComponent;
16
+ * 且 component.register() 会触发 saveConfig() 竞态。直接文件读写 +
17
+ * 写后重读校验,与 reset-command.ts 同级简单度。
18
+ *
19
+ * ⚠️ 与运行中 daemon 的关系:本命令改的是**磁盘配置**。运行中的
20
+ * EventSourceComponent 不会自动 reload(它只在启动时 loadConfig),
21
+ * 改完需 `##command##: reset` 重启进程生效 —— 在输出里明确提示。
22
+ *
23
+ * ## 安全
24
+ *
25
+ * - add 重名拒绝(与 component.register 一致)
26
+ * - mod 只允许白名单字段(enabled/name/interval/cron/profile);
27
+ * command/url 属于命令注入面,mod 明确不允许(需 del + add)
28
+ * - 写失败(磁盘/权限)→ ok:false,不静默
29
+ *
30
+ * ## 已知限制
31
+ *
32
+ * 参数按空白切分(飞书 `##command##:` 与 interactive `/es` 同规则),
33
+ * 因此含空格的值(如 cron 表达式)无法经这两个前端写入;
34
+ * CLI 侧可用引号传入。需要时用 CLI 或手工编辑 event-sources.json。
35
+ */
36
+ import type { CommandDefinition } from "./command-registry";
37
+ /** 从 configPath(config.jsonc)推导同目录的 event-sources.json */
38
+ export declare function resolveEsFilePath(configPath?: string): string;
39
+ export declare const esCommand: CommandDefinition;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @fileoverview Task #5193 — EventSource 插件加载共享模块
3
+ *
4
+ * ## 为什么抽出来
5
+ *
6
+ * 插件机制(BUILT_IN_PLUGIN_FACTORIES / getPluginHookPoints /
7
+ * registerPluginHooks)原来**只存在于 LarkCliInstance**(私有方法)。
8
+ * WecomCliInstance 要加载 WecomTaskNotifyPlugin 就得复用同一套逻辑,
9
+ * 但两者是独立的 class —— 直接复制会 drift(Task #4946「声明 ≠ 实现」
10
+ * 的教训:handleRule.plugins 配在 wecom 源上是 dead config)。
11
+ *
12
+ * 本模块把「按名字实例化 + 注册 hook 点」抽成**纯函数**,
13
+ * LarkCliInstance 与 WecomCliInstance 都可以调(lark 侧后续可迁移,
14
+ * 本次不动 lark 行为)。
15
+ *
16
+ * ## Hook 注册语义(与 LarkCliInstance.registerPluginHooks 一致)
17
+ *
18
+ * - Hook 模式(有 execute 无 hookPoint)→ 按 getPluginHookPoints
19
+ * 白名单(5 个 task hook 点)逐点注册
20
+ * - 显式 hookPoint → 单点注册
21
+ * - BasePlugin 模式(有 init)→ 调 init(registerHook 回调注册)
22
+ * - pluginEnabled 检查 key = `${sourceId}:${pluginName}`
23
+ */
24
+ /** 插件工厂配置(工厂收到的最小面) */
25
+ export interface PluginFactoryConfig {
26
+ sourceId: string;
27
+ pluginName: string;
28
+ [key: string]: unknown;
29
+ }
30
+ /**
31
+ * 内置插件工厂注册表。
32
+ *
33
+ * ⚠️ 与 event-source-handlers.ts 的 BUILT_IN_PLUGIN_FACTORIES **同源**:
34
+ * 该表在 handlers 里定义(静态 import 让 bundler 内联),本模块
35
+ * 通过注入函数引用避免循环依赖 —— 调用方(handlers / wecom handler)
36
+ * 传入自己的工厂表。
37
+ */
38
+ export type PluginFactory = (cfg: PluginFactoryConfig) => any;
39
+ /**
40
+ * 内置插件工厂注册表(单一真相源,Task #5193 迁入)。
41
+ *
42
+ * ⚠️ 原 BUILT_IN_PLUGIN_FACTORIES 在 event-source-handlers.ts,
43
+ * 迁到本模块让 wecom-cli-handler 也能复用(避免循环依赖)。
44
+ * handlers 侧改为引用本表,行为零变化。
45
+ */
46
+ export declare const BUILT_IN_PLUGIN_FACTORIES: Record<string, PluginFactory>;
47
+ /** wecom 侧工厂表(当前与全局同源;将来可裁剪 lark 专属插件) */
48
+ export declare function getWecomPluginFactories(): Record<string, PluginFactory>;
49
+ /**
50
+ * 获取插件的 hook 点列表(与 LarkCliInstance.getPluginHookPoints 同语义)
51
+ */
52
+ export declare function getPluginHookPointsShared(plugin: any): string[];
53
+ /**
54
+ * 把插件实例的 hooks 注册到 globalHookManager。
55
+ *
56
+ * @returns 注册的 hook 点列表(诊断用)
57
+ */
58
+ export declare function registerPluginHooksShared(plugin: any, pluginName: string, sourceId: string): string[];
59
+ /**
60
+ * 按名字实例化内置插件(工厂表由调用方注入)。
61
+ *
62
+ * @returns 插件实例;未知名字 / 实例化失败 → null
63
+ */
64
+ export declare function importPluginShared(name: string, factories: Record<string, PluginFactory>, pluginConfig?: Record<string, unknown>): any | null;
65
+ /**
66
+ * 注销插件注册的所有 hooks(stop 时调用)
67
+ */
68
+ export declare function unregisterPluginHooksShared(pluginName: string, hookPoints: string[]): void;
@@ -64,6 +64,21 @@ export declare class EventSourceComponent extends BaseComponent implements Event
64
64
  private buffers;
65
65
  private configPath;
66
66
  private persistenceEnabled;
67
+ constructor();
68
+ /**
69
+ * 测试环境守卫(Task #5181,2026-11-07/11-08 事故防再发)
70
+ *
71
+ * 事故:测试中实例化 EventSourceComponent 并触发 saveConfig(),
72
+ * 覆盖了生产 ~/.roy-agent/event-sources.json(10 个真实事件源配置差点丢失)。
73
+ *
74
+ * 规则:
75
+ * - 测试环境(NODE_ENV=test 或存在 BUN_TEST_WORKER_ID)⇒ persistence=false
76
+ * (只内存不落盘,绝不触碰真实 $HOME/.roy-agent/)
77
+ * - 显式 opt-out:ROY_AGENT_ES_PERSISTENCE=1 可在测试中强制开启持久化
78
+ * (用于守卫自身可测 / 隔离 HOME 的持久化测试)
79
+ */
80
+ static isTestEnvironment(): boolean;
81
+ private applyTestEnvironmentGuard;
67
82
  /** ConfigComponent 用于配置管理 */
68
83
  private configComponent?;
69
84
  /** 配置热更新 watcher 清理函数 */
@@ -56,6 +56,49 @@ export declare function buildLarkRecommendedAction(opts: {
56
56
  chatId?: string;
57
57
  messageId?: string;
58
58
  }): RecommendedAction;
59
+ /**
60
+ * 渠道静态规则章节标题(system prompt 与 lean 卡片互相指认的锚点)
61
+ */
62
+ export declare const LARK_CHANNEL_PROMPT_SECTION_TITLE = "\u2B50 \u98DE\u4E66\u56DE\u590D\u89C4\u8303\uFF08system prompt \u6CE8\u5165\u7248\uFF09";
63
+ /**
64
+ * ★ Task #5212(DS review MINOR-3):飞书 prompt 长度**防御性上限**(单一来源)。
65
+ *
66
+ * 两个出口共用同一预算:
67
+ * - lean 渠道 system prompt(buildLarkChannelSystemPrompt)
68
+ * - legacy recommendedAction.action(buildLarkRecommendedAction)
69
+ *
70
+ * ⚠️ 这是**防御性上限**,不是编辑预算:正常小幅扩写仍有余量,
71
+ * 但「各加一节」式回归会立刻超。改动相关章节前先量长度,
72
+ * 改完必须跑 lark-channel-system-prompt.test.ts (G) 组守护测试。
73
+ * (历史:旧 action 模板 ~4486 → profile=tongzhuli 时 4745 → #5164 追加
74
+ * 文档交付规范段后 4991 → #5212 追加引用上下文段后 5287。)
75
+ */
76
+ export declare const LARK_PROMPT_LENGTH_BUDGET = 5300;
77
+ /**
78
+ * 构建飞书渠道级 system prompt(ES-M1 lean 模式核心)。
79
+ *
80
+ * 内容 = 渠道静态规则(关键 0/1/2/3 + 禁止 + 成功判定,与旧
81
+ * recommendedAction 长模板字节级同源)+ profile 渠道约定(开头 2-3 行)。
82
+ *
83
+ * **禁止读事件**:入参只有 profile(渠道配置);同一渠道多次调用
84
+ * 逐字节一致,这是 LLM prompt cache 前缀稳定的根基。
85
+ *
86
+ * 静态规则中的定位占位(replyTo.messageId / replyTo.chatId)语义不变:
87
+ * system prompt 里写明「具体值取自 recommendedAction.replyTo」。
88
+ */
89
+ export declare function buildLarkChannelSystemPrompt(opts: {
90
+ profile?: string;
91
+ }): string;
92
+ /**
93
+ * 解析 lark-cli 渠道的 systemPrompt(单点构造,只读 config)。
94
+ *
95
+ * - lean(默认):config.systemPrompt(用户配置,若有)+ 渠道静态 prompt;
96
+ * - legacy(per-source 可回退):返回 config.systemPrompt 原值
97
+ * (未设置则 undefined,与旧版行为一致)。
98
+ *
99
+ * 同一 config 多次调用逐字节一致(测试守护)。
100
+ */
101
+ export declare function resolveLarkChannelSystemPrompt(config: EventSourceConfig): string | undefined;
59
102
  /**
60
103
  * 提取事件元信息
61
104
  *
@@ -156,6 +199,30 @@ export declare class LarkCliInstance implements EventSourceInstance {
156
199
  private recentEventIds;
157
200
  private readonly DEDUP_WINDOW_MS;
158
201
  private readonly DEDUP_MAX_SIZE;
202
+ /**
203
+ * ★ ES-M1(Task #5144):渠道 system prompt 的**实例级缓存**。
204
+ *
205
+ * resolveLarkChannelSystemPrompt(config) 只读 config(渠道配置),
206
+ * 输出对同一渠道是恒定的 —— 缓存到实例字段后:
207
+ * 1. 每条事件不再重复构造(字符串拼接成本省掉);
208
+ * 2. 更重要的是**杜绝字节漂移**:构造函数禁止读 event/rawEvent,
209
+ * 缓存值天然跨事件逐字节一致 → LLM prompt cache 前缀稳定
210
+ * (方案 §4.1 的"单点构造"规则)。
211
+ * legacy 模式下该字段是 config.systemPrompt(可能 undefined),
212
+ * 同样缓存一次。
213
+ */
214
+ private cachedChannelSystemPrompt?;
215
+ private channelSystemPromptResolved;
216
+ /**
217
+ * 获取本渠道的 systemPrompt(ES-M1,Task #5144)。
218
+ *
219
+ * lean 模式(默认):config.systemPrompt(用户配置,若有)+ 渠道静态 prompt;
220
+ * legacy 模式:config.systemPrompt 原值(未设置则 undefined)。
221
+ *
222
+ * 结果缓存在实例字段(构造一次),供 handleEvent 注入
223
+ * metadata.systemPrompt → query-executor → AgentComponent 追加。
224
+ */
225
+ getChannelSystemPrompt(): string | undefined;
159
226
  /**
160
227
  * Environment 引用(由 EventSourceComponent.startSource 调用 setEnvironment 注入)
161
228
  *
@@ -571,6 +638,7 @@ export { timerHandler };
571
638
  export { bountyIMHandler };
572
639
  export { bountyCliHandler };
573
640
  export { cliCommandHandler } from "./cli-command-handler";
641
+ export { wecomCliHandler } from "./wecom-cli-handler";
574
642
  /**
575
643
  * 根据类型获取内置 Handler
576
644
  */
@@ -1,7 +1,10 @@
1
1
  import {
2
+ LARK_CHANNEL_PROMPT_SECTION_TITLE,
3
+ LARK_PROMPT_LENGTH_BUDGET,
2
4
  LarkCliInstance,
3
5
  bountyCliHandler,
4
6
  bountyIMHandler,
7
+ buildLarkChannelSystemPrompt,
5
8
  buildLarkRecommendedAction,
6
9
  builtInHandlers,
7
10
  extractCommandText,
@@ -10,14 +13,18 @@ import {
10
13
  larkCliHandler,
11
14
  patchCardByLarkCli,
12
15
  renderRuleNoticeSection,
13
- timerHandler
14
- } from "../../shared/@ai-setting/roy-agent-core-3gpygqcx.js";
15
- import"../../shared/@ai-setting/roy-agent-core-bxkjk7zx.js";
16
+ resolveLarkChannelSystemPrompt,
17
+ timerHandler,
18
+ wecomCliHandler
19
+ } from "../../shared/@ai-setting/roy-agent-core-xf6z96qy.js";
20
+ import"../../shared/@ai-setting/roy-agent-core-vx8zc847.js";
16
21
  import {
17
22
  cliCommandHandler
18
23
  } from "../../shared/@ai-setting/roy-agent-core-v0hf14bj.js";
19
24
  import"../../shared/@ai-setting/roy-agent-core-tm1mxttk.js";
20
- import"../../shared/@ai-setting/roy-agent-core-rsn44n9y.js";
25
+ import"../../shared/@ai-setting/roy-agent-core-2stt4rp5.js";
26
+ import"../../shared/@ai-setting/roy-agent-core-b31v3qpv.js";
27
+ import"../../shared/@ai-setting/roy-agent-core-m3vjepzz.js";
21
28
  import"../../shared/@ai-setting/roy-agent-core-na18zwj0.js";
22
29
  import"../../shared/@ai-setting/roy-agent-core-9vxh6f8h.js";
23
30
  import"../../shared/@ai-setting/roy-agent-core-ezvpreqz.js";
@@ -25,9 +32,9 @@ import"../../shared/@ai-setting/roy-agent-core-1pg0fepg.js";
25
32
  import"../../shared/@ai-setting/roy-agent-core-tr3ep81v.js";
26
33
  import"../../shared/@ai-setting/roy-agent-core-m46fe3x4.js";
27
34
  import"../../shared/@ai-setting/roy-agent-core-4rtg69cn.js";
28
- import"../../shared/@ai-setting/roy-agent-core-hd3n57zs.js";
29
- import"../../shared/@ai-setting/roy-agent-core-w0qjeq2v.js";
30
- import"../../shared/@ai-setting/roy-agent-core-6cy43nss.js";
35
+ import"../../shared/@ai-setting/roy-agent-core-d1n4jbza.js";
36
+ import"../../shared/@ai-setting/roy-agent-core-7dd0s3xb.js";
37
+ import"../../shared/@ai-setting/roy-agent-core-rykf7c04.js";
31
38
  import"../../shared/@ai-setting/roy-agent-core-c2gbt68e.js";
32
39
  import"../../shared/@ai-setting/roy-agent-core-1v4g0d4x.js";
33
40
  import"../../shared/@ai-setting/roy-agent-core-cgtq6427.js";
@@ -48,7 +55,9 @@ import"../../shared/@ai-setting/roy-agent-core-7f7t9wwp.js";
48
55
  import"../../shared/@ai-setting/roy-agent-core-85xyctnb.js";
49
56
  import"../../shared/@ai-setting/roy-agent-core-fs0mn2jk.js";
50
57
  export {
58
+ wecomCliHandler,
51
59
  timerHandler,
60
+ resolveLarkChannelSystemPrompt,
52
61
  renderRuleNoticeSection,
53
62
  patchCardByLarkCli,
54
63
  larkCliHandler,
@@ -58,7 +67,10 @@ export {
58
67
  cliCommandHandler,
59
68
  builtInHandlers,
60
69
  buildLarkRecommendedAction,
70
+ buildLarkChannelSystemPrompt,
61
71
  bountyIMHandler,
62
72
  bountyCliHandler,
63
- LarkCliInstance
73
+ LarkCliInstance,
74
+ LARK_PROMPT_LENGTH_BUDGET,
75
+ LARK_CHANNEL_PROMPT_SECTION_TITLE
64
76
  };