fix(matrix): bind approval reactions before option emoji · openclaw/openclaw@1c2eda2
vincentkoc
·
2026-05-04
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -67,6 +67,7 @@ Docs: https://docs.openclaw.ai
|
67 | 67 | - Slack/Matrix: avoid creating blank progress-draft messages when `streaming.progress.label=false` and progress tool lines are disabled. Thanks @vincentkoc. |
68 | 68 | - Slack/Discord: suppress standalone tool-progress chatter when partial preview streaming has `streaming.preview.toolProgress: false`, matching the documented quiet-preview behavior. Thanks @vincentkoc. |
69 | 69 | - QA/Matrix: keep the mock OpenAI tool-progress provider aligned with exact-marker Matrix prompts so the hardened live preview scenario still forces a deterministic read before final delivery. Thanks @vincentkoc. |
| 70 | +- Matrix: bind native approval reaction targets before publishing option reactions, so fast approver reactions on threaded prompts are not dropped while the approval handler finishes setup. Thanks @vincentkoc. |
70 | 71 | - Google Meet: make realtime talk-back agent-driven by default with `realtime.strategy: "agent"`, keep the previous direct bidirectional model behavior available as `realtime.strategy: "bidi"`, route the Meet tab speaker output to `BlackHole 2ch` automatically for local Chrome realtime joins, coalesce nearby speech transcript fragments before consulting the agent, and avoid cutting off agent speech from server VAD or stale playback pipe errors. |
71 | 72 | - Google Meet: suppress queued assistant playback and assistant-like transcript echoes from the realtime input path, so the meeting does not hear the agent's own speech as a new user turn and loop or cut itself off. |
72 | 73 | - OpenAI/Google Meet: wait for realtime voice `session.updated` before treating the bridge as connected, so Meet joins do not return with audio queued behind an unconfigured realtime session. Thanks @vincentkoc. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -266,6 +266,52 @@ describe("matrixApprovalNativeRuntime", () => {
|
266 | 266 | ); |
267 | 267 | }); |
268 | 268 | |
| 269 | +it("binds Matrix approval reactions before publishing option reactions", async () => { |
| 270 | +const sendSingleTextMessage = vi.fn().mockResolvedValue({ |
| 271 | +messageId: "$approval", |
| 272 | +primaryMessageId: "$approval", |
| 273 | +messageIds: ["$approval"], |
| 274 | +roomId: "!room:example.org", |
| 275 | +}); |
| 276 | +const reactMessage = vi.fn().mockImplementation(async () => { |
| 277 | +expect( |
| 278 | +resolveMatrixApprovalReactionTarget({ |
| 279 | +roomId: "!room:example.org", |
| 280 | +eventId: "$approval", |
| 281 | +reactionKey: "✅", |
| 282 | +}), |
| 283 | +).toEqual({ |
| 284 | +approvalId: "req-1", |
| 285 | +decision: "allow-once", |
| 286 | +}); |
| 287 | +}); |
| 288 | +const view = buildExecApprovalView(); |
| 289 | +const pendingPayload = await buildPendingPayload(view); |
| 290 | + |
| 291 | +await matrixApprovalNativeRuntime.transport.deliverPending({ |
| 292 | +cfg: {} as never, |
| 293 | +accountId: "default", |
| 294 | +context: { |
| 295 | +client: {} as never, |
| 296 | +deps: { |
| 297 | + sendSingleTextMessage, |
| 298 | + reactMessage, |
| 299 | +}, |
| 300 | +}, |
| 301 | +request: {} as never, |
| 302 | +approvalKind: "exec", |
| 303 | +plannedTarget: buildMatrixApprovalRoomTarget("!room:example.org"), |
| 304 | +preparedTarget: { |
| 305 | +to: "room:!room:example.org", |
| 306 | +roomId: "!room:example.org", |
| 307 | +}, |
| 308 | + view, |
| 309 | + pendingPayload, |
| 310 | +}); |
| 311 | + |
| 312 | +expect(reactMessage).toHaveBeenCalled(); |
| 313 | +}); |
| 314 | + |
269 | 315 | it("falls back to chunked Matrix delivery when approval content exceeds one event", async () => { |
270 | 316 | const sendSingleTextMessage = vi |
271 | 317 | .fn() |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -408,7 +408,7 @@ export const matrixApprovalNativeRuntime = createChannelApprovalNativeRuntimeAda
|
408 | 408 | : null, |
409 | 409 | ); |
410 | 410 | }, |
411 | | -deliverPending: async ({ cfg, accountId, context, preparedTarget, pendingPayload }) => { |
| 411 | +deliverPending: async ({ cfg, accountId, context, preparedTarget, pendingPayload, view }) => { |
412 | 412 | const resolved = resolveHandlerContext({ cfg, accountId, context }); |
413 | 413 | if (!resolved) { |
414 | 414 | return null; |
@@ -447,6 +447,13 @@ export const matrixApprovalNativeRuntime = createChannelApprovalNativeRuntimeAda
|
447 | 447 | ); |
448 | 448 | const reactionEventId = |
449 | 449 | result.primaryMessageId?.trim() || messageIds[0] || result.messageId.trim(); |
| 450 | +registerMatrixApprovalReactionTarget({ |
| 451 | +roomId: result.roomId, |
| 452 | +eventId: reactionEventId, |
| 453 | +approvalId: pendingPayload.approvalId, |
| 454 | +allowedDecisions: pendingPayload.allowedDecisions, |
| 455 | +ttlMs: view.expiresAtMs - Date.now(), |
| 456 | +}); |
450 | 457 | await Promise.allSettled( |
451 | 458 | listMatrixApprovalReactionBindings(pendingPayload.allowedDecisions).map( |
452 | 459 | async ({ emoji }) => { |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。