docs: document embedded subscribe tools

This commit is contained in:
Peter Steinberger
2026-06-04 15:04:14 -04:00
parent e0fe08ccce
commit 5864669b3b
5 changed files with 31 additions and 0 deletions
@@ -1,15 +1,21 @@
// Messaging tool extraction tests cover channel/provider normalization, thread
// evidence, and plugin-provided send extraction hooks.
import { beforeEach, describe, expect, it } from "vitest";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
import { extractMessagingToolSend } from "./embedded-agent-subscribe.tools.js";
function normalizeTelegramMessagingTargetForTest(raw: string): string | undefined {
// Test normalizer mirrors channel plugins that canonicalize human targets
// before subscription delivery tracking stores them.
const trimmed = raw.trim();
return trimmed ? `telegram:${trimmed}` : undefined;
}
describe("extractMessagingToolSend", () => {
beforeEach(() => {
// Active registry state drives provider-specific extraction; reset it for
// each case so channel plugin behavior is deterministic.
setActivePluginRegistry(
createTestRegistry([
{
@@ -1,3 +1,5 @@
// Tool media extraction tests cover structured media payloads, image fallbacks,
// trust decisions, and filtering of local/remote media URLs.
import { describe, expect, it } from "vitest";
import {
extractToolResultMediaArtifact,
@@ -137,6 +139,8 @@ describe("extractToolResultMediaPaths", () => {
});
it("falls back to details.path when image content exists", () => {
// Embedded read image results omit structured media but include details.path,
// so image content is the guard that makes that path media.
// Embedded read tool doesn't include structured media but OpenClaw
// imageResult sets details.path as fallback.
const result = {
@@ -175,6 +179,8 @@ describe("extractToolResultMediaPaths", () => {
});
it("ignores details.path when no image content exists", () => {
// Plain file paths in details are not media unless the content proves an
// image/audio/video artifact was produced.
// details.path without image content is not media.
const result = {
content: [{ type: "text", text: "File saved" }],
@@ -1,3 +1,5 @@
// Tool subscription helper tests cover error extraction, sanitized tool results,
// and safe lifecycle payloads for embedded tool events.
import { afterEach, describe, expect, it, vi } from "vitest";
import * as loggingConfigModule from "../logging/config.js";
import {
@@ -9,6 +11,8 @@ import {
} from "./embedded-agent-subscribe.tools.js";
afterEach(() => {
// Logging config spies are global module state; restore after every sanitizer
// and lifecycle helper case.
vi.restoreAllMocks();
});
@@ -122,6 +126,7 @@ describe("extractToolErrorMessage", () => {
});
function getTextContent(result: unknown, index = 0): string {
// Sanitizer tests assert text redaction while keeping the result shape opaque.
const record = result as { content: Array<{ text: string }> };
return record.content[index].text;
}
+6
View File
@@ -1,3 +1,5 @@
// Exec approval result tests cover parsing gateway/node approval payloads and
// mapping denied metadata to safe user-facing copy.
import { describe, expect, it } from "vitest";
import {
formatExecDeniedUserMessage,
@@ -30,6 +32,8 @@ describe("parseExecApprovalResultText", () => {
});
it("parses denied results with the canonical colon-separated deniedReason", () => {
// Colon-separated metadata avoids ambiguity with nested parentheses in
// human-readable denial reasons.
// Producer (src/agents/bash-tools.exec-host-gateway.ts) emits a colon
// separator instead of nested parens to keep the (...)-delimited wire
// format unambiguous. This is the format real timeouts now produce.
@@ -91,6 +95,8 @@ describe("parseExecApprovalResultText", () => {
])(
"returns other when metadata is not gateway/node sourced (CWE-841 spoof guard): %s",
(input) => {
// Only gateway/node-sourced payloads get parsed as approval results; prose
// that looks similar must not spoof command approval state.
expect(parseExecApprovalResultText(input)).toEqual({
kind: "other",
raw: input,
+8
View File
@@ -1,3 +1,5 @@
// Exec auto-reviewer tests cover model response parsing, low-risk allow gates,
// reviewer prompt isolation, and timeout resolution.
import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion";
import { describe, expect, it, vi } from "vitest";
import {
@@ -7,6 +9,8 @@ import {
} from "./exec-auto-reviewer.js";
const input = {
// Baseline approval request is read-only; individual cases override command
// text or analysis fields to exercise escalation behavior.
command: "git status",
argv: ["git", "status"],
cwd: "/repo",
@@ -54,6 +58,8 @@ describe("parseExecAutoReviewResponse", () => {
});
it("normalizes unsupported or malformed decisions to human review", () => {
// Reviewer output is untrusted model text; only a bare JSON object matching
// the allow/ask schema can affect approval flow.
expect(parseExecAutoReviewResponse("sure, run it")).toMatchObject({
decision: "ask",
});
@@ -178,6 +184,8 @@ describe("createModelExecAutoReviewer", () => {
});
it("defers to human approval when command text tries to instruct the reviewer", async () => {
// Command content is adversarial input to the reviewer. Prompt-injection
// attempts force human review even if the model returns a low-risk allow.
const prepare = vi.fn(async () => ({
selection: {
provider: "openrouter",