@xgjktech/xg_cwork_im 1.16.44 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +433 -433
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +27 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/src/deliver-text.test.js +22 -22
  6. package/dist/src/dispatch-mentioned-reply.d.ts.map +1 -1
  7. package/dist/src/dispatch-mentioned-reply.js +8 -0
  8. package/dist/src/dispatch-mentioned-reply.js.map +1 -1
  9. package/dist/src/plan-blocked-notice.d.ts +15 -0
  10. package/dist/src/plan-blocked-notice.d.ts.map +1 -0
  11. package/dist/src/plan-blocked-notice.js +28 -0
  12. package/dist/src/plan-blocked-notice.js.map +1 -0
  13. package/dist/src/plan-blocked-notice.test.d.ts +2 -0
  14. package/dist/src/plan-blocked-notice.test.d.ts.map +1 -0
  15. package/dist/src/plan-blocked-notice.test.js +80 -0
  16. package/dist/src/plan-blocked-notice.test.js.map +1 -0
  17. package/dist/src/plan-event-subscription.d.ts +26 -0
  18. package/dist/src/plan-event-subscription.d.ts.map +1 -0
  19. package/dist/src/plan-event-subscription.js +121 -0
  20. package/dist/src/plan-event-subscription.js.map +1 -0
  21. package/dist/src/plan-event-subscription.test.d.ts +2 -0
  22. package/dist/src/plan-event-subscription.test.d.ts.map +1 -0
  23. package/dist/src/plan-event-subscription.test.js +257 -0
  24. package/dist/src/plan-event-subscription.test.js.map +1 -0
  25. package/dist/src/plan-session-registry.d.ts +24 -0
  26. package/dist/src/plan-session-registry.d.ts.map +1 -0
  27. package/dist/src/plan-session-registry.js +84 -0
  28. package/dist/src/plan-session-registry.js.map +1 -0
  29. package/dist/src/plan-session-registry.test.d.ts +2 -0
  30. package/dist/src/plan-session-registry.test.d.ts.map +1 -0
  31. package/dist/src/plan-session-registry.test.js +109 -0
  32. package/dist/src/plan-session-registry.test.js.map +1 -0
  33. package/dist/src/plan-store-access.d.ts +16 -0
  34. package/dist/src/plan-store-access.d.ts.map +1 -0
  35. package/dist/src/plan-store-access.js +76 -0
  36. package/dist/src/plan-store-access.js.map +1 -0
  37. package/dist/src/plan-store-access.test.d.ts +2 -0
  38. package/dist/src/plan-store-access.test.d.ts.map +1 -0
  39. package/dist/src/plan-store-access.test.js +139 -0
  40. package/dist/src/plan-store-access.test.js.map +1 -0
  41. package/dist/src/plan-tool-middleware.d.ts +20 -0
  42. package/dist/src/plan-tool-middleware.d.ts.map +1 -0
  43. package/dist/src/plan-tool-middleware.js +99 -0
  44. package/dist/src/plan-tool-middleware.js.map +1 -0
  45. package/dist/src/plan-tool-middleware.test.d.ts +2 -0
  46. package/dist/src/plan-tool-middleware.test.d.ts.map +1 -0
  47. package/dist/src/plan-tool-middleware.test.js +329 -0
  48. package/dist/src/plan-tool-middleware.test.js.map +1 -0
  49. package/dist/src/plan-tool-result.d.ts +20 -0
  50. package/dist/src/plan-tool-result.d.ts.map +1 -0
  51. package/dist/src/plan-tool-result.js +78 -0
  52. package/dist/src/plan-tool-result.js.map +1 -0
  53. package/dist/src/plan-tool-result.test.d.ts +2 -0
  54. package/dist/src/plan-tool-result.test.d.ts.map +1 -0
  55. package/dist/src/plan-tool-result.test.js +107 -0
  56. package/dist/src/plan-tool-result.test.js.map +1 -0
  57. package/dist/src/turn-deliver-buffer.test.js +25 -25
  58. package/openclaw.plugin.json +27 -26
  59. package/package.json +76 -72
