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

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 (155) 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 +23 -7
  4. package/dist/env/agent/agent-component.d.ts +43 -22
  5. package/dist/env/agent/agent-component.js +13 -12
  6. package/dist/env/agent/index.js +13 -12
  7. package/dist/env/agent/types.d.ts +46 -12
  8. package/dist/env/commands/index.js +7 -7
  9. package/dist/env/context/index.js +3 -3
  10. package/dist/env/debug/index.js +7 -7
  11. package/dist/env/error-reply.d.ts +6 -0
  12. package/dist/env/error-reply.js +1 -1
  13. package/dist/env/event-source/bounty-im-handler.d.ts +8 -0
  14. package/dist/env/event-source/es-command.d.ts +39 -0
  15. package/dist/env/event-source/es-plugins.d.ts +68 -0
  16. package/dist/env/event-source/event-source-component.d.ts +15 -0
  17. package/dist/env/event-source/event-source-handlers.d.ts +84 -0
  18. package/dist/env/event-source/event-source-handlers.js +40 -23
  19. package/dist/env/event-source/event-store.js +2 -2
  20. package/dist/env/event-source/index.d.ts +5 -1
  21. package/dist/env/event-source/index.js +75 -23
  22. package/dist/env/event-source/timer-handler.d.ts +98 -0
  23. package/dist/env/event-source/types.d.ts +154 -2
  24. package/dist/env/event-source/wecom-card-action-handler.d.ts +373 -0
  25. package/dist/env/event-source/wecom-card-builder.d.ts +426 -0
  26. package/dist/env/event-source/wecom-cli-handler.d.ts +291 -0
  27. package/dist/env/event-source/wecom-cli-sender.d.ts +88 -0
  28. package/dist/env/event-source/wecom-media.d.ts +130 -0
  29. package/dist/env/event-source/wecom-respond-registry.d.ts +151 -0
  30. package/dist/env/event-source/wecom-respond-registry.js +33 -0
  31. package/dist/env/event-source/wecom-session-card-sender.d.ts +135 -0
  32. package/dist/env/event-source/wecom-session-card-sender.js +153 -0
  33. package/dist/env/event-source/wecom-ws-bridge.d.ts +213 -0
  34. package/dist/env/hook/index.js +5 -5
  35. package/dist/env/index.js +51 -45
  36. package/dist/env/llm/index.js +9 -8
  37. package/dist/env/log-trace/index.js +7 -7
  38. package/dist/env/log-trace/trace-filters.d.ts +6 -1
  39. package/dist/env/mcp/index.js +7 -7
  40. package/dist/env/memory/index.js +7 -7
  41. package/dist/env/plugin/index.js +7 -7
  42. package/dist/env/prompt/index.js +8 -8
  43. package/dist/env/prompt/prompts-index.d.ts +1 -1
  44. package/dist/env/session/group-session-link.d.ts +99 -0
  45. package/dist/env/session/index.js +11 -10
  46. package/dist/env/session/session-component.d.ts +89 -0
  47. package/dist/env/session/storage/index.js +5 -4
  48. package/dist/env/session/types.d.ts +10 -0
  49. package/dist/env/skill/index.js +7 -7
  50. package/dist/env/task/delegate/delegate-channel-hint.d.ts +39 -0
  51. package/dist/env/task/delegate/delegate-tool.d.ts +132 -7
  52. package/dist/env/task/delegate/delegate-tool.js +13 -9
  53. package/dist/env/task/delegate/index.js +8 -8
  54. package/dist/env/task/index.js +18 -18
  55. package/dist/env/task/mcp/index.js +4 -4
  56. package/dist/env/task/plugins/card-subject.d.ts +117 -0
  57. package/dist/env/task/plugins/index.d.ts +2 -0
  58. package/dist/env/task/plugins/index.js +39 -16
  59. package/dist/env/task/plugins/lark-card-action-handler.d.ts +261 -6
  60. package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +542 -2
  61. package/dist/env/task/plugins/lark-card-patch-notifier.js +12 -2
  62. package/dist/env/task/plugins/lark-card-state-store.d.ts +60 -0
  63. package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +61 -0
  64. package/dist/env/task/plugins/sub-session-activity.d.ts +415 -0
  65. package/dist/env/task/plugins/wecom-card-state-store.d.ts +121 -0
  66. package/dist/env/task/plugins/wecom-session-card-store.d.ts +134 -0
  67. package/dist/env/task/plugins/wecom-session-card-store.js +23 -0
  68. package/dist/env/task/plugins/wecom-task-notify-plugin.d.ts +264 -0
  69. package/dist/env/task/storage/index.js +3 -3
  70. package/dist/env/task/tools/index.js +4 -4
  71. package/dist/env/tool/built-in/index.d.ts +2 -0
  72. package/dist/env/tool/built-in/index.js +9 -2
  73. package/dist/env/tool/index.js +14 -9
  74. package/dist/env/tool/types.d.ts +25 -0
  75. package/dist/env/tool/validator.d.ts +8 -0
  76. package/dist/env/workflow/engine/index.js +12 -12
  77. package/dist/env/workflow/hooks/index.js +7 -7
  78. package/dist/env/workflow/index.js +24 -23
  79. package/dist/env/workflow/nodes/index.js +2 -2
  80. package/dist/env/workflow/plugins/index.js +10 -8
  81. package/dist/env/workflow/service/index.js +2 -2
  82. package/dist/env/workflow/storage/index.js +3 -3
  83. package/dist/env/workflow/tools/index.js +16 -15
  84. package/dist/env/workflow/utils/index.js +4 -4
  85. package/dist/index.d.ts +1 -1
  86. package/dist/index.js +118 -62
  87. package/dist/shared/@ai-setting/{roy-agent-core-dc9zq75z.js → roy-agent-core-0crmsc12.js} +3 -3
  88. package/dist/shared/@ai-setting/{roy-agent-core-aa2j7bk9.js → roy-agent-core-0sbmdz8d.js} +171 -36
  89. package/dist/shared/@ai-setting/{roy-agent-core-87942bw4.js → roy-agent-core-1mhyh4c5.js} +1 -1
  90. package/dist/shared/@ai-setting/{roy-agent-core-v6vr6za7.js → roy-agent-core-1vv4fx15.js} +4 -4
  91. package/dist/shared/@ai-setting/roy-agent-core-2stt4rp5.js +134 -0
  92. package/dist/shared/@ai-setting/{roy-agent-core-nwbfepba.js → roy-agent-core-3e040tc5.js} +1 -1
  93. package/dist/shared/@ai-setting/roy-agent-core-418zc9ha.js +288 -0
  94. package/dist/shared/@ai-setting/{roy-agent-core-r5wt1h3c.js → roy-agent-core-4fkxy2gv.js} +1 -1
  95. package/dist/shared/@ai-setting/{roy-agent-core-na18zwj0.js → roy-agent-core-4t3yskp8.js} +1 -1
  96. package/dist/shared/@ai-setting/{roy-agent-core-5pr4zp88.js → roy-agent-core-5kzhaap1.js} +1 -1
  97. package/dist/shared/@ai-setting/{roy-agent-core-qq96p0tj.js → roy-agent-core-5s2t8z00.js} +22 -1
  98. package/dist/shared/@ai-setting/{roy-agent-core-6rh8k6ba.js → roy-agent-core-6gj1hc5z.js} +7 -7
  99. package/dist/shared/@ai-setting/{roy-agent-core-ezvpreqz.js → roy-agent-core-6k8mb7zx.js} +1 -1
  100. package/dist/shared/@ai-setting/{roy-agent-core-g8htfwxj.js → roy-agent-core-7f10p7m2.js} +1 -1
  101. package/dist/shared/@ai-setting/roy-agent-core-7kvpc0w5.js +177 -0
  102. package/dist/shared/@ai-setting/{roy-agent-core-h21gngxf.js → roy-agent-core-7yh5wps0.js} +1 -1
  103. package/dist/shared/@ai-setting/{roy-agent-core-efkjyh69.js → roy-agent-core-9710zwwz.js} +1 -1
  104. package/dist/shared/@ai-setting/{roy-agent-core-6cy43nss.js → roy-agent-core-9c565frx.js} +49 -9
  105. package/dist/shared/@ai-setting/{roy-agent-core-fghc2m8z.js → roy-agent-core-aa8kv18f.js} +34 -2
  106. package/dist/shared/@ai-setting/{roy-agent-core-ew6qb5yx.js → roy-agent-core-an35dzbf.js} +6 -6
  107. package/dist/shared/@ai-setting/{roy-agent-core-mww0mnd9.js → roy-agent-core-azyz78ky.js} +7 -7
  108. package/dist/shared/@ai-setting/{roy-agent-core-264qexac.js → roy-agent-core-b7h39vrc.js} +4 -4
  109. package/dist/shared/@ai-setting/{roy-agent-core-dqxec05w.js → roy-agent-core-bjtc9d35.js} +2 -2
  110. package/dist/shared/@ai-setting/roy-agent-core-c4ec7k0p.js +426 -0
  111. package/dist/shared/@ai-setting/{roy-agent-core-ddyk1rc0.js → roy-agent-core-cre2y2ct.js} +160 -4
  112. package/dist/shared/@ai-setting/{roy-agent-core-tr3ep81v.js → roy-agent-core-d6zdbgtc.js} +1 -1
  113. package/dist/shared/@ai-setting/{roy-agent-core-0qyrk5k0.js → roy-agent-core-ds6qzmf8.js} +14 -5
  114. package/dist/shared/@ai-setting/{roy-agent-core-cgtq6427.js → roy-agent-core-e08bvpaq.js} +1 -1
  115. package/dist/shared/@ai-setting/{roy-agent-core-12n08pg5.js → roy-agent-core-e69qxdyy.js} +1 -1
  116. package/dist/shared/@ai-setting/{roy-agent-core-rhtdspx6.js → roy-agent-core-fan8g0z1.js} +10 -10
  117. package/dist/shared/@ai-setting/{roy-agent-core-xcp8vwax.js → roy-agent-core-fepm835e.js} +2 -2
  118. package/dist/shared/@ai-setting/{roy-agent-core-6j02sbee.js → roy-agent-core-fhg03v43.js} +1 -1
  119. package/dist/shared/@ai-setting/{roy-agent-core-99nwgj84.js → roy-agent-core-fzw9xzm1.js} +1 -1
  120. package/dist/shared/@ai-setting/{roy-agent-core-dvtg4sn0.js → roy-agent-core-g8yr733d.js} +1 -1
  121. package/dist/shared/@ai-setting/{roy-agent-core-3gpygqcx.js → roy-agent-core-gr5h1dyr.js} +2753 -358
  122. package/dist/shared/@ai-setting/roy-agent-core-h1e1fm9b.js +380 -0
  123. package/dist/shared/@ai-setting/{roy-agent-core-c2gbt68e.js → roy-agent-core-h83zdsrq.js} +2 -2
  124. package/dist/shared/@ai-setting/{roy-agent-core-8ez6mccb.js → roy-agent-core-k3gcvybs.js} +5 -1
  125. package/dist/shared/@ai-setting/{roy-agent-core-pgvcf91v.js → roy-agent-core-m39v7qpx.js} +2 -2
  126. package/dist/shared/@ai-setting/{roy-agent-core-tm1mxttk.js → roy-agent-core-mf5p38yf.js} +2 -2
  127. package/dist/shared/@ai-setting/{roy-agent-core-83bewqx8.js → roy-agent-core-mvh0td7p.js} +2 -2
  128. package/dist/shared/@ai-setting/{roy-agent-core-v85n42ve.js → roy-agent-core-mx855td4.js} +17 -38
  129. package/dist/shared/@ai-setting/{roy-agent-core-v87njmq7.js → roy-agent-core-n197medr.js} +13 -13
  130. package/dist/shared/@ai-setting/{roy-agent-core-cmprygrp.js → roy-agent-core-nqkfkpf4.js} +4 -4
  131. package/dist/shared/@ai-setting/{roy-agent-core-7cbpmk4q.js → roy-agent-core-p31ftgq9.js} +1 -1
  132. package/dist/shared/@ai-setting/{roy-agent-core-vffccgf6.js → roy-agent-core-qwd2dq5q.js} +10 -3
  133. package/dist/shared/@ai-setting/{roy-agent-core-rsn44n9y.js → roy-agent-core-ra66sf50.js} +2526 -942
  134. package/dist/shared/@ai-setting/{roy-agent-core-1v4g0d4x.js → roy-agent-core-rm7seqwb.js} +1 -1
  135. package/dist/shared/@ai-setting/{roy-agent-core-w0qjeq2v.js → roy-agent-core-shb1qsyg.js} +1041 -18
  136. package/dist/shared/@ai-setting/{roy-agent-core-ax9e9szb.js → roy-agent-core-smf94h73.js} +1 -1
  137. package/dist/shared/@ai-setting/{roy-agent-core-acj8ag08.js → roy-agent-core-tjnabyvq.js} +1 -1
  138. package/dist/shared/@ai-setting/{roy-agent-core-qqaak24r.js → roy-agent-core-tkpp1ba0.js} +1 -1
  139. package/dist/shared/@ai-setting/{roy-agent-core-9vhq7fvv.js → roy-agent-core-tpm1dd1g.js} +34 -88
  140. package/dist/shared/@ai-setting/{roy-agent-core-ed233k5z.js → roy-agent-core-v1n0106v.js} +1 -1
  141. package/dist/shared/@ai-setting/{roy-agent-core-zxz5fkwj.js → roy-agent-core-v6yshv0z.js} +1 -1
  142. package/dist/shared/@ai-setting/{roy-agent-core-mcy5apay.js → roy-agent-core-vf9sp6s3.js} +1 -1
  143. package/dist/shared/@ai-setting/{roy-agent-core-q813vafd.js → roy-agent-core-vpcxv1ty.js} +1 -1
  144. package/dist/shared/@ai-setting/{roy-agent-core-bxkjk7zx.js → roy-agent-core-vx8zc847.js} +4 -1
  145. package/dist/shared/@ai-setting/{roy-agent-core-xvt7kf7p.js → roy-agent-core-w9ryeg2p.js} +3 -3
  146. package/dist/shared/@ai-setting/{roy-agent-core-es203w7p.js → roy-agent-core-wqs6yypq.js} +1 -1
  147. package/dist/shared/@ai-setting/{roy-agent-core-hd3n57zs.js → roy-agent-core-wr1qexzq.js} +5 -5
  148. package/dist/shared/@ai-setting/roy-agent-core-wxrkcb17.js +56 -0
  149. package/dist/shared/@ai-setting/{roy-agent-core-0snm5hc5.js → roy-agent-core-y6shd91r.js} +1 -1
  150. package/dist/shared/@ai-setting/{roy-agent-core-9vxh6f8h.js → roy-agent-core-y7fqtzk2.js} +7 -7
  151. package/dist/shared/@ai-setting/{roy-agent-core-cpcd24n2.js → roy-agent-core-yet36q5k.js} +2 -2
  152. package/dist/shared/@ai-setting/{roy-agent-core-3fh1f7s7.js → roy-agent-core-yfpnkc4z.js} +5 -5
  153. package/dist/shared/@ai-setting/{roy-agent-core-0v27y99n.js → roy-agent-core-yh68z1bg.js} +65 -4
  154. package/package.json +18 -1
  155. /package/dist/shared/@ai-setting/{roy-agent-core-b4x6x4k2.js → roy-agent-core-m1fgtnrc.js} +0 -0
