@xgjktech/xg_cwork_im 1.17.0 → 1.17.1

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 (49) hide show
  1. package/dist/index.d.ts.map +1 -1
  2. package/dist/index.js +3 -5
  3. package/dist/index.js.map +1 -1
  4. package/dist/src/connection.d.ts +1 -1
  5. package/dist/src/connection.d.ts.map +1 -1
  6. package/dist/src/dispatch-mentioned-reply.d.ts.map +1 -1
  7. package/dist/src/dispatch-mentioned-reply.js +2 -8
  8. package/dist/src/dispatch-mentioned-reply.js.map +1 -1
  9. package/dist/src/plan-blocked-notice.d.ts +6 -7
  10. package/dist/src/plan-blocked-notice.d.ts.map +1 -1
  11. package/dist/src/plan-blocked-notice.js +21 -8
  12. package/dist/src/plan-blocked-notice.js.map +1 -1
  13. package/dist/src/plan-blocked-notice.test.js +42 -26
  14. package/dist/src/plan-blocked-notice.test.js.map +1 -1
  15. package/dist/src/plan-event-subscription.d.ts +16 -6
  16. package/dist/src/plan-event-subscription.d.ts.map +1 -1
  17. package/dist/src/plan-event-subscription.js +15 -79
  18. package/dist/src/plan-event-subscription.js.map +1 -1
  19. package/dist/src/plan-event-subscription.test.js +67 -157
  20. package/dist/src/plan-event-subscription.test.js.map +1 -1
  21. package/dist/src/plan-stat-event.d.ts +28 -0
  22. package/dist/src/plan-stat-event.d.ts.map +1 -0
  23. package/dist/src/plan-stat-event.js +68 -0
  24. package/dist/src/plan-stat-event.js.map +1 -0
  25. package/dist/src/plan-stat-event.test.d.ts +2 -0
  26. package/dist/src/plan-stat-event.test.d.ts.map +1 -0
  27. package/dist/src/plan-stat-event.test.js +167 -0
  28. package/dist/src/plan-stat-event.test.js.map +1 -0
  29. package/dist/src/plan-store-access.d.ts +7 -0
  30. package/dist/src/plan-store-access.d.ts.map +1 -1
  31. package/dist/src/plan-store-access.js +20 -0
  32. package/dist/src/plan-store-access.js.map +1 -1
  33. package/dist/src/plan-store-access.test.js +69 -1
  34. package/dist/src/plan-store-access.test.js.map +1 -1
  35. package/dist/src/plan-tool-middleware.d.ts +6 -4
  36. package/dist/src/plan-tool-middleware.d.ts.map +1 -1
  37. package/dist/src/plan-tool-middleware.js +9 -23
  38. package/dist/src/plan-tool-middleware.js.map +1 -1
  39. package/dist/src/plan-tool-middleware.test.js +280 -121
  40. package/dist/src/plan-tool-middleware.test.js.map +1 -1
  41. package/dist/src/plan-ws-registry.d.ts +11 -0
  42. package/dist/src/plan-ws-registry.d.ts.map +1 -0
  43. package/dist/src/plan-ws-registry.js +27 -0
  44. package/dist/src/plan-ws-registry.js.map +1 -0
  45. package/dist/src/plan-ws-registry.test.d.ts +2 -0
  46. package/dist/src/plan-ws-registry.test.d.ts.map +1 -0
  47. package/dist/src/plan-ws-registry.test.js +43 -0
  48. package/dist/src/plan-ws-registry.test.js.map +1 -0
  49. package/package.json +3 -3
@@ -1,95 +1,36 @@
1
1
  import { isPlanEventPayload, planEventStream } from "@xgjktech/xg-openclaw-shared";
2
- import { getToken } from "./auth.js";
3
- import { sendTextMessage } from "./send-service.js";
4
2
  /**
5
3
  * xg-harness-tools 的 plugin id:streams 过滤需要与其 openclaw.plugin.json 的 id 一致。
6
4
  */
7
5
  const HARNESS_PLUGIN_ID = "xg-harness-tools";
8
6
  const LOG_PREFIX = "[xg_cwork_im] [plan-event-subscription]";
