Skip to content

Commit d2a1f62

Browse files
committed
fix(matrix): keep fallback tool warnings mention-inert
1 parent 98a9a52 commit d2a1f62

7 files changed

Lines changed: 106 additions & 20 deletions

File tree

extensions/matrix/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"docsLabel": "matrix",
4646
"blurb": "open protocol; install the plugin to enable.",
4747
"order": 70,
48+
"markdownCapable": true,
4849
"quickstartAllowFrom": true,
4950
"doctorCapabilities": {
5051
"dmAllowFromMode": "nestedOnly",

extensions/matrix/src/channel.message-adapter.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ describe("matrix channel message adapter", () => {
6060
mocks.sendMessageMatrix.mockReset();
6161
});
6262

63+
it("declares Matrix markdown rendering support for shared reply payloads", () => {
64+
expect(matrixPlugin.meta.markdownCapable).toBe(true);
65+
});
66+
6367
beforeEach(() => {
6468
mocks.sendMessageMatrix.mockReset();
6569
mocks.sendMessageMatrix.mockResolvedValue({ messageId: "$event-1", roomId: "!room:example" });

extensions/matrix/src/channel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ const meta = {
9494
docsLabel: "matrix",
9595
blurb: "open protocol; configure a homeserver + access token.",
9696
order: 70,
97+
markdownCapable: true,
9798
quickstartAllowFrom: true,
9899
};
99100

extensions/qa-matrix/src/runners/contract/scenarios.test.ts

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3887,8 +3887,7 @@ describe("matrix live qa scenarios", () => {
38873887
event: matrixQaMessageEvent({
38883888
kind: "message",
38893889
eventId: "$tool-progress-mention-edit",
3890-
body:
3891-
"Working...\n- `read matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt failed`",
3890+
body: "Working...\n- `read matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt failed`",
38923891
formattedBody:
38933892
"Working...<br><ul><li><code>read matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt failed</code></li></ul>",
38943893
mentions: {},
@@ -3958,8 +3957,7 @@ describe("matrix live qa scenarios", () => {
39583957
event: matrixQaMessageEvent({
39593958
kind: "message",
39603959
eventId: "$tool-progress-mention-final-first-progress",
3961-
body:
3962-
"Working...\n- `read matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt failed`",
3960+
body: "Working...\n- `read matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt failed`",
39633961
formattedBody:
39643962
"Working...<br><ul><li><code>read matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt failed</code></li></ul>",
39653963
mentions: {
@@ -3982,15 +3980,59 @@ describe("matrix live qa scenarios", () => {
39823980
);
39833981
});
39843982

3983+
it("keeps Matrix-looking top-level tool errors inert after final-first replies", async () => {
3984+
mockMatrixQaRoomClient({
3985+
driverEventId: "$tool-progress-mention-top-level-trigger",
3986+
events: [
3987+
{
3988+
event: ({ sendTextMessage }) =>
3989+
matrixQaMessageEvent({
3990+
kind: "message",
3991+
eventId: "$tool-progress-mention-top-level-final",
3992+
body: readMatrixQaReplyDirective(
3993+
mockMessageBody(sendTextMessage, "sendTextMessage"),
3994+
"MATRIX_QA_TOOL_PROGRESS_MENTION_SAFE_FIXED",
3995+
),
3996+
}),
3997+
since: "driver-sync-final",
3998+
},
3999+
{
4000+
event: matrixQaMessageEvent({
4001+
kind: "message",
4002+
eventId: "$tool-progress-mention-top-level-progress",
4003+
body: "⚠️ 🛠️ `show matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt (workspace)` failed",
4004+
formattedBody:
4005+
"<p>⚠️ 🛠️ <code>show matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt (workspace)</code> failed</p>",
4006+
mentions: {},
4007+
}),
4008+
since: "driver-sync-progress",
4009+
},
4010+
],
4011+
});
4012+
4013+
const scenario = requireMatrixQaScenario("matrix-room-tool-progress-mention-safety");
4014+
4015+
const result = await runMatrixQaScenario(scenario, matrixQaScenarioContext());
4016+
const artifacts = result.artifacts as {
4017+
previewEventId?: unknown;
4018+
previewFormattedBodyPreview?: unknown;
4019+
previewMentions?: unknown;
4020+
reply?: { eventId?: unknown };
4021+
};
4022+
expect(artifacts.previewEventId).toBe("$tool-progress-mention-top-level-progress");
4023+
expect(artifacts.previewFormattedBodyPreview).toContain("<code>show matrix-progress-@room");
4024+
expect(artifacts.previewMentions).toEqual({});
4025+
expect(artifacts.reply?.eventId).toBe("$tool-progress-mention-top-level-final");
4026+
});
4027+
39854028
it("does not accept top-level finals after a Matrix mention-safety preview", async () => {
39864029
const context = matrixQaScenarioContext();
39874030
const primeRoom = vi.fn().mockResolvedValue("driver-sync-start");
39884031
const sendTextMessage = vi.fn().mockResolvedValue("$tool-progress-mention-stale-trigger");
39894032
const previewEvent = matrixQaMessageEvent({
39904033
kind: "message",
39914034
eventId: "$tool-progress-mention-stale-preview",
3992-
body:
3993-
"Working...\n- `read matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt failed`",
4035+
body: "Working...\n- `read matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt failed`",
39944036
formattedBody:
39954037
"Working...<br><ul><li><code>read matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt failed</code></li></ul>",
39964038
mentions: {},
@@ -4001,19 +4043,21 @@ describe("matrix live qa scenarios", () => {
40014043
context.observedEvents.push(previewEvent);
40024044
return { event: previewEvent, since: "driver-sync-preview" };
40034045
})
4004-
.mockImplementationOnce(async (params: { predicate: (event: MatrixQaObservedEvent) => boolean }) => {
4005-
const topLevelFinal = matrixQaMessageEvent({
4006-
kind: "message",
4007-
eventId: "$tool-progress-mention-stale-final",
4008-
body: readMatrixQaReplyDirective(
4009-
mockMessageBody(sendTextMessage, "sendTextMessage"),
4010-
"MATRIX_QA_TOOL_PROGRESS_MENTION_SAFE_FIXED",
4011-
),
4012-
});
4013-
expect(params.predicate(topLevelFinal)).toBe(false);
4014-
context.observedEvents.push(topLevelFinal);
4015-
throw new Error("timed out after 8000ms waiting for Matrix room event");
4016-
});
4046+
.mockImplementationOnce(
4047+
async (params: { predicate: (event: MatrixQaObservedEvent) => boolean }) => {
4048+
const topLevelFinal = matrixQaMessageEvent({
4049+
kind: "message",
4050+
eventId: "$tool-progress-mention-stale-final",
4051+
body: readMatrixQaReplyDirective(
4052+
mockMessageBody(sendTextMessage, "sendTextMessage"),
4053+
"MATRIX_QA_TOOL_PROGRESS_MENTION_SAFE_FIXED",
4054+
),
4055+
});
4056+
expect(params.predicate(topLevelFinal)).toBe(false);
4057+
context.observedEvents.push(topLevelFinal);
4058+
throw new Error("timed out after 8000ms waiting for Matrix room event");
4059+
},
4060+
);
40174061
createMatrixQaClient.mockReturnValue({
40184062
primeRoom,
40194063
sendTextMessage,

scripts/lib/official-external-channel-catalog.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@
260260
"docsLabel": "matrix",
261261
"blurb": "open protocol; install the plugin to enable.",
262262
"order": 70,
263+
"markdownCapable": true,
263264
"quickstartAllowFrom": true,
264265
"doctorCapabilities": {
265266
"dmAllowFromMode": "nestedOnly",

src/agents/pi-embedded-runner/run/payloads.errors.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,23 @@ describe("buildEmbeddedRunPayloads", () => {
652652
expectSinglePayloadSummary(payloads, { text: warningText ?? "" });
653653
});
654654

655+
it("wraps markdown-capable mutating tool warnings so mention-looking names stay inert", () => {
656+
const payloads = buildPayloads({
657+
lastToolError: {
658+
toolName: "bash",
659+
meta: "show matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt (workspace)",
660+
error: "file missing",
661+
mutatingAction: true,
662+
},
663+
toolResultFormat: "markdown",
664+
});
665+
666+
expectSinglePayloadSummary(payloads, {
667+
text: "⚠️ 🛠️ `show matrix-progress-@room-@alice:matrix-qa.test-!room:matrix-qa.test.txt (workspace)` failed",
668+
isError: true,
669+
});
670+
});
671+
655672
it("keeps non-recoverable tool errors compact when verbose mode is on", () => {
656673
const payloads = buildPayloads({
657674
lastToolError: { toolName: "browser", error: "connection timeout" },

src/utils/message-channel.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
1+
import path from "node:path";
2+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
23
import type { ChannelPlugin } from "../channels/plugins/types.js";
34
import { setActivePluginRegistry } from "../plugins/runtime.js";
45
import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
@@ -81,6 +82,23 @@ describe("message-channel", () => {
8182
expect(isMarkdownCapableMessageChannel("demo-markdown-channel")).toBe(true);
8283
});
8384

85+
it("reads Matrix markdown capability from bundled channel catalog metadata", async () => {
86+
const previousBundledPluginsDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
87+
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = path.resolve("extensions");
88+
vi.resetModules();
89+
try {
90+
const module = await import("./message-channel.js");
91+
expect(module.isMarkdownCapableMessageChannel("matrix")).toBe(true);
92+
} finally {
93+
if (previousBundledPluginsDir === undefined) {
94+
delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
95+
} else {
96+
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = previousBundledPluginsDir;
97+
}
98+
vi.resetModules();
99+
}
100+
});
101+
84102
it("treats registered plugin channels without markdown metadata as plain text", () => {
85103
setActivePluginRegistry(
86104
createTestRegistry([

0 commit comments

Comments
 (0)