@@ -1,27 +1,326 @@
1
+ import {
2
+ formatActivityLines,
3
+ formatRecentToolCallLines
4
+ } from "./roy-agent-core-418zc9ha.js";
5
+ import {
6
+ createCompatibleDatabase,
7
+ init_sqlite_task_store
8
+ } from "./roy-agent-core-6k8mb7zx.js";
1
9
  import {
2
10
  TracedAs,
3
11
  init_decorator
4
- } from "./roy-agent-core-qq96p0tj.js";
12
+ } from "./roy-agent-core-5s2t8z00.js";
5
13
  import {
6
14
  createLogger,
7
15
  init_logger
8
16
  } from "./roy-agent-core-7f7t9wwp.js";
9
17
  import {
10
- __legacyDecorateClassTS
18
+ __legacyDecorateClassTS,
19
+ __require
11
20
  } from "./roy-agent-core-fs0mn2jk.js";
12
21
 
22
+ // src/env/task/plugins/lark-card-patch-notifier.ts
23
+ import { spawn } from "node:child_process";
24
+
25
+ // src/env/task/plugins/lark-card-state-store.ts
26
+ init_logger();
27
+ init_sqlite_task_store();
28
+ import { join } from "path";
29
+ var logger = createLogger("lark-cli:card-state");
30
+
31
+ class MemoryLarkCardStateStore {
32
+ records = new Map;
33
+ upsert(state) {
34
+ this.records.set(state.taskId, { ...state });
35
+ }
36
+ get(taskId) {
37
+ const r = this.records.get(taskId);
38
+ return r ? { ...r } : undefined;
39
+ }
40
+ list() {
41
+ return [...this.records.values()].map((r) => ({ ...r }));
42
+ }
43
+ markPinned(taskId, pinned) {
44
+ const r = this.records.get(taskId);
45
+ if (!r)
46
+ return;
47
+ r.pinned = pinned;
48
+ r.updatedAt = Date.now();
49
+ }
50
+ markPaused(taskId) {
51
+ const r = this.records.get(taskId);
52
+ if (!r)
53
+ return;
54
+ r.status = "paused";
55
+ r.updatedAt = Date.now();
56
+ }
57
+ markReopened(taskId) {
58
+ const r = this.records.get(taskId);
59
+ if (!r)
60
+ return;
61
+ r.status = "open";
62
+ delete r.closedAt;
63
+ r.updatedAt = Date.now();
64
+ }
65
+ markClosed(taskId) {
66
+ const r = this.records.get(taskId);
67
+ if (!r)
68
+ return;
69
+ const now = Date.now();
70
+ r.status = "closed";
71
+ r.closedAt = now;
72
+ r.updatedAt = now;
73
+ }
74
+ delete(taskId) {
75
+ this.records.delete(taskId);
76
+ }
77
+ close() {
78
+ this.records.clear();
79
+ }
80
+ }
81
+ function rowToState(row) {
82
+ const state = {
83
+ taskId: row.task_id,
84
+ messageId: row.message_id,
85
+ chatId: row.chat_id,
86
+ pinned: row.pinned === 1,
87
+ status: row.status ?? "open",
88
+ createdAt: row.created_at,
89
+ updatedAt: row.updated_at
90
+ };
91
+ if (row.profile)
92
+ state.profile = row.profile;
93
+ if (row.closed_at !== null && row.closed_at !== undefined) {
94
+ state.closedAt = row.closed_at;
95
+ }
96
+ if (row.timeline)
97
+ state.timeline = row.timeline;
98
+ if (row.session_id)
99
+ state.sessionId = row.session_id;
100
+ return state;
101
+ }
102
+
103
+ class SqliteLarkCardStateStore {
104
+ db;
105
+ constructor(dbPath) {
106
+ if (dbPath !== ":memory:") {
107
+ const { mkdirSync } = __require("fs");
108
+ const dir = __require("path").dirname(dbPath);
109
+ try {
110
+ mkdirSync(dir, { recursive: true });
111
+ } catch {}
112
+ }
113
+ this.db = createCompatibleDatabase(dbPath);
114
+ try {
115
+ this.db.run("PRAGMA journal_mode = WAL");
116
+ this.db.run("PRAGMA busy_timeout = 5000");
117
+ this.db.run("PRAGMA synchronous = NORMAL");
118
+ } catch {}
119
+ this.db.run(`
120
+ CREATE TABLE IF NOT EXISTS lark_card (
121
+ task_id INTEGER PRIMARY KEY,
122
+ message_id TEXT NOT NULL,
123
+ chat_id TEXT NOT NULL,
124
+ profile TEXT,
125
+ pinned INTEGER NOT NULL DEFAULT 0,
126
+ status TEXT NOT NULL DEFAULT 'open',
127
+ created_at INTEGER NOT NULL,
128
+ updated_at INTEGER NOT NULL,
129
+ closed_at INTEGER,
130
+ timeline TEXT,
131
+ session_id TEXT
132
+ )
133
+ `);
134
+ try {
135
+ this.db.run("ALTER TABLE lark_card ADD COLUMN timeline TEXT");
136
+ } catch {}
137
+ try {
138
+ this.db.run("ALTER TABLE lark_card ADD COLUMN session_id TEXT");
139
+ } catch {}
140
+ this.db.run("CREATE INDEX IF NOT EXISTS idx_lark_card_status ON lark_card(status)");
141
+ }
142
+ upsert(state) {
143
+ this.db.prepare(`INSERT INTO lark_card
144
+ (task_id, message_id, chat_id, profile, pinned, status, created_at, updated_at, closed_at, timeline, session_id)
145
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
146
+ ON CONFLICT(task_id) DO UPDATE SET
147
+ message_id = excluded.message_id,
148
+ chat_id = excluded.chat_id,
149
+ profile = excluded.profile,
150
+ pinned = excluded.pinned,
151
+ status = excluded.status,
152
+ updated_at = excluded.updated_at,
153
+ closed_at = excluded.closed_at,
154
+ timeline = excluded.timeline,
155
+ session_id = COALESCE(excluded.session_id, lark_card.session_id)`).run(state.taskId, state.messageId, state.chatId, state.profile ?? null, state.pinned ? 1 : 0, state.status, state.createdAt, state.updatedAt, state.closedAt ?? null, state.timeline ?? null, state.sessionId ?? null);
156
+ }
157
+ get(taskId) {
158
+ const row = this.db.prepare("SELECT * FROM lark_card WHERE task_id = ?").get(taskId);
159
+ return row ? rowToState(row) : undefined;
160
+ }
161
+ list() {
162
+ const rows = this.db.prepare("SELECT * FROM lark_card ORDER BY task_id ASC").all();
163
+ return rows.map(rowToState);
164
+ }
165
+ markPinned(taskId, pinned) {
166
+ this.db.prepare("UPDATE lark_card SET pinned = ?, updated_at = ? WHERE task_id = ?").run(pinned ? 1 : 0, Date.now(), taskId);
167
+ }
168
+ markPaused(taskId) {
169
+ this.db.prepare("UPDATE lark_card SET status = 'paused', updated_at = ? WHERE task_id = ?").run(Date.now(), taskId);
170
+ }
171
+ markReopened(taskId) {
172
+ this.db.prepare("UPDATE lark_card SET status = 'open', closed_at = NULL, updated_at = ? WHERE task_id = ?").run(Date.now(), taskId);
173
+ }
174
+ markClosed(taskId) {
175
+ const now = Date.now();
176
+ this.db.prepare("UPDATE lark_card SET status = 'closed', closed_at = ?, updated_at = ? WHERE task_id = ?").run(now, now, taskId);
177
+ }
178
+ delete(taskId) {
179
+ this.db.prepare("DELETE FROM lark_card WHERE task_id = ?").run(taskId);
180
+ }
181
+ close() {
182
+ try {
183
+ this.db.close?.();
184
+ } catch {}
185
+ }
186
+ }
187
+ function getDefaultLarkCardDbPath() {
188
+ const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
189
+ return join(home, ".local", "share", "roy-agent", "lark-cards.db");
190
+ }
191
+ function createLarkCardStateStore(dbPath) {
192
+ const isTestEnv = "BUN_TEST_WORKER_ID" in process.env;
193
+ if (!dbPath && !process.env.ROY_AGENT_LARK_CARD_DB && isTestEnv) {
194
+ return new MemoryLarkCardStateStore;
195
+ }
196
+ const path = dbPath ?? process.env.ROY_AGENT_LARK_CARD_DB ?? getDefaultLarkCardDbPath();
197
+ try {
198
+ return new SqliteLarkCardStateStore(path);
199
+ } catch (err) {
200
+ logger.warn(`LarkCardStateStore: sqlite unavailable at ${path} (${err.message}) ` + `→ falling back to in-memory (卡片仍可发,但重启后无法 patch 旧卡)`);
201
+ return new MemoryLarkCardStateStore;
202
+ }
203
+ }
204
+ var sharedStateStore;
205
+ function getSharedLarkCardStateStore() {
206
+ if (!sharedStateStore)
207
+ sharedStateStore = createLarkCardStateStore();
208
+ return sharedStateStore;
209
+ }
210
+ class MemoryLarkSessionCardStore {
211
+ records = new Map;
212
+ upsert(rec) {
213
+ this.records.set(rec.sessionId, { ...rec });
214
+ }
215
+ get(sessionId) {
216
+ const r = this.records.get(sessionId);
217
+ return r ? { ...r } : undefined;
218
+ }
219
+ list() {
220
+ return [...this.records.values()].map((r) => ({ ...r }));
221
+ }
222
+ delete(sessionId) {
223
+ this.records.delete(sessionId);
224
+ }
225
+ close() {
226
+ this.records.clear();
227
+ }
228
+ }
229
+
230
+ class SqliteLarkSessionCardStore {
231
+ db;
232
+ constructor(dbPath) {
233
+ this.db = createCompatibleDatabase(dbPath);
234
+ try {
235
+ this.db.run("PRAGMA journal_mode = WAL");
236
+ this.db.run("PRAGMA busy_timeout = 5000");
237
+ } catch {}
238
+ this.db.run(`
239
+ CREATE TABLE IF NOT EXISTS lark_session_card (
240
+ session_id TEXT PRIMARY KEY,
241
+ message_id TEXT NOT NULL,
242
+ chat_id TEXT NOT NULL,
243
+ profile TEXT,
244
+ created_at INTEGER NOT NULL,
245
+ updated_at INTEGER NOT NULL
246
+ )
247
+ `);
248
+ }
249
+ upsert(rec) {
250
+ this.db.prepare(`INSERT INTO lark_session_card
251
+ (session_id, message_id, chat_id, profile, created_at, updated_at)
252
+ VALUES (?, ?, ?, ?, ?, ?)
253
+ ON CONFLICT(session_id) DO UPDATE SET
254
+ message_id = excluded.message_id,
255
+ chat_id = excluded.chat_id,
256
+ profile = COALESCE(excluded.profile, lark_session_card.profile),
257
+ updated_at = excluded.updated_at`).run(rec.sessionId, rec.messageId, rec.chatId, rec.profile ?? null, rec.createdAt, rec.updatedAt);
258
+ }
259
+ get(sessionId) {
260
+ const row = this.db.prepare("SELECT * FROM lark_session_card WHERE session_id = ?").get(sessionId);
261
+ if (!row)
262
+ return;
263
+ return {
264
+ sessionId: row.session_id,
265
+ messageId: row.message_id,
266
+ chatId: row.chat_id,
267
+ ...row.profile ? { profile: row.profile } : {},
268
+ createdAt: row.created_at,
269
+ updatedAt: row.updated_at
270
+ };
271
+ }
272
+ list() {
273
+ const rows = this.db.prepare("SELECT * FROM lark_session_card ORDER BY updated_at DESC").all();
274
+ return rows.map((row) => ({
275
+ sessionId: row.session_id,
276
+ messageId: row.message_id,
277
+ chatId: row.chat_id,
278
+ ...row.profile ? { profile: row.profile } : {},
279
+ createdAt: row.created_at,
280
+ updatedAt: row.updated_at
281
+ }));
282
+ }
283
+ delete(sessionId) {
284
+ this.db.prepare("DELETE FROM lark_session_card WHERE session_id = ?").run(sessionId);
285
+ }
286
+ close() {
287
+ try {
288
+ this.db.close?.();
289
+ } catch {}
290
+ }
291
+ }
292
+ function createLarkSessionCardStore(dbPath) {
293
+ const isTestEnv = "BUN_TEST_WORKER_ID" in process.env;
294
+ if (!dbPath && isTestEnv)
295
+ return new MemoryLarkSessionCardStore;
296
+ const path = dbPath ?? getDefaultLarkCardDbPath();
297
+ try {
298
+ return new SqliteLarkSessionCardStore(path);
299
+ } catch (err) {
300
+ logger.warn(`LarkSessionCardStore: sqlite unavailable at ${path} (${err.message}) ` + `→ falling back to in-memory (卡片仍可发,但重启后无法认领旧卡)`);
301
+ return new MemoryLarkSessionCardStore;
302
+ }
303
+ }
304
+ var sharedSessionCardStore;
305
+ function getSharedLarkSessionCardStore() {
306
+ if (!sharedSessionCardStore) {
307
+ sharedSessionCardStore = createLarkSessionCardStore();
308
+ }
309
+ return sharedSessionCardStore;
310
+ }
311
+
13
312
  // src/env/task/plugins/lark-card-patch-notifier.ts