9
- /**
10
- * `deliveryContext` 类型在 `@xgjktech/xg-openclaw-shared` 侧声明为 `unknown`(跨插件契约不引入
11
- * openclaw 依赖),运行时实际形状是 openclaw `DeliveryContext`(`{ to?, accountId?, ... }`),
12
- * 这里只做安全的结构读取,不假设其余字段存在。
13
- */
14
- function resolveFromDeliveryContext(deliveryContext) {
15
- if (typeof deliveryContext !== "object" || deliveryContext === null) {
16
- return undefined;
17
- }
18
- const to = deliveryContext.to;
19
- if (typeof to !== "string" || to === "") {
20
- return undefined;
21
- }
22
- return { groupId: to };
23
- }
24
- /**
25
- * `agentAccountId`/`messageChannel` → IM 侧 groupId 的反查目前没有映射表
26
- * (spec §1.4:若映射彻底无法建立,该次事件应放弃推群,不影响正确性,
27
- * 因为主感知路径本就是 SQLite 反查而非事件)。保留函数形态与 spec 一致,
28
- * 便于未来接入映射表时零改动补上实现。
29
- */
30
- function resolveFromAccountAndChannel(_agentAccountId, _messageChannel) {
31
- return undefined;
32
- }
33
- /**
34
- * 路由定位(spec §1.2 优先级):
35
- * 1. `payload.deliveryContext`
36
- * 2. `payload.agentAccountId` / `payload.messageChannel`
37
- * 3. 备用:`payload.sessionKey ?? event.sessionKey`(当前无 sessionKey→群 映射,直接放弃)
38
- *
39
- * ⚠️ 这些路由字段都在 `event.data`(即 payload)内部,`AgentEventPayload` 顶层没有
40
- * `deliveryContext`/`agentAccountId`/`messageChannel`(DESIGN.md §3 已编译级确认),
41
- * 只有 `sessionKey` 在顶层也存在,用作备用兜底的次优先级来源。
42
- */
43
- function resolveDeliveryTarget(payload, event) {
44
- if (payload.deliveryContext !== undefined) {
45
- return resolveFromDeliveryContext(payload.deliveryContext);
46
- }
47
- if (payload.agentAccountId !== undefined || payload.messageChannel !== undefined) {
48
- return resolveFromAccountAndChannel(payload.agentAccountId, payload.messageChannel);
49
- }
50
- const sessionKey = payload.sessionKey ?? event.sessionKey;
51
- if (sessionKey === undefined) {
52
- return undefined;
53
- }
54
- // 当前无 sessionKey → 群 的映射表(spec §1.4:若没有可以不建,直接放弃推群)
55
- return undefined;
56
- }
57
- function buildBlockedNoticeText(payload) {
58
- if (payload.taskStatus !== "blocked" || payload.blockedNote === undefined) {
59
- return undefined;
60
- }
61
- const note = payload.blockedNote.trim();
62
- if (note === "") {
63
- return undefined;
64
- }
65
- return `计划「${payload.goal}」卡在任务「${payload.taskTitle}」:${note}\n需要人工介入才能继续。`;
66
- }
67
7
  /**
68
8
  * 构造事件订阅的 `handle` 回调,可独立单测(不依赖真实 api/网络)。
69
9
  *
70
10
  * 降级纪律同 middleware:handle 内全身 try/catch,绝不外抛(与 middleware 同进程)。
71
11
  *
12
+ * 保留理由(D6 二选一,已裁定保留 + 瘦身):① 它是唯一非 middleware 的触发面,
13
+ * middleware 注册受 trust 门控被拒时(docs/plan-integration-notes.md §4.1 的已知部署故障)
14
+ * 它不受影响,仍是一条独立兜底;② 瘦身后主体只剩"守卫 → 反查 → sendPlanStat"三行,
15
+ * 维护成本接近零;③ 删掉等于丢弃第一轮已投入的契约面,而 runId 限制解除是可预期的上游演进。
16
+ *
72
17
  * ⚠️ 当前 OpenClaw 版本下 xg-harness-tools 恒因缺少 runId 而不发送此事件,
73
18
  * 此 handle 不会被触发,保留以便未来该限制解除后零改动生效。
74
- * 不得将其作为任何正确性判断依据——权威状态来自 TASK-2 的 SQLite 只读反查。
19
+ * 不得将其作为任何正确性判断依据——权威状态来自 SQLite 只读反查。
75
20
  */
