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

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 (148) hide show
  1. package/dist/config/index.js +7 -7
  2. package/dist/env/agent/agent-component.d.ts +1 -1
  3. package/dist/env/agent/agent-component.js +13 -12
  4. package/dist/env/agent/index.js +13 -12
  5. package/dist/env/agent/types.d.ts +14 -0
  6. package/dist/env/commands/index.js +7 -7
  7. package/dist/env/context/index.js +3 -3
  8. package/dist/env/debug/index.js +7 -7
  9. package/dist/env/error-reply.js +1 -1
  10. package/dist/env/event-source/event-source-handlers.d.ts +18 -2
  11. package/dist/env/event-source/event-source-handlers.js +26 -21
  12. package/dist/env/event-source/event-store.js +2 -2
  13. package/dist/env/event-source/index.js +25 -22
  14. package/dist/env/event-source/timer-handler.d.ts +98 -0
  15. package/dist/env/event-source/types.d.ts +53 -1
  16. package/dist/env/event-source/wecom-card-action-handler.d.ts +235 -3
  17. package/dist/env/event-source/wecom-card-builder.d.ts +207 -1
  18. package/dist/env/event-source/wecom-cli-handler.d.ts +25 -0
  19. package/dist/{shared/@ai-setting/roy-agent-core-vyr6svt6.js → env/event-source/wecom-respond-registry.js} +8 -6
  20. package/dist/env/event-source/wecom-session-card-sender.d.ts +135 -0
  21. package/dist/env/event-source/wecom-session-card-sender.js +153 -0
  22. package/dist/env/hook/index.js +5 -5
  23. package/dist/env/index.js +50 -45
  24. package/dist/env/llm/index.js +9 -8
  25. package/dist/env/log-trace/index.js +7 -7
  26. package/dist/env/log-trace/trace-filters.d.ts +6 -1
  27. package/dist/env/mcp/index.js +7 -7
  28. package/dist/env/memory/index.js +7 -7
  29. package/dist/env/plugin/index.js +7 -7
  30. package/dist/env/prompt/index.js +8 -8
  31. package/dist/env/prompt/prompts-index.d.ts +1 -1
  32. package/dist/env/session/group-session-link.d.ts +99 -0
  33. package/dist/env/session/index.js +11 -10
  34. package/dist/env/session/session-component.d.ts +89 -0
  35. package/dist/env/session/storage/index.js +5 -4
  36. package/dist/env/session/types.d.ts +10 -0
  37. package/dist/env/skill/index.js +7 -7
  38. package/dist/env/task/backend/index.d.ts +3 -0
  39. package/dist/env/task/backend/index.js +151 -3
  40. package/dist/env/task/backend/remote-task-port.d.ts +85 -0
  41. package/dist/env/task/backend/types.d.ts +5 -0
  42. package/dist/env/task/delegate/delegate-channel-hint.d.ts +39 -0
  43. package/dist/env/task/delegate/delegate-tool.d.ts +71 -6
  44. package/dist/env/task/delegate/delegate-tool.js +11 -9
  45. package/dist/env/task/delegate/index.js +8 -8
  46. package/dist/env/task/index.d.ts +1 -1
  47. package/dist/env/task/index.js +34 -18
  48. package/dist/env/task/mcp/index.js +4 -4
  49. package/dist/env/task/plugins/card-subject.d.ts +117 -0
  50. package/dist/env/task/plugins/index.js +19 -17
  51. package/dist/env/task/plugins/lark-card-action-handler.d.ts +261 -6
  52. package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +501 -0
  53. package/dist/env/task/plugins/lark-card-patch-notifier.js +12 -2
  54. package/dist/env/task/plugins/lark-card-state-store.d.ts +60 -0
  55. package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +45 -0
  56. package/dist/env/task/plugins/sub-session-activity.d.ts +415 -0
  57. package/dist/env/task/plugins/wecom-session-card-store.d.ts +134 -0
  58. package/dist/env/task/plugins/wecom-session-card-store.js +23 -0
  59. package/dist/env/task/plugins/wecom-task-notify-plugin.d.ts +40 -0
  60. package/dist/env/task/storage/index.d.ts +1 -0
  61. package/dist/env/task/storage/index.js +24 -4
  62. package/dist/env/task/storage/migrations/006_add_task_owner.d.ts +24 -0
  63. package/dist/env/task/storage/remote-task-store.d.ts +186 -0
  64. package/dist/env/task/task-component.d.ts +7 -0
  65. package/dist/env/task/tools/index.js +4 -4
  66. package/dist/env/task/tools/task-types.d.ts +7 -0
  67. package/dist/env/task/types.d.ts +17 -0
  68. package/dist/env/tool/built-in/index.js +6 -4
  69. package/dist/env/tool/index.js +14 -10
  70. package/dist/env/tool/types.d.ts +25 -0
  71. package/dist/env/tool/validator.d.ts +8 -0
  72. package/dist/env/workflow/engine/index.js +12 -12
  73. package/dist/env/workflow/hooks/index.js +7 -7
  74. package/dist/env/workflow/index.js +24 -23
  75. package/dist/env/workflow/nodes/index.js +2 -2
  76. package/dist/env/workflow/plugins/index.js +10 -8
  77. package/dist/env/workflow/service/index.js +2 -2
  78. package/dist/env/workflow/storage/index.js +3 -3
  79. package/dist/env/workflow/tools/index.js +16 -15
  80. package/dist/env/workflow/utils/index.js +4 -4
  81. package/dist/index.d.ts +1 -1
  82. package/dist/index.js +71 -60
  83. package/dist/shared/@ai-setting/{roy-agent-core-q813vafd.js → roy-agent-core-05z35shp.js} +1 -1
  84. package/dist/shared/@ai-setting/{roy-agent-core-dc9zq75z.js → roy-agent-core-0crmsc12.js} +3 -3
  85. package/dist/shared/@ai-setting/{roy-agent-core-7dd0s3xb.js → roy-agent-core-0n1wwndq.js} +1027 -14
  86. package/dist/shared/@ai-setting/{roy-agent-core-cha0jc7n.js → roy-agent-core-0sbmdz8d.js} +137 -16
  87. package/dist/shared/@ai-setting/{roy-agent-core-87942bw4.js → roy-agent-core-1mhyh4c5.js} +1 -1
  88. package/dist/shared/@ai-setting/{roy-agent-core-v6vr6za7.js → roy-agent-core-1vv4fx15.js} +4 -4
  89. package/dist/shared/@ai-setting/{roy-agent-core-nwbfepba.js → roy-agent-core-3e040tc5.js} +1 -1
  90. package/dist/shared/@ai-setting/roy-agent-core-418zc9ha.js +288 -0
  91. package/dist/shared/@ai-setting/{roy-agent-core-qk9hd89p.js → roy-agent-core-49qcpqwg.js} +54 -19
  92. package/dist/shared/@ai-setting/{roy-agent-core-xf6z96qy.js → roy-agent-core-4b38m749.js} +818 -346
  93. package/dist/shared/@ai-setting/{roy-agent-core-r5wt1h3c.js → roy-agent-core-4fkxy2gv.js} +1 -1
  94. package/dist/shared/@ai-setting/{roy-agent-core-na18zwj0.js → roy-agent-core-4t3yskp8.js} +1 -1
  95. package/dist/shared/@ai-setting/{roy-agent-core-d1n4jbza.js → roy-agent-core-5dhvfgaw.js} +5 -5
  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-g8htfwxj.js → roy-agent-core-7f10p7m2.js} +1 -1
  100. package/dist/shared/@ai-setting/{roy-agent-core-h21gngxf.js → roy-agent-core-7yh5wps0.js} +1 -1
  101. package/dist/shared/@ai-setting/{roy-agent-core-efkjyh69.js → roy-agent-core-9710zwwz.js} +1 -1
  102. package/dist/shared/@ai-setting/{roy-agent-core-rykf7c04.js → roy-agent-core-9c565frx.js} +1 -1
  103. package/dist/shared/@ai-setting/{roy-agent-core-vffccgf6.js → roy-agent-core-9cen1jvg.js} +18 -7
  104. package/dist/shared/@ai-setting/{roy-agent-core-fghc2m8z.js → roy-agent-core-aa8kv18f.js} +34 -2
  105. package/dist/shared/@ai-setting/roy-agent-core-age3qseb.js +177 -0
  106. package/dist/shared/@ai-setting/{roy-agent-core-7bpj859q.js → roy-agent-core-azyz78ky.js} +7 -7
  107. package/dist/shared/@ai-setting/{roy-agent-core-264qexac.js → roy-agent-core-b7h39vrc.js} +4 -4
  108. package/dist/shared/@ai-setting/{roy-agent-core-dqxec05w.js → roy-agent-core-bjtc9d35.js} +2 -2
  109. package/dist/shared/@ai-setting/roy-agent-core-c4ec7k0p.js +426 -0
  110. package/dist/shared/@ai-setting/{roy-agent-core-ddyk1rc0.js → roy-agent-core-cre2y2ct.js} +160 -4
  111. package/dist/shared/@ai-setting/{roy-agent-core-tr3ep81v.js → roy-agent-core-d6zdbgtc.js} +1 -1
  112. package/dist/shared/@ai-setting/{roy-agent-core-0qyrk5k0.js → roy-agent-core-ds6qzmf8.js} +14 -5
  113. package/dist/shared/@ai-setting/{roy-agent-core-p6ks90tb.js → roy-agent-core-dvec4nn2.js} +6 -6
  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-xcp8vwax.js → roy-agent-core-fepm835e.js} +2 -2
  117. package/dist/shared/@ai-setting/{roy-agent-core-6j02sbee.js → roy-agent-core-fhg03v43.js} +1 -1
  118. package/dist/shared/@ai-setting/{roy-agent-core-dvtg4sn0.js → roy-agent-core-g8yr733d.js} +1 -1
  119. package/dist/shared/@ai-setting/{roy-agent-core-m3vjepzz.js → roy-agent-core-h1e1fm9b.js} +38 -202
  120. package/dist/shared/@ai-setting/{roy-agent-core-c2gbt68e.js → roy-agent-core-h83zdsrq.js} +2 -2
  121. package/dist/shared/@ai-setting/{roy-agent-core-tk98f92b.js → roy-agent-core-k3gcvybs.js} +2 -2
  122. package/dist/shared/@ai-setting/{roy-agent-core-b31v3qpv.js → roy-agent-core-k3h8t4q1.js} +992 -212
  123. package/dist/shared/@ai-setting/{roy-agent-core-pgvcf91v.js → roy-agent-core-m39v7qpx.js} +2 -2
  124. package/dist/shared/@ai-setting/{roy-agent-core-ezvpreqz.js → roy-agent-core-m66ysky0.js} +54 -6
  125. package/dist/shared/@ai-setting/{roy-agent-core-begm7ytz.js → roy-agent-core-m7d1t3db.js} +3 -3
  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-ze5cqpf2.js → roy-agent-core-mx855td4.js} +17 -38
  129. package/dist/shared/@ai-setting/{roy-agent-core-0dh8npnz.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-1v4g0d4x.js → roy-agent-core-rm7seqwb.js} +1 -1
  133. package/dist/shared/@ai-setting/{roy-agent-core-ax9e9szb.js → roy-agent-core-smf94h73.js} +1 -1
  134. package/dist/shared/@ai-setting/{roy-agent-core-acj8ag08.js → roy-agent-core-tjnabyvq.js} +1 -1
  135. package/dist/shared/@ai-setting/{roy-agent-core-qqaak24r.js → roy-agent-core-tkpp1ba0.js} +1 -1
  136. package/dist/shared/@ai-setting/{roy-agent-core-dkrvmjsh.js → roy-agent-core-tpm1dd1g.js} +23 -15
  137. package/dist/shared/@ai-setting/{roy-agent-core-ed233k5z.js → roy-agent-core-v1n0106v.js} +1 -1
  138. package/dist/shared/@ai-setting/{roy-agent-core-zxz5fkwj.js → roy-agent-core-v6yshv0z.js} +1 -1
  139. package/dist/shared/@ai-setting/{roy-agent-core-mcy5apay.js → roy-agent-core-vf9sp6s3.js} +1 -1
  140. package/dist/shared/@ai-setting/{roy-agent-core-xvt7kf7p.js → roy-agent-core-w9ryeg2p.js} +3 -3
  141. package/dist/shared/@ai-setting/{roy-agent-core-es203w7p.js → roy-agent-core-wqs6yypq.js} +1 -1
  142. package/dist/shared/@ai-setting/roy-agent-core-wxrkcb17.js +56 -0
  143. package/dist/shared/@ai-setting/{roy-agent-core-0snm5hc5.js → roy-agent-core-y6shd91r.js} +1 -1
  144. package/dist/shared/@ai-setting/{roy-agent-core-9vxh6f8h.js → roy-agent-core-y7fqtzk2.js} +7 -7
  145. package/dist/shared/@ai-setting/{roy-agent-core-cpcd24n2.js → roy-agent-core-yet36q5k.js} +2 -2
  146. package/dist/shared/@ai-setting/{roy-agent-core-3fh1f7s7.js → roy-agent-core-yfpnkc4z.js} +5 -5
  147. package/dist/shared/@ai-setting/roy-agent-core-zhwcje5a.js +352 -0
  148. package/package.json +9 -1
