@ai-setting/roy-agent-core 1.7.85 → 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.
- package/dist/config/index.js +7 -7
- package/dist/env/agent/agent-component.d.ts +1 -1
- package/dist/env/agent/agent-component.js +13 -12
- package/dist/env/agent/index.js +13 -12
- package/dist/env/agent/types.d.ts +14 -0
- package/dist/env/commands/index.js +7 -7
- package/dist/env/context/index.js +3 -3
- package/dist/env/debug/index.js +7 -7
- package/dist/env/error-reply.js +1 -1
- package/dist/env/event-source/event-source-handlers.d.ts +18 -2
- package/dist/env/event-source/event-source-handlers.js +26 -21
- package/dist/env/event-source/event-store.js +2 -2
- package/dist/env/event-source/index.js +25 -22
- package/dist/env/event-source/timer-handler.d.ts +98 -0
- package/dist/env/event-source/types.d.ts +53 -1
- package/dist/env/event-source/wecom-card-action-handler.d.ts +235 -3
- package/dist/env/event-source/wecom-card-builder.d.ts +207 -1
- package/dist/env/event-source/wecom-cli-handler.d.ts +25 -0
- package/dist/{shared/@ai-setting/roy-agent-core-vyr6svt6.js → env/event-source/wecom-respond-registry.js} +8 -6
- package/dist/env/event-source/wecom-session-card-sender.d.ts +135 -0
- package/dist/env/event-source/wecom-session-card-sender.js +153 -0
- package/dist/env/hook/index.js +5 -5
- package/dist/env/index.js +49 -45
- package/dist/env/llm/index.js +9 -8
- package/dist/env/log-trace/index.js +7 -7
- package/dist/env/log-trace/trace-filters.d.ts +6 -1
- package/dist/env/mcp/index.js +7 -7
- package/dist/env/memory/index.js +7 -7
- package/dist/env/plugin/index.js +7 -7
- package/dist/env/prompt/index.js +8 -8
- package/dist/env/prompt/prompts-index.d.ts +1 -1
- package/dist/env/session/group-session-link.d.ts +99 -0
- package/dist/env/session/index.js +11 -10
- package/dist/env/session/session-component.d.ts +89 -0
- package/dist/env/session/storage/index.js +5 -4
- package/dist/env/session/types.d.ts +10 -0
- package/dist/env/skill/index.js +7 -7
- package/dist/env/task/delegate/delegate-channel-hint.d.ts +39 -0
- package/dist/env/task/delegate/delegate-tool.d.ts +71 -6
- package/dist/env/task/delegate/delegate-tool.js +11 -9
- package/dist/env/task/delegate/index.js +8 -8
- package/dist/env/task/index.js +18 -18
- package/dist/env/task/mcp/index.js +4 -4
- package/dist/env/task/plugins/card-subject.d.ts +117 -0
- package/dist/env/task/plugins/index.js +19 -17
- package/dist/env/task/plugins/lark-card-action-handler.d.ts +261 -6
- package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +501 -0
- package/dist/env/task/plugins/lark-card-patch-notifier.js +12 -2
- package/dist/env/task/plugins/lark-card-state-store.d.ts +60 -0
- package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +45 -0
- package/dist/env/task/plugins/sub-session-activity.d.ts +415 -0
- package/dist/env/task/plugins/wecom-session-card-store.d.ts +134 -0
- package/dist/env/task/plugins/wecom-session-card-store.js +23 -0
- package/dist/env/task/plugins/wecom-task-notify-plugin.d.ts +40 -0
- package/dist/env/task/storage/index.js +3 -3
- package/dist/env/task/tools/index.js +4 -4
- package/dist/env/tool/built-in/index.js +6 -4
- package/dist/env/tool/index.js +14 -10
- package/dist/env/tool/types.d.ts +25 -0
- package/dist/env/tool/validator.d.ts +8 -0
- package/dist/env/workflow/engine/index.js +12 -12
- package/dist/env/workflow/hooks/index.js +7 -7
- package/dist/env/workflow/index.js +24 -23
- package/dist/env/workflow/nodes/index.js +2 -2
- package/dist/env/workflow/plugins/index.js +10 -8
- package/dist/env/workflow/service/index.js +2 -2
- package/dist/env/workflow/storage/index.js +3 -3
- package/dist/env/workflow/tools/index.js +16 -15
- package/dist/env/workflow/utils/index.js +4 -4
- package/dist/index.js +65 -60
- package/dist/shared/@ai-setting/{roy-agent-core-dc9zq75z.js → roy-agent-core-0crmsc12.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-cha0jc7n.js → roy-agent-core-0sbmdz8d.js} +137 -16
- package/dist/shared/@ai-setting/{roy-agent-core-87942bw4.js → roy-agent-core-1mhyh4c5.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-v6vr6za7.js → roy-agent-core-1vv4fx15.js} +4 -4
- package/dist/shared/@ai-setting/{roy-agent-core-nwbfepba.js → roy-agent-core-3e040tc5.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-418zc9ha.js +288 -0
- package/dist/shared/@ai-setting/{roy-agent-core-r5wt1h3c.js → roy-agent-core-4fkxy2gv.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-na18zwj0.js → roy-agent-core-4t3yskp8.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-5pr4zp88.js → roy-agent-core-5kzhaap1.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-qq96p0tj.js → roy-agent-core-5s2t8z00.js} +22 -1
- package/dist/shared/@ai-setting/{roy-agent-core-6rh8k6ba.js → roy-agent-core-6gj1hc5z.js} +7 -7
- package/dist/shared/@ai-setting/{roy-agent-core-ezvpreqz.js → roy-agent-core-6k8mb7zx.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-g8htfwxj.js → roy-agent-core-7f10p7m2.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-7kvpc0w5.js +177 -0
- package/dist/shared/@ai-setting/{roy-agent-core-h21gngxf.js → roy-agent-core-7yh5wps0.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-efkjyh69.js → roy-agent-core-9710zwwz.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-rykf7c04.js → roy-agent-core-9c565frx.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-fghc2m8z.js → roy-agent-core-aa8kv18f.js} +34 -2
- package/dist/shared/@ai-setting/{roy-agent-core-p6ks90tb.js → roy-agent-core-an35dzbf.js} +6 -6
- package/dist/shared/@ai-setting/{roy-agent-core-7bpj859q.js → roy-agent-core-azyz78ky.js} +7 -7
- package/dist/shared/@ai-setting/{roy-agent-core-264qexac.js → roy-agent-core-b7h39vrc.js} +4 -4
- package/dist/shared/@ai-setting/{roy-agent-core-dqxec05w.js → roy-agent-core-bjtc9d35.js} +2 -2
- package/dist/shared/@ai-setting/roy-agent-core-c4ec7k0p.js +426 -0
- package/dist/shared/@ai-setting/{roy-agent-core-ddyk1rc0.js → roy-agent-core-cre2y2ct.js} +160 -4
- package/dist/shared/@ai-setting/{roy-agent-core-tr3ep81v.js → roy-agent-core-d6zdbgtc.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-0qyrk5k0.js → roy-agent-core-ds6qzmf8.js} +14 -5
- package/dist/shared/@ai-setting/{roy-agent-core-cgtq6427.js → roy-agent-core-e08bvpaq.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-12n08pg5.js → roy-agent-core-e69qxdyy.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-qk9hd89p.js → roy-agent-core-fan8g0z1.js} +10 -10
- package/dist/shared/@ai-setting/{roy-agent-core-xcp8vwax.js → roy-agent-core-fepm835e.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-6j02sbee.js → roy-agent-core-fhg03v43.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-dvtg4sn0.js → roy-agent-core-g8yr733d.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-xf6z96qy.js → roy-agent-core-gr5h1dyr.js} +818 -346
- package/dist/shared/@ai-setting/{roy-agent-core-m3vjepzz.js → roy-agent-core-h1e1fm9b.js} +38 -202
- package/dist/shared/@ai-setting/{roy-agent-core-c2gbt68e.js → roy-agent-core-h83zdsrq.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-tk98f92b.js → roy-agent-core-k3gcvybs.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-pgvcf91v.js → roy-agent-core-m39v7qpx.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-tm1mxttk.js → roy-agent-core-mf5p38yf.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-83bewqx8.js → roy-agent-core-mvh0td7p.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-ze5cqpf2.js → roy-agent-core-mx855td4.js} +17 -38
- package/dist/shared/@ai-setting/{roy-agent-core-0dh8npnz.js → roy-agent-core-n197medr.js} +13 -13
- package/dist/shared/@ai-setting/{roy-agent-core-cmprygrp.js → roy-agent-core-nqkfkpf4.js} +4 -4
- package/dist/shared/@ai-setting/{roy-agent-core-7cbpmk4q.js → roy-agent-core-p31ftgq9.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-vffccgf6.js → roy-agent-core-qwd2dq5q.js} +10 -3
- package/dist/shared/@ai-setting/{roy-agent-core-b31v3qpv.js → roy-agent-core-ra66sf50.js} +990 -211
- package/dist/shared/@ai-setting/{roy-agent-core-1v4g0d4x.js → roy-agent-core-rm7seqwb.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-7dd0s3xb.js → roy-agent-core-shb1qsyg.js} +1027 -14
- package/dist/shared/@ai-setting/{roy-agent-core-ax9e9szb.js → roy-agent-core-smf94h73.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-acj8ag08.js → roy-agent-core-tjnabyvq.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-qqaak24r.js → roy-agent-core-tkpp1ba0.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-dkrvmjsh.js → roy-agent-core-tpm1dd1g.js} +23 -15
- package/dist/shared/@ai-setting/{roy-agent-core-ed233k5z.js → roy-agent-core-v1n0106v.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-zxz5fkwj.js → roy-agent-core-v6yshv0z.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-mcy5apay.js → roy-agent-core-vf9sp6s3.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-q813vafd.js → roy-agent-core-vpcxv1ty.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-xvt7kf7p.js → roy-agent-core-w9ryeg2p.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-es203w7p.js → roy-agent-core-wqs6yypq.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-d1n4jbza.js → roy-agent-core-wr1qexzq.js} +5 -5
- package/dist/shared/@ai-setting/roy-agent-core-wxrkcb17.js +56 -0
- package/dist/shared/@ai-setting/{roy-agent-core-0snm5hc5.js → roy-agent-core-y6shd91r.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-9vxh6f8h.js → roy-agent-core-y7fqtzk2.js} +7 -7
- package/dist/shared/@ai-setting/{roy-agent-core-cpcd24n2.js → roy-agent-core-yet36q5k.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-3fh1f7s7.js → roy-agent-core-yfpnkc4z.js} +5 -5
- package/dist/shared/@ai-setting/{roy-agent-core-begm7ytz.js → roy-agent-core-yh68z1bg.js} +3 -3
- 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-6k8mb7zx.js";
|
|
1
9
|
import {
|
|
2
10
|
TracedAs,
|
|
3
11
|
init_decorator
|
|
4
|
-
} from "./roy-agent-core-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|