Skip to content

Commit f533469

Browse files
authored
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
1 parent 96e676c commit f533469

146 files changed

Lines changed: 11593 additions & 765 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3077,6 +3077,144 @@ public struct SessionsUsageParams: Codable, Sendable {
30773077
}
30783078
}
30793079

3080+
public struct AuditEvent: Codable, Sendable {
3081+
public let eventid: String
3082+
public let sequence: Int
3083+
public let sourcesequence: Int
3084+
public let occurredat: Int
3085+
public let kind: AnyCodable
3086+
public let action: AnyCodable
3087+
public let status: AnyCodable
3088+
public let errorcode: AnyCodable?
3089+
public let actor: [String: AnyCodable]
3090+
public let agentid: String
3091+
public let sessionkey: String?
3092+
public let sessionid: String?
3093+
public let runid: String
3094+
public let toolcallid: String?
3095+
public let toolname: String?
3096+
public let redaction: String
3097+
3098+
public init(
3099+
eventid: String,
3100+
sequence: Int,
3101+
sourcesequence: Int,
3102+
occurredat: Int,
3103+
kind: AnyCodable,
3104+
action: AnyCodable,
3105+
status: AnyCodable,
3106+
errorcode: AnyCodable?,
3107+
actor: [String: AnyCodable],
3108+
agentid: String,
3109+
sessionkey: String?,
3110+
sessionid: String?,
3111+
runid: String,
3112+
toolcallid: String?,
3113+
toolname: String?,
3114+
redaction: String)
3115+
{
3116+
self.eventid = eventid
3117+
self.sequence = sequence
3118+
self.sourcesequence = sourcesequence
3119+
self.occurredat = occurredat
3120+
self.kind = kind
3121+
self.action = action
3122+
self.status = status
3123+
self.errorcode = errorcode
3124+
self.actor = actor
3125+
self.agentid = agentid
3126+
self.sessionkey = sessionkey
3127+
self.sessionid = sessionid
3128+
self.runid = runid
3129+
self.toolcallid = toolcallid
3130+
self.toolname = toolname
3131+
self.redaction = redaction
3132+
}
3133+
3134+
private enum CodingKeys: String, CodingKey {
3135+
case eventid = "eventId"
3136+
case sequence
3137+
case sourcesequence = "sourceSequence"
3138+
case occurredat = "occurredAt"
3139+
case kind
3140+
case action
3141+
case status
3142+
case errorcode = "errorCode"
3143+
case actor
3144+
case agentid = "agentId"
3145+
case sessionkey = "sessionKey"
3146+
case sessionid = "sessionId"
3147+
case runid = "runId"
3148+
case toolcallid = "toolCallId"
3149+
case toolname = "toolName"
3150+
case redaction
3151+
}
3152+
}
3153+
3154+
public struct AuditListParams: Codable, Sendable {
3155+
public let agentid: String?
3156+
public let sessionkey: String?
3157+
public let runid: String?
3158+
public let kind: AnyCodable?
3159+
public let status: AnyCodable?
3160+
public let after: Int?
3161+
public let before: Int?
3162+
public let limit: Int?
3163+
public let cursor: String?
3164+
3165+
public init(
3166+
agentid: String? = nil,
3167+
sessionkey: String?,
3168+
runid: String?,
3169+
kind: AnyCodable?,
3170+
status: AnyCodable?,
3171+
after: Int?,
3172+
before: Int?,
3173+
limit: Int?,
3174+
cursor: String?)
3175+
{
3176+
self.agentid = agentid
3177+
self.sessionkey = sessionkey
3178+
self.runid = runid
3179+
self.kind = kind
3180+
self.status = status
3181+
self.after = after
3182+
self.before = before
3183+
self.limit = limit
3184+
self.cursor = cursor
3185+
}
3186+
3187+
private enum CodingKeys: String, CodingKey {
3188+
case agentid = "agentId"
3189+
case sessionkey = "sessionKey"
3190+
case runid = "runId"
3191+
case kind
3192+
case status
3193+
case after
3194+
case before
3195+
case limit
3196+
case cursor
3197+
}
3198+
}
3199+
3200+
public struct AuditListResult: Codable, Sendable {
3201+
public let events: [AuditEvent]
3202+
public let nextcursor: String?
3203+
3204+
public init(
3205+
events: [AuditEvent],
3206+
nextcursor: String?)
3207+
{
3208+
self.events = events
3209+
self.nextcursor = nextcursor
3210+
}
3211+
3212+
private enum CodingKeys: String, CodingKey {
3213+
case events
3214+
case nextcursor = "nextCursor"
3215+
}
3216+
}
3217+
30803218
public struct TaskSummary: Codable, Sendable {
30813219
public let id: String
30823220
public let kind: String?

config/knip.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const rootEntries = [
1313
"src/entry.ts!",
1414
"src/cli/daemon-cli.ts!",
1515
"src/agents/code-mode.worker.ts!",
16+
"src/audit/audit-event-writer.worker.ts!",
1617
"src/agents/model-provider-auth.worker.ts!",
1718
"src/infra/kysely-node-sqlite.ts!",
1819
"src/infra/warning-filter.ts!",
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
d144128f5cf945ddbe411ae5d87a65e3dbb29b8deb04f6bb69dba868486f5b70 config-baseline.json
2-
5e06bdcca5f552ddbd496f6f413c090816a4bef47ea6610205d143653060c5ef config-baseline.core.json
1+
e7110e0263c74c772c8fc9075e3dc8c6e857ca84fa237ce9e182037e86f24dfa config-baseline.json
2+
66bb892f3350a6cbe0bdac55e36d8960dba621266c1d21903d005862ef8a06b4 config-baseline.core.json
33
cb7503a2c1e489fa4d154dfa4bf4a9e2f1277233ab65fac71c1ba8dcb371a032 config-baseline.channel.json
44
77a337c9f4f2e43d18da97427905c573abd8857014033d359bfb36b2f15c2f20 config-baseline.plugin.json
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
0dcad4f6899080c7858b4bdd709e8c3443b85659bb03525b49967abb262ecd8b plugin-sdk-api-baseline.json
2-
a67e0ed2b47d3fdb7e450837b6edb37e0796c3451bc2097be5d828a63b8e73d4 plugin-sdk-api-baseline.jsonl
1+
57289c57bab5188013ab1895d26acabada49aff864b3207f1a3ea3e1d92c7791 plugin-sdk-api-baseline.json
2+
445e5e72a321486831c2c936118302dbc2cde793f5e1dc5beab29100cae8150e plugin-sdk-api-baseline.jsonl

docs/.i18n/glossary.zh-CN.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,10 @@
12391239
"source": "Agent config reference",
12401240
"target": "Agent 配置参考"
12411241
},
1242+
{
1243+
"source": "Audit records",
1244+
"target": "审计记录"
1245+
},
12421246
{
12431247
"source": "Background process",
12441248
"target": "后台进程"

docs/cli/audit.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
summary: "CLI reference for metadata-only agent run and tool action audit records"
3+
read_when:
4+
- You need to answer who ran an agent or tool, when it ran, and how it ended
5+
- You need a bounded, redaction-safe activity export
6+
title: "Audit records"
7+
---
8+
9+
# `openclaw audit`
10+
11+
Query the Gateway's metadata-only audit ledger for agent runs and tool actions.
12+
13+
Recording is on by default; set [`audit.enabled: false`](/gateway/configuration-reference#audit)
14+
to stop new writes. Existing records stay queryable until they expire (30 days).
15+
The ledger is separate from conversation transcripts: it records identity,
16+
ordering, provenance, action, status, and normalized error codes, but never
17+
stores prompts, messages, tool arguments, tool results, command output, or raw
18+
error text.
19+
20+
The Gateway writes records to the shared OpenClaw state database through a
21+
bounded background writer. Queries never return records older than 30 days,
22+
and the ledger is capped at 100,000 rows. Expired rows are deleted during
23+
Gateway startup, hourly maintenance, and later writes.
24+
25+
```bash
26+
openclaw audit
27+
openclaw audit --agent main --status failed
28+
openclaw audit --session "agent:main:main" --after 2026-07-01T00:00:00Z
29+
openclaw audit --run 8c69f72e-8b11-4c54-98d5-1a3dd67450c3
30+
openclaw audit --kind tool_action --limit 50 --json
31+
```
32+
33+
## Filters
34+
35+
- `--agent <id>`: exact agent id
36+
- `--session <key>`: exact session key
37+
- `--run <id>`: exact run id
38+
- `--kind <kind>`: `agent_run` or `tool_action`
39+
- `--status <status>`: `started`, `succeeded`, `failed`, `cancelled`,
40+
`timed_out`, `blocked`, or `unknown`
41+
- `--after <timestamp>` / `--before <timestamp>`: inclusive ISO timestamp or
42+
Unix milliseconds
43+
- `--limit <count>`: page size from 1 to 500; default `100`
44+
- `--cursor <sequence>`: continue a previous newest-first query
45+
- `--json`: print the bounded page as JSON
46+
47+
Text output shows time, kind, status, agent, run, and action. Tool actions also
48+
show the tool name. JSON output is a safe bounded export of the same metadata
49+
and includes `nextCursor` when another page exists. Pass that value to
50+
`--cursor` to continue without reordering records that arrive during paging.
51+
52+
## Recorded events
53+
54+
The Gateway projects existing agent event streams into four actions:
55+
56+
- `agent.run.started`
57+
- `agent.run.finished`
58+
- `tool.action.started`
59+
- `tool.action.finished`
60+
61+
Every record has a stable event id, a monotonically increasing ledger sequence,
62+
the original run event sequence, lifecycle timestamp when the runtime provides
63+
one (otherwise observation time), agent/run provenance, actor, and a
64+
`redaction: "metadata_only"` marker. Terminal records distinguish success,
65+
failure, cancellation, timeout, and policy blocks with closed status and error
66+
codes. `unknown` is an explicit non-success result when an upstream runtime
67+
does not expose an authoritative terminal outcome. Tool call ids are exported
68+
only as stable one-way fingerprints. Tool names must match the compact
69+
model-facing name contract; other values become `unknown`. Session ids, session
70+
keys, run ids, and retained tool names are operator metadata; protect exports
71+
as operational records.
72+
73+
The audit ledger does not replace transcripts, task history, cron run history,
74+
or logs. It provides a small cross-run index for operator questions without
75+
copying conversation content into another store.
76+
77+
## Gateway RPC
78+
79+
`audit.list` requires `operator.read` and accepts the same filters. Example:
80+
81+
```bash
82+
openclaw gateway call audit.list --params '{"agentId":"main","status":"failed","limit":50}'
83+
```
84+
85+
The result is `{ "events": AuditEvent[], "nextCursor"?: string }`. Results are
86+
newest first and limited to 500 records per request.
87+
88+
## Related
89+
90+
- [Gateway protocol](/gateway/protocol#audit-ledger-rpc)
91+
- [Sessions](/cli/sessions)
92+
- [Tasks](/cli/tasks)
93+
- [Cron jobs](/automation/cron-jobs)

docs/cli/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Setup commands by intent:
2424
| Setup and onboarding | [`crestodian`](/cli/crestodian) · [`setup`](/cli/setup) · [`onboard`](/cli/onboard) · [`configure`](/cli/configure) · [`config`](/cli/config) · [`completion`](/cli/completion) · [`doctor`](/cli/doctor) · [`dashboard`](/cli/dashboard) |
2525
| Reset, backup, and migration | [`backup`](/cli/backup) · [`migrate`](/cli/migrate) · [`reset`](/cli/reset) · [`uninstall`](/cli/uninstall) · [`update`](/cli/update) |
2626
| Messaging and agents | [`message`](/cli/message) · [`agent`](/cli/agent) · [`agents`](/cli/agents) · [`attach`](/cli/attach) · [`acp`](/cli/acp) · [`mcp`](/cli/mcp) |
27-
| Health and sessions | [`status`](/cli/status) · [`health`](/cli/health) · [`sessions`](/cli/sessions) |
27+
| Health and sessions | [`status`](/cli/status) · [`health`](/cli/health) · [`sessions`](/cli/sessions) · [`audit`](/cli/audit) |
2828
| Gateway and logs | [`gateway`](/cli/gateway) · [`logs`](/cli/logs) · [`system`](/cli/system) |
2929
| Models and inference | [`models`](/cli/models) · [`infer`](/cli/infer) · `capability` (alias for [`infer`](/cli/infer)) · [`memory`](/cli/memory) · [`commitments`](/cli/commitments) · [`wiki`](/cli/wiki) |
3030
| Network and nodes | [`directory`](/cli/directory) · [`nodes`](/cli/nodes) · [`devices`](/cli/devices) · [`node`](/cli/node) |
@@ -238,6 +238,7 @@ openclaw [--dev] [--profile <name>] <command>
238238
health
239239
sessions
240240
cleanup
241+
audit
241242
tasks
242243
list
243244
audit

docs/concepts/agent-loop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ Auto-compaction emits `compaction` stream events and can trigger a retry. On ret
116116
- `assistant`: streamed deltas from the agent runtime.
117117
- `tool`: streamed tool events from the agent runtime.
118118

119+
The Gateway projects lifecycle and tool start/terminal events into the bounded,
120+
metadata-only [audit ledger](/cli/audit). This projection records provenance and
121+
result codes without copying prompts, messages, tool arguments, tool results,
122+
or raw errors out of the transcript/runtime path.
123+
119124
## Chat channel handling
120125

121126
Assistant deltas buffer into chat `delta` messages. A chat `final` is emitted on **lifecycle end/error**.

docs/docs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,6 +1745,7 @@
17451745
"pages": [
17461746
"cli/agent",
17471747
"cli/agents",
1748+
"cli/audit",
17481749
"cli/hooks",
17491750
"cli/infer",
17501751
"cli/memory",

docs/docs_map.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,16 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
12411241
- Route: /cli/attach
12421242
- Headings: none
12431243

1244+
## cli/audit.md
1245+
1246+
- Route: /cli/audit
1247+
- Headings:
1248+
- H1: openclaw audit
1249+
- H2: Filters
1250+
- H2: Recorded events
1251+
- H2: Gateway RPC
1252+
- H2: Related
1253+
12441254
## cli/backup.md
12451255

12461256
- Route: /cli/backup
@@ -3223,6 +3233,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
32233233
- H3: Secret providers config
32243234
- H2: Auth storage
32253235
- H3: auth.cooldowns
3236+
- H2: Audit
32263237
- H2: Logging
32273238
- H2: Diagnostics
32283239
- H2: Update
@@ -3587,6 +3598,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
35873598
- H2: RPC method families
35883599
- H3: Common event families
35893600
- H3: Node helper methods
3601+
- H2: Audit ledger RPC
35903602
- H2: Task ledger RPCs
35913603
- H2: Operator helper methods
35923604
- H3: models.list views

0 commit comments

Comments
 (0)