# How AI agent action review works without saving prompts

Review records for autonomous agents should explain an external action without becoming a second, poorly protected copy of the agent's entire working memory. That means recording who ran the agent, what authority allowed the run, what boundary the action crossed, and what happened. It does not mean preserving every prompt, tool message, scratchpad, and API payload forever.

Teams often start with conversation transcripts because they are easy to capture and feel complete. Then an incident arrives, and the transcript contains customer data, source code, pasted tokens, speculative instructions, and pages of material unrelated to the action under review. Meanwhile, the reviewer still cannot answer the basic question: which executable sent a request to which service, under whose authorization, using which credential scope, and with what result?

A review record should follow the action, not the conversation. That choice reduces exposure and produces evidence that an operator can actually use.

## An action record answers a different question than a transcript

An action record answers whether a particular process crossed a boundary and what the outside system did in response. A transcript answers what text passed through an agent context. Those are different artifacts with different access needs, retention periods, and failure modes.

Suppose an agent reads a large issue thread, inspects a repository, drafts a release note, and calls an API to create a deployment. A conversation export may contain thousands of lines. The meaningful review record can be much smaller:

- Process identity and session identifier.
- Authorization decision that covered the action.
- Destination, method, resource category, and credential reference.
- Outcome, including status and a safe result summary.
- Timestamps and integrity data.

That record lets an engineer reconstruct the operational event: a signed process started at a given time, received approval for a session, sent `POST` to a named API host and deployment endpoint, used the deployment credential, and received a success status with a deployment identifier. The reviewer can ask the owner of the destination service for deeper evidence if the event warrants it.

The transcript might explain *why* the agent thought it should deploy. It rarely proves that it did. Text in an agent context can be hypothetical, stale, fabricated by a model, or never acted on. The external action has a narrower and more defensible evidence trail.

Keep conversational material in the development environment only when the team has a clear reason to retain it, such as quality evaluation or a specific incident. Do not smuggle it into an audit system under the label of accountability.

## Full prompts create a data store nobody planned to protect

Full prompt capture turns an action log into a high-risk content archive. The risk is not theoretical or limited to obvious secrets.

Prompts often include fragments of source code, customer tickets, database output, internal URLs, design decisions, shell history, and copied error text. A model may repeat prior context in a tool argument or an error explanation. An API can echo a submitted header or request body back in an error response. If the logger treats all strings as harmless diagnostics, it will eventually retain information the team never intended to collect.

The common defense is, "We will redact secrets." That works only if the logging path sees every secret format before storage and understands every protocol. It will miss short-lived signed URLs, session cookies, proprietary token formats, secrets embedded in JSON strings, and credentials that a service returns inside an error. Redaction also cannot restore privacy after a broad group has read the original record.

OWASP's Logging Cheat Sheet makes a useful distinction: logs should support monitoring and investigation, but systems should not log access tokens, passwords, connection strings, encryption keys, or data whose collection creates unnecessary privacy exposure. Apply that advice to agent reviews with more discipline, not less. Agent context is unusually broad, so it carries more accidental material than a conventional request log.

Use data minimization at the point of capture. Store a request fingerprint or approved metadata, not the raw prompt that generated it. If an investigation later needs context, retrieve it from the originating system under the controls appropriate to that system. Do not make an audit journal the easiest place to browse every sensitive conversation.

There is one narrow exception: a team may need temporary, tightly limited capture while diagnosing a broken integration. Make that an explicit diagnostic mode with a defined owner, expiry, access restriction, and deletion date. If diagnostic capture becomes permanent because nobody turns it off, it was not diagnostic capture.

## Process identity must survive a friendly process name

A process name does not identify an agent. `agent`, `node`, `python`, and `shell` tell a reviewer almost nothing, and a malicious or careless program can choose any of those names.

Record enough identity data to distinguish the program that initiated the session:

