@ai-setting/roy-agent-core 1.7.71 → 1.7.75
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/env/agent/agent-component.js +6 -6
- package/dist/env/agent/agent-registry.d.ts +4 -1
- package/dist/env/agent/index.js +6 -6
- package/dist/env/agent/summary-agent.d.ts +6 -15
- package/dist/env/agent/types.d.ts +0 -17
- package/dist/env/error-reply.d.ts +46 -0
- package/dist/env/error-reply.js +3 -1
- package/dist/env/event-source/event-source-handlers.js +12 -12
- package/dist/env/event-source/index.js +13 -13
- package/dist/env/index.js +30 -31
- package/dist/env/llm/index.js +2 -2
- package/dist/env/llm/orphan-toolcall.d.ts +107 -0
- package/dist/env/llm/temperature-policy.d.ts +107 -0
- package/dist/env/llm/types.d.ts +2 -2
- package/dist/env/plugin/index.js +3 -3
- package/dist/env/prompt/index.js +3 -3
- package/dist/env/prompt/prompts-index.d.ts +26 -4
- package/dist/env/session/__bench__/replay-compact.d.ts +137 -0
- package/dist/env/session/__bench__/run-replay.d.ts +15 -0
- package/dist/env/session/compact-checkpoint-content.d.ts +67 -0
- package/dist/env/session/index.d.ts +1 -0
- package/dist/env/session/index.js +4 -5
- package/dist/env/session/session-component.d.ts +47 -30
- package/dist/env/session/storage/index.js +3 -3
- package/dist/env/session/storage/sqlite.d.ts +8 -0
- package/dist/env/session/types.d.ts +45 -30
- package/dist/env/task/delegate/delegate-tool.d.ts +8 -0
- package/dist/env/task/delegate/delegate-tool.js +4 -4
- package/dist/env/task/delegate/index.js +4 -4
- package/dist/env/task/index.js +7 -7
- package/dist/env/task/mcp/index.js +2 -2
- package/dist/env/task/plugins/index.d.ts +3 -0
- package/dist/env/task/plugins/index.js +32 -9
- package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +61 -0
- package/dist/env/task/plugins/lark-card-patch-notifier.js +6 -2
- package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +42 -0
- package/dist/env/task/prompt/index.js +1 -1
- package/dist/env/task/prompt/task-discipline.d.ts +9 -3
- package/dist/env/task/tools/cancel-tool.d.ts +28 -0
- package/dist/env/task/tools/index.d.ts +1 -0
- package/dist/env/task/tools/index.js +4 -2
- package/dist/env/task/tools/task-types.d.ts +22 -0
- package/dist/env/tool/built-in/compact-session-tool.d.ts +120 -0
- package/dist/env/tool/built-in/index.d.ts +2 -0
- package/dist/env/tool/built-in/index.js +6 -2
- package/dist/env/tool/index.js +4 -4
- package/dist/env/workflow/engine/index.js +6 -7
- package/dist/env/workflow/index.js +15 -14
- package/dist/env/workflow/plugins/index.js +2 -2
- package/dist/env/workflow/tools/ask-user-tool.d.ts +88 -0
- package/dist/env/workflow/tools/index.js +11 -10
- package/dist/env/workflow/tools/run-workflow.d.ts +2 -2
- package/dist/env/workflow/types/index.js +2 -2
- package/dist/env/workflow/types/workflow-hil.d.ts +25 -1
- package/dist/env/workflow/utils/index.js +5 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +33 -34
- package/dist/shared/@ai-setting/{roy-agent-core-hy3zn2q2.js → roy-agent-core-0qyrk5k0.js} +22 -2
- package/dist/shared/@ai-setting/{roy-agent-core-kmbct9yg.js → roy-agent-core-0snm5hc5.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-4jjwewvw.js → roy-agent-core-11shp00p.js} +31 -10
- package/dist/shared/@ai-setting/{roy-agent-core-yr1mryb8.js → roy-agent-core-1v4g0d4x.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-02j24hrg.js → roy-agent-core-264qexac.js} +71 -4
- package/dist/shared/@ai-setting/{roy-agent-core-9geem94g.js → roy-agent-core-29b6hc39.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-2akfd64z.js +374 -0
- package/dist/shared/@ai-setting/{roy-agent-core-zv8bswpw.js → roy-agent-core-3e6gg17d.js} +120 -28
- package/dist/shared/@ai-setting/{roy-agent-core-8cx7cfdg.js → roy-agent-core-3fh1f7s7.js} +2 -3
- package/dist/shared/@ai-setting/{roy-agent-core-mwa6apzx.js → roy-agent-core-4gr3k7qg.js} +17 -4
- package/dist/shared/@ai-setting/{roy-agent-core-zsbh9bfv.js → roy-agent-core-60jbrfye.js} +6 -6
- package/dist/shared/@ai-setting/{roy-agent-core-add1zqsh.js → roy-agent-core-6cy43nss.js} +22 -1
- package/dist/shared/@ai-setting/{roy-agent-core-mw2cen6s.js → roy-agent-core-6rh8k6ba.js} +5 -5
- package/dist/shared/@ai-setting/{roy-agent-core-kpg25s2q.js → roy-agent-core-8ez6mccb.js} +4 -2
- package/dist/shared/@ai-setting/{roy-agent-core-sjr1kmfz.js → roy-agent-core-acj8ag08.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-mds8tqd0.js → roy-agent-core-cgtq6427.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-9bshyqjn.js → roy-agent-core-ddyk1rc0.js} +183 -132
- package/dist/shared/@ai-setting/{roy-agent-core-hfr6bzts.js → roy-agent-core-djpq6yb0.js} +16 -7
- package/dist/shared/@ai-setting/{roy-agent-core-002g6nfk.js → roy-agent-core-dqxec05w.js} +7 -6
- package/dist/shared/@ai-setting/{roy-agent-core-07fvy74y.js → roy-agent-core-ed233k5z.js} +21 -7
- package/dist/shared/@ai-setting/{roy-agent-core-jrj30vq3.js → roy-agent-core-er070zjq.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-40rv283c.js → roy-agent-core-fghc2m8z.js} +152 -2
- package/dist/shared/@ai-setting/{roy-agent-core-7sj9xs9c.js → roy-agent-core-hnyk1by7.js} +110 -2
- package/dist/shared/@ai-setting/{roy-agent-core-101qtkyc.js → roy-agent-core-jg65qnwa.js} +11 -8
- package/dist/shared/@ai-setting/{roy-agent-core-e25xkv53.js → roy-agent-core-mkc3zshb.js} +3 -1
- package/dist/shared/@ai-setting/{roy-agent-core-0cb6w0k7.js → roy-agent-core-mww0mnd9.js} +231 -27
- package/dist/shared/@ai-setting/{roy-agent-core-5repth1n.js → roy-agent-core-n9q1encx.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-46rv4d7d.js → roy-agent-core-nmx9934r.js} +92 -2
- package/dist/shared/@ai-setting/{roy-agent-core-1w9y7mxj.js → roy-agent-core-p3zv1mx5.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-snvh4s2p.js +662 -0
- package/dist/shared/@ai-setting/{roy-agent-core-dt104qa1.js → roy-agent-core-v85n42ve.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-37tj4y6a.js → roy-agent-core-v87njmq7.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-k13pxrpk.js → roy-agent-core-y7x2qzwc.js} +553 -92
- package/package.json +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-1gsbqesr.js +0 -1083
- package/dist/shared/@ai-setting/roy-agent-core-58804s3j.js +0 -155
- package/dist/shared/@ai-setting/roy-agent-core-ze068vc9.js +0 -37
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./roy-agent-core-na18zwj0.js";
|
|
4
4
|
import {
|
|
5
5
|
WORKFLOW_EXTRACT_PROMPT
|
|
6
|
-
} from "./roy-agent-core-
|
|
6
|
+
} from "./roy-agent-core-3e6gg17d.js";
|
|
7
7
|
import {
|
|
8
8
|
createCompatibleDatabase,
|
|
9
9
|
init_sqlite_task_store
|
|
@@ -24,10 +24,10 @@ import {
|
|
|
24
24
|
import {
|
|
25
25
|
LarkCardPatchNotifier,
|
|
26
26
|
humanizeValue
|
|
27
|
-
} from "./roy-agent-core-
|
|
27
|
+
} from "./roy-agent-core-nmx9934r.js";
|
|
28
28
|
import {
|
|
29
29
|
validateWorkflowDefinition
|
|
30
|
-
} from "./roy-agent-core-
|
|
30
|
+
} from "./roy-agent-core-1v4g0d4x.js";
|
|
31
31
|
import {
|
|
32
32
|
enqueueToAddFakeUserMessage
|
|
33
33
|
} from "./roy-agent-core-f77k3gqt.js";
|
|
@@ -1254,6 +1254,423 @@ function createAutoTaskPlugin(config) {
|
|
|
1254
1254
|
execute
|
|
1255
1255
|
};
|
|
1256
1256
|
}
|
|
1257
|
+
// src/env/task/plugins/laya-completion-plugin.ts
|
|
1258
|
+
init_logger();
|
|
1259
|
+
init_decorator();
|
|
1260
|
+
var logger5 = createLogger("plugin:laya-completion");
|
|
1261
|
+
var LAYA_COMPLETION_THRESHOLD = 0.92;
|
|
1262
|
+
var CHOICE_PASS = "PASS_COMPLETED";
|
|
1263
|
+
var CHOICE_FAIL = "FAIL_INCOMPLETE";
|
|
1264
|
+
var MAX_OPERATIONS_IN_STATE = 20;
|
|
1265
|
+
var OP_TITLE_MAX = 60;
|
|
1266
|
+
var OP_DESC_MAX = 120;
|
|
1267
|
+
var GOALS_MAX = 400;
|
|
1268
|
+
function buildCompletionHint(task, confidence) {
|
|
1269
|
+
return [
|
|
1270
|
+
"[TASK_COMPLETION_HINT] 决策模型检测到「目标 vs 交付物」可能不匹配:任务已标记完成,但操作记录链显示目标或交付物可能未达成。",
|
|
1271
|
+
"",
|
|
1272
|
+
`任务:#${task.id} [${task.type}] ${task.title}`,
|
|
1273
|
+
`状态:${task.status} | 判定置信度:${confidence.toFixed(4)}`,
|
|
1274
|
+
"",
|
|
1275
|
+
"⚠️ 你必须核实本提示:先用 task_get(task_id=" + task.id + ") 复核任务的目标(description / goals_and_expected_deliverables)与交付物证据(context.deliverable_storage_path、操作记录链)。",
|
|
1276
|
+
"核实后二选一执行:",
|
|
1277
|
+
"(a) 确认未完成 → task_update(task_id=" + task.id + ", status=todo) 重开任务继续推进;",
|
|
1278
|
+
"(b) 确认已完成 → task_operation_create(task_id=" + task.id + ", milestone_type=decision, ...) 落一条操作记录说明已完成的理由。",
|
|
1279
|
+
"不允许无视本提示。"
|
|
1280
|
+
].join(`
|
|
1281
|
+
`);
|
|
1282
|
+
}
|
|
1283
|
+
function renderOperations(ops) {
|
|
1284
|
+
if (ops.length === 0)
|
|
1285
|
+
return "(无操作记录)";
|
|
1286
|
+
const sorted = [...ops].sort((a, b) => new Date(a.timestamp ?? 0).getTime() - new Date(b.timestamp ?? 0).getTime());
|
|
1287
|
+
let windowed;
|
|
1288
|
+
if (sorted.length <= MAX_OPERATIONS_IN_STATE) {
|
|
1289
|
+
windowed = sorted;
|
|
1290
|
+
} else {
|
|
1291
|
+
const tail = sorted.slice(-(MAX_OPERATIONS_IN_STATE - 1));
|
|
1292
|
+
const head = sorted.slice(0, 1);
|
|
1293
|
+
const seen = new Set(tail.map((o) => o.id));
|
|
1294
|
+
windowed = seen.has(head[0].id) ? tail : [...head, ...tail];
|
|
1295
|
+
}
|
|
1296
|
+
return windowed.map((op, i) => {
|
|
1297
|
+
const title = (op.milestoneTitle || "").slice(0, OP_TITLE_MAX);
|
|
1298
|
+
const desc = (op.milestoneDescription || "").replace(/\n/g, " ").slice(0, OP_DESC_MAX);
|
|
1299
|
+
const proc = (op.processDescription || "").replace(/\n/g, " ").slice(0, OP_DESC_MAX);
|
|
1300
|
+
const parts = [`#${i + 1} [${op.milestoneType}] ${title}`];
|
|
1301
|
+
if (desc)
|
|
1302
|
+
parts.push(` ${desc}`);
|
|
1303
|
+
if (proc)
|
|
1304
|
+
parts.push(` 过程:${proc}`);
|
|
1305
|
+
return parts.join(`
|
|
1306
|
+
`);
|
|
1307
|
+
}).join(`
|
|
1308
|
+
`);
|
|
1309
|
+
}
|
|
1310
|
+
function readDeliverablePath(task) {
|
|
1311
|
+
try {
|
|
1312
|
+
const parsed = JSON.parse(task.context || "{}");
|
|
1313
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1314
|
+
const p = parsed.deliverable_storage_path;
|
|
1315
|
+
return typeof p === "string" ? p : "";
|
|
1316
|
+
}
|
|
1317
|
+
} catch {}
|
|
1318
|
+
return "";
|
|
1319
|
+
}
|
|
1320
|
+
function looksLikeFilePath(token) {
|
|
1321
|
+
if (token.startsWith("//"))
|
|
1322
|
+
return false;
|
|
1323
|
+
if ((token.match(/\//g) ?? []).length >= 2)
|
|
1324
|
+
return true;
|
|
1325
|
+
return /\.[A-Za-z0-9]+$/.test(token);
|
|
1326
|
+
}
|
|
1327
|
+
function firstPathToken(text) {
|
|
1328
|
+
const m = text.match(/(?:^|[\s((【「::,,])(\/[A-Za-z0-9._~\-/@%+]{2,})/);
|
|
1329
|
+
if (!m)
|
|
1330
|
+
return null;
|
|
1331
|
+
return looksLikeFilePath(m[1]) ? m[1] : null;
|
|
1332
|
+
}
|
|
1333
|
+
var CHECKLIST_MAX = 1200;
|
|
1334
|
+
var DELIVERABLE_SPLIT = /[,,;;、\n]/;
|
|
1335
|
+
var DELIVERABLE_PLUS_SPLIT = /\s\+\s/;
|
|
1336
|
+
function stripDeliverableToken(t) {
|
|
1337
|
+
const seg = t.trim();
|
|
1338
|
+
if (!seg)
|
|
1339
|
+
return null;
|
|
1340
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(seg))
|
|
1341
|
+
return null;
|
|
1342
|
+
const m = seg.match(/(?:^|[\s((【「::,,])(\/[A-Za-z0-9._~\-/@%+]{2,})/);
|
|
1343
|
+
if (!m)
|
|
1344
|
+
return null;
|
|
1345
|
+
const path = m[1].replace(/[。,、;:)】」"'”’.,;:!?)\]}]+$/u, "");
|
|
1346
|
+
return path.startsWith("/") ? path : null;
|
|
1347
|
+
}
|
|
1348
|
+
function isUrlSegment(seg) {
|
|
1349
|
+
return /^[a-z][a-z0-9+.-]*:\/\//i.test(seg.trim());
|
|
1350
|
+
}
|
|
1351
|
+
function splitDeliverableSegments(deliverable) {
|
|
1352
|
+
return deliverable.split(DELIVERABLE_PLUS_SPLIT).flatMap((s) => s.split(DELIVERABLE_SPLIT)).flatMap((s) => splitUrlSegments(s)).flatMap((s) => splitSpaceSeparatedPaths(s)).map((s) => s.trim()).filter(Boolean);
|
|
1353
|
+
}
|
|
1354
|
+
function splitUrlSegments(seg) {
|
|
1355
|
+
const re = /([a-z][a-z0-9+.-]*:\/\/\S+)/gi;
|
|
1356
|
+
const parts = [];
|
|
1357
|
+
let last = 0;
|
|
1358
|
+
let m;
|
|
1359
|
+
while ((m = re.exec(seg)) !== null) {
|
|
1360
|
+
const before = seg.slice(last, m.index).trim();
|
|
1361
|
+
if (before)
|
|
1362
|
+
parts.push(before);
|
|
1363
|
+
const url = m[1];
|
|
1364
|
+
if (/^file:\/\//i.test(url)) {
|
|
1365
|
+
const local = url.replace(/^file:\/\//i, "");
|
|
1366
|
+
if (local)
|
|
1367
|
+
parts.push(local);
|
|
1368
|
+
} else {
|
|
1369
|
+
parts.push(url);
|
|
1370
|
+
}
|
|
1371
|
+
last = m.index + url.length;
|
|
1372
|
+
}
|
|
1373
|
+
const rest = seg.slice(last).trim();
|
|
1374
|
+
if (rest)
|
|
1375
|
+
parts.push(rest);
|
|
1376
|
+
return parts.length > 0 ? parts : [seg];
|
|
1377
|
+
}
|
|
1378
|
+
function spaceSeparatedPathStarts(seg) {
|
|
1379
|
+
const re = /(?:^|[\s((【「::,,])(\/[A-Za-z0-9._~\-/@%+]{2,})/g;
|
|
1380
|
+
const starts = [];
|
|
1381
|
+
let m;
|
|
1382
|
+
while ((m = re.exec(seg)) !== null) {
|
|
1383
|
+
const pathStart = m[0].startsWith("/") ? m.index : m.index + 1;
|
|
1384
|
+
if (m[1].startsWith("//"))
|
|
1385
|
+
continue;
|
|
1386
|
+
starts.push(pathStart);
|
|
1387
|
+
}
|
|
1388
|
+
return starts;
|
|
1389
|
+
}
|
|
1390
|
+
function splitSpaceSeparatedPaths(seg) {
|
|
1391
|
+
const starts = spaceSeparatedPathStarts(seg);
|
|
1392
|
+
if (starts.length < 2)
|
|
1393
|
+
return [seg];
|
|
1394
|
+
const parts = [seg.slice(0, starts[1])];
|
|
1395
|
+
for (let i = 1;i < starts.length; i++) {
|
|
1396
|
+
const end = i + 1 < starts.length ? starts[i + 1] : seg.length;
|
|
1397
|
+
parts.push(seg.slice(starts[i], end));
|
|
1398
|
+
}
|
|
1399
|
+
return parts;
|
|
1400
|
+
}
|
|
1401
|
+
function isPathLikeDeliverable(deliverable) {
|
|
1402
|
+
if (!deliverable.includes("/"))
|
|
1403
|
+
return false;
|
|
1404
|
+
const segs = splitDeliverableSegments(deliverable);
|
|
1405
|
+
if (segs.length === 0)
|
|
1406
|
+
return false;
|
|
1407
|
+
const paths = segs.map(stripDeliverableToken).filter((p) => p !== null);
|
|
1408
|
+
if (paths.length === 0)
|
|
1409
|
+
return false;
|
|
1410
|
+
return segs.every((s, i) => stripDeliverableToken(s) !== null || isUrlSegment(s));
|
|
1411
|
+
}
|
|
1412
|
+
function renderCriteriaChecklist(goalsText, deliverable) {
|
|
1413
|
+
if (!deliverable || deliverable === "(未声明)")
|
|
1414
|
+
return null;
|
|
1415
|
+
if (!isPathLikeDeliverable(deliverable))
|
|
1416
|
+
return null;
|
|
1417
|
+
const items = goalsText.split(`
|
|
1418
|
+
`).map((l) => l.trim()).filter((l) => /^\d+\s*[.、))]/.test(l));
|
|
1419
|
+
if (items.length === 0)
|
|
1420
|
+
return null;
|
|
1421
|
+
const tokens = items.map(firstPathToken);
|
|
1422
|
+
if (tokens.some((t) => t === null))
|
|
1423
|
+
return null;
|
|
1424
|
+
const declared = splitDeliverableSegments(deliverable).map(stripDeliverableToken).filter((t) => t !== null);
|
|
1425
|
+
const declaredSet = new Set(declared);
|
|
1426
|
+
const lines = items.map((item, i) => {
|
|
1427
|
+
const token = tokens[i];
|
|
1428
|
+
const done = declaredSet.has(token);
|
|
1429
|
+
const origNum = item.match(/^(\d+)\s*[.、))]/)?.[1] ?? String(i + 1);
|
|
1430
|
+
return done ? `${origNum}. ${token} 已产出` : `${origNum}. ${token} 未产出`;
|
|
1431
|
+
});
|
|
1432
|
+
const anyUndone = tokens.some((t) => !declaredSet.has(t));
|
|
1433
|
+
const raw = `【逐条对照】${lines.join(";")},目标${anyUndone ? "未完成" : "全部完成"}`;
|
|
1434
|
+
if (raw.length > CHECKLIST_MAX) {
|
|
1435
|
+
const suffix = `目标${anyUndone ? "未完成" : "全部完成"}`;
|
|
1436
|
+
const head = raw.slice(0, CHECKLIST_MAX - suffix.length - 2);
|
|
1437
|
+
const lastSep = head.lastIndexOf(";");
|
|
1438
|
+
const cut = lastSep > 0 ? head.slice(0, lastSep) : head;
|
|
1439
|
+
return `${cut};…,${suffix}`;
|
|
1440
|
+
}
|
|
1441
|
+
return raw;
|
|
1442
|
+
}
|
|
1443
|
+
function renderCompletionState(task, ops) {
|
|
1444
|
+
const goals = (task.goals_and_expected_deliverables || "(无)").replace(/\n/g, " ").slice(0, GOALS_MAX);
|
|
1445
|
+
const deliverable = readDeliverablePath(task) || "(未声明)";
|
|
1446
|
+
const checklist = renderCriteriaChecklist(task.goals_and_expected_deliverables || "", deliverable);
|
|
1447
|
+
return `【任务标题】${task.title}
|
|
1448
|
+
` + `【任务目标】${goals}
|
|
1449
|
+
` + `【交付物声明】${deliverable}
|
|
1450
|
+
` + (checklist ? `${checklist}
|
|
1451
|
+
` : "") + `【操作记录】
|
|
1452
|
+
` + renderOperations(ops);
|
|
1453
|
+
}
|
|
1454
|
+
function buildCompletionQuestion() {
|
|
1455
|
+
return {
|
|
1456
|
+
completion_check: {
|
|
1457
|
+
type: "choice",
|
|
1458
|
+
instructions: "根据任务目标、交付物声明与操作记录链,判断该任务是否真正达成目标且产出交付物。" + `若证据充分支持已完成,选 ${CHOICE_PASS};若目标或交付物疑似未达成,选 ${CHOICE_FAIL}。`,
|
|
1459
|
+
criteria: {
|
|
1460
|
+
[CHOICE_PASS]: "目标已达成且交付物已产出",
|
|
1461
|
+
[CHOICE_FAIL]: "目标或交付物疑似未达成"
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
class LayaCompletionPlugin {
|
|
1468
|
+
name = "laya-completion";
|
|
1469
|
+
version = "1.0.0";
|
|
1470
|
+
description = "Task #4760 — task:after.complete 完成判定(目标/交付物 match,只提示不拦截)";
|
|
1471
|
+
options;
|
|
1472
|
+
port = null;
|
|
1473
|
+
portResolved = false;
|
|
1474
|
+
constructor(options = {}) {
|
|
1475
|
+
this.options = options;
|
|
1476
|
+
if (options.decisionPort) {
|
|
1477
|
+
this.port = options.decisionPort;
|
|
1478
|
+
this.portResolved = true;
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
isDisabled() {
|
|
1482
|
+
return this.port?.isDisabled() ?? false;
|
|
1483
|
+
}
|
|
1484
|
+
async resolvePort() {
|
|
1485
|
+
if (this.portResolved)
|
|
1486
|
+
return this.port;
|
|
1487
|
+
this.portResolved = true;
|
|
1488
|
+
try {
|
|
1489
|
+
const auth = await loadLayaAuthForPurpose(this.options.authPath ?? `${process.env.HOME ?? "~"}/.roy-agent/auth.json`, "goal");
|
|
1490
|
+
if (!auth)
|
|
1491
|
+
return null;
|
|
1492
|
+
this.port = new LayaDecisionPort({
|
|
1493
|
+
baseURL: auth.baseURL,
|
|
1494
|
+
apiKey: auth.apiKey,
|
|
1495
|
+
model: auth.model,
|
|
1496
|
+
timeoutMs: this.options.timeoutMs,
|
|
1497
|
+
maxStrikes: this.options.maxStrikes
|
|
1498
|
+
});
|
|
1499
|
+
} catch (err) {
|
|
1500
|
+
logger5.warn(`[laya-completion] loadLayaAuth failed (fail-open): ${err?.message}`);
|
|
1501
|
+
this.port = null;
|
|
1502
|
+
}
|
|
1503
|
+
return this.port;
|
|
1504
|
+
}
|
|
1505
|
+
async execute(ctx, taskComponent) {
|
|
1506
|
+
try {
|
|
1507
|
+
const data = normalizeHookCtx2(ctx);
|
|
1508
|
+
await this.run(data, taskComponent);
|
|
1509
|
+
} catch (err) {
|
|
1510
|
+
logger5.warn(`[laya-completion] unexpected failure, ignored: ${err?.message}`);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
async run(ctx, taskComponent) {
|
|
1514
|
+
if (!taskComponent) {
|
|
1515
|
+
logger5.warn("[laya-completion] taskComponent not injected — skip");
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
if (ctx.terminalStatus !== "completed") {
|
|
1519
|
+
logger5.debug(`[laya-completion] terminalStatus=${ctx.terminalStatus} — skip (only completed)`);
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
const task = ctx.task;
|
|
1523
|
+
if (!task?.id)
|
|
1524
|
+
return;
|
|
1525
|
+
let ops = [];
|
|
1526
|
+
try {
|
|
1527
|
+
ops = await taskComponent.listOperations({
|
|
1528
|
+
taskId: task.id,
|
|
1529
|
+
limit: 1e6,
|
|
1530
|
+
offset: 0
|
|
1531
|
+
});
|
|
1532
|
+
} catch (err) {
|
|
1533
|
+
logger5.warn(`[laya-completion] listOperations failed (fail-open): ${err?.message}`);
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
logger5.info(`[laya-completion] judging task #${task.id}: chain=${ops?.length ?? 0} ops, terminalStatus=${ctx.terminalStatus}`);
|
|
1537
|
+
if (!ops || ops.length === 0) {
|
|
1538
|
+
logger5.debug("[laya-completion] empty operation chain — pass through");
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
const port = await this.resolvePort();
|
|
1542
|
+
if (!port) {
|
|
1543
|
+
logger5.debug("[laya-completion] no decision port configured — skip");
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
if (port.isDisabled()) {
|
|
1547
|
+
logger5.debug("[laya-completion] port self-disabled (circuit open) — skip");
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
const req = {
|
|
1551
|
+
state: renderCompletionState(task, ops),
|
|
1552
|
+
questions: buildCompletionQuestion()
|
|
1553
|
+
};
|
|
1554
|
+
let answer;
|
|
1555
|
+
try {
|
|
1556
|
+
const res = await port.decide(req);
|
|
1557
|
+
answer = res?.answers?.["completion_check"];
|
|
1558
|
+
} catch (err) {
|
|
1559
|
+
logger5.warn(`[laya-completion] decide failed (fail-open): ${err?.message}`);
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
if (!answer || typeof answer.choice !== "string") {
|
|
1563
|
+
logger5.debug("[laya-completion] malformed answer — skip");
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
const confidence = pickGateConfidence(answer);
|
|
1567
|
+
const threshold = this.options.completionThreshold ?? LAYA_COMPLETION_THRESHOLD;
|
|
1568
|
+
if (confidence < threshold) {
|
|
1569
|
+
logger5.debug(`[laya-completion] confidence ${confidence} < ${threshold} — pass through`);
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
if (answer.choice !== CHOICE_FAIL) {
|
|
1573
|
+
logger5.debug(`[laya-completion] choice=${answer.choice} (not FAIL_INCOMPLETE) — no hint`);
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
await this.writeHint(task, confidence, taskComponent);
|
|
1577
|
+
}
|
|
1578
|
+
async writeHint(task, confidence, taskComponent) {
|
|
1579
|
+
const hint = buildCompletionHint(task, confidence);
|
|
1580
|
+
try {
|
|
1581
|
+
let parsed = {};
|
|
1582
|
+
try {
|
|
1583
|
+
const p = JSON.parse(task.context || "{}");
|
|
1584
|
+
if (p && typeof p === "object" && !Array.isArray(p)) {
|
|
1585
|
+
parsed = p;
|
|
1586
|
+
}
|
|
1587
|
+
} catch {}
|
|
1588
|
+
parsed.completion_hint = hint;
|
|
1589
|
+
await taskComponent.updateTaskContext(task.id, JSON.stringify(parsed));
|
|
1590
|
+
logger5.info(`[laya-completion] hint written to task #${task.id} context (conf=${confidence.toFixed(4)})`);
|
|
1591
|
+
} catch (err) {
|
|
1592
|
+
logger5.warn(`[laya-completion] updateTask hint failed (ignored): ${err?.message}`);
|
|
1593
|
+
}
|
|
1594
|
+
try {
|
|
1595
|
+
await taskComponent.createOperation({
|
|
1596
|
+
taskId: task.id,
|
|
1597
|
+
sessionId: "laya-completion",
|
|
1598
|
+
milestoneType: "decision",
|
|
1599
|
+
milestoneTitle: "[TASK_COMPLETION_HINT] 目标/交付物疑似未达成",
|
|
1600
|
+
milestoneDescription: `决策模型判定 FAIL_INCOMPLETE(conf=${confidence.toFixed(4)})。hint 已写入 task.context.completion_hint,等待 agent 核实。`
|
|
1601
|
+
});
|
|
1602
|
+
} catch (err) {
|
|
1603
|
+
logger5.warn(`[laya-completion] createOperation hint failed (ignored): ${err?.message}`);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
__legacyDecorateClassTS([
|
|
1608
|
+
TracedAs("task.laya.completion.onAfterComplete", {
|
|
1609
|
+
recordParams: false,
|
|
1610
|
+
recordResult: false,
|
|
1611
|
+
log: false,
|
|
1612
|
+
paramFilter: (args) => {
|
|
1613
|
+
const raw = args[0] ?? {};
|
|
1614
|
+
const ctx = raw?.data ?? raw;
|
|
1615
|
+
return {
|
|
1616
|
+
taskId: ctx?.task?.id,
|
|
1617
|
+
terminalStatus: ctx?.terminalStatus,
|
|
1618
|
+
hasTaskComponent: Boolean(args[1])
|
|
1619
|
+
};
|
|
1620
|
+
},
|
|
1621
|
+
resultFilter: (_res) => ({ ok: true })
|
|
1622
|
+
})
|
|
1623
|
+
], LayaCompletionPlugin.prototype, "execute", null);
|
|
1624
|
+
function normalizeHookCtx2(ctx) {
|
|
1625
|
+
const maybe = ctx;
|
|
1626
|
+
return maybe && typeof maybe === "object" && maybe.data ? maybe.data : ctx;
|
|
1627
|
+
}
|
|
1628
|
+
function createLayaCompletionPlugin(options) {
|
|
1629
|
+
const inner = options && Object.keys(options).length > 0 ? new LayaCompletionPlugin(options) : getSharedCompletionPlugin();
|
|
1630
|
+
return {
|
|
1631
|
+
name: "laya-completion",
|
|
1632
|
+
priority: 20,
|
|
1633
|
+
execute: async (ctx) => {
|
|
1634
|
+
await inner.execute(ctx, inner.__taskComponent);
|
|
1635
|
+
},
|
|
1636
|
+
plugin: inner
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
var sharedCompletionPlugin = null;
|
|
1640
|
+
function getSharedCompletionPlugin() {
|
|
1641
|
+
if (!sharedCompletionPlugin) {
|
|
1642
|
+
sharedCompletionPlugin = new LayaCompletionPlugin;
|
|
1643
|
+
}
|
|
1644
|
+
return sharedCompletionPlugin;
|
|
1645
|
+
}
|
|
1646
|
+
function registerLayaCompletionTaskHook(taskComponent, options) {
|
|
1647
|
+
if (!taskComponent) {
|
|
1648
|
+
logger5.warn("[registerLayaCompletionTaskHook] TaskComponent not available — skip (fail-open)");
|
|
1649
|
+
return false;
|
|
1650
|
+
}
|
|
1651
|
+
try {
|
|
1652
|
+
const hook = createLayaCompletionPlugin(options);
|
|
1653
|
+
hook.plugin.__taskComponent = taskComponent;
|
|
1654
|
+
taskComponent.unregisterHook(TaskHookPoints.AFTER_COMPLETE, hook.name);
|
|
1655
|
+
taskComponent.registerHook(TaskHookPoints.AFTER_COMPLETE, {
|
|
1656
|
+
name: hook.name,
|
|
1657
|
+
priority: hook.priority,
|
|
1658
|
+
execute: async (ctx) => {
|
|
1659
|
+
try {
|
|
1660
|
+
const data = ctx && typeof ctx === "object" && "data" in ctx ? ctx.data : ctx;
|
|
1661
|
+
await hook.plugin.execute(data, taskComponent);
|
|
1662
|
+
} catch (err) {
|
|
1663
|
+
logger5.warn(`[registerLayaCompletionTaskHook] hook execution failed (ignored): ${err?.message}`);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
});
|
|
1667
|
+
logger5.info(`[registerLayaCompletionTaskHook] registered hook "${hook.name}" at ${TaskHookPoints.AFTER_COMPLETE} (priority=${hook.priority})`);
|
|
1668
|
+
return true;
|
|
1669
|
+
} catch (err) {
|
|
1670
|
+
logger5.warn(`[registerLayaCompletionTaskHook] registration failed (ignored): ${err?.message}`);
|
|
1671
|
+
return false;
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1257
1674
|
// src/env/task/plugins/lark-cli-task-notify-plugin.ts
|
|
1258
1675
|
init_decorator();
|
|
1259
1676
|
init_logger();
|
|
@@ -1263,7 +1680,7 @@ init_context();
|
|
|
1263
1680
|
init_logger();
|
|
1264
1681
|
init_sqlite_task_store();
|
|
1265
1682
|
import { join } from "path";
|
|
1266
|
-
var
|
|
1683
|
+
var logger6 = createLogger("lark-cli:card-state");
|
|
1267
1684
|
|
|
1268
1685
|
class MemoryLarkCardStateStore {
|
|
1269
1686
|
records = new Map;
|
|
@@ -1427,7 +1844,7 @@ function createLarkCardStateStore(dbPath) {
|
|
|
1427
1844
|
try {
|
|
1428
1845
|
return new SqliteLarkCardStateStore(path);
|
|
1429
1846
|
} catch (err) {
|
|
1430
|
-
|
|
1847
|
+
logger6.warn(`LarkCardStateStore: sqlite unavailable at ${path} (${err.message}) ` + `→ falling back to in-memory (卡片仍可发,但重启后无法 patch 旧卡)`);
|
|
1431
1848
|
return new MemoryLarkCardStateStore;
|
|
1432
1849
|
}
|
|
1433
1850
|
}
|
|
@@ -1509,7 +1926,7 @@ function decideCardChatTarget(input) {
|
|
|
1509
1926
|
}
|
|
1510
1927
|
|
|
1511
1928
|
// src/env/task/plugins/lark-cli-task-notify-plugin.ts
|
|
1512
|
-
var
|
|
1929
|
+
var logger7 = createLogger("lark-cli:task-notify");
|
|
1513
1930
|
var CTX_FIELD_CHAT_ID = "larkPerTaskChatId";
|
|
1514
1931
|
var CTX_FIELD_CHAT_PROFILE = "larkPerTaskChatProfile";
|
|
1515
1932
|
function parseLarkPerTaskChatId(ctxStr) {
|
|
@@ -1572,6 +1989,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1572
1989
|
cardStateStore;
|
|
1573
1990
|
restoredTaskIds = new Set;
|
|
1574
1991
|
lastNotifyTimestamps = new Map;
|
|
1992
|
+
trailingPatchTimers = new Map;
|
|
1575
1993
|
cardRoutingPorts;
|
|
1576
1994
|
static CARD_START_FAILED_SENTINEL = "__card_start_failed__";
|
|
1577
1995
|
static CARD_START_INFLIGHT_SENTINEL = "__card_start_inflight__";
|
|
@@ -1599,7 +2017,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1599
2017
|
}
|
|
1600
2018
|
setCardRoutingPorts(ports) {
|
|
1601
2019
|
this.cardRoutingPorts = ports;
|
|
1602
|
-
|
|
2020
|
+
logger7.debug(`CardRoutingPorts injected: getTask=${typeof ports.getTask}`);
|
|
1603
2021
|
}
|
|
1604
2022
|
hasCardRoutingPorts() {
|
|
1605
2023
|
return this.cardRoutingPorts !== undefined;
|
|
@@ -1612,7 +2030,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1612
2030
|
if (this.pluginName && this.sourceId) {
|
|
1613
2031
|
const key = `${this.sourceId}:${this.pluginName}`;
|
|
1614
2032
|
if (env.pluginEnabled && env.pluginEnabled[key] !== true) {
|
|
1615
|
-
|
|
2033
|
+
logger7.debug(`Plugin not enabled for source: ${key}`);
|
|
1616
2034
|
return;
|
|
1617
2035
|
}
|
|
1618
2036
|
}
|
|
@@ -1715,11 +2133,11 @@ class LarkCliTaskNotifyHook {
|
|
|
1715
2133
|
try {
|
|
1716
2134
|
record = this.cardStateStore?.get(taskId);
|
|
1717
2135
|
} catch (err) {
|
|
1718
|
-
|
|
2136
|
+
logger7.warn(`onAfterDelete: 读取卡片记录失败 task #${taskId}: ${err.message}`);
|
|
1719
2137
|
}
|
|
1720
2138
|
const messageId = record?.messageId || this.cardMessageIds.get(taskId);
|
|
1721
2139
|
if (!messageId) {
|
|
1722
|
-
|
|
2140
|
+
logger7.debug(`onAfterDelete: task #${taskId} 无卡片记录,skip(不发新卡)`);
|
|
1723
2141
|
return;
|
|
1724
2142
|
}
|
|
1725
2143
|
this.restoreCardState(taskId);
|
|
@@ -1734,17 +2152,18 @@ class LarkCliTaskNotifyHook {
|
|
|
1734
2152
|
finalStep: "任务已取消(删除)"
|
|
1735
2153
|
});
|
|
1736
2154
|
} catch (err) {
|
|
1737
|
-
|
|
2155
|
+
logger7.warn(`onAfterDelete: patch 卡片失败 task #${taskId} message=${messageId}: ${err.message}`);
|
|
1738
2156
|
}
|
|
1739
2157
|
try {
|
|
1740
2158
|
await this.cardNotifier.unpinMessage(messageId, profile);
|
|
1741
2159
|
} catch (err) {
|
|
1742
|
-
|
|
2160
|
+
logger7.warn(`onAfterDelete: unpin 失败 task #${taskId} message=${messageId}: ${err.message}`);
|
|
1743
2161
|
}
|
|
2162
|
+
this.cancelTrailingPatch(taskId);
|
|
1744
2163
|
this.persistClosed(taskId);
|
|
1745
2164
|
this.persistPinned(taskId, false);
|
|
1746
2165
|
this.cardMessageIds.delete(taskId);
|
|
1747
|
-
|
|
2166
|
+
logger7.debug(`onAfterDelete: task #${taskId} 卡片已收尾 status=deleted message=${messageId}`);
|
|
1748
2167
|
}
|
|
1749
2168
|
async onAfterOperationCreate(ctx) {
|
|
1750
2169
|
const { operation } = ctx.data;
|
|
@@ -1752,18 +2171,16 @@ class LarkCliTaskNotifyHook {
|
|
|
1752
2171
|
return;
|
|
1753
2172
|
if (!this.shouldNotify("operation_created" /* OPERATION_CREATED */))
|
|
1754
2173
|
return;
|
|
1755
|
-
if (this.shouldThrottleOperation(operation))
|
|
1756
|
-
return;
|
|
1757
2174
|
if (!this.isCardModeEnabled())
|
|
1758
2175
|
return;
|
|
1759
2176
|
const persisted = this.restoreCardState(operation.taskId);
|
|
1760
2177
|
const messageId = this.cardMessageIds.get(operation.taskId);
|
|
1761
2178
|
if (!messageId || messageId === LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL || messageId === LarkCliTaskNotifyHook.CARD_START_INFLIGHT_SENTINEL) {
|
|
1762
|
-
|
|
2179
|
+
logger7.debug(`onAfterOperationCreate: no usable card for task #${operation.taskId}, skip`);
|
|
1763
2180
|
return;
|
|
1764
2181
|
}
|
|
1765
2182
|
if (persisted && persisted.status !== "open") {
|
|
1766
|
-
|
|
2183
|
+
logger7.debug(`onAfterOperationCreate: task #${operation.taskId} card status=${persisted.status}, skip`);
|
|
1767
2184
|
return;
|
|
1768
2185
|
}
|
|
1769
2186
|
const { milestones, processSteps } = this.deriveTimelineFromOperation(operation);
|
|
@@ -1771,6 +2188,17 @@ class LarkCliTaskNotifyHook {
|
|
|
1771
2188
|
return;
|
|
1772
2189
|
const env = getEnvContextOrEmpty();
|
|
1773
2190
|
const profile = env.replyChannel?.profile;
|
|
2191
|
+
this.cardNotifier.mergeTimelineState?.(messageId, milestones, processSteps);
|
|
2192
|
+
this.refreshTimeline(operation.taskId);
|
|
2193
|
+
const throttled = this.shouldThrottleOperation(operation);
|
|
2194
|
+
if (throttled) {
|
|
2195
|
+
this.scheduleTrailingPatch(operation.taskId, messageId, profile);
|
|
2196
|
+
logger7.debug(`[throttle] OPERATION_CREATED for task #${operation.taskId} ` + `merged into state + ledger; card patch deferred to window end`);
|
|
2197
|
+
return;
|
|
2198
|
+
}
|
|
2199
|
+
await this.patchOperationTimeline(operation.taskId, messageId, profile, milestones, processSteps);
|
|
2200
|
+
}
|
|
2201
|
+
async patchOperationTimeline(taskId, messageId, profile, milestones, processSteps) {
|
|
1774
2202
|
try {
|
|
1775
2203
|
await this.cardNotifier.updateTask({
|
|
1776
2204
|
messageId,
|
|
@@ -1780,9 +2208,41 @@ class LarkCliTaskNotifyHook {
|
|
|
1780
2208
|
processSteps
|
|
1781
2209
|
});
|
|
1782
2210
|
} catch (err) {
|
|
1783
|
-
|
|
2211
|
+
logger7.error(`sendCardOperation failed for task #${taskId}: ${err.message}`);
|
|
1784
2212
|
}
|
|
1785
2213
|
}
|
|
2214
|
+
scheduleTrailingPatch(taskId, messageId, profile) {
|
|
2215
|
+
const existing = this.trailingPatchTimers.get(taskId);
|
|
2216
|
+
if (existing) {
|
|
2217
|
+
return;
|
|
2218
|
+
}
|
|
2219
|
+
const throttleMs = this.config.operationThrottleMs ?? 5000;
|
|
2220
|
+
const delay = throttleMs > 0 ? throttleMs : 0;
|
|
2221
|
+
const timer = setTimeout(() => {
|
|
2222
|
+
this.trailingPatchTimers.delete(taskId);
|
|
2223
|
+
const liveMessageId = this.cardMessageIds.get(taskId);
|
|
2224
|
+
if (liveMessageId !== messageId) {
|
|
2225
|
+
logger7.debug(`[throttle] trailing patch skipped for task #${taskId}: ` + `card no longer tracked (finished/closed)`);
|
|
2226
|
+
return;
|
|
2227
|
+
}
|
|
2228
|
+
const rec = this.cardStateStore?.get(taskId);
|
|
2229
|
+
if (rec && rec.status !== "open") {
|
|
2230
|
+
logger7.debug(`[throttle] trailing patch skipped for task #${taskId}: ` + `card status=${rec.status}`);
|
|
2231
|
+
return;
|
|
2232
|
+
}
|
|
2233
|
+
this.patchOperationTimeline(taskId, messageId, profile, [], []);
|
|
2234
|
+
}, delay);
|
|
2235
|
+
if (typeof timer.unref === "function")
|
|
2236
|
+
timer.unref();
|
|
2237
|
+
this.trailingPatchTimers.set(taskId, timer);
|
|
2238
|
+
}
|
|
2239
|
+
cancelTrailingPatch(taskId) {
|
|
2240
|
+
const timer = this.trailingPatchTimers.get(taskId);
|
|
2241
|
+
if (!timer)
|
|
2242
|
+
return;
|
|
2243
|
+
clearTimeout(timer);
|
|
2244
|
+
this.trailingPatchTimers.delete(taskId);
|
|
2245
|
+
}
|
|
1786
2246
|
restoreCardState(taskId) {
|
|
1787
2247
|
if (!this.cardStateStore)
|
|
1788
2248
|
return;
|
|
@@ -1795,12 +2255,12 @@ class LarkCliTaskNotifyHook {
|
|
|
1795
2255
|
if (rec.status !== "closed" && rec.messageId) {
|
|
1796
2256
|
this.cardMessageIds.set(taskId, rec.messageId);
|
|
1797
2257
|
this.hydrateNotifierState(rec);
|
|
1798
|
-
|
|
2258
|
+
logger7.debug(`restoreCardState: task #${taskId} ← 持久层 message ${rec.messageId} ` + `(status=${rec.status}, pinned=${rec.pinned}, timeline=${rec.timeline ? "yes" : "no"})`);
|
|
1799
2259
|
}
|
|
1800
2260
|
}
|
|
1801
2261
|
return rec;
|
|
1802
2262
|
} catch (err) {
|
|
1803
|
-
|
|
2263
|
+
logger7.warn(`restoreCardState failed for task #${taskId}: ${err.message}`);
|
|
1804
2264
|
return;
|
|
1805
2265
|
}
|
|
1806
2266
|
}
|
|
@@ -1811,7 +2271,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1811
2271
|
const snapshot = JSON.parse(rec.timeline);
|
|
1812
2272
|
this.cardNotifier.hydrateState?.(rec.messageId, snapshot);
|
|
1813
2273
|
} catch (err) {
|
|
1814
|
-
|
|
2274
|
+
logger7.warn(`hydrateNotifierState failed for task #${rec.taskId}: ${err.message}`);
|
|
1815
2275
|
}
|
|
1816
2276
|
}
|
|
1817
2277
|
snapshotTimeline(state) {
|
|
@@ -1823,7 +2283,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1823
2283
|
return state;
|
|
1824
2284
|
return { ...state, timeline: JSON.stringify(snap) };
|
|
1825
2285
|
} catch (err) {
|
|
1826
|
-
|
|
2286
|
+
logger7.warn(`snapshotTimeline failed for task #${state.taskId}: ${err.message}`);
|
|
1827
2287
|
return state;
|
|
1828
2288
|
}
|
|
1829
2289
|
}
|
|
@@ -1834,7 +2294,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1834
2294
|
const payload = includeTimeline ? this.snapshotTimeline(state) : state;
|
|
1835
2295
|
this.cardStateStore.upsert(payload);
|
|
1836
2296
|
} catch (err) {
|
|
1837
|
-
|
|
2297
|
+
logger7.warn(`persistCardState failed for task #${state.taskId}: ${err.message}`);
|
|
1838
2298
|
}
|
|
1839
2299
|
}
|
|
1840
2300
|
persistPinned(taskId, pinned) {
|
|
@@ -1843,7 +2303,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1843
2303
|
try {
|
|
1844
2304
|
this.cardStateStore.markPinned(taskId, pinned);
|
|
1845
2305
|
} catch (err) {
|
|
1846
|
-
|
|
2306
|
+
logger7.warn(`persistPinned failed for task #${taskId}: ${err.message}`);
|
|
1847
2307
|
}
|
|
1848
2308
|
}
|
|
1849
2309
|
persistPaused(taskId) {
|
|
@@ -1852,7 +2312,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1852
2312
|
try {
|
|
1853
2313
|
this.cardStateStore.markPaused(taskId);
|
|
1854
2314
|
} catch (err) {
|
|
1855
|
-
|
|
2315
|
+
logger7.warn(`persistPaused failed for task #${taskId}: ${err.message}`);
|
|
1856
2316
|
}
|
|
1857
2317
|
}
|
|
1858
2318
|
dropStaleMessageId(taskId) {
|
|
@@ -1869,7 +2329,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1869
2329
|
updatedAt: Date.now()
|
|
1870
2330
|
});
|
|
1871
2331
|
} catch (err) {
|
|
1872
|
-
|
|
2332
|
+
logger7.warn(`dropStaleMessageId failed for task #${taskId}: ${err.message}`);
|
|
1873
2333
|
}
|
|
1874
2334
|
}
|
|
1875
2335
|
persistReopened(taskId) {
|
|
@@ -1878,7 +2338,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1878
2338
|
try {
|
|
1879
2339
|
this.cardStateStore.markReopened(taskId);
|
|
1880
2340
|
} catch (err) {
|
|
1881
|
-
|
|
2341
|
+
logger7.warn(`persistReopened failed for task #${taskId}: ${err.message}`);
|
|
1882
2342
|
}
|
|
1883
2343
|
}
|
|
1884
2344
|
persistClosed(taskId) {
|
|
@@ -1887,7 +2347,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1887
2347
|
try {
|
|
1888
2348
|
this.cardStateStore.markClosed(taskId);
|
|
1889
2349
|
} catch (err) {
|
|
1890
|
-
|
|
2350
|
+
logger7.warn(`persistClosed failed for task #${taskId}: ${err.message}`);
|
|
1891
2351
|
}
|
|
1892
2352
|
}
|
|
1893
2353
|
refreshTimeline(taskId) {
|
|
@@ -1899,7 +2359,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1899
2359
|
return;
|
|
1900
2360
|
this.cardStateStore.upsert(this.snapshotTimeline(rec));
|
|
1901
2361
|
} catch (err) {
|
|
1902
|
-
|
|
2362
|
+
logger7.warn(`refreshTimeline failed for task #${taskId}: ${err.message}`);
|
|
1903
2363
|
}
|
|
1904
2364
|
}
|
|
1905
2365
|
async repinAfterResume(task, persisted) {
|
|
@@ -1915,9 +2375,9 @@ class LarkCliTaskNotifyHook {
|
|
|
1915
2375
|
try {
|
|
1916
2376
|
await this.cardNotifier.pinMessage(persisted.messageId, profile);
|
|
1917
2377
|
this.persistPinned(task.id, true);
|
|
1918
|
-
|
|
2378
|
+
logger7.debug(`repinAfterResume: task #${task.id} 卡片 ${persisted.messageId} 已重新置顶`);
|
|
1919
2379
|
} catch (err) {
|
|
1920
|
-
|
|
2380
|
+
logger7.warn(`repinAfterResume failed (non-fatal) for task #${task.id}: ${err.message}`);
|
|
1921
2381
|
}
|
|
1922
2382
|
}
|
|
1923
2383
|
buildCardState(task, messageId, chatId, profile, pinned) {
|
|
@@ -1925,7 +2385,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1925
2385
|
try {
|
|
1926
2386
|
existing = this.cardStateStore?.get(task.id);
|
|
1927
2387
|
} catch (err) {
|
|
1928
|
-
|
|
2388
|
+
logger7.warn(`buildCardState: read existing failed for task #${task.id}: ${err.message}`);
|
|
1929
2389
|
}
|
|
1930
2390
|
const now = Date.now();
|
|
1931
2391
|
return {
|
|
@@ -1959,7 +2419,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1959
2419
|
}
|
|
1960
2420
|
isCardModeEnabled() {
|
|
1961
2421
|
if (this.config.cardMode === false || !this.cardNotifier) {
|
|
1962
|
-
|
|
2422
|
+
logger7.debug("cardMode=false (legacy opt-out), skip task notification (no text fallback)");
|
|
1963
2423
|
return false;
|
|
1964
2424
|
}
|
|
1965
2425
|
return true;
|
|
@@ -2029,7 +2489,7 @@ class LarkCliTaskNotifyHook {
|
|
|
2029
2489
|
}
|
|
2030
2490
|
const claim = this.cardMessageIds.get(task.id);
|
|
2031
2491
|
if (claim) {
|
|
2032
|
-
|
|
2492
|
+
logger7.debug(`sendCardStart: task #${task.id} 已有首卡(${claim})→ 跳过重复发卡`);
|
|
2033
2493
|
return;
|
|
2034
2494
|
}
|
|
2035
2495
|
this.cardMessageIds.set(task.id, LarkCliTaskNotifyHook.CARD_START_INFLIGHT_SENTINEL);
|
|
@@ -2044,7 +2504,7 @@ class LarkCliTaskNotifyHook {
|
|
|
2044
2504
|
const profile = target.profile;
|
|
2045
2505
|
if (!chatId) {
|
|
2046
2506
|
this.cardMessageIds.delete(task.id);
|
|
2047
|
-
|
|
2507
|
+
logger7.debug(`sendCardStart: no chatId for task #${task.id}, skip`);
|
|
2048
2508
|
return;
|
|
2049
2509
|
}
|
|
2050
2510
|
try {
|
|
@@ -2062,10 +2522,10 @@ class LarkCliTaskNotifyHook {
|
|
|
2062
2522
|
});
|
|
2063
2523
|
this.cardMessageIds.set(task.id, messageId);
|
|
2064
2524
|
this.persistCardState(this.buildCardState(task, messageId, chatId, profile, pinned), true);
|
|
2065
|
-
|
|
2525
|
+
logger7.debug(`sendCardStart: task #${task.id} → message ${messageId} (pinned=${pinned}) ` + `chat=${chatId} source=${target.source} reason=${target.reason}`);
|
|
2066
2526
|
} catch (err) {
|
|
2067
2527
|
this.cardMessageIds.set(task.id, LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL);
|
|
2068
|
-
|
|
2528
|
+
logger7.error(`sendCardStart failed for task #${task.id}: ${err.message}`);
|
|
2069
2529
|
}
|
|
2070
2530
|
}
|
|
2071
2531
|
async sendCardUpdate(task, changes) {
|
|
@@ -2075,21 +2535,21 @@ class LarkCliTaskNotifyHook {
|
|
|
2075
2535
|
if (persisted?.status === "closed") {
|
|
2076
2536
|
const stillTerminal = isTerminalTaskStatus(String(task.status));
|
|
2077
2537
|
if (stillTerminal) {
|
|
2078
|
-
|
|
2538
|
+
logger7.debug(`sendCardUpdate: task #${task.id} 卡片已终结(closed)且任务仍终态 → skip patch`);
|
|
2079
2539
|
return;
|
|
2080
2540
|
}
|
|
2081
|
-
|
|
2541
|
+
logger7.info(`sendCardUpdate: task #${task.id} 已 reopen(closed 记录 + 当前 status=${task.status})` + `→ 重新建卡`);
|
|
2082
2542
|
this.cardMessageIds.delete(task.id);
|
|
2083
2543
|
this.dropStaleMessageId(task.id);
|
|
2084
2544
|
}
|
|
2085
2545
|
if (persisted?.status === "paused" && String(task.status) !== "paused") {
|
|
2086
2546
|
this.persistReopened(task.id);
|
|
2087
|
-
|
|
2547
|
+
logger7.debug(`sendCardUpdate: task #${task.id} resume(paused → open),复用卡片 ${persisted.messageId}`);
|
|
2088
2548
|
await this.repinAfterResume(task, persisted);
|
|
2089
2549
|
}
|
|
2090
2550
|
let messageId = this.cardMessageIds.get(task.id);
|
|
2091
2551
|
if (!messageId && isTerminalTaskStatus(String(task.status))) {
|
|
2092
|
-
|
|
2552
|
+
logger7.debug(`sendCardUpdate: task #${task.id} 状态=${task.status}(终结/归档)且无卡片 → skip`);
|
|
2093
2553
|
return;
|
|
2094
2554
|
}
|
|
2095
2555
|
if (!messageId) {
|
|
@@ -2101,15 +2561,15 @@ class LarkCliTaskNotifyHook {
|
|
|
2101
2561
|
if (!chatId) {
|
|
2102
2562
|
if (!messageId)
|
|
2103
2563
|
this.cardMessageIds.delete(task.id);
|
|
2104
|
-
|
|
2564
|
+
logger7.debug(`sendCardUpdate: no chatId for task #${task.id}, skip`);
|
|
2105
2565
|
return;
|
|
2106
2566
|
}
|
|
2107
2567
|
if (messageId === LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL) {
|
|
2108
|
-
|
|
2568
|
+
logger7.debug(`sendCardUpdate: startTask failed previously for task #${task.id}, skip`);
|
|
2109
2569
|
return;
|
|
2110
2570
|
}
|
|
2111
2571
|
if (messageId === LarkCliTaskNotifyHook.CARD_START_INFLIGHT_SENTINEL) {
|
|
2112
|
-
|
|
2572
|
+
logger7.debug(`sendCardUpdate: task #${task.id} 首卡正在发送中(in-flight)→ 跳过本次 update`);
|
|
2113
2573
|
return;
|
|
2114
2574
|
}
|
|
2115
2575
|
if (!messageId) {
|
|
@@ -2124,7 +2584,7 @@ class LarkCliTaskNotifyHook {
|
|
|
2124
2584
|
this.persistCardState(this.buildCardState(task, messageId, chatId, profile, res.pinned), true);
|
|
2125
2585
|
} catch (err) {
|
|
2126
2586
|
this.cardMessageIds.set(task.id, LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL);
|
|
2127
|
-
|
|
2587
|
+
logger7.error(`sendCardUpdate fallback start failed: ${err.message}`);
|
|
2128
2588
|
return;
|
|
2129
2589
|
}
|
|
2130
2590
|
}
|
|
@@ -2151,7 +2611,7 @@ class LarkCliTaskNotifyHook {
|
|
|
2151
2611
|
this.persistPinned(task.id, current?.pinned ?? persisted?.pinned ?? true);
|
|
2152
2612
|
this.refreshTimeline(task.id);
|
|
2153
2613
|
} catch (err) {
|
|
2154
|
-
|
|
2614
|
+
logger7.error(`sendCardUpdate failed: ${err.message}`);
|
|
2155
2615
|
}
|
|
2156
2616
|
}
|
|
2157
2617
|
async sendCardFinish(task) {
|
|
@@ -2162,13 +2622,14 @@ class LarkCliTaskNotifyHook {
|
|
|
2162
2622
|
const ctx = getEnvContextOrEmpty();
|
|
2163
2623
|
const profile = this.resolveProfile(task, ctx);
|
|
2164
2624
|
if (!messageId || messageId === LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL) {
|
|
2165
|
-
|
|
2625
|
+
logger7.debug(`sendCardFinish: no card messageId for task #${task.id}, skip ` + `(记录缺失 → 不发新卡,保持幂等)`);
|
|
2166
2626
|
return;
|
|
2167
2627
|
}
|
|
2168
2628
|
const rawStatus = String(task.status);
|
|
2169
2629
|
const status = mapTaskStatusToCardStatus(rawStatus);
|
|
2170
2630
|
const isPaused = rawStatus === "paused" || rawStatus === "archived";
|
|
2171
2631
|
try {
|
|
2632
|
+
this.cancelTrailingPatch(task.id);
|
|
2172
2633
|
const finish = isPaused ? this.cardNotifier.pauseTask.bind(this.cardNotifier) : this.cardNotifier.finishTask.bind(this.cardNotifier);
|
|
2173
2634
|
await finish({
|
|
2174
2635
|
messageId,
|
|
@@ -2188,9 +2649,9 @@ class LarkCliTaskNotifyHook {
|
|
|
2188
2649
|
this.persistPinned(task.id, false);
|
|
2189
2650
|
this.cardMessageIds.delete(task.id);
|
|
2190
2651
|
}
|
|
2191
|
-
|
|
2652
|
+
logger7.debug(`sendCardFinish: task #${task.id} → status=${status} ` + `(raw=${rawStatus}) message=${messageId}`);
|
|
2192
2653
|
} catch (err) {
|
|
2193
|
-
|
|
2654
|
+
logger7.error(`sendCardFinish failed: ${err.message}`);
|
|
2194
2655
|
}
|
|
2195
2656
|
}
|
|
2196
2657
|
resolveChatId(task, env) {
|
|
@@ -2267,7 +2728,7 @@ class LarkCliTaskNotifyHook {
|
|
|
2267
2728
|
const now = Date.now();
|
|
2268
2729
|
const lastTime = this.lastNotifyTimestamps.get(key);
|
|
2269
2730
|
if (lastTime && now - lastTime < throttleMs) {
|
|
2270
|
-
|
|
2731
|
+
logger7.debug(`[throttle] skip OPERATION_CREATED patch for task #${operation.taskId} (${now - lastTime}ms ago)`);
|
|
2271
2732
|
return true;
|
|
2272
2733
|
}
|
|
2273
2734
|
this.lastNotifyTimestamps.set(key, now);
|
|
@@ -2310,7 +2771,7 @@ init_decorator();
|
|
|
2310
2771
|
init_logger();
|
|
2311
2772
|
init_context();
|
|
2312
2773
|
import { spawn } from "child_process";
|
|
2313
|
-
var
|
|
2774
|
+
var logger8 = createLogger("lark-cli:per-task-group");
|
|
2314
2775
|
function isPluginDebugEnabled() {
|
|
2315
2776
|
return process.env.ROY_TASK_NOTIFY_DEBUG === "1" || process.env.ROY_TRACE_DEBUG === "1";
|
|
2316
2777
|
}
|
|
@@ -2365,7 +2826,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2365
2826
|
const renderedName = this.renderTemplate(this.config.nameTemplate, task);
|
|
2366
2827
|
const existingChatId = this.getTaskChatId(task);
|
|
2367
2828
|
if (existingChatId) {
|
|
2368
|
-
|
|
2829
|
+
logger8.debug(`ensureGroupChat[${this.pluginName}]: task #${task.id} 已有专属群 ${existingChatId} → 复用(不重复建群)`);
|
|
2369
2830
|
return {
|
|
2370
2831
|
chatId: existingChatId,
|
|
2371
2832
|
profile: this.parseProfileFromContext(task.context),
|
|
@@ -2374,7 +2835,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2374
2835
|
}
|
|
2375
2836
|
const inflight = this.groupChatInflight.get(task.id);
|
|
2376
2837
|
if (inflight) {
|
|
2377
|
-
|
|
2838
|
+
logger8.debug(`ensureGroupChat[${this.pluginName}]: task #${task.id} 建群已在进行中 → 复用同一 promise`);
|
|
2378
2839
|
return inflight;
|
|
2379
2840
|
}
|
|
2380
2841
|
const pending = this.doEnsureGroupChat(task, renderedName, replyChannelOverride);
|
|
@@ -2388,7 +2849,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2388
2849
|
async doEnsureGroupChat(task, name, replyChannelOverride) {
|
|
2389
2850
|
const replyChannel = replyChannelOverride ?? getEnvContextOrEmpty().replyChannel;
|
|
2390
2851
|
if (!replyChannel?.chatId && this.config.requireReplyChannel) {
|
|
2391
|
-
|
|
2852
|
+
logger8.warn(`ensureGroupChat[${this.pluginName}]: no replyChannel.chatId for task #${task.id} ` + `(requireReplyChannel=true) → 无法建群`);
|
|
2392
2853
|
return;
|
|
2393
2854
|
}
|
|
2394
2855
|
const description = this.renderTemplate(this.config.descriptionTemplate, task);
|
|
@@ -2406,30 +2867,30 @@ class PerTaskGroupChatPlugin {
|
|
|
2406
2867
|
await this.writeChatBinding(task, chatId, profile, "creating");
|
|
2407
2868
|
await this.attachChatIdToTask(task, chatId, profile);
|
|
2408
2869
|
await this.writeChatBinding(task, chatId, profile, "ready");
|
|
2409
|
-
|
|
2870
|
+
logger8.info(`ensureGroupChat[${this.pluginName}]: created group ${chatId} for task #${task.id}`);
|
|
2410
2871
|
return { chatId, profile, name };
|
|
2411
2872
|
} catch (error) {
|
|
2412
|
-
|
|
2873
|
+
logger8.error(`ensureGroupChat[${this.pluginName}]: failed for task #${task.id}: ${error}`);
|
|
2413
2874
|
return;
|
|
2414
2875
|
}
|
|
2415
2876
|
}
|
|
2416
2877
|
setTaskUpdater(updater) {
|
|
2417
2878
|
this.taskUpdater = updater;
|
|
2418
|
-
|
|
2879
|
+
logger8.debug(`TaskUpdater injected for plugin ${this.name} (sourceId=${this.sourceId})`);
|
|
2419
2880
|
}
|
|
2420
2881
|
hasTaskUpdater() {
|
|
2421
2882
|
return this.taskUpdater !== undefined;
|
|
2422
2883
|
}
|
|
2423
2884
|
setTaskGetter(getter) {
|
|
2424
2885
|
this.taskGetter = getter;
|
|
2425
|
-
|
|
2886
|
+
logger8.debug(`TaskGetter injected for plugin ${this.name} (sourceId=${this.sourceId})`);
|
|
2426
2887
|
}
|
|
2427
2888
|
hasTaskGetter() {
|
|
2428
2889
|
return this.taskGetter !== undefined;
|
|
2429
2890
|
}
|
|
2430
2891
|
setSessionComponent(sessionComponent) {
|
|
2431
2892
|
this.sessionComponent = sessionComponent;
|
|
2432
|
-
|
|
2893
|
+
logger8.debug(`SessionComponent injected for plugin ${this.name} (sourceId=${this.sourceId})`);
|
|
2433
2894
|
}
|
|
2434
2895
|
hasSessionComponent() {
|
|
2435
2896
|
return this.sessionComponent !== undefined;
|
|
@@ -2442,7 +2903,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2442
2903
|
if (process.env.ROY_HOOK_DEBUG === "1") {
|
|
2443
2904
|
console.error(`[DEBUG per-task-group-chat.execute] → SKIP (config.enabled=false)`);
|
|
2444
2905
|
}
|
|
2445
|
-
|
|
2906
|
+
logger8.debug(`execute: plugin disabled (pluginName=${this.pluginName})`);
|
|
2446
2907
|
return;
|
|
2447
2908
|
}
|
|
2448
2909
|
if (this.pluginName && this.sourceId) {
|
|
@@ -2455,13 +2916,13 @@ class PerTaskGroupChatPlugin {
|
|
|
2455
2916
|
if (process.env.ROY_HOOK_DEBUG === "1") {
|
|
2456
2917
|
console.error(`[DEBUG per-task-group-chat.execute] → SKIP (plugin NOT enabled for ${key})`);
|
|
2457
2918
|
}
|
|
2458
|
-
|
|
2919
|
+
logger8.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`);
|
|
2459
2920
|
return;
|
|
2460
2921
|
}
|
|
2461
2922
|
const enabledState = env.pluginEnabled ? env.pluginEnabled[key] === true ? "✅ enabled" : "❌ NOT enabled" : "⚠️ env.pluginEnabled 未设置 (env 没创建 / 插件未注册到 eventsource)";
|
|
2462
|
-
|
|
2923
|
+
logger8.debug(`execute[${this.pluginName}]: ${enabledState} (key=${key}, ` + `hookPoint=${ctx.hookPoint}, taskUpdaterInjected=${this.hasTaskUpdater()})`);
|
|
2463
2924
|
} else {
|
|
2464
|
-
|
|
2925
|
+
logger8.debug(`execute[${this.pluginName}]: missing pluginName/sourceId metadata ` + `(pluginName=${this.pluginName}, sourceId=${this.sourceId}), ` + `skipping pluginEnabled check. hookPoint=${ctx.hookPoint}`);
|
|
2465
2926
|
}
|
|
2466
2927
|
if (process.env.ROY_HOOK_DEBUG === "1") {
|
|
2467
2928
|
console.error(`[DEBUG per-task-group-chat.execute] → PASSED pluginEnabled check, will dispatch hookPoint=${ctx.hookPoint}`);
|
|
@@ -2480,13 +2941,13 @@ class PerTaskGroupChatPlugin {
|
|
|
2480
2941
|
await this.onAfterComplete(ctx);
|
|
2481
2942
|
break;
|
|
2482
2943
|
default:
|
|
2483
|
-
|
|
2944
|
+
logger8.debug(`execute: unhandled hookPoint=${ctx.hookPoint}, skip`);
|
|
2484
2945
|
}
|
|
2485
2946
|
}
|
|
2486
2947
|
async onAfterCreate(ctx) {
|
|
2487
2948
|
const data = ctx.data;
|
|
2488
2949
|
if (!data?.task) {
|
|
2489
|
-
|
|
2950
|
+
logger8.debug("onAfterCreate: task is missing, skip");
|
|
2490
2951
|
return;
|
|
2491
2952
|
}
|
|
2492
2953
|
const { task } = data;
|
|
@@ -2497,38 +2958,38 @@ class PerTaskGroupChatPlugin {
|
|
|
2497
2958
|
console.error(`[DEBUG per-task-group-chat.onAfterCreate] env.messageId=${env.messageId}`);
|
|
2498
2959
|
console.error(`[DEBUG per-task-group-chat.onAfterCreate] env.sourceId=${env.sourceId}`);
|
|
2499
2960
|
}
|
|
2500
|
-
|
|
2961
|
+
logger8.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}`);
|
|
2501
2962
|
if (!this.config.autoCreateGroup) {
|
|
2502
|
-
|
|
2963
|
+
logger8.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} autoCreateGroup=false ` + `→ skip group chat creation(按需建群:用户在卡片顶部按钮触发)`);
|
|
2503
2964
|
return;
|
|
2504
2965
|
}
|
|
2505
2966
|
const replyChannel = this.getReplyChannelFromCtx(ctx);
|
|
2506
2967
|
if (!replyChannel?.chatId && this.config.requireReplyChannel) {
|
|
2507
|
-
|
|
2968
|
+
logger8.debug(`onAfterCreate[${this.pluginName}]: no replyChannel.chatId for task #${task.id}, skip. ` + `修复:eventsource 收到 im.message.receive_v1 事件时 replyChannel 应有值;` + `或在 plugin config 设 requireReplyChannel=false 强制创建群聊(profile 降级到 fallbackProfile/无)`);
|
|
2508
2969
|
return;
|
|
2509
2970
|
}
|
|
2510
2971
|
if (!replyChannel?.chatId && !this.config.requireReplyChannel) {
|
|
2511
|
-
|
|
2972
|
+
logger8.debug(`onAfterCreate[${this.pluginName}]: no replyChannel but requireReplyChannel=false, ` + `proceeding with profile fallback (fallbackProfile="${this.config.fallbackProfile}")`);
|
|
2512
2973
|
}
|
|
2513
2974
|
if (this.isSubTask(task) && !this.config.disableSubTaskSkip) {
|
|
2514
|
-
|
|
2975
|
+
logger8.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} is sub-task, ` + `skip group chat creation. 关闭方式:config.disableSubTaskSkip=true`);
|
|
2515
2976
|
return;
|
|
2516
2977
|
}
|
|
2517
2978
|
if (this.isSubTask(task) && this.config.disableSubTaskSkip) {
|
|
2518
|
-
|
|
2979
|
+
logger8.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} is sub-task but ` + `disableSubTaskSkip=true, proceeding to create group chat`);
|
|
2519
2980
|
}
|
|
2520
2981
|
try {
|
|
2521
2982
|
const created = await this.ensureGroupChat(task, replyChannel);
|
|
2522
2983
|
if (!created?.chatId) {
|
|
2523
|
-
|
|
2984
|
+
logger8.error(`onAfterCreate[${this.pluginName}]: ensureGroupChat 未返回 chatId for task #${task.id}`);
|
|
2524
2985
|
if (!this.config.fallbackToMain) {
|
|
2525
2986
|
throw new Error(`per-task group chat unavailable for task #${task.id}`);
|
|
2526
2987
|
}
|
|
2527
2988
|
return;
|
|
2528
2989
|
}
|
|
2529
|
-
|
|
2990
|
+
logger8.info(`onAfterCreate[${this.pluginName}]: task #${task.id} → group ${created.chatId} ` + `(initial card owned by LarkCliTaskNotifyHook)`);
|
|
2530
2991
|
} catch (error) {
|
|
2531
|
-
|
|
2992
|
+
logger8.error(`onAfterCreate[${this.pluginName}]: group chat failed for task #${task.id}: ${error}`);
|
|
2532
2993
|
if (!this.config.fallbackToMain)
|
|
2533
2994
|
throw error;
|
|
2534
2995
|
}
|
|
@@ -2536,7 +2997,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2536
2997
|
async writeChatBinding(task, chatId, profile, status, errorMessage) {
|
|
2537
2998
|
if (!this.sessionComponent) {
|
|
2538
2999
|
if (isPluginDebugEnabled()) {
|
|
2539
|
-
|
|
3000
|
+
logger8.debug(`writeChatBinding[${this.pluginName}]: no sessionComponent injected, ` + `skip chat-task binding for chat ${chatId} (status=${status})`);
|
|
2540
3001
|
}
|
|
2541
3002
|
return;
|
|
2542
3003
|
}
|
|
@@ -2549,37 +3010,37 @@ class PerTaskGroupChatPlugin {
|
|
|
2549
3010
|
errorMessage: errorMessage ?? null
|
|
2550
3011
|
});
|
|
2551
3012
|
} catch (bindErr) {
|
|
2552
|
-
|
|
3013
|
+
logger8.warn(`writeChatBinding[${this.pluginName}]: failed to write chat-task binding for ` + `chat ${chatId} / task #${task.id} (status=${status}): ${bindErr}. ` + `群消息 handler 将无法自动注入 parentTaskId。`);
|
|
2553
3014
|
}
|
|
2554
3015
|
}
|
|
2555
3016
|
async onAfterUpdate(ctx) {
|
|
2556
3017
|
const data = ctx.data;
|
|
2557
3018
|
if (!data?.task) {
|
|
2558
|
-
|
|
3019
|
+
logger8.debug("onAfterUpdate: task is missing, skip");
|
|
2559
3020
|
return;
|
|
2560
3021
|
}
|
|
2561
|
-
|
|
3022
|
+
logger8.debug(`onAfterUpdate[${this.pluginName}]: task #${data.task.id} 卡片由 ` + `LarkCliTaskNotifyHook 负责(本插件不再 patch),skip`);
|
|
2562
3023
|
}
|
|
2563
3024
|
async onAfterOperationCreate(ctx) {
|
|
2564
3025
|
const data = ctx.data;
|
|
2565
3026
|
if (!data?.operation) {
|
|
2566
|
-
|
|
3027
|
+
logger8.debug("onAfterOperationCreate: operation is missing, skip");
|
|
2567
3028
|
return;
|
|
2568
3029
|
}
|
|
2569
|
-
|
|
3030
|
+
logger8.debug(`onAfterOperationCreate[${this.pluginName}]: operation #${data.operation.id} ` + `卡片由 LarkCliTaskNotifyHook 负责,skip`);
|
|
2570
3031
|
}
|
|
2571
3032
|
async onAfterComplete(ctx) {
|
|
2572
3033
|
const data = ctx.data;
|
|
2573
3034
|
if (!data?.task) {
|
|
2574
|
-
|
|
3035
|
+
logger8.debug("onAfterComplete: task is missing, skip");
|
|
2575
3036
|
return;
|
|
2576
3037
|
}
|
|
2577
|
-
|
|
3038
|
+
logger8.debug(`onAfterComplete[${this.pluginName}]: task #${data.task.id} 终态卡片由 ` + `LarkCliTaskNotifyHook 负责,skip`);
|
|
2578
3039
|
}
|
|
2579
3040
|
createGroupChat(opts) {
|
|
2580
3041
|
const args = this.buildCreateGroupChatArgs(opts);
|
|
2581
3042
|
if (isPluginDebugEnabled()) {
|
|
2582
|
-
|
|
3043
|
+
logger8.debug(`Creating group chat: lark-cli ${args.join(" ")}`);
|
|
2583
3044
|
}
|
|
2584
3045
|
return new Promise((resolve, reject) => {
|
|
2585
3046
|
const proc = spawn("lark-cli", args, {
|
|
@@ -2654,16 +3115,16 @@ class PerTaskGroupChatPlugin {
|
|
|
2654
3115
|
parsed[CTX_FIELD_CHAT_TASK_ID] = task.id;
|
|
2655
3116
|
const newContext = JSON.stringify(parsed);
|
|
2656
3117
|
if (!this.taskUpdater) {
|
|
2657
|
-
|
|
3118
|
+
logger8.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。`);
|
|
2658
3119
|
task.context = newContext;
|
|
2659
3120
|
return;
|
|
2660
3121
|
}
|
|
2661
3122
|
try {
|
|
2662
3123
|
await this.taskUpdater(task.id, newContext);
|
|
2663
3124
|
task.context = newContext;
|
|
2664
|
-
|
|
3125
|
+
logger8.debug(`attachChatIdToTask[${this.pluginName}]: ✅ persisted chat_id ${chatId} ` + `to task #${task.id} context`);
|
|
2665
3126
|
} catch (error) {
|
|
2666
|
-
|
|
3127
|
+
logger8.error(`Failed to persist chat_id to task #${task.id}: ${error}`);
|
|
2667
3128
|
task.context = newContext;
|
|
2668
3129
|
}
|
|
2669
3130
|
}
|
|
@@ -2758,15 +3219,15 @@ class PerTaskGroupChatPlugin {
|
|
|
2758
3219
|
}
|
|
2759
3220
|
resolveBotAppIds(replyChannel) {
|
|
2760
3221
|
if (this.config.botAppIds.length > 0) {
|
|
2761
|
-
|
|
3222
|
+
logger8.debug(`resolveBotAppIds[${this.pluginName}]: using config.botAppIds=${JSON.stringify(this.config.botAppIds)}`);
|
|
2762
3223
|
return this.config.botAppIds;
|
|
2763
3224
|
}
|
|
2764
3225
|
const appId = replyChannel ? replyChannel.appId : undefined;
|
|
2765
3226
|
if (appId) {
|
|
2766
|
-
|
|
3227
|
+
logger8.debug(`resolveBotAppIds[${this.pluginName}]: using replyChannel.appId=${appId} (auto-detected from event header.app_id)`);
|
|
2767
3228
|
return [appId];
|
|
2768
3229
|
}
|
|
2769
|
-
|
|
3230
|
+
logger8.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。`);
|
|
2770
3231
|
return [];
|
|
2771
3232
|
}
|
|
2772
3233
|
resolveUserIds(replyChannel) {
|
|
@@ -2776,7 +3237,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2776
3237
|
if (id && id !== "sender" && id.startsWith("ou_")) {
|
|
2777
3238
|
ids.add(id);
|
|
2778
3239
|
} else if (id && id !== "sender") {
|
|
2779
|
-
|
|
3240
|
+
logger8.debug(`resolveUserIds[${this.pluginName}]: skip non-open_id in defaultInviteUserIds: "${id}"`);
|
|
2780
3241
|
}
|
|
2781
3242
|
}
|
|
2782
3243
|
}
|
|
@@ -2799,7 +3260,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2799
3260
|
}
|
|
2800
3261
|
}
|
|
2801
3262
|
if (isPluginDebugEnabled()) {
|
|
2802
|
-
|
|
3263
|
+
logger8.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))}`);
|
|
2803
3264
|
}
|
|
2804
3265
|
return Array.from(ids);
|
|
2805
3266
|
}
|
|
@@ -2952,4 +3413,4 @@ __legacyDecorateClassTS([
|
|
|
2952
3413
|
function createPerTaskGroupChatPlugin(config = {}) {
|
|
2953
3414
|
return new PerTaskGroupChatPlugin(config);
|
|
2954
3415
|
}
|
|
2955
|
-
export { createAutoTaskPlugin,
|
|
3416
|
+
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 };
|