@@ -1,20 +1,318 @@
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-m66ysky0.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",
@@ -415,6 +713,7 @@ function buildTaskCard(opts) {
415
713
  const elements = [];
416
714
  const hasGroup = !!(opts.meta.perTaskChatId && opts.meta.perTaskChatId.length > 0);
417
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;
418
717
  elements.push({
419
718
  tag: "button",
420
719
  text: {
@@ -424,9 +723,22 @@ function buildTaskCard(opts) {
424
723
  type: hasGroup ? "danger" : "primary",
425
724
  value: {
426
725
  action: hasGroup ? "disband_group" : "create_group",
427
- taskId: taskIdForButton
726
+ taskId: taskIdForButton,
727
+ ...sessionIdForButton ? { sessionId: sessionIdForButton } : {}
428
728
  }
429
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
+ }
430
742
  if (hasGroup) {
431
743
  const link = buildChatAppLink(opts.meta.perTaskChatId);
432
744
  const label = opts.meta.perTaskChatName ? `\uD83D\uDCE8 进入专属群:${sanitizeInlineText(opts.meta.perTaskChatName, 60)}` : "\uD83D\uDCE8 进入专属群";
@@ -446,6 +758,21 @@ function buildTaskCard(opts) {
446
758
  }));
447
759
  appendHr(elements);
448
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
+ }
449
776
  const timeline = buildTimelineElements(milestones, processSteps);
450
777
  if (timeline.elements.length > 0) {
451
778
  elements.push(buildCollapsiblePanel({
@@ -479,6 +806,200 @@ function buildTaskCard(opts) {
479
806
  body: { elements }
480
807
  };
481
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
+ };
482
1003
  function buildChatAppLink(chatId, base = DEFAULT_APP_LINK_BASE) {
483
1004
  return `${base}/client/chat/open?openChatId=${chatId}`;
484
1005
  }
@@ -629,11 +1150,14 @@ class LarkCardPatchNotifier {
629
1150
  mutexes = new Map;
630
1151
  states = new Map;
631
1152
  workflowStates = new Map;
1153
+ sessionStates = new Map;
1154
+ sessionStore;
632
1155
  constructor(options = {}) {
1156
+ this.sessionStore = options.sessionCardStore === null ? undefined : options.sessionCardStore ?? getSharedLarkSessionCardStore();
633
1157
  this.options = {
634
1158
  pinOnStart: true,
635
1159
  unpinOnFinish: false,
636
- ...options
1160
+ ...(({ sessionCardStore: _scs, ...rest }) => rest)(options)
637
1161
  };
638
1162
  }
639
1163
  mutexFor(messageId) {
@@ -679,7 +1203,7 @@ class LarkCardPatchNotifier {
679
1203
  pinned = true;
680
1204
  } catch (pinErr) {
681
1205
  pinError = pinErr.message;
682
- logger.warn(`Pin failed (non-fatal) for message ${messageId}: ${pinError}`);
1206
+ logger2.warn(`Pin failed (non-fatal) for message ${messageId}: ${pinError}`);
683
1207
  }
684
1208
  }
685
1209
  return { messageId, pinned, pinError };
@@ -687,7 +1211,23 @@ class LarkCardPatchNotifier {
687
1211
  async updateTask(opts) {
688
1212
  await this.mutexFor(opts.messageId).run(async () => {
689
1213
  if (this.finalisedCards.has(opts.messageId)) {
690
- 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");
691
1231
  return;
692
1232
  }
693
1233
  const state = this.states.get(opts.messageId) ?? {
@@ -772,6 +1312,29 @@ class LarkCardPatchNotifier {
772
1312
  }
773
1313
  async pauseTask(opts) {
774
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
+ }
775
1338
  const state = this.states.get(opts.messageId);
776
1339
  const now = new Date().toISOString();
777
1340
  const updateCount = (state?.updateCount ?? 0) + 1;
@@ -816,7 +1379,7 @@ class LarkCardPatchNotifier {
816
1379
  profile: opts.profile ?? state?.profile
817
1380
  }), "unpin");
818
1381
  } catch (unpinErr) {
819
- logger.warn(`Unpin failed (non-fatal) for ${opts.messageId}: ${unpinErr.message}`);
1382
+ logger2.warn(`Unpin failed (non-fatal) for ${opts.messageId}: ${unpinErr.message}`);
820
1383
  }
821
1384
  }
822
1385
  }
@@ -828,6 +1391,30 @@ class LarkCardPatchNotifier {
828
1391
  }
829
1392
  async finishTask(opts) {
830
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
+ }
831
1418
  const state = this.states.get(opts.messageId);
832
1419
  const now = new Date().toISOString();
833
1420
  const updateCount = (state?.updateCount ?? 0) + 1;
@@ -862,13 +1449,22 @@ class LarkCardPatchNotifier {
862
1449
  profile: opts.profile ?? state?.profile
863
1450
  }), "unpin");
864
1451
  } catch (unpinErr) {
865
- logger.warn(`Unpin failed (non-fatal) for ${opts.messageId}: ${unpinErr.message}`);
1452
+ logger2.warn(`Unpin failed (non-fatal) for ${opts.messageId}: ${unpinErr.message}`);
866
1453
  }
867
1454
  }
868
1455
  this.states.delete(opts.messageId);
869
1456
  this.mutexes.delete(opts.messageId);
870
1457
  }
871
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
+ }
872
1468
  const state = this.states.get(messageId);