@@ -0,0 +1,139 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { after, afterEach, beforeEach, describe, it } from "node:test";
6
+ import { resolvePlanDbPath, SqlitePlanStore } from "@xgjktech/xg-openclaw-shared";
7
+ import { getPlanById, listPlanSummaries, resetPlanStoreAccessForTest, setPlanStateDir, } from "./plan-store-access.js";
8
+ const tempDirs = [];
9
+ function makeTempStateDir() {
10
+ const dir = mkdtempSync(join(tmpdir(), "xg-plan-store-access-test-"));
11
+ tempDirs.push(dir);
12
+ return dir;
13
+ }
14
+ function makePlan(overrides = {}) {
15
+ return {
16
+ planId: "plan-1",
17
+ goal: "迁移配置到新版本",
18
+ createdAt: "2026-07-27T00:00:00.000Z",
19
+ updatedAt: "2026-07-27T00:00:00.000Z",
20
+ tasks: [],
21
+ ...overrides,
22
+ };
23
+ }
24
+ beforeEach(() => {
25
+ resetPlanStoreAccessForTest();
26
+ });
27
+ afterEach(() => {
28
+ resetPlanStoreAccessForTest();
29
+ });
30
+ after(() => {
31
+ for (const dir of tempDirs) {
32
+ rmSync(dir, { recursive: true, force: true });
33
+ }
34
+ });
35
+ describe("getPlanById / listPlanSummaries: 缺库降级 (F4)", () => {
36
+ it("returns undefined / [] without throwing when plans.sqlite does not exist", () => {
37
+ const stateDir = makeTempStateDir();
38
+ setPlanStateDir(stateDir);
39
+ assert.equal(getPlanById("x"), undefined);
40
+ assert.deepEqual(listPlanSummaries(), []);
41
+ });
42
+ });
43
+ describe("getPlanById: 后建库恢复 (F5)", () => {
44
+ it("recovers once the harness plugin later creates and writes the db, without resetting module state", () => {
45
+ const stateDir = makeTempStateDir();
46
+ setPlanStateDir(stateDir);
47
+ // T1: 库不存在时先调用一次,期望降级为 undefined 且不缓存失败
48
+ assert.equal(getPlanById("plan-1"), undefined);
49
+ // T2: harness 侧后建库并写入
50
+ const dbPath = resolvePlanDbPath(stateDir);
51
+ const writer = new SqlitePlanStore(dbPath);
52
+ writer.set(makePlan({ planId: "plan-1", goal: "后建库恢复" }));
53
+ writer.close();
54
+ // T3: 不重置模块状态,再次调用应能读到(验证未缓存"打开失败")
55
+ const plan = getPlanById("plan-1");
56
+ assert.ok(plan);
57
+ assert.equal(plan.planId, "plan-1");
58
+ assert.equal(plan.goal, "后建库恢复");
59
+ });
60
+ });
61
+ describe("getPlanById: 正常反查", () => {
62
+ it("round-trips a plan with blocked task (note) and steps", () => {
63
+ const stateDir = makeTempStateDir();
64
+ setPlanStateDir(stateDir);
65
+ const dbPath = resolvePlanDbPath(stateDir);
66
+ const writer = new SqlitePlanStore(dbPath);
67
+ writer.set(makePlan({
68
+ planId: "plan-2",
69
+ goal: "带阻塞任务的计划",
70
+ tasks: [
71
+ {
72
+ title: "备份旧配置",
73
+ status: "blocked",
74
+ note: "缺少权限",
75
+ steps: [{ text: "导出 json", status: "completed" }],
76
+ },
77
+ { title: "校验数据", status: "pending" },
78
+ ],
79
+ }));
80
+ writer.close();
81
+ const plan = getPlanById("plan-2");
82
+ assert.ok(plan);
83
+ assert.equal(plan.goal, "带阻塞任务的计划");
84
+ assert.equal(plan.tasks.length, 2);
85
+ assert.equal(plan.tasks[0].status, "blocked");
86
+ assert.equal(plan.tasks[0].note, "缺少权限");
87
+ assert.deepEqual(plan.tasks[0].steps, [{ text: "导出 json", status: "completed" }]);
88
+ assert.equal(plan.tasks[1].status, "pending");
89
+ });
90
+ it("returns undefined for an unknown planId (normal case, not an error)", () => {
91
+ const stateDir = makeTempStateDir();
92
+ setPlanStateDir(stateDir);
93
+ const dbPath = resolvePlanDbPath(stateDir);
94
+ const writer = new SqlitePlanStore(dbPath);
95
+ writer.set(makePlan({ planId: "plan-exists" }));
96
+ writer.close();
97
+ assert.equal(getPlanById("plan-does-not-exist"), undefined);
98
+ });
99
+ });
100
+ describe("listPlanSummaries", () => {
101
+ it("lists all plans with correct taskCounts", () => {
102
+ const stateDir = makeTempStateDir();
103
+ setPlanStateDir(stateDir);
104
+ const dbPath = resolvePlanDbPath(stateDir);
105
+ const writer = new SqlitePlanStore(dbPath);
106
+ writer.set(makePlan({
107
+ planId: "plan-a",
108
+ goal: "计划 A",
109
+ tasks: [
110
+ { title: "t1", status: "completed" },
111
+ { title: "t2", status: "blocked", note: "n" },
112
+ { title: "t3", status: "in_progress" },
113
+ { title: "t4", status: "pending" },
114
+ ],
115
+ }));
116
+ writer.set(makePlan({
117
+ planId: "plan-b",
118
+ goal: "计划 B",
119
+ tasks: [{ title: "t1", status: "completed" }],
120
+ }));
121
+ writer.close();
122
+ const summaries = listPlanSummaries();
123
+ assert.equal(summaries.length, 2);
124
+ const byId = new Map(summaries.map((s) => [s.planId, s]));
125
+ const a = byId.get("plan-a");
126
+ assert.ok(a);
127
+ assert.deepEqual(a.taskCounts, { total: 4, completed: 1, blocked: 1, inProgress: 1 });
128
+ const b = byId.get("plan-b");
129
+ assert.ok(b);
130
+ assert.deepEqual(b.taskCounts, { total: 1, completed: 1, blocked: 0, inProgress: 0 });
131
+ });
132
+ });
133
+ describe("未设置 stateDir", () => {
134
+ it("getPlanById returns undefined without throwing", () => {
135
+ assert.equal(getPlanById("x"), undefined);
136
+ assert.deepEqual(listPlanSummaries(), []);
137
+ });
138
+ });
139
+ //# sourceMappingURL=plan-store-access.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-store-access.test.js","sourceRoot":"","sources":["../../src/plan-store-access.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
2
+ import type { Plan } from "@xgjktech/xg-openclaw-shared";
3
+ import { type ToolCallTarget } from "./plan-session-registry.js";
4
+ export type AgentToolResultMiddleware = Parameters<OpenClawPluginApi["registerAgentToolResultMiddleware"]>[0];
5
+ export interface PlanMiddlewareDeps {
6
+ /** 推群。实现方注入,内部走 sendTextMessage */
7
+ sendText: (target: ToolCallTarget, content: string) => Promise<void>;
8
+ /** details 缺失时按 planId 反查权威状态(注入 TASK-2 的 getPlanById) */
9
+ getPlanById: (planId: string) => Plan | undefined;
10
+ }
11
+ /**
12
+ * 创建计划工具结果的 middleware handler。
13
+ *
14
+ * 三条硬约束(RULING-1 §2 / DESIGN.md D1b,红线):
15
+ * 1. 绝不抛异常:全身 try/catch,捕获后记 warn 并正常返回。
16
+ * 2. 绝不修改结果:恒返回 void,不 mutate event.result。
17
+ * 3. 不在 handler 内 await 网络 IO:推群走 fire-and-forget,handler 立即返回。
18
+ */
19
+ export declare function createPlanToolResultMiddleware(deps: PlanMiddlewareDeps): AgentToolResultMiddleware;
20
+ //# sourceMappingURL=plan-tool-middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-tool-middleware.d.ts","sourceRoot":"","sources":["../../src/plan-tool-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAGzD,OAAO,EAAiD,KAAK,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEhH,MAAM,MAAM,yBAAyB,GAAG,UAAU,CAC9C,iBAAiB,CAAC,mCAAmC,CAAC,CACzD,CAAC,CAAC,CAAC,CAAC;AAML,MAAM,WAAW,kBAAkB;IAC/B,mCAAmC;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,0DAA0D;IAC1D,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;CACrD;AA2CD;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,kBAAkB,GAAG,yBAAyB,CA2DlG"}
@@ -0,0 +1,99 @@
1
+ import { collectBlockedNotices } from "./plan-blocked-notice.js";
2
+ import { parsePlanToolResultFromValue } from "./plan-tool-result.js";
3
+ import { diffAndMarkBlockedNotices, takeToolCallTarget } from "./plan-session-registry.js";
4
+ const PLAN_TOOL_NAMES = new Set(["xg_plan_write", "xg_plan_execute"]);
5
+ const LOG_PREFIX = "[xg_cwork_im] [plan-tool-middleware]";
6
+ function firstTextContent(result) {
7
+ const first = result.content[0];
8
+ if (!first || first.type !== "text" || typeof first.text !== "string") {
9
+ return undefined;
10
+ }
11
+ return first.text;
12
+ }
13
+ /**
14
+ * 从 middleware 事件中取 plan(优先级见 DESIGN.md D1):
15
+ * 1. `event.result.details`(结构守卫,纯对象无需 JSON.parse)
16
+ * 2. 回退解析 `event.result.content[0].text`
17
+ * 都拿不到 → undefined
18
+ */
19
+ function extractPlanToolResult(event) {
20
+ const fromDetails = parsePlanToolResultFromValue(event.result.details);
21
+ if (fromDetails) {
22
+ return { parsed: fromDetails, fromDetails: true };
23
+ }
24
+ const text = firstTextContent(event.result);
25
+ if (text === undefined) {
26
+ return undefined;
27
+ }
28
+ const fromText = parsePlanToolResultFromValue(safeJsonParse(text));
29
+ if (fromText) {
30
+ return { parsed: fromText, fromDetails: false };
31
+ }
32
+ return undefined;
33
+ }
34
+ function safeJsonParse(text) {
35
+ try {
36
+ return JSON.parse(text);
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ /**
43
+ * 创建计划工具结果的 middleware handler。
44
+ *
45
+ * 三条硬约束(RULING-1 §2 / DESIGN.md D1b,红线):
46
+ * 1. 绝不抛异常:全身 try/catch,捕获后记 warn 并正常返回。
47
+ * 2. 绝不修改结果:恒返回 void,不 mutate event.result。
48
+ * 3. 不在 handler 内 await 网络 IO:推群走 fire-and-forget,handler 立即返回。
49
+ */
50
+ export function createPlanToolResultMiddleware(deps) {
51
+ return (event, _ctx) => {
52
+ try {
53
+ if (!PLAN_TOOL_NAMES.has(event.toolName)) {
54
+ return;
55
+ }
56
+ const extracted = extractPlanToolResult(event);
57
+ if (!extracted) {
58
+ console.warn(`${LOG_PREFIX} 无法从工具返回中解析出合法 plan toolName=${event.toolName} toolCallId=${event.toolCallId}`);
59
+ return;
60
+ }
61
+ const { parsed, fromDetails } = extracted;
62
+ let plan = parsed.plan;
63
+ // 仅当 details 缺失(走了回退路径且仍拿不到完整 plan)时,才用权威反查
64
+ if (!fromDetails) {
65
+ const authoritative = deps.getPlanById(parsed.planId);
66
+ if (authoritative) {
67
+ plan = authoritative;
68
+ }
69
+ }
70
+ const notices = collectBlockedNotices(plan);
71
+ // 查表必须在 diffAndMarkBlockedNotices 之前:后者是标记即生效的副作用,
72
+ // 若先标记、查表再未命中(合成 id / TTL 过期 / onToolStart 早退,均为
73
+ // RULING-2 已确认的真实路径),通知会被误标记为「已推送」但从未送达,
74
+ // 导致同一 blocked key 后续永远不再重推(FINDING-1)。未命中时尚未标记,
75
+ // 下一次工具调用(onToolStart 用新 toolCallId 重新登记)能天然获得重推机会。
76
+ const target = takeToolCallTarget(event.toolCallId);
77
+ if (!target) {
78
+ console.warn(`${LOG_PREFIX} toolCallId 未命中投递目标,放弃本次推群 toolCallId=${event.toolCallId} planId=${parsed.planId}`);
79
+ return;
80
+ }
81
+ // 标记仍在 send 之前:异步 send 失败不重推,是 at-most-once 的有意设计(防刷屏)。
82
+ const fresh = diffAndMarkBlockedNotices(parsed.planId, notices);
83
+ if (fresh.length === 0) {
84
+ return;
85
+ }
86
+ for (const notice of fresh) {
87
+ void deps.sendText(target, notice.content).catch((err) => {
88
+ console.warn(`${LOG_PREFIX} 推群失败 planId=${parsed.planId} taskIndex=${notice.taskIndex}: ${String(err)}`);
89
+ });
90
+ }
91
+ return;
92
+ }
93
+ catch (err) {
94
+ console.warn(`${LOG_PREFIX} handler 内部异常,已吞: ${String(err)}`);
95
+ return;
96
+ }
97
+ };
98
+ }
99
+ //# sourceMappingURL=plan-tool-middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-tool-middleware.js","sourceRoot":"","sources":["../../src/plan-tool-middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=plan-tool-middleware.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-tool-middleware.test.d.ts","sourceRoot":"","sources":["../../src/plan-tool-middleware.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,329 @@
1
+ import assert from "node:assert/strict";
2
+ import { beforeEach, describe, it } from "node:test";
3
+ import { createPlanToolResultMiddleware } from "./plan-tool-middleware.js";
4
+ import { registerToolCallTarget, resetPlanSessionRegistryForTest, } from "./plan-session-registry.js";
5
+ const FAKE_CTX = { runtime: "openclaw" };
6
+ function makePlan(overrides = {}) {
7
+ return {
8
+ planId: "plan-1",
9
+ goal: "迁移配置到新版本",
10
+ createdAt: "2026-07-27T00:00:00.000Z",
11
+ updatedAt: "2026-07-27T00:00:00.000Z",
12
+ tasks: [],
13
+ ...overrides,
14
+ };
15
+ }
16
+ function makeEvent(overrides) {
17
+ const content = overrides.text !== undefined
18
+ ? [{ type: "text", text: overrides.text }]
19
+ : [];
20
+ return {
21
+ toolCallId: overrides.toolCallId ?? "call-1",
22
+ toolName: overrides.toolName ?? "xg_plan_write",
23
+ args: {},
24
+ result: {
25
+ details: overrides.details,
26
+ content,
27
+ },
28
+ };
29
+ }
30
+ function makeSuccessPayload(plan, withEvent = false) {
31
+ return {
32
+ ok: true,
33
+ planId: plan.planId,
34
+ plan,
35
+ ...(withEvent ? { event: { emitted: false, reason: "no runId" } } : {}),
36
+ };
37
+ }
38
+ class RecordingDeps {
39
+ calls = [];
40
+ sendTextImpl = async (target, content) => {
41
+ this.calls.push({ target, content });
42
+ };
43
+ planById = new Map();
44
+ sendText = (target, content) => {
45
+ return this.sendTextImpl(target, content);
46
+ };
47
+ getPlanById = (planId) => {
48
+ return this.planById.get(planId);
49
+ };
50
+ }
51
+ beforeEach(() => {
52
+ resetPlanSessionRegistryForTest();
53
+ });
54
+ describe("createPlanToolResultMiddleware: 非 plan 工具直接放行", () => {
55
+ it("does not touch sendText or getPlanById for unrelated tools", () => {
56
+ const deps = new RecordingDeps();
57
+ const handler = createPlanToolResultMiddleware(deps);
58
+ const event = makeEvent({
59
+ toolName: "some_other_tool",
60
+ details: { ok: true, data: 123 },
61
+ });
62
+ const result = handler(event, FAKE_CTX);
63
+ assert.equal(result, undefined);
64
+ assert.equal(deps.calls.length, 0);
65
+ });
66
+ it("does not push even when a non-plan tool's details is a structurally valid plan payload (pins the name filter)", () => {
67
+ const deps = new RecordingDeps();
68
+ const handler = createPlanToolResultMiddleware(deps);
69
+ // 表中已登记该 toolCallId,若名字过滤被删掉,结构守卫会放行并触发推送
70
+ registerToolCallTarget("call-evil", { groupId: "g-evil", token: "t-evil" });
71
+ const plan = makePlan({ tasks: [{ title: "备份旧配置", status: "blocked", note: "缺少权限" }] });
72
+ const event = makeEvent({
73
+ toolCallId: "call-evil",
74
+ toolName: "some_evil_tool",
75
+ details: makeSuccessPayload(plan),
76
+ });
77
+ const result = handler(event, FAKE_CTX);
78
+ assert.equal(result, undefined);
79
+ assert.equal(deps.calls.length, 0);
80
+ });
81
+ });
82
+ describe("createPlanToolResultMiddleware: details 路径", () => {
83
+ it("pushes a blocked notice when details carries a valid plan", () => {
84
+ const deps = new RecordingDeps();
85
+ const handler = createPlanToolResultMiddleware(deps);
86
+ registerToolCallTarget("call-1", { groupId: "g1", token: "t1" });
87
+ const plan = makePlan({
88
+ tasks: [{ title: "备份旧配置", status: "blocked", note: "缺少权限" }],
89
+ });
90
+ const event = makeEvent({
91
+ toolCallId: "call-1",
92
+ toolName: "xg_plan_write",
93
+ details: makeSuccessPayload(plan),
94
+ });
95
+ const result = handler(event, FAKE_CTX);
96
+ assert.equal(result, undefined);
97
+ assert.equal(deps.calls.length, 1);
98
+ assert.equal(deps.calls[0].target.groupId, "g1");
99
+ assert.ok(deps.calls[0].content.includes("备份旧配置"));
100
+ assert.ok(deps.calls[0].content.includes("缺少权限"));
101
+ });
102
+ });
103
+ describe("createPlanToolResultMiddleware: text 回退路径", () => {
104
+ it("falls back to content[0].text when details is undefined", () => {
105
+ const deps = new RecordingDeps();
106
+ const handler = createPlanToolResultMiddleware(deps);
107
+ registerToolCallTarget("call-2", { groupId: "g2", token: "t2" });
108
+ const plan = makePlan({
109
+ planId: "plan-2",
110
+ tasks: [{ title: "校验数据", status: "blocked", note: "依赖未就绪" }],
111
+ });
112
+ const rawBody = JSON.stringify(makeSuccessPayload(plan, true), null, 2);
113
+ const event = makeEvent({
114
+ toolCallId: "call-2",
115
+ toolName: "xg_plan_execute",
116
+ details: undefined,
117
+ text: rawBody,
118
+ });
119
+ const result = handler(event, FAKE_CTX);
120
+ assert.equal(result, undefined);
121
+ assert.equal(deps.calls.length, 1);
122
+ assert.ok(deps.calls[0].content.includes("校验数据"));
123
+ });
124
+ it("uses getPlanById authoritative state when details is missing (D5/RULING-1 §3)", () => {
125
+ const deps = new RecordingDeps();
126
+ const handler = createPlanToolResultMiddleware(deps);
127
+ registerToolCallTarget("call-3", { groupId: "g3", token: "t3" });
128
+ // text 路径拿到的 plan 里没有 blocked task,但权威反查里有
129
+ const textPlan = makePlan({ planId: "plan-3", tasks: [{ title: "t1", status: "pending" }] });
130
+ const authoritativePlan = makePlan({
131
+ planId: "plan-3",
132
+ tasks: [{ title: "t1", status: "blocked", note: "authoritative 才有的阻塞" }],
133
+ });
134
+ deps.planById.set("plan-3", authoritativePlan);
135
+ const rawBody = JSON.stringify(makeSuccessPayload(textPlan), null, 2);
136
+ const event = makeEvent({
137
+ toolCallId: "call-3",
138
+ toolName: "xg_plan_write",
139
+ details: undefined,
140
+ text: rawBody,
141
+ });
142
+ handler(event, FAKE_CTX);
143
+ assert.equal(deps.calls.length, 1);
144
+ assert.ok(deps.calls[0].content.includes("authoritative 才有的阻塞"));
145
+ });
146
+ });
147
+ describe("createPlanToolResultMiddleware: 守卫拒绝畸形 payload", () => {
148
+ it("does not push for ok:false", () => {
149
+ const deps = new RecordingDeps();
150
+ const handler = createPlanToolResultMiddleware(deps);
151
+ registerToolCallTarget("call-4", { groupId: "g4", token: "t4" });
152
+ const event = makeEvent({
153
+ toolCallId: "call-4",
154
+ toolName: "xg_plan_write",
155
+ details: { ok: false, error: { code: "PLAN_NOT_FOUND" } },
156
+ });
157
+ assert.equal(handler(event, FAKE_CTX), undefined);
158
+ assert.equal(deps.calls.length, 0);
159
+ });
160
+ it("does not push when planId is missing", () => {
161
+ const deps = new RecordingDeps();
162
+ const handler = createPlanToolResultMiddleware(deps);
163
+ registerToolCallTarget("call-5", { groupId: "g5", token: "t5" });
164
+ const event = makeEvent({
165
+ toolCallId: "call-5",
166
+ toolName: "xg_plan_write",
167
+ details: { ok: true, plan: makePlan() },
168
+ });
169
+ assert.equal(handler(event, FAKE_CTX), undefined);
170
+ assert.equal(deps.calls.length, 0);
171
+ });
172
+ it("does not push when tasks is not an array", () => {
173
+ const deps = new RecordingDeps();
174
+ const handler = createPlanToolResultMiddleware(deps);
175
+ registerToolCallTarget("call-6", { groupId: "g6", token: "t6" });
176
+ const event = makeEvent({
177
+ toolCallId: "call-6",
178
+ toolName: "xg_plan_write",
179
+ details: {
180
+ ok: true,
181
+ planId: "plan-6",
182
+ plan: { planId: "plan-6", goal: "g", createdAt: "t", updatedAt: "t", tasks: "not-an-array" },
183
+ },
184
+ });
185
+ assert.equal(handler(event, FAKE_CTX), undefined);
186
+ assert.equal(deps.calls.length, 0);
187
+ });
188
+ });
189
+ describe("createPlanToolResultMiddleware: handler 内部出错不外抛", () => {
190
+ it("resolves normally even when sendText throws synchronously", () => {
191
+ const deps = new RecordingDeps();
192
+ deps.sendTextImpl = () => {
193
+ throw new Error("boom");
194
+ };
195
+ const handler = createPlanToolResultMiddleware(deps);
196
+ registerToolCallTarget("call-7", { groupId: "g7", token: "t7" });
197
+ const plan = makePlan({ tasks: [{ title: "t1", status: "blocked", note: "n" }] });
198
+ const event = makeEvent({
199
+ toolCallId: "call-7",
200
+ toolName: "xg_plan_write",
201
+ details: makeSuccessPayload(plan),
202
+ });
203
+ assert.doesNotThrow(() => handler(event, FAKE_CTX));
204
+ });
205
+ it("resolves normally even when getPlanById throws", () => {
206
+ const deps = new RecordingDeps();
207
+ deps.getPlanById = () => {
208
+ throw new Error("boom");
209
+ };
210
+ const handler = createPlanToolResultMiddleware(deps);
211
+ registerToolCallTarget("call-8", { groupId: "g8", token: "t8" });
212
+ const plan = makePlan({ planId: "plan-8", tasks: [{ title: "t1", status: "blocked", note: "n" }] });
213
+ const rawBody = JSON.stringify(makeSuccessPayload(plan), null, 2);
214
+ const event = makeEvent({
215
+ toolCallId: "call-8",
216
+ toolName: "xg_plan_write",
217
+ details: undefined,
218
+ text: rawBody,
219
+ });
220
+ assert.doesNotThrow(() => handler(event, FAKE_CTX));
221
+ });
222
+ });
223
+ describe("createPlanToolResultMiddleware: 恒返回 void", () => {
224
+ it("handler's return value is undefined", () => {
225
+ const deps = new RecordingDeps();
226
+ const handler = createPlanToolResultMiddleware(deps);
227
+ registerToolCallTarget("call-9", { groupId: "g9", token: "t9" });
228
+ const plan = makePlan({ tasks: [{ title: "t1", status: "blocked", note: "n" }] });
229
+ const event = makeEvent({
230
+ toolCallId: "call-9",
231
+ toolName: "xg_plan_write",
232
+ details: makeSuccessPayload(plan),
233
+ });
234
+ const result = handler(event, FAKE_CTX);
235
+ assert.equal(result, undefined);
236
+ });
237
+ });
238
+ describe("createPlanToolResultMiddleware: 合成 id 未命中 → 静默降级 (RULING-2 追加约束 2)", () => {
239
+ it("does not push and does not throw when toolCallId is a synthetic openclaw-<uuid> not present in the table", () => {
240
+ const deps = new RecordingDeps();
241
+ const handler = createPlanToolResultMiddleware(deps);
242
+ // 故意不 registerToolCallTarget,模拟合成 id 未命中
243
+ const plan = makePlan({ tasks: [{ title: "t1", status: "blocked", note: "n" }] });
244
+ const event = makeEvent({
245
+ toolCallId: "openclaw-3f9a7c2e-1234-4a1b-9abc-000000000000",
246
+ toolName: "xg_plan_write",
247
+ details: makeSuccessPayload(plan),
248
+ });
249
+ assert.doesNotThrow(() => {
250
+ const result = handler(event, FAKE_CTX);
251
+ assert.equal(result, undefined);
252
+ });
253
+ assert.equal(deps.calls.length, 0);
254
+ });
255
+ });
256
+ describe("createPlanToolResultMiddleware: fire-and-forget", () => {
257
+ it("resolves immediately even when sendText returns a never-resolving promise", () => {
258
+ const deps = new RecordingDeps();
259
+ deps.sendTextImpl = () => new Promise(() => { });
260
+ const handler = createPlanToolResultMiddleware(deps);
261
+ registerToolCallTarget("call-10", { groupId: "g10", token: "t10" });
262
+ const plan = makePlan({ tasks: [{ title: "t1", status: "blocked", note: "n" }] });
263
+ const event = makeEvent({
264
+ toolCallId: "call-10",
265
+ toolName: "xg_plan_write",
266
+ details: makeSuccessPayload(plan),
267
+ });
268
+ // 若 handler 内部 await 了 sendText,这行会挂起整个测试进程(超时失败);
269
+ // handler 是同步函数,必须立即返回 undefined。
270
+ const result = handler(event, FAKE_CTX);
271
+ assert.equal(result, undefined);
272
+ });
273
+ });
274
+ describe("createPlanToolResultMiddleware: 查表未命中不得毒化去重状态 (FINDING-1)", () => {
275
+ it("a miss does not mark the notice as sent — a later call with a freshly-registered toolCallId for the same blocked key must still push", () => {
276
+ const deps = new RecordingDeps();
277
+ const handler = createPlanToolResultMiddleware(deps);
278
+ const plan = makePlan({
279
+ planId: "plan-finding1",
280
+ tasks: [{ title: "备份旧配置", status: "blocked", note: "缺少权限" }],
281
+ });
282
+ // 第一次:不登记 toolCallId,模拟未命中(合成 id / TTL 过期 / onToolStart 早退)
283
+ const missEvent = makeEvent({
284
+ toolCallId: "call-miss",
285
+ toolName: "xg_plan_write",
286
+ details: makeSuccessPayload(plan),
287
+ });
288
+ handler(missEvent, FAKE_CTX);
289
+ assert.equal(deps.calls.length, 0, "未命中时不应有任何推送");
290
+ // 第二次:同一 planId、同一 task、同一 note(key 完全相同),
291
+ // 但 toolCallId 换成新登记的 call-new——模拟 onToolStart 用新 toolCallId 重新登记
292
+ registerToolCallTarget("call-new", { groupId: "g-new", token: "t-new" });
293
+ const hitEvent = makeEvent({
294
+ toolCallId: "call-new",
295
+ toolName: "xg_plan_write",
296
+ details: makeSuccessPayload(plan),
297
+ });
298
+ handler(hitEvent, FAKE_CTX);
299
+ assert.equal(deps.calls.length, 1, "未命中不应污染去重状态,换新 toolCallId 后必须能重推");
300
+ assert.equal(deps.calls[0].target.groupId, "g-new");
301
+ assert.ok(deps.calls[0].content.includes("备份旧配置"));
302
+ assert.ok(deps.calls[0].content.includes("缺少权限"));
303
+ });
304
+ it("does not push again for the same key once it has actually been sent (dedup regression guard)", () => {
305
+ const deps = new RecordingDeps();
306
+ const handler = createPlanToolResultMiddleware(deps);
307
+ const plan = makePlan({
308
+ planId: "plan-finding1-dedup",
309
+ tasks: [{ title: "备份旧配置", status: "blocked", note: "缺少权限" }],
310
+ });
311
+ registerToolCallTarget("call-first", { groupId: "g-first", token: "t-first" });
312
+ const firstEvent = makeEvent({
313
+ toolCallId: "call-first",
314
+ toolName: "xg_plan_write",
315
+ details: makeSuccessPayload(plan),
316
+ });
317
+ handler(firstEvent, FAKE_CTX);
318
+ assert.equal(deps.calls.length, 1, "首次命中应推送一次");
319
+ registerToolCallTarget("call-second", { groupId: "g-second", token: "t-second" });
320
+ const secondEvent = makeEvent({
321
+ toolCallId: "call-second",
322
+ toolName: "xg_plan_write",
323
+ details: makeSuccessPayload(plan),
324
+ });
325
+ handler(secondEvent, FAKE_CTX);
326
+ assert.equal(deps.calls.length, 1, "同一 key 已成功推送过,不应重复推送");
327
+ });
328
+ });
329
+ //# sourceMappingURL=plan-tool-middleware.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-tool-middleware.test.js","sourceRoot":"","sources":["../../src/plan-tool-middleware.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAErD,OAAO,EAAE,8BAA8B,EAA2D,MAAM,2BAA2B,CAAC;AACpI,OAAO,EACH,sBAAsB,EACtB,+BAA+B,GAElC,MAAM,4BAA4B,CAAC;AAEpC,MAAM,QAAQ,GAAG,EAAE,OAAO,EAAE,UAAmB,EAAE,CAAC;AAElD,SAAS,QAAQ,CAAC,YAA2B,EAAE;IAC3C,OAAO;QACH,MAAM,EAAE,QAAQ;QAChB,IAAI,EAAE,UAAU;QAChB,SAAS,EAAE,0BAA0B;QACrC,SAAS,EAAE,0BAA0B;QACrC,KAAK,EAAE,EAAE;QACT,GAAG,SAAS;KACf,CAAC;AACN,CAAC;AAED,SAAS,SAAS,CAAC,SAKlB;IACG,MAAM,OAAO,GACT,SAAS,CAAC,IAAI,KAAK,SAAS;QACxB,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC;QACnD,CAAC,CAAC,EAAE,CAAC;IACb,OAAO;QACH,UAAU,EAAE,SAAS,CAAC,UAAU,IAAI,QAAQ;QAC5C,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,eAAe;QAC/C,IAAI,EAAE,EAAE;QACR,MAAM,EAAE;YACJ,OAAO,EAAE,SAAS,CAAC,OAAO;YAC1B,OAAO;SACV;KACmD,CAAC;AAC7D,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAU,EAAE,SAAS,GAAG,KAAK;IACrD,OAAO;QACH,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,IAAI;QACJ,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1E,CAAC;AACN,CAAC;AAED,MAAM,aAAa;IACf,KAAK,GAAuD,EAAE,CAAC;IAC/D,YAAY,GAA+D,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;QACjG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC;IACF,QAAQ,GAAsB,IAAI,GAAG,EAAE,CAAC;IAExC,QAAQ,GAAG,CAAC,MAAsB,EAAE,OAAe,EAAiB,EAAE;QAClE,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC,CAAC;IAEF,WAAW,GAAG,CAAC,MAAc,EAAoB,EAAE;QAC/C,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC,CAAC;CACL;AAED,UAAU,CAAC,GAAG,EAAE;IACZ,+BAA+B,EAAE,CAAC;AACtC,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,+CAA+C,EAAE,GAAG,EAAE;IAC3D,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QAClE,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QAErD,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,QAAQ,EAAE,iBAAiB;YAC3B,OAAO,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;SACnC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+GAA+G,EAAE,GAAG,EAAE;QACrH,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QAErD,0CAA0C;QAC1C,sBAAsB,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAE5E,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QACxF,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,UAAU,EAAE,WAAW;YACvB,QAAQ,EAAE,gBAAgB;YAC1B,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC;SACpC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4CAA4C,EAAE,GAAG,EAAE;IACxD,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACjE,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QAErD,sBAAsB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjE,MAAM,IAAI,GAAG,QAAQ,CAAC;YAClB,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;SAC/D,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC;SACpC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACjD,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACvD,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QAC/D,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QAErD,sBAAsB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjE,MAAM,IAAI,GAAG,QAAQ,CAAC;YAClB,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;SAC/D,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACxE,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,iBAAiB;YAC3B,OAAO,EAAE,SAAS;YAClB,IAAI,EAAE,OAAO;SAChB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACrF,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QAErD,sBAAsB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjE,2CAA2C;QAC3C,MAAM,QAAQ,GAAG,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7F,MAAM,iBAAiB,GAAG,QAAQ,CAAC;YAC/B,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC;SAC3E,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QAE/C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACtE,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,SAAS;YAClB,IAAI,EAAE,OAAO;SAChB,CAAC,CAAC;QAEH,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACzB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC5D,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QAClC,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QACrD,sBAAsB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjE,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE;SAC5D,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,SAAS,CAAC,CAAC;QAClD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC5C,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QACrD,sBAAsB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjE,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;SAC1C,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,SAAS,CAAC,CAAC;QAClD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAChD,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QACrD,sBAAsB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjE,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE;gBACL,EAAE,EAAE,IAAI;gBACR,MAAM,EAAE,QAAQ;gBAChB,IAAI,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE;aAC/F;SACJ,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,SAAS,CAAC,CAAC;QAClD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iDAAiD,EAAE,GAAG,EAAE;IAC7D,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACjE,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,IAAI,CAAC,YAAY,GAAG,GAAG,EAAE;YACrB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QACrD,sBAAsB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjE,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC;SACpC,CAAC,CAAC;QAEH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACtD,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,GAAG,EAAE;YACpB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QACrD,sBAAsB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjE,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACpG,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,SAAS;YAClB,IAAI,EAAE,OAAO;SAChB,CAAC,CAAC;QAEH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0CAA0C,EAAE,GAAG,EAAE;IACtD,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC3C,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QACrD,sBAAsB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjE,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC;SACpC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,oEAAoE,EAAE,GAAG,EAAE;IAChF,EAAE,CAAC,0GAA0G,EAAE,GAAG,EAAE;QAChH,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QACrD,yCAAyC;QAEzC,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,UAAU,EAAE,+CAA+C;YAC3D,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC;SACpC,CAAC,CAAC;QAEH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE;YACrB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YACxC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iDAAiD,EAAE,GAAG,EAAE;IAC7D,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACjF,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,IAAI,CAAC,YAAY,GAAG,GAAG,EAAE,CAAC,IAAI,OAAO,CAAO,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QACvD,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QACrD,sBAAsB,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAEpE,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,KAAK,GAAG,SAAS,CAAC;YACpB,UAAU,EAAE,SAAS;YACrB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC;SACpC,CAAC,CAAC;QAEH,mDAAmD;QACnD,kCAAkC;QAClC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2DAA2D,EAAE,GAAG,EAAE;IACvE,EAAE,CAAC,sIAAsI,EAAE,GAAG,EAAE;QAC5I,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QAErD,MAAM,IAAI,GAAG,QAAQ,CAAC;YAClB,MAAM,EAAE,eAAe;YACvB,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;SAC/D,CAAC,CAAC;QAEH,4DAA4D;QAC5D,MAAM,SAAS,GAAG,SAAS,CAAC;YACxB,UAAU,EAAE,WAAW;YACvB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC;SACpC,CAAC,CAAC;QACH,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC7B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC;QAElD,2CAA2C;QAC3C,kEAAkE;QAClE,sBAAsB,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACzE,MAAM,QAAQ,GAAG,SAAS,CAAC;YACvB,UAAU,EAAE,UAAU;YACtB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC;SACpC,CAAC,CAAC;QACH,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAE5B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,kCAAkC,CAAC,CAAC;QACvE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACpD,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8FAA8F,EAAE,GAAG,EAAE;QACpG,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;QAErD,MAAM,IAAI,GAAG,QAAQ,CAAC;YAClB,MAAM,EAAE,qBAAqB;YAC7B,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;SAC/D,CAAC,CAAC;QAEH,sBAAsB,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAC/E,MAAM,UAAU,GAAG,SAAS,CAAC;YACzB,UAAU,EAAE,YAAY;YACxB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC;SACpC,CAAC,CAAC;QACH,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC;QAEhD,sBAAsB,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;QAClF,MAAM,WAAW,GAAG,SAAS,CAAC;YAC1B,UAAU,EAAE,aAAa;YACzB,QAAQ,EAAE,eAAe;YACzB,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC;SACpC,CAAC,CAAC;QACH,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QAE/B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,sBAAsB,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}