- Executable path and a stable code identity, such as a signing authority where the operating system provides one.
- Process ID, parent process ID, launch time, and session identifier.
- User account and local host identifier.
- Agent client version or build identifier when the client provides it.
- The transport that reached the action gateway, such as a local stdio MCP connection.

The parent process matters. A signed editor launching an approved coding agent presents a different review story from an unknown shell process that launches a copied binary with the same command name. Parent identity does not prove good intent, but it gives investigators a starting point and makes simple impersonation easier to spot.

Do not confuse process identity with a human identity. A developer may start an agent, but the action record should say both facts separately: which local account started the process and which executable held the session. Shared workstations, remote shells, service accounts, and handoffs between tools make that separation necessary.

Code signing authority deserves special weight when available because an operating system can verify it at launch. It is still not a moral judgment about the publisher. A trusted signing authority can ship a flawed update, and an unsigned internal tool can be legitimate. The record needs the fact so a reviewer can compare it against the authorization decision and the expected deployment path.

## Authorization needs scope, time, and an approver

An approval without scope is a vague memory, not review evidence. The record must say what the person approved, when that approval began, and when it stopped applying.

Session authorization is often the right default for a developer-directed agent run. A person approves a known agent process once, then the process can perform actions until it exits. The review journal should bind that decision to the process session rather than pretending every later call received an independent human judgment.

For higher-risk credentials, capture a separate per-call decision. A record for that decision needs the approver identity, time, credential reference, action category, destination, and the exact call identifier it released. A generic note such as "user approved" leaves too much room for argument after a bad action.

A workable authorization object looks like this:

```json
{
  "authorization_id": "auth_7f3c",
  "kind": "session",
  "decision": "approved",
  "approved_at": "2025-03-08T14:22:31Z",
  "approver": "local-account:maya",
  "process_session": "sess_31a9",
  "process_identity": {
    "executable": "/Applications/Agent.app/Contents/agent",
    "signing_authority": "Example Development Team"
  },
  "scope": {
    "credential_refs": ["deploy-production"],
    "expires_when": "process exits"
  }
}
```

The names and identifiers above are examples, but the structure matters. The approver is a local account identity, not a claim that a named person watched every line of output. The credential reference is a label or internal ID, never the credential itself. The scope says whether the decision covered a session or one call.

Do not solve this with a sprawling policy language unless your operating environment truly requires one. Teams often build rules that nobody can read during an incident, then call the presence of rules a control. A small number of visible authorization choices can be easier to review and harder to misconfigure.

## Request metadata should describe the boundary crossed

A reviewer needs enough request metadata to understand the operation's scope without receiving a raw copy of the operation. For HTTP, capture the service host, port where relevant, method, normalized route template, credential reference, request size, correlation ID, and a digest of a canonical safe representation.

A normalized route template means recording `/v1/projects/{project_id}/deployments` instead of a literal URL that contains a customer identifier or an opaque secret-like value. The raw route can remain at the destination service, which already owns that data and has its own access controls.

For SSH, the equivalent record includes destination host, verified host identity, remote account, requested command or command classification, credential reference, and exit result. Avoid recording unrestricted command output. A command can print environment variables, private repository contents, or credentials from a misconfigured script.

HTTP Semantics, RFC 9110, distinguishes request method semantics such as safe, idempotent, and unsafe methods. Use that distinction as a review signal, not an authorization shortcut. A `GET` can disclose sensitive data. A `PUT` can be idempotent and still replace a production configuration. A `POST` may create an irreversible external effect. The method helps a reviewer reason about the action, but the destination and route decide the real risk.

Use an allowlisted metadata schema. Do not start with the entire request object and remove fields later. The safe direction is to define the fields an action record may contain, then reject or transform everything else.

```json
{
  "call_id": "call_c24e",
  "session_id": "sess_31a9",
  "channel": "https",
  "destination": "api.example.internal",
  "method": "POST",
  "route_template": "/v1/projects/{project_id}/deployments",
  "credential_ref": "deploy-production",
  "request_bytes": 842,
  "request_digest": "sha256:6d1d...",
  "started_at": "2025-03-08T14:24:09Z"
}
```

