mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-06 14:51:08 +00:00
* fix(status): strip internal runtime context from task detail surfaces * fix(agent): narrow legacy internal-context stripping * fix(tasks): sanitize user-facing task status surfaces * fix(agent): close remaining internal-context leak paths * fix(agent): harden internal context delimiter sanitization --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
188 lines
5.9 KiB
TypeScript
188 lines
5.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
formatTaskBlockedFollowupMessage,
|
|
formatTaskStateChangeMessage,
|
|
formatTaskTerminalMessage,
|
|
isTerminalTaskStatus,
|
|
shouldAutoDeliverTaskStateChange,
|
|
shouldAutoDeliverTaskTerminalUpdate,
|
|
shouldSuppressDuplicateTerminalDelivery,
|
|
} from "./task-executor-policy.js";
|
|
import type { TaskEventRecord, TaskRecord } from "./task-registry.types.js";
|
|
|
|
function createTask(partial: Partial<TaskRecord>): TaskRecord {
|
|
return {
|
|
taskId: partial.taskId ?? "task-1",
|
|
runtime: partial.runtime ?? "acp",
|
|
requesterSessionKey: partial.requesterSessionKey ?? partial.ownerKey ?? "agent:main:main",
|
|
ownerKey: partial.ownerKey ?? partial.requesterSessionKey ?? "agent:main:main",
|
|
scopeKind: partial.scopeKind ?? "session",
|
|
task: partial.task ?? "Investigate issue",
|
|
status: partial.status ?? "running",
|
|
deliveryStatus: partial.deliveryStatus ?? "pending",
|
|
notifyPolicy: partial.notifyPolicy ?? "done_only",
|
|
createdAt: partial.createdAt ?? 1,
|
|
...partial,
|
|
};
|
|
}
|
|
|
|
describe("task-executor-policy", () => {
|
|
it("identifies terminal statuses", () => {
|
|
expect(isTerminalTaskStatus("queued")).toBe(false);
|
|
expect(isTerminalTaskStatus("running")).toBe(false);
|
|
expect(isTerminalTaskStatus("succeeded")).toBe(true);
|
|
expect(isTerminalTaskStatus("failed")).toBe(true);
|
|
expect(isTerminalTaskStatus("timed_out")).toBe(true);
|
|
expect(isTerminalTaskStatus("cancelled")).toBe(true);
|
|
expect(isTerminalTaskStatus("lost")).toBe(true);
|
|
});
|
|
|
|
it("formats terminal, followup, and progress messages", () => {
|
|
const blockedTask = createTask({
|
|
status: "succeeded",
|
|
terminalOutcome: "blocked",
|
|
terminalSummary: "Needs login.",
|
|
runId: "run-1234567890",
|
|
label: "ACP import",
|
|
});
|
|
const progressEvent: TaskEventRecord = {
|
|
at: 10,
|
|
kind: "progress",
|
|
summary: "No output for 60s.",
|
|
};
|
|
|
|
expect(formatTaskTerminalMessage(blockedTask)).toBe(
|
|
"Background task blocked: ACP import (run run-1234). Needs login.",
|
|
);
|
|
expect(formatTaskBlockedFollowupMessage(blockedTask)).toBe(
|
|
"Task needs follow-up: ACP import (run run-1234). Needs login.",
|
|
);
|
|
expect(formatTaskStateChangeMessage(blockedTask, progressEvent)).toBe(
|
|
"Background task update: ACP import. No output for 60s.",
|
|
);
|
|
});
|
|
|
|
it("sanitizes leaked internal runtime context from terminal and progress copy", () => {
|
|
const leaked = [
|
|
"OpenClaw runtime context (internal):",
|
|
"This context is runtime-generated, not user-authored. Keep internal details private.",
|
|
"",
|
|
"[Internal task completion event]",
|
|
"source: subagent",
|
|
].join("\n");
|
|
const blockedTask = createTask({
|
|
status: "succeeded",
|
|
terminalOutcome: "blocked",
|
|
terminalSummary: leaked,
|
|
runId: "run-1234567890",
|
|
label: leaked,
|
|
});
|
|
const failedTask = createTask({
|
|
status: "failed",
|
|
error: leaked,
|
|
terminalSummary: "Needs manual approval.",
|
|
runId: "run-2234567890",
|
|
label: leaked,
|
|
});
|
|
const progressEvent: TaskEventRecord = {
|
|
at: 10,
|
|
kind: "progress",
|
|
summary: leaked,
|
|
};
|
|
|
|
expect(formatTaskTerminalMessage(blockedTask)).toBe(
|
|
"Background task blocked: Background task (run run-1234).",
|
|
);
|
|
expect(formatTaskBlockedFollowupMessage(blockedTask)).toBe(
|
|
"Task needs follow-up: Background task (run run-1234). Task is blocked and needs follow-up.",
|
|
);
|
|
expect(formatTaskTerminalMessage(failedTask)).toBe(
|
|
"Background task failed: Background task (run run-2234). Needs manual approval.",
|
|
);
|
|
expect(formatTaskStateChangeMessage(blockedTask, progressEvent)).toBeNull();
|
|
});
|
|
|
|
it("redacts raw exec denial text from blocked task updates", () => {
|
|
const blockedTask = createTask({
|
|
status: "succeeded",
|
|
terminalOutcome: "blocked",
|
|
terminalSummary: "Exec denied (gateway id=req-1, approval-timeout): bash -lc ls",
|
|
runId: "run-1234567890",
|
|
label: "ACP import",
|
|
});
|
|
|
|
expect(formatTaskTerminalMessage(blockedTask)).toBe(
|
|
"Background task blocked: ACP import (run run-1234). Command did not run: approval timed out.",
|
|
);
|
|
expect(formatTaskBlockedFollowupMessage(blockedTask)).toBe(
|
|
"Task needs follow-up: ACP import (run run-1234). Command did not run: approval timed out.",
|
|
);
|
|
});
|
|
|
|
it("keeps delivery policy decisions explicit", () => {
|
|
expect(
|
|
shouldAutoDeliverTaskTerminalUpdate(
|
|
createTask({
|
|
status: "succeeded",
|
|
deliveryStatus: "pending",
|
|
notifyPolicy: "done_only",
|
|
}),
|
|
),
|
|
).toBe(true);
|
|
expect(
|
|
shouldAutoDeliverTaskTerminalUpdate(
|
|
createTask({
|
|
runtime: "subagent",
|
|
status: "succeeded",
|
|
deliveryStatus: "pending",
|
|
}),
|
|
),
|
|
).toBe(false);
|
|
expect(
|
|
shouldAutoDeliverTaskStateChange(
|
|
createTask({
|
|
status: "running",
|
|
notifyPolicy: "state_changes",
|
|
deliveryStatus: "pending",
|
|
}),
|
|
),
|
|
).toBe(true);
|
|
expect(
|
|
shouldAutoDeliverTaskStateChange(
|
|
createTask({
|
|
status: "failed",
|
|
notifyPolicy: "state_changes",
|
|
deliveryStatus: "pending",
|
|
}),
|
|
),
|
|
).toBe(false);
|
|
expect(
|
|
shouldSuppressDuplicateTerminalDelivery({
|
|
task: createTask({
|
|
runtime: "acp",
|
|
runId: "run-duplicate",
|
|
}),
|
|
preferredTaskId: "task-2",
|
|
}),
|
|
).toBe(true);
|
|
expect(
|
|
shouldSuppressDuplicateTerminalDelivery({
|
|
task: createTask({
|
|
runtime: "acp",
|
|
runId: "run-duplicate",
|
|
}),
|
|
preferredTaskId: "task-1",
|
|
}),
|
|
).toBe(false);
|
|
expect(
|
|
shouldSuppressDuplicateTerminalDelivery({
|
|
task: createTask({
|
|
runtime: "acp",
|
|
runId: "run-duplicate",
|
|
}),
|
|
preferredTaskId: undefined,
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
});
|