873
1469
  if (!state)
874
1470
  return;
@@ -887,6 +1483,411 @@ class LarkCardPatchNotifier {
887
1483
  spinner: SPINNER_EMOJIS[state.spinnerIndex] ?? SPINNER_EMOJIS[0]
888
1484
  });
889
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
+ }
890
1891
  finalisedCards = new Set;
891
1892
  unfinaliseCard(messageId) {
892
1893
  this.finalisedCards.delete(messageId);
@@ -939,7 +1940,7 @@ class LarkCardPatchNotifier {
939
1940
  pinned = true;
940
1941
  } catch (pinErr) {
941
1942
  pinError = pinErr.message;
942
- logger.warn(`Pin failed (non-fatal) for workflow message ${messageId}: ${pinError}`);
1943
+ logger2.warn(`Pin failed (non-fatal) for workflow message ${messageId}: ${pinError}`);
943
1944
  }
944
1945
  }
945
1946
  return { messageId, pinned, pinError };
@@ -947,7 +1948,7 @@ class LarkCardPatchNotifier {
947
1948
  async updateWorkflow(opts) {
948
1949
  await this.mutexFor(opts.messageId).run(async () => {
949
1950
  if (this.finalisedCards.has(opts.messageId)) {
950
- logger.debug(`updateWorkflow: message ${opts.messageId} 已是终态卡片,忽略迟到的更新`);
1951
+ logger2.debug(`updateWorkflow: message ${opts.messageId} 已是终态卡片,忽略迟到的更新`);
951
1952
  return;
952
1953
  }
953
1954
  const state = this.workflowStates.get(opts.messageId) ?? {
@@ -1052,7 +2053,7 @@ class LarkCardPatchNotifier {
1052
2053
  profile: opts.profile ?? state?.profile
1053
2054
  }), "unpin");
1054
2055
  } catch (unpinErr) {
1055
- 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}`);
1056
2057
  }
1057
2058
  }
1058
2059
  this.workflowStates.delete(opts.messageId);
@@ -1372,5 +2373,17 @@ function sanitize(text) {
1372
2373
  return trimmed;
1373
2374
  return `${trimmed.slice(0, 500)}…`;
1374
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
+ }
1375
2388
 
1376
- 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 };