mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-09 16:02:36 +01:00
da34bcd48d67d4e457dd2cfc92cb1c53eb7a0918
2182
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5c185db77c |
fix(tree-control): resume work a subtree pause interrupted (#19394)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Task tree control lets the board pause a task tree and then resume
it.
> - A pause stops the runs that are active in the tree. The release must
then continue the work that was pending.
> - A child task whose run was active at pause time did not start again
after release. The child became blocked and asked for a user message.
> - A pause is an operator hold, not a failure. It must not strand work,
and it must not require reconciliation for a run that never started.
> - This pull request re-dispatches interrupted work one time on
release, and it settles a legacy Stop that never entered the adapter.
> - The benefit is that pause and resume keep a task tree moving without
manual recovery, and real safety gates stay in force.
## Linked Issues or Issue Description
**What happened?**
A parent task delegated two children. One child's run was active (in
host workspace preparation, before the adapter started) when the board
paused the parent's tree. The pause cancelled that run. After the board
released the hold, no new run started for that child. The child became
`blocked` with the execution blocker cause
`legacy_execution_requires_reconciliation`. Later the next action became
"Send a message to continue." A sibling child that had no active run at
pause time resumed normally.
**Expected behavior**
A run that a subtree pause cancels is an operator hold. On release, the
pending work starts again one time, automatically. It does not go
through execution reconciliation when the run never entered its adapter.
It does not wait for a user message.
**Steps to reproduce**
1. Create a parent task and a child task that is assigned to an agent.
2. Wake the child. While its run is in host preparation (or queued),
call `POST /api/issues/:parentId/tree-holds` with `{ "mode": "pause" }`.
3. Call `POST /api/issues/:parentId/tree-holds/:holdId/release` without
`wakeAgents`.
4. Before this change, no new run starts, and `getExecutionBlocker`
reports `legacy_execution_requires_reconciliation` for the child.
**Root cause**
- (a) The pause records each interrupted run in the hold member snapshot
(`activeRunId`). Nothing used that record on release.
- (b) Release woke tasks only when `metadata.wakeAgents === true`.
Without it, release relied on stranded-issue recovery. That recovery
stands down when the latest run was cancelled by an operator
(`isOperatorCancelledRun`). So the interrupted child had no owner.
- (c) The pause Stop of a legacy run in preparation is written directly
as `cancelled`. Legacy terminalization classifies a cancelled run with
no bootstrap evidence as an unknown outcome. It records
`legacy_execution_requires_reconciliation`, although the gated dispatch
never entered the adapter.
- Native runs: the verified native Stop does not create the
reconciliation hold, but gap (b) applies to native runs too. The release
change is runtime-independent.
## What Changed
- `issueTreeControlService.findOwedPauseResume` decides if a task is
owed one resume. The task's latest run must be an operator Stop that a
pause hold member snapshot names, recorded while that hold was active.
The task must still be executable by the same agent, and no pause may
still cover it. The obligation belongs to the run, so overlapping pauses
keep it until the last covering release.
- New `services/issue-tree-resume.ts` re-dispatches owed work with wake
reason `issue_tree_resumed` (`holdId` and `interruptedRunId` in the
context). A compare-and-set claim (`treeHoldResume`) on the interrupted
run makes the resume happen at most once. A failed wake returns the
claim.
- Pause release and `mode: "resume"` call it for every member of the
released hold, also without `wakeAgents`. When `wakeAgents` is true, the
existing loop wakes every executable member.
- A task that still has an execution blocker is not replayed. The
release writes `issue.tree_resume_wake_skipped`, the blocker stays
visible, and the obligation stays unclaimed.
- `releaseHold` updates only an `active` hold. A repeated release gets
`409`.
- `settleCancelledLegacyPreparation`: when the executor that owned a
cancelled legacy run proves that dispatch never entered the adapter, it
records bootstrap evidence and resolves that run's
`legacy-execution:<runId>` hold as `false_positive`. It also corrects an
automatic no-replay disposition and restores a `blocked` status only
when that disposition made the change. Then it re-dispatches owed work,
for a release that happened while the Stop was unwinding. It requires a
requested Stop with no existing recovery receipt, and no process,
session, native finalization, unreleased lease, or execution-start
event.
- `settleUnrecoverableExecutions` waits while a legacy executor still
holds its controller lease, and records a `legacyCancellationBlock`
receipt when it blocks a task for a cancelled legacy run.
- Docs: `doc/execution-semantics.md` and `doc/composer-stop.md` describe
the resume rule.
## Verification
- New regression
`server/src/__tests__/issue-tree-pause-resume-run.test.ts` uses embedded
Postgres, the real heartbeat, and the real tree-control routes:
- A run paused during preparation: nothing runs during the hold, there
is no blocker, and release creates exactly one `issue_tree_resumed` run
that succeeds. A second release returns `409` and creates no run.
- Release before the stopped preparation finishes unwinding (with the
periodic disposition run in between): exactly one resumed run after the
executor settles.
- An automatic no-replay disposition that ran first: the blocker clears,
the status returns to `in_progress`, and release resumes once.
- Overlapping child and parent pauses: no resume while the parent pause
remains, then exactly one resume after it is released.
- A queued run cancelled by the pause: release creates exactly one
resumed run.
- A task reassigned to a user during the hold: no resume.
- A mid-turn legacy Stop with unknown outcome: no replay, the
reconciliation blocker remains, and `issue.tree_resume_wake_skipped` is
logged.
- Red before each fix: the first 4 cases failed on master; the 3 review
cases failed on the first PR commit. All 7 pass now.
- Also passed locally: `issue-tree-control-routes`,
`issue-tree-control-service`, `heartbeat-workspace-busy`,
`heartbeat-undispatched-execution-wait`, `execution-control-work`,
`legacy-workspace-restore-recovery`, `legacy-continuation-authority`,
`task-retry`, `legacy-execution-recovery`, `agent-conversations`,
`heartbeat-retry-scheduling`, `satisfied-connection-intents`.
- Not verified here: a live staging pause/resume. Re-check on staging
after deploy.
## Risks
- Behavior change: release now starts the work that the pause
interrupted, also without `wakeAgents`. Before, that work stayed idle.
Other idle tasks still wake only with `wakeAgents`.
- The settlement trusts the executor's adapter-entry flag. The flag is
set only inside the atomic dispatch gate, which refuses a cancelled run.
The settlement also requires no process, session, lease, or execution
event.
- A native run whose Stop has not finalized at release time is not a
target. Stranded recovery and explicit wake still apply.
## Model Used
- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, used through Claude
Code with tool use (shell, file edit, test 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>
|
||
|
|
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>
|
||
|
|
879140cbbd | Retain ACPX read events before idle sidecar replacement (#19290) | ||
|
|
958f9aa9f3 |
feat(issues): limit agent-created task trees to parent and child (#19312)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Agents split work into tasks. A task can have a parent task. > - Today agents can nest tasks to any depth: parent, child, grandchild, and deeper. > - Deep agent-made trees are hard to follow, and an agent that works on a child task makes grandchildren by default. > - This pull request limits agent-created trees to two levels: a top-level task and its children. > - A request for a grandchild does not fail. The server puts the new task beside the requester and tells the agent. > - Board users keep full control. They can still nest to any depth. Existing deep trees stay valid. > - The pull request also records which task created each new task, so the audit trail stays complete when the parent changes. ## Linked Issues or Issue Description No public issue exists. This is the feature description. Related: #16853 (budgets that include descendant work) also reasons about task trees; this change does not alter its traversal. **Subsystem affected** Issues (tasks): create, child create and move in the issue service and routes. Agent tools, chat suggestions and creation provenance use these paths. **Problem or motivation** Agents create subtasks as deep as they want. An agent that runs on a child task gets that child as the default parent, so its follow-ups become grandchildren. The trees become deep and hard to read. The parent link is also the only record of which task created a follow-up, so that record is lost when the parent changes. **Proposed solution** Limit agent-created trees to a parent and its children. When an agent asks for a grandchild, put the new task under the requester's parent and return `parentAdjusted`. Do not reject the request. Keep board users unrestricted. Record the originating task in a new server-set `origin_issue_id` column. Keep `origin_run_id` as the originating run. **Alternatives considered** Reject grandchild requests with an error. We did not do this, because agents then fail mid-task. Add a database constraint. We did not do this, because existing deep trees must stay valid and nesting can return later. ## What Changed - Add `MAX_AGENT_TASK_HIERARCHY_DEPTH` (2) and the `IssueParentAdjustment` type in `packages/shared`. - Add `resolveNewTaskParent` and `planIssueParentChange` in `server/src/services/issues.ts`. `create`, `createChild` and `update` use the agent limit by default (`parentDepthPolicy: "flatten"`). - The issue create, `/children` and PATCH routes pass `allow_nested` for board users. For agents, the routes check authorization against the parent that the server actually writes. - The requester keeps its behavior when the server moves a task beside it: `blockParentUntilDone` blocks the requester, and the new task inherits the requester's workspace and privacy. - The server never moves a task above a low-trust boundary root that contains the requester. - These subsystems keep any depth, because they find their work by parent: watchdog reviews, monitor recovery, email-channel placement, and routines that a person configured. - An agent move of a task that has children skips only the parent change and returns `reason: "has_children"`. - Add `issues.origin_issue_id` (nullable, `on delete set null`, indexed) in migration `0434`. Only the server sets it. The `createdFrom` detail field, the `createdFromIssueId` filter and task-creation provenance read it first. - Chat prompts now let a chat agent create one task, a task with children, or separate tasks. The conversation is never a parent. - The runner `create_task` tool accepts an optional `parentTaskId`. A chat agent can create a top-level task and then put subtasks under it. The run must be able to read and change the parent. A conversation is never a parent. The depth limit still applies. Project tools map the field to `parentId` through the issue route. The protocol manifest, semantic contracts and eval fixture are regenerated. - Watchdog runs that have an active watchdog keep their requested parent on all create paths, because their access is limited to the watched subtree. - Fix a bug that the test drive found: the chat (MCP) `create_task` ignored the requested `status`, so a `backlog` task became `todo` and started to run. It now uses the same rule as the native tool. - Update `skills/paperclip`, `doc/SPEC-implementation.md`, and add the plan `doc/plans/2026-11-05-two-level-task-hierarchy.md`. ## Verification - `cd server && npx vitest run src/__tests__/agent-task-hierarchy-depth.test.ts src/__tests__/agent-task-hierarchy-depth-routes.test.ts`: 19 new tests for service and route behavior, all pass. - Related suites (64 files: issues service, thread interactions, privacy, watchdogs, provenance, runner tool authority, decisions, routines, project tools): 1208 tests pass. Two tests changed because of the intended behavior and pass now. Two `email-channels.integration` tests fail locally because the test cannot find a bundled skill file from this worktree path. This change does not touch that code. - `packages/db`: migration safety, snapshot drift and journal tests pass. - Typecheck passes for `server`, `packages/shared`, `packages/db`, `cli` and `ui` (local `ui` errors come only from generated Storybook catalog files that are absent). - Local test drive on an isolated instance with real Claude agents: 29 of 29 use cases pass, including an agent on a child task (follow-ups placed beside it, with the notice) and a chat that creates a top-level task with three subtasks. - I did not run the full `pnpm test:run` or `pnpm build` locally. CI runs them. ## Risks - Behavior change for agents: some follow-ups that were grandchildren are now siblings. The parent of the requester is woken when all its children finish, which now includes these tasks. - The runner `create_task` input contract adds one optional field, so the protocol manifest and the pack contract change. The tool output does not change: it shows the actual `parentId`, and the activity log records `parentAdjusted`. - Migration `0434` adds a nullable column, a foreign key and an index. It does not backfill rows. - `IssueCreationSource.run` is now nullable. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use (shell, file edits, local 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 - [ ] 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> |
||
|
|
5ab7b78f2b | Runner: retain the provider across hosted park and reclaim (#19140) | ||
|
|
96516a8622 | Keep ACPX output while Core reconnects (#19365) | ||
|
|
c9c4949c86 |
build(deps): bump zod from 4.4.3 to 4.6.6 (#17364)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The shared validators use zod to check API input, and the Runner contracts check some of the same values again > - zod below 4.6.6 has a high-severity memory issue (GHSA-mrhx-c7xc-m3w5), so this bump is necessary > - zod 4.6 also changes string `min`, `max`, and `length`: they now count Unicode code points, not UTF-16 units > - The Runner question parser and the goal readers still use JavaScript string length, so emoji text can pass zod and then fail later > - This pull request bumps zod and keeps the string-length bound on those schemas > - The benefit is that we get the security fix, and these inputs still fail at validation ## Linked Issues or Issue Description Refs GHSA-mrhx-c7xc-m3w5. **What happened?** With zod 4.6, three CI tests failed on this bump. Two showed an emoji body that is over the agent commentary ceiling and that zod now accepts. The third is the scheduler timeout described under Verification. **Expected behavior** A dependency bump does not widen an API limit. Text over the limit gets a validation error. **Impact** Emoji text between the two bounds passed validation and failed later: - A question set is created, and then each answer gets a 422. The question stays pending and nobody can answer it. A schema and parser run confirmed the mismatch. This was not run over HTTP. - The Muse route returns a 500 because `parsePaperclipQuestionSet` throws an error that is not an `HttpError`. This comes from reading the code. It was not run. - A Runner goal normalizes to null and replaces the stored goal, or it is marked blocked. This comes from reading the code. It was not run. ## What Changed - Bump zod to `^4.6.6`. The lockfile resolves 4.6.7. (Dependabot) - Add `maxStringLength` to `packages/shared/src/validators/text.ts`. It checks zod `max` and JavaScript string length. - Use `maxStringLength` for the question-set text fields, the Runner goal objective, and the agent commentary body. - Add regression tests for the question-set bound and the goal objective bound. - Dependabot recreated the branch on current `master` (`@dependabot recreate`) to resolve `pnpm-lock.yaml` conflicts. This happened twice. The lockfile is Dependabot's. Each time, the two fix commits were applied again without changes. ## Verification - Compared zod 4.4.3, 4.6.5, 4.6.6, and 4.6.7 directly. The change to `max` is in 4.6.5 or earlier, and 4.4.3 does not have it. - Passed locally: `packages/shared` tests `agent-commentary`, `validators/issue`, and `runner-goal`, plus `tsc --noEmit` in `packages/shared`. - Passed locally on the first fix commit: server `agent-commentary.integration`, `agent-commentary-helper`, and `openapi-routes`. The later commit changes these schemas again. CI passed these tests at `b5e9dc7352`. - Local testing was focused. GitHub CI is the full regression check. CI passed at `b5e9dc7352`, including `ci / verify` and `ci / e2e`. - Greptile reviewed `b5e9dc7352`. The score is 5/5, with no inline comments. - After the recreate, the fix commits applied cleanly as `bad62c3ba5` and `3294552e65` on Dependabot's `46789d1487`. Their diff is the same as the diff Greptile reviewed. The lockfile still resolves zod 4.6.7. The focused `packages/shared` tests (`validators/issue`, `runner-goal`) and `tsc --noEmit` in `packages/shared` passed again locally. A second `pnpm-lock.yaml` conflict came before CI finished at `3294552e65`. - Second recreate: Dependabot ignored the first `@dependabot recreate` (no reaction and no update job after 20 minutes). It acted on the second command and pushed `83c328c15c`. The fix commits applied cleanly as `dd3e87d93e` and `95b450048f`, with the same diff. The lockfile still resolves zod 4.6.7. The same focused tests and `tsc --noEmit` passed again locally. Greptile first scored `95b450048f` 4/5 with one P2: the description did not explain the source changes. Greptile read Dependabot's replacement description, which was live during that review. The description was restored and the thread resolved. The new review scored `95b450048f` 5/5 with no findings. CI failed at `95b450048f` in two shards. Neither failure comes from the zod change: - `server (9/12)`: `server/scripts/reporting-lock-policy.test.mjs` fails with "Reporting lock changed; review the dependency correction before install". #19283 added this test today. It requires the committed `pnpm-lock.yaml` hash to match `tests/runner-e2e/report-lock.json`. Every pull request that changes the lockfile fails it until the hashes are renewed, for example #17172 and #17911. - `workspaces-a (1/2)`: `cli/src/__tests__/company-import-export-e2e.test.ts` timed out waiting for its child server. The same test failed the same way on two unrelated branches in the same hour (runs 42115721271 and 42109846042). A rerun of this shard at `95b450048f` passed. The rerun of `server (9/12)` failed again on the reporting-lock test. - #19356 fixed the reporting-lock gate for Dependabot lockfiles and merged. A re-run reuses the old merge commit, so the empty commit `482d76686e` starts a run against current `master`. `pnpm-lock.yaml` and the reporting-lock proof on `master` did not change since this branch's base. - CI at `482d76686e`: `ci / policy` passed, and `server (9/12)` skipped the reporting-lock test as #19356 intends. `e2e shard (6/8)` failed once in `cloud-auth.spec.ts` ("apiResponse.json: Response has been disposed", a Playwright race in a route handler). The same test passed at `95b450048f` with the same code. A rerun passed. All checks pass, including `ci / verify` and `ci / e2e`. - Earlier failure: `issue-monitor-scheduler.test.ts` ("lets the board check a scheduled monitor on an in_progress task") timed out in CI at `d60b5f56d8`. It passed 5 of 5 local runs with the first fix. The cause is unknown. The test waits 3 seconds for heartbeat runs to settle, so CI load is possible, but this is not proven. The test passed in the CI run at `b5e9dc7352`. This shows recovery. It does not explain the earlier timeout. ## Risks - All other zod string bounds now count code points. Emoji text can be up to 2N UTF-16 units and 4N UTF-8 bytes (it was N units and at most 3N bytes). This pull request does not change those bounds. - An audit of those bounds found no other concrete failure. It checked index limits, byte limits, `.length` guards, provider limits, and security `min` checks. It used code search and sampling, so it is not exhaustive. - Not verified: Slack `topic` and `purpose` use `.max(250)`, and we did not confirm how Slack counts that limit. - `.min(N)` is now stricter for emoji text. A single emoji no longer passes `.min(2)`. - `@dependabot rebase` or `@dependabot recreate` on this branch will remove the manual commits. This happened twice to resolve lockfile conflicts, and the commits were applied again each time. ## 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 (focused tests only; see Verification) - [x] I have added or updated tests where applicable - [ ] I have updated relevant documentation to reflect my changes (no documentation change needed) - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green (at `482d76686e`) - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups (5/5 at `95b450048f`) - [x] I will address all Greptile and reviewer comments before requesting merge --- Bumps [zod](https://github.com/colinhacks/zod) from 4.4.3 to 4.6.6. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/colinhacks/zod/releases">zod's releases</a>.</em></p> <blockquote> <h2>v4.6.6</h2> <blockquote> <p>Six malicious commits pushed to <code>main</code> on September 21 with a compromised git credential were reverted the same day in <a href="https://redirect.github.com/colinhacks/zod/issues/6620">#6620</a> and have since been removed from the history of <code>main</code>. None were ever published to npm. The npm provenance for 4.6.6 names 284243fb, the pre-rewrite copy of 9c219833 with identical contents.</p> </blockquote> <h2>Commits:</h2> <ul> <li>0cd3af5e9f50ad11bbcd4407a46a21b8848ba574 docs: load the en locale directly in Zod Mini</li> <li>9d5e60f162997b764b40d45aa02481888db50dea docs: import locales from zod/locales, not z.locales</li> <li>34a626f307871ecbb0123947559328dc52ed5e3e docs: tighten the z.locales tree-shaking note</li> <li>74304ce5b5796b5edb9a88ceb8e170b6ea3e82df release: stage npm versions instead of publishing them (<a href="https://redirect.github.com/colinhacks/zod/issues/6622">#6622</a>)</li> <li>b2ef983e0711ff6517d6020b021071e2e5653efa docs: add Code for Japan to bronze sponsors</li> <li>8fb2faf0584828a6a027d8279db36f6f52784462 docs: crop the Code for Japan logo and list it first in bronze</li> <li>bacad518c41610d654658172d325874bf0ee6290 docs: square the Code for Japan logo to match the other bronze tiles</li> <li>c42596bd1c87e941d7241d10e012c5c196adcc10 release: hold the run on the release-approval environment until the staged versions are approved (<a href="https://redirect.github.com/colinhacks/zod/issues/6638">#6638</a>)</li> <li>a2ba4a67986a3e6b2b131be30084a2f460d9ea67 fix(types): declare record output and input as members, not base type arguments (<a href="https://redirect.github.com/colinhacks/zod/issues/6645">#6645</a>)</li> <li>a0ebf970f2d752e5f58527cd2942ff48be4f46bd fix(v4): check array, set and map size bounds before parsing elements (<a href="https://redirect.github.com/colinhacks/zod/issues/6652">#6652</a>)</li> <li>529f78dd9431b07f49647fee3aa0819b3f8b66b7 4.6.6</li> <li>9c219833c3a3764fa1f1ea9c5ecec8254a63c0b1 ci: stage at verbose so a failed OIDC token exchange says why</li> </ul> <h2>v4.6.5</h2> <h2>Commits:</h2> <ul> <li>d2b135cfb7a3582b9eb515756b9166bcb9521f4a docs: add the 4.6.x patch highlights to the 4.6 post</li> <li>f1448f7cee00df9fe1e9ad84a000aa1828cc8bc1 docs: fold the 4.6.x patch highlights into the 4.6 post's own sections</li> <li>de65a5cb39ed22a507fac935788f718fa88d104f docs: lead the properties section with the check and add a Zod Mini tab (<a href="https://redirect.github.com/colinhacks/zod/issues/6598">#6598</a>)</li> <li>56222cd1532c07bcb91b67df529cab4c0a215330 feat(instanceof): key the .properties() shape off the instance type (<a href="https://redirect.github.com/colinhacks/zod/issues/6600">#6600</a>)</li> <li>ca0229a404818290e6cdcfefcd7eb2d04bcbb543 Revert "feat: add z.currencyCode() over a vendored ISO 4217 list, refreshed weekly by CI (<a href="https://redirect.github.com/colinhacks/zod/issues/6595">#6595</a>)"</li> <li>cc4cd4ee9c52fcaa10964e48cc144541e41a5ed9 Revert "Revert "feat: add z.currencyCode() over a vendored ISO 4217 list, refreshed weekly by CI (<a href="https://redirect.github.com/colinhacks/zod/issues/6595">#6595</a>)""</li> <li>0f3f5ee3ca56c7574bf849e54f79e9a6e02562ee 4.6.5</li> <li>59bbc03e10c636b9eb3c393dfeb552819774ec21 chore: re-pin the integration peers to the workspace zod after the 4.6.5 bump</li> </ul> <h2>v4.6.4</h2> <p>A patch on top of <a href="https://github.com/colinhacks/zod/releases/tag/v4.6.3">4.6.3</a>.</p> <ul> <li><a href="https://github.com/colinhacks/zod/commit/d6bc1e30"><code>d6bc1e30</code></a> feat: add <code>z.currencyCode()</code> over a vendored ISO 4217 list, refreshed weekly by CI (<a href="https://redirect.github.com/colinhacks/zod/pull/6595">#6595</a>)</li> <li><a href="https://github.com/colinhacks/zod/commit/ad32d751"><code>ad32d751</code></a> perf: <code>z.url()</code> rejects an invalid URL with <code>URL.canParse()</code> instead of a throwing constructor, about 50x faster; fewer allocations on the validation path (<a href="https://redirect.github.com/colinhacks/zod/pull/6588">#6588</a>)</li> <li><a href="https://github.com/colinhacks/zod/commit/2bb08717"><code>2bb08717</code></a> chore: re-pin the integration peers to the workspace zod after the 4.6.4 bump</li> <li><a href="https://github.com/colinhacks/zod/commit/f6e1701a"><code>f6e1701a</code></a> chore(deps): bump next to 15.5.25 and vite to 7.3.6 (<a href="https://redirect.github.com/colinhacks/zod/pull/6153">#6153</a>)</li> </ul> <h2>v4.6.3</h2> <p>A patch on top of <a href="https://github.com/colinhacks/zod/releases/tag/v4.6.2">4.6.2</a>.</p> <ul> <li><a href="https://github.com/colinhacks/zod/commit/413cce9a"><code>413cce9a</code></a> fix(v4): make z.properties() a check again (<a href="https://redirect.github.com/colinhacks/zod/pull/6594">#6594</a>) — removes the standalone <code>z.properties()</code> schema from 4.6.0; <code>z.instanceof().properties()</code> and <code>.check(...z.properties())</code> are unchanged</li> <li><a href="https://github.com/colinhacks/zod/commit/75d63ee1"><code>75d63ee1</code></a> docs: show only the <code>.properties()</code> method form in the 4.6 post</li> <li><a href="https://github.com/colinhacks/zod/commit/46da9572"><code>46da9572</code></a> docs: match the error-message examples to what the parsers emit</li> </ul> <h2>v4.6.2</h2> <p>A patch on top of <a href="https://github.com/colinhacks/zod/releases/tag/v4.6.1">4.6.1</a>.</p> <ul> <li><a href="https://github.com/colinhacks/zod/commit/9446b5cc"><code>9446b5cc</code></a> fix: preserve undefined prefault outputs and object keys (<a href="https://redirect.github.com/colinhacks/zod/pull/6587">#6587</a>) — closes <a href="https://redirect.github.com/colinhacks/zod/issues/6585">#6585</a></li> <li><a href="https://github.com/colinhacks/zod/commit/0c483c58"><code>0c483c58</code></a> docs: the <a href="https://zod.dev/blog/zod-4-6">Zod 4.6 announcement post</a> (<a href="https://redirect.github.com/colinhacks/zod/pull/6546">#6546</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/colinhacks/zod/commit/9c219833c3a3764fa1f1ea9c5ecec8254a63c0b1"><code>9c21983</code></a> ci: stage at verbose so a failed OIDC token exchange says why</li> <li><a href="https://github.com/colinhacks/zod/commit/529f78dd9431b07f49647fee3aa0819b3f8b66b7"><code>529f78d</code></a> 4.6.6</li> <li><a href="https://github.com/colinhacks/zod/commit/a0ebf970f2d752e5f58527cd2942ff48be4f46bd"><code>a0ebf97</code></a> fix(v4): check array, set and map size bounds before parsing elements (<a href="https://redirect.github.com/colinhacks/zod/issues/6652">#6652</a>)</li> <li><a href="https://github.com/colinhacks/zod/commit/a2ba4a67986a3e6b2b131be30084a2f460d9ea67"><code>a2ba4a6</code></a> fix(types): declare record output and input as members, not base type argumen...</li> <li><a href="https://github.com/colinhacks/zod/commit/c42596bd1c87e941d7241d10e012c5c196adcc10"><code>c42596b</code></a> release: hold the run on the release-approval environment until the staged ve...</li> <li><a href="https://github.com/colinhacks/zod/commit/bacad518c41610d654658172d325874bf0ee6290"><code>bacad51</code></a> docs: square the Code for Japan logo to match the other bronze tiles</li> <li><a href="https://github.com/colinhacks/zod/commit/8fb2faf0584828a6a027d8279db36f6f52784462"><code>8fb2faf</code></a> docs: crop the Code for Japan logo and list it first in bronze</li> <li><a href="https://github.com/colinhacks/zod/commit/b2ef983e0711ff6517d6020b021071e2e5653efa"><code>b2ef983</code></a> docs: add Code for Japan to bronze sponsors</li> <li><a href="https://github.com/colinhacks/zod/commit/74304ce5b5796b5edb9a88ceb8e170b6ea3e82df"><code>74304ce</code></a> release: stage npm versions instead of publishing them (<a href="https://redirect.github.com/colinhacks/zod/issues/6622">#6622</a>)</li> <li><a href="https://github.com/colinhacks/zod/commit/34a626f307871ecbb0123947559328dc52ed5e3e"><code>34a626f</code></a> docs: tighten the z.locales tree-shaking note</li> <li>Additional commits viewable in <a href="https://github.com/colinhacks/zod/compare/v4.4.3...v4.6.6">compare view</a></li> </ul> </details> <br /> 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Nicky Leach <nicky@paperclip.ing> 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> |
||
|
|
33269e1fec |
fix(observability): report directory-merge lock holder diagnostics (#17755)
A directory-merge lock timeout (ERR_WORKSPACE_RESTORE_LOCK_TIMEOUT) already carries allowlisted holder evidence, but server Sentry capture dropped it. - Add directoryMergeLockTimeoutDiagnosticsFor in adapter-utils: closed labels, booleans and bounded durations; no owner PID or paths. - server captureException: lock timeouts get an event-local directory_merge_lock context and error_code=workspace_restore_lock_timeout tag; default grouping and message unchanged. - run-failure-diagnostics restoreLock* fields use the shared reader (same allowlist and order). - Document the record in doc/workspace-restore-locks.md. Diagnostics only; does not change lock acquisition, timeouts or retries. Refs PAPERCLIP-BACKEND-4Q Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e6c74982fe |
fix(workspaces): report why a workspace provider operation failed (#19362)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - A project can store its task workspaces with a workspace storage
provider plugin. Core calls the plugin's `workspaceOperation` method to
prepare, inspect, finalize and remove a workspace.
> - `callWorkspaceProvider` catches every rejection of that call and
throws the same conflict: "Workspace storage operation is incomplete;
retry the same workspace".
> - A provider error, an RPC timeout, a crashed worker and a worker that
never got the call all look the same in the run, the log and the API.
The message tells the user to retry, also when the failure is a fixed
configuration problem that a retry cannot fix.
> - In one diagnosis, three different configuration failures in a row
showed this same message. Finding each cause took trace span counts and
database reads.
> - This pull request lets a provider send a reason from a closed set,
and makes Core tell transport failures apart from provider failures.
> - The benefit is that a failed run says what failed and whether a
retry can help. Provider text and values still never cross into Core.
## Linked Issues or Issue Description
**What happened?**
A workspace provider's `workspaceOperation` failed. The run failed with
"Workspace storage operation is incomplete; retry the same workspace",
`errorCode: setup_failed`, and no reason in the run record or the
`heartbeat execution setup failed` log. Retrying did not help, because
the cause was a provider configuration problem.
**Expected behavior**
Core tells these cases apart: the worker did not receive the call, the
call timed out, the worker stopped during the call, or the provider
reported a failure. For a provider failure, Core shows a bounded reason.
Core tells the user to retry only when a retry can help.
**Steps to reproduce**
1. Install a workspace storage provider plugin whose
`onWorkspaceOperation` throws during `prepare` (for example, because of
missing provider settings).
2. Run an agent task in a project that uses that provider.
3. See the failed run: the error is the generic retry message, and the
run record and logs do not show the cause.
**Paperclip version / commit**
`master` at
|
||
|
|
f2593b7905 |
feat: make Coordinator and Agent Chat standard and speed up navigation (#19360)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - People enter that work through the workspace, agent conversations, and tasks. > - Coordinator and Agent Chat still require experimental enrollment. > - Task link prefetches do not warm the compact queries used by the default task view. Chat also waits for a request when its navigation list already holds the conversation. > - This pull request makes Coordinator and Agent Chat standard and reuses the reads needed for navigation. > - People can choose Classic workspace in Experimental settings. Cached work can appear while fresh data loads. ## Linked Issues or Issue Description **What existing behavior does this improve?** Workspace selection, Agent Chat availability, and navigation between Chat, Tasks, and task details. **Subsystem affected** Cross-cutting: UI, workspace preferences, instance settings, and the database migration. **Current behavior** People must enable Coordinator and Agent Chat in Experimental settings. Opening a task can repeat requests because prefetch warms the full detail and comment queries, while the default view reads compact task data and a recent comment window. Opening Chat waits for a detail request even when the current person's conversation list contains it. **Proposed behavior** Coordinator is the standard workspace for everyone. Experimental settings offer Classic workspace, off by default. Old Coordinator experiment values do not select Classic. Agent Chat is available to everyone, including instances with an old saved or managed off value. Task link intent warms the queries used by the mounted view. Chat can use the current person's company-scoped conversation list during refresh. **Reason and benefit** People can use the standard workspace without setup. Repeat navigation can show cached content while the server refreshes it. **Breaking changes** Existing Coordinator experiment and Agent Chat off values no longer disable these surfaces. Classic requires an explicit new opt-in. The workspace API keeps its existing response shape and audit checks. The migration retains legacy values and adds a separate Classic preference. Execution permissions, company access, and chat privacy checks still apply. **Additional context** Roadmap check: this graduates existing surfaces and improves their navigation. It does not add a new agent execution capability. Duplicate searches found no open Coordinator workspace PR. Related server cache work, Refs: #17828, addresses compact issue-list invalidation; this PR addresses client navigation queries. ## What Changed - Add a default-off Classic workspace preference. Derive the existing workspace API response from that choice. Keep personal persistence, account isolation, and audited writes in the workspace preference service. - Replace the Coordinator experiment control with Classic workspace. Use Coordinator while preferences load. Update the existing settings examples and navigation recipe. - Retire the Agent Chat experiment control. Ignore old stored and managed off values. Retain the compatibility key and the existing execution and access checks. - Reuse account- and company-scoped chat list data with its original freshness timestamp. - Share compact task and recent-comment query options between prefetch and the task view. Keep full-history prefetch for Classic tasks and expanded history. - Add upgrade, replay, cache, account isolation, and browser switching regressions. Update product, database, development, and roadmap documentation. ## Verification - Targeted preference, settings, migration, cache, and UI regression tests passed during implementation. The cache tests hold refresh requests pending and check that content still renders. They also check account isolation and stale refresh behavior. - A real browser test verified fresh Coordinator defaults, Classic opt-in persistence, and return to Coordinator. It used an isolated source checkout with fresh data on macOS. - Token, migration numbering and safety, map, and Storybook catalog checks pass after the rebase. - Repository typecheck and build pass. Typecheck and token gates also pass after review repairs. The broad local run passed all six chat integration shards (1,931 tests). It then found the host-CLI-dependent recovery fixture described below. Its remaining duplicate work was stopped after the full current-head CI matrix passed; this is not a claim that the broad local command completed green. - Review repairs passed 419 UI/migration fixture tests, 19 Coordinator navigation tests, and all 214 tests in the five affected server suites. The 24 Chat/workspace browser tests pass, including standard Coordinator switching and explicit Classic persistence. - The broader browser repair run passed 85 tests. It retained two failed assertions and skipped five later serial cases. The affected files then passed all 11 tests after the fixes: reopen the desktop panel after reload and open More for Audit and Connectors. This includes the five serial cases that had not run. - The broad local suite exposed a recovery fixture that executed the host Codex CLI during a simulated remote probe. On a host with Codex 0.154.0, model fallback broke its fixed-model assertion. The fake sandbox now reports Codex 0.160.0. All 20 selected recovery/provider compatibility cases pass after the repair. - A CLI company import/export suite timed out during server startup in the second CI run. The port fallback is intentional in that fixture. No root cause was established. That suite passes on the final commit in CI. This records recovery, not a claimed product fix for the earlier timeout. - The first CI run exposed remaining assumptions about the retired flags and Classic UI. The repair commit updates those fixtures and navigation steps. It also restores the missing enabled Cases destination in Coordinator. Earlier local recovery attempts used the wrong fixture notification and advanced fake time during live execution; the corrected tests use the subscribed recovery queue and wall time during execution. - Greptile reviewed commit `98972b030d4505d838427a2fca6b196b8cad6d47` at **5/5** with no new comments. The original finding is resolved. - Current-head CI is green: **56 successful checks**, with three checks skipped by workflow filters. This includes typecheck, build, the complete Vitest matrix, all eight browser shards, Runner verification, release checks, and security checks. [Final CI run](https://github.com/paperclipai/paperclip/actions/runs/42132651414). - Typecheck caught missing `pid` and `startedAt` fields in the simulated command result. The fixture now returns the complete process contract, and the server test-layer typecheck passes. - Storybook catalog alignment was checked. Storybook play tests were not run. Hosted latency and cold bundle loading were not measured. ## Risks - The new default changes the workspace for people who previously left the Coordinator experiment off. They can opt into Classic in Experimental settings. - The additive migration must run before the new server code. It is safe to replay and preserves explicit Classic choices. - Cached content can be stale during background refresh. Existing query invalidation and source timestamps still control freshness. - These changes reduce avoidable loading waits. They do not establish a measured improvement in hosted request latency or initial bundle download time. ## Model Used OpenAI Codex, based on GPT-6, with reasoning, repository tools, code execution, and browser automation. The exact serving revision and context-window size are not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Scott Tong <scott@scottsmbpm5max.lan> Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
8080620fc9 |
fix(workspaces): size archive deadlines and preserve resume budgets (#19364)
Give large workspace archives a bounded per-transfer deadline without changing provider settings. Preserve configured post-upload command limits and the saved provider budget when resuming an exact retained sandbox. Verification: 341 focused Core/SDK/adapter tests and 588 Daytona tests passed. Workspace and provider typecheck/build passed. Hosted CI: 54 successful checks, two intended Storybook skips. Greptile 5/5 with no unresolved review threads. The bounded full local run passed all six chat shards and stopped at twenty minutes during general server suites; that full local run remains incomplete. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
08f3e7ebec |
Retain passive Codex output while Core reconnects (#19029)
## Thinking Path > - Paperclip manages work that AI agents execute through a Runner. > - Core controls the work while the Runner owns the provider process. > - Core can restart while an admitted provider turn is active. > - Reconnect waits used to leave provider output in the process pipe. > - Ordinary provider polling can also wait on a provider RPC before Core reconnects. > - This pull request drains passive local Codex output into bounded durable storage between reconnect attempts. > - Core receives the original events after recovery and keeps authority over tools and approvals. ## Linked Issues or Issue Description Refs #17594. Related work: merged #19353 supplies the Codex receipt-limit fence and deadline-aware reconnect scheduler. This PR now contains only shared passive intake and the already-live native Codex adapter. A controller outage must preserve admitted work and event identity without restoring a provider, sending a provider RPC, or executing a tool during reconnect. ## What Changed - Add a default no-op disconnected-poll hook. Managed providers and ACPX retain that default. - Drain passive frames from the existing local Codex process between reconnect attempts. Requests, terminal reconciliation and lineage checks wait for the connected path. - Persist each accepted event before acknowledging the provider queue. Soft-limit pressure retains the rejected suffix until Core ACKs free space. Hard failures stop the owned provider and preserve the durable prefix. - Preserve credential observations and the existing lease, runtime, grace and provider deadline bounds. Document the intake contract and test replay through the real PRP controller. ## Verification The slice was replayed onto master `bf10bc695a`. `git range-diff f453509e6f..20626230b9 bf10bc695a..HEAD` reports an unchanged patch. The diff has 11 files and contains no ACPX adapter or sidecar changes. Targeted local checks on this head: - `cargo test --manifest-path packages/paperclip-runner/runner/Cargo.toml --locked -p paperclip-runner-core --lib disconnected -- --test-threads=1`: 12 passed. These cover passive deferral, no restoration, the real managed HTTP worker, durable backpressure, oversized output and existing receipt-limit cleanup. - The 10 `credential_observer::tests::` checks passed. The Runner daemon and recovery example build, and workspace Rust formatting passes. - `vitest run src/control-plane/runner-recovery-fixture.test.ts --maxWorkers=1`: four passed. Both controller replacement modes retain the same process and replay event identities. The deadline and silent-handshake cases also pass. - `vitest run src/live/runnerd-codex-transport.test.ts -t late-remote-release-error --maxWorkers=1`: one passed. Whitespace and added-line secret checks pass. Earlier review found that disconnected ordinary polling could perform synchronous managed history reads. The real-worker regression retained here proves no history or stream-reopen request occurs until connected polling. Earlier slice preparation hit a sandbox loopback denial and a missing local daemon; the permitted loopback test and the test after building the daemon both passed. The current-base checks above run with those prerequisites in place. Historical CI on the old combined head failed an unordered AI-connections migration comparison. The faithful full-file local check passed 158 tests, and the failure remains recorded [here](https://github.com/paperclipai/paperclip/actions/runs/42112385671/job/127543554397). Earlier receipt-limit delivery failures are addressed by merged #19353. Previous review scores belong to the old combined diff. On current head `b92d4b604ee6992eeefbc02c8d41ec4f2a608a4d`, [CI run 42125840239](https://github.com/paperclipai/paperclip/actions/runs/42125840239) passed. All 59 checks completed with 55 successes and four intentional skips. Standard review 13 and Apex review 14 each scored 5/5 on this exact head, with zero comments and no unresolved threads. The single Apex request is [recorded here](https://github.com/paperclipai/paperclip/pull/19029#issuecomment-6671993072). ## Risks During an outage, native Codex intake uses 25 ms ticks between reconnect attempts. A provider request or terminal waits for Core. Saturation can pause intake or produce a failure. Managed remote streams retain their existing limits. Tests use controlled local providers; live provider qualification and deployment are separate. ## Model Used OpenAI GPT-6.1 Sol (`gpt-6.1-sol`), high reasoning effort, through Codex. Used source inspection, local native and PRP tests, and code review. ## 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> |
||
|
|
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> |
||
|
|
b61a815d3d |
Enforce Codex receipt-limit deadlines while Core is offline (#19353)
## Thinking Path > - Paperclip uses a Runner to execute agent work. > - The Runner keeps a record of each tool call, called a receipt. > - When that storage reaches its limit, the Runner asks Codex to stop. > - Core can stop responding while the stop request is pending. > - Codex may already have written its completion behind earlier output. > - The Runner must keep its stop deadline and preserve that real completion. ## Linked Issues or Issue Description Refs #19029. This is its first isolated slice; #19029 remains unmerged. Related #17594 covers controller replacement. Related #17516 retains receipts during explicit stop; this change handles the autonomous receipt-limit deadline. ## What Changed - Keep the existing 2s/30s stop deadlines while Core is offline, including when a connection waits for a reply. Other lease and runtime deadlines still apply. The Runner sleeps between connection attempts and deadline checks. - Stop the owned local Codex process when its deadline expires. Read its remaining output within the existing size and message limits, then process it in order. Only after output has ended and all earlier messages have been processed can the Runner report a missing completion. - After the local process has stopped as requested, skip replies that only reject or cancel old requests. Commands, user responses and actual tool results still require successful delivery. - Keep the stop behavior, regression tests and transport documentation in this slice. ## Verification Standalone checks on this branch: - Seven existing Codex tool-record limit integration cases pass. - Twenty focused checks cover deadline cleanup and retained output, including 273 distinct messages followed by one real completion, missing completion, and a pipe that stays open. - Separate checks cover strict tool-result delivery, three process-stop cases, and lease/runtime/reconnect time limits. - Four controller process tests pass. They include Core accepting a connection but never replying, and a reconnect delay longer than the allowed recovery period. - Existing warm attachment still retains the completed turn's remaining output and usage. - Rust formatting, diff checks and the local secret scan pass. The extraction first omitted an existing test helper and the connected retry preamble. Standalone compilation and receipt tests caught both omissions. Restoring them produced the results above. Full regression checks run in GitHub CI. Hosted CI passed both native platforms and typecheck, but [server11 failed](https://github.com/paperclipai/paperclip/actions/runs/42118725143/job/127565828671) one unchanged status-card test. Its fake clock advances while real database work runs; the final clock assertion was 50 ms past its limit. The identical test, service and scheduler passed all 36 cases in [the earlier baseline shard](https://github.com/paperclipai/paperclip/actions/runs/42111889388/job/127541685625). The local comparison first needed a built SDK, then stopped at embedded Postgres startup before any test ran. It did not reproduce the assertion. The single failed-jobs-only retry passed. The original failure and the blocked local comparison remain recorded here; the retry does not establish a precise timing cause. At exact head `f453509e6f`, all 59 hosted checks completed: 55 succeeded and four were skipped. Snyk also passed. Standard review round 1 and selected review round 2 each scored 5/5 on this head, with no unresolved threads. ## Risks The Runner now stops its local Codex process at the original deadline even while Core is unavailable. If another program keeps the output pipe open, or queued output exceeds the existing limits, the Runner cannot confirm the outcome. It reports that uncertainty and keeps the records already written to disk. Tests use real child processes with controlled provider fixtures. Live provider and deployment qualification remain separate. ## Model Used OpenAI GPT-6.1 Sol (`gpt-6.1-sol`), high reasoning effort, through Codex. Used source inspection, native subprocess tests and targeted TypeScript tests. ## 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> |
||
|
|
a6ab6a301c |
fix(server): retry a run when its remote Runner is out of date (#19326)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - An agent can run on a remote Runner that an environment plugin
provides
> - A release can update the app before the remote Runner, so for a
short time the app sees an older Runner
> - In that window the plugin can refuse the task, but Core cannot tell
this refusal from other errors
> - Today the run fails as `setup_failed` with no retry, and the task
goes to board recovery. Before #19246, it was also reported as a project
authentication conflict
> - Agent verification also counts the refusal as a failed harness test,
and it parks the agent after three tries
> - This pull request gives the plugin a stable error code for a Runner
mismatch, and Core waits and retries when it gets that code
> - The benefit is that a release no longer blocks tasks or parks
agents, and a real authentication conflict keeps its own clear notice
## Linked Issues or Issue Description
Refs #19246 (it stopped reporting every precheck error as a project
authentication conflict)
Refs #19249 (the same bounded bootstrap retry for a stopped plugin
worker)
Refs #19302 (shares `environment-task-error.ts`; its diagnostic envelope
is unchanged)
Refs #19172 (related open PR: it reports an undescribable plugin Runner
as a retryable check in the same precheck)
Refs #18667 (a real `ai_connection_incompatible` conflict, which now
gets its own notice)
**What happened?**
A run started while its remote Runner was older than the app. The
environment plugin refused the task because the installed Runner did not
match the revision it expects. Core recorded a generic setup failure,
scheduled no retry, and blocked the task. Before #19246, Core reported
this as a project authentication conflict. An agent in `verifying`
counted the same refusal as a failed harness test. After three tries it
stopped verification until a person chose Retry.
**Expected behavior**
Core treats a Runner mismatch as temporary. A run retries without
blocking its task. A `verifying` agent checks again until the Runner
matches. Only a real `ai_connection_incompatible` result blocks the
task, with a notice that names the conflict.
**Steps to reproduce**
1. Use an agent on a plugin environment whose installed remote Runner
revision differs from the revision the plugin expects.
2. Wake the agent on a task, or put a new agent through verification.
3. The run fails with `setup_failed` and the task is blocked.
Verification parks after three tries.
**Paperclip version or commit**
master at
|
||
|
|
4ece2107b8 |
fix(connections): offer aggregator Slack sign-in on Cloud instead of a customer OAuth app (#19328)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Connectors let people give agents tools, such as Slack, from the
Connectors catalog or from an agent's inline "Connect" card
> - On Paperclip Cloud, the only Slack path is "Slack needs its own
OAuth app". The user must create a Slack app, paste a callback URL, and
enter a Client ID and secret
> - Managed Slack chat setup exists, but it is behind the experimental
Chat connectors setting, which is off on Cloud. Composio's and Arcade's
managed Slack sign-in exists, but the native Slack row hides it
> - In an onboarding test on Cloud, a tester got this card from the
onboarding agent, spent 7 minutes on it, and declined it. No Slack
connection was made
> - This pull request follows the existing Google Workspace pattern: on
Cloud, a native connector whose every setup path needs a
customer-created OAuth app yields new setup to the aggregators' managed
sign-in, in the catalog and in agent connection discovery
> - The benefit is that Cloud users can connect Slack with a sign-in
instead of creating a Slack app
## Linked Issues or Issue Description
**What happened?**
On a Paperclip Cloud instance, the onboarding agent asked the user to
connect Slack. Connectors > Slack > Connect showed "Slack needs its own
OAuth app. Slack does not let Paperclip register itself automatically,
so this connector needs an OAuth app you create." The form asks for a
callback URL, a Client ID and a Client secret. It has no link to Slack
app settings. The method description said "Use the provider-hosted
connection for the quickest setup." The tester gave up. The original
notes said that the cloud connector asked for an HTTPS server. That is
not correct: no HTTPS or deployment-mode check is in this path. The real
blocker is the customer OAuth app requirement.
**Expected behavior**
Slack can be connected from the supported Cloud flow without setup steps
that only an operator can do.
**Steps to reproduce**
1. On a Cloud instance with Chat connectors off (the Cloud default),
open Connectors and search for "Slack".
2. Click Connect on Slack. The "needs its own OAuth app" form opens.
3. Or, let an agent call `connections_search("Slack")`. It returns
native `slack` first, and its card opens the same form.
**Additional context**
Evidence: the Slack gallery entry on the Cloud instance had
`ownershipAvailability` `platform_shared: false`, `platform_provisioned:
false`, `customer: true`. `GET
/api/companies/:id/chat-slack/setup-options` returned 403 "Chat
connectors are disabled".
## What Changed
- `packages/shared`: add `nativeConnectorRequiresCustomerOAuthApp(app,
settings)`. It is true when every available setup path of a native
connector needs a customer-created OAuth app: no Paperclip-managed
client, no dynamic client registration, and no enabled chat setup.
- `ui/src/pages/apps/Browse.tsx`: on Cloud, such a connector yields to
aggregators, the same as the built-in Google connectors. The catalog
shows the "Connect through Composio or Arcade" row, and its Connect
button opens the provider choice. A saved native connection, a chat
endpoint, Chat connectors on, a self-hosted instance, or no aggregator
route keeps the native row.
- `server/src/services/connection-intents.ts` and new
`connection-native-yield.ts`: agent `connections_search` uses the same
rule. On Cloud, it does not prefer native Slack for new setup, so the
agent asks the provider-choice question (Composio, Arcade or Zapier).
The rule applies the advertised Paperclip Cloud connector profiles, so
apps with a managed Cloud client (for example Asana) do not yield. If
the profiles cannot be read, the native connector stays.
- `slack.json`: correct the Slack tool method copy, which said "quickest
setup". Add a "register" console link to `https://api.slack.com/apps`,
so the form now shows "Open Slack app settings".
- Tests in all three layers.
## Verification
Red/green. I committed the tests first (`2386fd83df`) and ran them
against unchanged source code.
**Red (before the fix):**
```
server › aggregator connection fallback › Slack on Paperclip Cloud
× offers aggregator Slack routes while native Slack only supports a customer-created OAuth app
AssertionError: expected [ 'slack', 'google-calendar', …(7) ] to deeply equal [ 'via:composio:slack', …(2) ]
+ "slack", "google-calendar", "google-docs", ...
Tests 1 failed | 3 passed | 45 skipped (49)
ui › Connectors landing page
× offers aggregator Slack sign-in on Cloud while the built-in Slack connector needs a customer OAuth app
AssertionError: expected 'SlackSend and read messages in your t…' to contain 'Connect through Composio or Arcade.'
Received: "SlackSend and read messages in your team's channels.Connect"
Tests 1 failed | 5 passed | 92 skipped (98)
shared › Slack tool connection guidance
× describes the customer-created Slack app honestly and links to Slack app settings
AssertionError: expected 'Use the provider-hosted connection fo…' not to match /quickest/i
Tests 1 failed | 3 passed (4)
```
**Green (after the fix):**
```
✓ server › Slack on Paperclip Cloud › offers aggregator Slack routes while native Slack only supports a customer-created OAuth app
✓ server › Slack on Paperclip Cloud › keeps native Slack when Chat connectors provide its own setup path
✓ server › Slack on Paperclip Cloud › keeps native Slack for a company that already saved a Slack connection
✓ server › keeps native Slack on self-hosted instances, where operators register their own Slack app
✓ ui › offers aggregator Slack sign-in on Cloud while the built-in Slack connector needs a customer OAuth app
✓ ui › keeps the built-in Slack connector when Chat connectors offer its setup path
✓ ui › keeps the built-in Slack connector when the instance is self-hosted
✓ ui › keeps a saved built-in Slack connection on its native row on Cloud
✓ shared › describes the customer-created Slack app honestly and links to Slack app settings
✓ shared › flags Slack only while its chat setup is unavailable
✓ shared › does not flag connectors with a managed, dynamic-registration, or credential path
```
Neighbouring suites:
- `packages/shared` (all): 98 files, 1060 tests passed
- `ui` `src/pages/apps` + `src/features/connections`: 54 files, 980
tests passed
- `server` `connection-aggregator-fallback` 49/49,
`connection-intents-service`, `native-connection-baseline-fixture`,
`runner-connection-eval-fixtures` passed. `satisfied-connection-intents`
52/52 passed when run alone. It had a suite-setup error only when three
embedded-Postgres runs went in parallel.
- `pnpm check:token-gates`: clean. `tsc --noEmit` shows no errors in
changed files. Local runs show unrelated errors because the
`paperclip-runner` workspace package is not built. CI runs the full
typecheck.
Still needs a live Slack workspace to prove:
- Complete Slack sign-in through Composio or Arcade on a Cloud instance,
and an agent tool call to Slack.
- An agent run on Cloud that asks the provider-choice question for Slack
and then connects through the selected aggregator.
## Risks
- Behaviour change on Cloud only, for native connectors whose every path
needs a customer-created OAuth app and that an aggregator serves. Today
that is Slack (when Chat connectors are off), and Box and Xero if the
aggregator catalog has them. On Cloud, these move to the aggregator
route for new setup, the same as the built-in Google connectors. Saved
connections keep their native row and discovery.
- Cloud users who want their own Slack app can still open
`/apps/connect?source=slack`, but the catalog row no longer links to it
while the rule applies.
- Self-hosted instances keep the native connector unchanged.
- Follow-up, not in this PR: enable managed Slack chat on Cloud (Chat
connectors Cloud default and `PAPERCLIP_SLACK_MANAGED_SETUP_ENABLED`).
This is a flag and operations change. It needs the Cloud Slack broker to
be proven first. When it is enabled, this rule stops yielding Slack
automatically.
## Model Used
- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, via Claude Code with
tool use (shell, browser inspection of a staging instance, file
editing).
## 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>
|
||
|
|
9a79384958 |
Relay bounded Runner source refusal diagnostics (#19302)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - An environment plugin can submit a Runner continuation from a retained source. > - The host can refuse that continuation when its source is unverified or has an unresolved operation. > - The worker and Core currently discard that bounded reason. > - Raw provider errors can contain credentials, text or foreign identities. > - This pull request relays only a closed diagnostic and keeps the existing admission and redaction rules. ## Linked Issues or Issue Description Refs #19134 and #19157. This independent fix is based on master. A search found no duplicate relay. **What happened?** A source submit can receive HTTP 409 with `SESSION_STATE_UNAVAILABLE`. The worker drops its structured data. Core then replaces the error with a generic message. The caller cannot distinguish the two known diagnostic reasons safely. **Expected behavior** Carry only the fixed status, code and reason through SDK worker RPC and Core. Keep all provider text and identities out of the relayed error. A diagnostic must not authorize a retry or change state. **Steps to reproduce** Submit an authorized retained source through a task provider. Have the provider throw the recognized state-unavailable error. Read the error after worker RPC and Core dispatch. ## What Changed - Export `PluginEnvironmentTaskErrorDiagnostic`, `environmentTaskErrorData` and `readEnvironmentTaskErrorDiagnostic`. - Accept only `httpStatus: 409`, `errorCode: "SESSION_STATE_UNAVAILABLE"` and reason `"unresolved_operation"` or `"source_state_unverified"`. - Attach the versioned envelope only to an environment task `submit` with `resumeFrom`. Replace its provider message with a fixed message. - Revalidate and reconstruct the closed error in Core. Keep other failures under the existing generic redaction. - Document older SDK behavior and the diagnostic-only boundary. No grant, retry, discard, lifecycle or admission rule changes. ## Verification - Rebased head `7d02bb3e4c` includes master’s UI fixture fix from #19342. The sole conflict was in the worker integration test imports. It now imports both the physical-task result schema from #19300 and the diagnostic reader and RPC error type from this PR. The 39 SDK/worker cases and 57 Core/runtime cases passed, including PostgreSQL worker integration. The SDK build and Runner/server TypeScript checks passed. [Current-head CI](https://github.com/paperclipai/paperclip/actions/runs/42111889388) passed without retries. All 56 check-rollup entries were green, including Snyk and Socket; [standard Greptile review 2](https://github.com/paperclipai/paperclip/pull/19302#issuecomment-6665823815) scored this exact head **5/5**, with zero unresolved threads. The PR was marked ready on November 5 at 21:37 UTC. - Both new worker relay cases fail against the baseline worker and pass with the fix. - 46 SDK/worker cases passed, including both reasons, malformed/foreign envelopes, extra identities, unsafe getters and non-source operations. - 51 Core/runtime cases passed. The integration uses real SDK serialization and dispatch with PostgreSQL through the existing authorized retained-source submit. It verifies both reasons, malformed RPC rejection, extra-field removal, preserved source outcome and existing dispatch bookkeeping. A valid envelope on an ordinary submit or status stays generic. - Runner and SDK builds and server TypeScript check passed. An initial Runner build lacked its eval-kernel build prerequisite; it passed after that prerequisite was built. - The first expanded DB fixture used a shared environment name and failed its unique constraint. It now uses its unique fixture ID; both complete focused files pass. - Diff and added-line secret scans passed. [GitHub workflow 42083410387](https://github.com/paperclipai/paperclip/actions/runs/42083410387) passed on its second attempt. All CI checks completed, and Snyk passed. Standard Greptile scored exact head `38559ec4df8c` 5/5 with no unresolved threads. That result applies to the previous head. - The first GitHub attempt failed in the unchanged concurrent-budget case in `decision-models.test.ts` (one provider call observed, two expected). That exact case passed locally without code or assertion changes. The workspace shard also passed 4,960 tests before the unchanged CLI company import/export fixture exceeded its 30-second child-server startup wait. No port-collision cause is claimed; that fixture deliberately reserves the requested port. Both initial logs are retained. One failed-job-only retry passed without source changes. ## Risks Older SDKs keep the generic failure. The envelope is diagnostic evidence only. Provider operation and call identities are never accepted. Specific user-visible operation reporting must use Core's retained event or receipt under its existing authority. Master does not contain the new claimed predecessor orchestration. This PR tests the existing retained-source submission boundary. It does not claim the #19157 source recovery or old-journal end-to-end qualification. Runtime pins and deployments remain separate. ## Model Used OpenAI GPT-6.1 Sol (`gpt-6.1-sol`), high reasoning. Used repository tools, TypeScript, SDK RPC and real PostgreSQL tests. ## 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 described the issue in-PR using the bug template - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change - [x] I have run the focused tests locally and they pass - [x] I have added tests for the new behavior and rejection paths - [x] I have updated relevant documentation - [x] I have considered and documented risks - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no unresolved findings - [x] I will address reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
c3550cfb38 |
fix(daytona): send archive chunks in bounded windows (#19339)
## Thinking Path > - Paperclip manages agents and saves their work when execution stops. > - The Daytona plugin saves an archive through a terminal when disk capacity prevents the normal archive or download copy. > - That fallback sent one 40 KiB chunk and waited for its host write acknowledgement. > - Network delay then applied to every small chunk. A large archive could exceed the recovery deadline even while bytes were moving safely. > - This pull request sends a bounded window of chunks and still acknowledges every completed host write. > - The benefit is faster recovery over delayed connections, with bounded memory and the existing size, space and completion checks. ## Linked Issues or Issue Description Refs #19034, which added the capacity fallback. **What happened?** The archive fallback waits for one network round trip after each 40 KiB chunk. A saved workspace archive can be much larger than that chunk. Repeating a timed-out transfer does not remove this throughput limit. **Expected behavior** Allow a small amount of data to travel before waiting for acknowledgements. Keep memory bounded, write bytes in order, and stop after any write or protocol failure. **Steps to reproduce** 1. Run the real Bash archive sender against a host that delays each acknowledgement by 400 ms. 2. Transfer 1 MiB plus a partial final chunk with a four-second deadline. 3. The original implementation times out. The bounded-window implementation completes and preserves the exact source bytes. **Paperclip version or commit** The failing regression uses the original implementation from `6a66c09f7f7`. ## What Changed - Send at most sixteen 40 KiB chunks before collecting their acknowledgements. Drain the final partial window before the completion receipt. - Serialize host writes and reject more than sixteen pending frames. Stop queued writes on the first failure. - Keep per-frame size checks, total archive limits, the 1 GiB host space reserve, the transfer deadline and bounded teardown. - Add a real Bash regression with delayed acknowledgements, plus overflow and failed-write coverage. Update the plugin documentation. - Update the agent-creator test fixture for the required directory membership role introduced by #15183, which otherwise breaks merged CI typechecking. ## Verification - Failing first: the final real-Bash regression fails against the original source with `Daytona archive stream timed out`. It passes with the change. The empty-stream case passes too. - Standalone Daytona package `pnpm test`: 578 passed, 8 skipped. The skips are the existing opt-in live tests. - Standalone package `pnpm typecheck` and `pnpm build`: passed. - `pnpm test src/file-sync-capacity.live.test.ts` with a staging Daytona key: 2 passed on isolated test sandboxes. This covers actual terminal transport, stop/start, archive and file-copy capacity failures, exact saved hashes, safe links, cleanup and a second export. The tests removed their own sandboxes. - The production dependency assumption is bounded: at most 640 KiB of decoded payloads can wait for host writes. The parser also has a bounded encoded buffer. No background work or idle timer is added. - Initial hosted CI failed with TS2741 in `ui/src/lib/agent-creator.test.ts` after #15183 made `membershipRole` required. Rebased onto that change and added the nullable field to the fixture. UI `pnpm typecheck`, all 4 existing creator-label tests, and `pnpm check:token-gates` now pass locally. Hosted CI subsequently passed on the updated head. - `git diff --check` and a local secret and PII scan: passed. - Full repository checks were not repeated locally for this standalone plugin change. Hosted verification at `bf6b3cb6a622a9523c72b86fdfe616555c8f3ef8`: all 53 check runs passed, with the 2 opt-in Storybook checks skipped; both required gates (`ci / verify`, `ci / e2e`) passed. Greptile is 5/5 at this exact head, with no review threads or merge conflicts. The separate non-required `security/snyk (cryppadotta)` status remains pending; it is not claimed as passed. ## Risks - A burst can queue up to sixteen chunks instead of one. The receiver enforces this bound and acknowledges a chunk only after its ordered host write. - The timeout budget is unchanged. A connection that cannot finish within that budget can still fail. This change does not retry a provider turn or change file-recovery authority. - Source confinement, archive validation, successful producer completion and publication rules are unchanged. No schema migration. ## Model Used - OpenAI `gpt-6.1-sol` with xhigh reasoning, in Codex through T3 Code. Tool use included source edits, real Bash and filesystem tests, and isolated Daytona tests. The runtime does not expose its context window. ## 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> |
||
|
|
fb2e07ebdd |
Wait for physical authority before activating hosted warm routes (#19277)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Core can give two consecutive runs, A and B, the same hosted Runner so B can continue A’s conversation. > - During that handoff, Core can record that B is next while the Runner still answers as A. > - If Core restarts at that point and tries to connect for B, the Runner rejects the connection. B’s next turn stalls. > - This PR makes Core finish the handoff over A’s connection and check which run the Runner now reports. > - Once the Runner reports B and authenticates B’s connection, B can continue on the same Runner. ## Linked Issues or Issue Description - Refs #17390 for hosted Runner reuse. - Refs #19157 for recovering a stopped Runner session before attaching the next run. ## What Changed - If the Runner still reports A, Core uses A’s connection to acknowledge the result of `run.attach`. This tells the Runner that Core saved the handoff and that it can switch to B. - Core reads the Runner’s saved state and checks it against the recorded handoff. If the Runner already reports B for that handoff, Core connects for B directly. Core waits for B’s connection to authenticate before sending its work. This applies both during an ordinary handoff and after Core restarts. ## Verification - Run `pnpm --filter @paperclipai/paperclip-runner exec vitest run src/live/runnerd-staged-handoff.test.ts` with the native test binaries built. The regression replaces Core at four points in the handoff. Before the fix, Core tried to connect for B while the Runner still reported A. - With the fix, B and a third run each start and finish one turn. The test observes one Runner process, one controlled Codex provider process, and one conversation thread throughout. - All seven handoff cases passed. GitHub CI passed after retrying an unrelated port collision and interrupted jobs. Standard Greptile scored the commit 5/5. [CI run](https://github.com/paperclipai/paperclip/actions/runs/42070818838) ## Risks - The host must be able to read the Runner’s state. If Core cannot verify the switch before the existing deadline, it leaves the handoff pending and sends no work for B. Live hosted verification is still separate. > 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 GPT-6.1 Sol (`gpt-6.1-sol`), high reasoning effort, through Codex with repository tools 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 - [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> |
||
|
|
00c7738513 |
fix(task-chat): keep messages sendable after a declined connection request (#19331)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The task chat composer shows a queue of saved input that waits for
the agent's next turn
> - When a person declines a connection request (for example Slack), the
server queues a read-only receipt so the agent learns the outcome
> - The composer showed that receipt with its raw JSON receipt in hover
text, and the receipt hid the person's next message
> - The next message could not be edited, discarded, or sent, so the
chat looked stuck after a simple "Decline"
> - This pull request shows a readable status for receipts and keeps the
person's own later messages visible and actionable
> - The benefit is that declining a connection resolves cleanly and the
conversation continues
## Linked Issues or Issue Description
**What happened?**
During an onboarding test on staging, a person declined the agent's
"Connect Slack" request. A queued steering item then stayed above the
composer. It could not be edited. Its hover text showed raw JSON.
Sending the next message produced an error.
**Steps to reproduce**
1. Give a task to a Paperclip runner agent whose run is active or waits
on a busy workspace.
2. Let the agent request a connection, then decline it.
3. See the queued item "Rejected: Connect Slack". Hover it: the title is
the full JSON receipt.
4. Send another message. The message never appears in the queue. Its row
controls stay disabled. Send or Interrupt on the receipt queue returns
`409 This queue does not support interruption`.
**Expected behavior**
A declined connection request resolves cleanly. It shows a readable
status. The next message can be sent.
**Evidence (staging wake diagnostics for the test task)**
- 17:52:09: the decline wake for the Slack request. It stayed deferred,
was never claimed, and was skipped at 18:21.
- 17:52:10: a later retry wake for the plan-approval continuation.
- `GET /issues/:id/queued-comments` projects the oldest matching wake.
From 17:52 to 18:21 the composer showed the Slack receipt.
Refs: related to the separate plan-approval queued-message fix, which
changes the recovery sweep. This PR only touches the queue projection
and the receipt label.
## What Changed
- `server/src/services/queued-interaction-response.ts`: add
`queuedInteractionSummary()`. A receipt now has one readable line, for
example `Declined: Slack connection`, `Connected: GitHub connection`, or
`Accepted: Build the app`. The entry `source.summary` carries it. The
steering body for the agent still includes the typed JSON receipt.
- `server/src/routes/issues.ts` (`findQueuedCommentWake`): a queued
user-message wake now takes precedence over a receipt wake. A receipt
shows only when it is the only queued input. The receipt wake itself is
unchanged and is still delivered to the agent.
- `server/src/modules/wake-queue/adapters/postgres.ts`
(`findExistingDeferredWake`): skip interaction-receipt wakes when a new
message picks the deferred wake it merges into. Before, the receipt was
the oldest deferred wake, the merge was skipped, and every later message
started its own wake. Only the first message then appeared in the queue.
Receipts still never absorb user input.
- `packages/shared/src/types/issue.ts`: add the optional `summary` field
to `IssueQueuedCommentEntry.source`.
- `ui/src/components/task-chat/TaskChatQueuedMessages.tsx`: add
`queuedMessageLabel()`. The row text, the hover title, and all
aria-labels use the summary. They never use the raw steering body.
## Verification
Red/green tests were written first and run against the unchanged code.
New tests:
- `server/src/__tests__/issue-queued-comments-routes.test.ts`
- `shows a declined connection receipt as a readable status, not its
JSON`
- `keeps the next message visible and actionable after a declined
connection (running run | stopped run)`: declines Slack, posts the next
message, and asserts that the queue lists that message with
`canEdit`/`canDiscard`, that the discard works, and that the receipt
wake is still pending for the agent.
- `queues every later message together after a declined connection
(legacy run | stopped run)`: two messages after a decline both appear in
one editable queue, and the receipt wake keeps no message ids.
- `ui/src/components/task-chat/TaskChatQueuedMessages.test.tsx`
- `shows a declined connection request as readable text, never its raw
JSON receipt (BUG-08)`
**Red (unchanged code):**
```
server: × shows a declined connection receipt as a readable status, not its JSON
AssertionError: expected { kind: 'interaction', …(3) } to match object { …, summary: 'Declined: Slack connection' }
server: × keeps the next message visible and actionable after a declined connection (running run)
server: × keeps the next message visible and actionable after a declined connection (stopped run)
AssertionError: ["Declined: Slack connection"]: expected undefined to match object { canEdit: true, canDiscard: true }
(the queue lists only the receipt; the new message is missing)
Tests 3 failed | 85 skipped (88)
ui: × shows a declined connection request as readable text, never its raw JSON receipt (BUG-08)
AssertionError: expected 'Reorder queued message: Rejected: Con…' not to contain '{'
Tests 1 failed | 23 skipped (24)
```
An exploratory probe on the unchanged code also showed the send error on
this path: `POST /queued-comments/interrupt` on the receipt queue with a
running native run returns `409 {"error":"This queue does not support
interruption"}`. The exact UI text that the tester saw was not
available.
Second commit (review follow-up), unchanged `findExistingDeferredWake`:
```
server: × queues every later message together after a declined connection (legacy run)
AssertionError: expected [ Array(1) ] to deeply equal [ …(2) ]
Tests 1 failed | 1 passed | 88 skipped (90)
```
With the change: `Tests 2 passed | 88 skipped (90)`.
**Green (with this change):**
```
pnpm --filter server exec vitest run src/__tests__/issue-queued-comments-routes.test.ts
Test Files 1 passed (1) Tests 90 passed (90)
server: heartbeat-comment-wake-batching + src/modules/wake-queue
Test Files 11 passed Tests 322 passed (322)
server: retry-scheduling, wake-diagnostics, question-response-delivery, comment-wake-batching, dot-runner, responsible-user-invariant, stale-queue-invalidation, queued-comments routes
Test Files 8 passed Tests 357 passed (357)
pnpm --filter ui exec vitest run src/components/task-chat/TaskChatQueuedMessages.test.tsx src/lib/issue-queued-comment-queue.test.ts
Test Files 2 passed (2) Tests 31 passed (31)
server: question-response-delivery, connection-intents-service, satisfied-connection-intents
Test Files 3 passed Tests 199 passed (199)
```
A first full run of the route file hit a 30s `beforeAll` timeout while
embedded Postgres started on a loaded machine. One new stopped-run case
was also flaky, because the agent could dispatch the new message before
the assertion. I fixed the flake: the test now occupies the agent on
another task (`maxConcurrentRuns: 1`), which is the existing pattern in
this file. I confirmed again that this test fails without the route fix.
Typecheck: `pnpm --filter @paperclipai/shared typecheck`, `pnpm --filter
@paperclipai/server typecheck`, and `pnpm --filter @paperclipai/ui
typecheck` pass. `pnpm check:token-gates`: all gates clean. I did not
run the full `pnpm test:run` or `pnpm build` locally. CI runs them.
## Risks
- Behavior change: when a receipt and a user message wait at the same
time, the composer now shows the message queue. The receipt is no longer
shown there until it is the only queued input. The receipt wake and its
delivery do not change. The resolved card in the thread still shows the
outcome.
- The first line of the steering body for connection receipts changes
from `Rejected: Connect Slack` to `Declined: Slack connection`. The JSON
receipt for the agent does not change.
- Out of scope (unchanged on master, reproduced without any receipt):
while a native run is running, each new message gets its own queued run,
and the queue shows only the first one. Messages that join a
`scheduled_retry` run's context also do not appear in the queue
projection.
- Overlap: the plan-approval queued-message fix (BUG-03) changes the
recovery sweep, not this code. Both fixes touch the same symptom area.
They do not edit the same functions.
## Model Used
- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, via Claude Code with
tool use (shell, browser inspection of staging, test 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>
|
||
|
|
2506bcdaf4 |
Admit exact stopped Runner sources under the current run lease (#19134)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Native execution keeps provider state and a durable controller journal. > - A completed process can stop before the next run attaches. > - The current run must own recovery of that exact source. > - An old controller must lose permission to change the consumed source journal. > - This pull request adds explicit source admission and protected task dispatch. > - The benefit is a narrow recovery boundary that preserves the original successful result. ## Linked Issues or Issue Description Native prerequisites #19132 and #19153 are merged into master. This PR targets master and updates the TypeScript Runner driver contract alongside Core and SDK admission. This change adds the SDK and Core admission boundary. Executor selection and fresh source preparation are a separate change. Related #17916 supplies the workspace export holds preserved here. ## What Changed - Plugins advertise `codexSourceRecoveryVersion: 1` for Codex App Server and `acpxSourceRecoveryVersion: 1` for ACPX. Hosts can qualify either, both, or neither. Core selects the matching field, because a stopped-source check for one provider cannot establish the other provider’s recovery guarantees. ACPX orchestration is tracked separately in #19196. Use the existing exact source identity and `resumeFrom` under the authenticated current run. No public recovery mode is added. Require the claimed source, current account and prepared launch at protected dispatch. - Keep the first source binding pinned on the consumer lease, including after uncertain dispatch. A newly acquired coordinator can refresh only the same source. - Claim one stopped successful source under the current run's acquired native lease. Store the token-free consumer reference in existing lease metadata. Preserve the source status and result. - Check the exact six source identity fields, encrypted journal header, company, task, agent, environment, driver, workspace, account selection, and current coordinator generation. Keep workspace export and legacy settlement holds. - Serialize claims and checkpoint writers in the same coordinator lock order. Reject stale source writes after consumption. Check the current consumer before recovered source writes. - Check source admission before and after protected worker dispatch. Keep the request and lease owned by the current run while the native identity remains the source. - Allow the same owner to prepare B after a fresh source submission, using the exact consumed-source proof instead of a generic warm-reuse flag. Recheck that proof after dispatch. - Preserve ordinary cached adoption and repeated preparation after launch. The current authenticated owner can differ from the adopted PRP identity without requesting a new source launch. Keep ordinary suspended recovery and live task attachment checks. Extract the shared source resolver to avoid a module cycle, and make ordinary selection honor the same durable source consumer. ## Verification - Current head `cac9cf188a` includes merged #19300 and the symmetric capabilities introduced in `b7e25a64b4`. The task-owner and warm-rebind fixtures now identify their physical task. All 29 SDK schema cases, 95 Core ownership and source-admission cases, 25 native Codex and ACPX recovery cases, and the binary metadata check passed on this head. The SDK and Runner builds and server typecheck passed. Both native providers reject the other provider’s capability. GitHub CI is green and [standard Greptile scored this commit 5/5](https://github.com/paperclipai/paperclip/pull/19134#issuecomment-6645574114) with no open threads. - Previous head `bda49271a4ae362a15baf3fe6d6d297e25fd2000` is directly based on master after #19132 and #19153 landed. [GitHub workflow 42072192023](https://github.com/paperclipai/paperclip/actions/runs/42072192023) passed on its first attempt. All check runs completed successfully or as intentional workflow skips; Snyk passed. Standard Greptile scored this exact head 5/5. That review had no unresolved findings. - Runner and SDK TypeScript builds and server typecheck passed. The focused SDK, task-owner, task-runtime, PostgreSQL source and authority suites passed all 161 cases. The production transport-factory getter regression and 14 native-lane selection checks also passed. - The Linux native lane executed the new ordinary warm A→B process case. It kept one Runner/provider process and completed two turns on the same thread. The eager-getter mutation fails with `native_runner_source_attachment_pending`; preserving the live getter passes. The production factory test changes the current owner after transport creation and checks that same options object. - PostgreSQL cases cover both concurrent source-writer/claim orders, exact source submission under the current caller, source-switch denial after uncertain dispatch, same-source coordinator refresh, stale same-boot generation, corrupted journal headers, competing consumers, account changes and export holds. - The protected dispatch sequence submits A, prepares B and retries B preparation. It rejects undispatched sources, changed accounts and foreign target bindings. Source status and accepted result remain unchanged. - Ordinary adopted A preparation under current B, repeated ordinary preparation after launch and ordinary suspended continuation retain their existing path. Absent or false claims preserve ordinary identity denial; malformed claims fail closed. - Earlier development attempts exposed ordinary preparation regressions and the live-getter omission; the final regressions cover those fixes. The first embedded PostgreSQL attempt needed missing library aliases repaired in ignored dependencies. Those attempts are not passing evidence. - Historical, superseded CI heads had unchanged UI fake-timer and Playwright health-response-lifetime failures. Their isolated cases and scoped retries passed. The response-lifetime cause remains unproven. No UI/test timeout was changed. The final rebased-head workflow above passed without a retry. - Whitespace and added-line secret/PII scans passed. Full workspace checks ran on GitHub. No live vendor probe or deployment was performed. ## Risks - Any driver that used the earlier Codex TypeScript option must use `codexSourceRecoveryVersion`. The new plugin description rejects the generic spelling. The Launcher’s existing Codex-specific HTTP header can map to this property when the plugin adopts the new SDK. The change requires no journal migration or ROM change. - This is an inactive admission foundation. Executor orchestration is separate in #19157. Native restore integrity and provider stopped-source/account validation must be present before executor activation. The native prerequisite uses ordinary recovery followed by attachment. The composed executor proofs are in #19157. - The source claim is durable. A later controller must recover the same consumer or keep the hold. It cannot select another source after an ambiguous launch. - Admission adds bounded database checks around protected startup calls. Busy issue locks fail closed; other lock waits have a one-second bound. - No migration, snapshot format, proactive receipt, recurring worker, or deployment is included. Full workspace checks and platform coverage run in CI. These tests do not prove vendor history durability or a hosted rollout. ## Model Used OpenAI GPT-6.1 Sol (`gpt-6.1-sol`), high reasoning, through Codex with repository tools and code execution. The runtime does not expose a 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 on the current head - [x] Standard Greptile is 5/5 with no unresolved findings on the current head - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
22281d4b3a |
feat(task-chat): approve harness-proposed plans (Codex plan mode, Claude ExitPlanMode) (#19291)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents run through the native runner. Codex and Claude Code each have their own plan mode, and each produces a proposed-plan document. > - Paperclip already has a planning work mode, an issue `plan` document, and a `request_confirmation` approval gate for plans. > - These parts were not connected. The Codex proposed-plan item was transcript text only. The server path never put Codex into plan mode. Claude was never put into plan mode, and its ExitPlanMode plan was dropped. A planning run could close its task as done with no approval. > - This pull request turns a harness's own proposed plan into the issue's Plan document and the existing approval gate, for Codex and Claude. > - The benefit is that users approve or send back the agent's real plan from the task chat. Approve resumes the agent. Request changes sends the reason back to the agent. ## Linked Issues or Issue Description Refs #19252 **Subsystem affected** Native runner provider events (Codex and ACPX Claude), the runner Codex provider (Rust), server native plan synchronization, and the task chat UI. **Problem or motivation** Codex and Claude both produce a proposed-plan document in their native plan mode. Paperclip did not use it. In live tests on the server path, a Codex planning run was not in Codex plan mode, saved its own `implementation-plan` document, and marked the task done without approval. Claude planning runs were not in Claude plan mode, and the ExitPlanMode plan text never reached Paperclip. **Proposed solution** Add a canonical `plan.proposed` event for a harness's proposed plan. Put Codex and Claude into their native plan mode for planning runs. Save a completed proposal as the issue `plan` document with the existing `request_confirmation` gate. Agents can also propose a plan on a standard task; the work mode stays standard. **Alternatives considered** Reuse `plan.updated`. Its schema is the within-turn checklist and is not the durable Plan document, so a separate event keeps the two clear. Stronger prompt text without native plan mode. Live tests showed that the model kept its own document workflow. **Roadmap alignment** This reuses the issue plan document, planning work mode and approval gate that already exist. It adds one canonical provider event. ## What Changed - `packages/paperclip-runner/protocol/schemas/*`: add the `plan.proposed` event (`paperclip.plan.proposed.v1`: `proposalId`, `status`, `markdown` up to 60k characters, `source`). Regenerate the schema bundle, validators and manifest. - `packages/paperclip-runner/src/provider-events.ts`: map the Codex `plan` item and a `<proposed_plan>` block in a completed Codex message to `plan.proposed`. Add `claudePlanProposalEvent`. - `packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs`: send Codex's native `collaborationMode: plan` on `turn/start` for a read-only (planning) Codex run. Before this change the server path never sent it. - `runner-core/src/acpx_event_payload.rs`: admit `plan.proposed` as ACPX rich display activity. - `drivers/acpx/codex-runtime-adapter.ts`: set Claude Code to native plan mode for a read-only run. Capture Claude's ExitPlanMode plan from its permission request, record it, and decline the native "Ready to code?" prompt. - `drivers/acpx/codex-acpx-driver.ts`, `cli/acpx-runtime-sidecar.ts`: emit the captured proposal as a rich event. Complete that turn instead of reporting it as cancelled. - `backends/codex-native-backend.ts`: per-harness plan-mode instructions, and a short opt-in hint for Claude on standard tasks. - `server/src/services/native-runtime/native-session-executor.ts`: synchronize a completed `plan.proposed` into the `plan` document and a `request_confirmation` in planning and standard mode. Do not change the issue status during the run (that revoked the run's lease). The run finalizer turns the pending confirmation into the review wait. - `server/src/services/heartbeat-run-summary.ts`: keep a `<proposed_plan>` block out of the posted reply. - UI: new `plan_proposal` provider family. The task chat shows a live "Writing plan" card while a proposal streams and a one-line record when it completes. Resolved plan reviews keep a receipt ("Approved Plan v1", "Requested changes to Plan v1: reason"). Plan previews skip section headings. - Storybook: `Tasks and conversations/.../Task chat/Proposed plan approval` shows the journey. - `doc/SPEC-implementation.md`: describe harness-proposed plan approval. ## Verification - `cargo test -p paperclip-runner-core --lib` (new: `read_only_codex_turns_use_native_plan_collaboration_mode`) - `cd packages/paperclip-runner && npx vitest run src/provider-events.test.ts src/drivers/acpx/codex-runtime-adapter.test.ts` - `cd server && npx vitest run src/__tests__/native-plan-proposal-sync.test.ts src/__tests__/heartbeat-run-summary.test.ts` - `cd ui && npx vitest run src/components/transcript/native-run-events.test.ts src/components/task-chat` - Live test on a local worktree instance with real harnesses: - Codex (`gpt-5.6-sol`), planning task: Codex asked one question, then produced a native plan item. The task showed Plan v1 with Approve plan / Request changes and moved to in review. Approve changed the work mode to standard. Codex then implemented the plan and its tests passed. - Codex, Request changes with a reason: Codex woke in planning mode and produced Plan v2 that includes the change. - Claude (`claude-opus-5-5`), planning task: Claude was in plan mode. ExitPlanMode became Plan v1 and the turn completed. Approve resumed Claude in standard mode, and Claude implemented the plan. - Claude, standard task that asks for a plan first: Claude stopped before code changes and asked for plan approval. The work mode stayed standard. - Storybook: open `Proposed plan approval` and check each stage. ## Risks - Codex planning runs now really use Codex plan mode on the server path. Codex can ask a clarifying question before it proposes. This matches Codex plan mode. - Claude planning runs now use Claude plan mode. A declined ExitPlanMode ends the Claude turn; the runner reports it as completed. Claude's tool row for that request records a rejection from Claude's side. - Codex does not stream the proposed plan on the server path yet, so the live card shows a placeholder until the plan completes. - A `<proposed_plan>` block in any completed Codex message on a standard task now asks for approval. This is the documented opt-in path. > 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`), via Claude Code with tool use (shell, file edits, browser). Long context. No extended thinking mode was used. ## 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> |
||
|
|
d807887534 |
fix(ui): use one status glyph mapping for every task view (#19329)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Task status is the main signal on every task surface: sidebar rows, the task header, the status menu, lists, the inbox, pills and chat cards. > - One task state rendered different status icons in different views. A blocked task that waits on an active sub-task showed the blue "in queue" glyph in the task header. The status menu, the chat chip and the sidebar rows showed the red "Blocked" glyph for the same task. > - The cause: each view passed its own subset of the presentation fields to `StatusIcon`. Many views dropped `blockerAttention` and some dropped `externalConversationState`. The status menu rendered its current row from the bare status. The summaries API did not return `blockerAttention`, so the recent-task sidebar could not know the state. > - This pull request puts the state-to-glyph/label mapping in one resolver and makes every task view pass all of its fields. > - The benefit is that the same state uses the same icon and label everywhere. ## Linked Issues or Issue Description No public issue. Found in a hands-on onboarding test on a staging cloud workspace. **What happened?** The sidebar and the task pane/status menu showed different status icons for the same state. On the test task (blocked, waiting on an active sub-task), the task header and the status button showed the blue "in queue" glyph. The selected "Blocked" row in the status menu showed the red blocked glyph. The coordinator sidebar "Recents" and project rows also showed the red blocked glyph, and the chat "Subtask created" chip showed a red "Blocked" chip. **Expected behavior** The same underlying state uses the same icon and label everywhere. **Steps to reproduce** 1. Create a task with a sub-task. Block the parent on the sub-task, and let the sub-task's agent run (the parent is "blocked, covered"). 2. Open the parent task. The header shows the blue "in queue" glyph. 3. Open the status menu. The current "Blocked" row shows the red glyph. 4. With the coordinator sidebar on, look at the task in Recents or in its expanded project. It shows the red glyph. **Paperclip version or commit** `master` at |
||
|
|
2575b941d0 |
feat(task-chat): show hook and auto-review decisions as governance rows (#19266)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents run on harnesses such as Codex and Claude, and the task chat shows what each run did > - Harnesses make automatic decisions: hooks block or allow tool calls, and Codex auto-review approves or denies actions > - Today Paperclip drops these decisions or shows them as generic rows. Codex `safety.review.*` always says `decision: "unknown"`, the native Rust runner drops `hook/*` and `item/autoApprovalReview/*`, and claude-agent-acp drops Claude hook messages > - An operator cannot see why an agent changed course or which action ran without a person's approval. This conflicts with Paperclip's approval-gate model > - This pull request records each hook and review decision with its reason and shows it as a small governance row in the task chat > - The benefit is that automatic decisions become auditable, and a blocked action explains itself without opening run logs ## Linked Issues or Issue Description Refs #19252 (precedent: canonical `plan.updated` events rendered as a task-chat card). Builds on #19259 (ACP `_meta` passthrough from ACPX), now merged. **Subsystem affected** Runner provider events (Rust native Codex normalizer, TS drivers, ACPX sidecar), the claude-agent-acp patch, the PRP provider-event schema, and the task chat activity UI. **Problem or motivation** Hook and auto-review outcomes reach Paperclip but are lost or flattened. Codex reviews always record an unknown decision. Native Codex runs drop hook and review notifications in the Rust normalizer. Claude hook messages never leave the adapter. Codex reuses one `run.id` for every call of a hook, and the id contains a host path. **Proposed solution** Map the real outcome, decision, action, risk and reason into additive optional fields on `paperclip.hook.v1` and `paperclip.safety.review.v1`. Forward Claude SDK hook messages. Render small grey rows: a stopped action is tinted and shows its reason, an approved review folds into the command it approved as an "auto-approved" mark, and routine hook passes stay out of activity summaries. **Alternatives considered** Separate rows for every approval. This duplicated each approved command, so approvals fold into the command row instead. ## What Changed - Runner (Rust): new `governance_events.rs`. `normalize_codex_notification` now maps `hook/started|completed` and `item/autoApprovalReview/started|completed`. Each hook call gets its own id (a digest of the run id and start time), so no host path is stored. - Runner (TS): new `governance-events.ts` with the same mapping for the TS Codex driver, and the Claude hook mapping. The ACPX sidecar publishes Claude hook updates as schema-validated `hook.*` rich events. - claude-agent-acp patch: turns on `includeHookEvents` and forwards `hook_started` / `hook_response` on `session_info_update._meta.claudeCode.hook`, with bounded output. - Protocol: additive optional fields. Hook records get `outcome`, `message`, `source` and `toolName`, and the `blocked` and `stopped` statuses. Review records get `actionKind`, `action`, `riskLevel`, `userConsent` (the reviewer's user-authorization level; keys that end in "authorization" are redacted as secrets), `rationale` and `decidedBy`, and the `timed_out` and `aborted` decisions. Regenerated the manifest and validators. - Fixed: the hook `blocking` flag read `executionMode === "blocking"`, but Codex sends `sync` / `async`. - UI: `native-run-events` keeps the full hook and review payloads. `governance-presentation.ts` writes the row labels. `fold-auto-reviews.ts` attaches an approved review to its command row. The rows show the tint, the reason line and the "auto-approved" mark. Activity summaries lead with a block or denial. - Storybook: "Task chat / Governance rows" shows every outcome, a Codex run, a Claude run and the live line. - Docs: added a governance activity section to `packages/paperclip-runner/docs/protocol-compatibility.md`. ## Verification - `pnpm --filter @paperclipai/paperclip-runner exec vitest run src/governance-events.test.ts src/provider-events.test.ts src/drivers/codex src/drivers/acpx src/cli`. 2305 tests pass. This includes a replay of real Codex 0.160 notifications from a session with hooks and auto-review (`test-fixtures/governance/codex-0.160-hooks-auto-review.json`, sanitized). - `cargo test --release -p paperclip-runner-core`. The full suite passes, and `governance_events::tests` replays the same fixture. - `cd ui && npx vitest run`. 11,047 tests pass. - `node scripts/typecheck-low-memory.mjs`, `pnpm check:token-gates`, and the runner contract checks (`check:protocol-manifest`, `check:protocol-types`, `check:capability-contract`, `check:protocol-coverage`, replay goldens, contract fingerprints) pass. - Live test on a local isolated instance. A Codex agent and a Claude agent each used a preconfigured home with a PreToolUse hook that blocks edits to `0001_init.sql`, plus UserPromptSubmit and PostToolUse hooks. Both runs showed "PreToolUse hook blocked a tool call" (Codex) or "PreToolUse hook blocked Bash" (Claude), with the hook's message. They also showed "UserPromptSubmit hook added context", and the group summary started with the block. - Auto-review cannot run in Paperclip today, because the native Codex runner only allows `approvalPolicy: never`. The review mapping and the approval fold are proven against real Codex 0.160 app-server output (fixture above) and in Storybook. ## Risks - Low risk to execution. These records are display-only and carry no authority. All new schema fields are optional, so older records stay valid and older UIs ignore the new fields. - The `auto-approved` fold depends on `targetItemId` matching the command's execution id. Real Codex 0.160 data confirms that it matches. When there is no match, the review stays as its own row. - In Paperclip's isolated homes, hooks do not run, because the runner never copies hooks into an isolated home. Rows appear only for agents with a preconfigured home, or with future managed hooks. - The claude-agent-acp patch changes. The ACPX profile check still passes, because the patch is Paperclip's ACPX layer and not the upstream release. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use (shell, browser and file editing) and extended reasoning. ## 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> |
||
|
|
be5709a25c |
fix(runner): classify incompatible saved provider profiles (#19334)
Classify deterministic OpenCode and Cursor Cloud saved-profile mismatches with the existing permanent provider-state class. Preserve saved state and stop same-run retries after the runner refuses that state. Verified 37 native-provider backend tests, durable classified-result replay, the server policy test, rustfmt, workspace typecheck and build. Complete hosted CI passed with Greptile 5/5 and no unresolved threads. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
dd889e6a0b |
feat: show the assignee's provider usage limit in task properties (#19270)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents run on the native Runner through the Codex app-server and the Claude ACP harness > - Both harnesses report the provider account's rate-limit windows: Codex sends `account/rateLimits/updated`, and Claude sends its SDK `rate_limit_event` as `usage_update._meta["_claude/rateLimit"]` > - Paperclip dropped both signals, so an operator could not see that an agent was near or past its provider limit > - This pull request normalizes both signals into one account-scoped snapshot, keeps the latest snapshot per agent, and shows it as one "Usage limit" line in the task properties pane > - The benefit is that an operator sees "5h 78% · resets 15:40" (amber above 80%, "Limited until …" when the provider refuses work, "using credits" when a used-up window falls back to credits) next to the assignee, and can click it for every window ## Linked Issues or Issue Description Refs #19246 Refs #19259 **Is your feature request related to a problem?** Codex and Claude report provider rate-limit windows during a run. Paperclip dropped them: the Codex notification fell through the Rust normalizer with no branch, and ACPX dropped Claude's `_meta`. Operators could not see how close an agent was to its provider limit. **Describe the solution you'd like** Store the latest reported windows per agent and show one compact line in the task properties pane, with a popover that lists every window. **Describe alternatives you've considered** A full gauge in the agent list and the run header. A design review chose one property line with a details popover instead. ## What Changed - Runner (Rust, the runnerd path): new `provider_rate_limits.rs`. Codex `account/rateLimits/updated` becomes a `provider.notice.recorded` event (category `provider.rate_limits`, scope `account`). `CodexProviderState` merges sparse updates and emits a notice only when the windows change. ACPX `usage_update` with a sanitized `rateLimit` field also emits the notice. - Runner (sidecar): `acpx-runtime-sidecar.ts` forwards only `status`, `rateLimitType`, `utilization` and `resetsAt` from `_meta["_claude/rateLimit"]`. This needs the ACPX `_meta` passthrough from #19259. Until that merges, Claude emits nothing. - Runner (TypeScript in-process drivers): the same mapping in `codex-session-notifications.ts` and `codex-acpx-driver.ts` through `drivers/provider-rate-limits.ts`. - Shared: `provider-rate-limits.ts` holds the snapshot type, the parser, the merge, and `summarizeProviderRateLimits` (fullest current window, expiry by reset time, limited versus using credits). - Server: `agent-provider-rate-limits.ts` merges each notice into `agent_runtime_state.state_json.providerRateLimits`, scoped by company and agent. A session reset keeps the snapshot. No migration. - UI: `AgentRateLimitPropertyValue` adds a "Usage limit" row under the assignee in `IssueProperties`. Transcripts treat the notice as run-log only. A run status event refreshes the assignee's runtime state. - Storybook: `Tasks and conversations/Task creation and lifecycle/Usage limit property`. - Docs: `doc/run-log-events.md` describes the notice and its storage. ## Verification - `cd packages/paperclip-runner/runner && cargo test -p paperclip-runner-core --lib rate_limit` and `--test acpx_provider_events`: sparse Codex merge, dedupe, reached window, checkpoint round trip, Claude utilization and rejection, notice shape. - `cd packages/paperclip-runner && npx vitest run src/drivers/provider-rate-limits.test.ts src/drivers/codex` - `cd packages/shared && npx vitest run src/provider-rate-limits.test.ts`: summary, expiry, limited, using credits, parse and merge. - `cd server && npx vitest run src/__tests__/agent-provider-rate-limits.test.ts`: create and merge, out-of-order snapshot, company isolation, unrelated notices, session reset keeps the snapshot. - `cd ui && npx vitest run src/components/transcript src/components/issue-properties src/context/LiveUpdatesProvider` - `pnpm -r typecheck` and `pnpm check:token-gates` pass. - Live test on a local worktree instance: a native Codex agent on a ChatGPT subscription login ran a real task. The runner recorded one `provider.rate_limits` notice. The server stored it. The task properties pane showed "7d 100% · using credits" (this account's weekly window was used up and had credits, and the run succeeded). The transcript showed no extra row. - Not verified live: Claude. The local Claude CLI was not signed in, and the Claude path also needs #19259. The Rust and sidecar paths have tests. - Not run locally: the full `pnpm test:run` and `pnpm build`. CI runs them. ## Risks - Low risk. The snapshot is display data. It does not hold or start work. The existing provider-limit wait (#19246) still reads typed run failures. - Codex can send a rate-limit update often. The runner emits a notice only when the windows change, so run-log growth stays small. - With API-key auth, the harnesses report no windows, so no row appears. This is the intended result. - `CodexProviderState` gains two optional serde fields with defaults, so older checkpoints still load. > 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, through Claude Code with tool use (shell, file edits, browser) and extended reasoning. ## 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> |
||
|
|
31bf6e4482 |
Pin plugin Runner cleanup to its physical task (#19300)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Plugin Runner owners manage a physical task through an environment lease. > - The existing replacement path can assign a new physical task to that same lease. > - A delayed old owner can then stop the replacement or mark it stopped. > - Cleanup must keep the original physical task identity across RPC and database writes. > - This pull request pins that identity and keeps cleanup available to the new owner. ## Linked Issues or Issue Description Refs #19134 and #19157. This fix is based on master and does not enable repeated stopped-source recovery. A search found no duplicate fix. **What happened?** The maintained `createOwnedSandbox` replacement path changes `providerLeaseId` on one lease. An old task owner uses that mutable lease for a later Stop. A stopped-state response already in flight can also update the new task's flags. **Expected behavior** The old owner must address its original task. Its late cleanup writes must not alter the replacement. The replacement's own owner must still stop and complete it. **Steps to reproduce** 1. Create a plugin task owner for a stopped task. 2. Replace that task through the existing describe/startup path on the same lease. 3. Resume an old checkpoint or a held old status response. 4. Observe a Stop for the replacement or an incorrect stopped flag before this fix. ## What Changed - Require each task owner to keep the physical ID returned by its description. Reject a rebind to a different task. - Carry the ID through the existing internal task context after startup ends. Reject a changed physical task before RPC dispatch. - Match the physical task in stopped-state, completion and credential-handoff writes. - Hold the exact lease row through the retained-session database update and local checkpoint save. - Cover the maintained replacement path with real PostgreSQL tests. Preserve positive cleanup for the replacement owner. Document this boundary. ## Verification - Both replacement regressions failed on unmodified master `10a7d039c8`: delayed old-owner Stop targeted the replacement, and a held old status response marked it stopped. - Six focused files passed 160 tests: task authority, task runtime, warm tasks, execution target, credential writeback and task owner. - Four selected executor factory cases passed. They cover task-only providers and the physical task binding after the startup context ends. - Runner TypeScript, plugin SDK and adapter-utils builds passed. Server TypeScript check passed. Diff and added-line secret scans passed. - All 15 setup-probe cases passed with the actual Runner and fake provider binaries. The first local attempt lacked cargo on PATH. A sandboxed native attempt timed out in seven process cases; the unchanged run with loopback permission passed all 15. A separate initial DB integration attempt was denied a loopback listener; its permission-correct run passed. - Review found three missed constructors in the existing SDK/Core lifecycle fixture. They now use the actual describe result. All four cases in that real PostgreSQL and worker RPC file passed. - The first GitHub attempt failed in the unchanged GitHub recovery pagination case in `chat-channels.integration.test.ts`: its zero-request assertion observed a configuration GET. That exact case passed locally without code or assertion changes. The one failed-job-only retry passed; the initial failure log is retained. - [GitHub workflow 42082587576](https://github.com/paperclipai/paperclip/actions/runs/42082587576) passed on its second attempt. All CI checks completed, and Snyk passed. Standard Greptile scored exact head `fb914d98b99e` 5/5 with no unresolved threads. The branch is mergeable and ready for review. ## Risks The task context change is internal. It adds no plugin wire field, schema or Runner mode. A stale owner now gets an error and cannot clean up a replacement. A new owner can still clean up that task. The retained checkpoint write holds one existing lease row during a bounded local file write. Repeated stopped-source incarnation admission, runtime pins and deployments are outside this change. ## Model Used OpenAI GPT-6.1 Sol (`gpt-6.1-sol`), high reasoning. Used repository tools, TypeScript and real PostgreSQL tests. ## 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 described the issue in-PR using the bug template - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change - [x] I have run the focused tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation - [x] I have considered and documented risks - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no unresolved findings - [x] I will address reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
62a5aa7705 |
fix(runner): forward configured bindings into OpenCode (#19319)
Forward controller-selected task bindings into OpenCode shell commands. Keep OpenCode runtime configuration isolated. Redact raw and JSON-escaped binding values while preserving the non-secret environment manifest. Fixes #19257 Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
86a9d3ec6c |
feat(task-chat): tool rows that say what ran and how it went (#19265)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The task chat shows each tool call an agent makes as a short activity row. > - Real Claude runs showed Bash rows named "Terminal" with no command and Write rows stuck at "Preparing file…". No row showed an exit code or a duration, and a tool refused by policy looked like any other failure. > - Codex already reported exit codes, durations and parsed command actions, but the UI dropped them, and every Codex read looked like a shell command. > - This pull request gives both harnesses one row grammar: a verb icon, the real subject, an exit chip and a duration. > - The benefit is that a reviewer can see what ran and how it went without opening each row. ## Linked Issues or Issue Description Refs #19259 (this branch includes its `_meta` passthrough commit; merge #19259 first or together). **Problem or motivation** Claude tool rows hide what ran: the runner keeps the first ACP title ("Terminal", "Preparing file…") and drops the refined one. `exitCode` and `durationMs` are null on every Claude tool event. The UI ignores `tool.execution.progressed`, `exitCode` and `durationMs` for every harness. **Proposed solution** Read real tool facts in the runner (Claude `_meta.claudeCode`, raw input and result text; Codex `commandActions`), carry them in the canonical tool event, and render one row grammar in the task chat. **Alternatives considered** Enable Claude's `terminal_output` client capability to get `terminal_exit`. That changes how command output arrives, and Claude's own exit code is derived from the error flag anyway, so this PR reads the error flag and the "Exit code N" text instead. ## What Changed - **Runner (TS):** `createAcpxToolEventNormalizer` keeps the latest concrete title instead of the first placeholder. It merges tool facts across the lifecycle and measures the duration. New `acpxToolDetailFromRuntimeEvent` reads the native tool name, the command or pattern, the description, the exit code and a "Permission denied" reason. Exit 0 is inferred only for Claude `Bash`, where a non-zero exit is always a failed result. - **ACPX sidecar:** reads the facts before `rawInput` is dropped and sends a bounded `toolDetail`. A file path argument becomes an attested workspace-relative location. - **runner-core (Rust):** validates `toolDetail`. `normalize_acpx_tool_call` uses the native name, exit code, duration, subject, description and blocked reason. Codex `commandExecution` gets its operation, a single-file target and a subject from `commandActions`. The shell wrapper is removed, and a single search reads as `"TODO" in src`. - **Protocol:** `toolExecution` gains the optional fields `subject`, `description` and `blockedReason`. Generated validators and the manifest are regenerated. - **UI:** - `nativeRunEventsToTranscript` handles `tool.execution.progressed` and passes a canonical `detail` on `tool_call` entries. - The task-chat row shows `$ command`, a path or a pattern, a red `exit N` chip (exit 0 only for commands) and a duration (0 is hidden). - Three or more consecutive reads fold into "Read N files a, b, c +1". - A refused tool shows "Blocked" with its reason. - **Storybook:** `Tasks/Tool rows` compares the data recorded today with the new runner data for Claude and Codex. ## Verification - `cd packages/paperclip-runner && npx vitest run src/provider-events.test.ts src/cli src/drivers/acpx` (1,957 pass) - `cd packages/paperclip-runner/runner && cargo test -p paperclip-runner-core --lib provider_events` - `cd ui && npx vitest run src/components/transcript src/components/task-chat` (973 pass) - `pnpm check:token-gates`, `pnpm --filter paperclip-runner build` (includes the protocol coverage and replay-golden checks) - Live runs on a local instance with real harnesses: - Codex (gpt-5.6-sol): the rows read `$ node --test` with `exit 1` and then `exit 0`, plus real durations. A three-file `sed` shows as one command and does not claim a single file. - Claude (claude-sonnet-5-5 through ACPX): the rows show `Bash` and `Edit` instead of "Terminal" and "Preparing file…". Commands show their real text, `exit 0` and durations, and edits show `Edited temp.js`. - Not seen live: a real Claude `Write`, a folded read run and a Claude policy denial. Unit and Rust tests cover them. ## Risks - The tool-execution schema gains only optional fields, so older events stay valid. Older runs keep rendering with what they recorded. - Commands now cross the sidecar boundary as `subject` (bounded to 4,000 characters). Codex already recorded commands as the tool name, and server redaction still applies. - The tool lifecycle now keeps the latest concrete title. A provider that retitles a call to something less useful would show the newer title. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use, code 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 - [ ] 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> |
||
|
|
ee72132a44 |
fix(daytona): bound one-shot exec requests by their command deadline (#19298)
Bound the Daytona one-shot executeCommand request at its command deadline plus 5 s and return an attributed timed-out result, instead of waiting for the host RPC deadline. Bound the stdin cleanup the same way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7f5d0793bd |
fix(daytona): record a bounded errno when a sandbox command fails (#19297)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Daytona sandbox provider copies the agent's workspace back to
the host after each turn (syncOut)
> - When the sandbox `tar` fails in the `archive_create` step, the
restore diagnostic records only `exitCode: 2` and `errorCode: "unknown"`
> - The command output is not sent across the plugin RPC boundary, so an
operator cannot tell a full disk from a permission error or a path that
is too long
> - This pull request maps the system error text at the end of a failed
sandbox command's output line to the existing allowlisted `errorCode`
> - The benefit is that the next failure records the real errno, and the
output text stays private
## Linked Issues or Issue Description
Refs #18172
Refs #18212
**What happened?**
A hosted Daytona run failed its workspace restore. The `archive_create`
step exited 2 on all three attempts. The run log recorded
`{"step":"workspace_transfer","transferStep":"archive_create","transferFailureKind":"command_failed","exitCode":2,"errorCode":"unknown"}`.
Two causes are possible. The sandbox disk was full, or a path was longer
than PATH_MAX, for example from deeply nested GitHub helper config
directories before #18212. The diagnostic cannot tell these causes
apart.
**Expected behavior**
A failed sandbox command records a bounded errno such as `ENOSPC`,
`EACCES` or `ENAMETOOLONG` when its output shows one. The output text
itself must not be recorded.
**Steps to reproduce**
1. Run `performSyncOut` against a sandbox whose `tar -c` exits 2 with
`tar: ./x: Cannot open: Permission denied`.
2. Read the environment sync diagnostic from the thrown error.
3. See that `errorCode` is `unknown`.
**Paperclip version or commit**
`master` at
|
||
|
|
55389554c2 |
fix(runner): paginate workspace discovery before journal persistence (#19311)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Native agents discover reusable workspaces through `list_workspaces`. > - The tool returned every authorized workspace in one result. > - A large inventory exceeded the durable command journal limit. The result stayed pending, and session cleanup blocked the next turn. > - This pull request limits discovery in SQL and returns explicit continuation metadata. > - Agents can inspect every authorized workspace without sending the full inventory in one command. ## Linked Issues or Issue Description Related: #15749 introduced the task workspace tools. No duplicate pagination fix was found. **What happened?** A native workspace discovery call returned a multi-megabyte inventory. Persisting its result failed with `command_payload_too_large`. The preceding answer could still be saved, but cleanup retained the unsettled tool call. A later turn failed before provider work with `native_session_cleanup_quarantined`. **Expected behavior** Workspace discovery must return a manageable page. Agents must be able to request the remaining authorized rows. Existing visibility checks and journal integrity checks must stay effective. **Steps to reproduce** 1. Create 2,000 task-selectable workspaces with ordinary repository paths. 2. Call `list_workspaces` from an admitted native run. 3. On the old code, the real API regression returns 869,781 bytes. The command journal allows about 510 KiB. 4. On the repaired code, follow `nextCursor` to inspect all rows. Pages contain at most 100 rows and fit within 128 KiB. **Paperclip version or commit** Reproduced on source commit `7351a9d74f` before the repair. **Deployment mode** Isolated source checkout with real PostgreSQL and authenticated native agent APIs. Hosted deployment and incident recovery remain separate. ## What Changed - Validate workspace list limits and UUID cursors with a JSON Schema pattern supported by strict validators. Return `workspaces`, `limit`, `returnedCount`, `hasMore`, and `nextCursor`. - Apply company, visibility, lifecycle, and cursor conditions before the SQL limit. Order pages by workspace ID. Fetch one extra row to detect continuation without a count query. - Fit encoded pages within 128 KiB, including JSON escaping and Unicode. If one summary cannot fit, return explicit string previews with `truncatedFields`. The authorized detail API retains its full record. - Keep existing unpaged summary callers on their array response and recent-use order. - Update the native tool descriptor, generated contracts, catalog fixture, and execution documentation. - Add regression coverage for large inventories, complete pagination, stable pages, private and foreign rows, invalid inputs, and legacy summary behavior. Add an opt-in two-turn live Codex acceptance check. ## Verification - Red before repair: `task-workspace-api.test.ts -t "pages a large authorized workspace inventory"` failed because the result was 869,781 bytes instead of below 128 KiB. The transport regression also failed at 2,284,671 bytes. - Green after repair and review changes: 120 tests passed across the workspace API, transport, routes, native authority, coordinator, historical-question, and Codex vertical-slice suites. They cover complete discovery, byte-sized pages, oversized individual summaries, full detail retrieval, private/foreign/lifecycle exclusions, and strict schema compatibility. - Journal safety: two focused durable control-plane tests passed. Oversized result rejection and lossless valid result delivery remain effective. - Tool catalog and reconciliation: 30 tests passed. - `pnpm -r typecheck`: passed. - `pnpm build`: passed again on final commit `bc10880da6`. Server typecheck also passed after the review changes. The original affected suites passed on the rebased first commit (31 tests), and the expanded review regression set passed afterward (120 tests). - Real provider acceptance passed again on final commit `bc10880da6`: local macOS arm64, rebuilt native Runner, `gpt-6.1-sol`, high effort, per-turn lifecycle, and 2,000 persisted workspaces. Both turns succeeded. Persisted call inputs and results prove that the second page uses the first result’s cursor, both calls succeed, their IDs do not overlap, and the encoded results stay within 128 KiB. The second turn kept the provider conversation and file. Both task locks were clear after completion, and no recovered execution failure was recorded. Duration: 166.92 seconds. - Full local `pnpm test:run` stopped in the chat shard: 330 tests passed and one Slack registration test failed with `socket hang up`. That exact test passed in isolation without changes, and the final-head CI chat shards passed. A broader local managed-Slack follow-up produced no result after about eight minutes and was interrupted; it is not counted as a pass. The original full-suite failure remains recorded. - The first CI head failed native tool-schema compilation because strict validators did not recognize `format: uuid`. The revised descriptor uses a UUID pattern. The previously failing coordinator, native vertical-slice, authority, and history suites now pass locally. The stronger live cursor-receipt acceptance check passed. Final-head CI completed on `bc10880da6`: 57 checks passed and five optional checks were skipped, with no failures or pending checks. Greptile reviewed that commit at 5/5; both prior findings are resolved. - Dependency cost: paged discovery runs the existing visibility-scoped summary query with an ID cursor and at most 101 returned rows. It adds no count query, filesystem scan, provider call, or background worker. ## Risks - Native `list_workspaces` now returns a page object instead of an array. Individual summaries can be explicit previews when a single record exceeds the byte budget. Its advertised descriptor and generated contracts change together. Agents must follow `nextCursor` until null for a complete inventory. - Each page rechecks current visibility. Concurrent insertions, deletions, or access changes require a fresh scan when a complete point-in-time inventory is needed. - No migration or journal-limit change. An existing cleanup quarantine still requires normal recovery after deployment; this PR does not bypass it. ## Model Used OpenAI Codex, GPT-6 family, with reasoning, shell tools, code editing, and test execution. The exact served model revision and context window are not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
a6f786479e |
Runner: verify provider eligibility before parking (#19133)
## Thinking Path > - Paperclip coordinates AI agents and their execution. > - Hosted Runners can retain a provider process between turns. > - The host must prove that process is idle before allowing its VM to sleep. > - A completed turn alone does not rule out an active goal or trailing provider work. > - This PR adds a conservative park-readiness hook for the existing Codex process. > - The later host-control consumer can use this proof with its identity and sequence fences. ## Linked Issues or Issue Description **Current behavior** There is no provider eligibility hook for host parking. The earlier implementation in #19060 also missed an active goal that arrived during its tail drain. **Proposed behavior** Default every executor to ineligible. Permit the surviving native Codex process only when its completed-turn authority still matches and it has no live or unsettled work. **Reason and benefit** Keep active goals and unresolved provider obligations awake. Review the provider proof separately from socket handling and the native park/reclaim loop. ## What Changed - Add `host_park_ready` with a default false result. - Refresh the configured credential observer before checking pending delivery. Refuse parking during credential delivery, without selecting or restoring a provider. - Verify the current Codex process and completed-turn identity; reject startup, active turns/goals/tools, and unsettled event queues. - Require completion actually observed by that process. Accepted-finish reconciliation still preserves ordinary result authority without qualifying a restored process to park. - Refuse known helper lineage, whose diagnostics do not prove that helper work has finished. - Drain bounded trailing output through the existing accounting path. Retain goal updates and clears in durable state and normal event delivery, then recheck live goal state. - Add process tests for normal eligibility and the late-goal race. Assert that a restored process is ineligible until it completes an authoritative turn in its new generation. This PR is based on master, which contains the merged Codex (#19132) and ACPX (#19153) recovery foundations. The private socket codec in #19129 is merged; #19140 consumes this proof in the shared native loop. This PR does not advertise parking support. A separate ACPX readiness change must prove its live sidecar/session has no active or unsettled work. Both Codex App Server and qualified ACPX are required in the remaining park/reclaim, Core restart, eviction, and live sleep/wake acceptance phases. ## Verification - `cargo test --offline --locked --manifest-path packages/paperclip-runner/runner/Cargo.toml -p paperclip-runner-core --test codex_provider hosted_park` — 5 passed. - `ordinary_recovery_restores_a_completed_idle_session` passes and asserts that the genuinely restored provider cannot park before it completes an authoritative turn in its new generation. The fixture proves two provider starts and only one admitted turn. - The initial live-goal recheck mutation failed as expected. Review then identified that goal changes also needed durable retention. The new activation and clear cases verify saved goal state, retained events, and eligibility after delivery. - Existing goal, active-goal attachment, warm-tail, and late-accounting process regressions passed after sharing the goal event path. - The initial late-goal fixture timed out because it omitted `--durable-turn-ids`, which enables its existing first-turn gate. Added that fixture switch; both park tests then passed. - The credential-observation regression fails before the fix and passes afterward. Three focused credential cases prove durable replay and acknowledgement, fresh ACPX startup, and readiness without provider selection. The three provider parking checks also pass after the fix. - Full regression testing is left to GitHub CI. - Changed lines scanned for secrets, personal data, and private deployment references before push. Current head `28d41b083f58ee93db4305b1217719796bf3171d` adds one scoped review-fix commit to readiness `e8ba82374e0f72c82ac3195d4b1b3798c9072bab`, based on master `3d0d178ff7051aa44e07cd873fd4281357e0d815` without duplicating either recovery foundation. The PR remains eight parking-specific files. Review identified quiet helper work and reconstructed accepted-finish authority that could incorrectly qualify. The provider now records its actually observed terminal authority separately in memory, and parking refuses known helper lineage in both the saved state and the live inventory after draining. No durable field, lifecycle, public API, or ordinary recovery policy changed. Both process regressions fail when the parking guards are removed and pass when restored. Seventeen focused checks pass, including ordinary recovery, helper requests, receipt replay, credentials, and ACPX ineligibility. Rustfmt, diff and added-line scans passed. All exact-head CI is green: [PR run](https://github.com/paperclipai/paperclip/actions/runs/42087773928), [Runner Windows](https://github.com/paperclipai/paperclip/actions/runs/42087773720), and [Docker Runner](https://github.com/paperclipai/paperclip/actions/runs/42087773712) passed; Storybook was skipped. Standard review 15 scored 5/5 before the authorized follow-up [Apex review](https://github.com/paperclipai/paperclip/runs/127471966630). Apex review 16 also scored [5/5 on this exact head](https://github.com/paperclipai/paperclip/pull/19133#issuecomment-6645371152), with zero new comments and zero unresolved threads. On this current head, the first server shard 11/12 [job 127458190155](https://github.com/paperclipai/paperclip/actions/runs/42087773928/job/127458190155) failed the unchanged decision-model concurrency test: after releasing the database lock, its `waitFor` observed one provider call instead of two. The identical file passed all 37 tests on the prior reviewed head. One targeted unchanged-head retry [job 127466020677](https://github.com/paperclipai/paperclip/actions/runs/42087773928/job/127466020677) passed all 1,779 tests, including those 37 decision-model cases; 13 tests were skipped. No code, test timeout, or CI configuration changed between attempts. The original failure's cause remains unestablished. On prior reviewed head `e8ba82374e0f72c82ac3195d4b1b3798c9072bab`, the first `General tests (workspaces-c)` job [127389771745](https://github.com/paperclipai/paperclip/actions/runs/42068371976/job/127389771745) failed the unchanged database SIGTERM test at `packages/db/src/test-postgres-command.test.ts:44`: `JSON.parse` read empty readiness-file content immediately after an existence check. The fixture writes directly to that path, leaving a create-before-content race. One targeted unchanged-head retry [127398676711](https://github.com/paperclipai/paperclip/actions/runs/42068371976/job/127398676711) passed all **313 tests in 72 files**, including SIGTERM and SIGINT. No source or timeout changed between attempts. On the superseded `ca088abd552b` head, one Cloud sign-in recovery case returned to the expected task URL but missed its heading within the five-second assertion window; the other 40 cases passed. One targeted unchanged-head retry (job 127136265280) passed all **41 cases**. No test timeout or application code changed between attempts, and the first failure's cause remains unestablished. The superseded c7 parent failed eight retained-cleanup settlement cases because prepared restoration started a provider during a drain-only epoch. The corrected parent preserves ordinary prepared behavior; the new completed-source path excludes prepared providers. The parent reports all 19 settlement rows passing. This PR adds no prepared-session proof or restoration policy. ## Risks Known helpers remain conservatively ineligible even after their progress diagnostics: this slice has no authoritative helper-completion ledger. The eligibility check drains the existing bounded provider tail and can persist late accounting. That accounting must settle before parking succeeds. This hook alone does not prove VM suspension or vendor goal behavior across a process restart. Live sleep/wake qualification remains a later rollout check. ## Model Used OpenAI GPT-6.1 Sol (`gpt-6.1-sol`), High reasoning, through Codex 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 - [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> |
||
|
|
377e98f1a2 |
test(runner): keep the production close budget for recovered prepared-descriptor closes (#19309)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Paperclip Runner closes a native session only after it proves
that the provider drained and the runner suspended
> - The test "runs an environment-prepared descriptor without Core
filesystem preparation (rotated-recovery)" closes recovered transports
with a 5s close grace
> - A 5s grace leaves only 2.5s for the provider drain, and a recovered
successor needs several drain round trips
> - On a loaded CI runner with the debug runnerd, the drain does not
finish in 2.5s, so close correctly fails closed and the test fails
> - This pull request makes those closes use the production close budget
> - The benefit is that the Runner Vitest lane stops failing on
unrelated PRs, and the close assertions stay the same
## Linked Issues or Issue Description
No issue exists. The problem, in the bug report format:
**What happened?**
The Runner Vitest lane failed on PR #19265 (run 42075789869, job "Verify
Paperclip Runner (vitest package)"). The change in that PR is not
related to the Runner:
```
FAIL src/live/runnerd-codex-transport.test.ts > runs an environment-prepared descriptor without Core filesystem preparation (rotated-recovery)
NativeSessionCloseUnrecoverableError: provider_transport_failed: runner did not durably suspend before checkpoint
❯ src/live/runnerd-codex-transport.test.ts:2768:21
Serialized Error: { settlement: { runnerSuspended: true, providerDrained: false, suspensionState: null, ... finalProviderState: null } }
```
**Expected behavior**
The test fails only when recovered close behavior is wrong. It does not
fail because the CI machine is slow.
**Steps to reproduce**
1. In the rotated loop of the test, set the close grace to 700ms instead
of 5_000.
2. Run `npx vitest run src/live/runnerd-codex-transport.test.ts -t
rotated-recovery`.
3. The second (recovered) close fails with the same error and the same
settlement (`runnerSuspended: true`, `providerDrained: false`,
`suspensionState: null`).
Root cause: `runnerCloseDeadlines` keeps half of the grace (max 2.5s)
for suspension. The rest is the preparation budget for the provider
drain. With `closeGraceMs: 5_000`, the drain gets 2.5s. In a local
measurement, the recovered close needed 4 `runner.drain` commands and
about 440ms on an idle machine. Each drain is a full runner round trip,
because a drain receipt is `true` only after the controller ACKs the
prefix that the previous drain moved. On a loaded CI runner with the
debug runnerd binary, those round trips use more than 2.5s. The
transport then fails closed, which is correct product behavior.
Production uses a 10s default grace (7.5s for preparation, with the
drain capped at 5s).
**Paperclip version or commit**
`master` at
|
||
|
|
03eb49053f |
test(runner): bound Codex provider event waits by a hang ceiling, not 5s (#19306)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The Rust runner drives provider processes such as Codex, and CI tests it on Linux, macOS and Windows > - The "Runner Rust (Windows x64)" job fails at random on pull requests that do not touch the runner > - The failures come from Codex provider integration tests that wait for fake-provider events under a fixed five-second deadline > - On a loaded Windows runner, process spawn, antivirus scans and fsyncs of durable state make these exchanges take longer than five seconds, so the wait stops before the event arrives > - This pull request keeps each wait on its condition and changes the deadline to a shared hang ceiling > - The benefit is that unrelated pull requests do not fail on this job, and a real hang still fails the test ## Linked Issues or Issue Description **What happened?** The "Runner Rust (Windows x64)" job failed on pull requests that do not change the runner. Examples: - Run 42073331106 (PR #19273) and run 42058757139: `rejected_declared_completion_stays_failed_when_the_provider_completes` panicked with `called Option::unwrap() on a None value`. The loop ended at its 5 s deadline before `session.failed` and `run.terminal` arrived. - Run 41892735530 (master): the same test failed in the same way. - Runs 41718881566 and 41670873220: `durable_descendant_lineage_survives_capacity_and_provider_restoration` failed with "capacity exhaustion must become a specific durable recovery reason". Its third phase waited only 5 s. An earlier phase of this test already had a 30 s deadline with a comment about this contention. **What did you expect?** The tests wait until the fake provider produces the event. They fail only when the provider hangs. **Steps to reproduce** Run `cargo test --release --workspace` for the runner on a contended Windows x64 runner. The suite takes about 380 s there, and some tests run for more than 60 s. The failures are intermittent. ## What Changed - `tests/codex_provider.rs`: add `EVENT_WAIT_TIMEOUT` (120 s) and `event_deadline()`. Replace all 50 fixed 5 s and 30 s wait deadlines with `event_deadline()`. Each wait already stops when its condition holds, so passing tests do not get slower. - `rejected_declared_completion_stays_failed_when_the_provider_completes`: assert that the run reached `run.terminal`, and print the observed event types if it did not. Replace the bare `unwrap()` on `session.failed` with a message that names the scenario. - `src/bin/fake-codex-app-server.rs`: the two background gate threads used 5 s and 10 s bounds. A test releases a gate only after its own waits succeed, so these bounds had the same problem. They now use one `TEST_GATE_TIMEOUT` of 120 s. - No production code changes. The two remaining `from_secs(5)` values are the unused `max_runtime` in the test config and a Unix-only latency assertion. Both stay the same. ## Verification - `cargo test --release -p paperclip-runner-core --test codex_provider --locked`: 109 passed, 0 failed, 2 ignored. - I ran the two flaky tests 48 times, with 6 copies in parallel per round to load the machine. All 48 runs passed. - `cargo fmt --all --check`: clean. - `cargo clippy -p paperclip-runner-core --test codex_provider --bin fake-codex-app-server`: no new warnings in the changed lines. The lib target already has clippy findings from the local toolchain (Rust 1.97). They are not in the changed files, and CI does not run clippy. - I did not reproduce the original failure on macOS. The slow condition occurs only on the loaded Windows runner. The Windows CI job on this pull request is the main check. ## Risks - Low risk. The change affects only tests and the test fixture binary. - If a test hangs for real, it now fails after 120 s and not after 5 s. This makes a broken run slower, but the failure message stays the same. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, used through Claude Code with tool use (shell, file edits, GitHub CLI). ## 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> |
||
|
|
1eff07f29e |
feat(task-chat): show Claude subagents as a collapsible tree in the runner turn (#19264)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The task chat shows what an agent does in a run. The Paperclip Runner sends this activity as canonical provider events. > - Claude runs subagents through its Agent (Task) tool. A run that delegates showed a confusing burst of unrelated tool calls. > - The subagent data was already sent, but it was lost: ACPX dropped `_meta`, the hardened sidecar dropped `rawInput`, and async subagents report "launched" as the tool result. > - This pull request maps each Agent call to a canonical `delegation` event, links each child tool to its subagent, and shows subagents as a collapsible card or tree. > - The benefit is that operators can see what each subagent was asked, what it is doing now, which tools it used, and what it returned. ## Linked Issues or Issue Description Refs #19252 (the docked plan card used the same canonical-event pattern) Refs #19259 (ACPX `_meta` passthrough, merged). This PR builds on it. **What happened?** When a Claude agent used its Agent tool, the task chat showed one generic "Task" row and then the subagents' tool calls as flat rows in the parent turn. The subagent type, prompt, progress and result were not visible. **Expected behavior** Each subagent shows as its own card with its type, description, prompt, live progress summary, nested tool rows and result. Subagents that start together show as one tree with status dots. **Steps to reproduce** 1. Assign a Claude (Paperclip Runner, ACPX) agent a task that asks it to launch three Explore subagents in parallel. 2. Open the task while the run is live. 3. Before this change, the subagents' tool calls appear flat in the parent turn. ## What Changed - **claude-agent-acp patch:** Under the isolated runner context, the patch turns on the SDK option `agentProgressSummaries`. It forwards each subagent's `task_progress` summary and its terminal state and summary onto the spawning tool call as `_meta.claudeCode.subagentProgress` and `subagentState`. Native subagent sessions stay off, because ACPX cannot follow child ACP sessions. - **Runner:** The new file `claude-subagent-lineage.ts` tracks each Agent/Task call across ACP update deltas. It maps the call to `delegation.started|updated|completed` events and stamps child tools with `parentItemId`. An async subagent stays running after its "Async agent launched" result until its own state settles. - **Sidecar path:** `acpx-runtime-sidecar.ts` reads the lineage before it drops `_meta` and `rawInput`. Delegations go out as schema-checked rich events. Child tools carry `parentToolCallId`, which Rust (`acpx_event_payload.rs`, `provider_events.rs`) maps to `parentItemId`. - **Contracts:** The provider-event schema adds optional `prompt` and `result` to delegation children and optional `parentItemId` to tool executions. The protocol modules are regenerated. ACPX Claude now advertises the `delegation` event family. - **UI:** `native-run-events.ts` keeps delegation payloads and the tool parent link. `buildTurnTimelineRows` groups adjacent delegations into a new `subagent_group` row, nests child tool rows (plain and provider-activity shapes), and hides the spawning call's own tool row. The new `TaskChatSubagentCard.tsx` renders the card and tree. A new size token caps the result height. - **Storybook:** The new story `Tasks and conversations/…/Task chat/Subagents` has 7 states. ## Verification - `pnpm --filter @paperclipai/paperclip-runner exec vitest run src/cli src/drivers/acpx src/provider-events.test.ts src/claude-subagent-lineage.test.ts src/protocol` (2069 passed) - `cargo test --release --locked -p paperclip-runner-core --lib` in `packages/paperclip-runner/runner` (488 passed, including the new parent-link test) - `node --test test/protocol-contract.test.mjs test/acpx-sidecar-contract.test.mjs test/acpx-codex-package-contract.test.mjs test/acpx-rich-extensions-package-contract.test.mjs` (48 passed) - `pnpm --filter @paperclipai/ui exec vitest run src/components/task-chat src/components/transcript`, including the new `subagent-timeline.test.ts` - `check:protocol-types`, `check:protocol-manifest`, `check:acpx-profiles` and `pnpm check:token-gates` pass. - **Live test:** A local test-drive instance ran a Claude Opus 5.5 agent on the Paperclip Runner (hardened sidecar path). The agent ran real tasks that launched three parallel Explore subagents. The live view showed "Running 3 subagents · 2 done" with a progress summary. The settled view showed "Ran 3 subagents", each with its prompt, nested tools and its own result text. ## Risks - `agentProgressSummaries` makes Claude Code generate a short progress summary for each running subagent about every 30 seconds. This adds a small model cost to runs that delegate. - The adapter patch reads `task_notification` and terminal `task_updated` messages. A future claude-agent-acp version can change these. The patch hunks then fail to apply, and the dependency bump must update them. - If a delegation event is lost, child tools stay at the top level as before. Nothing is hidden without a matching delegation. - Codex is not changed. Codex subagents need `multi_agent`, which Paperclip keeps off on purpose. Changing that is a separate policy decision. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use (shell, file edits, browser automation). It did the investigation, implementation, tests and live test. ## 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> |
||
|
|
d361f92071 |
feat(task-chat): show harness status notices as chips, out of the answer (#19267)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents run through the native runner. The runner turns harness events from Codex and Claude into canonical provider events, and the task chat shows them. > - Harnesses report their own lifecycle: context compaction, retries, model fallback, and warnings. Paperclip lost or mangled these reports. > - Claude (claude-agent-acp 0.73) sends compaction, model-fallback and warning banners as ordinary message text, so "Compacting..." ended up inside the agent's answer and the issue comment. > - Codex sends `error` with the text in `error.message` and a `willRetry` flag. Paperclip read `params.message`, so every Codex error said "Provider notice", and every retry looked fatal. > - This pull request keeps these reports out of the answer and shows them as small status chips between the turn's activity. > - The benefit is that users can see why an agent paused, retried or forgot earlier context, and the answer text stays clean. ## Linked Issues or Issue Description **Subsystem affected** Native runner provider-event mapping (TypeScript driver, ACPX sidecar and Rust runner-core) and the task chat transcript. **Problem or motivation** Claude status banners are mixed into assistant answer text. Codex error notices lose their message and their retry state. The task chat hid all of these notices, and settled turns stripped every provider notice. **Proposed solution** Classify Claude banners as provider notices at the ACPX boundary. Map Codex `TurnError` fields and `willRetry` correctly. Render status notices as inline chips. A retry chip fades after later provider work. A final error shows the real provider message. **Alternatives considered** Patch claude-agent-acp to tag banners in `_meta`. ACPX keeps only `origin`, `kind` and `source` from `_meta`, and other sessions own the ACPX `_meta` passthrough work. Banner chunks already have no `messageId`, and model output always has one, so the classification needs no adapter patch. **Roadmap alignment** This makes existing harness signals visible. It adds no new protocol schema. It uses the existing `paperclip.provider.notice.v1` `category` and `details` fields. ## What Changed - Codex `error` notifications (TypeScript `provider-events.ts` and Rust `runner-core/src/provider_events.rs`): read `error.message`, map `willRetry: true` to a recoverable `provider_retry` notice, keep category `error` for a final error, and add the `CodexErrorInfo` kind, HTTP status, and the "Reconnecting... n/m" attempt count as notice details. Retries in one turn share one notice id. - New `claude-status-banners.ts`: classifies the claude-agent-acp compaction, model-fallback and SDK informational banners. A chunk must have no `messageId` and match a known banner exactly. - ACPX sidecar and the TypeScript ACPX driver emit these banners as `provider_notice` events instead of answer text. Compaction start and end share one notice key and carry a `Phase` detail. - Rust runner-core accepts an optional `noticeKey` and bounded `details` on ACPX `provider_notice` events. - UI: new `TaskChatStatusNotice` chip and `status-notice-presentation.ts`. Status notices become top-level timeline rows, render as chips wherever protocol cards render, and stay visible in settled (historical) turns. Generic warnings stay hidden as before. - New tokens `--tc-status-notice-warning-*` in `ui/src/index.css`. - Storybook story `Tasks and conversations/.../Task chat/Status notices` with eight states. - `doc/run-log-events.md`: describes the status notice categories. ## Verification - `pnpm --filter @paperclipai/ui exec vitest run src/components/task-chat src/components/transcript` - `pnpm --filter @paperclipai/paperclip-runner exec vitest run src/provider-events.test.ts src/drivers/acpx/claude-status-banners.test.ts src/drivers/acpx/codex-acpx-driver.test.ts src/cli/acpx-runtime-sidecar.test.ts` - `cargo test -p paperclip-runner-core --lib provider_events` and `--test acpx_provider_events` - `pnpm check:token-gates` - Storybook: open `product-task-chat-status-notices--all-states-gallery`. - Live test-drive on an isolated worktree instance: - Codex (gpt-5.6-sol, app-server 0.160): a local pass-through proxy in front of the real OpenAI API cut the first two response streams. Codex ran its own retries. The chat showed an amber "Reconnecting" chip live, then "Recovered after retry" once work continued. With every stream cut, Codex made five retries and then sent a final error with the real message. - Claude (claude-haiku-5-5 through claude-agent-acp 0.73): a long reading task triggered Claude's own auto-compaction twice. The chat showed "Compacting context…" live and "Context compacted" afterwards. The final answer and the issue comment contained no compaction text. - Not verified live: model fallback and Claude informational banners. They need a model refusal or a blocking hook. Unit tests cover them. ## Risks - Low to medium. A Claude model message that is exactly a banner text and has no `messageId` would become a notice. Model output from claude-agent-acp always carries a `messageId`. - Older stored Codex `error` notices (category `error`, not recoverable) now render as a red row instead of being hidden. - Claude retries (`api_retry`) are still not visible. claude-agent-acp reports them only to clients that negotiate session-failure records, and ACPX drops `_meta`. This is a follow-up that depends on the ACPX `_meta` passthrough work. - On a failed Codex turn the thread collapses to a summary, so the red row does not show there. The existing task banner already shows the real provider message. - Some local suites failed on this machine. They do not touch the changed code: Cursor Cloud staging-custody tests, the ACPX Claude dependency-graph pin test (a timeout), and the Rust `task_memory` monitor tests. CI is the reference for these. > 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`) in Claude Code, with tool use, code execution and browser automation. Default context window. Extended reasoning was not enabled. ## 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 Refs paperclipai/paperclip#19252 (plan card precedent). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
088f85c5fe |
fix: rename the managed environment to Paperclip Sandbox (#19296)
## Thinking Path > - Paperclip manages AI agents and their execution environments. > - Managed installations supply a default sandbox through managed configuration. > - Most selectors show the configured environment name. > - A fallback label, two errors, and examples still use Paperclip Computer. > - This pull request changes that stock copy to Paperclip Sandbox. > - Existing and custom environment names continue to come from the stored row. ## Linked Issues or Issue Description **What existing behavior does this improve?** The managed environment label in agent settings and the errors shown when the managed sandbox is unavailable. **Subsystem affected** The agent configuration UI, runner selection, and environment reconciliation tests. **Current behavior** Fallback copy and current examples call the managed sandbox Paperclip Computer. The actual environment name is already configurable. **Proposed behavior** Use Paperclip Sandbox for stock copy and current examples. Keep configured names unchanged in selectors, including legacy names and custom names. **Reason and benefit** The name identifies this environment as a managed sandbox and removes ambiguity. **Breaking changes** The displayed fallback and error text change. Error codes, execution behavior, and environment identity remain the same. No migration is needed. **Additional context** No duplicate rename issue or pull request was found. This is a copy update, with no new roadmap feature. ## What Changed - Update the managed fallback label and two unavailable-sandbox error messages. - Update current test and Storybook fixtures without changing story IDs or behavior. - Test configured names, fallback copy, and the unavailable-sandbox message. - Add a PostgreSQL regression test for a rename that retains the environment ID, instance default, agent binding, environment variables, and lease. ## Verification - `pnpm -r typecheck` passed. - `pnpm build` passed. - Focused coverage: 956 cases across nine changed suites. The new fixture initially tried to insert an existing singleton; its corrected upsert passed with all 52 environment-service cases. The updated runner suite also passed all 243 cases. - The two UI suites passed all 189 cases, including configured legacy/custom names and the missing-default fallback. - `pnpm check:token-gates` passed. - `node scripts/storybook-catalog.mjs --check` passed. The catalog is structural evidence only. - `node --test scripts/check-maps.test.mjs scripts/storybook-catalog.test.mjs scripts/storybook-feature-index.test.mjs` passed. - Full `pnpm test:run` is in progress. No live provider or browser journey is claimed by these tests. - CI passed on the current head after one server-shard rerun. The first attempt hit a 10 ms deadline assertion in the unchanged status-card timing test; its isolated local run and the complete rerun shard passed. The original failure and rerun are retained in the PR comment. - Reviewed the diff for secrets, personal data, and run artifacts. ## Risks Low risk: runtime edits change three display strings. Managed reconciliation is unchanged. The regression test exercises the existing rename against PostgreSQL. The configured name still controls normal display, so this code change alone does not rename existing database rows. A managed configuration update uses the existing reconciliation path. Roll back the copy by reverting this commit. ## Model Used OpenAI GPT-6.1 Sol (`gpt-6.1-sol`) through Codex in T3 Code, xhigh reasoning, with repository tools 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, version, and capabilities - [x] I have checked ROADMAP.md; this copy update does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs - [x] I have described the issue in the PR with the enhancement template fields - [x] No internal or instance-local issue references - [x] The branch name describes the change and contains no internal ticket ID - [x] Focused tests pass locally - [x] Relevant tests were added or updated - [x] Relevant copy and examples were updated; no API or schema documentation changed - [x] Risks are documented - [x] All CI gates passed on the current head, including the server 11/12 rerun and aggregate verify - [ ] Full local test suite completed (still running; six chat shards passed) - [x] Greptile is 5/5 on the current head with no unresolved findings - [x] I will address all reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
cb0b7525e0 |
feat(task-chat): warn at the top of a run when an MCP server failed to start (#19289)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Each agent run shows its activity in the task chat. The Paperclip runner turns harness events into that activity. > - Codex reports `mcpServer/startupStatus/updated` when an MCP server fails to start. The runner dropped this event at the notification allowlist and in the Rust normalizer. > - When an MCP server fails, the agent runs without its tools. The agent then says "I could not reach the server", and the user cannot see why. > - This pull request maps a failed MCP start to a user-facing provider notice and shows it as one warning line at the top of the run. > - The benefit is that the cause of a "cannot reach my tools" answer is visible next to that answer. ## Linked Issues or Issue Description Refs #19252 (the plan card, which set the pattern for promoting a dropped harness signal to a dedicated task-chat surface). **Problem description** Codex sends `mcpServer/startupStatus/updated` (`name`, `status`, `error`, `failureReason`) during `thread/start`. The TypeScript allowlist (`codex-thread-normalization.ts`) and the Rust normalizer (`runner-core/src/provider_events.rs`) both dropped it. A run with a broken MCP server showed no sign of the failure. The agent only said that it could not reach the tools. **Proposed solution** Map `status: "failed"` to `provider.notice.recorded` with `category: "mcp_server_startup_failed"`, `severity: "warning"`, `scope: "session"` and `userActionable: true`. Show these notices as one run-health line under the runner header: "github MCP server failed to start: No such file or directory (os error 2)". **Alternatives considered** An inline notice row in the timeline. It scrolls away and sits among tool rows. A run-level line next to the header is easier to connect to the agent's answer. ## What Changed - Runner (Rust, production Codex path): `normalize_codex_notification` maps a failed `mcpServer/startupStatus/updated` to a `provider.notice.recorded` notice. `starting`, `ready` and `cancelled` stay out of the transcript. The new helper removes the Codex wrapper text ("MCP client for \`x\` failed to start: MCP startup failed:") so that the line shows the real reason. `reauthenticationRequired` becomes "needs re-authentication". - Runner (TypeScript): the same mapping in `canonicalProviderEventsFromCodex`, and the method is added to `isSupportedCodexNotificationMethod`. - Server: the `view=conversation` run-event query keeps `mcp_server_startup_failed` notices. A settled run can then still show the warning. Other notices stay out of that view. - UI: new `TaskChatRunHealthBanner`. `partitionRunHealthItems` takes MCP failures out of the run's items, with one line per server where the latest report wins. `TaskChatRunnerTurn` (live runs) and `TaskChatTurn` (settled, historical and merged turns) show it under the header. `coalesceSettledTurns` merges the failures when it merges turns. - Storybook: `Tasks and conversations/…/Task chat/Run health` shows one failure, several failures, a finished run and a healthy run, all through the production runner turn. ## Verification - `pnpm --filter @paperclipai/paperclip-runner exec vitest run src/provider-events.test.ts src/drivers/codex src/protocol`: 508 tests pass. - `cargo test --release -p paperclip-runner-core --lib -- provider_events`: 20 tests pass. - `pnpm --filter @paperclipai/ui exec vitest run src/components/task-chat src/components/transcript src/components/TaskChatThread.test.tsx`: 1340 tests pass, including the new `TaskChatRunHealthBanner.test.tsx` (persisted event to rendered banner, deduplication, settled historical turn, merged turns). - `pnpm --filter @paperclipai/server exec vitest run src/__tests__/task-cold-open.test.ts`: the conversation view keeps the MCP notice and still excludes other notices. - `pnpm check:token-gates` is clean. `node scripts/typecheck-low-memory.mjs` passes. - Live test on a local instance with Codex 0.160: a Codex agent with a preconfigured Codex home whose `config.toml` has `[mcp_servers.github] command = "github-mcp-server-not-installed"`. Codex reported the failure during `thread/start`. The persisted event had the summary "github MCP server failed to start: No such file or directory (os error 2)". The banner showed while the run was live. It also showed on the settled turn after "More run output", above the agent's answer that it could not reach the GitHub MCP server. ## Risks - Low risk. The change adds one notice category. It does not change how existing notices map or render. - A settled run whose transcript is deferred on a cold open (a successful run with a posted agent comment) shows the warning only after "More run output". This is the same as all other run activity under the current compact history. - Claude does not report MCP startup failures over ACP. `claude-agent-acp` reads MCP status only for OAuth. This change is therefore Codex-only. - Provisional task titles from provider session names are not part of this change. Codex app-server does not generate thread names (only its TUI does). Claude generates its title after the turn ends, and ACPX drops the title text. > 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`), through Claude Code with tool use (shell, file edits, browser automation for Storybook and the live test). ## 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 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b400f76647 |
feat(ui): submit task composers with Enter by default (#19272)
Use Enter to submit task replies and new tasks, with Shift+Enter for a new line. Add an experimental preference to require Command/Ctrl+Enter. Preserve autocomplete selection and IME confirmation, and retain the modifier shortcut until saved settings load. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
c6996799c4 |
feat(task-chat): show an inline diff for every file edit (#19269)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The task chat shows what an agent did during a run, including each tool call > - Codex and Claude both report the patch for every file edit, but Paperclip dropped it. Claude's ACP `diff` content was never read. The Codex `fileChange` item became a generic item with no patch. Codex `turn/diff/updated` had no native-runner mapping > - A reviewer could not see what an agent changed without opening the workspace > - This pull request carries a bounded patch on each edit call and shows it in the edit row. Each turn that changed files ends with a "Changed in this turn" card > - The benefit is that a reviewer can check every change in the conversation, with the same view for Codex and Claude ## Linked Issues or Issue Description Refs #19252 (precedent: canonical `plan.updated` events as a docked plan card). **Subsystem affected** Paperclip runner (Rust and TypeScript canonical event mapping), PRP provider-event schema, and the task-chat UI. **Problem or motivation** Edit rows say "Edited a file" with no content. Claude Edit/Write `diff` content is lost after the ACPX sidecar. The Codex native runner drops `fileChange.changes` and `turn/diff/updated`. The existing workspace-changes card showed a raw text patch in a dialog, and only on the in-process Codex path. **Proposed solution** Add an optional, bounded `fileChanges` list to `paperclip.tool.execution.v1`. Fill it from Codex `fileChange` items and Claude ACP `diff` content. Map Codex `turn/diff/updated` to `workspace.change.updated` on the native runner. In the UI, show `+N −M` on each edit row, open a compact colored diff, and end the turn with the change set. **Alternatives considered** A runner-side git diff at turn end. It needs workspace access and extra cost on every turn. The harness patches are already available. For Claude, the UI builds the turn card from the edit rows instead of new per-turn runner state. ## What Changed - Protocol: optional `fileChanges[]` on `toolExecution` in `provider-event.schema.json`, with the regenerated schema bundle, validators and manifest. Paths are workspace-relative. A patch is at most 32 KiB, and a call has at most 32 files. - Runner (Rust): new `tool_file_changes.rs`. A Codex `fileChange` item now also emits `tool.execution.*` with `fileChanges`; the native item event stays for the recovery and progress projections. `turn/diff/updated` becomes `workspace.change.updated`. `normalize_acpx_tool_call` re-validates the sidecar's `fileChanges`. `normalize_codex_notification_in` resolves absolute paths against the session cwd. - Runner (Rust): `has_bounded_rich_display_shape` accepts schema-valid workspace diffs. Without this change, durable storage cut each patch string to a 4 KiB preview. Patches use the uncapped secret redactor. - Runner (TS): new `tool-file-changes.ts`. The ACPX sidecar computes `fileChanges` from ACP `diff` content with the working directory. The in-process Codex and ACPX mappers do the same, with an optional `context` argument. - UI: new `TaskChatDiffView` (colored rows, hunk labels, a height cap, "Show N more lines"). Edit rows show counts and the diff. Collapsed activity groups show edit totals. The workspace card is now "Changed in this turn", with in-place per-file diffs, and it sits at the end of the turn. When a harness sends no whole-turn diff (Claude), the card is built from the edit rows. A change set is marked complete when its run ends. - UI: `native-run-events.ts` maps `workspace.change.updated` and attaches each edit's latest patch to its row (the `diff` transcript entries now carry `toolUseId`). - Design tokens: `--diff-add-*`, `--diff-remove-*` and `--sz-edit-diff-max-h` come from the status icon hues. - Storybook: `Tasks/Edit diffs` story. Docs: `doc/run-log-events.md`. ## Verification - Real runs on a local instance (`test-drive`, native runner): - Codex (`gpt-5.6-sol`): the "Applied a patch" rows show `+9` and `+35 −2` with their diffs. The turn ends with "Changed in this turn", 2 files, `+42`, from `turn/diff/updated`. - Claude (`claude-opus-5-5` via ACPX): the Write and Edit rows show `+5`, `+14 −1` and `+9` with correct hunks. The turn card is built from the edit rows. - Codex model note: with `gpt-5.4`, pinned Codex 0.160 reports "model metadata not found". The model then wrote files through the shell, so no `fileChange` items occurred. - `pnpm check:token-gates`: all gates clean. - `pnpm --filter @paperclipai/ui exec vitest run src/components/task-chat src/components/transcript src/adapters`: 1073 passed. - `packages/paperclip-runner`: `vitest run src/provider-events.test.ts src/tool-file-changes.test.ts src/cli/acpx-runtime-sidecar.test.ts` passed. `check:protocol-types` and `check:protocol-manifest` match. - `cargo test -p paperclip-runner-core --lib provider_events tool_file_changes`: passed. `cargo fmt --check`: clean. - Storybook: open `Tasks and conversations/…/Tasks/Edit diffs`. ## Risks - Larger payloads: an edit call can carry up to 96 KiB of patch in Rust, and the sidecar keeps 48 KiB under its 128 KiB frame. Above the budget, a file keeps its counts and drops its patch (`diffTruncated`). - Redaction: each patch goes through the shared secret redactor. Before this change, a 4 KiB preview cap also applied. - Claude turn card: it joins each edit's patch in order. It is not a net diff. For a file the turn created, the counts are net and exact. For an existing file, the counts are summed over its edits. - The workspace card moves to the end of the turn. One adapter test changed to match. - Local only: four Linux cgroup `task_memory` Rust tests fail on macOS. They do not touch these modules. ## Model Used Claude Opus 5.5 (`claude-opus-5-5`), via Claude Code with tool use (shell, browser and file editing) and a long context window. ## 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> |
||
|
|
29afc633f6 |
feat(runner): forward ACP _meta from ACPX as a bounded copy (#19259)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The native runner runs Claude through ACPX and maps ACP updates into canonical run events. > - Claude sends useful data in the ACP `_meta` field: the real tool name, the parent tool of a subagent call, the terminal exit code, and the rate-limit state. > - ACPX 0.13.1 drops all `_meta` before the runner sees it. > - Three planned features need this data: richer tool rows, a rate-limit gauge, and a subagent tree. > - This pull request forwards `_meta` from ACPX and bounds it once in the runner. > - The benefit is that each feature can read the data from one safe, shared place. ## Linked Issues or Issue Description **Problem or motivation** ACPX keeps only the title, status, kind, locations, raw input, raw output and content of an ACP tool update. It drops `_meta`. Claude's tool name, `parentToolUseId`, `terminal_exit` and `_claude/rateLimit` never reach the runner. **Proposed solution** Forward `_meta` from ACPX on tool and status events. Copy it into a bounded, JSON-safe object in the runner before any mapper reads it. **Alternatives considered** Read the full ACP notification through `onSessionNotification` and join it to runtime events by tool call ID. That needs a side table and depends on callback order. Forwarding on the event is simpler and keeps the data with the event. ## What Changed - `patches/acpx@0.13.1.patch`: ACPX now sets `_meta` on `tool_call` events and on status events (`usage_update`, `current_mode_update`, `config_option_update`, `session_info_update`). The type declarations document the field as unvalidated producer data. - `packages/paperclip-runner/src/provider-events.ts`: new `boundedAcpMeta()`. `createAcpxToolEventNormalizer` applies it to every runtime event. Limits: depth 6, 64 entries per object or array, 4 KB per string, about 16 KB in total. It drops `__proto__`, `constructor` and `prototype` keys, functions and non-finite numbers. - The hardened ACPX sidecar path keeps its explicit field allowlist. It does not forward `_meta` yet. A feature that needs a field there must add it to that allowlist. - Tests for the passthrough and for each bound. ## Verification - `cd packages/paperclip-runner && npx vitest run src/provider-events.test.ts` - The canonical mappers do not read `_meta` yet, so run events do not change. Follow-up pull requests use it. ## Risks - Low risk. The new field is additive and the mappers ignore it today. - The lockfile patch hash for `acpx@0.13.1` changes. A fresh `pnpm install` applies the new patch. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`) 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> |
||
|
|
d8c47159d4 |
feat(task-chat): show Codex reasoning summaries as headline bullets (#19271)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The task chat shows what an agent does while it runs, including a "Thinking" row for provider reasoning > - Real Codex runs produced reasoning items with no text > - So the Thinking row only says "Thinking" and never tells the user what the agent works on > - This pull request asks Codex for its own reasoning summaries, keeps every summary part, and shows the parts as bullets > - The benefit is that a user can see what the agent is thinking about, and a collapsed live run shows the newest headline as its status line ## Linked Issues or Issue Description Refs #19252 (same pattern: show a harness signal that Paperclip already receives) **Subsystem affected** Native runner (Rust runnerd Codex provider and event normalization, JS Codex driver) and the task chat UI (Thinking row and activity rows). **Problem or motivation** Codex 0.160 resolves an unset reasoning summary from the model, and current models resolve it to `"none"`. The rollout of a real gpt-5.6-sol run records `turn_context.summary = "none"`, and all of its reasoning items have `summary: []`. A local A/B with `codex exec` proved the cause: `model_reasoning_summary=none` gives no reasoning text, and `auto` gives headlines. Also, runnerd dropped `item/reasoning/*` notifications and ignored the reasoning item's `summary[]`. **Proposed solution** Send `summary: "auto"` on Codex `turn/start`. Map `item/reasoning/summaryTextDelta` and `summaryPartAdded` to reasoning deltas, with a blank line before each later part. Use the joined `summary[]` as the completed item text. In the UI, split the provider text at headline lines and show one bullet per part. **Alternatives considered** Use `summary: "detailed"`. That gives longer text, but `auto` lets the provider choose the best summarizer. **Roadmap alignment** This makes existing provider signals visible. It adds no new protocol event type or data model. **Additional context** Research note: overlooked harness signals, item 2. Claude is out of scope here. A live Claude run showed thinking blocks with signatures and empty text: current models default `thinking.display` to `"omitted"`. Claude Code ignores the `showThinkingSummaries` setting in SDK mode, and ACPX does not forward a `thinking` option, so the fix needs a claude-agent-acp patch hunk. That patch is also changed by #19264, so the Claude change follows after #19264 merges. The UI in this PR already shows prose-only (Claude) summaries. ## What Changed - Rust runnerd (`codex_provider.rs`): send `summary: "auto"` on every Codex `turn/start`. - Rust runnerd (`provider_events.rs`): map `item/reasoning/summaryTextDelta` and later `summaryPartAdded` to `item.delta` with `kind: "reasoning"` and `channel: "summary"`. Use the joined `summary[]` as a completed reasoning item's text. - JS Codex driver: send `summary: "auto"` on direct app-server transports, map `summaryPartAdded`, and read `summary[]` in `itemText`. - UI: new `reasoning-summary.ts` splits provider text into headline sections, and `TaskChatReasoningSummary` shows one bullet per part. The Thinking row and the activity row use the newest headline as their one-line status. Prose-only summaries (Claude) stay as prose. - Storybook: new "Task chat reasoning summaries" story with live, expanded, finished and Claude states. - Tests: Rust normalizer test, JS driver test, and splitter unit tests. ## Verification - `cargo test -p paperclip-runner-core` (provider events and the shared identity golden). - `pnpm --filter @paperclipai/paperclip-runner exec vitest run src/drivers/codex` - `pnpm --filter @paperclipai/ui exec vitest run src/components/task-chat src/components/transcript` - `pnpm check:token-gates` - Live test on a local worktree instance with a real Codex agent (gpt-5.6-sol). The Codex rollout now records `summary: "auto"`. Run events contain `**Assessing transaction boundary issues**`, a part break, and `**Planning atomic claim fix and verification**`. The task chat shows "Thought · Planning atomic claim fix and verification", and the expanded row lists both headlines as bullets. - Storybook: open "Tasks and conversations / Task creation and lifecycle / Tasks / Task chat reasoning summaries". - Claude was not changed. A live Claude run confirmed the cause for the follow-up (empty, signed thinking blocks). ## Risks - Codex now writes reasoning summaries, which adds a small number of output tokens per turn. This is the same cost as Codex CLI with its normal default. - The UI treats a line that is only bold text as a headline. A prose line that is only bold text also starts a new bullet. This is display only. > 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`) in Claude Code, with tool use, code execution and browser automation. Default context window. ## 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> |
||
|
|
036b1860c3 |
fix(runner): bind Cursor sidecars to the runner package, not the workspace root (#19255)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The native runner starts ACPX providers through a sidecar. runnerd
tells the sidecar which package owns the provider assets.
> - Cursor, Copilot, Pi, Kimi and Antigravity use pinned native
closures. Their asset guard requires the bound manifest to be the
`package.json` directly inside the bound root.
> - In a local source build, runnerd bound Cursor to the pnpm workspace
root, with the runner manifest one level down. The guard threw before it
checked the Cursor closure.
> - Every Cursor run on a local source instance failed with
`admission_installation`. The sidecar redacts the message, so the cause
was not visible.
> - This pull request gives Cursor the same runner-package authority as
the other native-asset providers.
> - The benefit is that Cursor runs start on local source builds.
## Linked Issues or Issue Description
**What happened?**
A `paperclip_runner` agent with `provider: "acpx"` and `acpxAgent:
"cursor"` failed at every start on a local macOS worktree instance. The
run log showed `ACPX sidecar command session.open was rejected
(retryable=false, classification=admission_installation)`.
**Expected behavior**
The run starts, because the pinned Cursor runtime cache matches
`cursor-distributions.json`.
**Steps to reproduce**
1. Run `pnpm dev` from a source checkout, with the Cursor runtime set up
in `~/.paperclip/runtimes/cursor`.
2. Create a native runner agent with `acpxAgent: "cursor"` and a bound
`CURSOR_AUTH_TOKEN`.
3. Start a run. It fails at `session.open`.
**Paperclip version or commit**
`master` at
|
||
|
|
3d0d178ff7 |
Reconcile qualified ACPX predecessors through ordinary recovery (#19153)
## Thinking Path > - Paperclip runs agent work through a controller and a Runner. > - A completed provider session can outlive an abrupt Runner exit. > - The next owner must reconcile the retained source before it attaches a new run. > - The ordinary recovery foundation in #19132 first supports Codex App Server. > - Qualified ACPX providers need their own saved and live consistency checks. > - This pull request adds those checks and a separate provider attestation. > - The benefit is session continuity through existing recovery and attachment. ## Linked Issues or Issue Description Builds on #19132, which has merged. This PR targets `master` and contains only the ACPX changes. **What existing behavior does this improve?** Recovery of a completed ACPX session after the Runner and provider stop. **Current behavior** Ordinary ACPX recovery and attachment preserve a qualified session. A completed predecessor also needs trusted admission by its new owner before restoration. Codex checks cannot attest ACPX state. **Proposed behavior** Reuse ordinary exact-source recovery. Compare live restored state with saved dormant state when no uncertain effect explains new work. Require the selected driver's separate ACPX prelaunch attestation. Settle retained events before authenticated target attachment. ## What Changed - Preserve baseline prepared cleanup and ordinary continuation. New predecessor admission excludes prepared provider state; snapshots and cleanup do not restart it. - Observe existing recovery command facts. Keep ordinary active same-run behavior and existing profile, workspace, model, permission, session and catalog checks. - Verify the restored qualified session's identity, turn, goal and outstanding requests when saved state was dormant. Failed verification reaps the owned process group. - Validate scoped goal updates arriving during the final status snapshot after the authoritative goal reply. Active or unknown updates fail admission; earlier and benign queued events remain available to ordinary polling. - Recognize the existing inactive `limited`, `usage_limited` and `budget_limited` goal statuses, so an unexpected active goal after restore cannot bypass live verification. Treat malformed nested working-state metadata as unproven while keeping missing-field compatibility. - Keep existing ordinary recovery of qualified session-open and suspended ACPX sessions. The new integrity check does not expand prepared restoration. - Advertise `acpxRecoveryIntegrityVersion: 1`, separate from Codex integrity. No native source mode or CLI remains. - Require `acpxSourceRecoveryVersion: 1` from the selected trusted execution driver for a stopped predecessor. Codex capability alone cannot grant it. - Reuse dynamic authenticated owner admission, retained source accounting, and existing ordinary ACPX attachment. ## Verification Current head `b294d8ae88d0` is one commit on `master` at `e80d745060fd`, the merge of #19132. The ten-file diff is byte-for-byte identical to the reviewed ACPX diff against its former base. The guide shows where `paperclip-runnerd --build-metadata` emits `acpxRecoveryIntegrityVersion: 1` and how host admission uses a separate capability. [ACPX recovery guide](https://github.com/paperclipai/paperclip/blob/b294d8ae88d019ef02a942e240bc5c14287239e0/packages/paperclip-runner/runner/DURABLE_TRANSPORT.md#recovering-acpx-between-turns) The rebased head passed all 14 ACPX process cases, three native snapshot checks, Runner typecheck and the binary metadata check locally. GitHub CI, Docker and Windows passed on their first attempts. Standard Greptile review 12 scored the rebased commit 5/5 with zero unresolved threads. [CI run](https://github.com/paperclipai/paperclip/actions/runs/42063188178) · [Standard review](https://github.com/paperclipai/paperclip/pull/19153#issuecomment-6646809075) The preceding code commit `08df48de20d613efcd861c278a7f9929c21d6670` includes the reviewed parent `c09ebb4a2a6c140521698cf9fd681de67cfb9745`. Paid review 9 scores this exact head 5/5 with no unresolved findings (summary updated 2026-11-05 09:13:11 UTC after the 09:07:15 UTC request). Standard review 8 also scored this head 5/5. [PR workflow 41995103666](https://github.com/paperclipai/paperclip/actions/runs/41995103666), [Runner Windows 41995103419](https://github.com/paperclipai/paperclip/actions/runs/41995103419), and [Docker Runner 41995103366](https://github.com/paperclipai/paperclip/actions/runs/41995103366) passed on this exact head. The initial main attempt lost self-hosted runners: the Runner package log records a shutdown signal, and the other affected jobs report lost communication or cancellation. Only unsuccessful jobs were retried; their successful retry and aggregate gate are green. No source, assertion or timeout change was made for this interruption. - Combined `runnerd-acpx-source-reconciliation.test.ts` and `runnerd-source-reconciliation.test.ts`: 25 real native/fake-provider process cases passed. ACPX contributes 14 cases, including the final-snapshot goal race, process-group cleanup, same-PID adoption, exact provider identities, retained accounting, limited/paused goals and ordinary active same-run recovery. - The maintained fake sidecar emits a real `runtime.goal` frame between the dormant goal reply and final snapshot response. Before the fix, recovery succeeded. Afterward it refuses admission, reaps the owned sidecar/provider group, and records only the original provider turn. - Three native wire tests passed. They cover active updates with low/equal/missing revisions, foreign run/session identity, and unchanged ordered delivery of earlier history plus benign new events. The queue is inspected without consuming frames. - Both parent Codex final-read tests passed in the combined checkout: active-goal refusal/reap and safe paused-goal delivery. - The real native backend completes A, settles its journals, kills Runner and provider, recovers the same ACPX session, adopts the replacement Runner after controller restart, attaches B and completes a second turn, then uses ordinary warm attachment for C and completes a third successful turn. Record, backend and agent session identities remain exact; ordinary ACPX attachment may restart its sidecar. - Provider compatibility checks also passed while preparing this stack: 30 permission ordering/forwarding cases, 19 factory cases, the native inactive-goal classification unit, and six native warm/suspend integration cases. - Combined offline Runner/fake-provider build, Runner `tsc --noEmit`, `cargo fmt --all -- --check`, parent-relative `git diff --check`, and added-line secret/personal-data scan passed. - Only focused local checks ran. Full suites and platform checks run in CI. ## Risks - Fake providers prove protocol behavior, not vendor restore inertness or all vendor background work. Real provider qualification remains required. - The trusted process owner must reject unsafe completed predecessors before launch. Ordinary same-run active-goal recovery remains supported. - Probes use the existing bounded sidecar budget. A timeout can terminate the sidecar through existing transport cleanup. Null goal projections remain supported. - Ordinary ACPX attachment can restart the sidecar. Record, backend and agent session identities remain exact; the Runner PID remains the same. - This draft adds no deployment, release pin change or parking hook. ## Model Used OpenAI GPT-6.1 Sol (`gpt-6.1-sol`), high reasoning, through Codex with local code and command tools. ## Checklist - [x] I have included a thinking path and specified the model - [x] I have checked ROADMAP.md and confirmed this does not duplicate planned work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have not included internal or instance-local references - [x] My branch name describes the change - [x] Focused local tests pass - [x] Tests and documentation cover the changes - [x] Risks are documented - [x] All Paperclip CI gates are green at the revised head - [x] Greptile is 5/5 with no unresolved comments at the revised head - [x] I will address review findings before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
b0fa2ef9bf |
feat(task-chat): show web searches, model switches, waits and verification checks as dedicated rows (#19262)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents run through the native runner. The runner turns harness activity into typed canonical events such as `research.*`, `model.route.changed`, `wait.*` and the run's own verification checks. > - The task chat flattened these events into generic tool rows. A web search showed as "Ran Research eventType: research.started, summary: …". The run's verification checks were dropped completely. > - Live tests found a second break: the Rust runner core forwarded Codex `webSearch` items without their query, action or results. The canonical `research.*` events therefore had `query: null`. > - This pull request keeps the full payload for these families (as #19252 did for plans), adds small dedicated rows, and keeps the web-search fields through the durable runner. > - The benefit is that users see what the agent searched for, which model actually ran, how long a wait lasts, and whether each check passed, without opening the run log. ## Linked Issues or Issue Description Refs #19252 (same pattern for the plan card) **Subsystem affected** Task chat activity rows (UI), the native run-event transcript mapping, and the Rust runner core Codex normalizer. **Problem or motivation** Provider activity that the runner already maps to canonical events reaches the task chat as unreadable generic rows. Verification checks never appear. For Codex, the web-search query and action are lost before they reach the canonical event. **Proposed solution** Keep `research`, `model_identity`, `artifact`, `review` and `wait` events as `provider_activity` entries with their full payload. Project the runner's `verification` items as a new `verification` family. Render dedicated rows: a web search with its quoted query and source-domain chips, a model switch as `from → to · reason`, a wait with a live countdown, and green/red check lines. Keep bounded web-search and sleep fields in the Rust normalizer. **Alternatives considered** A separate "Checks" block beside the final answer. We kept the checks in the activity group, because the group summary already leads with "Check failed" when a check fails. ## What Changed - `ui/src/components/transcript/native-run-events.ts`: keep research, model, artifact, review and wait events as `provider_activity` with the full payload. Map `item.completed` items of kind `verification` to the new `verification` family. Context, hook, safety and delegation stay on the generic path, because other work items own them. - `packages/adapter-utils/src/types.ts`, `ui/src/components/task-chat/task-chat-model.ts`, `ui/src/lib/issue-chat-messages.ts`: add the `verification` provider-activity family. - `ui/src/components/task-chat/task-chat-activity-presentation.ts`: add labels: "Searched the web “query”", "Opened a web page host/path", "Switched models a → b · reason", "Waiting 30s" / "Waited 30s", and "Check passed" / "Check failed" / "Check not run". - `ui/src/components/task-chat/TaskChatRunnerActivityGroup.tsx` and `TaskChatProtocolActivityRow.tsx`: show up to three source-domain chips under a finished search, a live countdown for a running wait, and a check line with a tinted icon and its detail. - `ui/src/components/task-chat/completed-activity-summary.ts`: the group summary leads with "Check failed" when a check fails, and says "Switched models" for a reroute. - `ui/src/components/task-chat/transcript-adapter.ts`: verification detail keys, and a `startedAt` timestamp that stays across lifecycle merges (used by the countdown). - `packages/paperclip-runner/runner/crates/runner-core/src/provider_events.rs`: keep bounded `query`, `action` and http(s) `results` for Codex `webSearch` items, and `durationMs` for `sleep` items. Other item types do not change. - `ui/storybook/stories/tasks/task-chat-provider-activity.stories.tsx`: new story. Its fixtures are persisted run events that go through the production transcript path. ## Verification - `pnpm --filter @paperclipai/ui exec vitest run src/components/transcript src/components/task-chat src/components/TaskChatThread.test.tsx src/adapters/paperclip-runner src/lib`: 275 files and 3669 tests pass. - `cargo test -p paperclip-runner-core`: all tests pass, including the new `keeps_bounded_web_search_and_sleep_identity`. - UI typecheck is clean. `pnpm check:token-gates` is clean. `cargo fmt --check` is clean. - Storybook: open "Tasks and conversations / … / Task chat / Provider activity". It shows a settled run, a live search, a live wait countdown and a narrow layout. - Live test on a local worktree instance with a real Codex agent (gpt-5.6-sol, Codex app-server 0.160). The task asked for a web search, a parser with tests, a pause, and two checks. The task chat showed "Searched the web “ISO 8601 week date first week Thursday conversion calendar date”" and two green check rows ("node --test week-date.test.js · 8 tests passed; 0 failed.", "node --check week-date.js"). The group summary read "Ran checks". Before the runner fix, the same search had `query: null`. Not verified live: - The source-domain chips. Codex 0.160 sends `results: null`, because it keeps search results on the server. The chips appear only when a provider reports sources. Unit tests and the story cover them. - The wait countdown and the model-switch row. In the live run Codex paused with a shell `sleep`, not a native `sleep` item, and no reroute occurred. Unit tests and the story cover both. - Claude. Its WebSearch and WebFetch calls arrive as generic tool executions, because ACPX drops the tool metadata. They show as tool rows, as before. The ACPX `_meta` work is separate. ## Risks - Low risk. The change only affects how canonical events display. The generic path stays for the families that this PR does not change. - The Rust normalizer now persists the web-search query and the source URLs/titles in the run events. This data was already intended for the canonical `research.v1` event, and it is bounded (4,000 characters per text field, 64 results, http(s) URLs only). - A search that Codex reports with `action: other` and no query shows as "Searched the web" with no subject. > 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`) in Claude Code, with tool use (shell, file edit, browser automation) and extended reasoning. ## 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> |
||
|
|
b1d00eeca0 |
fix(evals): derive the controlled Runner eval policy from committed defaults (#19253)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Runner protocol qualification runs real providers against 41 cases and reports them in TestOps. > - Controlled mode (#17889) adds a spend cap, private evidence storage, and cost and trace accounting. > - Operators must hand-maintain one large JSON variable for each campaign size, price review, and lockfile change. > - A one-cell policy blocked the first 41-case run, and its prices expired after 24 hours. > - This pull request derives that policy from committed defaults and the selected cases. > - The benefit is that a full run needs no policy edit, and all three safety properties stay. ## Linked Issues or Issue Description Refs #17889, #19011, #19070. **What existing behavior does this improve?** Controlled Runner protocol campaigns read `RUNNER_PROTOCOL_EVAL_CONTROLLER_POLICY_JSON`. That variable holds the cell limit, a price table with a review expiry, a pinned lockfile digest, and self-hashed bound proofs. **Current behavior** A campaign with more cells than `maxCells` fails before provider work. Expired prices also fail before provider work. Any change to the lockfile needs a new pin. A one-cell policy made all 41 TestOps cases fail as infrastructure, so an operator had to edit the variable by hand before the full run. **Proposed behavior** The workflow derives the policy at preparation time. The cell count comes from the selected catalog. Prices come from a committed table with a version and a source URL. The controller installs the committed frozen lockfile. One optional variable, `RUNNER_PROTOCOL_EVAL_MAX_USD`, can override the committed $400 spend cap. **Reason and benefit** Each piece that was hand-maintained duplicated a check that already exists. The manifest digest binds every allocation. The frozen install binds dependencies to the reviewed commit. Under model-request readiness, the self-hashed bounds never changed admission or accounting. **Breaking changes** The old variable is no longer read. It can stay set or be deleted. Retained manifests that include the old proofs still replay. ## What Changed - Add `packages/paperclip-runner/scripts/controlled-evals/hosted-defaults.json`. It holds the spend cap, duration, route, Claude Sonnet 5 prices, allocation, overhead reservation and trace limits. - Add `hostedPolicy()` in `hosted.mjs`. It derives `maxCells` from the selection. It gives each campaign a validity window from preparation to one hour after its deadline. It applies the optional spend cap override. - Remove `capacityBound` and the policy self-hash from hosted preparation. `manifest.mjs` needs model `provisionalBound` only when readiness is not `model_requests`. - Workflow: stop reading the policy variable. The controller build installs the committed `pnpm-lock.yaml` with `--frozen-lockfile` and fails on any lockfile drift. Preparation checks that the runtime receipt lockfile digest equals the committed lockfile. The catalog limit is now 1 to 512 selected cells. - Keep everything else the same: the spend-cap check before provider work, private S3 retention of the manifest and derived `hosted-policy.json`, receipts, tracing, first-attempt and zero-retry gates, and paid-environment approvals. - Update `hosted.test.mjs`, `portable.test.mjs`, `runner-protocol-eval-workflow-security.test.mjs`, and `docs/runner-protocol-live-evals.md`. ## Verification - `node --test scripts/controlled-evals/hosted.test.mjs`: 8 pass. The new tests show that `maxCells` equals the selection and that the validity window covers the deadline with no committed expiry. They also show that the spend cap override applies. With the committed defaults, 41 cells validate with $339 reserved, and 49 cells fail on the $400 budget. - Negative controls still fail: drift in selection, source or attempt; a bad lock digest; missing tracing; a duration over 4 hours; a malformed or too-low spend cap; and zero cells. - `node --test scripts/runner-protocol-eval-workflow-security.test.mjs`: 20 pass. Controlled admission now passes whether the old variable is absent, still set, or malformed. A real git checkout with a modified `pnpm-lock.yaml` fails the controller guard before install. - `node --test scripts/controlled-evals/controller.test.mjs scripts/controlled-evals/portable.test.mjs`: all pass. - `actionlint .github/workflows/runner-protocol-live-evals.yml` and `git diff --check` pass. - `tests/runner-e2e/workflow-security.test.ts` has 5 failures that this change does not cause. They concern `pr-trusted.yml`, `chat-catalog-testops-case.yml` and `runner-full-stack-e2e.yml`. This PR does not touch those files. - I have not run a live controlled campaign on this branch. Controlled mode runs only from the default branch. The current 41-case run uses master and the existing variable. ## Risks - The committed price table has no expiry. A provider price change needs a reviewed edit to the table. The spend cap still limits the maximum spend. - If the committed lockfile does not match the package manifests, the controller build fails before provider work. CI already requires a frozen lockfile. - The spend cap is now $400 by default. It was $339 in the last manual policy. The cap is a reservation limit, not expected spend. The expected spend for 41 cases is about $5–20. - Only ACPX Claude Sonnet 5 has a committed allocation, as in the old variable. A new controlled model needs a committed price entry and allocation. ## Model Used Anthropic Claude Opus 5.5 (`claude-opus-5-5`) 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 - [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> |
||
|
|
01dd6ab336 |
fix(recovery): show the saved-work recovery step and record why it failed (#19260)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Sometimes a failed run left files in a sandbox. Then Retry first
runs system recovery to save those files before the task continues.
> - Recovery has steps: confirm that the previous attempt stopped, save
its files, then finish.
> - The task page did not show these steps. People saw "Queued" or
"Trying again in…" with a retry time that kept moving.
> - Each unclassified failure of the stop-confirmation step was recorded
only as `provider_unavailable`, so a repeated deferral could not be
diagnosed.
> - This pull request shows the current recovery step on the task. It
also records why each failed attempt failed, in closed labels.
> - The benefit is that people can see what Retry waits for, and
operators can see why recovery deferred.
## Linked Issues or Issue Description
Refs #19249
**What happened?**
A person clicked Retry on a task whose failed run had a retained
sandbox. For about 7 minutes the task showed only "Queued", and the
retry time moved forward twice. Recovery deferred twice in the
stop-confirmation step. Each deferral was recorded only as
`provider_unavailable` with an opaque fingerprint, so the cause could
not be found.
**Expected behavior**
The task shows the recovery step that it waits for, such as "Checking
that the previous attempt stopped. Trying again in 30s…". Each failed
attempt keeps a closed-label cause, for example `sandbox_not_running` or
`transport_timeout`, with an optional errno-style code and HTTP status.
**Steps to reproduce**
1. Get a remote task run to fail after its sandbox was placed, so that
recovery owns saved work.
2. Click Retry while the sandbox provider does not respond to the stop
check.
3. The task shows "Queued" or "Trying again in…". The recovery evidence
records only `provider_unavailable`.
**Paperclip version or commit**
master at
|
||
|
|
5f61392cd6 |
Reconcile dormant Codex predecessors through ordinary recovery (#19132)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The native Runner keeps provider and controller journals between turns. > - A process can stop after a completed turn while those journals remain valid. > - The next owner must reconcile that source before it starts new work. > - This pull request reuses ordinary recovery and authenticated run attachment. > - Existing journals and owner admission keep retained work under its source. > - The benefit is conversation recovery without another snapshot format or native mode. ## Linked Issues or Issue Description Refs #19057. Built on merged #19129. This is the Codex recovery transport foundation. Process selection and controller scheduling remain separate changes. On normal startup, `paperclip-runnerd` selects durable transport from its connection arguments. With `--connect-url`, the Runner dials the controller. With `--listen-address 0.0.0.0 --listen-path /api/runner/v1/connect/<run-id>` and optional `--listen-port` (default `43127`), the controller dials the Runner. Both use the same durable entry point for fresh launches and recovery. The bootstrap ticket authenticates startup, and `recoveryIntegrityVersion` advertises a recovery capability. ## What Changed - Observe the existing recovered command journal before provider restoration. A saved dormant Codex session with no uncertain effectful command must still be dormant after restore. Ordinary active same-run recovery keeps its existing behavior. - Preserve prepared providers during snapshots, drain and cleanup. New completed-predecessor admission excludes prepared state. Existing cleanup and ordinary continuation keep their established behavior. - Advertise `recoveryIntegrityVersion: 1`. Reject the removed `--source-reconcile` flag. No separate native source mode or command whitelist remains. - Derive controller admission from the authenticated `authorityRunId` and actual Runner identity. Reuse retain-only settlement through attachment, authentication and checkpoint persistence. Preserve dynamic ownership across home configuration and later warm reuse. - Require the selected execution driver's trusted `sourceRecoveryVersion: 1` prelaunch attestation for a stopped predecessor. Binary metadata alone cannot authorize that launch. Ordinary live adoption keeps its existing path. - Support fresh authorized preparation from `recoveryLaunch: { requirements }` without replacing the source's ordinary cache. Require an authenticated owner throughout that transport's lifetime. Preserve immutable recorded policy through target preparation and later warm attachment; compare explicit settings using the provider factory's effective defaults. - Complete interrupted attachment at the existing fresh-snapshot boundary: persist the checkpoint, recheck current ownership, complete the external recovery callback, then release a real predecessor dispatch hold. Keep callback/checkpoint failures retryable and cancellation cleanup retained. - Preserve completed-session event delivery after target attachment, exact-thread history projections, source registration identity, exact command replay and nullable usage redaction. - Recheck current ownership and the queued run when a slow notification consumer presents a permission request. Suppress stale presentation while preserving the committed event and pending request for source-owner settlement. ## Verification Current head `e90bb379178db75f78e440c92e108f0bbe01e073` merges master `499712c68b3c` and is fully verified. [PR CI](https://github.com/paperclipai/paperclip/actions/runs/42058217937), [Runner Windows](https://github.com/paperclipai/paperclip/actions/runs/42058217814), and [Docker Runner](https://github.com/paperclipai/paperclip/actions/runs/42058217365) all passed. [Standard Greptile review 18](https://github.com/paperclipai/paperclip/pull/19132#issuecomment-6645302809) scores this exact head 5/5 with no unresolved threads. The earlier Apex review 16 applies to `c09ebb4a2a6c`, as recorded below; no new Apex review was requested after the documentation or master-forward changes. The conflict integration preserves upstream external cold-state restoration before completed-handoff inspection, predecessor ownership and pending-command checks, and the lost-source early-read exclusion. Live adoption still does not restore a backup. All 43 focused process cases passed across cold restore (including denied/foreign state), staged handoff, ordinary adoption and source recovery. Runner `tsc --noEmit`, parent-relative diff checks and added-line secret/personal-data scan passed. [Read the rewritten guide](https://github.com/paperclipai/paperclip/blob/e90bb379178db75f78e440c92e108f0bbe01e073/packages/paperclip-runner/runner/DURABLE_TRANSPORT.md#recovering-codex-between-turns): it explains dial and listener startup, then follows completed run A through ordinary recovery and authenticated attachment to run B. The guide was rewritten in documentation commit `75db893fb9ab` and is unchanged by the master-forward. Its listener CLI test and both goal-update process tests passed. Earlier feature head `c09ebb4a2a` includes a process regression for a goal notification arriving between the restored goal response and the final thread response. Before the fix the Runner admitted the provider. The new tests prove that an active goal refuses admission and reaps the provider, and that a paused goal is delivered normally. Both process tests and five adjacent ordinary recovery cases pass locally. GitHub CI passed on the first attempt, including the main, Windows, and Docker workflows. Standard review 15 and Apex review 16 each scored this commit 5/5, with zero unresolved threads. [GitHub CI](https://github.com/paperclipai/paperclip/actions/runs/41991764330) · [Greptile review](https://github.com/paperclipai/paperclip/pull/19132#issuecomment-6645302809) - The final ownership-race checks pass 7 focused cases, including owner changes during checkpoint persistence, handler publication and saved receipt recovery. - Native `codex_provider ordinary_recovery`: 6 passed, including saved/live mismatch, pending and indeterminate same-run goals, settled idle restoration and prepared reads/cleanup without a provider restart. - Native `paperclip-runnerd tests::`: 9 passed, including metadata and legacy flag rejection. - Native `interrupted_result_delivery_reconciles_exact_receipts_without_reexecuting_operations`: passed. - Final focused compatibility run: 24 process cases passed, including all 19 retained-settlement rows with queued snapshots, both home variants of A-to-B-to-C, ordinary attachment/suspension and environment-prepared close. - Existing `recovers an active second warm business run after deleting its controller context assets`: passed. - Runner `tsc --noEmit`, `git diff --check`, and added-line secret and personal-data scans passed. - Real native processes with the maintained fake Codex provider prove abrupt A exit, ordinary recover A, same-process attach B, a second successful turn, ordinary warm attach C and a third successful turn on the same thread. Both isolated and preconfigured homes pass. The isolated case also exercises environment option copying. - Additional process cases cover cacheless preparation, same-PID controller restart adoption, revoked preparation, retained tool-input admission, direct attachment, ordinary suspension and unexpected live goals. - A prior revised-head CI run exposed 8 retained-cleanup failures caused by prepared auto-restoration. That new behavior and its journal-binding code are removed. All 19 affected settlement variants now pass locally. The same CI run also had one environment-prepared close failure (focused check passes) and two unchanged Cloud sign-in E2E heading timeouts. Original logs remain recorded. Full suites and platform checks run in CI. Previous head `16772ad3e1abfdae9abcc64f8221fa3644ecee58`: [PR workflow 41981127140](https://github.com/paperclipai/paperclip/actions/runs/41981127140), [Runner Windows 41981126926](https://github.com/paperclipai/paperclip/actions/runs/41981126926), and [Docker Runner 41981126975](https://github.com/paperclipai/paperclip/actions/runs/41981126975) passed. Greptile review 14 scores this exact head 5/5 with no unresolved findings. The PR remains draft. CI evidence: an unchanged `idle-completed-history` local HTTP readiness case failed in the initial server 11/12 job (127092367616) and its first targeted retry (127096952128). The isolated case and the full 75-case file passed locally. The same-head debug retry (127103567635) passed, and the full workflow is green. Original failures remain recorded. No fixture diagnostic, assertion change, or timeout change was pushed. Focused review-fix validation: - 57 process cases passed across recovery, attachment and all 19 retained-cleanup variants. - Final staged matrix: 5 cases, each exercising queued/prepared/activated/completed boundaries, across owned connect/listen, legacy no-owner connect/listen, and retained cancellation cleanup. - Four completion-failure cases pass: synchronous/asynchronous callback failure, checkpoint failure before any callback, and owner rebind during persistence before any callback. Fresh-snapshot retry succeeds without duplicate provider work. - Saved-policy positive and explicit-conflict negative pass, including an authenticated compatible target grant and B-to-C continuity with omitted live settings. - Factory/Core checks: 34 passed, including effective ACPX defaults, real policy changes, already-dispatching target work and predecessor admission. - Ordinary adoption compatibility checks: 11 passed. Cacheless owner construction/revocation and policy checks: 6 passed. - Deferred permission ordering/forwarding: 30 passed. The new regressions reproduce owner changes before queueing and before slow-consumer delivery; matching-owner presentation and later source-owner settlement remain intact. - Runner `tsc --noEmit`, `git diff --check`, and full-PR added-line secret/personal-data scan passed at the corrected head. New tests reproduced both review findings and premature completion on failed persistence/changed ownership before the fixes. Full suites remain assigned to CI. ## Risks - Fake-provider tests do not prove vendor restore inertness or rollout fsync. - The process owner must prove stopped ownership, no successor and strict dormant provider admission before a predecessor launch. This PR does not select or stop host tasks. - Ordinary suspended active-goal continuation remains distinct from completed-predecessor admission. - Live dormant checks use bounded goal and thread reads. Unsupported or conflicting responses fail closed. - Prepared provider state is outside new predecessor admission. Reads and cleanup retain their baseline behavior. The feature targets completed between-turn session-open or exited sessions. - No tenant changes or image publication are included. ## Model Used OpenAI GPT-6.1 Sol (`gpt-6.1-sol`), high reasoning, through Codex with local code and command tools. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used - [x] I have checked ROADMAP.md and confirmed this does not duplicate planned work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have linked the related public issue or PR - [x] I have not referenced internal issues or instance links - [x] My branch name describes the change - [x] Focused local tests pass - [x] I have added tests and updated documentation - [x] I have documented risks - [x] All Paperclip CI gates are green at the revised head - [x] Greptile is 5/5 with no unresolved comments at the revised head - [x] I will address review findings before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |