Files
PaperClipAI/doc/AGENT-ARTIFACTS.md
DottaandPaperclip e912f0df53 fix(ui): open text attachments in task tabs (#14297)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent work often ends with a Markdown or plain-text file.
> - Task attachments currently open outside the task panel.
> - Users need to inspect those files while keeping the task
conversation in view.
> - This pull request opens text attachments in task tabs and adds
rendered, raw, and download controls.
> - The same controls work in the mobile task drawer.

## Linked Issues or Issue Description

**What happened?**
Opening a text attachment did not put its content in a task tab.
Markdown files had no in-task rendered/raw toggle.

**Expected behavior**
Open Markdown and text attachments in one reusable task tab. Show
Markdown as rendered content or raw text. Download the original file.

**Steps to reproduce**
1. Upload a Markdown file and a plain-text file to a task comment.
2. Open each attachment from the task conversation or artifact list.
3. Switch Markdown between Rendered and Raw. Download both files.
4. Repeat at a mobile viewport width.

Related work: #14193 controls artifact tab arrival. This change adds
text attachment content tabs.

## What Changed

- Route text attachment opens from conversation and artifact cards into
task tabs.
- Add a text attachment panel with accessible Rendered, Raw, and
Download controls.
- Preserve ordinary links for other file types.
- Support the selected attachment in the mobile drawer.
- Keep text-tab actions on the current rich artifact cards, including
CSV previews.
- Render attachment image references and diagram source without loading
media URLs.
- Add browser regression tests, component tests, Storybook examples, and
usage documentation.

## Verification

- Full workspace typecheck, production build, Storybook build, and UI
token gates pass locally.
- All 6,960 UI tests pass. The additional media regression passes
against the real Markdown renderer and fails before the fix. CSV
coverage verifies direct downloads and text tabs after preview.
- Both desktop and mobile browser cases pass locally. They check
rendered/raw Markdown, literal plain text, reusable tabs, review
controls, and exact original download bytes. The local fixture used a
separate database port because an existing socket occupied the default
range.
- The full CI test matrix passes on
`82be5efbef26927b237a031725bb3d7fa79f637f`. The duplicate local `pnpm
test:run` was stopped after this CI result; it did not complete locally.
- Greptile is 5/5 on the final commit. All review threads are resolved,
and the security scan passes.
- All 54 final-head checks pass, including the canary dry run. The two
optional Storybook jobs are skipped.

## Risks

- Text attachment links now open in the task panel. Other content types
keep their existing link behavior.
- File display still depends on the existing authenticated attachment
route. There are no API or database changes.
- Raw text is displayed as text, including strings that look like HTML.
Rendered Markdown keeps media references inert.

## Model Used

OpenAI Codex, based on GPT-6, with code execution, browser testing, and
subagent tool use. The runtime does not expose an exact serving model
variant or context-window size. Recovered earlier implementation changes
were reviewed and tested; their exact model metadata is unavailable.

## 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-09-29 16:40:10 -05:00

228 lines
9.5 KiB
Markdown

# Agent Artifact Upload Workflow
Generated files that a board user or reviewer should inspect as deliverables
must be attached to the Paperclip issue before the agent chooses a final
disposition. A local workspace path is not enough, because cloud users and
reviewers often cannot access the agent's disk.
## Native runner
When `register_deliverable` is available, use it for files in the bound local or
remote workspace. Supply a workspace-relative `contentRef`, basename `filename`,
`contentType`, exact `byteSize` and SHA-256, `title`, and a stable `idempotencyKey`.
The tool verifies the file, stores an attachment and artifact work product, and
binds it to the response. Generic API tools and a legacy API key are unnecessary.
Wait for the receipt. It includes `attachmentId`, `contentPath`, and
`downloadPath`, along with the existing command, revision, entity references,
and disposition. Reuse the original key after an ambiguous result. A receipt
confirms storage and response binding in Paperclip; it does not confirm delivery
to an external chat provider. If registration fails, use the returned error to
resolve the failure or explain the limitation; do not describe a workspace path
as an uploaded file.
## Legacy adapters
Use Bash to run the helper bundled with the Paperclip skill from the repo root; installed skill files may not retain executable permissions:
```sh
bash skills/paperclip/scripts/paperclip-upload-artifact.sh path/to/output.webm \
--title "Walkthrough render" \
--summary "Rendered walkthrough for review"
```
The helper uses the authenticated Paperclip API from the current heartbeat
environment:
- `PAPERCLIP_API_URL`
- `PAPERCLIP_API_KEY`
- `PAPERCLIP_COMPANY_ID`
- `PAPERCLIP_TASK_ID`
- `PAPERCLIP_RUN_ID`
It uploads the file to
`POST /api/companies/{companyId}/issues/{issueId}/attachments` and creates an
artifact work product on `POST /api/issues/{issueId}/work-products` by default.
The command prints issue-safe markdown links for the final task comment.
## Task artifact presentation
Existing and newly arriving agent attachments, work products, and documents add
the task's Artifacts tab without selecting it, opening the side panel or mobile
drawer, or changing the current document/file link. If the pane is closed, the
tab is available when the user opens it. This uses stored object IDs, so it works
with either runner. Uploading a file and registering its work product counts as
one arrival. Revisions and repeated query refreshes preserve dismissed tabs and
the user's selection. Plans retain their existing Plan-tab behavior;
unregistered user input attachments remain in the conversation.
## Uploaded Artifacts vs Workspace Files
Use uploaded artifacts for deliverables: videos, PDFs, screenshots, archives,
reports, rendered HTML, or any file the board should inspect without needing the
agent's checkout. Attachment-backed artifact work products set `type` to
`artifact` and `provider` to `paperclip`, with metadata canonicalized from the
uploaded `attachmentId`.
Use `workspace_file` metadata only for important files that intentionally remain
in a project or execution workspace, such as source files, committed markdown
plans, or generated files whose meaning depends on the checkout. Workspace-only
references are useful signposts, but they are not durable uploads.
Expected work product metadata shape:
```json
{
"resourceRef": {
"kind": "workspace_file",
"issueId": "<issue-id>",
"workspaceKind": "execution_workspace",
"workspaceId": "<execution-workspace-id>",
"relativePath": "doc/plans/example.md",
"line": 1,
"column": 1,
"displayPath": "doc/plans/example.md:1:1"
}
}
```
`workspaceKind` is `execution_workspace` or `project_workspace`. `line` and
`column` are optional. `relativePath` must be relative to that workspace root;
do not store host-local absolute paths as workspace references.
Workspace file links resolve only inside registered Paperclip workspaces. The
default target is the current issue's execution workspace first, then its
project workspace. A link may target another same-company project workspace only
when it carries both that `projectId` and `workspaceId`. Paperclip does not
resolve arbitrary machine-wide filesystem paths, absolute host paths, home
paths, or relative paths that escape the selected workspace.
## Completion Pattern
When a task produces a user-inspectable deliverable file:
1. Generate and verify the file locally.
2. Upload it with `skills/paperclip/scripts/paperclip-upload-artifact.sh`.
3. Keep the artifact work product unless the file is incidental; pass
`--no-work-product` only for supporting files that should not be promoted.
4. Link the printed attachment URL in the final issue comment.
5. Then set the final issue status.
For a response that is explicitly intended for an external chat conversation,
also pass each intended file with `paperclipai issue comment --attachment-id
<id>`. Paperclip binds only those exact uploaded files to that comment; other
task attachments remain internal.
Final comments should name and link the uploaded artifact or work product, not
just the local filesystem path. For workspace-only files, include the work
product title and recorded relative path. Local paths can be included as
diagnostic context, but they cannot be the only access path. Browse/search is a
fallback for recovering workspace files when the issue link or chip is not
available, not the preferred way to deliver files to users.
## Video Examples
Upload an `.mp4` render:
```sh
bash skills/paperclip/scripts/paperclip-upload-artifact.sh dist/demo.mp4 \
--title "Demo video render" \
--summary "MP4 render for board review"
```
Upload a `.webm` render:
```sh
bash skills/paperclip/scripts/paperclip-upload-artifact.sh out/walkthrough.webm \
--title "Walkthrough video" \
--summary "WebM walkthrough render"
```
The helper detects `.mp4`, `.webm`, and `.mov` content types. If a renderer uses
an unusual extension, pass the MIME type explicitly:
```sh
bash skills/paperclip/scripts/paperclip-upload-artifact.sh render.bin \
--title "Demo video render" \
--content-type video/mp4
```
## Direct API Pattern
If the helper is unavailable, use the same API shape:
```sh
curl -sS -X POST \
"$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/issues/$PAPERCLIP_TASK_ID/attachments" \
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
-F 'file=@"dist/demo.mp4";type=video/mp4'
```
Then create a work product when the uploaded file is the deliverable:
```sh
curl -sS -X POST \
"$PAPERCLIP_API_URL/api/issues/$PAPERCLIP_TASK_ID/work-products" \
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
-H "Content-Type: application/json" \
--data-binary @artifact-work-product.json
```
Use `type: "artifact"`, `provider: "paperclip"`, and metadata containing the
uploaded `attachmentId`. The server canonicalizes `contentType`, `byteSize`,
`contentPath`, `openPath`, `downloadPath`, and `originalFilename`.
The optional `executionWorkspaceId` on work-product create and update requests
must identify an execution workspace in the same company. A project workspace
ID is a different identifier and cannot be used here. Omit the field when no
execution workspace is available, or send `null` to clear an existing link.
Invalid references return `422` without changing the work product or the current
primary product.
## Verification
The file-delivery integration suite runs the real helper through queue and
HTTP/2 gateways against a disposable API, database, and storage. It also tests
native registration with generic API tools disabled, duplicate retries, Unicode
filenames, company isolation, and downloads after deleting the workspace.
```sh
pnpm exec vitest run server/src/__tests__/file-delivery-bridges.test.ts
```
To run the same suite on disposable Daytona sandboxes, install the standalone
Daytona plugin's dependencies and set `DAYTONA_API_KEY` in the test process:
```sh
PAPERCLIP_FILE_DELIVERY_DAYTONA=1 pnpm exec vitest run server/src/__tests__/file-delivery-bridges.test.ts
```
The live fixture deletes each sandbox before checking that its attachments
remain downloadable from Paperclip. It does not run unless explicitly enabled.
## Text attachment previews
In the task chat layout, select a text attachment in Artifacts, a work-product
card, or a chat attachment chip to open a named right-side tab. Reopening the
same attachment focuses its existing tab. Tabs can be switched and closed.
On mobile, the same viewer opens in the task details drawer.
Markdown work products keep their expandable review document, annotations,
revision indicator, and document links. **Open in tab** is a separate action.
Work-product cards keep **Download** as a direct original-file download.
Text file cards provide **Open in tab** beside their existing actions. CSV cards
keep this action before and after loading their data preview.
Markdown attachments offer **Rendered** and **Raw** views. Other supported text
files display literal text. Image references and diagram source remain inert;
opening a preview does not load attachment-selected media URLs. The viewer
provides a download action. Preview reads
are limited to 512 KiB; oversized, unsupported, or unavailable files show an
explicit fallback instead of attempting an unbounded render. A failed read can
be retried. Workspace files continue to use the existing workspace file viewer.
Storybook: **Tasks / Text file tabs** covers opening from Artifacts, Markdown,
plain text, empty files, oversized files, missing attachments, and a narrow panel.