A digest detects a changed canonical record when a reviewer has the original approved representation. It does not make the original content safe to publish. Treat hashes of low-entropy secrets with care too, because an attacker can guess and compare them. Do not hash a short token and call it redaction.

## Results need operational evidence, not response dumps

A result record should state what the destination reported and whether the gateway completed the requested operation. It should not default to storing the complete response body.

For an HTTP action, record transport completion, HTTP status, response size, elapsed time, a service request ID when safe, and a deliberately selected outcome field. For example, a deployment API might return a deployment ID that is safe to retain, while its full JSON response contains environment variables and a commit message from a private repository.

For an SSH action, retain the exit code, duration, host identity result, and a summary selected by the command adapter. If a command needs a proof of successful work, have it emit a constrained machine-readable result such as `{"release":"r42","status":"published"}`. Do not accept an arbitrary terminal transcript as an audit result.

This distinction matters during failures. Imagine a deploy call returns HTTP 403 and includes a diagnostic object that echoes the caller's authorization header. The agent sees the error, retries twice, and each attempt produces a different log entry. A careless review system now contains three copies of an exposed credential, all indexed under an incident that more people will open.

Build the error path before the success path. The action gateway should classify an error, strip unsafe fields, and record a bounded summary. Useful categories include network failure, authorization denied, destination rejected request, destination timeout, and local execution failure. Pair the category with safe facts such as status code or exit code, not a free-form blob from a remote service.

Retries deserve their own fields. Record `attempt`, `max_attempts`, and a causal link to the original call. Otherwise, a reviewer sees three destructive requests and cannot tell whether the agent intentionally repeated them or a transport retry produced duplicates. For unsafe operations, a retry may require fresh authorization or an idempotency mechanism at the destination. Logging cannot repair an action that the destination applied twice.

## A review schema should make forbidden fields impossible to miss

A schema review catches logging mistakes before production records accumulate. Treat the schema as a security boundary, with explicit allowed fields and explicit rejection of free-form prompt and payload fields.

The following example joins identity, authorization, action metadata, result, and integrity information. It deliberately lacks `prompt`, `messages`, `headers`, `request_body`, `response_body`, and `stderr` fields.

```json
{
  "event_type": "external_action",
  "event_id": "evt_91bd",
  "occurred_at": "2025-03-08T14:24:10Z",
  "actor": {
    "local_account": "maya",
    "process_session": "sess_31a9",
    "pid": 4812,
    "parent_pid": 4601,
    "executable_digest": "sha256:2a84...",
    "signing_authority": "Example Development Team"
  },
  "authorization": {
    "authorization_id": "auth_7f3c",
    "mode": "session",
    "decision": "approved"
  },
  "action": {
    "channel": "https",
    "destination": "api.example.internal",
    "operation": "POST /v1/projects/{project_id}/deployments",
    "credential_ref": "deploy-production",
    "request_digest": "sha256:6d1d..."
  },
  "result": {
    "category": "completed",
    "status_code": 201,
    "destination_request_id": "req_18c7",
    "duration_ms": 614
  },
  "integrity": {
    "previous_event_digest": "sha256:8f50...",
    "event_digest": "sha256:bd7e..."
  }
}
```

Do not put comments such as "redacted" next to fields that might once have held secret content. Omit the field. A present-but-empty `request_body` invites a later developer to fill it during debugging. Schema validation should reject unknown top-level fields and reject nested blobs except where a reviewed adapter owns their format.

Reviewers also need a readable projection of the event. Produce that projection from the canonical record rather than maintaining a separate handwritten narrative. A human-facing entry might read: "Approved process session used deploy-production to create a deployment at api.example.internal. The service returned 201 in 614 ms." The journal keeps the identifiers needed to inspect the event without exposing material in the default view.

## Integrity proves alteration, not completeness

