# Who changed this file?

Version history keeps the account owner visible and records how each new version reached Stuff:

- **Manual · Stuff web**: saved or uploaded through a signed-in browser session. This describes the action in Stuff, not proof that the original content was written without AI.
- **AI agent**: the client declares `kind: "agent"`, or supplies a model. The client name, model and provider appear when supplied.
- **Automation**: an API/MCP connection without an AI declaration, or an explicit `kind: "automation"`. Useful for importers, scripts and CLI use.
- **Connected app / Source not recorded**: older versions without the new fields. Stored connection IDs can identify the connection; historical agent/model details cannot be recovered reliably.

Stuff records the authenticated user, connection, channel (`browser`, `api`, `mcp`) and timestamp. Client names, agent/automation declarations and model IDs are **client-reported**, not verified by the model vendor. Never guess a model from a connection name, OAuth registration, user agent or API key. Omit unavailable details. A model value such as `openai-gpt-6-astra` is accepted as an opaque identifier; the examples below are illustrative.

## MCP

All mutation tools accept an optional `attribution` object. Send it with each write, including creates, replacements, targeted edits, batches, copies and restores:

```json
{
  "kind": "agent",
  "client": "Codex",
  "model": "openai-gpt-6-astra",
  "provider": "OpenAI"
}
```

For example, include that object as the `attribution` property alongside `reference`, `version`, `edits` and `idempotency_key` in `fs_edit`. A single declaration applies to every file written by `fs_batch`. Tool arguments override request-header declarations field by field. OAuth client names are a fallback when a write does not report its client; these names are also self-declared. The MCP initialization name does not reliably accompany stateless tool requests, so supply `attribution.client` explicitly.

## REST

Optional headers work across text saves, byte replacement, original uploads, restores and upload recovery:

```text
X-Stuff-Actor-Type: agent
X-Stuff-Client: Codex
X-Stuff-Model: openai-gpt-6-astra
X-Stuff-Model-Provider: OpenAI
```

`X-Stuff-Actor-Type` accepts `agent` or `automation`. `/api/v1/fs/edit` and `/api/v1/fs/batch` also accept the same JSON `attribution` property as MCP. For resumed uploads, supply headers on the **complete** request: attribution records the request that committed the original, not the earlier staging requests. All fields are optional; existing clients keep working. Limits: client 120 characters, model 160, provider 80; control characters and unknown JSON fields are rejected.

The CLI encodes non-ASCII header values as `utf-8''` followed by percent-encoded UTF-8; JSON tool arguments support Unicode directly.

`GET /api/v1/items/{id}/history` and MCP `file_history` return `attribution` for each version:

```json
{
  "channel": "mcp",
  "kind": "agent",
  "connection": { "id": "connection-id", "name": "My assistant" },
  "client": "Codex",
  "model": "openai-gpt-6-astra",
  "provider": "OpenAI",
  "clientReported": true
}
```

`actor` remains the account's display name. New versions snapshot connection names; a rename/revocation never changes those snapshots. Legacy connection names are looked up from their retained connection ID and may reflect its current name (`legacy: true`). No secret, prompt or full request headers are stored. Attribution never affects authentication, permissions, scope, content hashes or quota.

## CLI and local MCP

Set these environment variables in the agent's CLI/MCP process configuration:

```text
STUFF_ACTOR_TYPE=agent
STUFF_CLIENT=Codex
STUFF_MODEL=openai-gpt-6-astra
STUFF_MODEL_PROVIDER=OpenAI
```

The 1P wiki importer and Daily AI News publisher accept the same environment variables. Without overrides, their client names identify the publishing script.

Only set the model/provider when known. Plain CLI runs default to `Stuff CLI` and automation. Local MCP tools also accept per-call `attribution`, overriding environment defaults without leaking across concurrent calls. Folder push and upload recovery use the same request headers automatically.

## Storage and compatibility

Migration `012_edit_attribution.sql` adds bounded application-validated JSON snapshots to immutable versions and activity events. Text and binary storage are unchanged. Restores and copies record the person/client performing that action, not the author of the source version. Content-identical saves and idempotent retries do not create or rewrite provenance. Browser requests cannot use client headers to forge an agent connection or model. Historical versions are not backfilled with guesses.

No new service, subscription, worker job or live feed is needed. Historical model inference, cryptographic vendor attestation and detecting AI text pasted into the browser are outside this feature.

## Remembered client details

Connections → Edit details can save an assistant/app name and an AI-agent or automation default. These are fallback labels for future writes only; explicit request attribution wins, and old snapshots remain unchanged. OAuth client names remain a fallback too. There is no saved model default: absent a per-edit model, history says it was not reported. Plain CLI calls report Stuff CLI unless STUFF_CLIENT is set. [Profile API and access checks](https://stuff.ac/connect/workflows.md).
