mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-12 02:56:07 +00:00
feat: correlate native search outcomes in audit history (#98704)
* feat: correlate native search outcomes in audit history Metadata-only audit ledger for agent runs and tool actions in the shared state DB: stable event identity, closed action/status/error vocabularies, one-way-hashed tool-call ids, never-inferred terminal outcomes for native web-search (explicit completed/failed only; otherwise unknown), bounded retention, audit.list gateway RPC and openclaw audit CLI. Squashed from the 82-commit audit stack for replay onto current main. * feat(audit): add audit.enabled config gate (default on) The metadata-only audit ledger records by default: an audit trail enabled only after an incident cannot explain the incident, and the rows are strictly less sensitive than the transcripts every install already stores. audit.enabled=false stops new writes at the gateway subscription seam; audit.list and openclaw audit keep serving existing records until they expire. Documented in the configuration reference, protocol page, and CLI reference. * fix: repair full-matrix CI findings after rebase - break the dynamic-tools/dynamic-tool-execution import cycle by extracting resolveCodexToolAbortTerminalReason into a leaf module - restore main's session-worktree protocol exports lost in the index.ts auto-merge - register the audit event writer worker as a knip entry point - docs table formatting; subagent wait-cancellation test scoped to its audit intent (outcome + timing) and advanced past main's new lifecycle-timeout retry grace
This commit is contained in:
committed by
GitHub
parent
96e676c2b3
commit
f53346944d
@@ -87,6 +87,11 @@ const coreEntrySpecs: readonly CommandGroupDescriptorSpec<
|
||||
loadModule: () => import("./register.migrate.js"),
|
||||
exportName: "registerMigrateCommand",
|
||||
},
|
||||
{
|
||||
commandNames: ["audit"],
|
||||
loadModule: () => import("./register.audit.js"),
|
||||
exportName: "registerAuditCommand",
|
||||
},
|
||||
{
|
||||
commandNames: ["doctor", "dashboard", "reset", "uninstall"],
|
||||
loadModule: () => import("./register.maintenance.js"),
|
||||
|
||||
@@ -353,7 +353,7 @@ describe("ensureConfigReady", () => {
|
||||
"",
|
||||
`Fix: ${formatCliCommand("openclaw doctor --fix")}`,
|
||||
`Inspect: ${formatCliCommand("openclaw config validate")}`,
|
||||
"Status, health, logs, tasks list/audit, and doctor commands still run with invalid config.",
|
||||
"Audit, status, health, logs, tasks list/audit, and doctor commands still run with invalid config.",
|
||||
]);
|
||||
expect(runtime.exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
@@ -389,6 +389,9 @@ describe("ensureConfigReady", () => {
|
||||
const statusRuntime = await runEnsureConfigReady(["status"]);
|
||||
expect(statusRuntime.exit).not.toHaveBeenCalled();
|
||||
|
||||
const auditRuntime = await runEnsureConfigReady(["audit"]);
|
||||
expect(auditRuntime.exit).not.toHaveBeenCalled();
|
||||
|
||||
const bareGatewayRuntime = await runEnsureConfigReady(["gateway"]);
|
||||
expect(bareGatewayRuntime.exit).not.toHaveBeenCalled();
|
||||
|
||||
|
||||
@@ -10,7 +10,14 @@ import { resolveRequiredHomeDir } from "../../infra/home-dir.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { shouldMigrateStateFromPath } from "../argv.js";
|
||||
|
||||
const ALLOWED_INVALID_COMMANDS = new Set(["doctor", "logs", "health", "help", "status"]);
|
||||
const ALLOWED_INVALID_COMMANDS = new Set([
|
||||
"audit",
|
||||
"doctor",
|
||||
"logs",
|
||||
"health",
|
||||
"help",
|
||||
"status",
|
||||
]);
|
||||
const ALLOWED_INVALID_GATEWAY_SUBCOMMANDS = new Set([
|
||||
"run",
|
||||
"status",
|
||||
@@ -304,7 +311,7 @@ export async function ensureConfigReady(params: {
|
||||
);
|
||||
params.runtime.error(
|
||||
muted(
|
||||
"Status, health, logs, tasks list/audit, and doctor commands still run with invalid config.",
|
||||
"Audit, status, health, logs, tasks list/audit, and doctor commands still run with invalid config.",
|
||||
),
|
||||
);
|
||||
if (!allowInvalid) {
|
||||
|
||||
@@ -98,6 +98,11 @@ const coreCliCommandCatalog = defineCommandDescriptorCatalog([
|
||||
description: "Fetch health from the running gateway",
|
||||
hasSubcommands: false,
|
||||
},
|
||||
{
|
||||
name: "audit",
|
||||
description: "Inspect metadata-only agent run and tool action records",
|
||||
hasSubcommands: false,
|
||||
},
|
||||
{
|
||||
name: "sessions",
|
||||
description: "List stored conversation sessions",
|
||||
|
||||
51
src/cli/program/register.audit.ts
Normal file
51
src/cli/program/register.audit.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
// Audit command registration for privacy-preserving run/tool history.
|
||||
import type { Command } from "commander";
|
||||
import { formatDocsLink } from "../../../packages/terminal-core/src/links.js";
|
||||
import { theme } from "../../../packages/terminal-core/src/theme.js";
|
||||
import { auditListCommand, type AuditListCommandOptions } from "../../commands/audit.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { runCommandWithRuntime } from "../cli-utils.js";
|
||||
|
||||
/** Register the bounded operator audit query command. */
|
||||
export function registerAuditCommand(program: Command): void {
|
||||
program
|
||||
.command("audit")
|
||||
.description("Inspect metadata-only agent run and tool action records")
|
||||
.option("--agent <id>", "Filter by agent id")
|
||||
.option("--session <key>", "Filter by exact session key")
|
||||
.option("--run <id>", "Filter by run id")
|
||||
.option("--kind <kind>", "Filter by kind (agent_run or tool_action)")
|
||||
.option(
|
||||
"--status <status>",
|
||||
"Filter by status (started, succeeded, failed, cancelled, timed_out, blocked, unknown)",
|
||||
)
|
||||
.option("--after <timestamp>", "Include records at/after ISO time or Unix milliseconds")
|
||||
.option("--before <timestamp>", "Include records at/before ISO time or Unix milliseconds")
|
||||
.option("--cursor <sequence>", "Continue from a previous result cursor")
|
||||
.option("--limit <count>", "Maximum records (1-500)", "100")
|
||||
.option("--json", "Output a bounded JSON page", false)
|
||||
.addHelpText(
|
||||
"after",
|
||||
() =>
|
||||
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/audit", "docs.openclaw.ai/cli/audit")}\n`,
|
||||
)
|
||||
.action(async (opts) => {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
await auditListCommand(
|
||||
{
|
||||
agentId: opts.agent as string | undefined,
|
||||
sessionKey: opts.session as string | undefined,
|
||||
runId: opts.run as string | undefined,
|
||||
kind: opts.kind as AuditListCommandOptions["kind"],
|
||||
status: opts.status as AuditListCommandOptions["status"],
|
||||
after: opts.after as string | undefined,
|
||||
before: opts.before as string | undefined,
|
||||
cursor: opts.cursor as string | undefined,
|
||||
limit: opts.limit as string | undefined,
|
||||
json: Boolean(opts.json),
|
||||
},
|
||||
defaultRuntime,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user