Files
PaperClipAI/doc/agent-commentary.md
T
DottaandPaperclip e6dcd1e8e9 feat: add Commentary to Audit with filters and shareable messages (#16884)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents can submit suggestions and complaints while they work.
> - Operators currently need database access to read those messages.
> - That makes it hard to find feedback from one agent or share a
specific message.
> - This pull request adds a standard Commentary tab in Audit.
> - Operators can filter messages, browse 50 at a time, and share links
to full message dialogs.

## Linked Issues or Issue Description

Refs #15367, which added feedback storage and agent submission. The
duplicate search found no open PR for a commentary reader.

**Subsystem affected**

Server, shared API contracts, and the board UI.

**Problem or motivation**

Agent suggestions and complaints are stored but have no board reader.
Operators must query the database to inspect feedback.

**Proposed solution**

Add Commentary to Audit without an experimental toggle. Show previews,
agent and message-type filters, text search, and 50-message pages.
Opening a row updates the URL. A copied URL opens the full message with
its agent, task, and run context.

**Alternatives considered**

Direct database queries remain useful for operators, but do not provide
a normal product workflow. An experimental setting would add setup to a
read-only audit page.

**Roadmap alignment**

This completes the operator view for the existing feedback feature. It
supports human oversight and does not change agent execution or task
ownership.

## What Changed

- Add company-scoped, board-only list and detail endpoints with audit
permission and source-task privacy checks.
- Return bounded previews and counts. Fetch full bodies only when a
message opens.
- Add the Commentary tab, search, filters, 50-row pagination, and
URL-based selection. Audit tab destinations work in both layouts, with a
classic sidebar entry for Commentary.
- Add production Storybook examples for populated, filtered, linked,
long, paginated, empty, loading, failed, denied, mobile, and light
states.
- Update API documentation and the feature and agent access maps.

## Verification

- Final head `abc4a251c14cfbfec007542214beec528f2b18bf`: 55 checks pass;
two conditional Storybook checks are skipped. The PR workflow is
successful. Greptile is 5/5 with all findings resolved.
- Focused database, HTTP, submission, OpenAPI, and UI regression tests
cover company isolation, task privacy, filter bounds, 50-row pagination,
and copied message links.
- Browser Storybook journeys cover row opening, direct links, combined
filters, search, and pagination. Storybook uses synthetic API fixtures;
database authorization is tested separately.
- Token gates, feature maps, and Storybook catalog validation pass.
- `pnpm -r typecheck`, `pnpm build`, and `pnpm build-storybook` pass on
the rebased branch. All 80 focused tests and all 37 Audit tests pass,
including the real App route table in both layouts, task
grant/revocation through HTTP, deleted-task privacy, and URL UI
regressions. The full suite passes in the parallel PR CI matrix. The
local serial full-suite run was stopped before completion after checking
the recorded 94-minute serial duration. Local focused tests, workspace
typecheck, production build, and final server/UI checks pass.
- The linked-message Storybook test initially asserted against the
loading dialog. It now waits for the message body; both direct-link and
row-open journeys pass.
- Native and legacy deleted-task privacy regressions failed before the
fix and pass afterward. The reader checks the run task binding after the
task foreign key clears.
- A rapid search-then-agent-filter browser regression reproduced the
search overwriting the selected agent. The pending search now restarts
when URL filters change. The same browser test passes with both filters
applied.
- CI found two existing AuditHub assertions that still expected five
tabs. They now assert the six-tab model and selected-company Commentary
rendering. All 37 Audit tests pass. All other test and build jobs passed
before this test-only correction.

- The [agent-chat attachment browser
test](https://github.com/paperclipai/paperclip/actions/runs/39307881171/job/118250189664)
timed out waiting for an attachment preview. The [same shard passed on
the prior
head](https://github.com/paperclipai/paperclip/actions/runs/39306658703/job/118245498343)
with identical application code; the intervening change only updates
AuditHub unit tests. The single retry passed on the same head. The
precise cause of the earlier failure remains unconfirmed.

## Risks

- Commentary may contain sensitive task context. The reader requires
board access and audit permission, and filters source-task visibility
before returning previews, counts, or full bodies.
- Text search scans stored message bodies. Pages are bounded and full
bodies are fetched on demand.
- Offset pagination can shift if new messages arrive while paging.
Ordering uses timestamp and ID for stable ties.
- No database migration or agent submission behavior changes are
required.

## Model Used

- OpenAI Codex, GPT-6 family, with reasoning, tool use, browser
interaction, and code execution. The exact backend model ID and
context-window size are not exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-19 14:21:13 -05:00

9.3 KiB
Raw Blame History

Agent complaints and suggestions

Agents submit incidental feedback into the current instance's agent_commentary table. Complaints preserve a raw reaction; suggestions describe an improvement. Both are free-form, internally attributed records, not anonymous feedback.

Agent entry points

Skill-capable legacy adapters automatically receive complain and suggestion-box alongside the paperclip operational skill, including existing agents with empty optional-skill selections. The shared helper lives in that operational skill at scripts/submit-agent-commentary.mjs. It reads the body from stdin and uses the existing PAPERCLIP_API_URL, PAPERCLIP_API_KEY, PAPERCLIP_COMPANY_ID, and PAPERCLIP_RUN_ID. The helper uses the existing Node.js runtime with no additional dependencies; custom runtimes can call HTTP directly. Sandboxed runs use the callback bridge.

Native runs receive submit_complaint({body, idempotencyKey}) and submit_suggestion({body, idempotencyKey}) in standard, ask, and planning modes. Specialized completion-review runs retain their restricted tool set. Native submission uses the bound authority without a shell or API credential. Submit before finishing the run.

Instructions closely adapt Warp's complain and suggestion-box skills, with their MIT notices included. Paperclip changes the transport, attribution, suggestion form, and size limit. Agents submit proactively when warranted, without routine previews or announcements, then continue working. Answer truthfully if a user asks about feedback or submitted content. Operators can exclude these legacy skills through the existing runtime skill policy. Brevity and at most three suggestions per run are guidance, not server quotas. Do not report the same incident through both paths or retry failed submissions.

HTTP contract

POST /api/companies/:companyId/agent-commentary accepts an authenticated agent and its active run (Authorization: Bearer …, X-Paperclip-Run-Id: …):

{"kind":"suggestion","body":"The tool returned before persistence completed. Wait for the write before returning success.","idempotencyKey":"write-completion-1"}

Only these three fields are accepted. Identity and the optional task reference are server-derived. Board callers cannot impersonate agent feedback. Native runs use their dedicated tools rather than this legacy HTTP path.

Bodies must contain non-whitespace text and may contain up to 524,288 JavaScript string code units, matching ordinary issue documents. No truncation is applied. Existing 10 MiB HTTP and callback bridge request limits remain. Keys are nonempty and at most 240 characters.

Creation returns HTTP 201 with {id, kind, createdAt, replayed:false}. Identical replay in the same company/run returns HTTP 200 with the same ID and timestamp and replayed:true. Conflicting key reuse returns 409. Invalid input returns 400; invalid authority returns 401/403; storage failure returns a sanitized 503. Authorization precedes replay. The helper accepts an optional second argument for a stable key, generating one otherwise. It makes one request with a 10-second HTTP deadline and exits zero on failure with a content-free diagnostic. It never follows redirects with credentials or claims an uncertain write succeeded.

Storage and inspection

Rows contain company, agent, run, nullable issue, kind, body, retry key, payload hash, and timestamp. Known run secrets and credential syntax are redacted before storage; instructions remain essential because redaction cannot guarantee secrecy. Writes and one content-free activity record commit together. Feedback bodies are excluded from HTTP diagnostics and dedicated mutation receipts.

Browse feedback at Audit → Commentary (/activity/commentary). The page has text search, agent/type filters, 50-message pages and a full-message dialog with task/run context. Filters, page offsets and selected messages have shareable URLs (?entry=<message-id> opens a message directly).

The board-only read API is GET /api/companies/:companyId/agent-commentary (agentId, kind, q, limit up to 100, offset) and GET /api/companies/:companyId/agent-commentary/:id. Both require company access and audit:view_agent_actions. Source-task privacy filters previews, counts and details. List results contain at most 280 characters per message; the full body is loaded only on opening a message. Feedback whose source task was deleted stays hidden, using the task binding retained on its run.

There is no notification, automatic task creation, or external forwarding. This is separate from first-party Telemetry, OpenTelemetry Observability, and the run log. Ordinary provider transcripts can still contain submitted tool arguments; there is no anonymity or ephemeral-storage promise.

Authorized operators inspect the instance database, for example:

SELECT id, kind, body, agent_id, run_id, issue_id, created_at
FROM agent_commentary
WHERE company_id = '<company UUID>'
ORDER BY created_at DESC;

The service enforces company/run ownership. Foreign keys clear the task reference on task deletion and cascade deletion with its run, agent, or company. Normal logical database backups include these rows. No retention scheduler or backfill.

Verification

Focused coverage lives in server/src/__tests__/agent-commentary.integration.test.ts, agent-commentary-helper.test.ts, agent-commentary-skills.test.ts, the shared validator tests, and the HTTP logger and sandbox callback bridge suites. These exercise real PostgreSQL and routes, atomic audit rollback, replay races, authority changes, deletion, default mounts, Unicode, and the exact document-body boundary.

The opt-in live smoke uses existing Codex login credentials, copied to a private temporary home, and a disposable instance database. It calls the real legacy adapter and production native-session executor. It incurs normal provider usage:

node cli/node_modules/tsx/dist/cli.mjs server/scripts/verify-agent-commentary-live.ts

To run the same checks on real Daytona sandboxes, install the standalone provider's dependencies, provide its API key and an immutable image reference, and specify a Linux amd64 runner built from this checkout:

pnpm --dir packages/plugins/sandbox-providers/daytona install --frozen-lockfile
PAPERCLIP_LIVE_ENVIRONMENT=daytona \
PAPERCLIP_LIVE_DAYTONA_ENV_FILE=/path/to/private.env \
PAPERCLIP_LIVE_LINUX_RUNNER=/path/to/linux-amd64/paperclip-runnerd \
PAPERCLIP_LIVE_EVIDENCE_PATH=/tmp/commentary-evidence.json \
node cli/node_modules/tsx/dist/cli.mjs server/scripts/verify-agent-commentary-live.ts

The private env file needs only DAYTONA_API_KEY and PAPERCLIP_E2E_DAYTONA_IMAGE (...@sha256:...). Alternatively, export those variables directly. A positional legacy or native argument selects one path. For a current Linux runner, add an export stage to a temporary copy of the root Dockerfile and build it with --platform linux/amd64 --target commentary-runner-export --output type=local,dest=/tmp/commentary-runner-linux:

FROM scratch AS commentary-runner-export
COPY --from=runner-build /app/packages/paperclip-runner/runner/target/release/paperclip-runnerd /paperclip-runnerd

The smoke calls the production Daytona provider hooks for creation, execution, file transfer, private ingress, and confirmed deletion. Legacy feedback crosses the sandbox queue callback bridge. Native feedback crosses the runner's private WebSocket and live tool authority. The current Linux runner is staged and verified independently of the base image. Fixture setup creates authority; the real agent submits all feedback. This is a focused feedback transport smoke, not a full Runner E2E catalog qualification or browser onboarding test.

Both paths passed locally and on Daytona on 2026-10-06. Each persisted one complaint and one suggestion with company, agent, run, and task attribution; each then wrote the continuation marker. Both exited zero, left task status unchanged, created no task comments, and recorded two content-free activity entries. The script emits the attributed row IDs and timestamps as evidence, optionally saves a JSON report, and deletes its temporary data, credentials, and remote sandboxes. Remote reports are emitted only after confirmed sandbox deletion.

The Daytona verification used base image ghcr.io/paperclipai/paperclip-daytona-runner@sha256:b81a86d5242088f9d832666a411f09da7438d92e99f9962ccf88ebe439cd3b32 and the Linux runner built from abf47b5953d14aa7a8dbf2941460b5df8a4d66c3 (binary SHA-256 c794141152ae2e2986da0df14b01a54e4448949e06ec4031c83bad8a9fff8bac).

Environment Runner Complaint row Suggestion row Submitted at (UTC)
Local Legacy Codex 59413a00-1de2-4bb1-bcc6-9c4b54c64aa6 3db2364d-3e15-4f47-846f-875d3902999d 20:35:10
Local Native Codex 5da22b5f-41df-4de5-8ba0-d9345ab01267 2d5abe17-dd41-403c-a5ee-4729f2d58921 20:35:26–20:35:27
Daytona Legacy Codex 27c9d0aa-8477-409f-9da0-e8ffa48dee50 209681c9-d1e9-4ce1-999e-48fa07692389 20:33:07–20:33:09
Daytona Native Codex 6eb001bb-4bcf-43f7-8717-f662f53dc7c3 77c383d8-a997-49e5-a33e-25c70e15c0b2 20:31:54