76
21
  export function createPlanEventHandler(deps) {
77
- return async (event, _ctx) => {
22
+ return (event, _ctx) => {
78
23
  try {
79
24
  if (!isPlanEventPayload(event.data)) {
80
25
  return;
81
26
  }
82
27
  const payload = event.data;
83
- const target = resolveDeliveryTarget(payload, { sessionKey: event.sessionKey });
84
- if (!target) {
85
- console.warn(`${LOG_PREFIX} 无法定位投递目标,放弃本次推群 planId=${payload.planId} taskIndex=${payload.taskIndex}`);
28
+ const plan = deps.getPlanById(payload.planId);
29
+ if (!plan) {
30
+ console.warn(`${LOG_PREFIX} 反查不到 plan,跳过 planId=${payload.planId}`);
86
31
  return;
87
32
  }
88
- const content = buildBlockedNoticeText(payload);
89
- if (!content) {
90
- return;
91
- }
92
- await deps.sendText(target.groupId, content);
33
+ deps.sendPlanStat({ plan, source: "tool" });
93
34
  }
94
35
  catch (err) {
95
36
  console.warn(`${LOG_PREFIX} handle 内部异常,已吞: ${String(err)}`);
@@ -97,21 +38,16 @@ export function createPlanEventHandler(deps) {
97
38
  };
98
39
  }
99
40
  /**
100
- * 注册计划事件订阅(spec §1,前瞻性预留)。
41
+ * 注册计划事件订阅(前瞻性预留)。
101
42
  *
102
43
  * ⚠️ 当前 OpenClaw 版本下 xg-harness-tools 恒因缺少 runId 而不发送此事件,
103
44
  * 此订阅不会被触发,保留以便未来该限制解除后零改动生效。
104
45
  */
105
- export function registerPlanEventSubscription(api, config) {
106
- const handle = createPlanEventHandler({
107
- sendText: async (groupId, content) => {
108
- const identity = await getToken(config);
109
- await sendTextMessage(config, identity.token, groupId, content);
110
- },
111
- });
46
+ export function registerPlanEventSubscription(api, deps) {
47
+ const handle = createPlanEventHandler(deps);
112
48
  api.agent.events.registerAgentEventSubscription({
113
49
  id: "xg-cwork-im-plan-progress",
114
- description: "订阅 xg-harness-tools 计划进度事件,推送到群。前瞻性预留:" +
50
+ description: "订阅 xg-harness-tools 计划进度事件,反查权威 plan 后发送 PLAN_STAT。前瞻性预留:" +
115
51
  "当前 OpenClaw 版本下 xg-harness-tools 恒因缺少 runId 而不发送此事件," +
116
52
  "此订阅暂不会被触发,保留以便未来该限制解除后零改动生效。",
117
53
  streams: [planEventStream(HARNESS_PLUGIN_ID)],
@@ -1 +1 @@
1
- {"version":3,"file":"plan-event-subscription.js","sourceRoot":"","sources":["../../src/plan-event-subscription.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAyB,MAAM,8BAA8B,CAAC;AAC1G,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD;;GAEG;AACH,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAE7C,MAAM,UAAU,GAAG,yCAAyC,CAAC;AAM7D;;;;GAIG;AACH,SAAS,0BAA0B,CAAC,eAAwB;IACxD,IAAI,OAAO,eAAe,KAAK,QAAQ,IAAI,eAAe,KAAK,IAAI,EAAE,CAAC;QAClE,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,MAAM,EAAE,GAAI,eAA2C,CAAC,EAAE,CAAC;IAC3D,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACtC,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,SAAS,4BAA4B,CACjC,eAAmC,EACnC,eAAmC;IAEnC,OAAO,SAAS,CAAC;AACrB,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,qBAAqB,CAC1B,OAAyB,EACzB,KAA8B;IAE9B,IAAI,OAAO,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QACxC,OAAO,0BAA0B,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QAC/E,OAAO,4BAA4B,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,CAAC;IAC1D,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,oDAAoD;IACpD,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,SAAS,sBAAsB,CAAC,OAAyB;IACrD,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACxE,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QACd,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,OAAO,MAAM,OAAO,CAAC,IAAI,SAAS,OAAO,CAAC,SAAS,KAAK,IAAI,eAAe,CAAC;AAChF,CAAC;AAWD;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAA+B;IAClE,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACzB,IAAI,CAAC;YACD,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClC,OAAO;YACX,CAAC;YACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC;YAE3B,MAAM,MAAM,GAAG,qBAAqB,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;YAChF,IAAI,CAAC,MAAM,EAAE,CAAC;gBACV,OAAO,CAAC,IAAI,CACR,GAAG,UAAU,2BAA2B,OAAO,CAAC,MAAM,cAAc,OAAO,CAAC,SAAS,EAAE,CAC1F,CAAC;gBACF,OAAO;YACX,CAAC;YAED,MAAM,OAAO,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;YAChD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACX,OAAO;YACX,CAAC;YAED,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,GAAG,UAAU,oBAAoB,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjE,CAAC;IACL,CAAC,CAAC;AACN,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,GAAsB,EAAE,MAAkB;IACpF,MAAM,MAAM,GAAG,sBAAsB,CAAC;QAClC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;YACjC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACpE,CAAC;KACJ,CAAC,CAAC;IAEH,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,8BAA8B,CAAC;QAC5C,EAAE,EAAE,2BAA2B;QAC/B,WAAW,EACP,wCAAwC;YACxC,sDAAsD;YACtD,8BAA8B;QAClC,OAAO,EAAE,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;QAC7C,MAAM;KACT,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"plan-event-subscription.js","sourceRoot":"","sources":["../../src/plan-event-subscription.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAGnF;;GAEG;AACH,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAE7C,MAAM,UAAU,GAAG,yCAAyC,CAAC;AAa7D;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAA+B;IAClE,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACnB,IAAI,CAAC;YACD,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClC,OAAO;YACX,CAAC;YACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC;YAE3B,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC9C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACR,OAAO,CAAC,IAAI,CAAC,GAAG,UAAU,wBAAwB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;gBACpE,OAAO;YACX,CAAC;YAED,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,GAAG,UAAU,oBAAoB,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjE,CAAC;IACL,CAAC,CAAC;AACN,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,GAAsB,EAAE,IAA+B;IACjG,MAAM,MAAM,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAE5C,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,8BAA8B,CAAC;QAC5C,EAAE,EAAE,2BAA2B;QAC/B,WAAW,EACP,2DAA2D;YAC3D,sDAAsD;YACtD,8BAA8B;QAClC,OAAO,EAAE,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;QAC7C,MAAM;KACT,CAAC,CAAC;AACP,CAAC"}
@@ -6,6 +6,16 @@ const FAKE_CTX = {
6
6
  setRunContext: () => { },
7
7
  clearRunContext: () => { },
8
8
  };
9
+ function makePlan(overrides = {}) {
10
+ return {
11
+ planId: "plan-1",
12
+ goal: "迁移配置到新版本",
13
+ createdAt: "2026-07-27T00:00:00.000Z",
14
+ updatedAt: "2026-07-27T00:00:00.000Z",
15
+ tasks: [],
16
+ ...overrides,
17
+ };
18
+ }
9
19
  function makeBlockedPayload(overrides = {}) {
10
20
  return {
11
21
  kind: "plan_update",
@@ -21,17 +31,18 @@ function makeBlockedPayload(overrides = {}) {
21
31
  ...overrides,
22
32
  };
23
33
  }
24
- class RecordingSendText {
25
- calls = [];
26
- impl = async (groupId, content) => {
27
- this.calls.push({ groupId, content });
34
+ class RecordingDeps {
35
+ planById = new Map();
36
+ planStatCalls = [];
37
+ getPlanById = (planId) => this.planById.get(planId);
38
+ sendPlanStat = (params) => {
39
+ this.planStatCalls.push(params);
28
40
  };
29
- sendText = (groupId, content) => this.impl(groupId, content);
30
41
  }
31
42
  describe("createPlanEventHandler: isPlanEventPayload 守卫", () => {
32
43
  it("rejects malformed data without pushing or throwing", async () => {
33
- const recorder = new RecordingSendText();
34
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
44
+ const deps = new RecordingDeps();
45
+ const handle = createPlanEventHandler(deps);
35
46
  const event = {
36
47
  runId: "run-1",
37
48
  seq: 1,
@@ -40,11 +51,11 @@ describe("createPlanEventHandler: isPlanEventPayload 守卫", () => {
40
51
  data: { kind: "not_plan_update" },
41
52
  };
42
53
  await assert.doesNotReject(() => Promise.resolve(handle(event, FAKE_CTX)));
43
- assert.equal(recorder.calls.length, 0);
54
+ assert.equal(deps.planStatCalls.length, 0);
44
55
  });
45
56
  it("rejects when data is not an object at all", async () => {
46
- const recorder = new RecordingSendText();
47
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
57
+ const deps = new RecordingDeps();
58
+ const handle = createPlanEventHandler(deps);
48
59
  const event = {
49
60
  runId: "run-1",
50
61
  seq: 1,
@@ -53,203 +64,102 @@ describe("createPlanEventHandler: isPlanEventPayload 守卫", () => {
53
64
  data: null,
54
65
  };
55
66
  await assert.doesNotReject(() => Promise.resolve(handle(event, FAKE_CTX)));
56
- assert.equal(recorder.calls.length, 0);
67
+ assert.equal(deps.planStatCalls.length, 0);
57
68
  });
58
- it("rejects a payload that violates kind only — everything else (routing + copy fields) is valid (pins the guard)", async () => {
59
- const recorder = new RecordingSendText();
60
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
69
+ it("rejects a payload that violates kind only — everything else is valid (pins the guard)", async () => {
70
+ const deps = new RecordingDeps();
71
+ const handle = createPlanEventHandler(deps);
61
72
  const event = {
62
73
  runId: "run-1",
63
74
  seq: 1,
64
75
  stream: "xg-harness-tools.plan",
65
76
  ts: Date.now(),
66
- data: makeBlockedPayload({
67
- kind: "some_other_plugin_event", // 唯一的不合法项
68
- deliveryContext: { to: "group-1" },
69
- }),
77
+ data: makeBlockedPayload({ kind: "some_other_plugin_event" }),
70
78
  };
71
79
  await assert.doesNotReject(() => Promise.resolve(handle(event, FAKE_CTX)));
72
- assert.equal(recorder.calls.length, 0);
80
+ assert.equal(deps.planStatCalls.length, 0);
73
81
  });
74
82
  it("rejects a payload with a malformed summary — everything else is valid", async () => {
75
- const recorder = new RecordingSendText();
76
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
83
+ const deps = new RecordingDeps();
84
+ const handle = createPlanEventHandler(deps);
77
85
  const event = {
78
86
  runId: "run-1",
79
87
  seq: 1,
80
88
  stream: "xg-harness-tools.plan",
81
89
  ts: Date.now(),
82
- data: makeBlockedPayload({
83
- summary: {},
84
- deliveryContext: { to: "group-1" },
85
- }),
90
+ data: makeBlockedPayload({ summary: {} }),
86
91
  };
87
92
  await assert.doesNotReject(() => Promise.resolve(handle(event, FAKE_CTX)));
88
- assert.equal(recorder.calls.length, 0);
93
+ assert.equal(deps.planStatCalls.length, 0);
89
94
  });
90
95
  });
91
- describe("createPlanEventHandler: 路由字段取自 event.data 而非顶层", () => {
92
- it("uses deliveryContext.to from event.data, ignoring a same-shaped decoy on the top-level event", async () => {
93
- const recorder = new RecordingSendText();
94
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
96
+ describe("createPlanEventHandler: 反查到 plan → 发 PLAN_STAT", () => {
97
+ it("反查到全量 plan 调用 sendPlanStat,source === \"tool\"", async () => {
98
+ const deps = new RecordingDeps();
99
+ const plan = makePlan({
100
+ tasks: [{ title: "备份旧配置", status: "blocked", note: "缺少权限" }],
101
+ });
102
+ deps.planById.set("plan-1", plan);
103
+ const handle = createPlanEventHandler(deps);
95
104
  const event = {
96
105
  runId: "run-1",
97
106
  seq: 1,
98
107
  stream: "xg-harness-tools.plan",
99
108
  ts: Date.now(),
100
- sessionKey: "decoy-top-level-session-key",
101
- // 顶层伪造一个同名但不存在于真实 AgentEventPayload 上的字段,确认代码不会误取
102
- deliveryContext: { to: "decoy-top-level-group" },
103
- data: makeBlockedPayload({ deliveryContext: { to: "real-group-from-data" } }),
109
+ data: makeBlockedPayload(),
104
110
  };
105
111
  await handle(event, FAKE_CTX);
106
- assert.equal(recorder.calls.length, 1);
107
- assert.equal(recorder.calls[0].groupId, "real-group-from-data");
112
+ assert.equal(deps.planStatCalls.length, 1);
113
+ assert.equal(deps.planStatCalls[0].source, "tool");
114
+ assert.equal(deps.planStatCalls[0].plan.planId, "plan-1");
108
115
  });
109
116
  });
110
- describe("createPlanEventHandler: blocked payload 推送", () => {
111
- it("assembles the spec §1.3 copy and calls sendText", async () => {
112
- const recorder = new RecordingSendText();
113
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
114
- const event = {
115
- runId: "run-1",
116
- seq: 1,
117
- stream: "xg-harness-tools.plan",
118
- ts: Date.now(),
119
- data: makeBlockedPayload({
120
- deliveryContext: { to: "group-1" },
121
- goal: "迁移配置到新版本",
122
- taskTitle: "备份旧配置",
123
- blockedNote: "缺少权限",
124
- }),
125
- };
126
- await handle(event, FAKE_CTX);
127
- assert.equal(recorder.calls.length, 1);
128
- assert.equal(recorder.calls[0].groupId, "group-1");
129
- assert.equal(recorder.calls[0].content, "计划「迁移配置到新版本」卡在任务「备份旧配置」:缺少权限\n需要人工介入才能继续。");
130
- });
131
- it("does not push for non-blocked task status", async () => {
132
- const recorder = new RecordingSendText();
133
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
134
- const event = {
135
- runId: "run-1",
136
- seq: 1,
137
- stream: "xg-harness-tools.plan",
138
- ts: Date.now(),
139
- data: makeBlockedPayload({
140
- deliveryContext: { to: "group-1" },
141
- taskStatus: "completed",
142
- blockedNote: undefined,
143
- }),
144
- };
145
- await handle(event, FAKE_CTX);
146
- assert.equal(recorder.calls.length, 0);
147
- });
148
- it("does not push when no delivery target can be resolved", async () => {
149
- const recorder = new RecordingSendText();
150
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
151
- const event = {
152
- runId: "run-1",
153
- seq: 1,
154
- stream: "xg-harness-tools.plan",
155
- ts: Date.now(),
156
- data: makeBlockedPayload(), // 无 deliveryContext/agentAccountId/messageChannel/sessionKey
157
- };
158
- await handle(event, FAKE_CTX);
159
- assert.equal(recorder.calls.length, 0);
160
- });
161
- it("does not push when taskStatus is not blocked, even if blockedNote still carries a stale value (pins the taskStatus check)", async () => {
162
- const recorder = new RecordingSendText();
163
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
117
+ describe("createPlanEventHandler: 反查不到 不发不抛", () => {
118
+ it("getPlanById 返回 undefined 不调用 sendPlanStat,不抛", async () => {
119
+ const deps = new RecordingDeps();
120
+ const handle = createPlanEventHandler(deps);
164
121
  const event = {
165
122
  runId: "run-1",
166
123
  seq: 1,
167
124
  stream: "xg-harness-tools.plan",
168
125
  ts: Date.now(),
169
- data: makeBlockedPayload({
170
- deliveryContext: { to: "group-1" },
171
- taskStatus: "completed",
172
- blockedNote: "缺少权限", // 上游残留的旧阻塞原因,taskStatus 已不再是 blocked
173
- }),
126
+ data: makeBlockedPayload({ planId: "plan-does-not-exist" }),
174
127
  };
175
- await handle(event, FAKE_CTX);
176
- assert.equal(recorder.calls.length, 0);
177
- });
178
- it("does not push when deliveryContext.to is an empty string, everything else valid (pins the empty-string check)", async () => {
179
- const recorder = new RecordingSendText();
180
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
181
- const event = {
182
- runId: "run-1",
183
- seq: 1,
184
- stream: "xg-harness-tools.plan",
185
- ts: Date.now(),
186
- data: makeBlockedPayload({
187
- deliveryContext: { to: "" },
188
- }),
189
- };
190
- await handle(event, FAKE_CTX);
191
- assert.equal(recorder.calls.length, 0);
192
- });
193
- it("does not push when deliveryContext.to is not a string", async () => {
194
- const recorder = new RecordingSendText();
195
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
196
- const event = {
197
- runId: "run-1",
198
- seq: 1,
199
- stream: "xg-harness-tools.plan",
200
- ts: Date.now(),
201
- data: makeBlockedPayload({
202
- deliveryContext: { to: 123 },
203
- }),
204
- };
205
- await handle(event, FAKE_CTX);
206
- assert.equal(recorder.calls.length, 0);
128
+ await assert.doesNotReject(() => Promise.resolve(handle(event, FAKE_CTX)));
129
+ assert.equal(deps.planStatCalls.length, 0);
207
130
  });
208
- it("does not push when deliveryContext itself is not an object", async () => {
209
- const recorder = new RecordingSendText();
210
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
211
- const event = {
212
- runId: "run-1",
213
- seq: 1,
214
- stream: "xg-harness-tools.plan",
215
- ts: Date.now(),
216
- data: makeBlockedPayload({
217
- deliveryContext: "not-an-object",
218
- }),
131
+ });
132
+ describe("createPlanEventHandler: handle 内部出错不外抛", () => {
133
+ it("resolves normally even when getPlanById throws", async () => {
134
+ const deps = new RecordingDeps();
135
+ deps.getPlanById = () => {
136
+ throw new Error("boom");
219
137
  };
220
- await handle(event, FAKE_CTX);
221
- assert.equal(recorder.calls.length, 0);
222
- });
223
- it("does not push when blockedNote is whitespace-only, everything else valid (pins the trim/blank check)", async () => {
224
- const recorder = new RecordingSendText();
225
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
138
+ const handle = createPlanEventHandler(deps);
226
139
  const event = {
227
140
  runId: "run-1",
228
141
  seq: 1,
229
142
  stream: "xg-harness-tools.plan",
230
143
  ts: Date.now(),
231
- data: makeBlockedPayload({
232
- deliveryContext: { to: "group-1" },
233
- blockedNote: " ",
234
- }),
144
+ data: makeBlockedPayload(),
235
145
  };
236
- await handle(event, FAKE_CTX);
237
- assert.equal(recorder.calls.length, 0);
146
+ await assert.doesNotReject(() => Promise.resolve(handle(event, FAKE_CTX)));
147
+ assert.equal(deps.planStatCalls.length, 0);
238
148
  });
239
- });
240
- describe("createPlanEventHandler: handle 内部出错不外泄", () => {
241
- it("resolves normally even when sendText rejects", async () => {
242
- const recorder = new RecordingSendText();
243
- recorder.impl = async () => {
149
+ it("resolves normally even when sendPlanStat throws", async () => {
150
+ const deps = new RecordingDeps();
151
+ const plan = makePlan({ tasks: [{ title: "t1", status: "blocked", note: "n" }] });
152
+ deps.planById.set("plan-1", plan);
153
+ deps.sendPlanStat = () => {
244
154
  throw new Error("boom");
245
155
  };
246
- const handle = createPlanEventHandler({ sendText: recorder.sendText });
156
+ const handle = createPlanEventHandler(deps);
247
157
  const event = {
248
158
  runId: "run-1",
249
159
  seq: 1,
250
160
  stream: "xg-harness-tools.plan",
251
161
  ts: Date.now(),
252
- data: makeBlockedPayload({ deliveryContext: { to: "group-1" } }),
162
+ data: makeBlockedPayload(),
253
163
  };
254
164
  await assert.doesNotReject(() => Promise.resolve(handle(event, FAKE_CTX)));
255
165
  });
@@ -1 +1 @@
1
- {"version":3,"file":"plan-event-subscription.test.js","sourceRoot":"","sources":["../../src/plan-event-subscription.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plan-event-subscription.test.js","sourceRoot":"","sources":["../../src/plan-event-subscription.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { Plan } from "@xgjktech/xg-openclaw-shared";
2
+ import type { ImStreamClient } from "./connection.js";
3
+ /**
4
+ * 组装发往前端的 plan:剥离 origin(内部路由信息,含 sessionKey,前端不需要也不该看到)。
5
+ * 返回浅拷贝,不 mutate 入参(middleware 拿到的 plan 引用可能别处还要用)。
6
+ */
7
+ export declare function toWirePlan(plan: Plan): Plan;
8
+ /**
9
+ * 发送一条 PLAN_STAT。绝不抛、绝不 await 调用方(内部自己 catch)。
10
+ */
11
+ export declare function sendPlanStat(params: {
12
+ plan: Plan;
13
+ source: "tool" | "snapshot";
14
+ }): void;
15
+ /**
16
+ * 登记 WS client(供后续 PLAN_STAT 增量路由),登记完立刻 fire-and-forget 触发一次快照:
17
+ * 反查该群未完成的计划,逐个发 PLAN_STAT(source: "snapshot")。
18
+ *
19
+ * dispatch 是热路径:整个快照过程包 try/catch、绝不 await、绝不抛——快照失败不能连累
20
+ * WS 绑定本身(登记必须先于快照执行、且不受快照结果影响)。
21
+ *
22
+ * `deps.listPlansByOrigin` 默认注入 plan-store-access 的实现;测试可传入假实现
23
+ * 模拟反查异常(红线防御性场景——即便被注入的反查实现意外抛出,本函数也绝不外抛)。
24
+ */
25
+ export declare function registerPlanWsClientAndSnapshot(groupId: string, client: ImStreamClient, replyToMsgId: string, deps?: {
26
+ listPlansByOrigin: (channel: string, to: string) => Plan[];
27
+ }): void;
28
+ //# sourceMappingURL=plan-stat-event.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-stat-event.d.ts","sourceRoot":"","sources":["../../src/plan-stat-event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAMtD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAG3C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE,GAAG,IAAI,CA4BtF;AAOD;;;;;;;;;GASG;AACH,wBAAgB,+BAA+B,CAC3C,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,cAAc,EACtB,YAAY,EAAE,MAAM,EACpB,IAAI,GAAE;IAAE,iBAAiB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,EAAE,CAAA;CAA0B,GAC7F,IAAI,CAcN"}
@@ -0,0 +1,68 @@
1
+ import { getPlanWsClient, setPlanWsClient } from "./plan-ws-registry.js";
2
+ import { listPlansByOrigin } from "./plan-store-access.js";
3
+ const LOG_PREFIX = "[xg_cwork_im] [plan-stat-event]";
4
+ /**
5
+ * 组装发往前端的 plan:剥离 origin(内部路由信息,含 sessionKey,前端不需要也不该看到)。
6
+ * 返回浅拷贝,不 mutate 入参(middleware 拿到的 plan 引用可能别处还要用)。
7
+ */
8
+ export function toWirePlan(plan) {
9
+ const { origin: _origin, ...wirePlan } = plan;
10
+ return wirePlan;
11
+ }
12
+ /**
13
+ * 发送一条 PLAN_STAT。绝不抛、绝不 await 调用方(内部自己 catch)。
14
+ */
15
+ export function sendPlanStat(params) {
16
+ const { plan, source } = params;
17
+ const channel = plan.origin?.channel;
18
+ const to = plan.origin?.to;
19
+ if (channel !== "xg_cwork_im" || !to) {
20
+ console.warn(`${LOG_PREFIX} plan.origin 缺失或非本通道,跳过 PLAN_STAT planId=${plan.planId} channel=${channel ?? "(none)"}`);
21
+ return;
22
+ }
23
+ const target = getPlanWsClient(to);
24
+ if (!target) {
25
+ console.warn(`${LOG_PREFIX} 未登记 WS client,跳过 PLAN_STAT planId=${plan.planId} groupId=${to}`);
26
+ return;
27
+ }
28
+ void target.client
29
+ .sendEvent({
30
+ groupId: to,
31
+ replyToMsgId: target.replyToMsgId,
32
+ event: "PLAN_STAT",
33
+ data: { source, plan: toWirePlan(plan) },
34
+ })
35
+ .catch((err) => {
36
+ console.warn(`${LOG_PREFIX} 发送失败 planId=${plan.planId} groupId=${to}: ${String(err)}`);
37
+ });
38
+ }
39
+ /** 「未完成」判定:plan 中存在 status !== "completed" 的 task。 */
40
+ function isUnfinished(plan) {
41
+ return plan.tasks.some((task) => task.status !== "completed");
42
+ }
43
+ /**
44
+ * 登记 WS client(供后续 PLAN_STAT 增量路由),登记完立刻 fire-and-forget 触发一次快照:
45
+ * 反查该群未完成的计划,逐个发 PLAN_STAT(source: "snapshot")。
46
+ *
47
+ * dispatch 是热路径:整个快照过程包 try/catch、绝不 await、绝不抛——快照失败不能连累
48
+ * WS 绑定本身(登记必须先于快照执行、且不受快照结果影响)。
49
+ *
50
+ * `deps.listPlansByOrigin` 默认注入 plan-store-access 的实现;测试可传入假实现
51
+ * 模拟反查异常(红线防御性场景——即便被注入的反查实现意外抛出,本函数也绝不外抛)。
52
+ */
53
+ export function registerPlanWsClientAndSnapshot(groupId, client, replyToMsgId, deps = { listPlansByOrigin }) {
54
+ setPlanWsClient(groupId, client, replyToMsgId);
55
+ try {
56
+ const plans = deps.listPlansByOrigin("xg_cwork_im", groupId);
57
+ for (const plan of plans) {
58
+ if (!isUnfinished(plan)) {
59
+ continue;
60
+ }
61
+ sendPlanStat({ plan, source: "snapshot" });
62
+ }
63
+ }
64
+ catch (err) {
65
+ console.warn(`${LOG_PREFIX} 会话快照失败,已吞 groupId=${groupId}: ${String(err)}`);
66
+ }
67
+ }
68
+ //# sourceMappingURL=plan-stat-event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-stat-event.js","sourceRoot":"","sources":["../../src/plan-stat-event.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,MAAM,UAAU,GAAG,iCAAiC,CAAC;AAErD;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,IAAU;IACjC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC9C,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAAmD;IAC5E,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAEhC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;IACrC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;IAC3B,IAAI,OAAO,KAAK,aAAa,IAAI,CAAC,EAAE,EAAE,CAAC;QACnC,OAAO,CAAC,IAAI,CACR,GAAG,UAAU,4CAA4C,IAAI,CAAC,MAAM,YAAY,OAAO,IAAI,QAAQ,EAAE,CACxG,CAAC;QACF,OAAO;IACX,CAAC;IAED,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,OAAO,CAAC,IAAI,CAAC,GAAG,UAAU,sCAAsC,IAAI,CAAC,MAAM,YAAY,EAAE,EAAE,CAAC,CAAC;QAC7F,OAAO;IACX,CAAC;IAED,KAAK,MAAM,CAAC,MAAM;SACb,SAAS,CAAC;QACP,OAAO,EAAE,EAAE;QACX,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,KAAK,EAAE,WAAW;QAClB,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE;KAC3C,CAAC;SACD,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QACpB,OAAO,CAAC,IAAI,CAAC,GAAG,UAAU,gBAAgB,IAAI,CAAC,MAAM,YAAY,EAAE,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;AACX,CAAC;AAED,sDAAsD;AACtD,SAAS,YAAY,CAAC,IAAU;IAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,+BAA+B,CAC3C,OAAe,EACf,MAAsB,EACtB,YAAoB,EACpB,OAAuE,EAAE,iBAAiB,EAAE;IAE5F,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;IAE/C,IAAI,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAC7D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtB,SAAS;YACb,CAAC;YACD,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;QAC/C,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,GAAG,UAAU,sBAAsB,OAAO,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/E,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=plan-stat-event.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-stat-event.test.d.ts","sourceRoot":"","sources":["../../src/plan-stat-event.test.ts"],"names":[],"mappings":""}