@ai-setting/roy-agent-core 1.7.77 → 1.7.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/config-protection.d.ts +89 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +16 -0
- package/dist/env/agent/agent-component.d.ts +42 -21
- package/dist/env/agent/agent-component.js +2 -2
- package/dist/env/agent/index.js +2 -2
- package/dist/env/agent/types.d.ts +32 -12
- package/dist/env/error-reply.d.ts +6 -0
- package/dist/env/error-reply.js +1 -1
- package/dist/env/event-source/bounty-im-handler.d.ts +8 -0
- package/dist/env/event-source/es-command.d.ts +39 -0
- package/dist/env/event-source/es-plugins.d.ts +68 -0
- package/dist/env/event-source/event-source-component.d.ts +15 -0
- package/dist/env/event-source/event-source-handlers.d.ts +68 -0
- package/dist/env/event-source/event-source-handlers.js +20 -8
- package/dist/env/event-source/index.d.ts +5 -1
- package/dist/env/event-source/index.js +57 -8
- package/dist/env/event-source/types.d.ts +101 -1
- package/dist/env/event-source/wecom-card-action-handler.d.ts +141 -0
- package/dist/env/event-source/wecom-card-builder.d.ts +220 -0
- package/dist/env/event-source/wecom-cli-handler.d.ts +266 -0
- package/dist/env/event-source/wecom-cli-sender.d.ts +88 -0
- package/dist/env/event-source/wecom-media.d.ts +130 -0
- package/dist/env/event-source/wecom-respond-registry.d.ts +151 -0
- package/dist/env/event-source/wecom-ws-bridge.d.ts +213 -0
- package/dist/env/index.js +16 -14
- package/dist/env/task/delegate/delegate-tool.d.ts +62 -2
- package/dist/env/task/delegate/delegate-tool.js +3 -1
- package/dist/env/task/delegate/index.js +1 -1
- package/dist/env/task/index.js +2 -2
- package/dist/env/task/plugins/index.d.ts +2 -0
- package/dist/env/task/plugins/index.js +23 -2
- package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +41 -2
- package/dist/env/task/plugins/lark-card-patch-notifier.js +1 -1
- package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +16 -0
- package/dist/env/task/plugins/wecom-card-state-store.d.ts +121 -0
- package/dist/env/task/plugins/wecom-task-notify-plugin.d.ts +224 -0
- package/dist/env/tool/built-in/index.d.ts +2 -0
- package/dist/env/tool/built-in/index.js +6 -1
- package/dist/env/tool/index.js +3 -2
- package/dist/env/workflow/index.js +3 -3
- package/dist/env/workflow/plugins/index.js +2 -2
- package/dist/env/workflow/tools/index.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +68 -17
- package/dist/shared/@ai-setting/{roy-agent-core-v87njmq7.js → roy-agent-core-0dh8npnz.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-2stt4rp5.js +134 -0
- package/dist/shared/@ai-setting/{roy-agent-core-mww0mnd9.js → roy-agent-core-7bpj859q.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-w0qjeq2v.js → roy-agent-core-7dd0s3xb.js} +14 -4
- package/dist/shared/@ai-setting/{roy-agent-core-rsn44n9y.js → roy-agent-core-b31v3qpv.js} +851 -46
- package/dist/shared/@ai-setting/{roy-agent-core-0v27y99n.js → roy-agent-core-begm7ytz.js} +63 -2
- package/dist/shared/@ai-setting/{roy-agent-core-aa2j7bk9.js → roy-agent-core-cha0jc7n.js} +40 -26
- package/dist/shared/@ai-setting/{roy-agent-core-hd3n57zs.js → roy-agent-core-d1n4jbza.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-9vhq7fvv.js → roy-agent-core-dkrvmjsh.js} +12 -74
- package/dist/shared/@ai-setting/{roy-agent-core-99nwgj84.js → roy-agent-core-fzw9xzm1.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-m3vjepzz.js +544 -0
- package/dist/shared/@ai-setting/{roy-agent-core-ew6qb5yx.js → roy-agent-core-p6ks90tb.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-rhtdspx6.js → roy-agent-core-qk9hd89p.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-6cy43nss.js → roy-agent-core-rykf7c04.js} +49 -9
- package/dist/shared/@ai-setting/{roy-agent-core-8ez6mccb.js → roy-agent-core-tk98f92b.js} +4 -0
- package/dist/shared/@ai-setting/{roy-agent-core-bxkjk7zx.js → roy-agent-core-vx8zc847.js} +4 -1
- package/dist/shared/@ai-setting/roy-agent-core-vyr6svt6.js +31 -0
- package/dist/shared/@ai-setting/{roy-agent-core-3gpygqcx.js → roy-agent-core-xf6z96qy.js} +2086 -163
- package/package.json +10 -1
- /package/dist/shared/@ai-setting/{roy-agent-core-b4x6x4k2.js → roy-agent-core-m1fgtnrc.js} +0 -0
- /package/dist/shared/@ai-setting/{roy-agent-core-v85n42ve.js → roy-agent-core-ze5cqpf2.js} +0 -0
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WecomCardActionKey,
|
|
3
|
+
buildWecomButtonInteractionCard,
|
|
4
|
+
buildWecomTextNoticeCard,
|
|
5
|
+
sendWecomReply,
|
|
6
|
+
sendWecomTemplateCard
|
|
7
|
+
} from "./roy-agent-core-m3vjepzz.js";
|
|
1
8
|
import {
|
|
2
9
|
getSharedEventStore
|
|
3
10
|
} from "./roy-agent-core-na18zwj0.js";
|
|
@@ -24,7 +31,7 @@ import {
|
|
|
24
31
|
import {
|
|
25
32
|
LarkCardPatchNotifier,
|
|
26
33
|
humanizeValue
|
|
27
|
-
} from "./roy-agent-core-
|
|
34
|
+
} from "./roy-agent-core-7dd0s3xb.js";
|
|
28
35
|
import {
|
|
29
36
|
validateWorkflowDefinition
|
|
30
37
|
} from "./roy-agent-core-1v4g0d4x.js";
|
|
@@ -38,7 +45,8 @@ import {
|
|
|
38
45
|
init_context
|
|
39
46
|
} from "./roy-agent-core-qqaak24r.js";
|
|
40
47
|
import {
|
|
41
|
-
getEnvContextOrEmpty
|
|
48
|
+
getEnvContextOrEmpty,
|
|
49
|
+
init_env_context
|
|
42
50
|
} from "./roy-agent-core-7cbpmk4q.js";
|
|
43
51
|
import {
|
|
44
52
|
TracedAs,
|
|
@@ -2384,6 +2392,66 @@ class LarkCliTaskNotifyHook {
|
|
|
2384
2392
|
logger7.warn(`repinAfterResume failed (non-fatal) for task #${task.id}: ${err.message}`);
|
|
2385
2393
|
}
|
|
2386
2394
|
}
|
|
2395
|
+
async reopenReuseCard(task, persisted, changes) {
|
|
2396
|
+
if (!this.cardNotifier)
|
|
2397
|
+
return false;
|
|
2398
|
+
const messageId = persisted.messageId;
|
|
2399
|
+
if (!messageId) {
|
|
2400
|
+
logger7.debug(`reopenReuseCard: task #${task.id} 记录无 messageId → 走建卡路径`);
|
|
2401
|
+
return false;
|
|
2402
|
+
}
|
|
2403
|
+
const ctx = getEnvContextOrEmpty();
|
|
2404
|
+
const profile = this.resolveProfile(task, ctx);
|
|
2405
|
+
this.cardNotifier.unfinaliseCard?.(messageId);
|
|
2406
|
+
const reopenMilestone = {
|
|
2407
|
+
title: "\uD83D\uDD04 任务重新打开",
|
|
2408
|
+
timestamp: new Date().toISOString(),
|
|
2409
|
+
description: task.current_status || undefined
|
|
2410
|
+
};
|
|
2411
|
+
const changeKeys = Object.keys(changes ?? {});
|
|
2412
|
+
const reopenSteps = changeKeys.length > 0 ? [
|
|
2413
|
+
{
|
|
2414
|
+
title: "任务重新打开",
|
|
2415
|
+
timestamp: new Date().toISOString(),
|
|
2416
|
+
description: changeKeys.map((k) => `${k}=${humanizeValue(changes[k], 80)}`).join(" · ")
|
|
2417
|
+
}
|
|
2418
|
+
] : [];
|
|
2419
|
+
const hasState = this.cardNotifier.exportState?.(messageId) !== undefined;
|
|
2420
|
+
if (!hasState) {
|
|
2421
|
+
const persistedRec = this.cardStateStore?.get(task.id);
|
|
2422
|
+
if (persistedRec?.timeline) {
|
|
2423
|
+
this.hydrateNotifierState(persistedRec);
|
|
2424
|
+
logger7.debug(`reopenReuseCard: task #${task.id} 内存态缺失 → ` + `从持久层快照恢复卡片内容(历史时间线保留)`);
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
const merged = this.cardNotifier.mergeTimelineState?.(messageId, [reopenMilestone], reopenSteps) ?? false;
|
|
2428
|
+
try {
|
|
2429
|
+
await this.cardNotifier.updateTask({
|
|
2430
|
+
messageId,
|
|
2431
|
+
profile,
|
|
2432
|
+
status: "running",
|
|
2433
|
+
meta: this.buildTaskMeta(task),
|
|
2434
|
+
progress: typeof task.progress === "number" && task.progress > 0 ? task.progress : undefined,
|
|
2435
|
+
milestones: merged ? [] : [reopenMilestone],
|
|
2436
|
+
processSteps: merged ? [] : reopenSteps
|
|
2437
|
+
});
|
|
2438
|
+
} catch (err) {
|
|
2439
|
+
logger7.warn(`reopenReuseCard: task #${task.id} 原卡 patch 失败: ` + `${err.message}`);
|
|
2440
|
+
return false;
|
|
2441
|
+
}
|
|
2442
|
+
this.persistReopened(task.id);
|
|
2443
|
+
this.refreshTimeline(task.id);
|
|
2444
|
+
try {
|
|
2445
|
+
await this.repinAfterResume(task, {
|
|
2446
|
+
...persisted,
|
|
2447
|
+
status: "open"
|
|
2448
|
+
});
|
|
2449
|
+
} catch (pinErr) {
|
|
2450
|
+
logger7.warn(`reopenReuseCard: task #${task.id} 重新 pin 失败(非致命,卡片内容已更新): ` + `${pinErr.message}`);
|
|
2451
|
+
}
|
|
2452
|
+
logger7.info(`reopenReuseCard: task #${task.id} 已复用原卡 ${messageId} ` + `(patch + reopen 里程碑 + 重新 pin)`);
|
|
2453
|
+
return true;
|
|
2454
|
+
}
|
|
2387
2455
|
buildCardState(task, messageId, chatId, profile, pinned) {
|
|
2388
2456
|
let existing;
|
|
2389
2457
|
try {
|
|
@@ -2542,7 +2610,12 @@ class LarkCliTaskNotifyHook {
|
|
|
2542
2610
|
logger7.debug(`sendCardUpdate: task #${task.id} 卡片已终结(closed)且任务仍终态 → skip patch`);
|
|
2543
2611
|
return;
|
|
2544
2612
|
}
|
|
2545
|
-
|
|
2613
|
+
const reused = await this.reopenReuseCard(task, persisted, changes);
|
|
2614
|
+
if (reused) {
|
|
2615
|
+
this.cardMessageIds.set(task.id, persisted.messageId);
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
logger7.info(`sendCardUpdate: task #${task.id} 已 reopen 但原卡 patch 失败 → 降级发新卡`);
|
|
2546
2619
|
this.cardMessageIds.delete(task.id);
|
|
2547
2620
|
this.dropStaleMessageId(task.id);
|
|
2548
2621
|
}
|
|
@@ -2605,7 +2678,7 @@ class LarkCliTaskNotifyHook {
|
|
|
2605
2678
|
messageId,
|
|
2606
2679
|
chatId,
|
|
2607
2680
|
profile,
|
|
2608
|
-
status: "running",
|
|
2681
|
+
status: String(task.status) === "to_validate" ? "to_validate" : "running",
|
|
2609
2682
|
meta: this.buildTaskMeta(task),
|
|
2610
2683
|
progress: typeof task.progress === "number" && task.progress > 0 ? task.progress : undefined,
|
|
2611
2684
|
milestones: [],
|
|
@@ -2770,12 +2843,744 @@ __legacyDecorateClassTS([
|
|
|
2770
2843
|
function createLarkCliTaskNotifyHook(config = {}) {
|
|
2771
2844
|
return new LarkCliTaskNotifyHook(config);
|
|
2772
2845
|
}
|
|
2846
|
+
// src/env/task/plugins/wecom-task-notify-plugin.ts
|
|
2847
|
+
init_logger();
|
|
2848
|
+
init_env_context();
|
|
2849
|
+
|
|
2850
|
+
// src/env/task/plugins/wecom-card-state-store.ts
|
|
2851
|
+
init_logger();
|
|
2852
|
+
init_sqlite_task_store();
|
|
2853
|
+
import { join as join2 } from "path";
|
|
2854
|
+
var logger8 = createLogger("wecom:card-state");
|
|
2855
|
+
|
|
2856
|
+
class MemoryWecomCardStateStore {
|
|
2857
|
+
records = new Map;
|
|
2858
|
+
upsert(state) {
|
|
2859
|
+
this.records.set(state.taskId, { ...state });
|
|
2860
|
+
}
|
|
2861
|
+
get(taskId) {
|
|
2862
|
+
const r = this.records.get(taskId);
|
|
2863
|
+
return r ? { ...r } : undefined;
|
|
2864
|
+
}
|
|
2865
|
+
list() {
|
|
2866
|
+
return [...this.records.values()].map((r) => ({ ...r }));
|
|
2867
|
+
}
|
|
2868
|
+
markPaused(taskId) {
|
|
2869
|
+
const r = this.records.get(taskId);
|
|
2870
|
+
if (!r)
|
|
2871
|
+
return;
|
|
2872
|
+
r.status = "paused";
|
|
2873
|
+
r.updatedAt = Date.now();
|
|
2874
|
+
}
|
|
2875
|
+
markReopened(taskId) {
|
|
2876
|
+
const r = this.records.get(taskId);
|
|
2877
|
+
if (!r)
|
|
2878
|
+
return;
|
|
2879
|
+
r.status = "open";
|
|
2880
|
+
delete r.closedAt;
|
|
2881
|
+
r.updatedAt = Date.now();
|
|
2882
|
+
}
|
|
2883
|
+
markClosed(taskId) {
|
|
2884
|
+
const r = this.records.get(taskId);
|
|
2885
|
+
if (!r)
|
|
2886
|
+
return;
|
|
2887
|
+
const now = Date.now();
|
|
2888
|
+
r.status = "closed";
|
|
2889
|
+
r.closedAt = now;
|
|
2890
|
+
r.updatedAt = now;
|
|
2891
|
+
}
|
|
2892
|
+
delete(taskId) {
|
|
2893
|
+
this.records.delete(taskId);
|
|
2894
|
+
}
|
|
2895
|
+
close() {
|
|
2896
|
+
this.records.clear();
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
function createMemoryWecomCardStore() {
|
|
2900
|
+
return new MemoryWecomCardStateStore;
|
|
2901
|
+
}
|
|
2902
|
+
function rowToState2(row) {
|
|
2903
|
+
const state = {
|
|
2904
|
+
taskId: row.task_id,
|
|
2905
|
+
chatId: row.chat_id,
|
|
2906
|
+
pinned: row.pinned === 1,
|
|
2907
|
+
status: row.status ?? "open",
|
|
2908
|
+
createdAt: row.created_at,
|
|
2909
|
+
updatedAt: row.updated_at,
|
|
2910
|
+
updateCount: row.update_count ?? 0
|
|
2911
|
+
};
|
|
2912
|
+
if (row.closed_at !== null && row.closed_at !== undefined) {
|
|
2913
|
+
state.closedAt = row.closed_at;
|
|
2914
|
+
}
|
|
2915
|
+
if (row.timeline)
|
|
2916
|
+
state.timeline = row.timeline;
|
|
2917
|
+
return state;
|
|
2918
|
+
}
|
|
2919
|
+
|
|
2920
|
+
class SqliteWecomCardStateStore {
|
|
2921
|
+
db;
|
|
2922
|
+
constructor(dbPath) {
|
|
2923
|
+
if (dbPath !== ":memory:") {
|
|
2924
|
+
const { mkdirSync } = __require("fs");
|
|
2925
|
+
const dir = __require("path").dirname(dbPath);
|
|
2926
|
+
try {
|
|
2927
|
+
mkdirSync(dir, { recursive: true });
|
|
2928
|
+
} catch {}
|
|
2929
|
+
}
|
|
2930
|
+
this.db = createCompatibleDatabase(dbPath);
|
|
2931
|
+
try {
|
|
2932
|
+
this.db.run("PRAGMA journal_mode = WAL");
|
|
2933
|
+
this.db.run("PRAGMA busy_timeout = 5000");
|
|
2934
|
+
this.db.run("PRAGMA synchronous = NORMAL");
|
|
2935
|
+
} catch {}
|
|
2936
|
+
this.db.run(`
|
|
2937
|
+
CREATE TABLE IF NOT EXISTS wecom_card (
|
|
2938
|
+
task_id INTEGER PRIMARY KEY,
|
|
2939
|
+
chat_id TEXT NOT NULL,
|
|
2940
|
+
pinned INTEGER NOT NULL DEFAULT 0,
|
|
2941
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
2942
|
+
created_at INTEGER NOT NULL,
|
|
2943
|
+
updated_at INTEGER NOT NULL,
|
|
2944
|
+
closed_at INTEGER,
|
|
2945
|
+
update_count INTEGER NOT NULL DEFAULT 0,
|
|
2946
|
+
timeline TEXT
|
|
2947
|
+
)
|
|
2948
|
+
`);
|
|
2949
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_wecom_card_status ON wecom_card(status)");
|
|
2950
|
+
}
|
|
2951
|
+
upsert(state) {
|
|
2952
|
+
this.db.prepare(`INSERT INTO wecom_card
|
|
2953
|
+
(task_id, chat_id, pinned, status, created_at, updated_at, closed_at, update_count, timeline)
|
|
2954
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2955
|
+
ON CONFLICT(task_id) DO UPDATE SET
|
|
2956
|
+
chat_id = excluded.chat_id,
|
|
2957
|
+
pinned = excluded.pinned,
|
|
2958
|
+
status = excluded.status,
|
|
2959
|
+
updated_at = excluded.updated_at,
|
|
2960
|
+
closed_at = excluded.closed_at,
|
|
2961
|
+
update_count = excluded.update_count,
|
|
2962
|
+
timeline = excluded.timeline`).run(state.taskId, state.chatId, state.pinned ? 1 : 0, state.status, state.createdAt, state.updatedAt, state.closedAt ?? null, state.updateCount, state.timeline ?? null);
|
|
2963
|
+
}
|
|
2964
|
+
get(taskId) {
|
|
2965
|
+
const row = this.db.prepare("SELECT * FROM wecom_card WHERE task_id = ?").get(taskId);
|
|
2966
|
+
return row ? rowToState2(row) : undefined;
|
|
2967
|
+
}
|
|
2968
|
+
list() {
|
|
2969
|
+
const rows = this.db.prepare("SELECT * FROM wecom_card ORDER BY task_id ASC").all();
|
|
2970
|
+
return rows.map(rowToState2);
|
|
2971
|
+
}
|
|
2972
|
+
markPaused(taskId) {
|
|
2973
|
+
this.db.prepare("UPDATE wecom_card SET status = 'paused', updated_at = ? WHERE task_id = ?").run(Date.now(), taskId);
|
|
2974
|
+
}
|
|
2975
|
+
markReopened(taskId) {
|
|
2976
|
+
this.db.prepare("UPDATE wecom_card SET status = 'open', closed_at = NULL, updated_at = ? WHERE task_id = ?").run(Date.now(), taskId);
|
|
2977
|
+
}
|
|
2978
|
+
markClosed(taskId) {
|
|
2979
|
+
const now = Date.now();
|
|
2980
|
+
this.db.prepare("UPDATE wecom_card SET status = 'closed', closed_at = ?, updated_at = ? WHERE task_id = ?").run(now, now, taskId);
|
|
2981
|
+
}
|
|
2982
|
+
delete(taskId) {
|
|
2983
|
+
this.db.prepare("DELETE FROM wecom_card WHERE task_id = ?").run(taskId);
|
|
2984
|
+
}
|
|
2985
|
+
close() {
|
|
2986
|
+
try {
|
|
2987
|
+
this.db.close?.();
|
|
2988
|
+
} catch {}
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
function getDefaultWecomCardDbPath() {
|
|
2992
|
+
const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
|
|
2993
|
+
return join2(home, ".local", "share", "roy-agent", "wecom-cards.db");
|
|
2994
|
+
}
|
|
2995
|
+
function createWecomCardStateStore(dbPath) {
|
|
2996
|
+
const isTestEnv = "BUN_TEST_WORKER_ID" in process.env;
|
|
2997
|
+
if (!dbPath && !process.env.ROY_AGENT_WECOM_CARD_DB && isTestEnv) {
|
|
2998
|
+
return new MemoryWecomCardStateStore;
|
|
2999
|
+
}
|
|
3000
|
+
const path = dbPath ?? process.env.ROY_AGENT_WECOM_CARD_DB ?? getDefaultWecomCardDbPath();
|
|
3001
|
+
try {
|
|
3002
|
+
return new SqliteWecomCardStateStore(path);
|
|
3003
|
+
} catch (err) {
|
|
3004
|
+
logger8.warn(`WecomCardStateStore: sqlite unavailable at ${path} (${err.message}) ` + `→ falling back to in-memory (卡片仍可发,但重启后时间线丢失)`);
|
|
3005
|
+
return new MemoryWecomCardStateStore;
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
// src/env/task/plugins/wecom-task-notify-plugin.ts
|
|
3010
|
+
var logger9 = createLogger("wecom:task-notify");
|
|
3011
|
+
var WecomNotifyType = {
|
|
3012
|
+
TASK_CREATED: "task_created",
|
|
3013
|
+
TASK_UPDATED: "task_updated",
|
|
3014
|
+
TASK_COMPLETED: "task_completed",
|
|
3015
|
+
OPERATION_CREATED: "operation_created"
|
|
3016
|
+
};
|
|
3017
|
+
var WECOM_EVENT_TYPE = "event-source.event.wecom-cli";
|
|
3018
|
+
var WECOM_CHANNEL_TYPE = "wecom-cli";
|
|
3019
|
+
function extractWecomFields(env, fallbackSourceId) {
|
|
3020
|
+
const evt = env.envEvent;
|
|
3021
|
+
const rcType = env.replyChannel?.type;
|
|
3022
|
+
const isWecom = evt?.type === WECOM_EVENT_TYPE || rcType === WECOM_CHANNEL_TYPE;
|
|
3023
|
+
if (!isWecom)
|
|
3024
|
+
return { isWecom: false };
|
|
3025
|
+
const chatId = env.replyChannel?.chatId || (typeof evt?.metadata?.chatId === "string" ? evt.metadata.chatId : undefined);
|
|
3026
|
+
const sourceId = (typeof evt?.metadata?.sourceId === "string" ? evt.metadata.sourceId : undefined) ?? fallbackSourceId ?? undefined;
|
|
3027
|
+
return { isWecom: true, chatId, sourceId };
|
|
3028
|
+
}
|
|
3029
|
+
function statusLabelOf(task, terminalStatus) {
|
|
3030
|
+
const s = String(terminalStatus ?? task.status ?? "");
|
|
3031
|
+
switch (s) {
|
|
3032
|
+
case "todo":
|
|
3033
|
+
return "\uD83D\uDCDD 待办";
|
|
3034
|
+
case "active":
|
|
3035
|
+
return "\uD83D\uDD04 进行中";
|
|
3036
|
+
case "to_validate":
|
|
3037
|
+
return "\uD83D\uDD0D 待验证";
|
|
3038
|
+
case "completed":
|
|
3039
|
+
return "\uD83C\uDF89 已完成";
|
|
3040
|
+
case "paused":
|
|
3041
|
+
return "⏸️ 已暂停";
|
|
3042
|
+
case "cancelled":
|
|
3043
|
+
return "\uD83D\uDEAB 已取消";
|
|
3044
|
+
case "archived":
|
|
3045
|
+
return "\uD83D\uDCE6 已归档";
|
|
3046
|
+
case "failed":
|
|
3047
|
+
case "timeout":
|
|
3048
|
+
return "\uD83D\uDEA8 已失败";
|
|
3049
|
+
default:
|
|
3050
|
+
return "\uD83D\uDD04 进行中";
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
function isTerminalUpdate(task, changes) {
|
|
3054
|
+
const effective = String(changes?.status ?? task.status ?? "");
|
|
3055
|
+
return effective === "completed" || effective === "cancelled" || effective === "failed" || effective === "timeout" || effective === "archived" || effective === "paused";
|
|
3056
|
+
}
|
|
3057
|
+
function isHardTerminal(status) {
|
|
3058
|
+
return status === "completed" || status === "cancelled" || status === "failed" || status === "timeout" || status === "archived";
|
|
3059
|
+
}
|
|
3060
|
+
function clip(text, max = 200) {
|
|
3061
|
+
if (!text)
|
|
3062
|
+
return "";
|
|
3063
|
+
const t = text.trim();
|
|
3064
|
+
return t.length > max ? `${t.slice(0, max)}…` : t;
|
|
3065
|
+
}
|
|
3066
|
+
function buildWecomTaskCard(input) {
|
|
3067
|
+
const { task, statusLabel, milestones } = input;
|
|
3068
|
+
const lines = [];
|
|
3069
|
+
lines.push(`### \uD83D\uDCCB Task #${task.id} · ${task.title}`);
|
|
3070
|
+
lines.push("");
|
|
3071
|
+
const progress = typeof task.progress === "number" && task.progress > 0 ? `(${task.progress}%)` : "";
|
|
3072
|
+
lines.push(`**状态**:${statusLabel}${progress}`);
|
|
3073
|
+
if (input.terminal) {
|
|
3074
|
+
lines.push(`---`);
|
|
3075
|
+
}
|
|
3076
|
+
lines.push("");
|
|
3077
|
+
const desc = clip(task.description, 120);
|
|
3078
|
+
if (desc) {
|
|
3079
|
+
lines.push(`> ${desc}`);
|
|
3080
|
+
lines.push("");
|
|
3081
|
+
}
|
|
3082
|
+
if (input.changeSummary) {
|
|
3083
|
+
lines.push(`**本次更新**:${clip(input.changeSummary, 120)}`);
|
|
3084
|
+
lines.push("");
|
|
3085
|
+
}
|
|
3086
|
+
if (milestones.length > 0) {
|
|
3087
|
+
lines.push(`**时间线**`);
|
|
3088
|
+
for (const m of milestones) {
|
|
3089
|
+
const ts = formatTs(m.timestamp);
|
|
3090
|
+
const d = m.description ? `:${clip(m.description, 100)}` : "";
|
|
3091
|
+
lines.push(`- \uD83D\uDD50 ${ts} — ${m.title}${d}`);
|
|
3092
|
+
}
|
|
3093
|
+
lines.push("");
|
|
3094
|
+
}
|
|
3095
|
+
return lines.join(`
|
|
3096
|
+
`).trim();
|
|
3097
|
+
}
|
|
3098
|
+
function formatTs(iso) {
|
|
3099
|
+
try {
|
|
3100
|
+
const d = new Date(iso);
|
|
3101
|
+
if (isNaN(d.getTime()))
|
|
3102
|
+
return iso;
|
|
3103
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
3104
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` + `${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
3105
|
+
} catch {
|
|
3106
|
+
return iso;
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
function milestoneFromOperation(op) {
|
|
3110
|
+
const raw = op;
|
|
3111
|
+
const title = op.milestoneTitle ?? raw.action_title ?? String(op.milestoneType ?? "里程碑");
|
|
3112
|
+
const detail = op.milestoneDescription ?? raw.action_description ?? undefined;
|
|
3113
|
+
return {
|
|
3114
|
+
title,
|
|
3115
|
+
timestamp: op.timestamp ?? new Date().toISOString(),
|
|
3116
|
+
description: detail ?? op.processDescription ?? undefined
|
|
3117
|
+
};
|
|
3118
|
+
}
|
|
3119
|
+
|
|
3120
|
+
class WecomTaskNotifyHook {
|
|
3121
|
+
name;
|
|
3122
|
+
priority;
|
|
3123
|
+
description;
|
|
3124
|
+
pluginName;
|
|
3125
|
+
sourceId;
|
|
3126
|
+
config;
|
|
3127
|
+
sender;
|
|
3128
|
+
cardStateStore;
|
|
3129
|
+
lastNotifyTimestamps = new Map;
|
|
3130
|
+
constructor(config = {}) {
|
|
3131
|
+
this.name = config.pluginName || "WecomTaskNotifyHook";
|
|
3132
|
+
this.priority = config.priority ?? 5;
|
|
3133
|
+
this.description = "Wecom 任务进展通知 Hook(markdown 卡片)";
|
|
3134
|
+
this.pluginName = config.pluginName;
|
|
3135
|
+
this.sourceId = config.sourceId;
|
|
3136
|
+
this.config = {
|
|
3137
|
+
enabled: true,
|
|
3138
|
+
priority: 5,
|
|
3139
|
+
notifyTypes: [
|
|
3140
|
+
WecomNotifyType.TASK_CREATED,
|
|
3141
|
+
WecomNotifyType.TASK_UPDATED,
|
|
3142
|
+
WecomNotifyType.TASK_COMPLETED,
|
|
3143
|
+
WecomNotifyType.OPERATION_CREATED
|
|
3144
|
+
],
|
|
3145
|
+
...config
|
|
3146
|
+
};
|
|
3147
|
+
this.sender = config.sender ?? {
|
|
3148
|
+
send: (input) => this.defaultSend(input)
|
|
3149
|
+
};
|
|
3150
|
+
this.cardStateStore = config.cardStateStore ?? createWecomCardStateStore();
|
|
3151
|
+
}
|
|
3152
|
+
async defaultSend(input) {
|
|
3153
|
+
const env = getEnvContextOrEmpty();
|
|
3154
|
+
const { sourceId } = extractWecomFields(env, this.sourceId);
|
|
3155
|
+
return sendWecomReply({
|
|
3156
|
+
sourceId: sourceId ?? "unknown",
|
|
3157
|
+
rawChatId: input.chatId,
|
|
3158
|
+
content: input.content
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3161
|
+
async execute(ctx) {
|
|
3162
|
+
if (!this.config.enabled)
|
|
3163
|
+
return;
|
|
3164
|
+
const env = getEnvContextOrEmpty();
|
|
3165
|
+
if (this.pluginName && this.sourceId) {
|
|
3166
|
+
const key = `${this.sourceId}:${this.pluginName}`;
|
|
3167
|
+
if (env.pluginEnabled && env.pluginEnabled[key] !== true) {
|
|
3168
|
+
logger9.debug(`Plugin not enabled for source: ${key}`);
|
|
3169
|
+
return;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
const fields = extractWecomFields(env, this.sourceId);
|
|
3173
|
+
if (!fields.isWecom) {
|
|
3174
|
+
logger9.debug("not a wecom event, skip");
|
|
3175
|
+
return;
|
|
3176
|
+
}
|
|
3177
|
+
switch (ctx.hookPoint) {
|
|
3178
|
+
case TaskHookPoints.AFTER_CREATE:
|
|
3179
|
+
await this.onAfterCreate(ctx);
|
|
3180
|
+
break;
|
|
3181
|
+
case TaskHookPoints.AFTER_UPDATE:
|
|
3182
|
+
await this.onAfterUpdate(ctx);
|
|
3183
|
+
break;
|
|
3184
|
+
case TaskHookPoints.AFTER_COMPLETE:
|
|
3185
|
+
await this.onAfterComplete(ctx);
|
|
3186
|
+
break;
|
|
3187
|
+
case TaskHookPoints.OPERATION_AFTER_CREATE:
|
|
3188
|
+
await this.onAfterOperationCreate(ctx);
|
|
3189
|
+
break;
|
|
3190
|
+
case TaskHookPoints.AFTER_DELETE:
|
|
3191
|
+
await this.onAfterDelete(ctx);
|
|
3192
|
+
break;
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
async onAfterCreate(ctx) {
|
|
3196
|
+
const { task } = ctx.data;
|
|
3197
|
+
if (!task || !this.shouldNotify(WecomNotifyType.TASK_CREATED))
|
|
3198
|
+
return;
|
|
3199
|
+
await this.sendCardStart(task);
|
|
3200
|
+
}
|
|
3201
|
+
async onAfterUpdate(ctx) {
|
|
3202
|
+
const data = ctx.data;
|
|
3203
|
+
if (!data?.task)
|
|
3204
|
+
return;
|
|
3205
|
+
const { task, changes } = data;
|
|
3206
|
+
const changeKeys = Object.keys(changes ?? {});
|
|
3207
|
+
if (changeKeys.length === 0)
|
|
3208
|
+
return;
|
|
3209
|
+
if (isTerminalUpdate(task, changes)) {
|
|
3210
|
+
if (!this.shouldNotify(WecomNotifyType.TASK_COMPLETED))
|
|
3211
|
+
return;
|
|
3212
|
+
await this.sendCardFinish(task, String(changes?.status ?? task.status));
|
|
3213
|
+
return;
|
|
3214
|
+
}
|
|
3215
|
+
if (!this.shouldNotify(WecomNotifyType.TASK_UPDATED))
|
|
3216
|
+
return;
|
|
3217
|
+
await this.sendCardUpdate(task, changes);
|
|
3218
|
+
}
|
|
3219
|
+
async onAfterComplete(ctx) {
|
|
3220
|
+
const data = ctx.data;
|
|
3221
|
+
const task = data?.task ?? data;
|
|
3222
|
+
if (!task || !this.shouldNotify(WecomNotifyType.TASK_COMPLETED))
|
|
3223
|
+
return;
|
|
3224
|
+
const terminalStatus = data.terminalStatus;
|
|
3225
|
+
if (terminalStatus === "to_validate" || task.status === "to_validate") {
|
|
3226
|
+
return;
|
|
3227
|
+
}
|
|
3228
|
+
await this.sendCardFinish(task, String(task.status));
|
|
3229
|
+
}
|
|
3230
|
+
async onAfterDelete(ctx) {
|
|
3231
|
+
const data = ctx.data;
|
|
3232
|
+
const taskId = data?.id;
|
|
3233
|
+
if (data?.deleted !== true || typeof taskId !== "number")
|
|
3234
|
+
return;
|
|
3235
|
+
if (!this.shouldNotify(WecomNotifyType.TASK_COMPLETED))
|
|
3236
|
+
return;
|
|
3237
|
+
const rec = this.getRecord(taskId);
|
|
3238
|
+
if (!rec) {
|
|
3239
|
+
logger9.debug(`onAfterDelete: task #${taskId} 无卡片记录,skip`);
|
|
3240
|
+
return;
|
|
3241
|
+
}
|
|
3242
|
+
const milestones = this.parseTimeline(rec.timeline);
|
|
3243
|
+
milestones.push({
|
|
3244
|
+
title: "任务已取消(删除)",
|
|
3245
|
+
timestamp: new Date().toISOString()
|
|
3246
|
+
});
|
|
3247
|
+
await this.deliver({
|
|
3248
|
+
chatId: rec.chatId,
|
|
3249
|
+
task: {
|
|
3250
|
+
id: taskId,
|
|
3251
|
+
title: `(已删除)`,
|
|
3252
|
+
description: "",
|
|
3253
|
+
status: "cancelled",
|
|
3254
|
+
progress: 0
|
|
3255
|
+
},
|
|
3256
|
+
statusLabel: statusLabelOf({ status: "cancelled" }),
|
|
3257
|
+
milestones,
|
|
3258
|
+
terminal: true
|
|
3259
|
+
});
|
|
3260
|
+
this.cardStateStore.markClosed(taskId);
|
|
3261
|
+
logger9.debug(`onAfterDelete: task #${taskId} 卡片已收尾`);
|
|
3262
|
+
}
|
|
3263
|
+
async onAfterOperationCreate(ctx) {
|
|
3264
|
+
const { operation } = ctx.data;
|
|
3265
|
+
if (!operation)
|
|
3266
|
+
return;
|
|
3267
|
+
if (!this.shouldNotify(WecomNotifyType.OPERATION_CREATED))
|
|
3268
|
+
return;
|
|
3269
|
+
const rec = this.getRecord(operation.taskId);
|
|
3270
|
+
if (!rec) {
|
|
3271
|
+
logger9.debug(`onAfterOperationCreate: task #${operation.taskId} 无卡片记录,skip`);
|
|
3272
|
+
return;
|
|
3273
|
+
}
|
|
3274
|
+
if (rec.status !== "open") {
|
|
3275
|
+
logger9.debug(`onAfterOperationCreate: task #${operation.taskId} status=${rec.status}, skip`);
|
|
3276
|
+
return;
|
|
3277
|
+
}
|
|
3278
|
+
const milestone = milestoneFromOperation(operation);
|
|
3279
|
+
const milestones = this.parseTimeline(rec.timeline);
|
|
3280
|
+
milestones.push(milestone);
|
|
3281
|
+
const trimmed = this.trimTimeline(milestones);
|
|
3282
|
+
this.persistTimeline(operation.taskId, rec, trimmed, rec.updateCount);
|
|
3283
|
+
logger9.debug(`[single-card] OPERATION_CREATED for task ` + `#${operation.taskId} merged into timeline`);
|
|
3284
|
+
}
|
|
3285
|
+
async sendCardStart(task) {
|
|
3286
|
+
const env = getEnvContextOrEmpty();
|
|
3287
|
+
const fields = extractWecomFields(env, this.sourceId);
|
|
3288
|
+
const chatId = fields.chatId;
|
|
3289
|
+
if (!chatId) {
|
|
3290
|
+
logger9.debug(`sendCardStart: no chatId for task #${task.id}, skip`);
|
|
3291
|
+
return;
|
|
3292
|
+
}
|
|
3293
|
+
const existing = this.getRecord(task.id);
|
|
3294
|
+
if (existing?.status === "open") {
|
|
3295
|
+
logger9.debug(`sendCardStart: task #${task.id} 已有 open 记录 → 跳过重复发卡`);
|
|
3296
|
+
return;
|
|
3297
|
+
}
|
|
3298
|
+
const milestones = [
|
|
3299
|
+
{
|
|
3300
|
+
title: "Task 创建",
|
|
3301
|
+
timestamp: new Date().toISOString(),
|
|
3302
|
+
description: task.current_status || undefined
|
|
3303
|
+
}
|
|
3304
|
+
];
|
|
3305
|
+
const ok = await this.deliver({
|
|
3306
|
+
chatId,
|
|
3307
|
+
task,
|
|
3308
|
+
statusLabel: statusLabelOf(task),
|
|
3309
|
+
milestones
|
|
3310
|
+
});
|
|
3311
|
+
if (ok) {
|
|
3312
|
+
this.cardStateStore.upsert({
|
|
3313
|
+
taskId: task.id,
|
|
3314
|
+
chatId,
|
|
3315
|
+
pinned: false,
|
|
3316
|
+
status: "open",
|
|
3317
|
+
createdAt: Date.now(),
|
|
3318
|
+
updatedAt: Date.now(),
|
|
3319
|
+
updateCount: 1,
|
|
3320
|
+
timeline: JSON.stringify(milestones)
|
|
3321
|
+
});
|
|
3322
|
+
} else {
|
|
3323
|
+
this.cardStateStore.upsert({
|
|
3324
|
+
taskId: task.id,
|
|
3325
|
+
chatId,
|
|
3326
|
+
pinned: false,
|
|
3327
|
+
status: "open",
|
|
3328
|
+
createdAt: Date.now(),
|
|
3329
|
+
updatedAt: Date.now(),
|
|
3330
|
+
updateCount: 0,
|
|
3331
|
+
timeline: JSON.stringify(milestones)
|
|
3332
|
+
});
|
|
3333
|
+
logger9.warn(`sendCardStart: task #${task.id} 首卡发送失败(已落库待重试)`);
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
async sendCardUpdate(task, changes) {
|
|
3337
|
+
const rec = this.getRecord(task.id);
|
|
3338
|
+
if (!rec) {
|
|
3339
|
+
if (isHardTerminal(String(task.status))) {
|
|
3340
|
+
logger9.debug(`sendCardUpdate: task #${task.id} 终态且无卡片 → skip`);
|
|
3341
|
+
return;
|
|
3342
|
+
}
|
|
3343
|
+
logger9.debug(`sendCardUpdate: task #${task.id} 无卡片记录 → ` + `单卡语义不补发(进展落时间线)`);
|
|
3344
|
+
return;
|
|
3345
|
+
}
|
|
3346
|
+
if (rec.status === "closed" && isHardTerminal(String(task.status))) {
|
|
3347
|
+
logger9.debug(`sendCardUpdate: task #${task.id} closed + 终态 → skip`);
|
|
3348
|
+
return;
|
|
3349
|
+
}
|
|
3350
|
+
const reopened = rec.status === "closed" && !isHardTerminal(String(task.status));
|
|
3351
|
+
if (reopened) {
|
|
3352
|
+
this.cardStateStore.markReopened(task.id);
|
|
3353
|
+
logger9.debug(`sendCardUpdate: task #${task.id} reopen(复用记录)`);
|
|
3354
|
+
}
|
|
3355
|
+
const fresh = reopened ? this.getRecord(task.id) : rec;
|
|
3356
|
+
const milestones = this.parseTimeline(fresh.timeline);
|
|
3357
|
+
if (reopened) {
|
|
3358
|
+
const last = milestones[milestones.length - 1];
|
|
3359
|
+
if (last?.title !== REOPEN_TITLE) {
|
|
3360
|
+
milestones.push({
|
|
3361
|
+
title: REOPEN_TITLE,
|
|
3362
|
+
timestamp: new Date().toISOString(),
|
|
3363
|
+
description: task.current_status ? task.current_status.slice(0, 80) : undefined
|
|
3364
|
+
});
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
this.persistTimeline(task.id, fresh, milestones, fresh.updateCount);
|
|
3368
|
+
logger9.debug(`sendCardUpdate: task #${task.id} 单卡语义(不发新卡,` + `进展走时间线;点「刷新详情」可见)`);
|
|
3369
|
+
}
|
|
3370
|
+
async sendCardFinish(task, rawStatus) {
|
|
3371
|
+
const rec = this.getRecord(task.id);
|
|
3372
|
+
if (!rec) {
|
|
3373
|
+
logger9.debug(`sendCardFinish: no card record for task #${task.id}, skip(不发新卡)`);
|
|
3374
|
+
return;
|
|
3375
|
+
}
|
|
3376
|
+
if (rec.status === "closed" && rec.closedAt !== undefined && Date.now() - rec.closedAt < 60000) {
|
|
3377
|
+
const milestones2 = this.parseTimeline(rec.timeline);
|
|
3378
|
+
milestones2.push({
|
|
3379
|
+
title: FINISH_TITLE[rawStatus] ?? "任务收尾",
|
|
3380
|
+
timestamp: new Date().toISOString(),
|
|
3381
|
+
description: task.current_status || undefined
|
|
3382
|
+
});
|
|
3383
|
+
this.persistTimeline(task.id, rec, milestones2, rec.updateCount);
|
|
3384
|
+
logger9.debug(`sendCardFinish: task #${task.id} 已收尾(60s 内重复终态)→ ` + `合并不发新卡`);
|
|
3385
|
+
return;
|
|
3386
|
+
}
|
|
3387
|
+
const milestones = this.parseTimeline(rec.timeline);
|
|
3388
|
+
milestones.push({
|
|
3389
|
+
title: FINISH_TITLE[rawStatus] ?? "任务收尾",
|
|
3390
|
+
timestamp: new Date().toISOString(),
|
|
3391
|
+
description: task.current_status || undefined
|
|
3392
|
+
});
|
|
3393
|
+
await this.deliver({
|
|
3394
|
+
chatId: rec.chatId,
|
|
3395
|
+
task,
|
|
3396
|
+
statusLabel: statusLabelOf(task, rawStatus),
|
|
3397
|
+
milestones,
|
|
3398
|
+
terminal: true
|
|
3399
|
+
});
|
|
3400
|
+
this.persistTimeline(task.id, rec, milestones, rec.updateCount + 1);
|
|
3401
|
+
if (rawStatus === "paused") {
|
|
3402
|
+
this.cardStateStore.markPaused(task.id);
|
|
3403
|
+
} else {
|
|
3404
|
+
this.cardStateStore.markClosed(task.id);
|
|
3405
|
+
}
|
|
3406
|
+
logger9.debug(`sendCardFinish: task #${task.id} → status=${rawStatus}`);
|
|
3407
|
+
}
|
|
3408
|
+
async deliver(input) {
|
|
3409
|
+
const content = buildWecomTaskCard({
|
|
3410
|
+
task: input.task,
|
|
3411
|
+
statusLabel: input.statusLabel,
|
|
3412
|
+
milestones: input.milestones,
|
|
3413
|
+
changeSummary: input.changeSummary,
|
|
3414
|
+
terminal: input.terminal
|
|
3415
|
+
});
|
|
3416
|
+
const cardResult = await this.deliverTemplateCard(input);
|
|
3417
|
+
if (cardResult)
|
|
3418
|
+
return true;
|
|
3419
|
+
try {
|
|
3420
|
+
const r = await this.sender.send({
|
|
3421
|
+
chatId: input.chatId,
|
|
3422
|
+
content
|
|
3423
|
+
});
|
|
3424
|
+
if (!r.ok) {
|
|
3425
|
+
logger9.warn(`deliver failed for task #${input.task.id}: ${r.error ?? "unknown"}`);
|
|
3426
|
+
return false;
|
|
3427
|
+
}
|
|
3428
|
+
return true;
|
|
3429
|
+
} catch (err) {
|
|
3430
|
+
logger9.error(`deliver threw for task #${input.task.id}: ${err.message}`);
|
|
3431
|
+
return false;
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
async deliverTemplateCard(input) {
|
|
3435
|
+
try {
|
|
3436
|
+
const env = getEnvContextOrEmpty();
|
|
3437
|
+
const fields = extractWecomFields(env, this.sourceId);
|
|
3438
|
+
const sourceId = fields.sourceId ?? "unknown";
|
|
3439
|
+
const chatType = env.replyChannel?.params?.chatType === "group" || fields.chatId !== input.chatId ? "group" : "single";
|
|
3440
|
+
const notice = buildWecomTextNoticeCard({
|
|
3441
|
+
taskId: input.task.id,
|
|
3442
|
+
title: input.task.title,
|
|
3443
|
+
statusLabel: input.statusLabel,
|
|
3444
|
+
progress: Number(input.task.progress ?? 0),
|
|
3445
|
+
description: input.task.description,
|
|
3446
|
+
changeSummary: input.changeSummary,
|
|
3447
|
+
milestones: input.milestones,
|
|
3448
|
+
terminal: input.terminal
|
|
3449
|
+
});
|
|
3450
|
+
const card = input.terminal ? notice : buildWecomButtonInteractionCard({
|
|
3451
|
+
taskId: input.task.id,
|
|
3452
|
+
title: input.task.title,
|
|
3453
|
+
statusLabel: input.statusLabel,
|
|
3454
|
+
progress: Number(input.task.progress ?? 0),
|
|
3455
|
+
description: input.task.description,
|
|
3456
|
+
changeSummary: input.changeSummary,
|
|
3457
|
+
milestones: input.milestones,
|
|
3458
|
+
buttons: [
|
|
3459
|
+
{ text: "刷新详情", key: WecomCardActionKey.VIEW, style: 1 }
|
|
3460
|
+
]
|
|
3461
|
+
});
|
|
3462
|
+
const r = await sendWecomTemplateCard({
|
|
3463
|
+
sourceId,
|
|
3464
|
+
chatId: input.chatId,
|
|
3465
|
+
chatType,
|
|
3466
|
+
card
|
|
3467
|
+
});
|
|
3468
|
+
if (!r.ok) {
|
|
3469
|
+
logger9.warn(`deliverTemplateCard failed for task #${input.task.id}: ` + `${r.error ?? "unknown"}(回落 markdown)`);
|
|
3470
|
+
return false;
|
|
3471
|
+
}
|
|
3472
|
+
return true;
|
|
3473
|
+
} catch (err) {
|
|
3474
|
+
logger9.warn(`deliverTemplateCard threw for task #${input.task.id}: ` + `${err.message}(回落 markdown)`);
|
|
3475
|
+
return false;
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
getRecord(taskId) {
|
|
3479
|
+
try {
|
|
3480
|
+
return this.cardStateStore.get(taskId);
|
|
3481
|
+
} catch (err) {
|
|
3482
|
+
logger9.warn(`getRecord failed for task #${taskId}: ${err.message}`);
|
|
3483
|
+
return;
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
parseTimeline(timeline) {
|
|
3487
|
+
if (!timeline)
|
|
3488
|
+
return [];
|
|
3489
|
+
try {
|
|
3490
|
+
const arr = JSON.parse(timeline);
|
|
3491
|
+
return Array.isArray(arr) ? arr : [];
|
|
3492
|
+
} catch {
|
|
3493
|
+
return [];
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
trimTimeline(milestones) {
|
|
3497
|
+
const max = this.config.maxTimelineEntries ?? 20;
|
|
3498
|
+
if (milestones.length <= max)
|
|
3499
|
+
return milestones;
|
|
3500
|
+
return milestones.slice(milestones.length - max);
|
|
3501
|
+
}
|
|
3502
|
+
persistTimeline(taskId, rec, milestones, updateCount) {
|
|
3503
|
+
try {
|
|
3504
|
+
this.cardStateStore.upsert({
|
|
3505
|
+
...rec,
|
|
3506
|
+
updatedAt: Date.now(),
|
|
3507
|
+
updateCount,
|
|
3508
|
+
timeline: JSON.stringify(milestones)
|
|
3509
|
+
});
|
|
3510
|
+
} catch (err) {
|
|
3511
|
+
logger9.warn(`persistTimeline failed for task #${taskId}: ${err.message}`);
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
hydrateTask(taskId, rec) {
|
|
3515
|
+
return {
|
|
3516
|
+
id: taskId,
|
|
3517
|
+
title: `Task #${taskId}`,
|
|
3518
|
+
description: "",
|
|
3519
|
+
status: "active",
|
|
3520
|
+
priority: "medium",
|
|
3521
|
+
type: "normal",
|
|
3522
|
+
progress: 0,
|
|
3523
|
+
current_status: "",
|
|
3524
|
+
goals_and_expected_deliverables: "",
|
|
3525
|
+
createdAt: new Date(rec.createdAt).toISOString(),
|
|
3526
|
+
updatedAt: new Date(rec.updatedAt).toISOString(),
|
|
3527
|
+
project_path: "",
|
|
3528
|
+
context: "{}"
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
shouldThrottleOperation(operation) {
|
|
3532
|
+
const throttleMs = this.config.operationThrottleMs ?? 5000;
|
|
3533
|
+
if (throttleMs <= 0)
|
|
3534
|
+
return false;
|
|
3535
|
+
const key = `op:${operation.taskId}`;
|
|
3536
|
+
const now = Date.now();
|
|
3537
|
+
const lastTime = this.lastNotifyTimestamps.get(key);
|
|
3538
|
+
if (lastTime && now - lastTime < throttleMs) {
|
|
3539
|
+
logger9.debug(`[throttle] skip OPERATION_CREATED send for task #${operation.taskId} ` + `(${now - lastTime}ms ago)`);
|
|
3540
|
+
return true;
|
|
3541
|
+
}
|
|
3542
|
+
this.lastNotifyTimestamps.set(key, now);
|
|
3543
|
+
return false;
|
|
3544
|
+
}
|
|
3545
|
+
shouldNotify(type) {
|
|
3546
|
+
const { notifyTypes } = this.config;
|
|
3547
|
+
if (!notifyTypes || notifyTypes.length === 0)
|
|
3548
|
+
return true;
|
|
3549
|
+
return notifyTypes.includes(type);
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
__legacyDecorateClassTS([
|
|
3553
|
+
TracedExecute
|
|
3554
|
+
], WecomTaskNotifyHook.prototype, "execute", null);
|
|
3555
|
+
var FINISH_TITLE = {
|
|
3556
|
+
completed: "\uD83C\uDF89 任务完成",
|
|
3557
|
+
cancelled: "\uD83D\uDEAB 任务取消",
|
|
3558
|
+
failed: "\uD83D\uDEA8 任务失败",
|
|
3559
|
+
timeout: "\uD83D\uDEA8 任务超时",
|
|
3560
|
+
archived: "\uD83D\uDCE6 任务归档",
|
|
3561
|
+
paused: "⏸️ 任务暂停"
|
|
3562
|
+
};
|
|
3563
|
+
var REOPEN_TITLE = "\uD83D\uDD04 已重开";
|
|
3564
|
+
function createWecomTaskNotifyHook(config = {}) {
|
|
3565
|
+
return new WecomTaskNotifyHook(config);
|
|
3566
|
+
}
|
|
3567
|
+
function TracedExecute(_target, _propertyKey, descriptor) {
|
|
3568
|
+
const original = descriptor.value;
|
|
3569
|
+
descriptor.value = async function(...args) {
|
|
3570
|
+
try {
|
|
3571
|
+
return await original.apply(this, args);
|
|
3572
|
+
} catch (err) {
|
|
3573
|
+
logger9.error(`wecom-task-notify execute threw: ${err.message}`);
|
|
3574
|
+
}
|
|
3575
|
+
};
|
|
3576
|
+
return descriptor;
|
|
3577
|
+
}
|
|
2773
3578
|
// src/env/task/plugins/per-task-group-chat-plugin.ts
|
|
2774
3579
|
init_decorator();
|
|
2775
3580
|
init_logger();
|
|
2776
3581
|
init_context();
|
|
2777
3582
|
import { spawn } from "child_process";
|
|
2778
|
-
var
|
|
3583
|
+
var logger10 = createLogger("lark-cli:per-task-group");
|
|
2779
3584
|
function isPluginDebugEnabled() {
|
|
2780
3585
|
return process.env.ROY_TASK_NOTIFY_DEBUG === "1" || process.env.ROY_TRACE_DEBUG === "1";
|
|
2781
3586
|
}
|
|
@@ -2830,7 +3635,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2830
3635
|
const renderedName = this.renderTemplate(this.config.nameTemplate, task);
|
|
2831
3636
|
const existingChatId = this.getTaskChatId(task);
|
|
2832
3637
|
if (existingChatId) {
|
|
2833
|
-
|
|
3638
|
+
logger10.debug(`ensureGroupChat[${this.pluginName}]: task #${task.id} 已有专属群 ${existingChatId} → 复用(不重复建群)`);
|
|
2834
3639
|
return {
|
|
2835
3640
|
chatId: existingChatId,
|
|
2836
3641
|
profile: this.parseProfileFromContext(task.context),
|
|
@@ -2839,7 +3644,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2839
3644
|
}
|
|
2840
3645
|
const inflight = this.groupChatInflight.get(task.id);
|
|
2841
3646
|
if (inflight) {
|
|
2842
|
-
|
|
3647
|
+
logger10.debug(`ensureGroupChat[${this.pluginName}]: task #${task.id} 建群已在进行中 → 复用同一 promise`);
|
|
2843
3648
|
return inflight;
|
|
2844
3649
|
}
|
|
2845
3650
|
const pending = this.doEnsureGroupChat(task, renderedName, replyChannelOverride);
|
|
@@ -2853,7 +3658,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2853
3658
|
async doEnsureGroupChat(task, name, replyChannelOverride) {
|
|
2854
3659
|
const replyChannel = replyChannelOverride ?? getEnvContextOrEmpty().replyChannel;
|
|
2855
3660
|
if (!replyChannel?.chatId && this.config.requireReplyChannel) {
|
|
2856
|
-
|
|
3661
|
+
logger10.warn(`ensureGroupChat[${this.pluginName}]: no replyChannel.chatId for task #${task.id} ` + `(requireReplyChannel=true) → 无法建群`);
|
|
2857
3662
|
return;
|
|
2858
3663
|
}
|
|
2859
3664
|
const description = this.renderTemplate(this.config.descriptionTemplate, task);
|
|
@@ -2871,30 +3676,30 @@ class PerTaskGroupChatPlugin {
|
|
|
2871
3676
|
await this.writeChatBinding(task, chatId, profile, "creating");
|
|
2872
3677
|
await this.attachChatIdToTask(task, chatId, profile);
|
|
2873
3678
|
await this.writeChatBinding(task, chatId, profile, "ready");
|
|
2874
|
-
|
|
3679
|
+
logger10.info(`ensureGroupChat[${this.pluginName}]: created group ${chatId} for task #${task.id}`);
|
|
2875
3680
|
return { chatId, profile, name };
|
|
2876
3681
|
} catch (error) {
|
|
2877
|
-
|
|
3682
|
+
logger10.error(`ensureGroupChat[${this.pluginName}]: failed for task #${task.id}: ${error}`);
|
|
2878
3683
|
return;
|
|
2879
3684
|
}
|
|
2880
3685
|
}
|
|
2881
3686
|
setTaskUpdater(updater) {
|
|
2882
3687
|
this.taskUpdater = updater;
|
|
2883
|
-
|
|
3688
|
+
logger10.debug(`TaskUpdater injected for plugin ${this.name} (sourceId=${this.sourceId})`);
|
|
2884
3689
|
}
|
|
2885
3690
|
hasTaskUpdater() {
|
|
2886
3691
|
return this.taskUpdater !== undefined;
|
|
2887
3692
|
}
|
|
2888
3693
|
setTaskGetter(getter) {
|
|
2889
3694
|
this.taskGetter = getter;
|
|
2890
|
-
|
|
3695
|
+
logger10.debug(`TaskGetter injected for plugin ${this.name} (sourceId=${this.sourceId})`);
|
|
2891
3696
|
}
|
|
2892
3697
|
hasTaskGetter() {
|
|
2893
3698
|
return this.taskGetter !== undefined;
|
|
2894
3699
|
}
|
|
2895
3700
|
setSessionComponent(sessionComponent) {
|
|
2896
3701
|
this.sessionComponent = sessionComponent;
|
|
2897
|
-
|
|
3702
|
+
logger10.debug(`SessionComponent injected for plugin ${this.name} (sourceId=${this.sourceId})`);
|
|
2898
3703
|
}
|
|
2899
3704
|
hasSessionComponent() {
|
|
2900
3705
|
return this.sessionComponent !== undefined;
|
|
@@ -2907,7 +3712,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2907
3712
|
if (process.env.ROY_HOOK_DEBUG === "1") {
|
|
2908
3713
|
console.error(`[DEBUG per-task-group-chat.execute] → SKIP (config.enabled=false)`);
|
|
2909
3714
|
}
|
|
2910
|
-
|
|
3715
|
+
logger10.debug(`execute: plugin disabled (pluginName=${this.pluginName})`);
|
|
2911
3716
|
return;
|
|
2912
3717
|
}
|
|
2913
3718
|
if (this.pluginName && this.sourceId) {
|
|
@@ -2920,13 +3725,13 @@ class PerTaskGroupChatPlugin {
|
|
|
2920
3725
|
if (process.env.ROY_HOOK_DEBUG === "1") {
|
|
2921
3726
|
console.error(`[DEBUG per-task-group-chat.execute] → SKIP (plugin NOT enabled for ${key})`);
|
|
2922
3727
|
}
|
|
2923
|
-
|
|
3728
|
+
logger10.debug(`execute[${this.pluginName}]: plugin NOT enabled (key=${key}, ` + `env.pluginEnabled=${JSON.stringify(env.pluginEnabled)}, ` + `hookPoint=${ctx.hookPoint}). ` + `→ skip. 修复:roy-agent eventsource update <id> 必须包含 PerTaskGroupChatPlugin`);
|
|
2924
3729
|
return;
|
|
2925
3730
|
}
|
|
2926
3731
|
const enabledState = env.pluginEnabled ? env.pluginEnabled[key] === true ? "✅ enabled" : "❌ NOT enabled" : "⚠️ env.pluginEnabled 未设置 (env 没创建 / 插件未注册到 eventsource)";
|
|
2927
|
-
|
|
3732
|
+
logger10.debug(`execute[${this.pluginName}]: ${enabledState} (key=${key}, ` + `hookPoint=${ctx.hookPoint}, taskUpdaterInjected=${this.hasTaskUpdater()})`);
|
|
2928
3733
|
} else {
|
|
2929
|
-
|
|
3734
|
+
logger10.debug(`execute[${this.pluginName}]: missing pluginName/sourceId metadata ` + `(pluginName=${this.pluginName}, sourceId=${this.sourceId}), ` + `skipping pluginEnabled check. hookPoint=${ctx.hookPoint}`);
|
|
2930
3735
|
}
|
|
2931
3736
|
if (process.env.ROY_HOOK_DEBUG === "1") {
|
|
2932
3737
|
console.error(`[DEBUG per-task-group-chat.execute] → PASSED pluginEnabled check, will dispatch hookPoint=${ctx.hookPoint}`);
|
|
@@ -2945,13 +3750,13 @@ class PerTaskGroupChatPlugin {
|
|
|
2945
3750
|
await this.onAfterComplete(ctx);
|
|
2946
3751
|
break;
|
|
2947
3752
|
default:
|
|
2948
|
-
|
|
3753
|
+
logger10.debug(`execute: unhandled hookPoint=${ctx.hookPoint}, skip`);
|
|
2949
3754
|
}
|
|
2950
3755
|
}
|
|
2951
3756
|
async onAfterCreate(ctx) {
|
|
2952
3757
|
const data = ctx.data;
|
|
2953
3758
|
if (!data?.task) {
|
|
2954
|
-
|
|
3759
|
+
logger10.debug("onAfterCreate: task is missing, skip");
|
|
2955
3760
|
return;
|
|
2956
3761
|
}
|
|
2957
3762
|
const { task } = data;
|
|
@@ -2962,38 +3767,38 @@ class PerTaskGroupChatPlugin {
|
|
|
2962
3767
|
console.error(`[DEBUG per-task-group-chat.onAfterCreate] env.messageId=${env.messageId}`);
|
|
2963
3768
|
console.error(`[DEBUG per-task-group-chat.onAfterCreate] env.sourceId=${env.sourceId}`);
|
|
2964
3769
|
}
|
|
2965
|
-
|
|
3770
|
+
logger10.debug(`onAfterCreate[${this.pluginName}]: enter task=${task.id} "${task.title}" ` + `parent_task_id=${task.parent_task_id ?? "none"} ` + `requireReplyChannel=${this.config.requireReplyChannel} ` + `disableSubTaskSkip=${this.config.disableSubTaskSkip} ` + `sendWelcomeMessage=${this.config.sendWelcomeMessage} ` + `autoCreateGroup=${this.config.autoCreateGroup}`);
|
|
2966
3771
|
if (!this.config.autoCreateGroup) {
|
|
2967
|
-
|
|
3772
|
+
logger10.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} autoCreateGroup=false ` + `→ skip group chat creation(按需建群:用户在卡片顶部按钮触发)`);
|
|
2968
3773
|
return;
|
|
2969
3774
|
}
|
|
2970
3775
|
const replyChannel = this.getReplyChannelFromCtx(ctx);
|
|
2971
3776
|
if (!replyChannel?.chatId && this.config.requireReplyChannel) {
|
|
2972
|
-
|
|
3777
|
+
logger10.debug(`onAfterCreate[${this.pluginName}]: no replyChannel.chatId for task #${task.id}, skip. ` + `修复:eventsource 收到 im.message.receive_v1 事件时 replyChannel 应有值;` + `或在 plugin config 设 requireReplyChannel=false 强制创建群聊(profile 降级到 fallbackProfile/无)`);
|
|
2973
3778
|
return;
|
|
2974
3779
|
}
|
|
2975
3780
|
if (!replyChannel?.chatId && !this.config.requireReplyChannel) {
|
|
2976
|
-
|
|
3781
|
+
logger10.debug(`onAfterCreate[${this.pluginName}]: no replyChannel but requireReplyChannel=false, ` + `proceeding with profile fallback (fallbackProfile="${this.config.fallbackProfile}")`);
|
|
2977
3782
|
}
|
|
2978
3783
|
if (this.isSubTask(task) && !this.config.disableSubTaskSkip) {
|
|
2979
|
-
|
|
3784
|
+
logger10.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} is sub-task, ` + `skip group chat creation. 关闭方式:config.disableSubTaskSkip=true`);
|
|
2980
3785
|
return;
|
|
2981
3786
|
}
|
|
2982
3787
|
if (this.isSubTask(task) && this.config.disableSubTaskSkip) {
|
|
2983
|
-
|
|
3788
|
+
logger10.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} is sub-task but ` + `disableSubTaskSkip=true, proceeding to create group chat`);
|
|
2984
3789
|
}
|
|
2985
3790
|
try {
|
|
2986
3791
|
const created = await this.ensureGroupChat(task, replyChannel);
|
|
2987
3792
|
if (!created?.chatId) {
|
|
2988
|
-
|
|
3793
|
+
logger10.error(`onAfterCreate[${this.pluginName}]: ensureGroupChat 未返回 chatId for task #${task.id}`);
|
|
2989
3794
|
if (!this.config.fallbackToMain) {
|
|
2990
3795
|
throw new Error(`per-task group chat unavailable for task #${task.id}`);
|
|
2991
3796
|
}
|
|
2992
3797
|
return;
|
|
2993
3798
|
}
|
|
2994
|
-
|
|
3799
|
+
logger10.info(`onAfterCreate[${this.pluginName}]: task #${task.id} → group ${created.chatId} ` + `(initial card owned by LarkCliTaskNotifyHook)`);
|
|
2995
3800
|
} catch (error) {
|
|
2996
|
-
|
|
3801
|
+
logger10.error(`onAfterCreate[${this.pluginName}]: group chat failed for task #${task.id}: ${error}`);
|
|
2997
3802
|
if (!this.config.fallbackToMain)
|
|
2998
3803
|
throw error;
|
|
2999
3804
|
}
|
|
@@ -3001,7 +3806,7 @@ class PerTaskGroupChatPlugin {
|
|
|
3001
3806
|
async writeChatBinding(task, chatId, profile, status, errorMessage) {
|
|
3002
3807
|
if (!this.sessionComponent) {
|
|
3003
3808
|
if (isPluginDebugEnabled()) {
|
|
3004
|
-
|
|
3809
|
+
logger10.debug(`writeChatBinding[${this.pluginName}]: no sessionComponent injected, ` + `skip chat-task binding for chat ${chatId} (status=${status})`);
|
|
3005
3810
|
}
|
|
3006
3811
|
return;
|
|
3007
3812
|
}
|
|
@@ -3014,37 +3819,37 @@ class PerTaskGroupChatPlugin {
|
|
|
3014
3819
|
errorMessage: errorMessage ?? null
|
|
3015
3820
|
});
|
|
3016
3821
|
} catch (bindErr) {
|
|
3017
|
-
|
|
3822
|
+
logger10.warn(`writeChatBinding[${this.pluginName}]: failed to write chat-task binding for ` + `chat ${chatId} / task #${task.id} (status=${status}): ${bindErr}. ` + `群消息 handler 将无法自动注入 parentTaskId。`);
|
|
3018
3823
|
}
|
|
3019
3824
|
}
|
|
3020
3825
|
async onAfterUpdate(ctx) {
|
|
3021
3826
|
const data = ctx.data;
|
|
3022
3827
|
if (!data?.task) {
|
|
3023
|
-
|
|
3828
|
+
logger10.debug("onAfterUpdate: task is missing, skip");
|
|
3024
3829
|
return;
|
|
3025
3830
|
}
|
|
3026
|
-
|
|
3831
|
+
logger10.debug(`onAfterUpdate[${this.pluginName}]: task #${data.task.id} 卡片由 ` + `LarkCliTaskNotifyHook 负责(本插件不再 patch),skip`);
|
|
3027
3832
|
}
|
|
3028
3833
|
async onAfterOperationCreate(ctx) {
|
|
3029
3834
|
const data = ctx.data;
|
|
3030
3835
|
if (!data?.operation) {
|
|
3031
|
-
|
|
3836
|
+
logger10.debug("onAfterOperationCreate: operation is missing, skip");
|
|
3032
3837
|
return;
|
|
3033
3838
|
}
|
|
3034
|
-
|
|
3839
|
+
logger10.debug(`onAfterOperationCreate[${this.pluginName}]: operation #${data.operation.id} ` + `卡片由 LarkCliTaskNotifyHook 负责,skip`);
|
|
3035
3840
|
}
|
|
3036
3841
|
async onAfterComplete(ctx) {
|
|
3037
3842
|
const data = ctx.data;
|
|
3038
3843
|
if (!data?.task) {
|
|
3039
|
-
|
|
3844
|
+
logger10.debug("onAfterComplete: task is missing, skip");
|
|
3040
3845
|
return;
|
|
3041
3846
|
}
|
|
3042
|
-
|
|
3847
|
+
logger10.debug(`onAfterComplete[${this.pluginName}]: task #${data.task.id} 终态卡片由 ` + `LarkCliTaskNotifyHook 负责,skip`);
|
|
3043
3848
|
}
|
|
3044
3849
|
createGroupChat(opts) {
|
|
3045
3850
|
const args = this.buildCreateGroupChatArgs(opts);
|
|
3046
3851
|
if (isPluginDebugEnabled()) {
|
|
3047
|
-
|
|
3852
|
+
logger10.debug(`Creating group chat: lark-cli ${args.join(" ")}`);
|
|
3048
3853
|
}
|
|
3049
3854
|
return new Promise((resolve, reject) => {
|
|
3050
3855
|
const proc = spawn("lark-cli", args, {
|
|
@@ -3119,16 +3924,16 @@ class PerTaskGroupChatPlugin {
|
|
|
3119
3924
|
parsed[CTX_FIELD_CHAT_TASK_ID] = task.id;
|
|
3120
3925
|
const newContext = JSON.stringify(parsed);
|
|
3121
3926
|
if (!this.taskUpdater) {
|
|
3122
|
-
|
|
3927
|
+
logger10.warn(`attachChatIdToTask[${this.pluginName}]: ⚠️ no taskUpdater injected, ` + `chat_id ${chatId} NOT persisted to db for task #${task.id}. ` + `后果:下次 AFTER_UPDATE 从 DB 加载 task 时 getTaskChatId() 返回 undefined,` + `所有 update/complete/operation 通知 silently 丢失。` + `修复:event-source-handlers.ts:registerPluginHooks 应自动注入 setTaskUpdater。`);
|
|
3123
3928
|
task.context = newContext;
|
|
3124
3929
|
return;
|
|
3125
3930
|
}
|
|
3126
3931
|
try {
|
|
3127
3932
|
await this.taskUpdater(task.id, newContext);
|
|
3128
3933
|
task.context = newContext;
|
|
3129
|
-
|
|
3934
|
+
logger10.debug(`attachChatIdToTask[${this.pluginName}]: ✅ persisted chat_id ${chatId} ` + `to task #${task.id} context`);
|
|
3130
3935
|
} catch (error) {
|
|
3131
|
-
|
|
3936
|
+
logger10.error(`Failed to persist chat_id to task #${task.id}: ${error}`);
|
|
3132
3937
|
task.context = newContext;
|
|
3133
3938
|
}
|
|
3134
3939
|
}
|
|
@@ -3223,15 +4028,15 @@ class PerTaskGroupChatPlugin {
|
|
|
3223
4028
|
}
|
|
3224
4029
|
resolveBotAppIds(replyChannel) {
|
|
3225
4030
|
if (this.config.botAppIds.length > 0) {
|
|
3226
|
-
|
|
4031
|
+
logger10.debug(`resolveBotAppIds[${this.pluginName}]: using config.botAppIds=${JSON.stringify(this.config.botAppIds)}`);
|
|
3227
4032
|
return this.config.botAppIds;
|
|
3228
4033
|
}
|
|
3229
4034
|
const appId = replyChannel ? replyChannel.appId : undefined;
|
|
3230
4035
|
if (appId) {
|
|
3231
|
-
|
|
4036
|
+
logger10.debug(`resolveBotAppIds[${this.pluginName}]: using replyChannel.appId=${appId} (auto-detected from event header.app_id)`);
|
|
3232
4037
|
return [appId];
|
|
3233
4038
|
}
|
|
3234
|
-
|
|
4039
|
+
logger10.warn(`resolveBotAppIds[${this.pluginName}]: ⚠️ no botAppIds found, creating group WITHOUT --bots. ` + `Bot will NOT be invited automatically. ` + `可能原因:(1) eventsource 没有收到 im.message.receive_v1 事件(replyChannel.appId 缺失);` + `(2) 配置了 requireReplyChannel=false 模式;` + `(3) buildReplyChannel 没透传 appId(应在 v1.10+ 修复)。` + `修复:手动设置 config.botAppIds: ["cli_xxx"] 或确认 eventsource 处理了 lark event。`);
|
|
3235
4040
|
return [];
|
|
3236
4041
|
}
|
|
3237
4042
|
resolveUserIds(replyChannel) {
|
|
@@ -3241,7 +4046,7 @@ class PerTaskGroupChatPlugin {
|
|
|
3241
4046
|
if (id && id !== "sender" && id.startsWith("ou_")) {
|
|
3242
4047
|
ids.add(id);
|
|
3243
4048
|
} else if (id && id !== "sender") {
|
|
3244
|
-
|
|
4049
|
+
logger10.debug(`resolveUserIds[${this.pluginName}]: skip non-open_id in defaultInviteUserIds: "${id}"`);
|
|
3245
4050
|
}
|
|
3246
4051
|
}
|
|
3247
4052
|
}
|
|
@@ -3264,7 +4069,7 @@ class PerTaskGroupChatPlugin {
|
|
|
3264
4069
|
}
|
|
3265
4070
|
}
|
|
3266
4071
|
if (isPluginDebugEnabled()) {
|
|
3267
|
-
|
|
4072
|
+
logger10.debug(`resolveUserIds[${this.pluginName}]: ` + `defaultInviteUserIds=${JSON.stringify(this.config.defaultInviteUserIds)} ` + `inviteUserIds=${JSON.stringify(this.config.inviteUserIds)} ` + `replyChannel.userId=${replyChannel?.userId ?? "(none)"} ` + `→ final userIds=${JSON.stringify(Array.from(ids))}`);
|
|
3268
4073
|
}
|
|
3269
4074
|
return Array.from(ids);
|
|
3270
4075
|
}
|
|
@@ -3417,4 +4222,4 @@ __legacyDecorateClassTS([
|
|
|
3417
4222
|
function createPerTaskGroupChatPlugin(config = {}) {
|
|
3418
4223
|
return new PerTaskGroupChatPlugin(config);
|
|
3419
4224
|
}
|
|
3420
|
-
export { createAutoTaskPlugin, LAYA_REUSE_THRESHOLD, LayaDecisionPlugin, createLayaDecisionPlugin, LAYA_COMPLETION_THRESHOLD, buildCompletionHint, renderCompletionState, buildCompletionQuestion, LayaCompletionPlugin, createLayaCompletionPlugin, registerLayaCompletionTaskHook, WORKFLOW_EXTRACTOR_PROMPT, formatExtractorInput, formatExtractorInputFromDescription, parseExtractorOutput, TaskTagPlugin, MemoryLarkCardStateStore, SqliteLarkCardStateStore, getDefaultLarkCardDbPath, createLarkCardStateStore, extractPerTaskChatId, extractPerTaskChatName, NotifyType, LarkCliTaskNotifyHook, createLarkCliTaskNotifyHook, PerTaskGroupChatPlugin, createPerTaskGroupChatPlugin };
|
|
4225
|
+
export { createAutoTaskPlugin, LAYA_REUSE_THRESHOLD, LayaDecisionPlugin, createLayaDecisionPlugin, LAYA_COMPLETION_THRESHOLD, buildCompletionHint, renderCompletionState, buildCompletionQuestion, LayaCompletionPlugin, createLayaCompletionPlugin, registerLayaCompletionTaskHook, WORKFLOW_EXTRACTOR_PROMPT, formatExtractorInput, formatExtractorInputFromDescription, parseExtractorOutput, TaskTagPlugin, MemoryLarkCardStateStore, SqliteLarkCardStateStore, getDefaultLarkCardDbPath, createLarkCardStateStore, extractPerTaskChatId, extractPerTaskChatName, NotifyType, LarkCliTaskNotifyHook, createLarkCliTaskNotifyHook, MemoryWecomCardStateStore, createMemoryWecomCardStore, SqliteWecomCardStateStore, getDefaultWecomCardDbPath, createWecomCardStateStore, WecomNotifyType, extractWecomFields, buildWecomTaskCard, WecomTaskNotifyHook, createWecomTaskNotifyHook, PerTaskGroupChatPlugin, createPerTaskGroupChatPlugin };
|