Tamper-evident records help only if the team understands their limit. A hash chain can reveal that someone changed, deleted, or reordered an event after it entered the chain, assuming reviewers retain the chain material needed for verification. It cannot prove that a compromised logger recorded an action in the first place.

NIST SP 800-92, Guide to Computer Security Log Management, advises organizations to protect log integrity, define what events merit logging, and review logs with clear operational ownership. The useful part is the combination. Integrity without a defined event boundary gives you trustworthy records of an incomplete story. A long event list without integrity gives you a story that somebody can quietly edit.

Use the action gateway as the observation point because it sees the credential use and external call. If an agent can bypass that gateway and make direct calls with copied credentials, the audit trail covers only the cooperative path. Fix the credential distribution problem rather than claiming the journal sees everything.

Keep verification independent from normal log reading. A verification command should work against stored encrypted records and report whether the chain holds. Sallyport exposes this check through `sp audit verify`, which verifies its encrypted hash-chained audit log without requiring the vault key. That design matters because an investigator should be able to test record continuity without gaining access to credentials.

Verification results need operational handling. If a chain check fails, preserve the affected storage, stop treating the journal as complete evidence, identify the first broken sequence point, and compare destination-side records for the interval. Do not simply regenerate a clean chain and move on. That converts a detectable integrity failure into an unanswerable gap.

## Access and retention decide whether the journal becomes another breach

A minimal record can still cause harm if too many people can search it forever. Authorization history may reveal employee activity. Destination names can reveal infrastructure. Project identifiers can expose commercial work. Limit access according to the investigation role, not general curiosity.

Separate operational views from forensic views. Most engineers need a recent list of actions, statuses, destinations, and session identities to diagnose a failed run. A smaller group may need event digests, signing information, approval details, and raw encrypted record access during an incident. The people who operate an agent do not automatically need permanent access to every other developer's history.

Set retention by answering two questions: how long can the team realistically investigate a disputed action, and how long does the destination retain its own authoritative record? If the destination keeps deployment history for a short period, retain action metadata long enough to correlate with it. If a legal or contractual requirement changes that period, document the requirement and its owner. "Keep everything" is usually an absence of a decision.

Deletion needs evidence too. Record the retention policy version and the fact that a scheduled deletion or aggregation occurred. Do not retain the deleted content merely to prove deletion. For long-term trend analysis, aggregate counts by operation category and outcome after the detailed records expire.

## Build the record at the gateway, then test the bad paths

The safest implementation captures review data where the credential-bearing action occurs. An agent should request an action through a constrained interface, the gateway should authenticate the local process and apply authorization, and the gateway should execute the HTTP or SSH operation. The agent receives the result, while the review record captures the action facts.

Sallyport follows this shape for MCP-capable agents: the local `sp mcp` shim routes HTTP and SSH actions through the app, whose encrypted vault keeps API and SSH credentials out of the agent context. Its session journal and activity journal project from the same write-blind encrypted audit log, so session authorization and individual calls remain connected without making the agent hold a secret.

Test the design with failures that ordinary happy-path demonstrations avoid:

1. Send a request that fails after the destination echoes a fake authorization header. Confirm the record stores an error category and status, not the header or response body.
2. Start two processes with the same display name but different executable identities. Confirm the review view separates their sessions.
3. Approve one session, terminate it, then start a new process. Confirm the old approval does not apply to the new run.
4. Force a timeout and retry. Confirm the records connect attempts to one call and preserve the fact that the outcome is uncertain.
5. Alter a stored test event and run integrity verification. Confirm the verifier reports failure and that the team has a written response procedure.

Do this before adding dashboards, summaries, or model-generated explanations. A polished activity feed cannot compensate for an event record that leaks a token or cannot distinguish one executable from another.

Review records earn trust when they are narrow enough to protect, specific enough to investigate, and anchored at the place where an outside action actually occurs. If a record cannot tell you who acted, what authority covered it, what boundary it crossed, and what result came back, it needs better fields. If it contains the full conversation, it contains too much.