14
313
  init_logger();
15
314
  init_decorator();
16
- import { spawn } from "node:child_process";
17
- var logger = createLogger("lark-cli:card-patch-notifier");
315
+ var logger2 = createLogger("lark-cli:card-patch-notifier");
18
316
  var HEADER_TEMPLATE = {
19
317
  running: "blue",
20
318
  completed: "green",
21
319
  failed: "red",
22
320
  stopped: "grey",
23
321
  cancelled: "grey",
24
- deleted: "grey"
322
+ deleted: "grey",
323
+ to_validate: "turquoise"
25
324
  };
26
325
  var STATUS_PHRASE = {
27
326
  running: "实时同步中",
@@ -29,7 +328,8 @@ var STATUS_PHRASE = {
29
328
  failed: "\uD83D\uDEA8 任务失败",
30
329
  stopped: "⏹️ 已停止",
31
330
  cancelled: "\uD83D\uDEAB 已取消",
32
- deleted: "\uD83D\uDDD1️ 已删除"
331
+ deleted: "\uD83D\uDDD1️ 已删除",
332
+ to_validate: "\uD83D\uDD0D 待验证"
33
333
  };
34
334
  var FINAL_MILESTONE_TITLE = {
35
335
  running: "Task 进行中",
@@ -37,7 +337,8 @@ var FINAL_MILESTONE_TITLE = {
37
337
  failed: "Task 失败",
38
338
  stopped: "Task 已暂停",
39
339
  cancelled: "Task 已取消",
40
- deleted: "Task 已删除"
340
+ deleted: "Task 已删除",
341
+ to_validate: "Task 待验证"
41
342
  };
42
343
  var SPINNER_EMOJIS = ["⏳", "\uD83D\uDD04", "\uD83D\uDD03", "⌛", "⏰", "\uD83D\uDD01"];
43
344
  var PROGRESS_BAR_CELLS = 20;
@@ -412,6 +713,7 @@ function buildTaskCard(opts) {
412
713
  const elements = [];
413
714
  const hasGroup = !!(opts.meta.perTaskChatId && opts.meta.perTaskChatId.length > 0);
414
715
  const taskIdForButton = opts.meta.id !== undefined ? String(opts.meta.id) : "";
716
+ const sessionIdForButton = typeof opts.meta.sessionId === "string" && opts.meta.sessionId.trim() ? opts.meta.sessionId : undefined;
415
717
  elements.push({
416
718
  tag: "button",
417
719
  text: {
@@ -421,9 +723,22 @@ function buildTaskCard(opts) {
421
723
  type: hasGroup ? "danger" : "primary",
422
724
  value: {
423
725
  action: hasGroup ? "disband_group" : "create_group",
424
- taskId: taskIdForButton
726
+ taskId: taskIdForButton,
727
+ ...sessionIdForButton ? { sessionId: sessionIdForButton } : {}
425
728
  }
426
729
  });
730
+ if (taskIdForButton) {
731
+ elements.push({
732
+ tag: "button",
733
+ text: { tag: "plain_text", content: "\uD83D\uDCAC 会话查看" },
734
+ type: "default",
735
+ value: {
736
+ action: "refresh_activity",
737
+ taskId: taskIdForButton,
738
+ ...sessionIdForButton ? { sessionId: sessionIdForButton } : {}
739
+ }
740
+ });
741
+ }
427
742
  if (hasGroup) {
428
743
  const link = buildChatAppLink(opts.meta.perTaskChatId);
429
744
  const label = opts.meta.perTaskChatName ? `\uD83D\uDCE8 进入专属群:${sanitizeInlineText(opts.meta.perTaskChatName, 60)}` : "\uD83D\uDCE8 进入专属群";
@@ -443,6 +758,21 @@ function buildTaskCard(opts) {
443
758
  }));
444
759
  appendHr(elements);
445
760
  }
761
+ if (opts.activity !== undefined) {
762
+ elements.push(buildCollapsiblePanel({
763
+ title: "⚡ 子会话活性",
764
+ elementId: "task_activity",
765
+ expanded: true,
766
+ elements: [
767
+ {
768
+ tag: "markdown",
769
+ content: formatActivityLines(opts.activity ?? undefined, opts.activityNow ?? Date.now(), opts.activityTerminal ?? false).join(`
770
+ `)
771
+ }
772
+ ]
773
+ }));
774
+ appendHr(elements);
775
+ }
446
776
  const timeline = buildTimelineElements(milestones, processSteps);
447
777
  if (timeline.elements.length > 0) {
448
778
  elements.push(buildCollapsiblePanel({
@@ -476,6 +806,200 @@ function buildTaskCard(opts) {
476
806
  body: { elements }
477
807
  };
478
808
  }
809
+ function buildSessionCard(opts) {
810
+ const sessionId = opts.sessionId;
811
+ const updateCount = opts.updateCount ?? 1;
812
+ const spinner = SPINNER_EMOJIS[updateCount % SPINNER_EMOJIS.length];
813
+ const elements = [];
814
+ const taskIdNum = opts.task?.id;
815
+ const hasTaskId = typeof taskIdNum === "number" && taskIdNum > 0;
816
+ const hasGroup = !!(opts.perTaskChatId && opts.perTaskChatId.length > 0);
817
+ elements.push({
818
+ tag: "button",
819
+ text: {
820
+ tag: "plain_text",
821
+ content: hasGroup ? "解散该群" : hasTaskId ? "为这个任务建群" : "为本会话建群"
822
+ },
823
+ type: hasGroup ? "danger" : "primary",
824
+ value: {
825
+ action: hasGroup ? "disband_group" : "create_group",
826
+ sessionId,
827
+ ...hasTaskId ? { taskId: String(taskIdNum) } : {}
828
+ }
829
+ });
830
+ if (hasGroup) {
831
+ elements.push({
832
+ tag: "markdown",
833
+ content: `[\uD83D\uDCE8 进入专属群](${buildChatAppLink(opts.perTaskChatId)})`
834
+ });
835
+ }
836
+ elements.push({
837
+ tag: "button",
838
+ text: { tag: "plain_text", content: "\uD83D\uDCAC 会话查看" },
839
+ type: "default",
840
+ value: {
841
+ action: "refresh_activity",
842
+ sessionId,
843
+ ...hasTaskId ? { taskId: String(taskIdNum) } : {}
844
+ }
845
+ });
846
+ const infoLines = [];
847
+ infoLines.push(`- **\uD83C\uDD94 会话 ID**:${sanitizeInlineText(sessionId, 120)}`);
848
+ if (opts.title)
849
+ infoLines.push(`- **\uD83D\uDCDD 标题**:${sanitizeInlineText(opts.title, 120)}`);
850
+ if (opts.agentType) {
851
+ infoLines.push(`- **\uD83E\uDD16 执行 Agent**:${sanitizeInlineText(opts.agentType, 60)}`);
852
+ }
853
+ if (opts.startedAt) {
854
+ infoLines.push(`- **\uD83D\uDD50 开始时间**:${formatDateTimeForDisplay(opts.startedAt)}`);
855
+ }
856
+ if (opts.elapsedText)
857
+ infoLines.push(`- **⏳ 已运行**:${opts.elapsedText}`);
858
+ if (infoLines.length > 0) {
859
+ elements.push(buildCollapsiblePanel({
860
+ title: "\uD83D\uDCCB 会话详情",
861
+ elementId: "session_detail",
862
+ elements: [{ tag: "markdown", content: infoLines.join(`
863
+ `) }]
864
+ }));
865
+ appendHr(elements);
866
+ }
867
+ const taskInfo = opts.task;
868
+ if (taskInfo) {
869
+ const taskLines = [];
870
+ taskLines.push(`- **\uD83C\uDD94 任务 ID**:#${taskInfo.id}`);
871
+ if (taskInfo.title) {
872
+ taskLines.push(`- **\uD83D\uDCCC 标题**:${sanitizeInlineText(taskInfo.title, 120)}`);
873
+ }
874
+ if (taskInfo.status) {
875
+ taskLines.push(`- **\uD83D\uDEA6 状态**:${sanitizeInlineText(taskInfo.status, 40)}`);
876
+ }
877
+ if (typeof taskInfo.progress === "number") {
878
+ taskLines.push(`- **\uD83D\uDCCA 进度**:${renderProgressBar(taskInfo.progress)}`);
879
+ }
880
+ if (taskInfo.currentStatus) {
881
+ taskLines.push(`- **\uD83D\uDCDD 当前状态**:${sanitizeInlineText(taskInfo.currentStatus, 200)}`);
882
+ }
883
+ if (taskInfo.priority) {
884
+ taskLines.push(`- **\uD83D\uDD25 优先级**:${sanitizeInlineText(taskInfo.priority, 20)}`);
885
+ }
886
+ if (taskInfo.parentTaskId !== undefined) {
887
+ taskLines.push(`- **\uD83E\uDDE9 父任务**:#${taskInfo.parentTaskId}`);
888
+ }
889
+ if (taskInfo.goals) {
890
+ taskLines.push(`- **\uD83C\uDFAF 目标**:${sanitizeInlineText(taskInfo.goals, 200)}`);
891
+ }
892
+ if (taskInfo.createdAt) {
893
+ taskLines.push(`- **\uD83D\uDD50 创建时间**:${formatDateTimeForDisplay(taskInfo.createdAt)}`);
894
+ }
895
+ if (taskInfo.updatedAt) {
896
+ taskLines.push(`- **\uD83D\uDD04 更新时间**:${formatDateTimeForDisplay(taskInfo.updatedAt)}`);
897
+ }
898
+ elements.push(buildCollapsiblePanel({
899
+ title: "\uD83D\uDCCB 任务信息",
900
+ elementId: "task_detail",
901
+ elements: [{ tag: "markdown", content: taskLines.join(`
902
+ `) }]
903
+ }));
904
+ appendHr(elements);
905
+ if (taskInfo.milestones && taskInfo.milestones.length > 0) {
906
+ elements.push(buildCollapsiblePanel({
907
+ title: `\uD83D\uDCCC 操作记录(${taskInfo.milestones.length})`,
908
+ elementId: "task_timeline",
909
+ expanded: false,
910
+ elements: [
911
+ { tag: "markdown", content: renderMilestones(taskInfo.milestones) }
912
+ ]
913
+ }));
914
+ appendHr(elements);
915
+ }
916
+ }
917
+ if (opts.activity !== undefined) {
918
+ elements.push(buildCollapsiblePanel({
919
+ title: "⚡ 子会话活性",
920
+ elementId: "task_activity",
921
+ expanded: true,
922
+ elements: [
923
+ {
924
+ tag: "markdown",
925
+ content: formatActivityLines(opts.activity ?? undefined, opts.activityNow ?? Date.now(), isSessionLifecycleTerminal(opts.lifecycle)).join(`
926
+ `)
927
+ }
928
+ ]
929
+ }));
930
+ appendHr(elements);
931
+ }
932
+ if (opts.recentMessages !== undefined) {
933
+ elements.push(buildCollapsiblePanel({
934
+ title: "\uD83D\uDD27 最近工具调用",
935
+ elementId: "session_recent_messages",
936
+ expanded: true,
937
+ elements: [
938
+ {
939
+ tag: "markdown",
940
+ content: formatRecentToolCallLines(opts.recentMessages).join(`
941
+ `)
942
+ }
943
+ ]
944
+ }));
945
+ appendHr(elements);
946
+ }
947
+ const milestones = opts.milestones ?? [];
948
+ if (milestones.length > 0) {
949
+ elements.push(buildCollapsiblePanel({
950
+ title: `\uD83D\uDCCC 执行动态(${milestones.length})`,
951
+ elementId: "session_timeline",
952
+ expanded: false,
953
+ elements: [{ tag: "markdown", content: renderMilestones(milestones) }]
954
+ }));
955
+ }
956
+ const titleRaw = opts.title?.trim();
957
+ const headLabel = titleRaw ? truncateHeadTitle(titleRaw) : truncateHeadTitle(sessionId);
958
+ const lifecycle = opts.lifecycle ?? "running";
959
+ const lifecyclePhrase = opts.statusPhrase ?? SESSION_LIFECYCLE_PHRASE[lifecycle];
960
+ const headerTemplate = lifecycle === "running" ? HEADER_TEMPLATE.running : lifecycle === "completed" ? HEADER_TEMPLATE.completed : lifecycle === "stopped" ? HEADER_TEMPLATE.stopped : HEADER_TEMPLATE.failed;
961
+ return {
962
+ schema: "2.0",
963
+ header: {
964
+ title: { tag: "plain_text", content: `\uD83D\uDCCA 子会话实时更新(${headLabel})` },
965
+ subtitle: {
966
+ tag: "plain_text",
967
+ content: `spinner: '${spinner}' · 第 ${updateCount} 次更新 | ${lifecyclePhrase}`
968
+ },
969
+ template: headerTemplate
970
+ },
971
+ body: { elements }
972
+ };
973
+ }
974
+ function isSessionLifecycleTerminal(lifecycle) {
975
+ return lifecycle !== undefined && lifecycle !== "running";
976
+ }
977
+ function cardStatusToSessionLifecycle(status) {
978
+ switch (status) {
979
+ case "completed":
980
+ return "completed";
981
+ case "failed":
982
+ return "failed";
983
+ case "stopped":
984
+ case "cancelled":
985
+ case "deleted":
986
+ case "paused":
987
+ case "archived":
988
+ case "blocked":
989
+ return "stopped";
990
+ case "timeout":
991
+ return "timeout";
992
+ default:
993
+ return "running";
994
+ }
995
+ }
996
+ var SESSION_LIFECYCLE_PHRASE = {
997
+ running: "运行中",
998
+ completed: "已完成",
999
+ timeout: "已超时",
1000
+ failed: "已失败",
1001
+ stopped: "已停止"
1002
+ };
479
1003
  function buildChatAppLink(chatId, base = DEFAULT_APP_LINK_BASE) {
480
1004
  return `${base}/client/chat/open?openChatId=${chatId}`;
481
1005
  }
@@ -626,11 +1150,14 @@ class LarkCardPatchNotifier {
626
1150
  mutexes = new Map;
627
1151
  states = new Map;
628
1152
  workflowStates = new Map;
1153
+ sessionStates = new Map;
1154
+ sessionStore;
629
1155
  constructor(options = {}) {
1156
+ this.sessionStore = options.sessionCardStore === null ? undefined : options.sessionCardStore ?? getSharedLarkSessionCardStore();
630
1157
  this.options = {
631
1158
  pinOnStart: true,
632
- unpinOnFinish: true,
633
- ...options
1159
+ unpinOnFinish: false,
1160
+ ...(({ sessionCardStore: _scs, ...rest }) => rest)(options)
634
1161
  };
635
1162
  }
636
1163
  mutexFor(messageId) {
@@ -676,7 +1203,7 @@ class LarkCardPatchNotifier {
676
1203
  pinned = true;
677
1204
  } catch (pinErr) {
678
1205
  pinError = pinErr.message;
679
- logger.warn(`Pin failed (non-fatal) for message ${messageId}: ${pinError}`);
1206
+ logger2.warn(`Pin failed (non-fatal) for message ${messageId}: ${pinError}`);
680
1207
  }
681
1208
  }
682
1209
  return { messageId, pinned, pinError };
@@ -684,7 +1211,23 @@ class LarkCardPatchNotifier {
684
1211
  async updateTask(opts) {
685
1212
  await this.mutexFor(opts.messageId).run(async () => {
686
1213
  if (this.finalisedCards.has(opts.messageId)) {
687
- logger.debug(`updateTask: message ${opts.messageId} 已是终态卡片,忽略迟到的更新`);
1214
+ logger2.debug(`updateTask: message ${opts.messageId} 已是终态卡片,忽略迟到的更新`);
1215
+ return;
1216
+ }
1217
+ if (this.sessionStates.has(opts.messageId)) {
1218
+ const card2 = this.applyTaskEventToSessionCard(opts.messageId, {
1219
+ ...opts.milestones ? { milestones: opts.milestones } : {},
1220
+ ...opts.processSteps ? { processSteps: opts.processSteps } : {},
1221
+ ...opts.meta ? { meta: opts.meta } : {},
1222
+ ...opts.status ? { status: opts.status } : {},
1223
+ ...opts.status ? { lifecycle: cardStatusToSessionLifecycle(opts.status) } : {},
1224
+ ...opts.progress !== undefined ? { progress: opts.progress } : {}
1225
+ });
1226
+ await this.runLarkCli(this.buildPatchCardArgs({
1227
+ messageId: opts.messageId,
1228
+ profile: opts.profile,
1229
+ card: card2
1230
+ }), "patch");
688
1231
  return;
689
1232
  }
690
1233
  const state = this.states.get(opts.messageId) ?? {
@@ -769,6 +1312,29 @@ class LarkCardPatchNotifier {
769
1312
  }
770
1313
  async pauseTask(opts) {
771
1314
  await this.mutexFor(opts.messageId).run(async () => {
1315
+ if (this.sessionStates.has(opts.messageId)) {
1316
+ const now2 = new Date().toISOString();
1317
+ const pauseMilestone2 = {
1318
+ title: opts.pausedMilestoneTitle ?? "Task 已暂停",
1319
+ timestamp: now2,
1320
+ description: opts.finalStep
1321
+ };
1322
+ const card2 = this.applyTaskEventToSessionCard(opts.messageId, {
1323
+ milestones: [pauseMilestone2],
1324
+ ...opts.processSteps ? { processSteps: opts.processSteps } : {},
1325
+ ...opts.meta ? { meta: opts.meta } : {},
1326
+ ...opts.status ? { status: opts.status } : {},
1327
+ ...opts.status ? { lifecycle: cardStatusToSessionLifecycle(opts.status) } : {},
1328
+ ...opts.progress !== undefined ? { progress: opts.progress } : {},
1329
+ nowIso: now2
1330
+ });
1331
+ await this.runLarkCli(this.buildPatchCardArgs({
1332
+ messageId: opts.messageId,
1333
+ profile: opts.profile,
1334
+ card: card2
1335
+ }), "patch");
1336
+ return;
1337
+ }
772
1338
  const state = this.states.get(opts.messageId);
773
1339
  const now = new Date().toISOString();
774
1340
  const updateCount = (state?.updateCount ?? 0) + 1;
@@ -813,7 +1379,7 @@ class LarkCardPatchNotifier {
813
1379
  profile: opts.profile ?? state?.profile
814
1380
  }), "unpin");
815
1381
  } catch (unpinErr) {
816
- logger.warn(`Unpin failed (non-fatal) for ${opts.messageId}: ${unpinErr.message}`);
1382
+ logger2.warn(`Unpin failed (non-fatal) for ${opts.messageId}: ${unpinErr.message}`);
817
1383
  }
818
1384
  }
819
1385
  }
@@ -825,6 +1391,30 @@ class LarkCardPatchNotifier {
825
1391
  }
826
1392
  async finishTask(opts) {
827
1393
  await this.mutexFor(opts.messageId).run(async () => {
1394
+ if (this.sessionStates.has(opts.messageId)) {
1395
+ const now2 = new Date().toISOString();
1396
+ const finalMilestone2 = {
1397
+ title: FINAL_MILESTONE_TITLE[opts.status] ?? "Task 结束",
1398
+ timestamp: now2,
1399
+ description: opts.finalStep
1400
+ };
1401
+ const card2 = this.applyTaskEventToSessionCard(opts.messageId, {
1402
+ milestones: [finalMilestone2],
1403
+ ...opts.processSteps ? { processSteps: opts.processSteps } : {},
1404
+ ...opts.meta ? { meta: opts.meta } : {},
1405
+ ...opts.status ? { status: opts.status } : {},
1406
+ ...opts.status ? { lifecycle: cardStatusToSessionLifecycle(opts.status) } : {},
1407
+ progress: opts.status === "completed" ? 100 : opts.progress,
1408
+ nowIso: now2
1409
+ });
1410
+ await this.runLarkCli(this.buildPatchCardArgs({
1411
+ messageId: opts.messageId,
1412
+ profile: opts.profile,
1413
+ card: card2
1414
+ }), "patch");
1415
+ this.finalisedCards.add(opts.messageId);
1416
+ return;
1417
+ }
828
1418
  const state = this.states.get(opts.messageId);
829
1419
  const now = new Date().toISOString();
830
1420
  const updateCount = (state?.updateCount ?? 0) + 1;
@@ -859,13 +1449,22 @@ class LarkCardPatchNotifier {
859
1449
  profile: opts.profile ?? state?.profile
860
1450
  }), "unpin");
861
1451
  } catch (unpinErr) {
862
- logger.warn(`Unpin failed (non-fatal) for ${opts.messageId}: ${unpinErr.message}`);
1452
+ logger2.warn(`Unpin failed (non-fatal) for ${opts.messageId}: ${unpinErr.message}`);
863
1453
  }
864
1454
  }
865
1455
  this.states.delete(opts.messageId);
866
1456
  this.mutexes.delete(opts.messageId);
867
1457
  }
868
1458
  updateMetaAndBuildCard(messageId, meta, opts = {}) {
1459
+ if (this.sessionStates.has(messageId)) {
1460
+ return this.applyTaskEventToSessionCard(messageId, {
1461
+ meta,
1462
+ bumpUpdateCount: false,
1463
+ ...opts.clearPerTaskChat ? { clearPerTaskChat: true } : {},
1464
+ ...meta.perTaskChatId ? { perTaskChatId: meta.perTaskChatId } : {},
1465
+ ...meta.perTaskChatName ? { perTaskChatName: meta.perTaskChatName } : {}
1466
+ });
1467
+ }
869
1468
  const state = this.states.get(messageId);
870
1469
  if (!state)
871
1470
  return;
@@ -884,7 +1483,416 @@ class LarkCardPatchNotifier {
884
1483
  spinner: SPINNER_EMOJIS[state.spinnerIndex] ?? SPINNER_EMOJIS[0]
885
1484
  });
886
1485
  }
1486
+ buildCardWithActivity(messageId, activity, opts = {}) {
1487
+ const state = this.states.get(messageId);
1488
+ if (!state)
1489
+ return;
1490
+ return buildTaskCard({
1491
+ status: "running",
1492
+ meta: state.meta,
1493
+ milestones: state.milestones,
1494
+ processSteps: state.processSteps,
1495
+ progress: state.progress,
1496
+ updateCount: state.updateCount,
1497
+ spinner: SPINNER_EMOJIS[state.spinnerIndex] ?? SPINNER_EMOJIS[0],
1498
+ activity,
1499
+ ...opts.now !== undefined ? { activityNow: opts.now } : {},
1500
+ ...opts.terminal !== undefined ? { activityTerminal: opts.terminal } : {}
1501
+ });
1502
+ }
1503
+ async updateSessionCard(opts) {
1504
+ const rec = this.findSessionCardBySessionId(opts.sessionId);
1505
+ if (!rec)
1506
+ return false;
1507
+ const { messageId, state } = rec;
1508
+ const mergedMilestones = mergeUnique(state.milestones, opts.milestones ?? [], milestoneKey);
1509
+ const updateCount = state.updateCount + 1;
1510
+ const card = buildSessionCard({
1511
+ sessionId: opts.sessionId,
1512
+ title: state.title,
1513
+ statusPhrase: state.statusPhrase,
1514
+ agentType: state.agentType,
1515
+ startedAt: state.startedAt,
1516
+ elapsedText: state.elapsedText,
1517
+ milestones: mergedMilestones,
1518
+ lifecycle: opts.lifecycle,
1519
+ updateCount,
1520
+ ...opts.activity !== undefined ? { activity: opts.activity } : state.activity !== undefined ? { activity: state.activity } : {},
1521
+ ...opts.task !== undefined ? { task: opts.task } : state.task !== undefined ? { task: state.task } : {},
1522
+ ...opts.recentMessages !== undefined ? { recentMessages: opts.recentMessages } : state.recentMessages !== undefined ? { recentMessages: state.recentMessages } : {},
1523
+ ...opts.perTaskChatId ?? state.perTaskChatId ? { perTaskChatId: opts.perTaskChatId ?? state.perTaskChatId } : {},
1524
+ ...opts.perTaskChatName ?? state.perTaskChatName ? { perTaskChatName: opts.perTaskChatName ?? state.perTaskChatName } : {}
1525
+ });
1526
+ await this.runLarkCli(this.buildPatchCardArgs({
1527
+ messageId,
1528
+ profile: opts.profile ?? state.profile,
1529
+ card
1530
+ }), "patch");
1531
+ this.sessionStates.set(messageId, {
1532
+ ...state,
1533
+ milestones: mergedMilestones,
1534
+ updateCount,
1535
+ lifecycle: opts.lifecycle,
1536
+ ...opts.activity !== undefined ? { activity: opts.activity } : {},
1537
+ ...opts.task !== undefined ? { task: opts.task } : {},
1538
+ ...opts.perTaskChatId ? { perTaskChatId: opts.perTaskChatId } : {},
1539
+ ...opts.perTaskChatName ? { perTaskChatName: opts.perTaskChatName } : {}
1540
+ });
1541
+ this.sessionCardIds.set(opts.sessionId, messageId);
1542
+ if (typeof opts.task?.id === "number") {
1543
+ this.taskToSessionId.set(opts.task.id, opts.sessionId);
1544
+ }
1545
+ return true;
1546
+ }
1547
+ sessionCardIds = new Map;
1548
+ taskToSessionId = new Map;
1549
+ findSessionCardBySessionId(sessionId) {
1550
+ const messageId = this.sessionCardIds.get(sessionId);
1551
+ if (!messageId)
1552
+ return;
1553
+ const state = this.sessionStates.get(messageId);
1554
+ if (!state)
1555
+ return;
1556
+ return { messageId, state };
1557
+ }
1558
+ isSessionCardMessage(messageId) {
1559
+ return this.sessionStates.has(messageId);
1560
+ }
1561
+ applyTaskEventToSessionCard(messageId, delta) {
1562
+ const state = this.sessionStates.get(messageId);
1563
+ if (!state) {
1564
+ throw new Error(`applyTaskEventToSessionCard: ${messageId} 不是 session 卡(调用方应先 isSessionCardMessage)`);
1565
+ }
1566
+ const nowIso = delta.nowIso ?? new Date().toISOString();
1567
+ const stepsAsMilestones = (delta.processSteps ?? []).filter((s) => typeof s.title === "string" && s.title.trim() !== "").map((s) => ({
1568
+ title: s.title,
1569
+ ...s.timestamp ? { timestamp: s.timestamp } : {},
1570
+ ...s.description ? { description: s.description } : {}
1571
+ }));
1572
+ const mergedMilestones = mergeUnique(state.milestones ?? [], [...delta.milestones ?? [], ...stepsAsMilestones], milestoneKey);
1573
+ const prevTask = state.task;
1574
+ const metaId = delta.meta?.id;
1575
+ const metaIdNum = typeof metaId === "number" ? metaId : typeof metaId === "string" && metaId.trim() !== "" ? Number(metaId) : undefined;
1576
+ const hasTaskDelta = delta.meta !== undefined || delta.status !== undefined || delta.progress !== undefined;
1577
+ const hasValidTaskId = typeof metaIdNum === "number" && Number.isFinite(metaIdNum) && metaIdNum > 0;
1578
+ const shouldRenderTaskPanel = hasValidTaskId || prevTask !== undefined;
1579
+ const nextTask = hasTaskDelta && shouldRenderTaskPanel ? {
1580
+ id: typeof metaIdNum === "number" && Number.isFinite(metaIdNum) ? metaIdNum : prevTask?.id ?? 0,
1581
+ ...delta.meta?.title ?? prevTask?.title ? { title: delta.meta?.title ?? prevTask?.title } : {},
1582
+ ...delta.status ?? prevTask?.status ? { status: delta.status ?? prevTask?.status } : {},
1583
+ ...delta.progress !== undefined || prevTask?.progress !== undefined ? { progress: delta.progress ?? prevTask?.progress } : {},
1584
+ ...delta.meta?.currentStatus ?? prevTask?.currentStatus ? { currentStatus: delta.meta?.currentStatus ?? prevTask?.currentStatus } : {},
1585
+ ...delta.meta?.priority ?? prevTask?.priority ? { priority: delta.meta?.priority ?? prevTask?.priority } : {},
1586
+ ...delta.meta?.parentTaskId !== undefined || prevTask?.parentTaskId !== undefined ? { parentTaskId: delta.meta?.parentTaskId ?? prevTask?.parentTaskId } : {},
1587
+ ...delta.meta?.goals ?? prevTask?.goals ? { goals: delta.meta?.goals ?? prevTask?.goals } : {},
1588
+ ...prevTask?.createdAt ? { createdAt: prevTask.createdAt } : {},
1589
+ ...prevTask?.updatedAt ? { updatedAt: prevTask.updatedAt } : {},
1590
+ ...prevTask?.milestones ? { milestones: prevTask.milestones } : {}
1591
+ } : prevTask;
1592
+ const perTaskChatId = delta.clearPerTaskChat ? undefined : delta.perTaskChatId ?? state.perTaskChatId;
1593
+ const perTaskChatName = delta.clearPerTaskChat ? undefined : delta.perTaskChatName ?? state.perTaskChatName;
1594
+ const lifecycle = delta.lifecycle ?? state.lifecycle ?? "running";
1595
+ const bump = delta.bumpUpdateCount !== false;
1596
+ const updateCount = bump ? (state.updateCount ?? 1) + 1 : state.updateCount ?? 1;
1597
+ const card = buildSessionCard({
1598
+ sessionId: state.sessionId ?? "",
1599
+ title: state.title,
1600
+ statusPhrase: state.statusPhrase,
1601
+ agentType: state.agentType,
1602
+ startedAt: state.startedAt,
1603
+ elapsedText: state.elapsedText,
1604
+ milestones: mergedMilestones,
1605
+ lifecycle,
1606
+ updateCount,
1607
+ ...delta.activity !== undefined ? { activity: delta.activity } : state.activity !== undefined ? { activity: state.activity } : {},
1608
+ ...state.recentMessages !== undefined ? { recentMessages: state.recentMessages } : {},
1609
+ ...nextTask !== undefined ? { task: nextTask } : {},
1610
+ ...perTaskChatId ? { perTaskChatId } : {},
1611
+ ...perTaskChatName ? { perTaskChatName } : {}
1612
+ });
1613
+ this.sessionStates.set(messageId, {
1614
+ ...state,
1615
+ milestones: mergedMilestones,
1616
+ updateCount,
1617
+ lifecycle,
1618
+ ...nextTask !== undefined ? { task: nextTask } : {},
1619
+ ...perTaskChatId ? { perTaskChatId } : { perTaskChatId: undefined },
1620
+ ...perTaskChatName ? { perTaskChatName } : { perTaskChatName: undefined },
1621
+ ...delta.activity !== undefined ? { activity: delta.activity } : {}
1622
+ });
1623
+ return card;
1624
+ }
1625
+ bindTaskToSession(taskId, sessionId) {
1626
+ if (!Number.isFinite(taskId) || taskId <= 0)
1627
+ return;
1628
+ if (typeof sessionId !== "string" || !sessionId.trim())
1629
+ return;
1630
+ this.taskToSessionId.set(taskId, sessionId);
1631
+ }
1632
+ adoptSessionCard(sessionId, messageId, state) {
1633
+ if (!sessionId || !messageId)
1634
+ return;
1635
+ this.sessionCardIds.set(sessionId, messageId);
1636
+ this.rememberSessionCard(messageId, {
1637
+ sessionId,
1638
+ ...state ?? {}
1639
+ });
1640
+ }
1641
+ persistSessionCardRecord(sessionId, messageId, chatId, profile) {
1642
+ if (!this.sessionStore)
1643
+ return;
1644
+ try {
1645
+ const now = Date.now();
1646
+ const existing = this.sessionStore.get(sessionId);
1647
+ this.sessionStore.upsert({
1648
+ sessionId,
1649
+ messageId,
1650
+ chatId,
1651
+ ...profile ?? existing?.profile ? { profile: profile ?? existing?.profile } : {},
1652
+ createdAt: existing?.createdAt ?? now,
1653
+ updatedAt: now
1654
+ });
1655
+ } catch (err) {
1656
+ logger2.warn(`persistSessionCardRecord failed for ${sessionId}: ${err.message}`);
1657
+ }
1658
+ }
1659
+ restoreSessionCardsFromStore() {
1660
+ if (!this.sessionStore)
1661
+ return 0;
1662
+ let n = 0;
1663
+ try {
1664
+ for (const rec of this.sessionStore.list()) {
1665
+ if (!rec.sessionId || !rec.messageId)
1666
+ continue;
1667
+ if (this.sessionCardIds.has(rec.sessionId))
1668
+ continue;
1669
+ this.sessionCardIds.set(rec.sessionId, rec.messageId);
1670
+ if (!this.sessionStates.has(rec.messageId)) {
1671
+ this.sessionStates.set(rec.messageId, {
1672
+ ...rec.profile ? { profile: rec.profile } : {},
1673
+ sessionId: rec.sessionId,
1674
+ milestones: [],
1675
+ updateCount: 1
1676
+ });
1677
+ }
1678
+ n += 1;
1679
+ }
1680
+ } catch (err) {
1681
+ logger2.warn(`restoreSessionCardsFromStore failed: ${err.message}`);
1682
+ }
1683
+ if (n > 0) {
1684
+ logger2.info(`restoreSessionCardsFromStore: 回灌 ${n} 张 session 卡`);
1685
+ }
1686
+ return n;
1687
+ }
1688
+ applySessionGroupChange(messageId, opts) {
1689
+ const state = this.sessionStates.get(messageId);
1690
+ if (!state)
1691
+ return;
1692
+ const chatId = opts.perTaskChatId?.trim();
1693
+ if (chatId) {
1694
+ state.perTaskChatId = chatId;
1695
+ if (opts.perTaskChatName)
1696
+ state.perTaskChatName = opts.perTaskChatName;
1697
+ } else {
1698
+ delete state.perTaskChatId;
1699
+ delete state.perTaskChatName;
1700
+ }
1701
+ return buildSessionCard({
1702
+ sessionId: opts.sessionId,
1703
+ title: state.title,
1704
+ statusPhrase: state.statusPhrase,
1705
+ agentType: state.agentType,
1706
+ startedAt: state.startedAt,
1707
+ elapsedText: state.elapsedText,
1708
+ milestones: state.milestones,
1709
+ updateCount: state.updateCount,
1710
+ lifecycle: state.lifecycle,
1711
+ ...state.activity !== undefined ? { activity: state.activity } : {},
1712
+ ...state.recentMessages !== undefined ? { recentMessages: state.recentMessages } : {},
1713
+ ...state.task !== undefined ? { task: state.task } : {},
1714
+ ...state.perTaskChatId ? { perTaskChatId: state.perTaskChatId } : {},
1715
+ ...state.perTaskChatName ? { perTaskChatName: state.perTaskChatName } : {}
1716
+ });
1717
+ }
1718
+ getSessionIdByTask(taskId) {
1719
+ if (!Number.isFinite(taskId))
1720
+ return;
1721
+ return this.taskToSessionId.get(taskId);
1722
+ }
1723
+ getSessionCardMessageId(sessionId) {
1724
+ return this.sessionCardIds.get(sessionId);
1725
+ }
1726
+ getSessionCardState(sessionId) {
1727
+ const messageId = this.sessionCardIds.get(sessionId);
1728
+ if (!messageId)
1729
+ return;
1730
+ return this.sessionStates.get(messageId);
1731
+ }
1732
+ async syncGroupCardFromOrigin(originSessionId, opts, findGroupSession) {
1733
+ if (!findGroupSession)
1734
+ return false;
1735
+ let chatId;
1736
+ try {
1737
+ const group = await findGroupSession(originSessionId);
1738
+ chatId = group?.chatId;
1739
+ } catch (err) {
1740
+ logger2.warn(`syncGroupCardFromOrigin: findGroupChatForSession(${originSessionId}) 失败(跳过双写): ` + `${err.message}`);
1741
+ return false;
1742
+ }
1743
+ if (!chatId || chatId === originSessionId)
1744
+ return false;
1745
+ try {
1746
+ await this.upsertSessionCard({
1747
+ chatId,
1748
+ sessionId: chatId,
1749
+ lifecycle: opts.lifecycle ?? "running",
1750
+ ...opts.title ? { title: opts.title } : {},
1751
+ ...opts.milestones ? { milestones: opts.milestones } : {}
1752
+ });
1753
+ return true;
1754
+ } catch (err) {
1755
+ logger2.warn(`syncGroupCardFromOrigin: 群卡同步失败(chatId=${chatId}): ` + `${err.message}`);
1756
+ return false;
1757
+ }
1758
+ }
1759
+ async upsertSessionCard(opts) {
1760
+ const mutex = this.sessionUpsertMutexes.get(opts.sessionId) ?? new MutexImpl;
1761
+ this.sessionUpsertMutexes.set(opts.sessionId, mutex);
1762
+ const result = await mutex.run(async () => {
1763
+ const patched = await this.updateSessionCard(opts);
1764
+ if (patched) {
1765
+ const messageId = this.sessionCardIds.get(opts.sessionId);
1766
+ return { messageId, created: false };
1767
+ }
1768
+ const res = await this.startSessionCard(opts);
1769
+ return { messageId: res.messageId, created: true };
1770
+ });
1771
+ if (opts.lifecycle !== "running") {
1772
+ this.sessionUpsertMutexes.delete(opts.sessionId);
1773
+ }
1774
+ return result;
1775
+ }
1776
+ sessionUpsertMutexes = new Map;
1777
+ async startSessionCard(opts) {
1778
+ const card = buildSessionCard({
1779
+ sessionId: opts.sessionId,
1780
+ title: opts.title,
1781
+ statusPhrase: opts.statusPhrase,
1782
+ agentType: opts.agentType,
1783
+ startedAt: opts.startedAt,
1784
+ elapsedText: opts.elapsedText,
1785
+ milestones: opts.milestones,
1786
+ lifecycle: opts.lifecycle,
1787
+ updateCount: 1,
1788
+ ...opts.activity !== undefined ? { activity: opts.activity } : {},
1789
+ ...opts.task !== undefined ? { task: opts.task } : {},
1790
+ ...opts.perTaskChatId ? { perTaskChatId: opts.perTaskChatId } : {},
1791
+ ...opts.perTaskChatName ? { perTaskChatName: opts.perTaskChatName } : {}
1792
+ });
1793
+ const stdout = await this.runLarkCli(this.buildSendCardArgs({ chatId: opts.chatId, profile: opts.profile, card }), "send");
1794
+ const messageId = this.extractMessageId(stdout);
1795
+ if (!messageId) {
1796
+ throw new Error(`lark-cli +messages-send did not return message_id: ${stdout.slice(0, 500)}`);
1797
+ }
1798
+ this.sessionCardIds.set(opts.sessionId, messageId);
1799
+ if (typeof opts.task?.id === "number" && opts.task.id > 0) {
1800
+ this.taskToSessionId.set(opts.task.id, opts.sessionId);
1801
+ }
1802
+ this.rememberSessionCard(messageId, {
1803
+ profile: opts.profile,
1804
+ sessionId: opts.sessionId,
1805
+ title: opts.title,
1806
+ statusPhrase: opts.statusPhrase,
1807
+ agentType: opts.agentType,
1808
+ startedAt: opts.startedAt,
1809
+ elapsedText: opts.elapsedText,
1810
+ milestones: opts.milestones ?? [],
1811
+ updateCount: 1,
1812
+ lifecycle: opts.lifecycle,
1813
+ ...opts.activity !== undefined ? { activity: opts.activity } : {},
1814
+ ...opts.task !== undefined ? { task: opts.task } : {},
1815
+ ...opts.perTaskChatId ? { perTaskChatId: opts.perTaskChatId } : {},
1816
+ ...opts.perTaskChatName ? { perTaskChatName: opts.perTaskChatName } : {}
1817
+ });
1818
+ let pinned = false;
1819
+ let pinError;
1820
+ if (this.options.pinOnStart) {
1821
+ try {
1822
+ await this.pinMessage(messageId, opts.profile);
1823
+ pinned = true;
1824
+ } catch (err) {
1825
+ pinError = err.message;
1826
+ }
1827
+ }
1828
+ this.persistSessionCardRecord(opts.sessionId, messageId, opts.chatId, opts.profile);
1829
+ return { messageId, pinned, ...pinError ? { pinError } : {} };
1830
+ }
1831
+ buildSessionCardWithActivity(messageId, sessionId, activity, opts = {}) {
1832
+ const state = this.sessionStates.get(messageId);
1833
+ if (!state)
1834
+ return;
1835
+ const card = buildSessionCard({
1836
+ sessionId,
1837
+ title: state.title,
1838
+ statusPhrase: state.statusPhrase,
1839
+ agentType: state.agentType,
1840
+ startedAt: state.startedAt,
1841
+ elapsedText: state.elapsedText,
1842
+ milestones: state.milestones,
1843
+ updateCount: state.updateCount,
1844
+ activity,
1845
+ ...opts.lifecycle !== undefined ? { lifecycle: opts.lifecycle } : state.lifecycle !== undefined ? { lifecycle: state.lifecycle } : {},
1846
+ ...opts.task !== undefined ? { task: opts.task } : state.task !== undefined ? { task: state.task } : {},
1847
+ ...state.perTaskChatId ? { perTaskChatId: state.perTaskChatId } : {},
1848
+ ...state.perTaskChatName ? { perTaskChatName: state.perTaskChatName } : {},
1849
+ ...opts.recentMessages !== undefined ? { recentMessages: opts.recentMessages } : state.recentMessages !== undefined ? { recentMessages: state.recentMessages } : {},
1850
+ ...opts.now !== undefined ? { activityNow: opts.now } : {}
1851
+ });
1852
+ if (opts.task !== undefined) {
1853
+ this.rememberSessionCard(messageId, { task: opts.task });
1854
+ if (typeof opts.task.id === "number" && opts.task.id > 0) {
1855
+ this.taskToSessionId.set(opts.task.id, sessionId);
1856
+ }
1857
+ }
1858
+ if (opts.recentMessages !== undefined) {
1859
+ this.rememberSessionCard(messageId, {
1860
+ recentMessages: opts.recentMessages
1861
+ });
1862
+ }
1863
+ if (opts.lifecycle !== undefined) {
1864
+ this.rememberSessionCard(messageId, { lifecycle: opts.lifecycle });
1865
+ }
1866
+ if (activity !== undefined) {
1867
+ this.rememberSessionCard(messageId, { activity });
1868
+ }
1869
+ return card;
1870
+ }
1871
+ rememberSessionCard(messageId, state) {
1872
+ const prev = this.sessionStates.get(messageId);
1873
+ this.sessionStates.set(messageId, {
1874
+ title: state.title ?? prev?.title,
1875
+ statusPhrase: state.statusPhrase ?? prev?.statusPhrase,
1876
+ agentType: state.agentType ?? prev?.agentType,
1877
+ startedAt: state.startedAt ?? prev?.startedAt,
1878
+ elapsedText: state.elapsedText ?? prev?.elapsedText,
1879
+ milestones: state.milestones ?? prev?.milestones ?? [],
1880
+ updateCount: state.updateCount ?? prev?.updateCount ?? 1,
1881
+ lifecycle: state.lifecycle ?? prev?.lifecycle,
1882
+ profile: state.profile ?? prev?.profile,
1883
+ sessionId: state.sessionId ?? prev?.sessionId,
1884
+ activity: state.activity !== undefined ? state.activity : prev?.activity,
1885
+ recentMessages: state.recentMessages !== undefined ? state.recentMessages : prev?.recentMessages,
1886
+ task: state.task ?? prev?.task,
1887
+ perTaskChatId: state.perTaskChatId ?? prev?.perTaskChatId,
1888
+ perTaskChatName: state.perTaskChatName ?? prev?.perTaskChatName
1889
+ });
1890
+ }
887
1891
  finalisedCards = new Set;
1892
+ unfinaliseCard(messageId) {
1893
+ this.finalisedCards.delete(messageId);
1894
+ }
1895
+ testRunner;
888
1896
  trackedCardCount() {
889
1897
  return this.states.size;
890
1898
  }
@@ -932,7 +1940,7 @@ class LarkCardPatchNotifier {
932
1940
  pinned = true;
933
1941
  } catch (pinErr) {
934
1942
  pinError = pinErr.message;
935
- logger.warn(`Pin failed (non-fatal) for workflow message ${messageId}: ${pinError}`);
1943
+ logger2.warn(`Pin failed (non-fatal) for workflow message ${messageId}: ${pinError}`);
936
1944
  }
937
1945
  }
938
1946
  return { messageId, pinned, pinError };
@@ -940,7 +1948,7 @@ class LarkCardPatchNotifier {
940
1948
  async updateWorkflow(opts) {
941
1949
  await this.mutexFor(opts.messageId).run(async () => {
942
1950
  if (this.finalisedCards.has(opts.messageId)) {
943
- logger.debug(`updateWorkflow: message ${opts.messageId} 已是终态卡片,忽略迟到的更新`);
1951
+ logger2.debug(`updateWorkflow: message ${opts.messageId} 已是终态卡片,忽略迟到的更新`);
944
1952
  return;
945
1953
  }
946
1954
  const state = this.workflowStates.get(opts.messageId) ?? {
@@ -1045,7 +2053,7 @@ class LarkCardPatchNotifier {
1045
2053
  profile: opts.profile ?? state?.profile
1046
2054
  }), "unpin");
1047
2055
  } catch (unpinErr) {
1048
- logger.warn(`Unpin failed (non-fatal) for workflow ${opts.messageId}: ${unpinErr.message}`);
2056
+ logger2.warn(`Unpin failed (non-fatal) for workflow ${opts.messageId}: ${unpinErr.message}`);
1049
2057
  }
1050
2058
  }
1051
2059
  this.workflowStates.delete(opts.messageId);
@@ -1119,6 +2127,9 @@ class LarkCardPatchNotifier {
1119
2127
  return args;
1120
2128
  }
1121
2129
  runLarkCli(argv, op) {
2130
+ if (this.testRunner) {
2131
+ return this.testRunner(argv, op);
2132
+ }
1122
2133
  return new Promise((resolve, reject) => {
1123
2134
  if (process.env.ROY_HOOK_DEBUG === "1") {
1124
2135
  console.error(`[DEBUG LarkCardPatchNotifier.runLarkCli] op=${op} args=${JSON.stringify(argv).slice(0, 300)}`);
@@ -1362,5 +2373,17 @@ function sanitize(text) {
1362
2373
  return trimmed;
1363
2374
  return `${trimmed.slice(0, 500)}…`;
1364
2375
  }
2376
+ var sharedNotifier;
2377
+ function getSharedLarkCardNotifier() {
2378
+ if (!sharedNotifier)
2379
+ sharedNotifier = new LarkCardPatchNotifier;
2380
+ return sharedNotifier;
2381
+ }
2382
+ function __resetSharedLarkCardNotifierForTest() {
2383
+ sharedNotifier = undefined;
2384
+ }
2385
+ function hasSharedLarkCardNotifier() {
2386
+ return sharedNotifier !== undefined;
2387
+ }
1365
2388
 
1366
- export { formatTimestampForDisplay, sanitizeInlineText, HEAD_TITLE_MAX_WIDTH, truncateHeadTitle, formatDateTimeForDisplay, renderProgressBar, resolveTaskTitleForDisplay, humanizeValue, isTerminalStatusForTimeline, isTerminalStatusForWorkflowTimeline, buildCollapsiblePanel, renderTaskInfoLines, renderContextAsLines, renderMilestones, renderProcessSteps, partitionProcessSteps, buildTaskCard, buildChatAppLink, DEFAULT_APP_LINK_BASE, formatDurationMs, renderWorkflowInfoLines, buildWorkflowCard, LarkCardPatchNotifier };
2389
+ export { MemoryLarkCardStateStore, SqliteLarkCardStateStore, getDefaultLarkCardDbPath, createLarkCardStateStore, getSharedLarkCardStateStore, formatTimestampForDisplay, sanitizeInlineText, HEAD_TITLE_MAX_WIDTH, truncateHeadTitle, formatDateTimeForDisplay, renderProgressBar, resolveTaskTitleForDisplay, humanizeValue, isTerminalStatusForTimeline, isTerminalStatusForWorkflowTimeline, buildCollapsiblePanel, renderTaskInfoLines, renderContextAsLines, renderMilestones, renderProcessSteps, partitionProcessSteps, buildTaskCard, buildSessionCard, buildChatAppLink, DEFAULT_APP_LINK_BASE, formatDurationMs, renderWorkflowInfoLines, buildWorkflowCard, LarkCardPatchNotifier, getSharedLarkCardNotifier, __resetSharedLarkCardNotifierForTest, hasSharedLarkCardNotifier };