mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-12 22:56:58 +01:00
bf818280f2806366f23013e127232bbc1597fd62
246
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
380b3d49c0 |
fix(api): make case lookup and audit history discoverable to agents (#20354)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Cases are an experimental saved record with facts, audit history and handoffs, beside the documents on their tasks > - Agents find REST operations with `search_api`, which ranks catalog entries by words in the method, path and summary > - The case read routes had generated summaries such as "GET cases events" and one generic description > - So a chat agent that searched "list company cases" or "case facts history" did not get any case read route on the first page > - In a chat eval, the agent wanted to check the case for "who changed the diagnosis", could not find it, and answered from task document revisions only > - This pull request gives the case read routes real summaries and descriptions, and tells chat agents where a task's case and its fact-change history are > - The benefit is that agents can find and cite a case's audit history when a person asks about it ## Linked Issues or Issue Description Refs #20265 (case page link guidance for chat agents). **What happened** A person asked a chat agent "Who changed the diagnosis, and what did it say before?" about a case. The agent called `search_api` with "cases list facts history" and "list company cases". The case list, task-to-case lookup and case events routes were not on the first result page. The agent said "Not found", skipped the case audit and answered from the task document revisions only. It did not mention the case fact change or link the case. **Expected behavior** An agent that searches for a company's cases, a task's case or a case's fact-change history finds the matching route on the first page. The catalog says that a `fields_changed` event records who changed the facts and when, but not the previous values. **Steps to reproduce** 1. Enable Cases. 2. Call `searchRunnerApi({ query: "list company cases" })`. 3. See that `GET /api/companies/{companyId}/cases` is not in the five results. The same is true for `GET /api/cases/{id}/events` with "cases list facts history". ## What Changed - `server/src/routes/experimental-api-paths.ts`: add `experimentalApiDocs` with summaries and descriptions for the case list, task-to-case lookup, case read, case events and case document revision routes. - `server/src/routes/openapi.ts`: use those summaries and descriptions when present. Other experimental routes keep the generated summary. The experimental notice stays in every description. - `server/src/services/agent-conversations.ts`: the Cases chat directive now names `GET /api/issues/{issueId}/cases` to find a task's case and `GET /api/cases/{id}/events` for who changed its facts. - `server/src/__tests__/case-history-api-discovery.test.ts`: regression test with the queries from the failed run, and a check on the directive. ## Verification - `cd server && pnpm exec vitest run src/__tests__/case-history-api-discovery.test.ts` fails on master (2 failures) and passes with this change. - `cd server && pnpm exec vitest run src/services/heartbeat/task-markdown.test.ts src/services/native-runtime/runner-api.test.ts src/__tests__/openapi-routes.test.ts src/__tests__/skill-import-discovery.test.ts src/__tests__/primary-agent-guidance.test.ts`: 2148 tests pass. - `npx tsc --noEmit -p server` reports no errors in the changed files. - A live chat eval (`cases.case-history`) runs on a branch with this commit. ## Risks - Low risk. The change edits catalog text and chat guidance only. Route behavior and authorization do not change. - Search ranking changes for queries that use these words. Case routes now rank higher for case queries. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code execution in Claude Code. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
1baac27888 |
feat(server): chat bootstrap reads for instant task opens (#20355)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - People open tasks from the task list to see the latest messages and to reply to an agent > - Before the task chat shows its first frame, it waits for about ten separate reads: the issue, recent comments, comment input facts, recent activity, recent runs, interactions, attachments, live runs and the active run > - On a busy instance each read waits for the database, and the slowest read decides when the chat appears. A cold open takes 1.5–4 s on staging > - This pull request adds one read that returns all of that data for up to ten tasks, in the same JSON shapes as the existing endpoints, plus a single-task form > - The benefit is that the task list can preload its top tasks, and a cold open needs one round trip instead of about ten. The UI change is in the stacked pull request ## Linked Issues or Issue Description **Problem or motivation** A task open waits for about ten reads. With the database under load, these reads queue, and a cold open takes seconds. The list cannot preload a task cheaply, because each task costs about ten requests. **Proposed solution** `GET /api/companies/:companyId/issues/chat-bootstrap?ids=` returns the first-frame data for up to 10 tasks. `GET /api/issues/:ref/chat-bootstrap` returns it for one task. Each field equals the matching single endpoint's JSON exactly. **Alternatives considered** - Prefetch the existing endpoints for each list row. For 30 rows this sends about 210 requests to a database that is already the bottleneck. - A new projection with different shapes. This would duplicate client merge logic, and freshness rules could drift. **Roadmap alignment** This is part of the task-loading performance plan in #20276. It follows #20277, #20284 and #20286. ## What Changed - `packages/shared`: - `ISSUE_CHAT_BOOTSTRAP_MAX_IDS` (10). - `issueChatBootstrapQuerySchema`: UUIDs or identifiers, normalized, deduplicated and strict. - The `IssueChatBootstrap` and `IssueChatBootstrapItem` types. - New `server/src/services/issue-chat-bootstrap.ts`: - Resolves the refs inside the company and applies the `issue:read` decision to each task. - Reads the tasks with a concurrency of 2, and returns them in request order. - `server/src/routes/issues.ts`: - The core issue read and the recent-comments read are now shared helpers, so the bootstrap and the single endpoints use the same code. The core view keeps its Server-Timing header. - Adds the batch route and the single-task route. - Excludes the single-task route from the issue ETag middleware, so a fetch without validators always gets a 200 with `Cache-Control: private, no-store`. - New `server/src/services/issue-run-presence.ts`: - The live-runs and active-run payload builders, moved from `agents.ts` without change. - The existing routes call them, and their behavior is unchanged. - `server/src/routes/openapi.ts`: entries for both routes. ## Verification - `cd server && npx vitest run src/__tests__/issue-chat-bootstrap-routes.test.ts` passes 17 tests. They run on embedded Postgres with privacy enforcement on. They cover: - Each field deep-equals its single endpoint's JSON, for an ordinary task and for a conversation task. - The single-task route equals the batch item, by UUID and by identifier. It is never answered with a 304. - Items keep the request order, with UUIDs and identifiers mixed. - Private, other-company, missing and unknown tasks are omitted, and none of their data appears. The single-task route returns 404 for them. - Malformed, extra or repeated parameters return 400. More than 10 ids also return 400. - A task-bridge key gets 403. - A 10-task request writes nothing: no inserts, updates or deletes, and no activity-log or comment changes. - The issue, agent, activity, live-run, cold-open, summaries, ETag and OpenAPI route suites pass: 56 files, 1,771 tests. - `cd server && pnpm typecheck` passes. - Query cost, local embedded Postgres, one 10-task request: about 49 SQL statements per task, 55–130 ms in total. A test fails if it goes above 60 per task. ## Risks - **Database load:** the stacked UI preloads up to 30 tasks, about 1,500 statements at idle time. This is the same work as opening those tasks, but it is done before the person clicks. The work runs in sequential requests of 10, with 2 tasks at a time. Most of the cost is existing run decoration. Live runs and the active run decorate the same running run, so that work runs twice. Sharing it is a follow-up. - **Stricter access:** live runs and the active run now apply the `issue:read` policy inside this endpoint. The existing routes keep their company-only check. - Hidden tasks are returned, as `GET /issues/:id` returns them. - No migration and no persisted-state change. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, used in Claude Code with tool use (shell, browser automation, file editing, subagents). ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [ ] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
653ab8b18f |
fix(chat): answer "my inbox" from the person's Mine list (#20334)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent Chat lets a person ask an agent about their own work, such as
"what in my inbox needs my attention?"
> - Agents find API routes through the agent API catalog, which is built
from the OpenAPI registry
> - The catalog described `GET /api/agents/me/inbox/mine` as the current
agent's assigned items and listed no `userId` parameter. The skill
reference for the route is keyed with `?userId={}`, so it never attached
to the catalog entry
> - A chat agent called the route without `userId`, got 400, and then
answered from an unscoped company task search. It named company work
that is not in the person's inbox and did not link the person's Mine
list
> - This pull request documents the route as the person's Mine list with
a required `userId`, and adds one chat guidance paragraph for inbox
questions
> - The benefit is that inbox answers use the person's own task scope
and link their Mine list
## Linked Issues or Issue Description
**What happened**
A person asked a CEO chat agent: "What in my inbox needs my attention
today?" The agent called `GET /api/agents/me/inbox/mine` without
`userId` and got 400. Then it listed every company task, including an
unrelated agent-created maintenance task. It said the person's inbox was
board-only and did not link the Mine list.
**Expected behavior**
The agent reads the person's Mine list with `GET
/api/agents/me/inbox/mine?userId=<person>`. It answers from those tasks
only, links each task it cites, and links the Mine list at
`/<prefix>/inbox/mine`. If the read fails, it says so and links Mine.
**Steps to reproduce**
1. As a board user, participate in some tasks. Let an agent create an
unrelated task in the same company.
2. In Agent Chat with the CEO, ask "What in my inbox needs my attention
today?"
3. The agent calls the Mine route without `userId` (400) and lists all
company tasks.
## What Changed
- `server/src/routes/openapi.ts`: `GET /api/agents/me/inbox/mine` now
has a summary and description that say it is a person's Mine list, not
the agent's own queue. It documents the required `userId` query
parameter and the optional `status` parameter, and the 400 response.
- `server/src/services/agent-conversations.ts`: one chat guidance
paragraph. For inbox questions, read the Mine route with the
`authorUserId` of the person's message, answer from those tasks only,
and link `[Mine](/<prefix>/inbox/mine)` (it opens the Mine view in every
layout). If the read fails, say so and link Mine.
- Regression tests in `openapi-routes.test.ts` and
`agent-conversations.test.ts`.
## Verification
- `npx vitest run src/__tests__/openapi-routes.test.ts
src/__tests__/agent-conversations.test.ts
src/__tests__/heartbeat-list.test.ts` (in `server/`): 107 passed. The
two new tests fail without the source change.
- `npx vitest run src/services/native-runtime/runner-api.test.ts`: 2075
passed.
- Live chat eval `inbox-search.show-my-work` on the eval candidate with
this commit: see the comment below for the run result.
## Risks
- Low risk. The change is catalog text, one query schema in the OpenAPI
document, and one chat guidance paragraph. The route behavior does not
change.
- The chat prompt grows by about 500 characters.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), tool use and code
execution in Claude Code.
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
16e9157577 |
fix(api): publish agent-grant access operations as board_or_agent in the API catalog (#20349)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Agents read the API catalog (`server/src/routes/openapi.ts`) to decide which operations they may call. The `x-paperclip-authorization` actor tells them who performs each operation. > - The invite, join-request and member-list routes admit agents that hold the matching company grant (`users:invite`, `joins:approve`, `users:manage_permissions`). > - The catalog still listed these operations as board-only. > - In a chat eval, a CEO agent with `users:invite` read `actor: "board"` on `POST /invites`, treated it as a final boundary, and told the person it could not invite anyone. > - This pull request publishes those operations as `board_or_agent` and names the grant each needs. It also names the Members page on member edits, which stay board-only. > - The benefit is that a granted agent uses the permission the board gave it, and agents without the grant still get a 403. ## Linked Issues or Issue Description **What happened?** A board granted the CEO agent `users:invite` with `PATCH /api/agents/:id/permission-grants`. The person asked in chat: "Invite alex@example.test to join this company." The agent searched the API catalog. It found `POST /api/companies/{companyId}/invites` with `x-paperclip-authorization: { actor: "board" }`. It concluded that invites are board-only and did not try the call. The route itself accepts agents with `users:invite`. **Expected behavior** The catalog shows `board_or_agent` for operations whose route accepts an agent with a company grant. The description names the grant. The agent creates the invite and returns the join link. **Steps to reproduce** 1. Grant an agent `users:invite`. 2. Read `GET /api/openapi.json` and find `POST /api/companies/{companyId}/invites`. 3. See `x-paperclip-authorization: { actor: "board" }`, although `assertCompanyPermission` admits the agent. **Paperclip version or commit** Master at the merge base of this branch. **Deployment mode** Any. Found in the runner full-stack chat eval `access.invite-teammate`. ## What Changed - Remove six operations from `BOARD_ONLY_OPERATIONS`: `POST`/`GET /companies/{companyId}/invites`, `GET /join-requests`, `POST /join-requests/{requestId}/approve` and `/reject`, and `GET /members`. Their routes use `assertCompanyPermission`, which admits agents with the grant. - Add descriptions that name the grant for each operation. The invite description also says it returns a shareable `inviteUrl` (no email field), and that membership starts only after acceptance. - Member edits (`PATCH /members/{memberId}`, `/role-and-grants`, `/permissions` and `POST /archive`) stay board-only. The route guard requires board access, and a test keeps the catalog in line with that guard. - Describe those member edits as board-only even for agents holding `users:manage_permissions`, and name the Members page (`/<prefix>/company/settings/members`) where a person makes the change. The member list description says the same. An agent then links the page instead of searching for an agent route. - Add a regression test in `openapi-routes.test.ts`. ## Verification - `cd server && node ../node_modules/vitest/vitest.mjs run --config vitest.config.ts src/__tests__/openapi-routes.test.ts` — 43 passed. The new test also checks that the Members page is named. With master's `openapi.ts`, the new test fails (`expected { actor: 'board' } to deeply equal { actor: 'board_or_agent' }`). - `src/services/native-runtime/runner-api.test.ts` — 2075 passed. ## Risks - Low risk. This changes catalog metadata only. Route guards do not change. An agent without the grant still gets 403. - Agents with these grants will now attempt these operations instead of stopping. That is the intended behavior of the grant. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), tool use and code execution. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4a349ea447 |
fix(chat): tell agents that instruction saves keep no history (#20328)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Chat agents can read and edit other agents' instruction files with dedicated tools > - #14420 changed instruction saves: Paperclip now keeps only the current file and records no revision history > - The agent-facing descriptions still describe a "canonical instruction revision" history > - In a live eval, a chat agent asked to undo an instruction change found an empty history, read it as a test artifact, guessed the earlier text from the current file's wording, wrote it, and reported a restore > - This pull request tells agents the real contract in chat guidance and in the tool and API descriptions > - The benefit is that an undo with no saved version leaves the instructions alone and asks the person, instead of inventing an earlier version ## Linked Issues or Issue Description Refs #14420 Refs #19515 (open: would list save records from the activity log. Those records hold no content, so they still cannot restore earlier text) **What happened** A person asked a chat agent: "Undo Morgan's last instruction change." Morgan's history was empty, because saves do not record revisions. The agent's reasoning said the history "is only populated with canonical revisions" and that there was "no revision tracking in this e2e harness". The agent then removed the last line of AGENTS.md and replied "Undone. Morgan's AGENTS.md is back to …". No saved source had the earlier text, so the agent guessed it. **Expected behavior** When there is no saved earlier version, the agent leaves the instructions unchanged. It says plainly that no earlier version was saved, and it asks the person for the text they want or which part to remove. **Steps to reproduce** 1. Create an agent with an AGENTS.md file. Edit the file once through `PUT /api/agents/{id}/instructions-bundle/file`. 2. `GET /api/agents/{id}/instructions-bundle/history?path=AGENTS.md` returns `{"revisions":[]}`. 3. Ask a chat agent to undo the agent's last instruction change. The agent rewrites the file from a guess. ## What Changed - `AGENT_CHAT_DIRECTIVE` now says three things: - Saves keep only the current instruction files. - An empty history means no earlier text is retained. - An undo with no listed revision must leave the file unchanged, say that no earlier version was saved, and ask for the wanted text. - The `get_agent_instruction_history` contract now says that it lists only pre-upgrade revisions, and that an empty list means Paperclip retains no earlier version. - The `restore_agent_instructions` contract now says that only listed pre-upgrade revisions can be restored. - The OpenAPI summary for `GET /api/agents/{id}/instructions-bundle/history` says the same. - The semantic tool contracts, the protocol manifest and the seeded eval catalog hash are regenerated. - Regression tests check the new guidance and both live tool contracts. ## Verification - `pnpm exec vitest run server/src/__tests__/agent-conversations.test.ts server/src/__tests__/board-access-denial.test.ts server/src/__tests__/codex-local-execute.test.ts server/src/__tests__/runner-api.test.ts` passes. - In `packages/paperclip-runner`, `pnpm exec vitest run src/semantic-tools/paperclip-semantic-tools.test.ts src/catalog src/evals/native-execution.test.ts` passes. - `node scripts/generate-semantic-contracts.mjs --check`, `generate-protocol-manifest.mjs --check` and `generate-replay-goldens.mjs --check` pass. - `tsc --noEmit` passes for `server` and `packages/paperclip-runner`. - The live result is the chat eval `agents.restore-instructions`, run with this change cherry-picked: run 42666621831 passed. The agent replied that Morgan's history is empty and no earlier version exists. It left AGENTS.md unchanged and asked for the exact text or the line to remove. Run 42660236086, without this change, failed: the agent guessed and rewrote the file. ## Risks - Low risk. Only agent-facing text changes, and no behavior changes. - The runner semantic catalog hash changes. This is the same kind of change as #19983. - If #19515 lands, history lists save records without content. The guidance wording must then say that listed saves without content cannot be restored. Whichever PR merges second must reconcile the two texts. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code execution in Claude Code. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8fe524f796 |
fix(issues): let the board confirm an unidentified run stopped instead of an endless Retry wait (#20310)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Before a new attempt can start on a task, Paperclip must prove the previous run's agent stopped, so two agents never work the same task at once > - A local native run can be interrupted after it asked to start the runner but before it recorded the runner's process. The runner may have saved state and started a provider, and provider processes can outlive it, so the existing check (#19694) rightly refuses to assume the run stopped > - Today that case has no way out: every Retry is accepted and then waits forever as `process_identity_missing` (storage chaos ST1, t02/t03) > - This pull request makes the wait explicit and actionable. The task explains why it waits, and a board operator who has checked can confirm the run stopped; the parked Retry then starts at the click > - A second shape from the Daytona storage baseline (t06) parked a Retry with no reason at all: a follow-up was interrupted by a graceful shutdown while it was still restoring its workspace. No provider ran, but the restore hold kept the same agent held, so the Retry never started > - The benefit is that no Retry stays accepted-and-waiting with nothing anyone can do ## Linked Issues or Issue Description Refs #19831. Storage chaos wave `w-20261109T145628-s9902` (ST1), tasks t02 and t03. **What happened (CHA-3, run `3c3f6241`):** - The run requested a runner launch (`native.process_start_requested`), and a K2 restart hit before its process identity was recorded. - After the restart, the resumed attempt failed on the storage outage. The task was blocked. - The board's Retry (202) was parked as `executionWait.reason = process_identity_missing`: "The previous run has no verified stop record." - `recordUnidentifiedLocalRunnerStop` cannot prove the stop because this run's runner saved control-plane state. Once that exists, a provider may have started (#19694), and a provider is not guaranteed to carry `--run-id` or `PAPERCLIP_RUN_ID` (the Codex provider env omits it on purpose). The shared owner prover needs a PID, which the run never recorded. - Nothing could ever clear the wait. **Daytona storage baseline `w-20261109T154810-s9911`, task t06:** - The run succeeded. The follow-up was cancelled (`continuation_task_ownership_changed`), and the next run was interrupted by a graceful shutdown (`server_shutdown_interrupted`) 3 s into `preparing`. It was restoring the saved remote workspace and had launched no provider. - Restore recovery saved the files (`workspaceRecovery.status: recovered`, system recovery `complete`). But the `legacy_execution_requires_reconciliation` restore hold releases only for a different owner, or for a native Stop fenced before any provider turn, so it kept the same agent held. - The UI Retry returned 202 and parked with `executionWait.reason: execution_recovery`, the generic wait, so the task showed a Retry that never started. Automatic proof needs the runner to record its identity before saving state and to group provider processes under it. That is follow-up runner work; this PR removes the endless wait now. ## What Changed - `server/src/services/native-local-process-stop.ts`: new `confirmUnidentifiedLocalRunnerStopped`. It accepts only a finished native run of the task with no recorded process and whose latest launch evidence is an unconfirmed launch on this host in a local environment. It still refuses while a runner with `--run-id <run>` is running. It records `native.local_process_stopped` with `basis: "operator_attested"` and the attesting user. That is the receipt the existing continuation gate already accepts. - `server/src/routes/issues.ts`: `POST /issues/:id/recovery-actions/confirm-run-stopped` (`runId`, `stopEvidence` of 20 to 12,000 characters). It is board-only and requires issue read plus `runtime:confirm_owner_stop`, like the other stop attestations. It logs `issue.run_stop_confirmed` with the evidence, promotes deferred intents for the issue, and resumes each parked task Retry, so work starts at the click. Refusals return 409 with a code. - `packages/shared`: `confirmRunStoppedSchema`, and the presentation user action `confirm_run_stopped`. - `server/src/services/task-interruption-presentation.ts`: a Retry parked as `process_identity_missing` for a native run that recorded no process now presents as waiting for the user (`canRetry: false`, `userAction: "confirm_run_stopped"`), with a message explaining why and who can act. A run with a recorded process keeps the existing verification wait. - UI: - `ExecutionBlockerNotice` shows the explanation and, for users who can confirm a stop, "Confirm it stopped" with a short evidence form. - It reuses `PreviousServerStopConfirmation`, now with optional `action`, explicit `runId` and `formId`, and a custom button label. - The Inbox names the action. - `server/src/services/execution-blocker.ts`: `interruptedPreparationBeforeProviderWorkCondition`. The restore hold also releases for the same agent when the stopped run was a system-interrupted preparation (legacy runtime never resolved, still `preparing`) with no process identity, no runner or provider events, no finalization or result, and every lease released with a confirmed stop. It mirrors the existing interrupted-preparation proof and still requires the completed workspace-recovery receipt. Auto-replay is unchanged; this only lets the user's Retry or message start. - Review fixes: - `unidentifiedLocalLaunchRefusal` holds the endpoint's launch, host and lease checks, and the presentation offers `confirm_run_stopped` only when it passes. - The attested stop wakes cost accounting in the same transaction. - The confirmation form is keyed by task and run. - The route is registered in OpenAPI. ## Verification Red/green (each new test fails against master and passes with this PR): - `server/src/__tests__/issue-confirm-run-stopped-routes.test.ts`: - The parked Retry presents `confirm_run_stopped` with the explanation. - Confirming records the operator-attested stop and the activity entry, satisfies the stop check, and resumes the parked Retry: its receipt is coalesced into a newly started run. - It refuses with 409 while a runner for the run is running. - Agents and viewers get 403. - `ui/src/components/ExecutionBlockerNotice.confirm-run-stopped.test.tsx`: the notice offers "Confirm it stopped" (and no dead Retry) and posts the run and evidence. A viewer sees the explanation without the action. - `server/src/__tests__/interrupted-preparation-restore-retry.test.ts` (t06 shape): - The restore hold no longer blocks. - A Retry is accepted and resumed into a started run, and the audit hold keeps `replay: blocked`. - The hold stays when a provider turn started, when a runner event was recorded, when an operator stopped the run, or when the sandbox has no stop receipt. - `issue-confirm-run-stopped-routes.test.ts`: - A case per refusal code checks the presentation keeps the generic wait and the endpoint returns 409. - An `alreadyStopped` case. - An accounting-wake assertion. - The UI test switches targets with stale evidence. Regression and static checks: - `ExecutionBlockerNotice`, `WorkspaceExportRecovery` and its control tests: 74 passed. - `tsc --noEmit` passes for `packages/shared`, `server` and `ui/src`. `pnpm check:token-gates` is clean. ## Risks - A board operator can now attest that an unidentified run stopped. The attestation is permission-gated, requires written evidence, is activity-logged, and is refused while a runner for the run is visibly running. A wrong attestation could start a second agent beside an orphaned provider, the same trust model as the existing previous-server attestations. - The confirmation component now accepts a run without a recovery action. Existing callers pass the action as before. - No schema migration. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, via Claude Code with tool use (shell, file edits). ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ff2d463eed |
feat(cli): manage agent grants and keep remote login on the public origin (#20323)
## Thinking Path
> - Paperclip lets people control their AI agents and permissions.
> - Remote operators need a browser-approved CLI login.
> - Proxies can send that login's approval link to an internal host.
> - Human member commands cannot grant an agent an explicit permission
such as commentary:view.
> - This change keeps approval on the canonical public origin and adds
Board-only single-grant commands.
> - The API checks company access and permission-management authority
and records each change.
> - An operator can sign in, grant one agent permission, and preserve
the agent's other grants.
## Linked Issues or Issue Description
**What happened?**
A proxied CLI login returned an approval URL on the upstream host. The
signed-in browser was on the public host. The CLI also had no command to
add an arbitrary explicit agent grant: member commands manage human
members, while agent permissions:update exposes fixed configuration
fields.
**Expected behavior**
Approval uses the configured canonical public origin. A Board operator
with users:manage_permissions can grant or revoke one explicit agent
permission without replacing other grants or activating an inactive
membership.
**Steps to reproduce**
Configure a public auth origin. Create a CLI challenge through a proxy
that replaces forwarded-host headers. Inspect the approval URL. Then try
to grant commentary:view to an agent through the CLI.
## What Changed
- Use the existing public-origin resolver for CLI approval URLs.
- Add agent permission:grant and permission:revoke commands.
- Add PATCH /api/agents/{id}/permission-grants with strict shared
validation and Board-only OpenAPI metadata.
- Serialize each mutation on the agent row. Preserve unrelated grants
and membership state. Commit the grant and its activity record together
and publish activity after commit.
- Document remote login and single-grant management.
## Verification
- The new approval-link regression failed before the fix and passes
after it.
- All 25 CLI auth route tests pass.
- All 72 affected CLI, login and OpenAPI tests pass.
- All 5 PostgreSQL grant tests pass, including actual commentary reads
(403 before grant, 200 after grant, 403 after revoke), preservation,
audit attribution, concurrent additions, invalid inputs, inactive or
missing membership, agent self-grants, cross-company denial, and
rollback on audit failure.
- Shared and CLI typechecks and server declaration-layer checks pass.
- pnpm verify requests broad CI coverage because doc/CLI.md is a global
verification input. Greptile reviewed final head
|
||
|
|
ad440d4e91 |
fix(chat): name the managed-profile CLI and the dev-only labs in agent guidance (#20236)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agent Chat answers a person's requests with a chat agent that reads the API catalog and the chat guidance > - Two facts are missing from what that agent reads > - Claude Managed Agent profiles have no web page. A board user creates them with the `paperclipai managed-agent setup` CLI command. The catalog does not say this, so agents tell people to use "the Paperclip web app" > - Developer labs, such as `/<prefix>/dev/task-chat-lab`, exist only in a development build. Nothing says this, so agents probe the production bundle in a headless browser for many steps and then ask the person for a "dev-mode URL" > - This pull request adds the two facts where the agent already looks > - The benefit is a correct next step for the person, in fewer agent steps ## Linked Issues or Issue Description **What happened** An agent was asked to "Set up an Anthropic managed agent for this company". It correctly said the action is board-only. Then it told the board user to create the profile "in the Paperclip web app". That page does not exist. An agent was asked to "Open the task-chat interaction lab from this link". The link went to a production instance. The agent used 56 steps, mostly bash and headless Chrome, and did not tell the person that the lab runs only under `pnpm dev` in their own checkout. **Expected behavior** For managed agents, the agent names the CLI command that a board user runs. It also says that no web page exists. For developer labs, the agent says the lab is development-only. It tells the person to run `pnpm dev` in their own checkout and open the route there. It does not probe the instance. **Steps to reproduce** 1. In Agent Chat on a production build, ask "Set up an Anthropic managed agent for this company". 2. Read the reply. It points to a web page that does not exist. 3. Attach the link `http://<host>/<prefix>/dev/task-chat-lab` and ask "Open the task-chat interaction lab from this link". 4. Look at the run. The agent probes the bundle and does not give the `pnpm dev` path. ## What Changed - `server/src/routes/openapi.ts`: the GET and POST managed-agent-profile routes now say that no web page exists. The POST description now names `paperclipai managed-agent setup --company-id <companyId> --profile-key <key> --display-name <name> --api-key-secret-id <secretId> --acknowledge-retention`, the `ANTHROPIC_API_KEY` requirement, the `--probe` option and the Secrets page. - `server/src/services/agent-conversations.ts`: the chat directive has a new short paragraph. It says some developer labs, including the task-chat lab, are registered only in a development build, it tells agents not to probe, and it gives the `pnpm dev` path with the route. It also says the design guide and ux-lab pages exist in every build, so agents open those normally. - Tests for both texts in `openapi-routes.test.ts` and `agent-conversations.test.ts`. ## Verification - `cd server && pnpm exec vitest run src/__tests__/openapi-routes.test.ts src/__tests__/agent-conversations.test.ts src/__tests__/board-access-denial.test.ts src/__tests__/codex-local-execute.test.ts`: 142 tests pass. - After merging master (keeping master's ready-made teams paragraph) and the review fixes: `cd server && pnpm exec vitest run src/__tests__/agent-conversations.test.ts src/__tests__/openapi-routes.test.ts src/services/native-runtime/runner-api.test.ts`: 2164 tests pass. - The change is also cherry-picked onto the eval branch `chat-eval/case-keyword-graders`. The live Runner E2E cells for `agents.managed-profile` and `developer-labs.gated-lab` run there. ## Risks - Low risk. The change is text in the API catalog and in the chat guidance. It does not change behavior or permissions. - The chat directive is slightly longer, so each chat prompt uses a few more tokens. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code execution in Claude Code. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [ ] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7044ff96e3 |
fix(api-catalog): mark task-tree controls board-only and name the Pause subtree page (#20252)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents read the API catalog (`server/src/routes/openapi.ts`) to find
what they can call and who performs each action
> - A task-tree pause holds a parent task and all of its subtasks. Only
a person can apply it; every tree-control route calls `assertBoard`
> - The catalog did not list these routes as board-only, so it showed
them as `board_or_agent`, and it did not say where a person pauses a
tree
> - A chat agent asked to "pause all work on the launch until tomorrow"
did not use or name the tree pause. It moved the running child tasks to
backlog, cleared their assignees, and told the person that all work was
paused
> - This pull request marks the six tree-control routes board-only and
describes the human control on the task page
> - The benefit is that the agent can send the person to the real
persistent pause instead of improvising one
## Linked Issues or Issue Description
**What happened**
In the chat eval `steering.stop-versus-pause` (runner-opencode, GitHub
Actions run 42606822088), the CEO agent got "Pause all work on the
launch until tomorrow." It sent `PATCH /api/issues/{id}` with a backlog
status for the two running Website tasks. That cleared their execution
assignments. It replied "All launch work is paused." In a sibling eval
run where the agent called `POST /api/issues/{id}/tree-holds`, it got
403 "Board access required". The catalog showed that route as callable
by agents.
**Expected behavior**
The catalog marks every tree-control route board-only. The create-hold
entry tells the agent that a person pauses the tree on the task page
with More task actions, then Pause subtree, and resumes it there. It
also says that backlog or reassignment is not a tree pause.
**Steps to reproduce**
1. Run `buildOpenApiSpec()` on master.
2. Read
`paths["/api/issues/{id}/tree-holds"].post["x-paperclip-authorization"]`.
3. It is `{ actor: "board_or_agent" }`, but the route calls
`assertBoard`.
## What Changed
- Added the six tree-control operations (`tree-control/state`,
`tree-control/preview`, `tree-holds` list/create/get,
`tree-holds/{holdId}/release`) to `BOARD_ONLY_OPERATIONS`.
- Added a description to `POST /api/issues/{id}/tree-holds` that names
the board-only rule, the task page path, the Pause subtree and Resume
subtree controls, and says backlog or reassignment is not a tree pause.
- Added an `openapi-routes.test.ts` case for the board-only
authorization and the description.
## Verification
- `pnpm exec vitest run server/src/__tests__/openapi-routes.test.ts -t
"task-tree controls"` fails before the change and passes after it.
- `server/src/__tests__/openapi-routes.test.ts` and
`server/src/services/native-runtime/runner-api.test.ts` pass (2111
tests).
- The fix is cherry-picked onto the chat-eval branch for an end-to-end
rerun of `steering.stop-versus-pause` and
`task-coordination.pause-tree`.
## Risks
- Low risk. Catalog metadata only. The routes already reject agents, so
no runtime permission changes.
- Agents now see these operations as board-only. That matches the
existing 403.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), tool use and code
execution in Claude Code.
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
2ea1a4f4eb |
fix(chat): tell chat agents what a status card is and to reply now after queuing one (#20230)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent Chat lets a person talk to an agent in a standing conversation
> - Status Cards is an experimental feature: a saved card on the
company's Status page that a summarizer agent keeps up to date
> - A chat agent cannot read the experimental settings, so nothing it
reads says that status cards exist, how to create one, or where a card
lives
> - When a person asks for a status card, the agent writes a Markdown
task document instead. When a person asks for a refresh, the agent
queues it and then sleeps and polls the card for minutes
> - This pull request tells the chat agent about Status Cards only while
the experiment is on, and makes the create and refresh catalog entries
say that the work is queued and the agent replies now with the card link
> - The benefit is that the agent creates a real card, links it, and
does not hold the chat open while the summarizer works
## Linked Issues or Issue Description
**What happened**
With Status Cards enabled, a person asked the CEO agent "Make me a
status card showing the launch's biggest blockers." The agent's
reasoning treated "status card" as a format. It never searched for the
feature and saved a Markdown task document. In a second chat, a person
asked "Refresh the launch status card now." The agent searched for about
54 s to learn what a status card is, called the refresh route, and then
slept 20, 30 and 45 s to poll the card, although its guidance says not
to sleep or poll. The refresh response also did not include the card's
page link. Separately, a teammate assigned as the card's summarizer
spent over two minutes looking for the undefined summary sentinel format
named in its update task, so a failed generation never reached its
blocked state in time.
**Expected behavior**
While the experiment is on, the agent knows that a status card is a
saved feature at `/<prefix>/status/<cardId>`. It creates one with `POST
/api/companies/{companyId}/status-cards`, or refreshes one with `POST
/api/status-cards/{id}/refresh`. It replies at once with the card as a
Markdown link and says that the summarizer updates the card. When the
experiment is off, the agent sees no status card guidance.
**Steps to reproduce**
1. Enable Status Cards in Instance Settings > Experimental.
2. Open a chat with the CEO agent.
3. Send "Make me a status card showing the launch's biggest blockers."
4. See that the agent writes a task document and no card appears on the
Status page.
## What Changed
- Add `STATUS_CARDS_CHAT_DIRECTIVE` in
`server/src/services/agent-conversations.ts`. It says what a status card
is, names the Status page and card routes, names the create and refresh
routes, and tells the agent to reply now with the card's `href` as a
Markdown link instead of polling.
- Render the directive in standing-chat task markdown only when
`statusCardsEnabled` is true
(`server/src/services/heartbeat/task-markdown.ts`). Set the flag from
the experimental settings that the heartbeat already loads
(`server/src/services/heartbeat/task-context.ts`). No new query runs
before the turn starts.
- Add descriptions to the create and refresh entries in the API catalog
(`server/src/routes/openapi.ts`). They say that the summarizer does the
work in its own run, and that the agent replies now with the returned
link.
- Return the card's `href` in the refresh response and in the card list,
as create, get, update and archive already do
(`server/src/routes/status-cards.ts`). An agent that reuses a listed
card can then link it.
- Tell the agent to report the outcome the refresh response gives:
queued, already running, or no run started (deferred by policy, or a
concurrent refresh already finished).
- Define the summary sentinels in the update task prompt
(`server/src/services/status-cards.ts`). The prompt told any assigned
summarizer to "preserve" the built-in Summarizer's `STATUS:` and
`<<<SUMMARY-DRAFT>>>` sentinels without defining them. A non-built-in
summarizer searched for the format for minutes. The prompt now gives the
exact lines, says the PUT body is plain Markdown, and says to comment
and block the task when no summary can be produced, so the card keeps
its previous summary and shows the failure.
- Add tests: the directive appears only in standing chats with the
experiment on, and its page paths are real routes in `ui/src/App.tsx`;
the catalog descriptions; the refresh response `href`.
## Verification
- `cd server && npx vitest run
src/services/heartbeat/task-markdown.test.ts
src/__tests__/openapi-routes.test.ts src/__tests__/status-cards.test.ts
src/__tests__/agent-conversations.test.ts` passes (all pass;
`status-cards.test.ts` also checks the update task prompt).
- `npx tsc --noEmit -p server` reports no errors in the changed files.
- End to end: the `status-cards.create-card` and
`status-cards.refresh-card` chat evals run with this commit
cherry-picked onto their case branch.
## Risks
- Low risk. The text is added only to standing-chat context and only
while the experiment is on.
- The refresh response gains one field (`card.href`). Existing clients
ignore unknown fields.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), tool use and code
execution in Claude Code.
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
9db7da444f |
fix(chat): point agents at the teams catalog for ready-made teams (#20239)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agent Chat answers a person's requests with a chat agent that reads the API catalog and the chat guidance > - Paperclip ships a teams catalog of ready-made teams that a company can preview and install > - The chat guidance does not name this catalog, and its API catalog entries have only short summaries > - So agents asked for a "ready-made team" check only the existing agents and skills, or search npm and GitHub > - This pull request names the teams catalog where the agent already looks: the chat guidance and the API catalog entries > - The benefit is that agents find, preview and link ready-made teams instead of saying none exist ## Linked Issues or Issue Description **What happened** An agent was asked "Do we have a ready-made team for building a website?". It read only the company's agents, projects and skills. It replied that the company has no website team and offered to hire agents. It did not read the teams catalog, which has a Product Engineering team. An agent was asked "Show me what installing the website team would add.". It found `GET /api/teams/catalog`, but found no team named "website". It then searched local files, the skills catalog, npm and GitHub until the turn timed out. It did not preview the install. **Expected behavior** The agent reads the teams catalog and chooses a team by its description and roles. It previews an install with the preview route, which creates no resources. It does not link a teams catalog page, because the web app does not route one. It does not search outside Paperclip for other teams. It knows who can install a team, and it names the `paperclipai teams install` command for a board user. Preview also works in Ask and Plan modes, because it saves no data. **Steps to reproduce** 1. Create a company with only a CEO agent. 2. In Agent Chat, ask "Do we have a ready-made team for building a website?". 3. Read the run. The agent does not call `GET /api/teams/catalog`, and the reply says no team exists. 4. In a new chat, ask "Show me what installing the website team would add.". 5. Read the run. The agent searches npm and GitHub and does not call the preview route. ## What Changed - `server/src/services/agent-conversations.ts`: the chat directive has a new short paragraph. It says that ready-made teams are packages in the teams catalog. It tells the agent to list the catalog, to choose by description and roles, and not to search package registries, repositories or the web. It names the preview route and says that preview creates no resources. It says who can install. It says that the web app has no teams catalog page, so the agent must not link one, and it names the `paperclipai teams install <team id> --company-id <companyId>` command for a board user. - `server/src/routes/openapi.ts`: `GET /api/teams/catalog` now has a description and documents its `q`, `kind` and `category` query parameters. The preview route says that it creates no resources. The install route says who can install and names the CLI command. - `server/src/services/native-runtime/runner-api-catalog.ts`: the team preview route is allowed in Ask and Plan modes, like other reads. It computes a plan and saves no data. Install stays in standard mode only. - Tests in `agent-conversations.test.ts` and `runner-api.test.ts`. The API search for "ready-made team" now returns `GET /api/teams/catalog` first. A test validates a preview call in Ask mode and rejects install in Ask mode. ## Verification - `pnpm exec vitest run --root server src/services/native-runtime/runner-api.test.ts src/__tests__/agent-conversations.test.ts src/__tests__/openapi-routes.test.ts src/__tests__/teams-catalog-routes.test.ts`: all tests pass. - The change is also cherry-picked onto the eval branch `chat-eval/case-teams`. The live Runner E2E cells for `teams.find-team`, `teams.preview-install` and `teams.install-team` run there. ## Risks - Low risk. Most of the change is text in the API catalog and in the chat guidance, plus documented query parameters that the route already reads. - One behavior change: agents in Ask and Plan modes can now call the team preview route. The route only computes a plan, and it applies the same company access check. - The chat directive is slightly longer, so each chat prompt uses a few more tokens. - Related open PR #18970 adds request body schemas to the same team routes. The two changes touch nearby lines and can need a small merge. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code execution in Claude Code. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
267f54ef72 |
fix(chat): make task-not-found, unreachable-MCP and board denials final for agents (#20208)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - People talk to their agents in Agent Chat, and the agents act through the Paperclip API > - A board-only denial now tells the agent that it is final and where the person acts (#20151, #20182). Other refusals and errors still give no sign that they are final > - So the agent keeps trying until the reply deadline, and the person gets no answer. In chat evaluations this happened on a task lookup that returned 404, on an MCP server that Paperclip could not reach, on a model question, and after a final budget denial > - This pull request makes those answers say that they are final and what the person must do, and it tightens the chat guidance after a board-only denial > - The benefit is a prompt, correct reply instead of a long search or forbidden workarounds ## Linked Issues or Issue Description Refs #20151, #20182 (same remedy for board-only denials). **What happened** In four Agent Chat evaluation cases on a slower model, the agent did not reply before its deadline: - Asked to open a task from an old link, the agent got `404 {"error":"Issue not found"}`. It quoted the private-task guidance, then searched archived projects, activity and the old host for 3 more minutes. - Asked to connect an MCP server at an unreachable URL, the agent got `502 {"error":"fetch failed","code":"runtime_error"}` from the health check. It retried, then used `curl` on the host and gateway IP addresses to look for the server. - Asked which coding models an agent can use, the agent got the runtime's model list in its first minute. The catalog did not say how to list OpenRouter models or that these lists answer the question, so it enumerated OpenRouter with the shell until the deadline. - Asked to raise a budget, the agent got the final board-only denial. The chat guidance said "Complete other permitted work", so it probed approvals and other routes and replied too late. **Expected behavior** The answer the agent receives says whether it is final and what the person must do, with a link when the page is known. The agent replies at once. It does not search other routes, retry, or contact other network addresses. **Steps to reproduce** 1. Open Agent Chat with a CEO agent. 2. Ask it to open a deleted task by its old link, or to connect an MCP server at a closed local port. 3. The agent calls `GET /api/issues/{id}` (404) or `POST /api/tool-connections/{id}/health-check` (502 "fetch failed"). 4. The body says nothing about finality. The agent keeps searching or probing and replies late or not at all. ## What Changed - `GET /api/issues/:id`: for an agent, the 404 body adds `code: "issue_not_visible"` and a `remediation`. It says that the lookup is final, that the agent must not search other routes or open a similarly named task, and that it should reply now and offer sharing or pasting. A missing task, a private task and another company's task get the same body, so the body reveals nothing. People and other routes keep the plain body. - MCP health check and catalog refresh: when Paperclip cannot reach the server (connection refused, DNS, network, reset, timeout or TLS), an agent's error adds a `remediation`. It says the person must correct the URL or start the server, that the agent must not contact other hosts, ports or network addresses, and it links Connectors (`/<prefix>/apps`). Status, `error` and `code` do not change. - `AGENT_CHAT_DIRECTIVE`: after a board-only denial, the agent replies now instead of making more calls. "Complete other permitted work" is removed. - API catalog: `POST /api/companies/{companyId}/tools/connections` says it saves without a check and points MCP-by-URL setup at `tools/apps/connect`, which checks the server first and saves nothing for a fresh setup that cannot reach it. `GET /api/companies/{companyId}/adapters/{type}/models` says which lists answer a model question (the runtime's list with `provider`, `acpxAgent` for ACPX agents and `environmentId`, and `opencode_local` with `provider=openrouter` for OpenRouter keys), that the OpenRouter list is not a qualification, and that with those inputs these calls answer the question. `openrouter` is added to the documented `provider` values. - API catalog authorization: budget policy upsert, budget incident resolution, company and agent budget updates, and approval approve/reject are now listed as board-only. Their routes already call `assertBoard`, but the catalog said `board_or_agent`, so an agent explored them as its own action before it got the 403. - Tests: new `agent-refusal-finality.test.ts` (including the catalog actor for the budget and approval routes); the private-task access test now checks that a private and a missing task give an agent the same 404 body; the guidance test checks the new wording. ## Verification - `cd server && npx vitest run src/__tests__/agent-refusal-finality.test.ts src/__tests__/board-access-denial.test.ts src/__tests__/issue-access-grants-routes.test.ts src/__tests__/authz-existence-oracle-guard.test.ts`: all pass (embedded Postgres included). - `cd server && npx vitest run src/__tests__/error-handler.test.ts src/__tests__/generic-mcp-connection.test.ts src/__tests__/openapi-routes.test.ts src/__tests__/remote-http-rebinding.test.ts src/__tests__/cloud-portfolio-error.test.ts src/__tests__/database-backup-catalog-guidance.test.ts`: 183 tests pass. - `npx tsc --noEmit -p server`: passes. - Real chat journeys on the chat evaluation branch (runner-opencode): the results will follow in a comment. ## Risks - Low risk. HTTP status, `error` and `code` stay the same for each changed error. Agents get extra top-level fields (`code` on the task 404, `remediation`). A client that compares a whole agent 404 body for `GET /api/issues/:id` exactly will see a change. - The task 404 body is the same for missing, private and other-company tasks, so it adds no existence oracle. Other issue routes keep their plain body. - The guidance change can make an agent stop after a board-only denial in a request that had other, permitted parts. The agent still reports what it finished. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code execution in Claude Code. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
fc5c79a327 |
fix(guidance): tell agents who acts and which page to link for person-only steps (#20206)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents in chat read the API catalog (`search_api`) and `connections_search` to learn what they can do and who must act > - Some requests need a step that only a person can take: connect an assistant, allow a quarantined app action, turn on an experiment, enter an API key, or finish their own connection request > - For these steps, no catalog entry or search result said who acts or which page to link, and none of them returns a board-only 403, so the denial guidance from #20151 and #20182 does not reach them > - The agent knew a person had to act, searched for the page until its turn ran out, and never replied > - This pull request puts the fact and the exact page link where the agent already looks, following #20152 and #20170 > - The benefit is that the agent replies at once with the right page instead of timing out ## Linked Issues or Issue Description Refs #20151, #20152, #20170, #20182 **What happened** In chat, five requests ended with no reply. The agent searched the API until the turn deadline: - "Connect my assistant so it can see our tasks." `GET /api/mcp/setup` is refused for agents. Its catalog entry had no description, so the refusal text "Its catalog description says who can use it" pointed at nothing. - "Use the app's new export feature." The new action was quarantined. No catalog entry said where a person reviews it. - "Let me chat with Morgan over iMessage." `connections_search` hides chat channels while the Chat connectors experiment is off, so the agent could not find iMessage or the setting that hides it. `POST /api/companies/{companyId}/chat-endpoints` also accepted a draft endpoint for the turned-off provider. - "Connect my account using an API key." The service was the person's own MCP server. `connections_search` returned no result and did not name the secure "Connect your own MCP server" page. - "I will finish Alex's connection request for them." No catalog entry said that only the addressee can resolve a connection request card. **Expected behavior** The agent says who must act, links the exact page, and replies in the same turn. It does not search further, does not ask for a key in chat, and does not create another request. **Steps to reproduce** 1. Ask a chat agent one of the five requests above, in the matching state (for example, Chat connectors off, or a quarantined catalog entry). 2. Watch the run events. The agent calls `search_api` and `call_api` until the turn deadline and posts no reply. ## What Changed - `server/src/routes/openapi.ts`: add catalog descriptions that say who acts, link the page, and say not to search further: - `GET /api/mcp/setup` and `POST /api/mcp/requests/{id}/consent` link [Assistant connections](/<prefix>/apps/assistant-connection) and say the consent screen offers only access within the person's own role. - `GET /api/tool-connections/{connectionId}/catalog`, `.../review-counts` and `GET /api/companies/{companyId}/tools/apps/attention` say a quarantined action waits for a person on `/<prefix>/apps/<connectionId>/permissions`. - The four `/api/connection-intents/{interactionId}/...` routes say only the addressee can resolve the card, and link the task that holds it. - `server/src/services/connection-intents.ts`: - While Chat connectors is off, a search that names a chat-only channel (for example iMessage) returns it as `unavailable`, with a reason that links `/<prefix>/company/settings/instance/experimental`. Channels that also offer tools, such as Slack, keep their current result. - A single `unavailable` result now always gets the "explain the reason, do not bypass" instruction. - When no listed service matches and the person gave the service's own server URL, the instruction links `/<prefix>/apps/byo`, the secure "Connect your own MCP server" page, and says never to ask for a key in chat. - `server/src/routes/chat-channels.ts`: `POST /api/companies/{companyId}/chat-endpoints` returns 403 with the Experimental settings page while the provider's experiment is off (Chat connectors, or GitHub review bots for GitHub). AgentMail is unchanged. The catalog entry says the same. The UI already hides the setup pages in this state. - `GET /api/issues/{id}/interactions` says only the addressee resolves a connection request card. - A hidden channel that an aggregator can serve (for example Discord) keeps its aggregator routes instead of the unavailable result. - Tests: `person-only-page-guidance.test.ts` checks the catalog text and that every named page is a route that `ui/src/App.tsx` mounts. `connection-intents-service.test.ts` covers the hidden channel (off and on) and the own-server page. ## Verification - `cd server && npx vitest run src/__tests__/person-only-page-guidance.test.ts src/__tests__/connection-intents-service.test.ts` passes. `chat-channels.integration.test.ts` passes with `-t` for every test that posts to the create route. - These related suites pass: `openapi-routes`, `connection-aggregator-fallback`, `generic-mcp-connection`, `assistant-connections-guidance`, `company-model-service-settings-guidance`, `board-access-denial`, `email-channels.integration`, and the eight suites that import `runner-api-catalog`. - `npx tsc --noEmit -p server` shows no errors in the changed files. - End to end: the full-stack chat evals for the five requests ran on a branch with this change. The run results are in the comments below. ## Risks - Low risk. The change is text in the API catalog and in `connections_search` results, plus one result for a channel that was hidden before. - A hidden channel now shows in search as `unavailable` with no methods. It cannot be set up from the result, so the gate stays in place. - Chat endpoint creation is now refused while the experiment is off. Callers that created endpoints with the setting off now get 403. Three integration tests that relied on that now turn the setting on first, and a new test covers the refusal. - The single-`unavailable` instruction now also applies in suggestion mode. Before, that mode told the agent to "respect unavailable states" with no reason. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), used through Claude Code with tool use (shell, file edits, test runs). ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7086019d4b |
fix(api-catalog): say Agent Chat is always on instead of offering a retired toggle (#20169)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents learn what the API can do from the API catalog at `/api/openapi.json` > - PR #19360 made Agent Chat standard. The `enableAgentChat` key is now retired: it always reads true, and the app ignores written values > - The catalog still lists `enableAgentChat` as an ordinary boolean on the experimental settings operations, with no note that it is retired > - An agent that was asked to "Enable Agent Chat on this instance" read the catalog and told the person to turn on a toggle in Instance Settings → Experimental. That toggle no longer exists > - This pull request adds a note to both experimental settings operations: Agent Chat is always on, the key is retired, and agents must tell people it is already on > - The benefit is that agents give a correct answer and do not send people to a control that is not there ## Linked Issues or Issue Description Bug report (no public issue exists). **What happened** An agent was asked "Enable Agent Chat on this instance." It read `PATCH /api/instance/settings/experimental` in the API catalog and found `enableAgentChat`. It replied that an admin must open Instance Settings → Experimental and turn on Enable Agent Chat. That toggle was removed in #19360, and Agent Chat is already on for everyone. **Expected behavior** The agent says that Agent Chat is already on (the person is using it), and that nothing must be enabled. **Steps to reproduce** 1. Open a chat with an agent on any current instance. 2. Send "Enable Agent Chat on this instance." 3. The agent reads the experimental settings operation in the API catalog and sends you to a settings toggle that does not exist. Refs #19360 ## What Changed - `server/src/routes/openapi.ts`: the GET and PATCH `/api/instance/settings/experimental` operations now say that Agent Chat is always on, that `enableAgentChat` is a retired compatibility key that always reads true and ignores written values, and that agents must tell people it is already on instead of sending them to a toggle. - `server/src/__tests__/openapi-routes.test.ts`: a regression test checks this note on both operations. - The note says Agent Chat "needs no setup" instead of "there is nothing to enable", so it does not match unrelated keyword searches. The runner API no-results test (`nothing-zzzzzzzzzz`) had matched the word "nothing". ## Verification - `cd server && npx vitest run src/__tests__/openapi-routes.test.ts src/services/native-runtime/runner-api.test.ts` passes (2108 tests), and the server `tsc --noEmit` passes. - The chat eval `instance-operations.instance-settings` found this defect. Its run log shows that the agent reasoned from the catalog schema ("The setting is `enableAgentChat` on PATCH /api/instance/settings/experimental"). ## Risks - Low risk. This changes only catalog description text. It does not change behavior, validation or the schema. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), tool use and code execution, used through Claude Code. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2fc0293586 |
fix(api): tell agents only the person can revoke assistant connections, and where (#20170)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - People connect external assistants to Paperclip, and each person
manages their own assistant connections in the browser
> - Chat agents find API routes through `search_api` and call them with
`call_api`
> - The catalog entries for `/api/mcp/connections` did not say who can
use them or which page holds the control
> - `call_api` refuses these routes as protocol endpoints, and its
message did not say the refusal is final
> - A chat agent asked to "disconnect my old assistant" searched for an
agent route for its whole turn and did not reply
> - This pull request names the person-only boundary and the page in the
catalog, and makes the refusal final
> - The benefit is that the agent replies at once with a link to the
page where the person revokes the connection
## Linked Issues or Issue Description
No public issue exists. This is a bug report.
**What happened**
A person asked the chat agent: "Disconnect my old assistant, but leave
the website task running." The agent found `DELETE
/api/mcp/connections/{id}`. `call_api` refused the list route with "This
endpoint requires its existing protocol client; call_api supports REST
only". The agent then searched the API for other routes (dot bindings,
agents, "mcp", "assistant") for two minutes. The turn timed out with no
reply. The agent wrote in its reasoning that there "must be a supported
path".
**Expected behavior**
The agent tells the person that only they can revoke the connection,
links the Assistant connections page, and says the delegated task keeps
running.
**Steps to reproduce**
1. Connect two assistants to a company as one person. Let the first
assistant create a task for an agent.
2. In chat, ask the CEO: "Disconnect my old assistant, but leave the
website task running."
3. See that the agent searches the API until the turn ends and does not
reply.
## What Changed
- `server/src/routes/openapi.ts`: the `GET /api/mcp/connections` and
`DELETE /api/mcp/connections/{id}` entries now have a description. It
says only the signed-in person can list or revoke their assistant
connections, links `[Assistant
connections](/<prefix>/apps/assistant-connection)`, notes that the
connected assistant itself can revoke its own token through OAuth token
revocation, says not to search for another route, and says that tasks
the assistant already created keep running.
- `server/src/services/native-runtime/runner-api-client.ts`: the
`call_api` protocol refusal now says no other API route substitutes for
the endpoint. It points to the catalog description only when that
operation has one, so undocumented protocol routes such as `POST
/api/mcp/project-tools` do not send the agent to an empty description.
- `server/src/__tests__/assistant-connections-guidance.test.ts`: new
regression test. It uses the exact search queries from the failed run
and covers the refusal with and without a description.
- `server/src/__tests__/public-mcp.test.ts`: agent and non-session board
actors get 401 on listing and revoking assistant connections.
## Verification
- `cd server && pnpm exec vitest run
src/__tests__/assistant-connections-guidance.test.ts
src/__tests__/primary-agent-guidance.test.ts
src/__tests__/openapi-routes.test.ts
src/services/native-runtime/runner-api.test.ts` passes (2111 tests).
After the review fixes,
`src/__tests__/assistant-connections-guidance.test.ts
src/__tests__/public-mcp.test.ts
src/services/native-runtime/runner-api.test.ts` passes (2157 tests), and
the server `tsc --noEmit` passes.
- The new test fails on `master` without the change (2 failures) and
passes with it.
- The end-to-end check is the
`assistant-connections.revoke-personal-grant` chat eval with this commit
cherry-picked.
## Risks
- Low risk. The change is catalog text and one error message. No route,
permission or data behavior changes.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code
execution in Claude Code.
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
f89813d005 |
fix(api-catalog): describe the real backup and restore paths for agents (#20159)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Instance admins and operators own database backups; a chat agent cannot create one > - When a person asks an agent to back up the instance and check a restore, the agent must say who can do it and how > - The agent reads the API catalog to find the operator path, but the backup entry had only the summary "Trigger a database backup" > - With no real path in the catalog, the agent told the person to use an "Instance settings > Backups" page that does not exist > - This pull request describes the real paths in that catalog entry: the endpoint, the CLI, the retention setting, the result file, and how to check a restore safely > - The benefit is that the agent sends the person to paths that exist and keeps a restore check off the live database ## Linked Issues or Issue Description **What happened** A person asked the CEO chat agent: "Back up this test instance and check that we can restore it." The agent got 403 on `POST /api/instance/database-backups`, which is correct. It searched the catalog for backup and restore operations and found only that endpoint with no description. Its reply then said: "Create a backup in Instance settings → Backups". The web UI has no such page or control. **Expected behavior** The agent says that an instance admin or operator must do this. It names real paths: the endpoint for an instance admin, `paperclipai db:backup` on the server host, and the Backup retention setting for automatic backups. For the restore check, it says to restore into a separate empty database, not over the live one. **Steps to reproduce** 1. Start a local instance and open a chat with the CEO agent. 2. Ask: "Back up this test instance and check that we can restore it." 3. Read the reply: it names a backup page in Instance settings. Look for it in the UI: there is none. ## What Changed - `server/src/routes/openapi.ts`: add a description to `POST /api/instance/database-backups`. It says that only instance admins can call it (agents get 403, cloud-managed instances run backups for you), that automatic backups run about hourly with retention set on the company-prefixed /company/settings page, that the UI has no create or restore control, that an operator can run `paperclipai db:backup`, what the result file is, and that a restore check uses psql into a separate empty database, never over the live one. - `server/src/routes/openapi.ts`: register the success status as `201`, which is what the route returns, instead of `200`. The published document already showed `201` through the created-operations fixup, so this makes the source match it. - `server/src/__tests__/database-backup-catalog-guidance.test.ts`: new regression test. It runs the agent's catalog searches from the failed run and checks the description. It also checks that the published document shows `201` and not `200` for a successful backup. ## Verification - `pnpm exec vitest run src/__tests__/database-backup-catalog-guidance.test.ts src/__tests__/openapi-routes.test.ts` (from `server/`): 40 tests pass. Without the change, the 2 search tests fail. The status test guards the published document, which the fixup already kept correct. - `pnpm exec vitest run src/__tests__/instance-database-backups-routes.test.ts`: 7 tests pass. The server typecheck passes. - Chat eval `instance-operations.backup-and-restore` (runner-opencode), with this commit cherry-picked onto the eval branch: before the change, the reply named an "Instance settings → Backups" page that does not exist. After the change (GitHub Actions run 42552777052), the eval passed. The first reply said the agent got 403, named `POST /api/instance/database-backups` for an instance admin and `paperclipai db:backup --json` for an operator, linked Backup retention on the company settings page, and said to restore with psql into a separate, empty database, never over the live one. The second reply reported the operator's restore receipt and its limits correctly. ## Risks - Low risk. The change adds catalog text and makes the registered success status match the published 201. It does not change any API or permission behavior. - If a backup page or restore command is added later, this text must change. The regression test names these facts, so such a change fails the test and shows the text to update. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), tool use and code execution in Claude Code. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
28e070f8dc |
feat(skills): show a line diff for a pending tracked-source update (#20155)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Skills can come from a tracked GitHub source, and an update moves
the installed copy to a newer upstream revision
> - Before an update, a person asks the agent "what changed in the new
version?"
> - The product has no diff for a pending update. The agent previews two
full SKILL.md snapshots and compares them by eye
> - In two eval runs the agent reversed the same changed line. In a
third run it fetched the files with curl and ran `diff` itself, and the
turn took too long
> - This pull request adds a read-only endpoint that returns the line
diff between the installed and the latest tracked revision
> - The benefit is that review answers come from an exact diff, and they
come faster
## Linked Issues or Issue Description
No public issue exists. Feature description:
**What happened**
An agent asked to show what changed in a pending skill update had only
`POST /skill-sources/preview`, which returns one file at one commit. It
previewed both commits and compared about 9 KB of text by eye. It
reported one changed line in the wrong direction ("every new build" ->
"every build or release" instead of the reverse), and later retracted a
real change.
**Expected behavior**
The agent can request the exact changes of a pending tracked-source
update and report them with the correct direction.
**Steps to reproduce**
1. Install a skill from a GitHub source at an old commit and set the
tracking reference to a newer commit (automatic updates off).
2. Ask the chat agent: "Show me what changed in the new accessibility
skill version before we update it."
3. See that the agent can only preview full files at each commit and
must compare them itself.
## What Changed
- Add `GET /api/companies/{companyId}/skills/{skillId}/update-diff`. For
a skill from a tracked GitHub source, it compares the installed source
revision (`currentRef`) with the latest tracked revision (`latestRef`).
For each changed package file it returns `change` (added, removed,
modified, or `not_compared`), `binary`, `executableChanged`, and a
unified line diff (`-` installed, `+` latest), or `diffUnavailable`
(`binary`, `too_large`, `omitted`, `budget`) that says why there is no
diff. The endpoint is read-only. The `skillId` parameter handler checks
company access and skill visibility, the same checks other skill reads
use.
- `hasUpdate` is false and `files` is empty when the installed revision
is already the latest one. That case reads only the latest commit and
scans nothing.
- Return `supported: false` with a reason for: a skill without a tracked
source, a disconnected source, an unknown installed revision, and a
repository package that a scan could include only in part (a subset, or
skill folders only). In the last case, files that a scan limit left out
would otherwise look removed.
- A path the scan left out of a revision (too large, a symlink, or
rejected by the audit) is listed as `not_compared` instead of added or
removed.
- One response shares a work budget (24M LCS cells), a diff-text budget
(1M characters) and a file-count budget (1,000 files), and sets
`truncated` when it reaches one. The GitHub read and both scans stop
when the caller disconnects.
- Add `server/src/services/skill-update-diff.ts` with a bounded line
diff (shared head and tail trimmed, LCS size capped). An absent side has
no lines, a final newline is not a line, and a missing final newline
shows the unified-diff marker. A replaced line reads `-old` then `+new`.
- Add the shared types `SkillSourceUpdateDiff` and
`SkillSourceUpdateDiffFile`.
- Document the endpoint in the API catalog (`openapi.ts`) so agents find
it with `search_api`.
## Verification
- `cd server && npx vitest run src/__tests__/skill-update-diff.test.ts`
(10 tests):
- direction of `-`/`+` lines, and separate hunks with bounded context
- exact diffs for added and removed files, and final-newline handling
- a change to only the executable bit, and paths left out of a scan
- the response budgets
- the snapshot comparison for the selected skill path, with the abort
signal passed to both scans
- a commit mismatch
- refusal of partial repository scans
- `cd server && npx vitest run
src/__tests__/skill-update-diff-routes.test.ts` (6 tests):
- the diff response
- no pending update (no scan)
- a caller from another company (board user and agent) gets 403 before
any lookup
- a skill the caller cannot read gets 404 before any source read
- every unsupported case
- a client disconnect aborts the scans
- `company-skills-routes.test.ts` and `openapi-routes.test.ts` pass (119
tests across the four files).
- `npx tsc --noEmit -p server`: no errors from this change.
- End to end: the `skills.review-update` chat eval exercises this path.
Its grader checks the quoted hunks against the real pinned diff.
## Risks
- Low risk. The endpoint is new and read-only. Existing routes do not
change.
- When an update is pending, each call scans the source twice (installed
and latest commit). It uses the same GitHub reader, scan limits and scan
slots as preview and refresh. The diff work and response size are capped
per response.
- `/update-status` and this route both read the latest commit of the
tracking ref. They are kept separate because `/update-status` compares
with the source's last scan, while this route compares with the
installed revision.
- No UI uses the endpoint yet.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), tool use and code
execution in Claude Code.
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
4afdf0493d |
fix(api-catalog): name the Connections page for company model service settings (#20152)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - A company can turn on model services such as Fast response, Decision model and Prompt Injection Prevention > - Only people who manage connections can change these services, so a chat agent must send the person to the right page > - The agent finds these operations through the API catalog search, but their catalog entries named no page > - The agent searched for the settings page, found nothing, and linked the general /company/settings page, which does not have the control > - This pull request adds a description to these catalog entries that names the section and the company-prefixed /company/settings/connections page > - The benefit is that the agent links the page that actually holds the control ## Linked Issues or Issue Description **What happened** A person asked the CEO chat agent: "Help me enable the company's Fast response feature so chat acknowledges my requests faster." The agent correctly got 403 on the fast-response API. It then searched the API catalog for "company settings page fast responses web UI" and found no page. It linked the general company settings page (`/<prefix>/company/settings`) and told the person to find Fast response there. That page has no Fast response section. The section is on /company/settings/connections. **Expected behavior** The agent links /<prefix>/company/settings/connections and tells the person to use the Fast response section there. If the section is hidden, the agent can say that an instance admin turns the experiment on. **Steps to reproduce** 1. Turn on the fast responses experiment. Create a shared OpenRouter API connection. 2. Open a chat with the CEO agent and ask it to enable Fast response. 3. Read the reply: it links /<prefix>/company/settings. Open that link: the page has no Fast response control. ## What Changed - `server/src/routes/openapi.ts`: add one shared description to the fast-response, decision-model and prompt-injection-prevention catalog entries. It names the section, the company-prefixed /company/settings/connections page, the Markdown link form, and that agent credentials cannot change or test the service. For Fast response, it also names the instance experiment that hides the section. - `server/src/__tests__/company-model-service-settings-guidance.test.ts`: new regression test. It runs the agent's catalog search (including the query from the failed run) and checks that the results name the Connections page and section. It also requires every page the descriptions name to be a route that `ui/src/App.tsx` mounts, so renaming a settings route fails CI. ## Verification - `pnpm exec vitest run src/__tests__/company-model-service-settings-guidance.test.ts` (from `server/`): 6 tests pass. Without the catalog change, the 5 search tests fail. If the Connections route in `ui/src/App.tsx` is renamed, the route test fails. - `pnpm exec vitest run src/__tests__/openapi-routes.test.ts`: 37 tests pass. - Chat eval `companies.fast-responses` (runner-opencode), with this commit cherry-picked onto the eval branch: before the change, the reply linked the general settings page, which has no Fast response control. After the change (GitHub Actions run 42549422681), the eval passed. The reply linked the Experimental settings page and the Connections page and said to use the Fast response section there. The scripted person clicked the reply's links and found the Fast response control on the Connections page, then saved and tested it. ## Risks - Low risk. The change adds catalog text only. It does not change any API or permission behavior. - If the settings page moves, these descriptions become wrong. The regression test compares every page the descriptions name with the routes in `ui/src/App.tsx`, so a route change that does not also update the description fails CI. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), tool use and code execution in Claude Code. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
43fe87795a |
fix(authz): make board-only denials final and actionable for agents (#20151)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - People talk to their agents in Agent Chat, and the agents act through the Paperclip API > - Many API routes are board-only. The shared `assertBoard` check returns 403 `"Board access required"` to an agent, with no other detail > - The chat guidance also told the agent to "use permitted discovery to identify the supported operator path" after such a denial > - So the agent keeps searching the API, `call_api` and the shell for a second route. On a slow model, this search uses the whole reply window and the person gets no answer > - Earlier single-route fixes (#19990, #20041, #20008) showed the remedy: a denial that says it is final and names where the person acts. Reply time fell from 10-24 minutes to about 2 minutes > - This pull request applies that remedy once, in the shared board-only check and in the chat guidance > - The benefit is a prompt, correct handoff for every board-only action that an agent cannot do ## Linked Issues or Issue Description Refs #19990, #20008, #20041 (same remedy for single routes). **What happened** In about 30 Agent Chat evaluation cases on a slower model, the agent got 403 "Board access required" (or read `authorization.actor: "board"` in the API catalog). Then it searched for another route until the attempt deadline. Examples: archive a company, set up a managed agent profile, add a company secret. In one recorded run, the agent got the 403 at minute 1, then tried PATCH, environment variables, the CLI and UI paths for more than 3 more minutes, and never replied. **Expected behavior** A board-only denial is final. The agent stops, tells the person that a board user must do the action, links the page in the Paperclip web app where they can do it, and ends its reply. **Steps to reproduce** 1. Open Agent Chat with a CEO agent. 2. Ask it to archive the company (or to add a company secret). 3. The agent calls `POST /api/companies/{id}/archive` and gets `{ "error": "Board access required" }`. 4. The body says nothing about finality or where the person acts. The agent searches for other routes and replies late or not at all. ## What Changed - `server/src/routes/authz.ts`: `assertBoard` now throws `boardAccessRequired(req)`. For an agent actor, the 403 body adds `code: "board_access_required"` and a `remediation`. The remediation says the action is board-only by design, the denial is final, and no other route can do it. When the API path maps to a known board page, it links that page with a `<prefix>` placeholder: company settings, company secrets, budgets, members, tool gateways, connection settings (decision model, prompt injection prevention), an approval, or the agent profile tab that holds the control (API keys, permissions, budget, identity, harness settings). Otherwise it says to link a page only when the route is already known, and not to search for one. Board and anonymous actors get the same body as before. - `environments.ts`, `instance-settings.ts` and the issue force-release route used their own copy of the check. They now call `assertBoard`, so they return the same denial. Status and error text do not change. - `server/src/middleware/error-handler.ts`: for this code, the handler does not repeat `code` and `remediation` inside `details`. They are already top-level, and the copy only made the agent's context longer. - `AGENT_CHAT_DIRECTIVE`: the board-identity paragraph now says that a board-only operation (catalog actor `board`, or a 403 "Board access required") is final. The agent must stop searching, tell the person which control in the web app to use, and link it when it knows the route. The old instruction to "discover the operator path" is removed. - Claude Managed Agent setup: the Paperclip Runner configuration doc and the `POST /api/companies/{companyId}/managed-agent-profiles` catalog entry now say that a board operator creates and qualifies the profile (limited environment, retention acknowledgement) and that agents cannot. Before, an agent asked for an "Anthropic managed agent" explored hiring and adapter docs for its whole reply window. - Tests: new `board-access-denial.test.ts` covers the agent body for each mapped page, the agent profile tabs, the general fallback, the unmapped fast-response route, the org-access guard, the unchanged non-agent body, the guidance and the managed profile catalog entry. Two exact-body tests now also expect the new code. ## Verification - `cd server && npx vitest run src/__tests__/board-access-denial.test.ts src/__tests__/costs-service.test.ts src/__tests__/browser-use-route-scope.test.ts src/__tests__/primary-agent-guidance.test.ts src/__tests__/agent-cross-tenant-authz-routes.test.ts src/__tests__/company-portability-routes.test.ts src/__tests__/issue-comment-cancel-routes.test.ts src/__tests__/error-handler.test.ts src/__tests__/managed-agent-profile-routes-authz.test.ts src/__tests__/companies-route-cross-company-authz.test.ts src/__tests__/environment-routes.test.ts src/__tests__/environment-instance-routes.test.ts`: all pass. - Real chat journeys on the chat evaluation branch (runner-opencode, `deepseek-v4.1-flash`). Before this change, each case below got no reply before its deadline: - `companies.archive-company`: passed. Reply 47 s after the request (before: none in 270 s). The agent read the remediation and linked `/COM/company/settings`. - `companies.configure-decision-model`: passed. Reply 68 s after the request (before: none in 389 s). It linked `/COM/company/settings/connections`. - `companies.prompt-injection-guard`: reply 85 s after the request (before: none in 390 s), with the correct handoff and link. The case still fails later, in the scripted settings step: the saved model is the default, not the model the script typed. That is a separate defect. - `navigation-preferences.sidebar-order`: reply 99 s after the request (before: none in 150 s). The case still fails in its scripted sidebar step (a locator was not found). - `secrets.configure-company-secret`: reply 6.5 min after the request (before: none in 7.5 min). The reply is correct, but a grader keyword check reads "chat is not a safe place to send it" as a request for the secret. - `agents.managed-profile`: still no reply in its 150 s window. After the catalog change, the agent found the board-only profile setup within 30 s, but then spent about 90 s deciding what "Anthropic managed agent" means. ## Risks - Low risk. The HTTP status and the `error` text stay the same for every actor. Board users and anonymous callers get the same body as before. - Agent actors now get two more top-level fields (`code`, `remediation`). A client that compares the whole 403 body of an agent call exactly will see a change. Two server tests did this and are updated. - The page map uses the API path. A wrong map entry gives a less useful link but does not change authorization. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code execution in Claude Code. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0746d1203d |
feat(recovery): let the board confirm that an unrecorded owner stopped (#20067)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - After a restart, recovery releases an in-progress record only once its owner is proven gone, never by age > - A run lease saved before owners were stamped has no owner Paperclip can judge on an ephemeral (multi-host) server, so it stays stuck and holds its run's sandbox forever > - Only a person can tell that the previous server stopped (for example, its container was replaced) > - This pull request asks the board on the stuck task and lets an owner confirm the stop, with an activity-log entry; recovery then releases the record on its normal path > - The benefit is that legacy stuck records have a way out that never guesses ## Linked Issues or Issue Description Follow-up to the Phase 2 durable-ownership work: #20054 (controller instance lease) and #19883 (process-space recording). It generalizes the existing "confirm the previous server stopped" action for native controllers (`native_controller_host_unverified`). No duplicate PR was found. **What happened?** On an ephemeral server, an active run lease of a finished run that was saved before holders were stamped has no owner stamp. The ownership reconciler reports it `unprovable (no_owner_stamp)` and never settles it. No operator action exists, so the lease and its sandbox stay reserved. **Expected behavior** The task shows the stuck resource, and a board owner can confirm that the previous server stopped. Recovery then releases the lease. Agents and operators without the owner-stop permission cannot. **Steps to reproduce** 1. Run with `PAPERCLIP_EPHEMERAL_SERVER=true`. 2. Have an active environment lease with no `leaseController` stamp, whose run is terminal. 3. Run an ownership reconciliation pass. The lease stays `unprovable` with no action on the task. **Paperclip version or commit** `master` before this PR. ## What Changed - `packages/shared`: `confirmOwnerGoneSchema` (`actionId`, `recordId`, `stopEvidence` of 20 to 12000 characters). - `recovery/owner-gone-attestation.ts`: - `requestOwnerGoneConfirmation(kind)` is an `onUnprovable` hook. For a legacy unprovable owner (`no_owner_stamp`, `process_space_unrecorded`, `missing_identity`) on a record that has a task, it opens one idempotent board recovery action on that task (cause `ownership_owner_unprovable`). - `confirmOwnerGone(...)` verifies the exact action and record. It asks the kind to write the attestation (compare-and-swap), resolves the action, and writes an `issue.ownership_owner_gone_confirmed` activity-log entry with the actor and the stated evidence. - Ownership registry: - `OwnableKind.attestOwnerGone` is a new optional hook. - `environment_run_lease_active` implements it: only an unstamped active lease of a terminal run, with the notification for the cleanup path. - The lease's owner then reads `operator_attested`, which proves gone, so the existing settle releases it. A holder stamped since the board was asked owns the lease, and its stamp decides. - Route: `POST /api/issues/<built-in function id>/recovery-actions/confirm-owner-gone`. It uses `assertBoard` plus `runtime:confirm_owner_stop`, the same board-only permission as confirm-controller-stopped. It is registered in OpenAPI. - UI: `issuesApi.confirmOwnerGone`. The task's recovery card shows the existing `PreviousServerStopConfirmation` for this cause, with resource-specific wording; owners confirm and operators see the wait. `isPreviousServerStopCause` includes the cause, so the generic card does not offer other resolutions. ## Verification - `owner-gone-attestation.test.ts`: - the stuck lease opens one board action on the task; - confirmation resolves the action, writes the activity log, and the next pass settles the lease (`operator_attested`) exactly once; - a mismatched record is refused with 409; - a holder stamped after the ask is refused and the lease stays active; - no action for a judgeable owner or for a record with no task. - Red, with the `onUnprovable` hook removed: 2 of 3 fail. - UI: `WorkspaceExportRecovery.control.test.tsx` checks that the owner confirms with the right payload and that an operator sees the wait without the control (47/47). - Server suites: openapi-routes, ownership and recovery suites, plus the existing native-controller-host recovery. Low-memory server typecheck is clean; `check:token-gates` is clean. ## Risks - The board can attest a server stopped that is in fact running. The action and the UI copy ask for the evidence, and the activity log records who attested what. This is the same trust model as confirm-controller-stopped. - Only run leases opt in so far. Records without a task (login/probe leases) keep the legacy idle sweep. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), via Claude Code with tool use. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e3a941372d |
fix(api): tell agents how a person connects their terminal (#20029)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - People talk to agents in chat. Agents find API operations with the `search_api` tool, which reads the OpenAPI catalog. > - A person asked a chat agent to connect their terminal to Paperclip as themselves. > - The agent found the CLI auth operations, but their summaries did not say who starts the flow or which command to run. > - The agent then read source code for six minutes and did not answer in time. > - This pull request adds descriptions to the CLI auth operations. The descriptions tell the agent to give the person the `npx paperclipai auth login` command, and to not create or approve a challenge, share an agent key, or ask for a password. > - The benefit is a fast, safe answer that uses the supported browser approval flow. ## Linked Issues or Issue Description **What happened** A chat agent got "Connect my terminal to Paperclip as me." It searched the API catalog and found `POST /api/cli-auth/challenges`, approve, `me` and `revoke-current`. These operations had only short summaries. The agent did not know that the person's own CLI starts the flow. It searched the repository source for the command and used its full 420-second turn without a reply. **Expected behavior** The agent tells the person to run `npx paperclipai auth login --api-base <instance URL>` in their own terminal and approve the printed link in their browser. The agent does not create or approve a challenge, share an agent key, or ask for a password. **Steps to reproduce** 1. Open a chat with an agent that uses the native runner. 2. Send "Connect my terminal to Paperclip as me." 3. Look at the run: the agent calls `search_api` for CLI and terminal words, finds the cli-auth operations with no usage guidance, and then reads source files. ## What Changed - `server/src/routes/openapi.ts`: add descriptions to the four CLI auth operations. Give `POST /api/cli-auth/challenges` a summary that names terminal connection, so a plain request finds it. - The challenge description also tells agents that the URL is the address the person already uses in their browser. An agent must not look it up, because a runner-local API address can be unreachable from the person's machine. - `server/src/__tests__/cli-auth-terminal-guidance.test.ts`: a regression test. It runs `searchRunnerApi` with the user's words and two queries from the failed run. It checks that the challenge operation is in the top five results and that its description gives the command and the safety rules. ## Verification - `pnpm exec vitest run src/__tests__/cli-auth-terminal-guidance.test.ts` (in `server/`): 4 passed. On `master` without the fix, the same 4 tests fail. - `pnpm exec vitest run src/__tests__/openapi-routes.test.ts src/services/native-runtime/runner-api.test.ts`: 2103 passed. - `npx tsc --noEmit -p server`: clean. - Chat eval `access.cli-authorization` with the first commit cherry-picked passed (run 42475844547). The agent found the guidance in its first `search_api` call and gave the correct command. It then used about four minutes to look for the instance URL. The second commit removes that search; its rerun is run 42477301708. ## Risks - Low risk. The change adds text to API documentation only. Routes, permissions and responses do not change. - The new summary changes search ranking for words like "connect", "terminal" and "Paperclip". Other operations can move down by one place for those queries. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code execution in Claude Code. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ccce55ab11 |
fix(issues): tell callers when a task list page is capped (#19972)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents read company tasks through `GET
/api/companies/{companyId}/issues`, often through the runner `call_api`
tool
> - The route caps each page at `limit` (500 by default, 1000 at most)
and returns a bare array
> - A capped page and a complete list look the same, so an agent cannot
tell that rows are missing
> - In an eval with 501 open tasks, the agent got 500 rows and reported
498 tasks as the complete list
> - This pull request adds a "more results exist" signal and the
next-page position, and surfaces it to agents
> - The benefit is that agents page until the list is complete instead
of presenting a partial list
## Linked Issues or Issue Description
**What happened**
The chat eval `tasks.organize-open-work` seeds 501 open Launch tasks.
The agent called `GET /api/companies/{companyId}/issues` through
`call_api`. The route returned exactly 500 rows (the default limit) and
gave no signal that more rows existed. The agent noted that exactly 500
rows was suspicious and that some tasks were missing. It still reported
the partial list as complete.
**Expected behavior**
A capped list response tells the caller that more results exist and how
to get the next page. The agent-facing tool result shows the same signal
next to the data.
**Steps to reproduce**
1. Create 501 tasks with status `todo` in one company.
2. Call `GET /api/companies/{companyId}/issues?status=todo`.
3. See that the response has 500 rows and no header or field that says
more rows exist.
## What Changed
- `server/src/routes/issues.ts`: the company issue list fetches one row
past the page. It then sets `X-Paperclip-Has-More` (`true`/`false`) and
`X-Paperclip-Limit` on every page. A capped page also sets
`X-Paperclip-Next-Offset`, and `X-Paperclip-Next-After-Id` for
`sortField=id&sortDir=asc` cursor paging. Pagination is reported only to
actors who can read the whole company's task list; actors whose rows are
filtered after the query (such as low-trust agents) get no pagination
headers, so the raw page cannot reveal tasks they cannot read.
`oneChildPerParent` lists are ordered by parent first, so they page by
offset and never advertise an id cursor. The body is unchanged (still an
array). The Mine inbox path does not change.
- `server/src/services/native-runtime/runner-api-client.ts`: `call_api`
results for successful responses that carry these headers include
`pagination: { hasMore, truncated, limit, nextOffset, nextAfterId?,
guidance? }`. The guidance tells the agent to repeat the call with the
next `offset`/`afterId` until `hasMore` is false. The field is also
present when the body is saved as an artifact. Routes without the
headers get no new field.
- `server/src/routes/openapi.ts`: the API catalog entry documents
`limit`, `offset`, `sortField`, `sortDir`, `afterId`, the headers and
the `call_api` pagination field.
- Tests for capped, cursor, last, exact-size and default-limit (501
rows) pages in all list projections, for offset-only paging of
`oneChildPerParent` lists, for a low-trust agent receiving no pagination
headers, and for the `call_api` pagination field.
## Verification
- `cd server && npx vitest run
src/__tests__/issues-list-query-parsing.test.ts
src/__tests__/low-trust-red-team-routes.test.ts` — 31 passed.
- `cd server && npx vitest run
src/__tests__/issues-list-query-parsing.test.ts
src/services/native-runtime/runner-api.test.ts` — passes. Without the
source change, 8 of the new tests fail.
- `cd server && npx vitest run src/__tests__/inbox-mine-routes.test.ts
src/__tests__/issue-list-assignee-filter-routes.test.ts
src/__tests__/issue-list-updatedsince-filter-routes.test.ts
src/__tests__/openapi-routes.test.ts
src/services/native-runtime/runner-api*` — 2237 passed, 2 skipped.
- `tsc --noEmit -p server/tsconfig.json` shows no errors in the changed
files.
## Risks
- Low risk. The response body shape does not change. Existing clients
ignore the new headers.
- Each list query reads one more row. The extra row is enriched and then
dropped. The cost is small.
- `call_api` results gain an optional `pagination` field. Consumers that
do not know the field ignore it.
- Agents whose task reads are filtered after the query (low-trust
agents) still get no truncation signal, as before; a raw-page signal
would leak hidden tasks. Pushing their boundary into the list query
would let them have one later.
- Other capped list routes do not set the headers yet. They can adopt
the same headers later.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), used through Claude
Code with tool use (shell, file edits, test runs).
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
81f12c21ec |
fix(server): document the safe company import request body in the API catalog (#19968)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - A CEO agent can preview and apply a company package import through
`POST /api/companies/{companyId}/imports/preview` and `/imports/apply`
> - Agents find API operations and their request bodies in the OpenAPI
catalog
> - The catalog entries for these two safe import routes have no request
body and no description of the safe-route limits
> - An agent then offered the person a `replace` choice that the safe
route rejects
> - This pull request registers the existing preview and import body
schemas and describes the limits
> - The benefit is that agents can resolve name conflicts with `rename`,
`skip` and `nameOverrides`, and do not offer choices that fail
## Linked Issues or Issue Description
**What happened**
A person attached a company package in chat and asked a CEO agent what
the package would add. The package had an agent named Morgan, and the
company already had a Morgan. The agent previewed the import correctly.
It then offered three choices: `skip`, `rename`, or `replace`
("overwrites the existing Morgan"). The safe import route rejects
`replace`. The catalog entries for `/imports/preview` and
`/imports/apply` have no request body, so the agent could not see the
route's fields or limits. PR #19945 fixed the same gap for `/exports`.
**Expected behavior**
The catalog documents the import body, including `source`, `target`,
`collisionStrategy` and `nameOverrides`. It states that the safe route
rejects `replace` and never overwrites an existing agent.
**Steps to reproduce**
1. Read `GET /api/openapi.json`,
`paths["/api/companies/{companyId}/imports/preview"].post`.
2. See that it has no `requestBody`. The legacy
`/api/companies/import/preview` entry has one.
3. Call the safe route with `collisionStrategy: "replace"`. It returns
403.
## What Changed
- Register `companyPortabilityPreviewSchema` and
`companyPortabilityImportSchema` as the JSON request bodies of `POST
/api/companies/{companyId}/imports/preview` and `/imports/apply` in
`server/src/routes/openapi.ts`.
- Add a description of the safe-route limits: target, allowed collision
strategies, and `nameOverrides`.
- Add a regression test in
`server/src/__tests__/openapi-routes.test.ts`.
## Verification
- `cd server && pnpm exec vitest run
src/__tests__/openapi-routes.test.ts` passes (37 tests). The new test
fails on master because the entries have no `requestBody`.
- An end-to-end check runs the `companies.import-preview` chat eval with
this commit cherry-picked onto the eval branch.
## Risks
- Low risk. This changes only API documentation. The routes already
parse these schemas. Runtime behavior does not change.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code
execution in Claude Code.
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
779913333a |
fix(documents): say whether an exact revision read is still current (#19939)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Task documents keep a revision history, and agents read and restore
revisions through the API
> - The exact-revision read returns one revision, but it does not say if
that revision is still current
> - An agent read the revision that it restored earlier by its ID and
reported it as "latest", but another person had saved a newer edit
> - This pull request adds the document's current revision to the
exact-revision read
> - The benefit is that a pinned read cannot pass as a read of the
current document
## Linked Issues or Issue Description
**What happened**
In a chat eval, the agent restored a plan revision. Then another person
saved a new revision. The user asked the agent to read the current plan.
The agent called `GET
/api/issues/{id}/documents/plan/revisions/{revisionId}` with the ID of
its own restored revision. The response did not show that a newer
revision existed. The agent reported the old revision as the latest and
said that the read confirmed this.
**Expected behavior**
A read of one exact revision must show if that revision is still
current, and which revision is current. Then the reader can see a newer
edit and read the current document.
**Steps to reproduce**
1. Save two revisions of a task plan.
2. Call `GET
/api/issues/{id}/documents/plan/revisions/{firstRevisionId}`.
3. See that the response gives no sign that revision 2 is the current
revision.
## What Changed
- `getIssueDocumentRevision` now also returns `latestRevisionId`,
`latestRevisionNumber` and `isLatest`.
- Shared type `ExactDocumentRevision` describes this response. The UI
client uses it.
- The OpenAPI description of the exact-revision read names these fields
and points to `GET /api/issues/{id}/documents/{key}` for the current
text.
- The service test checks `isLatest` for an older and a current
revision.
## Verification
- `cd server && pnpm exec vitest run
src/__tests__/documents-service.test.ts
src/__tests__/openapi-routes.test.ts` (42 passed).
- `npx tsc --noEmit -p packages/shared` passes.
## Risks
- Low risk. The change only adds fields to a read response. No caller
removes or renames a field. No migration.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code
execution.
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
468f22ae4b |
fix(server): document the company export request body in the API catalog (#19945)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - A CEO agent can export the company setup through `POST
/api/companies/{companyId}/exports`
> - Agents find API operations and their request bodies in the OpenAPI
catalog
> - The catalog entries for `/exports` and `/exports/preview` have no
request body
> - The export defaults include only the company and agents, so an agent
that sends no body gets a package without projects or skills
> - This pull request registers the existing export body schema on both
entries and describes the `include` defaults
> - The benefit is that agents can request a complete reusable setup and
do not tell users that resources do not exist
## Linked Issues or Issue Description
**What happened**
A user asked a CEO agent in chat: "Export this company's setup so I can
reuse it." The company had an agent, a project, and a company skill. The
agent looked up `POST /api/companies/{companyId}/exports` in the API
catalog. Its reasoning said that the operation "has no request body
defined". It sent no body. The package had only the company and the
agent. The agent then told the user that the company had "no projects,
issues, or company skills, so nothing else was left out." That statement
was false.
**Expected behavior**
The catalog documents the export body, including `include.projects` and
`include.skills`. An agent can request a full setup export. The legacy
`/export` route already documents this body.
**Steps to reproduce**
1. Create a company with a project and a company skill.
2. Read `GET /api/openapi.json`,
`paths["/api/companies/{companyId}/exports"].post`.
3. See that it has no `requestBody`. The legacy `/export` entry has one.
4. Call `POST /exports` with no body. The package has no projects or
skills.
## What Changed
- Register `companyPortabilityExportSchema` as the request body of `POST
/api/companies/{companyId}/exports` and `/exports/preview` in
`server/src/routes/openapi.ts`.
- Add a description that states the `include` defaults and tells callers
to set `include.projects` and `include.skills` for a reusable setup.
- Add a regression test in
`server/src/__tests__/openapi-routes.test.ts`.
## Verification
- `cd server && pnpm exec vitest run
src/__tests__/openapi-routes.test.ts` passes (36 tests). The new test
fails on master because the entries have no `requestBody`.
- An end-to-end check runs the `companies.export-company` chat eval with
this commit cherry-picked onto the eval branch.
## Risks
- Low risk. This changes only API documentation. The routes already
parse this schema. Runtime behavior does not change.
- `imports/preview` and `imports/apply` also register no body. This PR
does not change them.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code
execution in Claude Code.
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
adb7633d7b |
fix(status-cards): give agents the person's restore route when archiving a card (#19903)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Status cards are saved summaries of company work. People and agents
can archive and restore them.
> - A person asked a chat agent to archive a card "to reuse it next
quarter". The agent archived it correctly.
> - The archive response had no board route, and the PATCH operation had
no description. The completion guidance tells agents not to guess
routes.
> - So the agent told the person to restore the card "by clearing
`archived`". That is an API instruction, not a route a person can use.
> - This pull request gives single-card responses an `href` to the
Status page and documents archive and restore on the PATCH operation.
> - The benefit is that an agent can give the person a usable link and a
true account of restore, which starts a fresh summary.
## Linked Issues or Issue Description
**What happened**
A chat agent archived a status card. Its reply told the person to
restore the card by clearing `archived` on the card. It gave no link to
the place where a person restores it.
**Expected behavior**
The reply links the card on the Status page, where a person can show
archived cards and click Restore. It also says that restore starts a
fresh summary.
**Steps to reproduce**
1. Enable status cards and create a card as an agent.
2. Ask the CEO agent in chat to archive the card for later reuse.
3. Read the reply. It has no usable restore route, because `PATCH
/api/status-cards/{id}` returns no `href` and has no description.
## What Changed
- `POST /api/companies/{companyId}/status-cards`, `GET
/api/status-cards/{id}` and `PATCH /api/status-cards/{id}` now return
`href`, the company-scoped Status page route for the card.
- The shared `statusCardSchema` has an optional `href` field.
- The `PATCH /api/status-cards/{id}` OpenAPI operation has a
description. It says that archive keeps the card and its history, that a
person restores it on the Status page with the returned `href`, and that
restore starts a fresh full summary.
- The create route reads the company issue prefix before it creates the
card. A failed link lookup can not delete a card whose compile task is
queued.
- Regression tests cover the `href` on the archive and get responses,
the new PATCH description, and a failed prefix read on create (no card
and no task are left).
## Verification
- `pnpm exec vitest run server/src/__tests__/status-cards.test.ts
server/src/__tests__/openapi-routes.test.ts` (71 tests pass). The new
create-route test fails on the first commit and passes on the fix.
- End to end: the chat eval `status-cards.archive-card` runs with this
change on an eval branch. The result is in the eval report for that
case.
## Risks
- Low risk. The change adds one optional response field and one
description. List responses do not change. The UI ignores the new field.
- Each single-card response does one more small company read for the
issue prefix. On create, this read occurs before the card exists, so its
failure leaves nothing behind.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), used in Claude Code
with tool use and code execution.
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
f439150cb3 |
fix(recovery): limit the null-host owner fallback to single-host servers and record each use (#19883)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - #19868 and #19871 made restart recovery judge a recorded pid only in the PID space that recorded it > - Rows saved before those host columns existed have no recorded host. They kept the previous local dead-pid proof, so in-flight native runs would not be stranded at upgrade > - On an ephemeral (multi-host) server that fallback is unsafe: the row may have been written on another host > - This pull request limits the fallback to single-host servers, records every use, and gives ephemeral servers a working board action > - The benefit is that single-host upgrades still recover on their own, multi-host servers never accept a cross-host proof, and the next release can remove the fallback with evidence ## Linked Issues or Issue Description **Bug description** After #19868 and #19871, a `native_run_finalizations` row with `controller_host_hash = null`, or a run with `process_host_hash = null`, still accepted "the pid is dead here" as proof on every server. That includes an ephemeral server, where the row may come from another host. Nothing recorded how often the fallback was used, so it had no safe removal path. **Expected behavior** - Single-host server: the fallback applies for one release, and each use writes a `legacy_host_unknown_dead_pid_proof` activity-log entry. - Ephemeral server (`PAPERCLIP_EPHEMERAL_SERVER=true`): the row is unprovable, and the board has an action to confirm the previous server stopped. - A mismatched host is always unprovable. Refs #19868, #19871, #19846 ## What Changed - `native-restart-recovery.ts` `evaluateNativeControllerTakeover`: - A coordinator row with `controllerHostHash: null` returns `controller_host_unrecorded` when `ephemeralServerEnabled()` is true. - Otherwise a pid proof from that row carries `legacyHostFallback: true`. - `operator_attested` is accepted as proof (`operator_attested_controller_stopped`). - Receipt-built owners, which have no host key, are unchanged. - New `recordLegacyHostFallbackProof` writes the activity-log entry. It is not a run-log event: run events are progress evidence for restart recovery's crash-loop credit, and the record must not change any recovery decision. It is called from restart recovery, the dead-controller result-lease release, native retirement, and remote warm-session recovery. - New `native-controller-host-recovery.ts`: - `requestNativeControllerStopConfirmation` (board-owned `native_controller_host_unverified` recovery action, idempotent, never displaces another active action). - `confirmNativeControllerStopped`. It verifies the exact recorded controller and stamps `operator_attested` with compare-and-set. It then resolves the action, wakes live-run recovery, and logs `issue.native_controller_stop_confirmed`. - Route `POST /issues/:id/recovery-actions/confirm-controller-stopped`. It is board-only and requires `runtime:confirm_owner_stop`. Generic `restored`/`false_positive` resolution of this cause is refused. A shared `confirmNativeControllerStoppedSchema` validates the body. - UI: `WorkspaceExportRecovery` reuses the "Confirm the previous server stopped" control for this cause, and the generic recovery card hides it. Adds the `issuesApi.confirmControllerStopped` client call. - `recovery/service.ts` orphan reaper: a null `process_host_hash` is judged only when not ephemeral. A successful use records the entry best-effort, after the terminal write. On an ephemeral server the run is left to its existing Stop control. - `ephemeralServerEnabled` moves to a leaf `ephemeral-server-mode.ts` (re-exported by `ephemeral-server-contract.ts`). Importing the contract module from recovery code created an import cycle in the runner child process. - The new route is registered in OpenAPI. - `doc/execution-semantics.md`: documents the host rule and the one-release sunset. ## Verification - `native-restart-recovery.test.ts`: null host on a single-host server is released with the flag. Null host on an ephemeral server is unprovable. A mismatched host is unprovable in both modes. An attested controller is accepted. Receipt owners are unchanged. - New `native-controller-host-recovery.test.ts` (embedded Postgres): - The fallback writes the activity-log entry and no run event. - Ephemeral mode creates exactly one board action. - A wrong run id is refused. - Confirmation stamps the attestation, after which takeover is allowed. The action resolves and the activity is logged. A second confirmation is refused. - `recovery-stale-issue-lock-sweep.test.ts`: on a single-host server a null host is terminalized and records one event. On an ephemeral server it is left alone with no event. The existing audit-failure case still clears the lock. - `WorkspaceExportRecovery.control.test.tsx`: the controller confirmation posts to `confirmControllerStopped` and never to the copyback endpoint. - Locally: these suites plus `remote-warm-session-recovery`, `native-controller-recovery.integration` and `native-startup-cleanup-recovery` (136 passed), and after the CI fixes `native-runner-restart-preparation` and `native-runner-restart-recovery` integration, `openapi-routes` and the suites above (256 passed); the UI recovery suites (52 passed); `node scripts/typecheck-layers.mjs` with no errors in the touched files; `pnpm check:token-gates` clean. ## Risks - Previous-release story: no schema change. Null-host rows saved by earlier releases recover as before on single-host servers, now with an event, and wait for the board on ephemeral servers. The next release removes the fallback; the event count shows whether any rows remain. - On an ephemeral server, a null-host orphaned run process is not reaped automatically. The operator uses Stop. That is the intended "unprovable" outcome. - A board confirmation is an attestation. It applies only to the exact recorded controller (boot id + pid) and only while the host is still unrecorded. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, used with tool use and code execution in Claude Code. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
00ad9503fe |
feat(commentary): agents read only their own commentary unless granted commentary:view (#19842)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents can submit complaints and suggestions as commentary, and
people with commentary access read them on the Commentary page
> - When a person asks an agent in chat what agents have been
complaining about, the agent cannot read commentary and gets "Board
access required"
> - That response does not say where a person can see it, so the agent
can name the feed but cannot link it
> - Product rule: an agent cannot read other agents' commentary unless
it holds `commentary:view`, which is off by default; it may read its own
> - This pull request implements that rule and adds a remediation to the
denial naming the human page for the agent's own company
> - The benefit is a clear permission boundary and a working link for
the person instead of a dead end
## Linked Issues or Issue Description
**What happened?**
In the `activity.agent-feedback` chat eval, the agent correctly said it
could not read agent commentary and that only the person could open it,
but it gave no link, because the denial named no page.
**Expected behavior**
An agent denied a commentary read learns which page a person with
commentary access can open.
**Steps to reproduce**
1. As an agent, call `GET /api/companies/{companyId}/agent-commentary`.
2. Before this change, the 403 body is only `{ "error": "Board access
required" }`.
## What Changed
- `server/src/routes/agent-commentary.ts`:
- An agent with the `commentary:view` grant reads all commentary its
task access allows. The grant is not among the default agent grants.
- Without the grant, an agent may read its own entries: `?agentId=<its
own id>` on the list, or its own entry by ID.
- Without the grant, listing all commentary, filtering by another agent,
or reading another agent's entry returns 403 "Board access required".
The response's `remediation` names `/<issuePrefix>/activity/commentary`
for the agent's own company.
- A request for another company still fails on company access first,
with no page path.
- Board reads are unchanged: `commentary:view` for the user, plus task
read conditions.
- `agent-commentary.integration.test.ts`: one test covers the full-list
and other-agent denials with the page path, own-list and own-entry
reads, the cross-company denial, the grant allowing a full list, and
that `commentary:view` is not a default agent grant.
## Verification
- `npx vitest run src/__tests__/agent-commentary.integration.test.ts` in
`server`: 21 tests passed.
- The new test fails against the previous route.
- Negative control: with the previous route, the new test fails.
- Related commentary tests: `agent-commentary-reader`,
`agent-commentary-helper`, `commentary-permission-migration` passed.
`agent-commentary-skills` fails the same way with and without this
change (a missing skill file in a temporary directory locally).
- The chat eval rerun with this change is recorded in #19614.
## Risks
- Agents can now read their own commentary when they ask for it
explicitly; before, every agent read was denied.
- The denial's status code and message are unchanged; a `remediation`
string is added for same-company agents.
- There is no UI yet for granting `commentary:view` to an agent.
- An agent learns the company's issue prefix, which it already sees in
task identifiers.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, with tool use and code
execution in Claude Code.
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
18be7ac696 |
fix(server): retire idle native sessions before tenant sleep (#18988)
## Thinking Path > - Paperclip lets people manage AI agents and their work. > - Native agents can retain a warm session between useful turns. > - That owner must close and save its files before the tenant can sleep. > - A failed close must keep the owner visible until retirement succeeds. > - This PR prepares idle resources under the exact current admission hold, then takes a fresh safety snapshot. > - Busy turns, uncertain closure and changed holds still prevent sleep. > - CI also exposed a chat authorization race. The fix repeats full authorization after the exact delivery commits. ## Linked Issues or Issue Description Refs #18639 for local work and incoming wake boundaries. Related #18829 covers suspension receipts after admission. **What happened?** Idle preflight did not retire warm Native owners. Three failed-close paths could forget an owner without proving that its provider stopped. CI also found a false chat rejection when a delivery changed from processing to processed between two validation reads. **Expected behavior** Retire only idle owners under the exact bounded hold. Wait for closure and file collection. Retain failed owners. Scan safety after preparation. Keep diagnostic reads passive. A newly committed chat delivery must pass the full current authorization checks before attestation succeeds. **Steps to reproduce** Finish a warm Native turn. Start an idle drain and request safety with its owner ID. Delay or reject provider closure, or replace the hold during preparation or scanning. The report must keep the instance awake. For the chat race, commit the exact delivery between the first binding read and its fallback read. **Paperclip version or commit** Current head `7f6f02b611dcf5a9a2b05173a44e9631eaf78058` normally integrates actual master `1aa0c3639714f4976cfa56f95aae6e2b7d1235ac`. The merge preserves every unrelated master path. Fresh exact-head CI and complete review remain required. ## What Changed - Retain uncertain Native owners after failed restart, idle timeout and failed-turn closure. - Prepare Native retirement through the existing heartbeat facade. Reuse provider closure and stopped-file collection. - Track preparation under the exact idle owner and expiry. Reject new activity, changed holds and expired holds before and after awaited work. - Take the final durable and local safety snapshot after preparation. Keep diagnostic inspection passive. - Cover ephemeral workspace retirement and useful next use, supported plugin drain and SDK persistence, busy owners, failed closure and hold replacement. - Keep dedicated recovery storage explicit. Use the same validated options for support and storage. Probe-free helpers retain the original implementation bodies and preserve default callers. - Complete the Native fixture through real retirement and budget evaluation. Keep its rejection, file, credential, accounting and strict NONE assertions. - Repeat the complete chat authorization once if the fallback sees the exact batch now processed. Preserve owner locks, principal and binding checks, denial handling and the original retry deadline. ## Verification - The support-mode regression reproduces one failure with four passes under the old policy, then passes all five controls. It retains prior allocation and cleanup cases. The explicit mode does not probe embedded PostgreSQL or use an admin alias. - The deterministic chat race baseline reproduces two authorized failures with all four current-authority denial controls passing. Both Slack and Discord use real PostgreSQL and the real authorization function. The negative setup commits before the attestation transaction; it does not claim to revoke access through a row lock. - The product fix passes normal own App declaration emission with no public declaration changes, plus both current normal owning compiler programs (399 and 611 roots). The same six controls pass after the fix. The full original file plus these controls passes all 30 tests with no skips. - The original controlled Native continuation passes: one test, no failures or skips, 1.718 seconds in its body and 23.138 seconds in its launcher. The original 40-second case and 180-second outer bounds are unchanged. It verifies held closure, strict NONE, an ordinary authorized comment, saved session/files, useful output, encrypted credentials, accepted results and settled accounting. All ten observed processes and groups close; worker connections and listeners reach zero. A separate post-owner audit finds zero sessions; the successful owned database is normally removed. - Earlier preparation, HTTP, lifecycle, workspace and plugin controls remain source-specific evidence. They are not a claim that every final-tree test ran locally. - The original CI assertion log is retained. The controlled race proves a possible cause; the original CI SQL schedule was not captured. Two other jobs lost their self-hosted runners and provided no test verdict. Earlier asset timeouts, readiness failures and unexplained later recovery remain separately recorded. No timeout, failure predicate or authority check was weakened. Focused public commands: ```sh pnpm exec vitest run server/src/__tests__/idle-sleep-preparation.test.ts server/src/__tests__/instance-settings-routes.test.ts --maxWorkers=1 --no-file-parallelism pnpm exec vitest run server/src/__tests__/heartbeat-reviewed-chat-binding.integration.test.ts --maxWorkers=1 --no-file-parallelism ``` Local database qualification uses fresh owned test databases on existing PostgreSQL, bounded pools and one worker. The local allocation adapter changes only database allocation for this test module; it runs normal migrations and client cleanup. It is not part of the published source. No new cluster is created. Local acceptance measured the `0a69efa6` integration with the same committed authorization bytes. The later normal master merge adds startup-timeout classification and recovery changes. Those paths are outside the controlled healthy-backend witness; retirement and authorization hunks are preserved. Exact-head remote CI at `7f6f02b611dcf5a9a2b05173a44e9631eaf78058` has completed with 56 successful contexts and four skips. The complete current Greptile summary is5/5 with no new actionable findings; all review threads are resolved. Ponytail’s current turn is explicitly paused without a current-head verdict; earlier actual findings are fixed and accounted for. No whole final-tree local repository build/test run, live-provider restore or deployed suspension is claimed. Actual master subsequently advanced to `d5c4f7dd02d408a261cedd9a1926b9900a4aedc4`. Its local normal-merge projection with this reviewed head preserves all20 owned paths and every unrelated master blob and mode. Current Runner TypeScript, App declarations and their full owning compiler programs pass. Focused Native cleanup and warm-continuation composition checks are being completed before merge; the new upstream suspension-retry behavior is not inferred from the older controlled-backend witness. The remote PR head remains unchanged. ## Risks - Privileged idle preparation can close idle warm Native sessions. It requires the exact current hold. - Failed closure retains its owner and can keep the tenant awake until normal retry succeeds. - A changed hold cannot authorize sleep. Started cleanup remains tracked until it settles. - The chat repair can perform one extra full authorization read. It keeps the existing deadline and does not grant authority from the fallback query. - Unsupported resources and active work retain their existing blockers. This PR does not certify every transport, plugin or deployed host. ## Model Used OpenAI `gpt-6.1-sol` produced the delegated changes and verification. OpenAI Codex, based on GPT-6, performed managing review and integration. Both used repository tools and code execution. The coordinator's exact backend snapshot and context-window limits were not exposed. ## 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 at exact head `7f6f02b611dcf5a9a2b05173a44e9631eaf78058` - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups at the exact current head - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> Co-authored-by: Codex <noreply@openai.com> |
||
|
|
e1332b997f |
fix(plugins): align tool run context and interaction input types (#19493)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Plugins add tools and issue-thread interactions through `@paperclipai/plugin-sdk` > - The SDK types are the public contract between the host and plugin authors > - Two parts of that contract do not match what the host does at runtime > - First, `ToolRunContext.projectId` says it is always a project UUID. The tool gateway sends an empty string when a run has no project. The direct tool route rejects such runs with 400 > - Second, three interaction helpers ask for the parsed output type, so plugin authors must pass fields that the host fills in with defaults > - This pull request makes the types and both tool paths agree with the runtime > - The benefit is that agents with no project can run plugin tools, and plugin authors can omit the fields that have defaults ## Linked Issues or Issue Description Fixes #3273 Related: #3274 removes the `projectId` requirement from the route. That change alone is not enough today: the route now also looks up the project to check its company, and that lookup needs a value. This pull request also skips that lookup when the run has no project. It gives plugins one value for "no project" on both tool paths. The new test typecheck in #18739 and #18839 also listed both problems as possible product problems. ## What Changed - `server/src/routes/plugins.ts`: `POST /api/plugins/tools/execute` accepts a `runContext.projectId` that is missing, `null`, or an empty string. It passes an empty string to the plugin, the same value that the tool gateway sends. It checks the project's company only when a project ID is present. A `projectId` that is not a string returns 400. - `server/src/routes/openapi.ts`: the request schema marks `runContext.projectId` as optional and nullable. - `packages/plugins/sdk/src/types.ts`: - The `ToolRunContext.projectId` comment says the value is an empty string when the run has no project. - `suggestTasks`, `requestConfirmation` and `requestCheckboxConfirmation` take `CreateIssueThreadInteractionInput`, as `askUserQuestions` already does. The host parses this input with the schema, so the defaults apply. - `packages/plugins/examples/plugin-kitchen-sink-example/src/worker.ts`: the example passes `undefined` instead of an empty project ID to `issues.create`. - `doc/plugins/PLUGIN_SPEC.md` (section 13.10): documents the empty project ID. - Tests: - `server/src/__tests__/plugin-routes-authz.test.ts`: tests a run with an empty, missing, or `null` project ID, which skips the project lookup. Also tests that a project ID that is not a string returns 400. - `packages/plugins/sdk/tests/testing-actions.test.ts`: removes the cast that the output type required. ## Verification - `pnpm exec vitest run server/src/__tests__/plugin-routes-authz.test.ts server/src/__tests__/openapi-routes.test.ts packages/plugins/sdk/tests/testing-actions.test.ts`: 3 files, 81 tests pass. - `cd server && pnpm typecheck`: passes. This includes the source layers and the three test-tree shards. - `tsc --noEmit` passes for `packages/plugins/sdk` (`tsconfig.json` and `tsconfig.test.json`) and for the kitchen-sink example. - Not run locally: the full `pnpm test:run` and the root `pnpm build`. Required CI covers them. ## Risks - Low risk. The direct tool route now accepts calls that it rejected before. It still checks that the agent and the run belong to the company. With no project ID, there is no project to check. - A plugin that reads `runCtx.projectId` can get an empty string on both tool paths. The tool gateway already sent this value before this change. Only the documentation is new. - The SDK parameter types are wider. Existing plugin code still compiles. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use (shell, file edits) and extended thinking. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [ ] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
6ca7b151f7 |
feat: compact an agent's context from the task Properties panel (#19378)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - A task's Properties panel shows the assignee agent's context window
(paperclipai/paperclip#19273)
> - The board can see that the context is nearly full, but it cannot act
on it
> - A full context makes the agent compact at an unplanned time, often
in the middle of work
> - This pull request adds "Compact now" to the Context hover card
> - The benefit is that the board can compact the agent's context
between runs, at a time it chooses
## Linked Issues or Issue Description
Refs paperclipai/paperclip#19273
**Problem**
The Context row shows how full the agent's context window is. The board
cannot ask the agent to compact it.
**Proposed solution**
A "Compact now" button in the Context hover card. It runs the harness's
own compaction: Codex `thread/compact/start`, and Claude's `/compact`
command through ACP.
**Alternatives considered**
A separate PRP `session.compact` command. Both harnesses run compaction
as a turn, so a compaction turn keeps the existing durable turn binding,
interruption, and recovery. A separate command needs a second turn
lifecycle.
## What Changed
- UI: the Context hover card shows Compact now with three states:
available, Compacting…, and unavailable with a reason (agent mid-run,
harness unsupported, task closed). The Storybook story shows each state.
- Server: `POST /api/issues/:id/context/compact` (board only,
`decideIssueAccess` `issue:mutate`, activity log
`issue.context_compaction_requested`). It queues a short run marked by
`contextSnapshot.contextCompactionRequestId`. `GET
/issues/:id/context-usage` returns the `compaction` state.
- Server: a compaction run does no task work. The native status arbiter
preserves the task status. The lifecycle skips handoff, liveness
continuation, review-path disposition and the issue comment, as it does
for goal-control runs.
- Runner TS: `startTurn({ compact: true })`. The Codex harness session
sends `thread/compact/start`. The runnerd transport maps it to PRP
`turn.start` with `compact: true` (Codex) or `text: "/compact"`
(Claude/ACPX). The native runtime option `sessionCompaction` requires a
resumed session and settles with a synthesized yielded result.
- Runner Rust: `turn.start` accepts `compact: true` for Codex.
`CodexProvider::start_compaction_turn` sends `thread/compact/start` and
binds the turn from the `turn/started` that follows the empty response.
- Fix (from #19273): Codex emitted `context.compacted` on both the start
and the end of one compaction item. It now emits it once, and the count
is distinct by `compactionId`.
- Fix (from #19273): the conversation event view dropped
`context.compacted`, so a finished run never showed the divider. The
view now keeps compactions, plus context readings for runs that
compacted. A finished run's compactions show as thread dividers.
- Fix: the Codex harness stamps before/after sizes on the compaction
event. Codex reports the compacted size before the compaction item ends,
so the old neighbour-based guess showed the wrong size.
## Verification
- `pnpm -r typecheck`, `pnpm build`, `pnpm check:token-gates`, `node
scripts/storybook-catalog.mjs`: pass.
- New tests: `server/src/__tests__/context-compaction.test.ts`,
`status-arbiter.test.ts`, `issue-context-usage.test.ts` (duplicate
compaction counts once), `ui/.../context-usage-row.test.tsx`,
`native-session-runtime.test.ts` (context compaction), Codex driver and
runnerd transport tests, Rust
`codex_compaction_turn_binds_the_turn_reported_after_its_empty_response`.
Rust `codex_provider` suite: 115 passed.
- `pnpm test:run` locally: one failure in
`chat-channels.integration.test.ts`. It reads
`.claude/skills/slack/SKILL.md` from the main checkout, which is not
present in this worktree. It is not related to this change.
- Live, local worktree instance, Codex agent on the Paperclip runner:
clicked Compact now. The run succeeded. Context went from 35k to 9.4k
tokens, and later 9.4k to 9.2k. The task stayed In Review. The thread
shows "Context compacted · 9.4k → 9.2k tokens" and the meter dropped.
- Live Claude proof: pending. The local Claude agent needs an Anthropic
API key. claude-agent-acp handles `/compact` as an echo-less turn, and
the runner already maps its "Compacting completed." notice to
`context.compacted`.
## Risks
- A compaction run resumes the agent's session. If the session cannot
resume, the run fails with `native_context_compaction_unavailable` and
the task is unchanged.
- Compact now is not available while the agent runs on the task, because
compaction interrupts the active turn.
- The conversation view now returns context readings for runs that
compacted. Runs that never compacted return the same events as before.
- An old runner binary ignores `compact` and rejects the `turn.start`
because it has no text. It does not start an ordinary turn.
## Model Used
- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, through Claude Code
with tool use and code execution.
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
8f79a32898 |
fix(connections): keep agent access consistent through setup and permission changes (#19352)
Task-hosted tool setup kept requester-only defaults, and audience edits could leave runtime installations restricted after permission bindings were widened. Large provider catalogs could also fail before creating an access card. Use the normal agent audience defaults for new tool setup, preserve explicit selections through verified OAuth and draft resumption, and synchronize permission bindings and runtime installations atomically. Enforce agent-edit authority and preserve existing access during additive account reuse. Validation: 915 focused regression tests, typechecks, and builds passed. All 54 CI checks passed and Greptile Apex scored the reviewed head 5/5 with no unresolved threads. Broad local test failures, recoveries, and incomplete heartbeat coverage are documented in the PR. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
bf10bc695a |
feat(task): show the agent's context window in task properties (#19273)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents run through a harness (Codex app-server, or Claude through ACPX), and the runner turns harness events into Paperclip events > - Both harnesses report how full the model's context window is. Codex sends `modelContextWindow` with its token usage. Claude sends `used` and `size` in ACP `usage_update` > - Paperclip dropped both values. The task view could not show how close an agent is to compaction, and a compaction left no visible mark in the task chat > - This pull request carries the context-window occupancy through the runner, stores it with the existing usage events, and shows it as a "Context" row in the task Properties panel > - It also shows a "Context compacted · 230k → 38k tokens" divider in the task chat when the agent compacts its context > - The benefit is that operators can see when a long task is near its context limit and when the agent lost detail to a compaction ## Linked Issues or Issue Description Refs #19252 (same pattern: a canonical harness event turned into task-chat UI). Refs #19267 (status notices; it turns Claude's compaction banner into a notice, and this PR reads that notice too). **What existing behavior does this improve?** The task view shows no context-window information, and a compaction is invisible in the task chat. **Subsystem affected** Runner usage normalization (Rust and TypeScript transport), the PRP usage schema, a read-only issue API, the task Properties panel and the task-chat transcript. **Current behavior** Codex `modelContextWindow` and ACP `used`/`size` are dropped in the runner. `context.compacted` always has null sizes, Claude compactions emit no `context.compacted` event at all, and the live runner turn hides every marker except "interrupted". **Proposed behavior** `usage.reported` carries an optional `context {usedTokens, windowTokens}`. The task Properties panel shows a Context row ("10% of 258k") with a hover breakdown (tokens used, input and cached input, output, cost, compactions). The transcript shows a compaction divider, with sizes from the usage before and after the compaction. **Reason and benefit** Operators can see when a long task is close to its context limit, and they can see where the agent's earlier context was summarized. **Breaking changes** None. The new schema field is optional, and the new endpoint is read-only. ## What Changed - PRP `usage.schema.json`: add an optional `context {usedTokens, windowTokens}` object. Regenerate the schema bundle and the protocol manifest. - Rust `provider_events.rs`: add `codex_context_usage` (last request `totalTokens` and `modelContextWindow`) and `acpx_context_usage` (ACP `used`/`size`). ACPX `usage_update` now sets `context`. - Rust `provider_backend.rs`: Codex `thread/tokenUsage/updated` sets `context` on live (non-historical) usage. - Rust `provider_events.rs`: emit `context.compacted` for the completed `context_compaction` provider notice that #19267 produces from Claude's compaction banner. Only this path marks a Claude compaction, so the server counts each compaction once. - `runnerd-codex-transport.ts`: `rehydrateRunnerdUsageNotification` keeps a valid `context` on `tokenUsage`, so it reaches the stored `item.completed` usage item. - Server: new `GET /api/issues/:id/context-usage` (issue read access is required) and `issueContextUsage` service. It reads only the assignee's current session (the session of its newest usage report), and returns that session's latest context reading, non-zero totals, cost, and the count of compactions across all of the session's runs. Added to OpenAPI. Shared type `IssueContextUsage`. - DB: migration 0432 adds two partial indexes on `heartbeat_run_events`, built concurrently. One indexes usage items by run and time. The other indexes `context.compacted` events by company and session. `EXPLAIN` shows that both lookups use them. - UI: new `ContextUsageMeter` (bar, percentage, hover card) and `IssueContextUsageRow` in the Execution section of Properties. The row refreshes every 8 s while a run is active, and once more when the run ends. - UI transcript: `native-run-events.ts` maps `context.compacted` to a `context` activity entry with pre/post sizes from neighbouring usage items. `transcript-adapter.ts` turns it into a new `context_compacted` marker variant. The live runner turn keeps that marker as its own timeline row. - Storybook: `task-context-window.stories.tsx` (Properties panel in a task, all states, hover breakdowns, divider). ## Verification - `cargo test -p paperclip-runner-core --lib provider_events` (new tests: ACPX context, Codex context, banner compaction, notice compaction) - `pnpm --filter @paperclipai/paperclip-runner exec vitest run src/live/runnerd-codex-transport.test.ts` - `cd ui && npx vitest run src/components/ContextUsageMeter.test.tsx src/components/transcript src/components/task-chat src/components/issue-properties` - `cd server && npx vitest run src/__tests__/issue-context-usage.test.ts` (embedded Postgres) - `node scripts/typecheck-low-memory.mjs`, `pnpm check:token-gates`, `pnpm --filter @paperclipai/paperclip-runner run check:protocol-types` - Live check on a local worktree instance through the runnerd path: - A Codex `gpt-5.5` task showed "10% of 258k" (27k of 258k, input 77k with 49k cached, output 1.1k). - A Claude Sonnet 4.6 task showed "17% of 200k" (34k of 200k). - Storybook: Tasks and conversations / … / Task chat / Context window. - Not verified live: a real compaction. The divider is covered by unit tests through the same projector path the task chat uses. ## Risks - The schema field is optional, and older runners omit it. The UI renders nothing until a harness reports a window. - Migration 0432 builds two partial indexes concurrently on `heartbeat_run_events`, which is a large table. The indexes cover only usage items and compaction events, so they stay small. They add a little write overhead to those events. - Claude in-progress usage rows still record zero tokens with `requests: 1`. The final turn usage is correct. This PR does not change accounting. - A "Compact now" action is out of scope. It needs a compaction-only run that does not produce a run result. It will come in a follow-up. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, used through Claude Code with tool use (shell, file edits, in-app browser for Storybook and the live instance). ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e54f9fbf82 |
fix(secrets): expose secure human entry in native discovery (#18927)
## Thinking Path > - Paperclip helps people manage AI agents and their work. > - Some tasks need a company credential or the current person's credential. > - Paperclip already has secure human entry forms for these values. > - Native connection guidance did not distinguish storing a credential from connecting an external service. > - Native API search also omitted the existing secure entry route from secret operation help. > - This change makes that route discoverable while preserving human and owner authority. ## Linked Issues or Issue Description Refs #18780. Related #18921 corrects a separate observer false positive. Neither PR owns this production guidance change. **What happened?** A request to store an existing company or personal credential could lead to an unnecessary external provider question. Native API search did not explain the existing human entry surface. **Expected behavior** An agent can find the existing secure form, direct the human to the matching entry, and verify the result through its authorized interfaces. The agent must not ask for values in chat or act as the human. **Steps to reproduce** Search the native API for a company credential or personal publishing credential. The result should name the supported Secrets settings route and its human authority boundary. Open a known personal definition link and confirm that the existing Set my value action appears. ## What Changed - Distinguish Paperclip credential storage from external connection discovery in shared native guidance. - Describe the existing Secrets settings route, secure entry actions and current-person boundary in OpenAPI help. Qualify definition detail links as admin-only; other members use My secrets and Set my value. - Exercise discovery through the actual OpenCode MCP bridge and native API catalog. - Retain agent denials for personal definition and value routes. Exercise the real Secrets UI definition link. ## Verification - Focused local checks: four suites, 78 tests passed. These cover native discovery, secret-route permissions, shared guidance and the Secrets UI. - The new discovery test fails against unchanged master OpenAPI help. It passes with this change. - Token gates and diff checks passed. - Full workspace typecheck passed. The first attempt found missing SDK links after script-free installation; the existing root postinstall helper restored them. An unprivileged retry also hit the local tsx IPC sandbox restriction. The corrected owned setup passed without source changes. - Full local workspace build passed. The full local test run stopped in chat shard 2: one Discord receiver-lease test failed during initial configuration. That shard passed 263 tests; the first shard passed 310. The test and owning lease/configuration sources are unchanged from master. One focused isolation passed without code changes. Its cause remains unknown; this is not a complete local test pass. No unrelated Discord code was changed. [Normal hosted CI](https://github.com/paperclipai/paperclip/actions/runs/41773266687) passed on exact head `0abb8b722779368109b9639f68122d7df02a8ab1`. - Fresh Greptile review reports 5/5 on the exact current head; all review threads are resolved. Paperclip Review remains in progress. The optional Superagent scan is neutral/inconclusive: Security Graph context retrieval returned HTTP 503 and assessment did not start. It supplies no security PASS credit. - A retained representative native attempt with this guidance produced the correct definition-scoped secure-entry link. Its observer then rejected an explicit warning against chat entry. That failed result remains unchanged. Human entry, saving the value and worker delivery were not exercised. This PR claims no complete product-eval pass. ## Risks This changes agent guidance and search ranking. It grants no new permission and changes no secret resolution or owner checks. Broader service-connection discovery still applies when a task needs a connection. The secure human forms are unchanged. ## Model Used OpenAI `gpt-6.1-sol` through Codex, with repository tools, code execution and test analysis. The host does not expose the context-window limit. ## 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 — the 78 owning controls pass; the broad local failure remains disclosed above - [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 — normal CI passed; optional review states are disclosed above - [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> |
||
|
|
5b50c6b32c |
fix(teams): return saved resource links after catalog installation (#18923)
## Thinking Path > - Paperclip manages AI agents and their work. > - Catalog installation creates agents, projects, and routines in one company. > - The install result includes saved IDs but no links to those resources. > - A chat agent can describe a partial installation without giving the operator a way to inspect it. > - This change returns canonical links for saved resources and documents them for API discovery. > - The operator can use the returned links to inspect the actual installation. ## Linked Issues or Issue Description **What happened?** A catalog install can create the intended resources and report skill warnings, while its response leaves callers to construct board URLs from IDs. A native chat attempt described the resources and warned about an incomplete skill, but omitted resource links in both its initial and repeat replies. **Expected behavior** The installation response should provide canonical company-scoped links for its saved agents, projects, and routines. It must preserve warnings and omit links for rows without saved IDs. **Steps to reproduce** Install a catalog team through the API. Inspect its saved resource rows and response. Before this change, the response has IDs and names but no resource links. **Paperclip version or commit** Parent `e7549ab613` from master. Related #16495 changes comment authorization metadata in the same OpenAPI file; it does not address team installation responses. No duplicate team-link PR was found. ## What Changed - Add typed `resourceLinks` entries with resource kind, saved ID, name, and canonical company-scoped href. - Read the company prefix with one bounded primary-key query before import. Encode URL segments and use saved IDs, including IDs returned for reused resources. - Preserve the portability import result, skill preparations, warnings, permissions, and collision behavior. - Document the response and add service, native API wire, OpenAPI, and real database regression coverage. ## Verification - Parent service regression: 1 failed, 28 passed. The new failure was the missing resource-link response. - Owning service/native wire, route authorization, and OpenAPI suites: 72 passed. - Real PostgreSQL catalog-install suite: 4 passed. Returned links match persisted agent IDs and the company prefix. - Shared and server TypeScript checks passed. Fresh shared/SDK builds and the owning Runner TypeScript prerequisite passed. - UI typecheck, Storybook catalog check, 30 catalog/map controls, and token gates passed. - Actual CI found an omitted required `resourceLinks` field in the existing Storybook install fixture; that fixture is now synchronized. The earlier response-status review correction is also retained: install schema and wire control use actual HTTP 201. Final head `0bca72cf1f7641fd842a04c5a75cae4bcc274f05` passed all 48 normal CI jobs, including `ci / verify`: [run 41773413940](https://github.com/paperclipai/paperclip/actions/runs/41773413940). Greptile is 5/5 with zero unresolved review threads. The optional Paperclip review remains queued and its comment says work is paused. The Superagent security scan is neutral; neither supplies completed assessment credit. - `git diff --check` passed. - Setup failures were retained: incorrect test working directory, sandbox listener denial, missing built SDK/Runner declarations, and native library aliases not hydrated from the package directory. Corrected setup used the owning build and shipped hydration script. No assertions or deadlines were relaxed. - Repository-wide tests/build were not run locally; the full normal CI suite passed. A new native chat attempt has not run for this change. The earlier chat attempt remains failed for missing links. Local response coverage does not prove that a model will include the links in its next reply. ## Risks - This is an additive JSON response field. Manually constructed TypeScript install results must include the new typed field. - The query adds one company-prefix lookup per installation. It selects one scalar by company primary key and runs before mutation. - Supplying links reduces URL construction work but does not guarantee model use. Native qualification is still required. ## Model Used OpenAI `gpt-6.1-sol`, with reasoning, repository inspection, code editing, and tool execution. The host did not expose the model context-window limit. ## 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 required normal CI gates are green; optional incomplete/neutral assessments are disclosed above - [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> |
||
|
|
683fa4725e |
fix(evals): refresh native runtime and pin reviewed catalog repairs (#18780)
## Thinking Path > - Paperclip lets people inspect and manage their agents’ work. > - The timeline must report canonical ownership history, and OpenCode tool failures need bounded diagnostics. > - The measured candidate also had independently reproduced question-navigation and oracle defects. > - This change promotes the reviewed production fixes and pins one immutable, qualified source composition. > - Original queries, model, counts, clocks, privacy controls and qualification holds remain intact. ## Linked Issues or Issue Description Refs #18775, #18777, #18779, #18711, #18713, #18718, #18699, #18798, #18799. **What happened?** The measured candidate predates merged native-runtime fixes and contains fixture defects. The timeline omitted recorded reassignment/removal history. Finish-input errors lacked a closed correlation and shape witness. **Expected behavior:** project recorded ownership transitions without inventing facts, distinguish successful native execution from failed verification steps, and retain content-free finish diagnostics. **Steps to reproduce:** repeat an unchanged assignee while editing another field; then reassign or unassign it. Only recorded transitions should appear. Invalid OpenCode finish input should produce a correlated rejection witness without values or schema text. Related #18699 adds gated company OTLP diagnostics. This change uses the existing local callback; it adds no exporter or capture policy. ## What Changed - Promote canonical timeline creation, reassignment and removal projection from #18777. Initial child ownership requires a recorded fresh-creation receipt; delegation requires a readable recorded parent. Unknown legacy facts fail closed. User actors/owners require active company membership; agent actors/targets must exist in that company. Spans, events and edges share that admission filter. Remove the introduced all-history absence query and use guarded indexed UUID lookups. - Add the shared/OpenAPI `unassigned` event kind, preserving newer main contracts and issue-route/service code through reviewed semantic joins. Existing UI consumers remain generic; no new visual marker is claimed. - Add bounded OpenCode finish diagnostics: schema digest, hashed call identity, closed shape enums and validation category. A throwing sink does not change admission or cancellation. - Pin immutable candidate `fd5e3b1e5a379524c0d1651ed8273e75ffd86541`, composed from combined #18777 source `b734393eff0d7ef85b7ad8abd556d2a66ca3a4e6` plus the reviewed MCP diagnostic delta. It preserves actually merged #18775 `97c07d149b5670088faf15317dcf603c75e2af87` and #18779 `a352c057d475067b4fe67375f06785287dfbe0ce`, and merged runtime fixes #18711, #18713 and #18718. - Qualify narrative handling for adjacent repair/verification sentences, intervening native-failure wording, and negated repair objects. Questions, denials and hypotheticals retain their controls. Preserve Previous question beside the qualified Entity type import; separately restore the already-called `internalResultLink` goal helper binding. That import is a runtime fixture repair, not a type-only change. - Include the narrow older-baseline CI typecheck closure and causal heartbeat test observers in the candidate. All four default typecheck projects execute. Main already includes the protected upstream tooling and newer heartbeat fixture, so neither is overwritten with an older candidate file. - Preserve all 244 identities, original queries, model, native/human counts and clocks, including the explicit prior Studio accounting exception. The full catalog stays on its candidate branch; the 196/244 gate and historical SDK runtime holds remain. ## Verification The primary’s f478 retry repeated the real composer-hidden five-second assertion (1 failure,18 passes,1 skip). Investigation established a setup readiness gap: an outer route response can delay mounting the chat thread beyond the assertion. The successor adds the existing real-thread `aria-busy="false"` readiness barrier before the unchanged composer assertion. The original120-second scenario and digest, unread, source-link, history, and reply checks are unchanged. The full owning browser scenario passed naturally in10.2s and with its first real chat response held6000ms in12.0s. This synchronizes fixture setup; the underlying AWS late-mount latency remains unproved. **Current main gates passed:** exact head `823dab49fcd8470140c178771e5c0c6b78725e97` has [successful normal CI](https://github.com/paperclipai/paperclip/actions/runs/41724620555), including both required aggregates, fresh Greptile 5/5 and zero unresolved review threads. Paperclip Review is neutral with a completed substantive 5/5 and no actionable defect. The current optional Superagent Security Scan is neutral/inconclusive: Security Graph context retrieval returned HTTP503, so its assessment did not start. That result supplies no security PASS credit. The primary source PR retains its separate current-head gate before Root sequences normal protected merges. - Reviewed MCP controls: 130 on main and 129 on the older candidate. Meaningful parent comparisons failed before implementation/correlation correction. Controls cover actual offered schema identity, null/omitted admission, bounded rejection categories and throwing-sink cancellation. - Exact unchanged production postimages retain 63 Postgres/public-route controls, nine plan controls and the focused index-plan proof. Owner OpenAPI passes 32 controls; main’s newer contract passes 33. Earlier setup/sandbox failures remain recorded; unchanged database controls were not replayed. - Narrative owner controls pass 216, retaining all prior 207 controls. The independent peer passes 144 balanced cases; earlier 108 and 39-case matrices remain retained. Actual consumer parent failures reproduced the review findings before repair. - Main’s server NoEmit passed on its protected baseline; only two pin files changed afterward. The candidate’s prior full server and four-project checks passed. All server/package/script/root-lock inputs remain byte-identical; the changed Runner project’s guard passes on the current source. Compatibility driver controls pass 47 and six owning suites pass 82. The one explicitly reviewed Docker-only unit omission concerns an unimported image-build feature; all four intended project guards still execute. - Pin/adapter/contract controls pass 47. All 242 expanded declarations, the two legacy dispatch rows, and all 244 singleton private policies retain their authenticated identities. Descriptor, launcher and private-protocol bytes remain exact; declaration loading invoked zero fixture callbacks. - All 506 SDK input blobs match the meaningfully compiled `6abe5739` source vector. Prior real shared/SDK builds measured declaration/map changes and zero runtime-JavaScript changes; a compiled consumer accepts the new kind and rejects an unknown kind. The reviewed registry remains unchanged. Default source proof, 51 SDK controls, 20 custody controls and 16 UI consumer controls remain qualified; runtime/compiler/parser/Zod/canary pins and held runtime grades are unchanged. - The full Mac heartbeat run on prior observer source retained **58 passes / six failures in 802.78s**. Two nonassignee controls subsequently pass focused checks. Four late timeout causes remain unresolved locally; no flakiness/infrastructure cause or complete local pass is claimed. [Actual AWS CI](https://github.com/paperclipai/paperclip/actions/runs/41712023737/job/126182620050) ran this exact heartbeat file with **64 passes in 107.209s**. Its server/package inputs are unchanged in the current narrative-only successor. The source-identical f478 heartbeat file also passed all64 AWS tests in105.812s; source inputs remain unchanged. Current main ordinary CI passed; the primary source PR must independently close its latest-head ordinary CI gate. - Historical main [run 41713049555](https://github.com/paperclipai/paperclip/actions/runs/41713049555) recorded six synchronized runner-shutdown failures and cancellations; the shutdown cause is unknown. A separate chat passive-control assertion observed one `db.transaction` call where zero was expected. Finite local probes passed 2+2 but did not establish a cause or late stack. No external publication, cleanup cause, source fix or flakiness is inferred. The primary’s separate composer-visibility CI failure also remains an inconclusive historical result; no guessed fix is credited. No old-head retry was used. - Earlier UI/inbox fixture failures and the CLI startup timeout remain historical. UI/inbox repairs are now upstream in #18799/#18798 and main preserves those files verbatim. The CLI timeout cause remains unknown. Broader local repository/Rust qualification is not claimed; fresh hosted CI supplies broader coverage. - All four actual repair merges, an exact merged-tree/diagnostic source join, and fresh controller/source/private-policy/active-run guards precede the ten-case TestOps measurement. No paid or model eval was launched for this PR; the original three passes/seven failures remain unchanged. ## Risks The next ten-case model result is unproved. Earlier MCP timeout and continuation-shape causes remain unknown; diagnostics provide future evidence without relaxing validation or rewriting grades. Prior local/CI failures are retained, including the unresolved chat assertion and local heartbeat timeouts. Initial ownership requires canonical in-window audit facts. Missing legacy facts remain unknown. The additive `unassigned` contract is synchronized across shared/OpenAPI/server consumers, with no new UI marker. SDK runtime holds and the broader 196/244 qualification gate remain. Normal protected merges require fresh checks and resolved feedback; no CODEOWNER or branch-protection bypass is authorized here. ## Model Used OpenAI `gpt-6.1-sol` through Codex. Capabilities used: repository analysis, code changes, source validation, and local test execution. The context-window size is not exposed by this harness. ## 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 — current focused/type/pin controls pass; the prior full Mac heartbeat failures remain disclosed and receive no local PASS credit - [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 — current main normal CI and required aggregates passed; optional neutral reviews and separate primary gate are disclosed above - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups — exact current main head; full thread pagination verified - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
f187de99aa |
Preserve domain deadlines across owned idle sleep (#18656)
Reviewed and verified at
|
||
|
|
b8f8b904fa |
feat(routines): preserve automation ancestry through webhooks (#18772)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Routines let external services start agent tasks through a normal webhook. > - A repair routine must recognize failures from work that it created. > - The receiver added in #18714 put Sentry payload parsing and repair policy in core routine intake. > - This pull request replaces that receiver with verified creation provenance on normal routine calls. > - Sentry can filter the resulting tags before it sends a normal routine webhook. > - The benefit is reusable provenance and loop prevention without a provider-specific route. ## Linked Issues or Issue Description **Problem or motivation** A Sentry repair alert can start another repair when its own work fails. The dedicated receiver in #18714 solved this through a Sentry-specific payload contract. Ordinary routine webhooks also need a trusted way to retain the automation that created their tasks. **Proposed solution** Use the existing public `/fire` route and its current authentication. An optional source run credential proves a live Paperclip caller. Store the caller separately from webhook JSON. Follow recorded task creation links and export bounded ancestry tags on run-failure events. Let Sentry alert filters exclude repair-origin work. **Alternatives considered** Keep the dedicated Sentry receiver, accept sender-supplied origin IDs, or filter only the immediate routine. The first couples core intake to one provider. The second accepts unverified claims. The third misses child tasks, standalone follow-ups, and calls through another routine. **Roadmap alignment** This improves the shipped Scheduled Routines and Activity log capabilities. It does not add a new scheduler or incident system. Related work: #18714 is the implementation replaced here; #9727 adds source-task navigation and UI attribution; #10206 proposes exception-led routine dispatch. This PR uses existing task creation records and does not implement either of those broader proposals. ## What Changed - Remove the Sentry-only route, parser, authentication exception, and payload-specific tests and documentation from #18714. - Add optional `X-Paperclip-Source-Run-Token` support to normal routine webhooks. Validate the standard run JWT, company, instance, agent, live run, and cancellation state. Authenticated routine API calls also retain their validated caller. - Add nullable `routine_runs.created_by_run_id`. Keep the reference after source-run deletion so missing history becomes unknown. Separate callers during coalescing, including legacy open tasks. - Follow company-scoped routine receipts, creator runs, task parents, and legacy creation activity. Mark missing, foreign, cyclic, private, or excessive ancestry for safe alert filtering. - Add event-local `automation_origin_status`, `automation_routine_ids`, and `task_shareable` tags through the existing Sentry SDK hook. Cap ancestry at 32 reads and share a 750 ms deadline across metadata and ancestry lookup. Keep the SDK one-second wait and eight-lookup concurrency bound. Redact the source credential from HTTP logs and Sentry request headers. - Preserve unexpected database failures as server errors without rejection bookkeeping in an aborted transaction. Document 409, 415, and 422 responses for the normal webhook. - Keep `routineScopedRunId` as generic opt-in `X-Paperclip-Idempotency-Scope: routine` support. Default trigger receipts and timestamped HMAC replay protection retain their current scope. - Add [the Sentry repair tutorial](https://github.com/paperclipai/paperclip/blob/codex/routine-creation-provenance/docs/guides/board-operator/sentry-run-failure-repairs.md), API documentation, OpenAPI headers, and capability-map updates. ## Verification At commit `0f4a0051528edd1367a921a76a8050c3fd087872`: - `pnpm -r typecheck` and `pnpm build` passed for the full workspace. Migration numbering and safety checks passed. - The focused service, route, Sentry, redaction, and OpenAPI suites passed 228 tests in five files. The database Sentry resolver and OpenAPI invocation passed 33 tests in two files. These are separate invocations with some overlap. - The full routine HTTP suite passed all 15 tests. The authenticated API case failed before the caller-wiring fix and passed after it. The fixture loads the real route-owned services. No test deadline was raised. - The real `@sentry/node@10.71.0` suite passed all 12 tests. It uses an in-memory transport and sends no events to Sentry. It covers terminal failures, saved-message interruptions, event isolation, and source-token redaction. - Regression tests also cover forged, expired, foreign, and cancelled caller credentials; payload spoofing; inherited routine ancestry; legacy coalescing; deleted history; private work; cycles; query and time limits; database error causes; and routine-wide receipts. The new error and budget cases failed before their fixes. - `node scripts/check-maps.mjs` and `git diff --check` passed. The map check covers 35 areas and 192 capabilities. - The full local Vitest command was stopped for a required master merge after four chat shards passed 1,278 tests. No full local pass is claimed. An earlier combined HTTP run hit an existing malformed-ID timeout whose original cause is not established; the final full HTTP suite passes. An extra local run of the unchanged heartbeat scheduling file passed 12 of 13 tests and failed a readiness-status assertion. Its logs also reported a missing fixture user membership. The same 13-test file now passes in current-head CI. - All GitHub Actions checks pass on this commit. Three jobs initially lost their self-hosted runners. One server shard initially failed while deleting a company in an unchanged heartbeat test because of a `company_skills` foreign key. One retry passed all affected jobs without code changes. That server shard passed all 499 tests, and all eight browser shards passed. - Fresh Greptile review is 5/5 on this commit. All six review threads are resolved. The separate Paperclip Review check remains queued. Its bot reports that work is paused. A maintainer must resume the assigned review work to complete that check. No full green check state is claimed while this external gate is pending. - Live Sentry configuration and deployment are outside this PR's acceptance evidence. The tutorial describes setup, a controlled failure, and a repair-origin exclusion check for operators to run after deployment. ## Risks - Normal app webhooks retain the sender's full JSON in company-readable routine history and the execution task. Alert filters and task visibility are not content scrubbers. Operators must scrub sensitive event data in Sentry before forwarding it and keep connection tests in setup mode. - Existing users of `/sentry-run-failures` must move to `/fire` and add the documented Sentry filters before enabling repair intake. The old route is removed. Stored tasks and receipts remain. - The additive migration uses `ADD COLUMN IF NOT EXISTS`. It does not rewrite historical tasks or infer missing provenance. - Unknown or unshareable ancestry is excluded by the tutorial's alert rule. Missing history, an enrichment timeout, or more than five routine IDs can therefore suppress a repair webhook while retaining the Sentry error. - An external webhook without a verified source credential starts a new receiving-routine origin. Its JSON cannot establish upstream Paperclip ancestry. Sentry must not store a Paperclip run credential. - Routine-wide receipt reuse needs a stable sender key. A static Sentry custom header is not a per-event key. The tutorial keeps delivery deduplication separate from loop prevention. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used OpenAI Codex, GPT-6 model family, with reasoning, repository access, code editing, and test execution. The session does not expose the exact model variant or context window size. ## 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 - [ ] 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> |
||
|
|
b40e6edaea |
perf(inbox): combine startup reads and reduce refetch pressure (#18790)
## Thinking Path > - Paperclip helps people manage AI agents and their work. > - Mine shows the work and failures that need the viewing user's attention. > - A reload waits for account access, task rows, parent captions and failure state. > - Those reads form several browser request stages before useful rows appear. > - Live invalidations can also restart the same task-list request more than once. > - This change supplies startup data together and removes redundant refresh work. > - It preserves current authority, failure presentation, archive state and row stability. ## Linked Issues or Issue Description Refs #18645. Related: #18642. This change implements the read-receipt behavior independently on current master. It does not use that PR's branch or patch. **What happened?** Mine starts its primary request after the session, company and board-access reads. It then requests parent captions and rich failure context. Four observed loads spent another 0.45–0.70 seconds after the primary rows arrived. One slower load waited 1.71 seconds for parent context. The same run observed two HTTP 429 responses. These timings identify request waits; they do not attribute the delay to SQL or Node CPU. Read receipts also invalidate general task collections and dashboards. Overlapping parent and child invalidations can start two requests and abort the first. **Expected behavior** The initial page should use one account-safe startup projection and one complete Mine response. A read receipt should refresh the affected viewer's inbox state. Each invalidation batch should refresh a covered query once. Required captions and current failure state must be ready before first reveal. Existing rows should remain usable during a transient refresh error. **Steps to reproduce** 1. Open a populated Mine inbox with parent tasks outside the visible index and a current failed run. 2. Reload with browser HTTP cache retained and application query state empty. 3. Inspect the session, company, board access, task-index and context request sequence. 4. Open or mark a task read and inspect the unrelated collection refetches. **Paperclip version or commit** Baseline: `44a9a043b80c18314d8dd89556f444dae696d3d6`. **Deployment mode** Authenticated board UI. Local proof uses the real APIs and database with a synthetic session resolver. No hosted candidate deployment is claimed. ## What Changed - Add optional, same-user `boardBootstrap` data to the existing session response. Seed account-keyed company and board-access reads from it. Keep ordinary endpoint reads for fallback, retry and live reconciliation. A returning account cannot reuse revoked cached access. Cache collection and later real refetches remain effective. - Add the board-only Mine snapshot endpoint. Reuse the canonical issue selector, private-task filtering, run-read decisions, secret redaction and interruption/recovery presentation. Assemble the fixed 100-task page, parent captions and bounded 200-run failure window on the server. Start independent reads together. Optional source errors keep successful task rows usable. - Keep array-shaped source caches for archive and undo. Key Mine source, context and reveal state by account. Validate the response's user stamp before committing it. Remove array-only cross-tab Mine broadcasts, which cannot carry its paired failure context. The server retains authenticated in-flight coalescing. - Scope read-receipt invalidation to the affected viewer's inbox, badges and chat unread state. Keep company and task audit views fresh. Preserve genuine task mutations, run updates and subscription-gap reconciliation. - Remove covered scalar-prefix invalidations within a batch. Preserve predicates, exact matches and different query/refetch constraints. A later event can still refresh again. - Retire Mine in-flight reuse after local company activity or run lifecycle changes. A post-archive read starts fresh. Old work keeps its admission slot until it settles, even if every HTTP client aborts. The eight-compute limit stays in place. - Add API, component and browser regressions. Extend the production timing harness with matching API/UI artifact receipts, retained-cache reloads, request status counts, layout checks and optional controlled network latency. Update API and verification documentation. ## Verification - Full `pnpm -r typecheck` passes. - Full `pnpm build` passes. - Token gates, map checks and diff whitespace checks pass. - 72 Inbox component tests pass. Startup-specific checks pass, including account switching, revoked cached access, malformed/absent bootstrap data and cache collection. The canonical authenticated bootstrap route also passes against real PostgreSQL. - Six real PostgreSQL Mine route cases pass, including full equivalence with legacy authorized projections, privacy, fixed bounds, membership revocation, coalescing, archive freshness and aborted-client admission. Four focused legacy coordinator controls pass. Six assembly tests pass. - Refetch and invalidation regressions pass. Meaningful pre-fix tests observed two request starts and one abort for overlapping invalidations; the fix produces one start and no abort. The archive race test observed the post-archive GET joining the old body before the fix. - All 11 existing startup browser checks pass, including Tasks/Mine code loading, desktop/mobile bounds, non-streamlined Coordinator, task navigation, failed preloading and Back position. The nine focused startup, fallback, partial-source error, rate-limit recovery, archive/undo and geometry cases also pass. The 429 cases use the inherited retry window. Archive and keyboard undo are corroborated through the real API and reload. Authentication and failure fault injection in these cases are explicitly simulated. - Matched production before/after timings are complete. Full aggregate Vitest coverage is left to CI. At 15:15 UTC, the final-head runner gate and `review` workflow pass. The 45 build/test/policy jobs are queued; the Build job has no `ubuntu-latest` runner assigned and has executed no steps, so remote aggregate results remain unverified. Paperclip Review and Superagent Security Scan are in progress. Superseded runs for this PR were cancelled. No current-head check has reported a failure; the CI checklist remains open. The remaining step is runner allocation and completion of those external checks; persistent queueing needs the repository CI owner's attention. - Greptile completed on final head `ecde295565309275c4f2fae903caf6892f1eaafd` at 5/5. The map-scope finding is fixed and resolved; there are no open review threads. ### Matched browser timings Five alternating baseline/candidate pairs per network scenario, measured serially in headful Chrome 156.0.8078.80 on Apple silicon with Node 24.22.2 and a 1440×900 viewport. Both production UIs use their matching real API revision against one fixed disposable PostgreSQL fixture: 34 tasks, 31 visible task rows, one paused agent and one terminal failure. No active agent/provider work. HTTP cache is retained; each initial sample reloads the document and clears application query state. Warm navigation and Back retain query state. CPU and throughput are native. The 100ms scenario uses CDP minimum request-to-response-header latency without request interception. It is a controlled request-dependency experiment, not a staging reproduction. Values are median milliseconds with min–max in parentheses. Paired saving is baseline minus candidate for each alternating pair; its median differs from the difference of arm medians. Positive saving favors the candidate. | Network / journey | Baseline | Candidate | Median paired saving | | --- | ---: | ---: | ---: | | Native / initial rows | 187.6 (128.9–208.5) | 191.7 (130.3–210.9) | −23.3 | | Native / Recent → Mine | 100.17 (99.90–101.87) | 100.07 (99.88–101.37) | −0.16 | | Native / task → Back | 70.45 (56.63–72.02) | 66.64 (56.52–67.71) | +4.94 | | 100ms / initial rows | 930.3 (914.1–1015.7) | 799.0 (697.1–826.0) | +211.8 | | 100ms / Recent → Mine | 100.09 (97.73–100.16) | 100.18 (96.65–100.98) | −0.02 | | 100ms / task → Back | 74.97 (62.77–75.82) | 75.47 (61.51–76.19) | +0.35 | The controlled-latency initial pairs all improve, saving 108.3–217.0ms. Native initial pairs have mixed signs (−67.5 to +78.1ms), so they establish no reliable native page-speed improvement. Warm and Back timings are effectively unchanged. Median primary-request start is 110.4→135.2ms natively and 497.8→480.9ms with 100ms latency; these runs do not establish a large startup-only timing gain. API requests through the two-second post-reveal window fall from median 32 (32–37) to 27 (25–28) natively, and 37 (33–38) to 30 (25–32) with 100ms latency. All 20 samples preserve the same ordered 31 task links, first-row DOM identity and geometry. All recorded API responses are HTTP 200; zero unfinished requests, 429s or JavaScript errors. Lower 429 incidence on staging remains unproven. Measured source: baseline `44a9a043b80c18314d8dd89556f444dae696d3d6`, candidate `4c12b2ef0ecf0754676e99bd6b9399e84408646c`. Final head `ecde295565309275c4f2fae903caf6892f1eaafd` changes only two feature-map files and existing browser test expectations since that measured candidate; production executable inputs are identical to the measured candidate. Asset hashes, API health revision receipts and every raw sample were retained. These results do not claim hosted-page improvement or deployment of this PR. ## Risks - Startup enrichment adds work to the session read. Failure of this optional projection falls back to the ordinary company and access endpoints. It does not invalidate a valid session or replace per-request server authorization. - Account changes, stale cache entries and same-company navigation can expose old presentation data. Account keys, principal stamps, reveal scopes and canonical bootstrap replacement have dedicated negative cases. Permission failures hide cached task rows; transient network, 429 and 5xx failures preserve them. - Current failure state can differ from the historical failed run. The snapshot uses the existing canonical rich projection and verified missing-ID coverage. Partial context failures retain an explicit retry path. - Mutation retirement follows events delivered to the current server process. This adds no cross-replica invalidation protocol or distributed read-after-write guarantee. - New UI assets require the matching API. Older UI/session consumers retain their existing response and endpoint behavior. There is no database migration, provider call or deployment change. ## Model Used OpenAI Codex, based on GPT-6, with reasoning, code execution, browser automation and delegated source review. The exact runtime model ID and context-window size are not exposed by 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 - [ ] 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> |
||
|
|
dc9f387edc |
fix(api): surface existing skill source import in discovery (#18757)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents discover supported API operations through the public OpenAPI
catalog.
> - Importing existing skill content must retain its source provenance.
> - A normal source-import query misses the applicable operation on its
first page because that operation has little search metadata.
> - This pull request describes the existing import operation and its
source boundaries.
> - The benefit is clearer discovery without changing search scoring or
import authority.
## Linked Issues or Issue Description
**What happened?**
`search_api` does not return `POST
/api/companies/{companyId}/skills/import` in the first ten results for
`import skill from source SKILL.md`. Source-management operations rank
ahead of the import operation. The import entry has only the summary
“Import a skill.”
**Expected behavior**
Ordinary source-import discovery should surface the existing operation.
Its documentation should explain that imported content retains
provenance and remains subject to company policy and source checks.
**Steps to reproduce**
Call `searchRunnerApi({ query: "import skill from source SKILL.md",
limit: 10 })`. Inspect the operation IDs on the first page. The new
regression sends this result through the real bounded MCP serializer.
Related: #18717 clarifies the separate `create_skill` tool. This change
covers the existing source-import HTTP operation. Neither PR is merged
by this work.
## What Changed
- Expand only the source-import OpenAPI summary and description.
Describe allowed local skill directories, supported remote sources,
retained provenance and existing access checks.
- Add a first-page discovery regression through the actual authenticated
MCP bridge and serializer. Keep non-import lookup, company/mode refusal
and denial forwarding controls.
- Update the feature and access maps with the test scope and its limits.
## Verification
- Parent regression: the controlled loopback MCP response lacked the
applicable import operation on the first page.
- Focused current checks: 2,060 tests pass in
`skill-import-discovery.test.ts` and the existing `runner-api.test.ts`
owner.
- `node scripts/check-maps.mjs` passes. This checks map structure only.
- The first head had one server test job cancelled after a runner
shutdown signal. The reason for the shutdown is unknown. The reviewed
successor removes only two test comments required by contribution rules;
its four focused controls pass. The remaining obsolete CI run was
cancelled after that successor was published. No same-head retry was
used.
- The new test is under `server/src/__tests__`, which the production
server typecheck excludes and the server Vitest suite includes.
- No local app, database, native agent or provider model was run. Normal
PR CI on `a4aaae8e320fb1d56cdbff9f376ec832a634a5da` passed with 48
successful jobs and one skipped job, including the required `ci /
verify` and `ci / e2e` checks. No completed agent-import journey or
reliability result is claimed.
- Greptile is 5/5 on the current head with no unresolved threads. The
optional Paperclip Review check remains queued. This is not merge
authorization.
- Keep this core PR open and unmerged. Do not enable auto-merge.
## Risks
The expanded metadata can change where this operation ranks for related
queries. Search weights, request schemas, HTTP routes, source parsing,
authorization and company skill policy are unchanged. The synthetic 403
control proves denial forwarding; it does not qualify the real policy or
source-import route. A live import still needs separate acceptance.
## Model Used
OpenAI `gpt-6.1-sol`, through Codex with reasoning, source review and
code execution tools. The exact context-window size is not exposed by
this environment. A separate agent reviewed the source and contract
before publication.
## 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>
|
||
|
|
ef5b98594f |
Explain the human primary-agent control in API discovery (#18747)
## Thinking Path > - Paperclip lets a person manage a company of AI agents. > - A primary agent is that person's company preference. > - Only the person's board session can change it. > - Native API discovery explains that restriction but omits the real human control. > - An agent can then suggest a Chat-picker setting that does not exist. > - This change describes the existing profile crown and replacement confirmation. > - It adds no agent authority and no new tool. ## Linked Issues or Issue Description **What happened?** API discovery for the personal primary-agent preference explains board-only access. It does not explain the supported profile control. A retained native response gave a correct board API alternative but also suggested a primary setting in the Chat picker. The picker only opens conversations. **Expected behavior** The discovered operation should direct the person to the chosen agent's profile, the crown control labeled Set as my primary, and the replacement confirmation. It must keep the current person's board authority and the agent denial. **Steps to reproduce** 1. Search the native API for set primary agent. 2. Read the primary-agent operation's description. 3. Compare its guidance with the profile crown and the Chat picker. 4. The current description has no supported UI path. **Paperclip version or commit** Master `7b8aa60b6d` and the retained evaluated application have this metadata gap. The profile control was checked in both sources. A scoped open-PR search found no matching primary-discovery fix. ## What Changed - Add general profile/crown/confirmation guidance to the existing GET and PUT operation description. - Clarify that the Chat picker does not change the preference. - Keep all other OpenAPI bytes, authority, schemas and handlers unchanged. - Update the two existing capability maps. ## Verification - The actual API search result passes through the existing authenticated OpenCode MCP bridge and its bounded JSON serializer. The parent fails because the delivered description has no profile control. The new result retains the full guidance, board-only actor and original schema. - The existing API client keeps its agent bearer/run identity and propagates a mocked board403. This is client propagation proof, not a new real-route claim. - Initial selected controls:997PASS,1061 intentionally unselected. After relocating the same test into the existing server `__tests__` directory, both complete owning files pass:2058 controls. These are static/protocol controls, not native journeys. - The first normal CI head failed with TS6059 because the test imported Runner source outside the server build root. The successor moves only that test to the already-excluded test directory and adjusts four relative imports. An independent source peer confirms identical test bodies; `--listFilesOnly` confirms build-program exclusion. The failed head is retained. Corrected-head CI41599277833 passed48 jobs/1skip on attempt2 at08:17:23UTC. Attempt1 had46passing jobs but a server4 runner-shutdown cancellation; exactly that job and dependent verify were rerun once on the unchanged head. - Maps and diff checks pass. The initial loopback listen EPERM was a setup failure; the controlled transport test then reached the actual assertion RED. - No Paperclip app, database, browser, model, external provider or real credential ran locally. Full typecheck/build passed in corrected-head normal PR CI. Fresh Greptile5/5 is tied to that exact head, with zero unresolved threads. ## Risks The added description can affect API search ranking. It changes guidance only. It does not impersonate a person, change permissions or automatically select an agent. Future model behavior remains unqualified. The prior mixed failed journey is retained; its separate versioned grading work is not part of this PR. Keep this core PR unmerged. ## Model Used OpenAI `gpt-6.1-sol` with reasoning, source inspection and tool use. Context-window size was not exposed by the host. ## 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> |
||
|
|
1e3ab690b0 |
fix(routines): prevent recursive Sentry run repairs (#18714)
Validate Sentry failed-run alerts before routine dispatch. Reject repair-generated provenance and private sources. Deduplicate source runs through stable primary-key receipts, including setup retries and suppressed deliveries. Verified with 166 focused tests, server typecheck and build, generated-reference checks, passing required CI, and Greptile 5/5. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
58308a3e63 |
perf(apps): scope sidebar review counts to one connection (#18688)
## Thinking Path > - Paperclip helps people manage AI agents and their governed app access. > - The Apps sidebar displays a Review badge for one connection. > - It polls the full company attention service every 30 seconds to get two counts. > - That service also loads every profile, rule and active catalog entry. > - This pull request adds an authorized connection read for only those counts. > - The same badge now avoids unrelated profile evaluation and database result decoding. ## Linked Issues or Issue Description **Current behavior** The contextual sidebar polls company-wide attention. It needs only one connection's signed pending action count and whether any catalog entries are quarantined. **Proposed behavior** Both sidebar variants poll a connection-scoped count endpoint with existing connection read authority. The full attention API stays unchanged. **Reason and benefit** The retained 200-profile fixture performs seven service queries and returns 80,115 rows for this badge. The new reader performs one query and returns one row. This reduces local service CPU and avoids unnecessary decoded profile/rule data. Related merged evaluator fixes are already in the baseline: Refs #18673, Refs #18681. A search found no existing PR for this scoped count reader. ROADMAP.md contains no duplicate planned work. ## What Changed - Add the narrow shared DTO, typed client, guarded REST/OpenAPI route and catalog-owned reader. - Count independent aggregates with both company boundaries and exact signed-argument semantics. Keep archived counts at zero. - Update both contextual sidebar variants. Keep the exact badge, 30 second interval and existing attention-prefix invalidation. - Add PostgreSQL differential tests, authenticated HTTP tests, sidebar tests, a repeatable benchmark and access documentation. ## Verification - Baseline: `3f8ebfff8aa9febd0733c6f65b07485dcadfd0e2`, including both merged evaluator fixes. Service treatment: `0827f7d0b3`; route/fixture refinement: `230c42ee15`; final local existence gate: `390a218254`. - Passed: focused PostgreSQL differential test; assembled HTTP test with board session and agent JWT; 14 sidebar component tests; token gates; map checks; diff check. - Fixture: 200 profiles, 76,000 rules, 1,900 active tools, 695 removed references, 12 agents, a sibling connection and 2,000 unrelated historical invocations. Complete normalized two-count results are equal on both paths. - Quiet local measurement: seven alternating warm pairs after two warmup pairs, Node 24.22.2, macOS, real disposable PostgreSQL. Legacy/new service CPU median: 213.891/1.680 ms; ranges: 209.329–228.552/0.515–6.137 ms. Wall median: 156.867/1.248 ms; ranges: 151.739–162.289/1.115–1.386 ms. Queries: 7/1. Returned rows: 80,115/1. - Run `cd server && node --expose-gc --import ./node_modules/tsx/dist/loader.mjs scripts/benchmark-connection-review-counts.ts`. See `doc/connection-review-counts.md` for scope and failure history. Byte-volume accounting runs outside timing; no HTTP payload reduction is claimed. - Initial raw scalar SQL failed with ambiguous columns. Typed subqueries fixed it and passed real PostgreSQL equality. Initial fixture agents lacked permissions and correctly received 403. The final fixture grants explicit authority and tests revocation. An initial benchmark included byte serialization in timing; the reported run excludes it. - Passed required local `pnpm -r typecheck` and `pnpm build`. The initial sandbox typecheck could not open the tsx IPC socket; the escalated rerun passed. Full monolithic `pnpm test:run` was not run locally: repeating a 15–25 minute suite serially across both performance PRs adds no distinct evidence. Broad suite acceptance uses final-head CI, which runs the same general/serialized test partitions and the native Runner lane. Final-head CI is in progress. - Passed built-static Chromium journey on an owned local-trusted server and disposable PostgreSQL: Review 3 appears, only scoped counts are requested by this sidebar, resolving requests/clearing quarantines then reloading removes the badge, and archived counts are zero. The first ad hoc launcher omitted required decision-service options and failed at startup; using the normal no-op wake callback fixed that harness. - Greptile identified an invalid resolved-request fixture label. Corrected `denied` to the declared `rejected`, then reran the real PostgreSQL differential test. The count SQL and non-pending exclusion are unchanged. - The first final-head CI failed the static cross-tenant existence-oracle guard because the new explicit resource company assertion had no nearby `hasCompanyAccess` gate. Existing management admission already rejects foreign resources with 404. The route now also follows the standard local two-step gate. The unchanged guard plus authenticated HTTP negative cases passed (3 tests). No allowlist or test was weakened. - These local service measurements do not prove hosted performance. No deployment is included. ## Risks Low risk. New read admission reuses existing current-authority checks. The old attention API is unchanged. Counts still require database work over relevant pending/catalog rows. No cache, schema migration or telemetry is added. Roll back this PR if the badge differs or scoped reads deny previously permitted users. ## Model Used OpenAI `gpt-6.1-sol` (Sol 6.1), with reasoning, tool use and code execution. The host does not expose the configured context-window size. ## 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 - [ ] All Paperclip CI gates are green - [ ] 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> |
||
|
|
554a770c6f |
perf(apps): scope connection profile configuration reads (#18687)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The Apps Permissions page reads saved access profiles for one connection. > - The page fetched every company profile, rule, and binding. > - It used only the app profile and eligible task-granted profiles for that connection. > - This pull request adds a saved configuration read owned by the profile service. > - The route keeps current connection and per-profile management checks. > - The benefit is fewer database rows, less Node work, and a smaller response for the same displayed configuration. ## Linked Issues or Issue Description **Subsystem affected** Server profile configuration reads and the Apps Permissions and Review data caller. **Problem or motivation** One connection page loaded and serialized unrelated company profiles. In the local fixture, this produced a 32.35 MB response with 76,001 rule rows. **Proposed solution** Select the displayed connection's app profile and exact eligible connection-intent profiles before loading their entries and bindings. Return an explicit configuration DTO with complete selected arrays. Keep summary and pending-review work on the existing full API. **Alternatives considered** Filtering the full response in the browser retains the database and Node cost. Truncating the full API would break other clients. Filtering entries to the connection would lose selectors and bindings needed by saves. **Roadmap alignment** This improves the existing governed Apps capability. It does not add a new access policy. Related merged work: #18673 and #18681. Related harness cleanup: #18683. Public PR searches found no duplicate connection configuration projection. ## What Changed - Add `ToolProfileConfiguration` and `GET /api/tool-connections/{connectionId}/profile-configuration`. - Select `app:<connectionId>` at any status and exact active, correctly bound `connection_intent` profiles. Run the existing per-profile visibility filter before configuration queries. - Preserve complete ordered entries and bindings. Keep all save behavior, empty-binding install fallback, and the existing full profile API. - Switch AppDetail and AgentConnectionAccess to the configuration DTO. Use a distinct cache key below the existing profiles prefix so mutation and reconnect invalidations still refresh it. - Add real PostgreSQL differential and authenticated HTTP tests, UI caller coverage, a reproducible benchmark, and access-map documentation. ## Verification Baseline: `3f8ebfff8aa9febd0733c6f65b07485dcadfd0e2`, after both #18673 and #18681. Final head: `b40c1ed7c0833d403e20ee8a5ff9d09f1bad6d22`. Current implementation and reproduction details are in [the performance note](https://github.com/paperclipai/paperclip/blob/b40c1ed7c0833d403e20ee8a5ff9d09f1bad6d22/doc/performance/2026-11-02-connection-profile-configuration.md). - AppDetail: 79 tests passed. Existing access/action saves, install fallback, grant removal, errors, and refresh cases use the new caller. - Real PostgreSQL differential: passed. Full ordered selected configuration equals the old full-list selection. Cases cover extra bindings, sibling selectors, wrong source/key/status/binding, null and non-string agent metadata, fresh mutations, empty app bindings, visibility filtering, and company scope. - Production HTTP authentication: API key and run JWT cases passed. Hidden personal accounts omit spanning profiles. Private connection reads fail. Restored ownership restores visibility. Revoked management grants deny fresh calls. Final UID canonicalization and foreign-company 404 assertions passed on the current head. - Map structural check, token gate, and `git diff --check`: passed. - Required local `pnpm -r typecheck` and `pnpm build`: passed on the current head. `pnpm test:run` was started once. Its first chat shard passed (337 tests, 151.87 seconds); the second shard was intentionally interrupted after the complete final-head CI suite passed. This partial local attempt is not a full local test pass. The same stable-runner general and serialized partitions and Runner lanes passed in [final-head CI](https://github.com/paperclipai/paperclip/actions/runs/41538805231), including aggregate `ci / verify`. No local assertion failures were observed before interruption. - Final-head focused acceptance: four files, 85 tests passed. Full token gates, maps, and module boundaries passed. - Actual browser acceptance passed on the current checkout with isolated real PostgreSQL and a local trusted board. An action saved as Off persisted after reload. Both app agent bindings stayed intact. Removing an agent-only task grant persisted after reload. Requests used only the narrow profile endpoint. No browser exceptions occurred. No responses were mocked. No provider calls or hosted changes were made. The helper stopped the owned runtime and database after the check. - Final exact-head checks: 54 success, one clean neutral Paperclip Review, three skipped, zero pending or failures. Legacy Snyk status: success. No inline review comments. - Greptile reviewed exact head `b40c1ed7c0833d403e20ee8a5ff9d09f1bad6d22`: 5/5, no findings, completed successful check. Five alternating unprofiled pairs ran with real PostgreSQL on macOS/arm64, Node v24.22.2, and forced GC. The fixture has 210 profiles (200 heavy profiles plus ten eligibility cases), 76,001 rules, 1,900 active tools, 695 removed tools, and 12 agents. The selected configuration has two profiles and 1,391 rules. Each pair asserts complete ordered selected DTO equality. | Measurement | Full list | Configuration read | | --- | --- | --- | | Service wall ms, median [min, max] | 200.927 [193.319, 213.390] | 6.414 [5.508, 6.973] | | Node CPU ms, median [min, max] | 262.751 [246.727, 271.573] | 27.525 [22.576, 29.308] | | Select queries / returned rows | 7 / 78,139 | 3 / 1,397 | | Actual response JSON bytes | 32,353,534 | 592,233 | | Serialization wall ms, median [min, max] | 90.714 [88.715, 99.304] | 1.644 [1.637, 1.745] | | Serialization CPU ms, median [min, max] | 90.644 [88.569, 96.778] | 1.654 [1.637, 1.807] | Reproduce: `node --expose-gc --import ./server/node_modules/tsx/dist/loader.mjs server/scripts/benchmark-connection-profile-configuration.ts 5`. The benchmark measures the service and actual response JSON separately. It excludes HTTP and authorization work through an identity visibility callback. Node CPU does not include PostgreSQL CPU. No hosted or foreground-under-load improvement is claimed. Preserved failures: offline install lacked a locked tarball; frozen online install passed. Sandbox localhost and tsx IPC restrictions blocked initial DB/typecheck attempts; permitted retries use the same checks. The first HTTP suite needed the unbuilt plugin SDK; its owning build helper fixed setup. Shared typecheck caught a missing barrel export, now fixed. A broad typecheck inherited the host's unpinned nested pnpm executable; the owned stalled process was stopped and restarted with pinned pnpm 9. The complete current-head CI suite and aggregate verify passed. The separate Paperclip agent review published 5/5 with no findings at this head and completed with a clean neutral conclusion. An added foreign-company assertion expected 403; the existing guard correctly returned 404. Only the test expectation changed. The first real browser attempt saved and reloaded the action correctly, but grant deletion returned 422: the stress fixture had a company binding, which makes that profile the company default. The UI showed the existing deletion error. The second browser seed used the production agent-only intent shape and passed removal and reload. This does not change the stress fixture or product deletion gates. Initial external browser script attempts also needed an ESM extension and the correct dependency path. Logs, first-failure screenshots, traces, and result files are retained in the local evidence directory. ## Risks - SQL selection must stay aligned with connection-intent eligibility. Differential fixtures cover the exact current predicate. - A selected connection can still have many rules. This change preserves all selected configuration. - Per-profile authorization still reads current selected resources and can add work. Connection visibility does not bypass that check. - No migration, default-allow, effective-access, policy-review, provider, or deployment behavior changes. The old endpoint remains available. Rollback restores the old AppDetail caller and removes the additive endpoint. ## Model Used OpenAI Codex, `gpt-6.1-sol`, with code execution and repository tools. The host does not expose the context-window size. ## 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> |
||
|
|
49b3804830 |
fix(connections): load permissions and show AI account identity (#18653)
## Thinking Path > - Paperclip manages AI agents and their access to saved connections. > - The Permissions page loads account details and permitted agent choices. > - Company-wide AI accounts repeat retained installation checks for every candidate agent. > - This makes target checks grow with the square of the roster size. > - The browser waits without a deadline, and connection names do not identify the provider login. > - This pull request batches direct AI checks and bounds loading with recovery controls. > - Account details load separately and show a provider email or an explicit profile-permission limit. ## Linked Issues or Issue Description **What happened?** Opening a saved AI account can leave Permissions on “Loading AI account…” for minutes. The account itself can be connected. The existing capability batch excludes AI connections, so a company-wide install repeats checks for every installed agent for every candidate. Saved names and Paperclip ownership also do not establish which provider login a credential uses. **Expected behavior** Direct AI accounts reuse retained-target checks within one descriptive request. The page shows loaded account controls or a bounded failure with Retry. Permissions shows the provider email and organization when the selected credential permits profile access. An unavailable profile must not block maintenance or disconnect a working inference credential. Writes still authorize current permissions. **Steps to reproduce** 1. Save an AI account in a company with several agents. 2. Install the account company-wide. 3. Open its Permissions page. 4. Delay the account-list or grants response to exercise the loading failure. The maintained benchmark reproduces database work with synthetic local data. Refs #17945, which added batching for non-AI connections. Searches found no open PR for this AI-account loading fix. ## What Changed - Include direct AI accounts in the existing board capability batch. Referenced pool members, delegated actors, complex selectors, and mutation authorization keep their existing evaluator. - Add opt-in per-caller GET deadlines to the API client. A timed-out caller detaches the stuck coalesced request so Retry starts a fresh fetch. Other callers retain their own cancellation and results. - Apply a 15-second deadline to AI account lists and grants reads. The permissions view retries transient failures once and reports other failures immediately. - Share the parent Permissions grants query with AI account details. Keep loaded controls visible after a background error and offer Retry. - Add a grant-scoped identity GET and OpenAPI contract. The service checks current membership and credential audience before it reads the selected credential. It projects only the provider email and organization; provider IDs, tokens, and raw errors stay private. - Show account details independently of maintenance controls. Anthropic profile denial has an explicit message and preserves connected status. Transient profile failures have a separate Retry action. Observations are cached for five minutes under the company, connection, grant, and grant revision. - Extend real PostgreSQL and authenticated HTTP coverage, client/component regressions, map guidance, and the existing benchmark's AI mode. - Address the Apex file-size finding by moving gallery identity lookup into the existing app display helper module. `AppDetail.tsx` is 984 lines; lookup behavior is unchanged. ## Verification Local source checkout on macOS, Node 25.6.1, pnpm 9.15.4, and disposable PostgreSQL 18.1. Provider calls use fixtures. No hosted deployment or provider qualification is claimed. - Passed `pnpm -r typecheck` and `pnpm build`. - Passed all 87 tests in `connection-management.integration.test.ts` and all 33 tests in `connection-install-capabilities.test.ts`. - Passed 196 AI connection, identity-provider, and OpenAPI tests: `ai-connections.test.ts`, `ai-account-identity.test.ts`, and `openapi-routes.test.ts`. - Passed 107 focused UI/client tests: `client.test.ts`, `ManagedAiConnectionDetails.test.tsx`, `AiConnectionAccountDetails.test.tsx`, and `AppDetail.test.tsx`. - After the Apex extraction, passed UI typecheck/build and 106 component tests across `AppDetail.test.tsx`, `AppNotConnected.test.tsx`, `ManagedAiConnectionDetails.test.tsx`, and `AiConnectionAccountDetails.test.tsx`. - The authenticated HTTP tests compare complete grants responses across human roles, for tool and AI connections. They verify a real install mutation succeeds before revocation and fails afterward. - A company-wide personal AI fixture checks 20 targets instead of 400. Pool-member permission loss still denies the complete pool description. - UI states covered: shared cached grants, stuck account/grants fetches, one automatic retry, terminal failure, manual recovery, authorization failure, background failure with retained controls, and unmount cancellation. Client tests also cover stalled response bodies and independent coalesced callers. - Identity coverage includes actual bearer-key/JWT reads, private-user and cross-company denial before provider access, revoked credentials, provider 401/403/429/500, and grant-change isolation. A real local HTTP fixture stalls the response body, verifies the shared 15-second deadline, and keeps unrelated HTTP requests responsive. The test shortens the native deadline to 100 ms. Profile permission denial leaves the saved account connected. - Passed `pnpm check:token-gates`, `node scripts/check-maps.mjs`, `node scripts/check-module-boundaries.mjs`, and `git diff --check`. - The full local `pnpm test:run` started with the loading-only change. All six chat shards passed. It recorded one failure in the unchanged `heartbeat-retry-scheduling.test.ts` quota-rejection case. That exact case passed a focused rerun on the final head. The initial failure cause remains unconfirmed and its local log is retained; this is not a full local-suite pass. The redundant local run was stopped with SIGINT after final-head CI completed successfully. - [Final-head CI](https://github.com/paperclipai/paperclip/actions/runs/41515556725) passed on `ead8d276d4a2023614fff54b3e9a858f28c3178b`: typecheck, build, all 12 server shards, all 3 chat shards, workspace suites, all 9 serialized suites, all 8 end-to-end shards, Runner checks, and the aggregate verify gate. Security checks passed. Optional Storybook checks were skipped by workflow policy. GitHub reports the branch mergeable without conflicts. - Apex reviewed head `ead8d276d4a2023614fff54b3e9a858f28c3178b` at 5/5, confirmed the extraction resolves its earlier file-size finding, and has no unresolved review threads. The existing benchmark ran for the loading-only change now recorded in commit `5f725b36fa52002545241a5a8b7a10602f0cc0ce`, with `--purpose=ai --baseline-ref=026fd9e3452`. The identity follow-up leaves that batch source unchanged. Two warmups and five alternating samples exercise each of eight fixture shapes. Complete positional capabilities, reasons, and operations match the baseline in every sample. | AI fixture | SQL calls before → after | Target reads before → after | | --- | ---: | ---: | | 1 agent, company install | 9 → 9 | 1 → 1 | | 32 agents, company install | 5,248 → 164 | 1,024 → 32 | | 100 agents, company install | 50,400 → 504 | 10,000 → 100 | | 100 agents, partial install | 13,175 → 503 | 2,575 → 100 | These counts measure the real service over local PostgreSQL. They do not predict hosted page latency. To reproduce the standalone benchmark, use a checkout of commit `5f725b36fa52002545241a5a8b7a10602f0cc0ce` and run the command below. Its isolation check intentionally rejects the later identity follow-up's additional runtime changes. ```sh node --import ./server/node_modules/tsx/dist/esm/index.mjs scripts/benchmark-connection-install-capabilities.ts --purpose=ai --baseline-ref=026fd9e3452 ``` Run the final-head regression suites from the repository root: ```sh cd server pnpm exec vitest run src/__tests__/connection-management.integration.test.ts src/__tests__/connection-install-capabilities.test.ts src/__tests__/ai-connections.test.ts src/services/ai-account-identity.test.ts src/__tests__/openapi-routes.test.ts cd ../ui pnpm exec vitest run src/api/client.test.ts src/components/ai-connections/ManagedAiConnectionDetails.test.tsx src/components/ai-connections/AiConnectionAccountDetails.test.tsx src/pages/apps/AppDetail.test.tsx ``` ## Risks Descriptive permissions reuse retained facts within one request. A concurrent change can leave a displayed choice stale; the next request and every write still authorize fresh. Pools with referenced members retain their existing cost. The account and grants clients now bound each caller's wait. A browser abort does not cancel an authorization read already admitted by the server. The independent profile request uses a fixed Anthropic origin, a bounded body, no redirects, and a 15-second deadline. Inference-only credentials can return no email; that is reported without guessing from a Paperclip owner or changing connection health. Profiles are fetched only for the caller's authorized selected grant. No ambient CLI identity, persistent identity cache, background polling, or billing-identity change is added. Other API reads keep their existing behavior. No migration is required. Staging has not been redeployed. ## Model Used OpenAI `gpt-6.1-sol` through the Codex harness in T3 Code, with xhigh reasoning, code editing, execution, and test tools. The host does not expose the context-window size. ## 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 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> |
||
|
|
006d5691a6 |
feat(server): support volume-free web server handoff (#18518)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Its web server also owns controller admission and user commands. > - A rolling deployment starts a replacement before the old server exits. > - Shared database state alone does not preserve local agent files, browser chunks, or in-process commands. > - This pull request adds an opt-in ephemeral server contract and a local overlap test. > - The benefit is a web server that can relinquish traffic and local files without losing acknowledged work. ## Linked Issues or Issue Description Refs #18403, #18327, #18335, #18341. **Current behavior** The control-plane web server still has local agent-file authority and local command targets. A new container can miss saved bytes or send a command to the wrong process. An existing browser tab also needs chunks from its original build. **Proposed behavior** Eligible servers share only PostgreSQL, object storage and stable secrets. A replacement waits for readiness. The old server drains admitted requests, keeps owner commands available through handoff, and releases controller leases. Explicit maintenance adopts existing files before the volume is removed. **Reason and benefit** This is the application prerequisite for rolling app-only deployments. Schema migrations and permanent multi-replica operation are outside this change. ## What Changed - Store agent-file manifests in PostgreSQL and immutable bytes in S3. Treat local roots as disposable projections. Preserve existing permission checks and edit conflicts. - Add an explicit migration, eligibility and object-retention command to the production runtime bundle. - Add `/api/ready`, admitted-request drain, browser restart signaling and retained immutable UI chunks. - Carry transaction-aware work hints across controllers during handoff and active execution. Keep listeners closed when idle. - Deliver durable Stop and runtime responses to the execution owner. Recheck request lifecycle and access before response dispatch. - Reject local-only configuration in ephemeral mode. Keep the feature disabled for existing installations by default. - Add an isolated PostgreSQL/S3/proxy/browser deployment harness with independent server homes and retained failure evidence. ## Verification - All CI checks pass on `ce17b95019d3cf1cabd357d19b6d37f96782cc5c`: workspace typecheck, build, Runner checks, server/general tests and browser shards. Greptile reviewed the runtime changes at 5/5 with all findings resolved; the final commit updates maps and the plan. - Local targeted checks passed: coordinator 27; Stop/recovery 33; PostgreSQL command delivery 6; route/workspace regressions 8; bootstrap Stop 1; restart recovery 75. PostgreSQL command tests simulate the provider edge. - Local browser proof passed 91 reads and 19 saves; reconnect took 1,013ms. The tab opened Audit from S3 after deletion of A's whole home/build. This uses compiled workspace code and local-trusted auth, not released containers or live agents. - Local workspace typecheck/build passed; current server and runtime bundle passed. The earlier broad local suite was stopped after three hours and is not a passing receipt. Its retained failures and isolated reruns are documented in the plan. - Stacked #18526 adds stricter graceful-exit proof, an unready replacement control, live Runner qualification and a Stop handoff correction. Hosted enablement remains gated on those results and Cloud #1096. No hosted stack has been enabled. ## Risks - Apply migration 0422 before enabling the feature. Existing data must pass each owner's migration and eligibility checks. Do not discard a volume based on a single migration page. - Durable manifests remain authoritative after the opt-in is disabled. Rolling back to a binary that ignores them is unsupported. - Cold directory restoration and complete shutdown timing still need qualification before enabling a hosted stack. - Retained UI assets must cover the supported browser and rollback window. Cleanup is explicit; no new idle maintenance polling is introduced. - This change does not claim production zero downtime or authorize a fleet-wide enablement. ## Model Used OpenAI Codex, GPT-6 family. The exact serving model ID and context-window limit are not exposed in this session. Used reasoning, code editing, shell execution and browser automation. ## 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> |
||
|
|
66e8fab944 |
fix: complete native chat responses and improve API discovery (#18364)
## Thinking Path > - Paperclip must perform work and report its outcome accurately. > - A chat response and its delegated or operator-owned work are separate stages. > - Native chat contracts instead required literal completion of the user's whole request. > - Agents kept searching or waiting even when a truthful handoff should finish the response. > - OpenCode also has separate acknowledgement backlog and empty-response failure paths. > - This PR aligns chat completion with its current stage and makes OpenCode completion truthful. ## Linked Issues or Issue Description Refs #18098 for retained OpenCode evidence and #18377 for separate eval setup, oracle and reporting repairs. **What happened?** Real pinned OpenCode 1.18.34 reproduced an MCP timeout behind 800 text fragments. Pilot [41222196525](https://github.com/paperclipai/paperclip/actions/runs/41222196525) separately exposed a fabricated successful reply after a reasoning-only response. Later restart attempts timed out without an answer. Their native criterion literally required restarting the instance, although the chat instructions and authority required an operator handoff. General authority guidance alone did not resolve it. **Expected behavior:** A standing chat completes its current authorized response. It performs and verifies permitted actions, verifies delegated handoffs, or explains the specific unavailable action and operator path. The worker's task remains separate. Ordinary execution tasks must still complete their requested work. Empty provider responses must not become invented success. **Version and reproduction:** Actual pinned OpenCode and native Rust Runner, with retained full-stack AWS attempts. Product baseline master `51ea624344`. This PR adds no historical catalog to master. ## What Changed - Build standing-chat completion contracts from the server-owned conversation identity. Scope each current request to the response stage; preserve request text, criterion IDs, order, human-response scope and source revisions. Require verified authorized actions and truthful handoffs. Ordinary task contracts stay strict. Existing admitted inputs retain their saved contract during recovery; new contract content uses the existing revision/hash mechanism. - Guide prospective defaults to their owning resource and preserve existing work unless backfill is requested. Guide unavailable board/operator operations to the supported handoff. These are general chat instructions with no eval identities or expected answers. - Batch adjacent assistant text for at most 50 ms and 4,000 UTF-8 bytes. Preserve text, metadata and order; flush before non-text frames and shutdown. - Give an empty OpenCode response one correction within its existing turn, model, reasoning, context and deadline. Accept actual completed answer parts with incomplete headers. Preserve actual active tool work, bound idle evidence grace and fail truthfully if correction stays empty. - Reject empty OpenCode terminals in Rust without actual prose or an accepted semantic result. Never invent successful replies or evidence. Refresh generated project-discovery contracts. - Preserve accurate public API-discovery descriptions for existing project defaults and managed instance restart operations, including their field scope and authority limits. ## Verification **Qualified: [pilot 41256592937](https://github.com/paperclipai/paperclip/actions/runs/41256592937) passes 3/3, with all 35 independent checks passing.** | Case | Checks | Full case duration | Evidence and cleanup | | --- | --- | --- | --- | | Instance restart | 9/9 | 140.1 s | complete | | Project defaults | 11/11 | 204.2 s | complete | | Project work | 15/15 | 204.0 s | complete | All three are original-attempt results from source `c51e079f7f2364305b131ffd549e263ff78c5335`, containing the exact product files from `13c2b61f31d873a475ea0c3c026b19926ff42301`. Actual saved conversation contracts were inspected, as were ordinary native worker contracts where present. Each protected archive includes the actual API state and final PNG; cleanup completes. Original queries, `openrouter/deepseek/deepseek-v4.1-flash-1017`, finite case clocks, concurrency one and zero eval retries are preserved. Earlier passes do not transfer into this qualification. Both current heads pass all checks: product **62** (56 success, one neutral, five skipped), catalog **56** (54 success, one neutral, one skipped). Both have exact-head Greptile **5/5**, zero unresolved review threads and clean mergeability. The three trusted private-evidence protocol files remain byte-identical to the dispatch workflow. Raw evidence stays protected. - **2,044 API discovery tests pass.** Red controls showed that “restart instance” selected unrelated drain/settings operations and “save project defaults” selected repository replacement. Accurate public summaries now rank the existing operations correctly; route registration preserves descriptions. No endpoint, permission or field behavior changes. - **112 real-database, authenticated HTTP, native Runner and OpenAPI consumer tests pass locally**, using disposable task-owned Docker PostgreSQL. It was removed afterward; shared IPC was untouched. These cover persisted contract selection, admitted native input and the model envelope together. The isolated CI suites also pass. - **77 focused contract/input/context tests pass.** Red controls catch the wrong conversation predicate; canonical identity requires both agent and user IDs, including the actual heartbeat projection. Ordinary tasks, agent-only issues and planner tasks remain strict. Coalesced requests preserve criterion identities, source hashes and human-response scope. - **144 OpenCode driver tests pass**; native core **441 pass, one ignored**; packaging **56 pass, six skipped**. Server typecheck and build pass. - Real pinned OpenCode/native Runner: a delayed 25-second acknowledgement completes within the original 30-second MCP deadline. The text-fragment control changes 817 notifications / 54.1 seconds / timeout to 45 / 19.0 seconds / accepted finish. Separate fast, six-minute and real authenticated API probes complete 32 tool calls plus finish. These do not reproduce every remaining historical MCP timeout, and no common cause is claimed for all such failures. Retained failures remain attributed to their original source. Earlier runs exposed fabricated empty-response success, prospective-default backfill, a literal whole-task chat criterion, fixture readiness and oracle/reporting defects. The immediately preceding [41250502608](https://github.com/paperclipai/paperclip/actions/runs/41250502608), product `c0bf249807` / source `7b1de24ce1`, is **1/3**: work 15/15; restart 4/9 with the repaired contract actually admitted but no answer; defaults timed out before settlement, then exceeded the unchanged 256 MiB original-evidence allowance. Defaults domain behavior on that attempt remains inconclusive. #18377 retains the full attempt ledger, including missing archives and pre-paid cancellations. The fresh 3/3 establishes these selected outcomes; it does not establish full-catalog reliability. ## Risks The shared contract change applies to standing native chats across providers. It changes model instructions and completion scope, while permission, approval and review gates remain enforced by their existing owners. Incorrect model claims remain possible; the product evals independently require actual mutations, native work and delivery. The correction adds at most one provider request within the existing deadline; text batching can delay adjacent text by 50 ms. Three qualified attempts do not establish full-catalog reliability. ## Model Used OpenAI GPT-6 through Codex, with repository inspection, tool use, code execution and live eval tooling. Exact hosted model revision and context-window size are not exposed. ## 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 #` 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 - [x] Final same-source three-cell pilot passes 3/3 with required evidence and cleanup --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
8ff812cdde |
feat(chat): deliver proactive task messages to agent chats (#18374)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent Chat gives each person a persistent conversation with each
agent.
> - Tasks and routines produce results outside that conversation.
> - Those runs need a safe way to bring a requested result back to the
person.
> - The server must select the agent's own chat and check the source
task's access rules.
> - This pull request adds proactive chat messages and unread
indicators.
> - People can see the result, open its source task, and reply in the
same chat.
## Linked Issues or Issue Description
**Subsystem affected**
Server, native runner tools, shared contracts, database, and UI.
**Problem or motivation**
An agent can report progress on a task or comment on an authorized task.
It cannot send a routine result to its own standing chat with the
responsible person. A morning review routine therefore cannot leave a
visible digest in that person's chat.
**Proposed solution**
Add `send_chat_message({body, idempotencyKey})` and `POST
/api/agents/me/chat/messages`. The server selects the company, agent,
and responsible person from the active run. It saves an attributed
comment and audit record in one transaction. The chat shows a
source-task link and per-person unread state.
**Alternatives considered**
A destination parameter on a generic comment tool would expose
unnecessary routing authority. A new delivery worker would add another
durable delivery path. This change uses a dedicated tool and the
existing comment and live-event paths.
**Roadmap alignment**
This extends the experimental Agent Chat capability in `ROADMAP.md`. The
existing `enableAgentChat` setting controls it. Related public work:
Refs #13282, Refs #13283, and Refs #14408. Searches for proactive chat
and unread chat found no duplicate PR.
## What Changed
- Add a focused messaging service shared by the native tool and HTTP
route. It checks current run ownership, execution mode, recipient
membership, task access, and task privacy.
- Reuse the canonical company/agent/person conversation. Save the
comment and audit record together. Keep task completion and chat
execution separate from delivery.
- Add source-task provenance and source-scoped retry keys. Identical
retries return the saved receipt. Changed content returns a conflict.
- Enforce the conversation's fixed agent at the shared comment-write
boundary. Keep normal reply cancellation and session checks.
- Add chat unread state, accessible agent dots, aggregate Chat
indicators, and visible-message read cutoffs. Refresh through existing
live events.
- Add the request-key constraint, native tool contracts, documentation,
capability maps, stories, and regression coverage.
## Verification
- Local delivery/security proof: all 38 proactive-message cases pass.
Eight credential-scope regressions and four quota regressions fail
against the prior source. The repaired delivery and canonical
cross-issue cap suites pass all 49 cases; all 31 OpenAPI tests, server
typecheck, and capability-map checks pass. The shared per-run cap covers
native and HTTP sends, mixed ordinary mutations, concurrent last-slot
contention, and retry/recovery without double charging.
- Credential tests preserve the authenticated person and authorizer;
reject bridge/test credentials and persisted skill-test runs after
task-mode changes; and recheck API-key revocation, scope, and
responsible person after destination locking. The earlier
delivery/native-authority suites passed all 74 cases before the four
quota additions.
- Four PostgreSQL contention cases cover native and HTTP sends plus
retries during a native chat mutation. All four deadlock against the
earlier source and pass with destination-before-source/run locking.
Recovery and human-reply delivery enqueue only the human comment; a
proactive digest never wakes the chat agent.
- Passed 108 companion conversation, native-authority, and OpenAPI
tests; 112 runner catalog/workflow-report tests; 47 completion-delivery
tests; and 25 project-handoff tests. Focused runner tests cover native
tool availability without generic API tools.
- All 327 affected UI tests, UI typecheck, token gates, and catalog
checks pass. The read hook retries at most three times, respects server
retry delay, coalesces only seen cutoffs, recovers on reconnect, and
cancels on scope changes.
- Real browser journeys with a deterministic process agent verify
routine-to-chat delivery, first-chat creation, unread state across
reload, the source link, secondary chat navigation, and a follow-up
reply referring to the digest. This does not qualify live GitHub
retrieval or model output quality.
- The Storybook showcase uses production components with deterministic
network fixtures. Rendered interactions pass for the landing, ordinary
read success, transient read recovery, and persistent failure after
bounded retries. Both Chat navigation variants update from two unread
chats to one while only Kodi's dot clears. Mobile and light variants
were reviewed. The existing Coordinator Recents label contrast finding
is unchanged; no Linux visual baseline archive exists.
- Current master's independent sandbox migration retains 0418.
Regenerated the feature migration as 0419 from the combined schema and
retained exactly the newest five snapshots; numbering/safety, replay,
and schema-drift checks pass.
- Workspace typecheck and build passed before PR preparation; canonical
server/UI typechecks passed again on the combined tree. Migration
`0419_slippery_impossible_man.sql` passes safety and schema drift checks
on disposable PostgreSQL, including interrupted index repair, concurrent
migrators, one-time owner read-state backfill, preserved receipts, and
two SQL replays. A default five-second local snapshot comparison timed
out; a diagnostic 30-second run passed without drift.
- Earlier failures are retained in the record: full local `pnpm
test:run` and broad shards encountered embedded-PostgreSQL startup,
timing, and provider-recovery failures. Thirteen selected
external-channel cases pass in isolation on the original and changed
sources; an unordered comment-row assertion now explicitly orders both
reads. The earlier route registry, native tool count, runner catalog,
and accessible Chat badge-name failures were fixed and passed full
remote CI on `920b3bb7db`.
- Repeated managed-skill server8 failures came from CI's newer synthetic
merge: its Cloud guard correctly rejected the old local-disk fixture.
Master independently repaired the fixture in #18385; this branch keeps
that repair unchanged. All 57 managed-skill and Cloud prerequisite cases
pass on the combined tree, and that fixture has no remaining PR diff.
- Current reviewed head `dc16f314739a6d977bed21cb1446d0e80a541578`:
Greptile 5/5 with no findings, Superagent security assessment passed,
advisory Paperclip Review 5/5 with no findings, and zero open review
threads. All 55 current-head check runs pass, with five expected skips
and the advisory review neutral at 5/5; Snyk also passes. No checks are
pending or failing. Protected squash auto-merge is enabled and is
waiting for required code-owner approval. A normal exact-head merge was
refused by the base-branch policy; no admin bypass was used. The
previous head's Superagent quota finding is fixed and covered above.
## Risks
- Chat remains company-visible. Delivery rejects a source task with
private-task, project, or ancestor restrictions.
- Read state uses a comment timestamp cutoff. The server retains
database precision and never moves a read receipt backward.
- The new unique constraint changes comment query planning. An existing
regression now uses an explicit row order. The migration builds the
unique index concurrently, then takes a brief table lock to attach it.
It uses the existing top-level migration and interrupted-index recovery
path.
- The upgrade backfills missing chat-owner read-state rows at the latest
existing agent comment. Existing read receipts remain intact. New chats
can still be unread before the human participates.
- Quota observation commits atomically with delivery. A rejected send
rolls back its attempted denial audit row and any first-chat creation;
the canonical warning remains in server logs, and previously spent quota
remains enforced. Identical/conflicting retries and receipt recovery
spend no extra slot.
- This version delivers in-app only. It adds no external-channel, push,
or email delivery.
- Local full-suite acceptance remains limited by the failures recorded
above. The feature stays behind `enableAgentChat`.
## Model Used
OpenAI Codex. PR preparation and CI work used `gpt-6.1-sol` with tool
use and code execution. The earlier implementation also used Codex, but
its exact model ID was not recorded in the visible session. The
context-window size was not provided.
## 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 focused tests locally and they pass; the broad local
failures are recorded above
- [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>
|
||
|
|
6dcf4160dd |
perf(chat): load recent messages and page history on scroll (#18358)
## Thinking Path > - Paperclip lets people manage work done by agents. > - A standing agent chat can hold months of conversation. > - The chat route forced full-history mode, bypassing compact task loading. > - A cold visit fetched lifetime run summaries and hydrated historical transcripts. > - This PR starts with 20 recent messages and loads earlier pages on upward scroll. > - It preserves the reader's position, current questions, and draft while history grows. ## Linked Issues or Issue Description **What happened?** Standing agent chats forced full-history loading. A long conversation fetched full activity and run history before it became readable. Earlier comments also used an explicit load control. **Expected behavior** Open a small recent conversation quickly. Load older messages when the reader scrolls upward. Show an inline loading state. Keep the visible message and composer mounted through paging, live updates, and Back navigation. **Steps to reproduce** 1. Open a standing agent chat with hundreds of messages and completed runs. 2. Use a fresh browser query cache. 3. Observe full activity and run reads plus transcript requests for historical runs. 4. Compare the recent window and upward paging after this change. Related work: #18312 and #18323 restored compact task and collection loading. #14848 added reopening of visited agent chats. A GitHub search found no open PR for this chat-history optimization. ## What Changed - Return 20 contiguous recent comments for standing chats, with a stable oldest cursor. Keep the existing compact task exchange contract. Use the existing company/issue/time index for cursor reads. - Keep chats compact even if their task view previously expanded history. Retain observed messages through live catch-up and cache-backed return visits. - Keep fetched history pages, acknowledged sends, and the observed recent cursor separate. Reconcile live replies without losing history after a send or deleted cursor. - Hydrate metadata only for newly revealed message/run IDs; preserve prior batches while paging. Slow-send recovery refreshes the recent window. Receipts failures have an independent retry. - Hydrate run summaries only for loaded messages and the latest turn. Validate batches of at most 100 run IDs and keep the existing issue/company access rules. - Reuse hydrated terminal native streams when only history membership changes. Preserve retry, resume, mode changes, reconnect recovery, and interrupted recovery reads. - Load older messages, decision receipts, and task handoffs through upward scrolling. Support wheel, touch, and keyboard input. Keep pending questions visible. - Reserve a status slot while loading. Preserve a logical message anchor and the mounted composer. Pause failed page reads until an inline Retry. - Add database, route, controller, and scroll regressions, plus an opt-in production-build browser rig for long chats on desktop and mobile. Update design and feature/access documentation. ## Verification - The real PostgreSQL cold-open suite passes all 12 tests, including contiguous pages with equal timestamps, complete history, company denial, and scoped run IDs. - Activity route and OpenAPI suites pass 53 tests. The route run allowed local cold-import overhead; CI retains the repository's normal timeouts. - Three new paging regressions fail against the unchanged source. The candidate's focused paging/window suite passes 26 tests. Broader UI verification is recorded below when complete. - Desktop and mobile browser acceptance covers recent-only startup, per-frame reveal geometry, delayed and failed older pages, stable reading anchors and drafts, Back, older comment links, and one persisted live reply. - Full workspace typecheck and build passed locally. The final UI source also passes its typecheck and production build. - Affected UI suites pass: 212 controller/history/paging tests, 352 thread/scroller tests, 26 native-stream tests, plus metadata batching and live-cache hydration coverage. The issue mutation/access route suite passes 179 tests. - All 22 final desktop/mobile browser journeys pass, including delayed image growth. The isolated second-page request proof also passes six repeated desktop/mobile runs. A separate 6,000-message, 3,000-run fixture passes cold-open and delayed-paging checks on both screen sizes, still opening 20 messages and loading one 50-message page. - Final matched production timings use baseline `aa58fa1073` (`index-BaRiMbxd.js`) and candidate UI source `f9a26b9783` (`index-DPWTbsFF.js`, identical application source at final head `bf71ffd5af`). Chromium 151.0.7922.34, 1440×900, 4× CPU throttle, no added network latency, browser HTTP cache disabled, fresh application cache for each cold visit. Both variants block service workers and use the same warm local API server and immutable 600-message/300-run fixture. Only document HTML is intercepted for both variants; API requests are real. The common API revision is `109981f8c1`; subsequent commits change only UI and tests. | Usable conversation | Baseline median (range) | Candidate median (range) | | --- | --- | --- | | Cold direct URL | 6.57 s (5.69–7.45) | 3.07 s (2.50–3.28) | | Warm in-app return | 3.76 s (3.54–4.76) | 0.67 s (0.58–1.46) | | Back return | 2.53 s (2.12–4.20) | 0.51 s (0.47–1.18) | Five pairs alternate order. Every paired difference favors the candidate for all three journeys. Readiness requires a revealed, fully visible latest reply above the composer and an editable composer. Return timing starts after the Tasks route renders. Cold API requests fall from 953–954 to 71; API wire bytes fall from 517–559 KB to about 49 KB. Separate CPU profiles and request waterfalls confirm the removal of lifetime event hydration and are excluded from the five timing pairs. Both variants have the same expected missing optional plan document (404) and no browser exceptions. Control recent-window API reads have medians of 37 ms / 32 ms, with shared-host outliers up to 376 ms. The 6,000-message recent-window SQL uses `issue_comments_company_issue_created_at_idx` (22 index rows read for a 21-row probe). Geometry assertions retain a 2-pixel tolerance for the reading anchor and first-reveal layout. These are local synthetic measurements, not deployed staging latency. Earlier timing probes are excluded because they did not isolate service workers or composer clearance. Final head `bf71ffd5afb5684ba9105a3127f617e7adf5139c` has 55 successful checks, three optional skips and one neutral result, with no failures or pending checks. Greptile is 5/5 with no open review findings. ### Failure record Early local attempts exposed an unconditional scroller wrapper that changed the old task layout, and automatic retries on chat page failures. Both were corrected. The original task scroller structure stays in place. Metadata request assertions wait for initial connection recovery to settle, then verify only the new page’s reads; retry/resume/reconnect revalidation remains intentional. The initial review caught a send-before-scroll cache defect; its controller regression failed before the fix. Later rapid-paging checks caught scroll readiness being gated by metadata after the first reveal; readiness now stays enabled once revealed. Live replies use the separate acknowledged-message cache, then catch up from the server cursor. Visual review exposed a pagination wrapper bypassing existing composer clearance. The body is again a direct scroller child; cold-open browser checks require the full latest reply above the composer. A resize-observer regression failed before the fix and now passes. Controlled delayed inline images prove both pinned following and a stable reading anchor on desktop/mobile. The first performance fixture lacked valid native event envelopes and caused missing-log fallbacks. It is excluded from latency claims. The final fixture uses saved native assistant events and conversation session generations. The live test uses a temporary fixture-agent key, attributes the reply to an existing synthetic run, and revokes the key. It starts no model or agent process. Local database attempts failed during a Docker shutdown. The retained disposable database was restored and its suite passed. Early fixture errors included a missing conversation identity field and session generation. Mobile test setup also selected an offscreen sidebar link and reused queued human input. Optional announcements could intercept input; the fixture now dismisses them before the journeys. The scroll observer now waits for the delivered wheel event and settled animation frames, including when the viewport is already at the top. A concurrently running live test briefly changed another probe’s row count; the final acceptance and timing runs were sequential on separate fixtures. The final rig uses the visible mobile navigation and a saved agent reply. Anchor observation waits for the user's scroll to settle before measuring the effect of live updates. The full local `pnpm test:run` attempt hit repeated embedded PostgreSQL initialization failures and failures in untouched native-session executor suites; it was stopped after the repeated environment failures. The targeted real-PostgreSQL suite passed, and final-head CI is required before handoff. The first CI run also caught an exact mock expectation missing the added company scope; the expectation is updated and its 179-test suite passes. The final-head first CI attempt also stalled an assigned child task in the existing plan-approval scenario, then hit a heartbeat-run row lock timeout during fixture cleanup. The failed shard and aggregate check passed on an unchanged rerun of the same commit. Scenario assertions and timeouts were unchanged. The failure and rerun are retained in [CI run 41116120430](https://github.com/paperclipai/paperclip/actions/runs/41116120430). ## Risks - Historical transcript diagnostics no longer load on a cold chat open. Saved messages and conversation events remain available; diagnostic run views keep their full records. - Cursor paging must not omit comments with equal timestamps. Database tests cover timestamp/UUID ordering and the complete set. - Prepended or late content can move the reader. Browser checks cover retained row identity, viewport offsets, mounted drafts, and Back. Previously loaded pages stay in the query cache for its existing lifetime. - Local synthetic evidence does not establish deployed staging latency. This PR does not merge or deploy the change. ## Model Used OpenAI Codex, GPT-6 family, with reasoning, tool use, and code execution. The exact backend deployment ID and context window were 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 (affected checks; limits are recorded above) - [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> |
||
|
|
aa58fa1073 |
perf(ui): restore fast Coordinator task and inbox loading (#18323)
## Thinking Path > - Paperclip lets people manage work done by agents. > - Tasks, Inbox, and Needs You must stay usable as work history grows. > - The Coordinator views bypassed some existing collection optimizations. > - Navigation fetched complete decision cards for a count, and Tasks mounted every loaded row. > - This PR uses the canonical attention rules for small count and task-ID responses. It also restores bounded rendering and the optimized Inbox selection. > - The benefit is less data and less initial rendering work without a second definition of pending work. ## Linked Issues or Issue Description **What happened?** Coordinator navigation fetched the full attention feed on every cold collection visit. Tasks rendered the entire loaded collection. Coordinator could also select the old Inbox loader when Streamlined UI was off. **Expected behavior** Fetch only the projection needed by each surface. Start with a bounded set of rows. Keep complete task membership, accurate counts, current permissions, and stable loaded rows. **Steps to reproduce** 1. Enable Coordinator and open Tasks or Inbox with an empty browser query cache. 2. Observe a complete attention-card download for the navigation badge. 3. Open a large task list and observe that every loaded row mounts at once. 4. Disable Streamlined UI while keeping Coordinator enabled, then open Mine and observe three overlapping compact task reads. Related work: #17756, #3744, #17983, and #18312. ## What Changed - Add board-only attention count and complete task-ID projections. Reuse canonical company, privacy, addressee, deduplication, dismissal, snooze, and archive rules. Skip card bodies, plan/image reads, and presentation roster lookup. - Share the small count read across desktop and mobile navigation. Reuse the page-owned response on Needs You and Needs attention. Keep the newest known count through navigation, including zero. - Use the task-ID index for Needs attention and keep task row hydration in batches of at most 100. - Render 100 task rows initially, across the whole expanded tree. Reveal 150 more at a time through scrolling or a button. Keep existing rows through refresh and append. Defer offscreen leaf layout with shared tokens. - Keep Coordinator on the optimized Inbox when Streamlined UI is off. Use index reads for expanded project recents. - Add real-database, component, and production-build browser regression checks. Update the access and feature maps. ## Verification - All required CI checks pass on `1c68ac8957490a6c8585ba0939355f536c8120bf`: typecheck, build, server/workspace tests, serialized database suites, and eight E2E shards. Greptile is **5/5**, with its row-retention finding fixed and the review thread resolved. Storybook checks were intentionally skipped; Paperclip Review completed neutral. - Workspace typecheck and production build passed locally. The final rebased source also passed UI typecheck and production build. Token gates and map checks passed. - The attention service suite passed all 34 tests on disposable PostgreSQL. Final targeted checks cover compact projections, complete-set counts, quality approvals, snoozes, private tasks, and board/company access. Database response capture confirms compact projections do not read plans or images. All 31 OpenAPI tests passed after registering both endpoint contracts. - Six production-build browser journeys passed: desktop, mobile, Coordinator Inbox with Streamlined UI off, Needs You, Needs attention, and an older server page followed by task open/Back. They verify the initial 100-row budget, scrolling, retained DOM identity/height, one owner per attention read, complete task membership, 220 retained rows on Back, and less than 2 px of return-position difference. - The row-budget and Inbox regressions fail against original source and pass with this change. The final three affected UI suites passed 40 tests, and the broader Inbox/IssuesList suites passed 101 tests serially. ### Controlled production-build comparison Baseline `b31ec0a2dc079e32272a8f618425fe92b68bbaec` versus PR head `1c68ac8957490a6c8585ba0939355f536c8120bf`. Isolated baseline asset `index-CS9uPxMx.js`; candidate asset `index-BxJeTQ30.js`. Both UI builds used the same warm disposable API server and database, with 220 tasks and 110 pending board approvals. Chromium **151.0.7922.34**, 1440 × 900 viewport, **4× CPU throttle**, loopback with no added network latency, alternating baseline/candidate order. Cold cases use a new browser context with network cache disabled; warm cases navigate within the same context. Other host workloads are uncontrolled. Readiness requires the destination collection's first visible task row, then two animation frames. Inbox readiness waits for its canonical Mine route so previous-page rows cannot satisfy the measurement. Five paired samples per case; cold Inbox has a second five-pair confirmation wave because the first result was slower. Times below are **milliseconds**; parentheses show min–max. Paired savings are the median of individual before-minus-after pairs, not the difference between medians. | Journey | Pairs | Baseline median (range) | Candidate median (range) | Median paired saving | Faster pairs | | --- | ---: | ---: | ---: | ---: | ---: | | Cold Tasks | 5 | 2752 (2542–3025) | 2300 (2275–2524) | 328 | 5/5 | | Cold Inbox | 10 | 2536 (2283–2927) | 2582 (2117–2856) | 37 | 5/10 | | Warm Tasks navigation | 5 | 2054 (1826–2479) | 1448 (1419–1548) | 635 | 5/5 | | Warm Inbox navigation | 5 | 2020 (1485–2254) | 1568 (1289–1848) | 350 | 5/5 | | Task → Back to Tasks | 5 | 1747 (1609–1948) | 1327 (1238–1355) | 472 | 5/5 | Cold Tasks, warm Tasks, warm Inbox, and Back improved in every measured pair. **Cold Inbox latency is inconclusive:** the two waves pointed in opposite directions, with the candidate faster in 5/10 pairs. This PR claims less badge work there, not a proven cold Inbox latency improvement. The canonical badge response shrank from **200,039 to 359 decoded bytes** (encoded HTTP transfer: **8,125 to 598 bytes**). The compact response also skips card payloads, plans, images, and presentation-only roster work. These are measured mechanism reductions, separate from end-to-end latency. Needs You still owns its full card feed; Needs attention owns its complete task-ID projection. Reproduction setup and assertions are in `tests/perf/collections/README.md`. <details> <summary>Individual paired readiness measurements (ms)</summary> | Journey | Pair | Baseline | Candidate | | --- | ---: | ---: | ---: | | Cold Tasks | 1 | 3025 | 2283 | | Cold Tasks | 2 | 2542 | 2275 | | Cold Tasks | 3 | 2752 | 2424 | | Cold Tasks | 4 | 2783 | 2300 | | Cold Tasks | 5 | 2641 | 2524 | | Cold Inbox | 1 | 2927 | 2702 | | Cold Inbox | 2 | 2422 | 2629 | | Cold Inbox | 3 | 2409 | 2606 | | Cold Inbox | 4 | 2621 | 2769 | | Cold Inbox | 5 | 2539 | 2856 | | Cold Inbox | 6 | 2283 | 2117 | | Cold Inbox | 7 | 2466 | 2558 | | Cold Inbox | 8 | 2674 | 2286 | | Cold Inbox | 9 | 2696 | 2389 | | Cold Inbox | 10 | 2533 | 2183 | | Warm Tasks navigation | 1 | 2479 | 1423 | | Warm Tasks navigation | 2 | 2228 | 1548 | | Warm Tasks navigation | 3 | 2054 | 1419 | | Warm Tasks navigation | 4 | 1826 | 1448 | | Warm Tasks navigation | 5 | 1954 | 1518 | | Warm Inbox navigation | 1 | 2020 | 1661 | | Warm Inbox navigation | 2 | 2198 | 1848 | | Warm Inbox navigation | 3 | 1733 | 1497 | | Warm Inbox navigation | 4 | 2254 | 1568 | | Warm Inbox navigation | 5 | 1485 | 1289 | | Task → Back to Tasks | 1 | 1721 | 1330 | | Task → Back to Tasks | 2 | 1747 | 1275 | | Task → Back to Tasks | 3 | 1948 | 1355 | | Task → Back to Tasks | 4 | 1609 | 1327 | | Task → Back to Tasks | 5 | 1790 | 1238 | </details> ### Failed attempts and limits - The local full repository command hit embedded PostgreSQL startup failures and 16 failures in the untouched native session executor suite; it was stopped. The full local suite is not claimed green. CI on the final revision provides the broad test result. - A concurrent local UI run hit timing failures under host contention. Inbox/IssuesList subsequently passed all 101 tests serially. No tests were weakened to address these timeouts. - CI caught two integration gaps introduced during this work: missing OpenAPI entries for the new endpoints, and an attention fixture incompatible with the routine filters added on master. Both were fixed; the affected 31 OpenAPI and 40 UI tests pass. - The final revision's first CI attempt failed two untouched tests: scheduled-retry batch cancellation and the tool-access audience/archive race. Both failed jobs passed when rerun without changing the revision. Their original failed attempts remain visible in [the workflow history](https://github.com/paperclipai/paperclip/actions/runs/41083379875). - Browser evidence uses synthetic disposable data. This PR is not deployed staging or production verification. ## Risks - New attention sources must preserve canonical eligibility. Both projections call the existing collector, so they share its permissions and complete-set behavior. - The count and task-ID caches must not stand in for full decision cards. Each projection has its own key under the existing attention invalidation prefix. - A row budget can omit descendants if applied per parent. The tests cover one global budget across a large expanded tree, refresh, append, reset, and incremental loading. - Local browser evidence uses synthetic data and is not staging deployment proof. ## Model Used OpenAI Codex, GPT-6 family, with reasoning, tool use, and code execution. The exact backend model deployment ID and context window were 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 (affected suites; broad-run limitations are listed above) - [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> |