mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-06 21:05:21 +02:00
fd472d02ba950519ca5eff75ed9fd55cf976f4c2
3662
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fd472d02ba |
Show ordered live blocker work in task chat (#11487)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The task view shows operators why work cannot continue > - The redesigned task thread now shows direct and ultimate blockers > - But it does not show the ordered task queue while a blocker chain has live work > - This pull request adds a compact ordered live-work queue to the redesigned thread > - The benefit is that operators can see completed, running, and queued dependencies without opening the larger legacy notice ## Linked Issues or Issue Description **What existing behavior does this improve?** The redesigned task thread blocker summary is improved. The merged predecessor is #11456. **Current behavior** The redesigned task thread shows compact direct and ultimate blocker links. It does not show the ordered queue when the blocker tree has live work. The legacy task view shows this queue in a larger notice. **Proposed behavior** Show a compact blue live-work queue at both ends of the redesigned task thread. Order completed tasks first, then running tasks, then queued tasks. Show a live terminal leaf as `Now running`. Return to the amber blocker links when no live dependency remains. **Reason and benefit** Operators can see the active dependency order without leaving the redesigned task view. The compact presentation preserves the new thread's low-chrome layout. **Breaking changes** None. The change only adds UI for blocker data that the task view already receives. ## What Changed - Shared the live blocker ordering helper between the legacy notice and the redesigned task thread. - Added compact ordered dependency links at the top and bottom of the redesigned thread. - Added a separate `Now running` link for a live terminal blocker leaf. - Preserved the compact amber blocker rows when live work is not present. - Added component tests and a Storybook state for the new presentation. ## Verification - `pnpm exec vitest run ui/src/components/TaskChatThread.test.tsx ui/src/components/IssueBlockedNotice.test.tsx` - `pnpm check:token-gates` - `pnpm -r typecheck` - `pnpm test:run` - `pnpm build` - `pnpm --filter @paperclipai/ui build-storybook` - Captured and reviewed the new Storybook state in a headless browser. ## Risks - Low risk. The queue appears only for blocked tasks whose blocker attention state is `covered` and whose dependency set contains live work. - The API does not provide an explicit queue position. The UI preserves the existing legacy ordering rule: completed, running, queued, then numeric task identifier. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - OpenAI Codex, GPT-5. The runtime does not expose the exact snapshot or context-window size. Reasoning, code execution, repository tools, and browser automation were 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 Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.816.0-canary.8 |
||
|
|
ac91b7f3b2 |
fix(ui): scope the company selection to the signed-in account (#11430)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Every company-scoped screen reads the active company from `CompanyProvider`, which picks one from the `["companies"]` list and remembers it in localStorage > - That cache entry is shared app-wide and carries no account identity, so it survives a change of account in the tab > - The provider therefore auto-selects from whatever list is cached, which can belong to the account that just went away > - This pull request makes the provider watch the account and refuse to derive a selection from a list fetched for a different one > - The benefit is that the app stops pointing at a company the signed-in account may not be able to see ## Linked Issues or Issue Description No public issue exists. Refs #11380, #11382, #11417. The problem follows. **What happened?** `CompanyProvider` auto-selects a company from the shared `["companies"]` cache entry and writes that id to `localStorage`. Nothing ties that entry to an account. When the account changes in the tab, the previous account's list is still served, so the provider can select — and persist — a company belonging to the account that just went away. Company-scoped screens then render against a company the current account may not be able to see. Signing in through `Auth.tsx` invalidates the entry, so the in-app sign-in path is covered. Two paths are not: a session that lapses server-side, and a second account signing in on another tab. The sign-out sweep in #11380 does not cover them either, because neither presses the sign-out button. **Expected behavior** The company selection is derived only from a company list fetched for the account that is signed in now. **Steps to reproduce** 1. On a self-hosted instance in `authenticated` mode, sign in as account A, which belongs to company X. 2. In a second tab, sign in as account B, which does not belong to company X. 3. Return to the first tab. The session query refetches and reports account B, while the company list is still account A's. 4. The provider keeps company X selected and leaves its id in `localStorage`. **Paperclip version or commit** `master` at `6542ad1f4`. ## What Changed - `ui/src/context/CompanyContext.tsx` — the provider observes `queryKeys.auth.session`. On a change of session user it clears the live selection, removes the shared company list, and holds auto-select until a list fetched for the new account lands. The stored id is left alone on purpose: `resolveBootstrapCompanySelection` re-validates it, so an account signing back in keeps its company while an unrelated account cannot inherit it. - `ui/src/context/CompanyContext.tsx` — an errored list is treated as undecided rather than as "no companies". With `retry: false` a single network blip sticks, and the empty-list branch read it as proof the account owns nothing and cleared the stored selection. - `ui/src/api/client.ts` — new `detachInflightGet(path)`. GET coalescing keys on the request path alone, so a `/companies` request issued under the previous session could be joined by the replacement fetch and answer it with the previous account's companies. Detaching leaves that request to settle for its own callers and makes the next call issue a fresh one. - `ui/src/api/companies.ts` — `companiesApi.detachInflightList()` wraps that for the list path. - `ui/src/context/CompanyContext.tsx` — `companyListUnavailable` separates "no usable list because a request failed" from "this account owns nothing", and `retryCompanies` gives a recovery action that fetches. Both are derived from the query rather than tracked beside it; a second copy of "did the last attempt succeed" drifted out of step during review, reporting a failure over a later empty list that was simply the truth. - `ui/src/components/SidebarCompanyMenu.tsx` — renders "Couldn't load companies" and a Try again item in place of "No companies", which is a claim about the account that a failed request cannot support. This is the menu `Sidebar` mounts, so it is the only place a customer can act on the failure. - `ui/src/components/CompanySwitcher.tsx` — the same treatment. The application does not render this component (its only mount is a Storybook story), so it is kept in step rather than relied on. - `ui/src/context/CompanyContext.test.tsx`, `ui/src/components/SidebarCompanyMenu.test.tsx`, `ui/src/api/client.test.ts` — coverage for the account switch, a same-account re-observation not churning, the detached GET, the failed replacement and its recovery, a single blip self-healing, unavailability not outliving the failure, and the sidebar rendering the recovery action for a failure but plain "No companies" for an account that owns nothing. ### No `retry` override on the replacement fetch The obvious fix for a failed replacement is a retry, and it is not load-bearing here. A transient failure already gets a second attempt: the observer rebinds to a fresh query on the render those state updates schedule, and issues its own request — measured as two attempts with or without the option. Retries would only add failed round trips before a real outage is reported, and the outage is what needs a way out, which is what `companyListUnavailable` and `retryCompanies` provide. ### Why `removeQueries` here, and why that does not generalise Removal notifies no observer. What rebinds them at this call site is the render the surrounding state updates schedule; every observer re-binds to a fresh query on the next render. A caller without that guarantee would leave mounted observers serving the previous account's value, so this is not a pattern to lift elsewhere — the sign-out sweep in #11380 must use `resetQueries` instead, and its measurements are at [#11380](https://github.com/paperclipai/paperclip/pull/11380#issuecomment-5300984911). The inverse caveat holds for a local reset under an observer that stays mounted, which is why #11417 and #11382 avoid `resetQueries`. ## Verification - `pnpm vitest run` in `ui`: **4014 passed, 1 failed**. - `pnpm tsc -b` in `ui`: clean. - `CompanyContext.test.tsx`: 16 passed. `SidebarCompanyMenu.test.tsx`: 15 passed. `client.test.ts`: 9 passed. The failure is pre-existing and unrelated: `IssueProperties.test.tsx` expects `4:08 PM` and gets `9:08 AM`, a timezone-dependent assertion. It reproduces on a tree without this change, and #11478 fixes it. Each new test was confirmed to fail against the implementation it covers, by reverting that change and re-running rather than by assuming. The account-switch test fails without the fix (the selection stays on the previous account's company and no refetch is issued); the flag-clearing test fails without its clause (an empty list keeps reading as "couldn't load"). **Not done:** no manual two-account run in a browser. The path needs two accounts on an `authenticated` instance, which a local dev instance cannot exercise. ## Risks Low. The failure direction is a company selection withheld for one extra round trip, which resolves when the list arrives. The direction it removes is one account's company selected and persisted for another. It adds one company-list request per account change, on a query key the app already uses. It adds no request at boot: the session query it observes is already fetched app-wide. **This does not close the class.** Company-scoped entries other than the list — `["companies", id]`, stats, and the rest of the per-account cache — still survive an account change. That is the cache-lifetime work in #11380, not this provider's. ## Model Used Claude Opus 5 (`claude-opus-5`), through Claude Code. Extended thinking enabled. Tool use enabled: file read and edit, shell for typecheck and test runs, and a scratch vitest harness to measure `removeQueries` and `resetQueries` notification behaviour against the installed `@tanstack/query-core` 5.101.4. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>canary/v2026.816.0-canary.7 |
||
|
|
10d0555189 |
fix(interactions): authorize resolvers consistently (#11376)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Issue interactions give agents and people a structured decision record. > - Resolver routes used different authorization rules. > - Some routes blocked valid agents, including task watchdogs with normal issue access. > - The API did not show who could resolve a pending interaction. > - This pull request gives every interaction kind one resolver policy evaluator. > - The benefit is a clear decision path with consistent governance and company isolation. ## Linked Issues or Issue Description Fixes: #8087 Refs: #7403 Related PR: #11082 proposes board-only confirmation rules. This change keeps human-only review as an explicit policy. **What happened?** Agents could create issue interactions. Some resolver routes still required board access. This left valid agent confirmations pending. Task watchdogs could see the same problem without board identity. **Expected behavior** Every interaction kind must use one resolver policy contract. The contract must support `anyone`, `not_creator`, and `human_only`. It must also apply all normal governance controls. **Steps to reproduce** 1. Create a `request_confirmation` interaction as an agent. 2. Resolve it with another authorized agent. 3. Observe the board-only denial. **Paperclip version or commit** The problem exists on `master` before this change. **Deployment mode** Local development with `pnpm dev`. ## What Changed - Add canonical policies for `anyone`, `not_creator`, and `human_only`. - Use one server evaluator for every interaction kind. - Apply named addressees, company limits, review rules, and task watchdog scope. - Charge cross-issue resolutions to the existing per-run action limit. - Return the effective resolver audience in attention and interaction data. - Show the audience, governance choices, and denial reasons in the board UI. - Add telemetry, API documents, product documents, and regression fixtures. - Add migration provenance for safe legacy behavior. - Make migration `0218` safe for complete replays and partial prior runs. ## Product Rules - An interaction records a response. It does not grant authority for the next action. - `anyone` lets any authorized issue participant respond. - `not_creator` requires a responder other than the interaction creator. - `human_only` requires an authorized person. - A named addressee, company policy, or governed action can narrow the audience. - These controls cannot widen the audience. - A task watchdog uses the same rules as an ordinary agent. - A task watchdog does not receive board authority. - An agent resolution on another issue uses the shared cross-issue action limit. - Legacy pending interactions keep their earlier restrictions. - The UI shows the effective audience and a permanent denial reason. ## Verification - `pnpm --filter @paperclipai/db check:migrations` - `pnpm --filter @paperclipai/db typecheck` - `pnpm exec vitest run packages/db/src/issue-thread-interaction-resolver-policy-migration.test.ts` - The focused PostgreSQL test applies migration `0218` twice. - The test also completes a partial prior run and preserves existing provenance. - The latest GitHub head has 29 successful checks. - The opt-in Storybook visual check skipped as expected. - Greptile reports 5/5 with no open comments. ## Risks - New interaction writes use `anyone` by default. - Callers must select `not_creator` or `human_only` when they need stricter review. - Legacy pending interactions keep the old creator and human restrictions. - Migration `0218` fills only missing provenance fields during recovery. - Cross-issue resolutions can reach the existing action limit. - The shared evaluator affects every interaction kind. - Route, service, database, shared contract, and UI tests cover these rules. > This work matches the Agent Reviews and Approvals direction in `ROADMAP.md`. It does not duplicate a planned item. ## Model Used OpenAI Codex, GPT-5. The runtime does not expose the exact deployment ID or context window. The agent used reasoning, repository tools, shell commands, and 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 linked public issues or described the issue with the required labels - [x] I have not referenced internal Paperclip issues or links - [x] My branch name describes the change and contains no internal ticket id - [x] I have run 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 the risks - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open comments - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
d6acb48551 |
feat(ui): show a calm in-flight notice when a live run is on the issue (#11423)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - When an agent run ends without a recorded disposition, Paperclip raises a "missing disposition" handoff so the work does not stall silently > - The issue page shows that handoff as an amber alarm: "This task still needs a next step." > - The server tells the UI whether the issue has a live continuation, and an earlier change used that flag to hide the alarm while a correction run is active > - Hiding it removed the false alarm but replaced it with nothing, so a reader cannot tell "nothing is wrong" from "nothing is tracked" > - This pull request puts a quiet informational line where the alarm was, and links the live run > - The benefit is that the page stays honest in both states: it is calm while an agent works, and it is loud only when the issue is really stuck ## Linked Issues or Issue Description No public GitHub issue exists for this gap. Description follows `.github/ISSUE_TEMPLATE/enhancement.yml`: **What existing behavior does this improve?** The missing-disposition handoff notice on the issue page. It is the amber banner that reads "This task still needs a next step." **Subsystem affected** Web UI — `ui/src/components/IssueBlockedNotice.tsx`. **Current behavior** An issue with an outstanding missing-disposition handoff shows nothing at all in the blocked-notice slot while a correction run is live. `IssueBlockedNotice` calls `isSuccessfulRunHandoffRequired()`. That helper returns `false` when `successfulRunHandoff.hasLiveContinuation` is set. The component then renders no handoff content. Two tests asserted the empty render. **Proposed behavior** The page states, quietly, that a correction run is in progress. It also states that the alarm returns if the run stops without choosing a next step. The reader can open the live run from that line. The amber alarm does not change when no run is live. **Reason and benefit** Silence and "healthy" look the same. A user who saw the alarm earlier cannot tell whether the handoff was resolved, whether the alert was withdrawn, or whether an agent is working on it now. One muted line removes that ambiguity. It also keeps the loud state meaningful, because the alarm now appears only when the issue is really stuck. **Breaking changes** None. The change is presentational and adds no API or data-shape change. ## What Changed - Added `SuccessfulRunHandoffInFlightNotice` to `ui/src/components/IssueBlockedNotice.tsx`. It renders a muted row with a pulsing live dot and this copy: "A correction run is in progress — the agent is working. This alert returns if the run stops without choosing a next step." - The notice links the live run when the server sends `liveRunId` and the handoff has an `assigneeAgentId`. It shows the short run id as plain text when no agent id is available, and it shows no run reference when `liveRunId` is absent. - Liveness reads either the server `hasLiveContinuation` flag or the fresher client `liveIssueIds` set. This matches the rule that already suppressed the alarm. - The amber alarm is unchanged when no live continuation exists. The unpromoted scheduled-retry carve-out still shows the alarm, so the "Retry now" control stays reachable. - The calm line also renders above the blocker notice when an issue has blockers and a live run at the same time. - Storybook: added `InFlightNotice` and `LivenessComparison` stories to `ui/storybook/stories/successful-run-handoff.stories.tsx`, and removed a duplicated panel from the overview story. - Tests: the two cases that asserted an empty render now assert the calm line. New cases cover a missing `liveRunId`, a missing agent id, a handoff that is not required, and the two "alarm is unchanged" guards. ## Verification Run the component and helper suites from `ui/`: ``` cd ui && NODE_ENV=test npx vitest run \ src/components/IssueBlockedNotice.test.tsx \ src/components/IssueChatThread.test.tsx \ src/components/IssueChatThreadSystemNotice.test.tsx \ src/lib/successful-run-handoff.test.ts ``` Result: 4 files, 111 tests, all pass. Also run: - `cd ui && npx tsc -b --force` — clean. - `node scripts/check-token-gates.mjs` — all gates clean. Manual check in Storybook (`pnpm --dir ui storybook`), story `Paperclip/Successful Run Handoff → Liveness Comparison`: - The alarm panel keeps its 4 remediation bullets, its amber surface, and its run chips. - The calm panel shows one 39 px muted row, no bullets, and a working link to the live run. - Measured contrast of the calm text against its rendered surface: 4.58:1 in light mode and 6.52:1 in dark mode. Both pass WCAG AA for normal text. ## Risks Low risk. The change is limited to one presentational component and its stories. It adds a render path where the component previously returned `null`, so a surface that expected an empty render now shows one muted row. No server, API, or data-shape change. The amber alarm path and the scheduled-retry carve-out are covered by tests that assert the calm line does not appear. ## Model Used Claude Opus 5 (Anthropic), model id `claude-opus-5[1m]`, 1M context window, extended thinking, 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 Co-authored-by: ClaudeCoder <claudecoder@paperclip.ing> Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.816.0-canary.6 |
||
|
|
92047cac46 |
test(adapter-utils): make the next sandbox flake diagnosable (#11483)
`execution-target-sandbox` has failed twice in CI and not once in several hundred local runs. This does not fix it. It makes the next occurrence carry its own evidence, because a third unreproducible failure would teach nothing. The observed signature was an empty stdout with exit code 0 - the child exited cleanly having produced nothing, which is what a lost stdin frame looks like from the test's side. Three mechanisms were checked and ruled out rather than assumed: the helper resolving on `exit` rather than `close` (a 200-iteration probe produced no truncations, and the failure was empty rather than partial); the wrapper reporting exit before stdout drains (it already listens on `close`); and frame writes racing (the stream wrapper's `writeEvent` is synchronous and sequence-numbered). Two candidates remain and the runtime tree separates them. A stdin queue frame still present means the host wrote it and the wrapper never consumed it; a drained queue with no output means it was consumed and the reply was lost on the way back. The report prints that tree, both proxy streams, the exit code, and the elapsed time - the last because the bridge and proxy run on 5s budgets that are generous locally and tight on a runner sharing a box with 19 other lanes. Timeouts are deliberately unchanged. Raising them would probably make the symptom go away, which is the reason not to do it blind. The first revision capped the tree walk one level above the queue frames, so "the queue is empty" and "the walk never looked" printed identically - the distinction the report exists to make. Caught in review. Verifying that the reporter printed something was not enough; it had to print the thing that discriminates, which is now checked by planting a frame and forcing the assertion. adapter-utils typecheck clean; 44 pass, stable across repeated runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>canary/v2026.816.0-canary.5 |
||
|
|
add65ba4a2 |
test(ui): make the suite pass outside UTC (#11480)
The ui suite was green in CI and red on a clean checkout in any other
timezone. GitHub's runners default to UTC, so nothing ever reported it. A
contributor elsewhere sees two failures on their first run, which reads as
"this project is broken" rather than "your clock differs from the runner's".
Two independent causes.
`IssueProperties` supplies a UTC instant and asserts on the local-time string
the UI renders from it - "2026-07-17T16:08:00.000Z" is expected to read
"Today, 4:08 PM". That holds only where local time is UTC. Pinned with
`env: { TZ: "UTC" }` rather than rewritten: those assertions are about what a
person sees, and "4:08 PM" is worth more to a reader than an expectation
computed from the same formatter the component uses, which would pass whatever
that formatter did.
`StatusCards/format` was wrong in two ways at once, and the pin hides only one,
so it is fixed directly. `rollupUpdatesToday` filters on the *UTC* day
boundary, while the test built fixtures from local noon on the real clock.
East of UTC+12, "today at local noon" is already yesterday in UTC and the rows
the test means to count are filtered out; and any run crossing midnight UTC
lands `iso(0)` and the function's default `now` on different days. The
fixtures now come from a fixed instant, passed as `now` - the parameter exists
for this, and the sibling test already used it.
Each fix was confirmed load-bearing by removing it under TZ=Pacific/Auckland.
Without the pin, IssueProperties fails; without the fixed instant, StatusCards
fails even with the pin removed, so neither rides on the other.
Full ui suite 4017 pass, 0 fail, in UTC, Pacific/Auckland and Asia/Kolkata.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
canary/v2026.816.0-canary.4
|
||
|
|
9e9f744f58 |
Show blocker links in the task chat (#11456)
<!-- Write all pull request text in Simplified Technical English (ASD-STE100): short sentences, one instruction per sentence, simple approved vocabulary, and the active voice. --> ## Thinking Path > - Paperclip helps operators supervise agent work through tasks and task threads. > - The redesigned task thread shows the current work and its state. > - A blocked task did not show the dependency that prevented progress. > - Operators had to leave the thread to find the direct and final blockers. > - This pull request adds compact blocker links at the top and bottom of the task thread. > - The benefit is that operators can identify and open the relevant tasks without adding a large notice to the thread. ## Linked Issues or Issue Description **What existing behavior does this improve?** The redesigned task thread did not show which task directly blocked the current task or which task ultimately blocked its dependency chain. **Subsystem affected** `server/`, `packages/shared/`, and `ui/` task-blocker presentation. **Current behavior** A blocked task can open in the redesigned thread without a visible dependency link at the top or bottom of the conversation. **Proposed behavior** Show one compact amber row for the direct blocker. Show a second row for the selected final blocker when one exists. Render the rows at both ends of the thread. **Reason and benefit** Operators can see the reason for the blocked state and open the relevant task from the conversation. The compact rows preserve thread density. **Breaking changes** None. The new blocker-attention fields are optional. Existing clients remain compatible. ## What Changed - Added a compact task-chat component for direct and selected final blocker links. - Added the blocker rows to the top and bottom of populated and empty task threads. - Added link-ready blocker-attention details so an intermediate selected task stays on its correct direct chain. - Included blocker-link changes in the thread content key so pinned threads follow a newly added bottom row. - Added component, scrolling, server contract, and Storybook coverage for the new states. ## Verification - `pnpm exec vitest run ui/src/components/TaskChatThread.test.tsx server/src/__tests__/issue-blocker-attention.test.ts` (38 tests passed) - `pnpm --filter @paperclipai/shared typecheck` - `pnpm --filter @paperclipai/server typecheck` - `pnpm --filter @paperclipai/ui typecheck` - `pnpm check:token-gates` ## Risks - Low risk. The rows only render while the task status is `blocked` and an unresolved blocker is available. - Long titles are truncated to keep each blocker on one line. The full task label remains available in the link title. - Older server payloads keep the original leaf-selection behavior because the new sampled details are optional. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - OpenAI Codex with GPT-5. The run used tool-enabled reasoning and code execution. The context-window size was not exposed to the run. ## 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> |
||
|
|
e384d0a2bd |
fix(ui): keep the stored company when the company request fails (#11477)
An error is not an answer, and this branch is destructive.
`companiesListQueryOptions` sets `retry: false`, so a request that fails before
ever succeeding leaves `data` undefined - which `CompanyProvider` defaults to
`{ companies: [], unauthorized: false }`. That is indistinguishable from "this
account was asked, and owns nothing", so `shouldClearStoredCompanySelection`
returned true and the effect removed the customer's stored company. With
`refetchOnWindowFocus: true`, a blip on focus during a cold load was enough,
and the next visit drops them onto whichever company sorts first.
The predicate now takes `errored` and refuses to clear on it. Required rather
than optional, so the compiler made both existing call sites state their
answer instead of inheriting a default.
Not clearing costs nothing: a stored id that no longer resolves is ignored by
`resolveBootstrapCompanySelection`, which checks it against the current list
before using it. Clearing wrongly costs the customer's selection, which cannot
be recovered.
Scoped deliberately. This file had been described as carrying the same defect
as the onboarding draft gate and the sign-out sweep, and that was overstated.
Those two *trusted* a stale list to answer "does this account own this
company?". This one validates membership against the current list and only
picks a default, so a stale list here self-corrects rather than leaking. The
failed-request branch is the part that is genuinely wrong, and it is the only
part changed. The transient re-decision during a background refetch is real,
self-correcting, and left alone.
Tested at both levels, because the predicate alone would not have caught it:
the provider is what defaults a failed request to an empty list, so the wiring
is where the decision goes wrong. Removing the guard fails both.
ui typecheck clean; full ui suite 4016 pass, with only the timezone-dependent
IssueProperties failure already present on master.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
canary/v2026.816.0-canary.3
|
||
|
|
0023c5c4a6 |
refactor(ui): record why sign-out resets rather than removes (#11473)
Comment only. Salvaged from `claude/sign-out-cache-note`, written while #11380 was in progress and never opened as a PR; that branch is deleted with this. #11380 landed the reasoning for `resetQueries` but not the evidence, and not the part that stops someone reversing it later. The choice was measured rather than argued. Against query-core 5.101.4, removal produced 0 notifications and left the observer holding the signed-out account's session; reset produced 3 and null. The consequence of the former is not only a stale read - CloudAccessGate's redirect fires on the session going empty, so it never runs. The opposite advice really does hold for a local reset of a key an observer is still mounted against: reset rewinds the update counters `isFetchedAfterMount` derives from while that observer keeps its bind-time baseline, so anything gated on the flag withholds forever. Sign-out is not that case, because it resets the session too and the consumer unmounts on the redirect. Two sessions reached opposite recommendations on this API within a day, both correct about different situations, which is the kind of thing a later reader re-litigates without a note in the file. The first revision of this note named the wrong consumers - InviteLanding and the onboarding draft gate, neither of which reads `isFetchedAfterMount`. `AppsConnect` is the only one on master and is what it names now. ui typecheck clean; 13 pass across the sign-out suites. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>canary/v2026.816.0-canary.2 |
||
|
|
cd501499a2 |
test: add ACPX run lifecycle characterization baselines (#11461)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The adapter runtime starts, turns, settles, and composes ACPX runs > - Recent lifecycle corrections changed several order and cleanup rules > - Those rules need regression coverage before the planned engine refactor > - This pull request adds characterization suites for the corrected behavior > - The benefit is a clear test baseline for the next refactor ## Linked Issues or Issue Description **What existing behavior does this improve?** The ACPX adapter runtime and server heartbeat lifecycle need stable regression coverage for their current corrected behavior. **Subsystem affected** Cross-cutting (multiple of the above): `packages/adapter-utils` and `server` test suites. **Current behavior** The runtime has corrected rules for startup, turns, settlement, composed results, and heartbeat terminalization. The repository lacks a single characterization baseline for these rules. **Proposed behavior** Keep the current lifecycle rules pinned by five test suites. Let the later engine refactor change behavior only when it updates these tests with a clear reason. **Reason and benefit** The suites expose order, cleanup, transport, timeout, retry, result, and lease-release changes during the refactor. They also record one known latent defect as current behavior. **Breaking changes** None. This pull request adds tests only. ## What Changed - Add startup characterization coverage for commands, launch values, session fingerprints, sync order, bridge overlap, and cleanup paths. - Add turn characterization coverage for inputs, events, transports, timeout and cancel behavior, retry rules, errors, and usage. - Add settlement characterization coverage for teardown, adapter sync-back, workspace restore order, native sync, and error policy. - Add composed-run characterization coverage for result forms, finalization sets, and host-lane warm save and warm hit behavior. - Add server coverage that checks run terminalization before environment lease release. ## Verification - Run `npx vitest run packages/adapter-utils/src/acpx-engine/startup-characterization.test.ts packages/adapter-utils/src/acpx-engine/turn-characterization.test.ts packages/adapter-utils/src/acpx-engine/settlement-characterization.test.ts packages/adapter-utils/src/acpx-engine/composed-run-characterization.test.ts packages/adapter-utils/src/acpx-engine/execute.test.ts`. - Run `npx vitest run server/src/__tests__/heartbeat-run-terminalize-before-release.test.ts`. - The adapter-utils run passes 178 tests, and the server run passes 4 tests. - Check `pnpm --filter @paperclipai/adapter-utils typecheck`. - Check `pnpm --filter @paperclipai/server typecheck`. ## Risks Low risk. The change adds test files and does not change production code. One known cold ensure-session cleanup defect remains pinned as current behavior. ## Model Used OpenAI Codex, GPT-5, with tool use and code execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [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>canary/v2026.816.0-canary.1 |
||
|
|
e52b8a343f |
fix: ACP run lifecycle corrections — failure settlement, workspace sync-back, lease cleanup (#11454)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Agent adapters run ACP sessions and manage runtime, workspace, and lease resources. > - Several failure paths left runtime bridges, staged workspaces, or environment leases active after an error. > - These leaks reduce run reliability and can leave later runs without clean resources. > - This pull request closes the failure paths, applies one teardown policy, and adds regression tests. > - The benefit is consistent failure settlement and safer reuse of agent workspaces and leases. ## Linked Issues or Issue Description **What happened?** ACP runs could leave runtime bridges, staged workspaces, or environment leases active after failures. Claude and Gemini ACP runs did not restore the sandbox workspace on teardown. Lease release stopped when one lease returned an error. **Expected behavior** Each ACP failure must return an error result and settle its resources. Teardown must run each step, release leases independently, and restore the host workspace when the sandbox ends. Pending cleanup leases must receive bounded retry attempts. **Steps to reproduce** 1. Run an ACP session that fails after runtime creation or during turn preparation. 2. Run an ACP session that fails during a warm hit or staged runtime handoff. 3. Run lease cleanup with more than one lease when the first release returns an error. 4. Inspect the result phase, teardown calls, workspace state, and lease metadata. 5. Run the regression suites listed in the Verification section. ## What Changed - Settle every ACP failure after runtime creation with an error result and one sandbox.startup span closure. - Close the ACP runtime and remove warm entries after every pre-turn failure. - Run all teardown steps, record teardown errors, release staging leases in finally, and prevent duplicate teardown. - Dispose staged runtimes after seam failures and remove borrowed staged entries with identity guards. - Add fail-open workspace sync-back teardown for Claude and Gemini ACP adapters. - Isolate lease release errors and add bounded retry sweeps for stranded pending_cleanup leases. - Atomically claim pending_cleanup retries and clamp attempt readers to keep the five-attempt bound. - Default absent provider reusableLeases values to false and align the fake provider with its runtime declaration. - Add regression tests for engine, adapter, server, and shared environment behavior. ## Verification - [x] `npx vitest run packages/adapter-utils/src/acpx-engine/execute.test.ts` — 124 tests passed. - [x] `npx vitest run packages/adapters/codex-local/src/server/acp.test.ts packages/adapters/claude-local/src/server/acp.test.ts packages/adapters/gemini-local/src/server/acp.test.ts` — 61 tests passed. - [x] `npx vitest run server/src/__tests__/environment-runtime.test.ts server/src/__tests__/heartbeat-pending-cleanup-sweep.test.ts server/src/__tests__/reusable-leases-default.test.ts server/src/__tests__/environment-routes.test.ts packages/shared/src/environment-support.test.ts` — passed. - [x] All listed suites ran from the repository root. - [x] GitHub CI completed successfully for `cfc349c9f232711433897915112a1c52c0e462ca`. - [x] Greptile completed with a 5/5 confidence score and no blocking finding. ## Risks The engine changes affect failure settlement and teardown order across ACP runs. The server changes add retry state to existing lease metadata without a schema migration. The adapter changes restore workspaces after sandbox execution. Regression tests cover the changed paths. GitHub CI and Greptile passed for the current head. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. This change fixes runtime reliability and does not duplicate a roadmap feature. ## Model Used OpenAI GPT-5 Codex. The model used tool-based repository inspection, GitHub operations, and code review support. The runtime does not expose a context-window value. ## 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 (for example, `docs/...` or `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>canary/v2026.816.0-canary.0 |
||
|
|
38752c4e5e |
fix(ui): clear account-scoped query caches on sign-out (#11380)
Sign-out invalidated two query keys - the auth session and health - and left everything else in the cache. `invalidateQueries` also keeps serving the old data while it refetches, so it was insufficient even for the two it did touch. The company list was never touched at all, which is how one account's companies could still be in hand when the next account signed in. The self-hosted path now resets every account-scoped entry. Scoping is an allowlist of instance-scoped roots rather than a list of things to clear, so a query key added later is account-scoped unless someone deliberately says otherwise - forgetting this file fails closed. `health` is the only exemption. It carries deployment mode, bootstrap state and Cloud metadata, nothing account-scoped, and `useCloudInstance` observes it with `enabled: false` and leaves the fetch to CloudAccessGate. Dropping the entry would strand every such observer on `null` until the gate happened to refetch, flipping Cloud instances into their self-hosted rendering mid sign-out. It is refreshed in place instead. `resetQueries` rather than `removeQueries`: removal empties the cache without notifying the observers already subscribed, so a mounted `useQuery` keeps returning its last result until an unrelated re-render rebuilds it. CompanyProvider sits above the router and stays mounted across the whole sign-out and sign-in cycle, so that is the common case here rather than a corner one. Reset notifies them, so the old data leaves the cache and everything reading it. The cloud path is untouched: it is a top-level navigation, and the document reload builds a new QueryClient with nothing left to clear. This is the root cause behind the onboarding draft-ownership gate added in #11382. That gate stays, and its comment now says why: this fix covers the sign-out button, not the question. An account can change without it - a session lapsing server-side, a second account signing in on a warm tab, a caller supplying the company context from somewhere else - so the gate stays independent rather than deferring to this. ui typecheck clean; full ui suite 4014 pass, with only the timezone-dependent IssueProperties failure already present on master. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>canary/v2026.815.0-canary.8 |
||
|
|
b38d6ddb81 |
fix(onboarding): choose the launcher's step from the company's mission too (#11429)
The last of the three ways into onboarding for a company that already exists.
The route resolver and the dashboard both pick the step from whether the
company already has its mission; the "Add Agent" card on `/{prefix}/onboarding`
hardcoded the mission step. So the one entry point whose own copy reads "Add
another agent to X" was the one that stopped to ask X for the mission it
already had.
It now calls `onboardingStepForCompany`, like the other two. `matchedCompany`
moves above the early return because a hook cannot be called after it.
An unsettled or failed lookup still reads as "no mission" and costs the step,
which the customer can answer - the same fail-open rule the other callers
follow, and safe now that confirming the mission updates the company's existing
goal rather than adding a second one.
`OnboardingRoutePage` is exported so this can be driven directly. The
alternative was the whole `<App>` route table, which is a much heavier harness
for a question about one button's argument.
Four cases, and the first fails against the hardcoded step. The button lookup
asserts it matched something before clicking, because a lookup that silently
matches nothing turns the click into a no-op and the test into decoration.
This is the last piece of #11259 that had not landed.
ui typecheck clean; full ui suite 4010 pass. The one failure, in
IssueProperties, is timezone-dependent and reproduces on master.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
canary/v2026.815.0-canary.7
|
||
|
|
6542ad1f4d |
fix(onboarding): carry an existing company's mission into the wizard (#11416)
Ports the substance of #11259, which predated the recent onboarding work and was still open. One of the parts it solves is a regression #11352 introduced. A company that already has its mission opens on the agent step, which is the point of #11352. What that missed is that the mission field is filled only by the step being skipped, and that the field is not decoration: `composeCeoInstructions` seeds the lead agent's instructions from it, and the Review checklist reads it. So every Cloud-seeded company hired its lead agent with no Mission line at all, having been routed there precisely *because* it had a mission. Before #11352 those companies dead-ended on the mission step; a dead end became a quiet data loss, which is worse, because it completes. `selectExistingCompanyMission` reads the company's own goal back into the shape the mission field holds, and the wizard hydrates from it - only when the field is empty, so a customer editing their mission is never overwritten by the stored copy. The marker recording that hydration travels with the field it describes, cleared wherever `companyGoal` is. `isExistingCompanyMissionUnresolved` holds the hire while that read is outstanding, counting an in-flight refetch over cached goals as unresolved. That is the rule #11382 settled on a day earlier for a different consumer - `isFetching`, not `isLoading`, because retained data is not an answer to the question being asked now. #11259 had it first, on 11 August. `canGoBackFromOnboardingStep` and `canJumpToOnboardingStep` bound how far back a run can walk by the step it entered on. The Back button already applied that rule inline; the progress bar applied only the "already completed" half, so a run holding a company could still jump to step 1 - the step whose job is to create one. The entry step is captured once, when the wizard opens, for the same reason the step itself is. `planMissionPersistence` came with them and turned out to be required rather than tidying. Hydration sets `createdCompanyGoalId` from the company's existing goal, and confirming the mission read that id as "already written" and skipped the write, discarding the customer's edit. That skip was safe only while the id could arrive one way - by writing. A goal in hand now means update it. Each piece was checked by removing it and confirming a specific case fails. The hydration case asserts on `saveInstructionsFile`'s content, the actual consumer, rather than on the mission textarea, because the entry path never renders that field and the navigation bound now prevents reaching it. One caveat recorded rather than smoothed over: the reopen case fails only when both marker-clears are removed, since `reset()` also clears the company id and the next introduction routes through `clearCompanyScopedState`. They are kept as one invariant rather than one guard plus a coincidence. ui typecheck clean; full ui suite 4004 pass. Two failures remain, in IssueProperties and StatusCards/format; both are date-dependent, both reproduce on master with these changes stashed, and neither file is touched here. Co-Authored-By: Jannes Stubbemann <stubbi@users.noreply.github.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>canary/v2026.815.0-canary.6 |
||
|
|
484b1f626c |
fix(onboarding): verify draft ownership against a list fetched this session (#11382)
#11370 stopped onboarding restoring a saved draft when the company list had *errored*. It still trusted the list when the list looked healthy — and the wider door was exactly that. `main.tsx` sets `staleTime: 30_000` app-wide and `Auth.tsx` invalidates rather than resets on sign-in, so `invalidateQueries` keeps serving the previous account's companies with `isLoading` false and no error at all. On a self-hosted instance, where sign-out does not reload the page, signing in as a second account in the same warm tab could restore the first account's draft. No request had to fail. The wizard now judges ownership against a list it fetched for the current session: its own `useQuery` on the shared key with `staleTime: 0`, gated so it runs only when a parseable draft exists and adds no request otherwise. Every clause of that gate earns its place, and each was verified by removing it and watching a specific case fail: - `isSuccess` ties the answer to this session. React Query retains the last good `data` when a refetch fails, so after an account switch the retained value is the previous account's list; a failed refetch flips status to error and this rejects it. - The `unauthorized` check catches the opposite error. `companiesListQueryOptions` folds 401 and 403 into `{ companies: [], unauthorized: true }` rather than throwing, so an auth blip arrives as a *successful* empty list and would otherwise read as "this account owns nothing" and delete the draft. - The mount gate keys on `isFetching`, not `isLoading`. `isLoading` is false whenever retained data exists, so a refetch over a warm cache mounted the wizard undecided — and with the wizard open, the persist effect overwrote the customer's own draft with defaults before the answer arrived. It still releases on failure, so the "Get Started" dead end stays fixed. - An unreadable draft is judged, and cleared, before any of the above, and does not enable the query at all. `isFetchedAfterMount` was in an earlier revision and is deliberately not here: it is true after a failed refetch too, so it rejects nothing `isSuccess` has not, and no test could distinguish it. Worth recording how the first defect survived a check. I fault-injected it, saw a test fail, and concluded the guard worked. It was failing for an unrelated reason — the inner wizard mounted during the fetch and locked its state initializers to defaults, so the draft could not appear whatever the gate decided. Fixing the mount gate exposed the real behaviour. An injection is only evidence if the failure it produces is the one being claimed. This narrows onboarding only. The general fault is that a sign-out leaves account-scoped caches in place, and account changes that skip the button — a session lapsing server-side, a second account in a warm tab — reach the same stale list. Tracked separately; this defence should not be removed as redundant when that lands. ui typecheck clean; full ui suite 3963 pass, with only the timezone-dependent IssueProperties failure already present on master. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>canary/v2026.815.0-canary.5 |
||
|
|
bc9f70f54c |
fix(plugin-daytona): bound the sandbox liveness calls with a per-call timeout (#11408)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Sandbox provider plugins run agent work in remote execution environments > - The Daytona sandbox liveness read can stay pending when the connection stops responding > - A pending read blocks the plugin until a broad host-to-worker limit expires > - This pull request adds bounded deadlines to Daytona liveness calls and clears stale handles > - The benefit is a fast and clear error when a Daytona connection stops responding ## Linked Issues or Issue Description Refs #11341 **What happened?** The Daytona sandbox liveness read had no per-call timeout. A silent connection failure left the read pending until the broad host-to-worker RPC limit expired. **Expected behavior** The plugin should stop a liveness call within a defined limit and report a clear timeout error. **Steps to reproduce** 1. Create a Daytona sandbox handle. 2. Make the cached handle freshness read never resolve. 3. Run the next sandbox operation. 4. Observe that the operation waits for the outer RPC limit without a liveness timeout. **Paperclip version or commit** `master` before this change. **Deployment mode** Any deployment mode that uses the Daytona sandbox provider. ## What Changed - Add `withLivenessTimeout` with timer cleanup and `SandboxLivenessTimeoutError`. - Bound `refreshData` with configurable `livenessTimeoutMs`, which defaults to 30000 milliseconds. - Bound sandbox start and recovery calls with the SDK timeout plus a 5000 millisecond margin. - Reject `livenessTimeoutMs` values above 86400000 milliseconds and document the setting. - Evict a cached handle after a failed freshness refresh so the next operation fetches a new handle. - Add a test for a never-resolving freshness refresh and the cached-handle eviction. ## Verification - Run the Daytona plugin test suite with its package Vitest configuration. - Confirm that 150 of 150 tests pass. - Confirm that the new test reports a bounded timeout and a fresh handle on the next operation. - Confirm that GitHub Actions reports green status checks after the pull request starts. ## Risks This change adds an early timeout only to Daytona liveness calls. A value of 0 or less disables the extra bound. The default leaves normal SDK calls within their expected time limit. The main risk is a timeout value that is too short for a slow but healthy connection. ## Model Used OpenAI Codex, GPT-5. The model used tool calls and code execution. The model supplied the PR handoff and did not author the code in this pull request. ## 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>canary/v2026.815.0-canary.4 |
||
|
|
fdb9a4880d |
fix(security): route paperclipai CLI guidance through safe npx form (CWE-78) (#11400)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Paperclip provides CLI commands and guidance for operators and agents > - The `pnpm paperclipai` script can pass argument values through a shell > - Shell re-parsing can execute command substitutions inside quoted values > - This pull request routes guidance through inert-argv `npx paperclipai` commands and adds regression coverage > - The benefit is safer operator guidance across documentation and runtime hints ## Linked Issues or Issue Description This pull request fixes a command-injection-class defect in Paperclip CLI guidance. **What happened?** The `pnpm paperclipai <sub> --flag "$VALUE"` form can re-parse argument values through a shell. A command substitution inside a quoted value can execute on the host. **Expected behavior** Paperclip guidance must pass CLI values as inert argument values. Host-derived values must not appear in copyable commands. **Steps to reproduce** 1. Run a Paperclip guidance command that uses the `pnpm paperclipai` script. 2. Provide a quoted value that contains a command substitution. 3. Observe that the shell can evaluate the substitution before the CLI starts. 4. Compare the result with the `npx paperclipai` form. **Paperclip version or commit** `5670984b75d109950c968542a0111ebb6967f4da` **Deployment mode** All deployment modes that show or use the affected CLI guidance. **Installation method** Built from source and installed CLI guidance. **Agent adapter(s) involved** Not adapter-specific (core bug). **Database mode** Not database-related. **Access context** Both. **Additional context** The earlier merged PR [#11343](https://github.com/paperclipai/paperclip/pull/11343) used the unsafe `pnpm exec paperclipai` form. This fresh PR replaces that guidance with the safe `npx paperclipai` form. ## What Changed - Standardize documentation and runtime hints on `npx paperclipai`. - Remove the broken `pnpm exec paperclipai` guidance. - Use a static `<host>` placeholder in private-hostname guidance. - Add regression tests for unsafe forms, continued lines, static hosts, and offline guidance. ## Verification - `git diff --check origin/master...origin/fix/paperclipai-cli-npx-safe-invocation` passes. - The branch adds `server/src/__tests__/cli-invocation-safety.test.ts` and updates private-hostname tests. - CI must run the new tests, typecheck, lint, and build checks. - Local Vitest execution was not available because this worktree has no installed Vitest binary. ## Risks - The change affects operator and agent documentation text. - The runtime hints now show `<host>` instead of a request-derived host value. - No database schema or migration changes exist. - CI will detect any missed unsafe invocation or type error. ## Model Used OpenAI GPT-5, exact model ID `gpt-5`, with tool use and code-review assistance. The model used repository inspection, Git operations, and PR preparation. ## 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] CI ran the test suites and they pass; local test execution was unavailable in this worktree - [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 addressed all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.815.0-canary.3 |
||
|
|
ea3a5ea7d2 |
fix(recovery): skip successful-run handoff for recovery-action-driven runs (#9010)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The recovery subsystem repairs issues stranded without a valid disposition: `decideSuccessfulRunHandoff` queues one corrective wake per successful-but-dispositionless run, and `source_scoped_recovery_action` wakes a recovery owner for stranded issues > - `decideSuccessfulRunHandoff` already refuses to treat corrective handoff runs, issue-monitor runs, and comment-driven wakes as handoff *sources* — but not runs woken by `source_scoped_recovery_action` > - Because the handoff idempotency key includes `sourceRunId`, every succeeding recovery run is a brand-new source: recovery run → handoff wake → corrective run → new recovery action → recovery run → … with `DEFAULT_MAX_SUCCESSFUL_RUN_HANDOFF_ATTEMPTS` never binding (it is per-source-run) and the source-scoped recovery action created with `maxAttempts: null` > - The cycle is unbounded, each leg is a ~15s no-op "succeeded" run, and the designed handoff-exhausted escalation (blocked + exhausted notice) never engages > - This PR adds recovery-action-driven runs to the existing skip list, so recovery runs own their own follow-up path and the stranded-issue escalation remains the exit when the disposition is still missing > - The benefit is that missing-disposition recovery converges (one handoff, then escalation) instead of ping-ponging wake volume unboundedly ## Linked Issues or Issue Description Refs #6523 — same wake-loop family (repeated `source_scoped_recovery_action` wakes); this PR fixes the variant where the loop partner is the successful-run handoff. **Observed behavior:** in a 16-agent deployment, one agent produced 223 runs in 2 hours, every run `succeeded` with ~15s duration, with `contextSnapshot.wakeReason` alternating exactly between `source_scoped_recovery_action` (109) and `finish_successful_run_handoff` (108). The source issue never reached the exhausted escalation. ## What Changed - `server/src/services/recovery/successful-run-handoff.ts`: new `isRecoveryActionDrivenRun` predicate (matches `contextSnapshot.wakeReason === "source_scoped_recovery_action"` or a present `contextSnapshot.recoveryActionId`), consulted in `decideSuccessfulRunHandoff` alongside the existing corrective-handoff / issue-monitor / comment-driven skip guards. - `server/src/services/recovery/successful-run-handoff.test.ts`: cases asserting recovery-driven runs are skipped via both markers. ## Verification - `pnpm -F @paperclipai/server exec vitest run src/services/recovery/successful-run-handoff.test.ts` → 17 passed (16 existing unchanged + 1 new). - Production validation (same logic deployed as a dist patch on 2026.626.0): the alternating recovery/handoff wake pattern stopped after restart; ordinary successful-run handoffs (first corrective wake per genuine source run) continue to queue. ## Risks Low-to-moderate, scoped to one decision function. The behavioral shift: a recovery-action run that succeeds without fixing the disposition no longer gets a corrective handoff wake — instead the stranded-issue detector escalates (blocked + recovery owner + exhausted notice), which per the existing `escalateStrandedAssignedIssue` code is the designed terminal path. Runs not woken by a recovery action are unaffected (covered by the existing 16 tests, all green). ## Model Used Claude Fable 5 (`claude-fable-5`), extended thinking, agentic tool use via Claude Code. Human-reviewed before submission. ## 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 mergecanary/v2026.815.0-canary.2 |
||
|
|
57edb26db4 |
Merge pull request #11405 from paperclipai/fix/review-policy-verdict-enforcement
Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
edb8083538 |
fix(server): serialize interaction review verdicts
Lock the issue before accepting or rejecting review confirmations, reauthorize against the current policy, and cover concurrent policy tightening. Co-Authored-By: Codex <noreply@openai.com> |
||
|
|
ed8075b535 |
fix(adapter-utils): order stdin file writes in the sandbox process-session bridge (#11406)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agent adapters can run through a sandbox process-session bridge > - The bridge writes streamed standard input to files before the remote process reads them > - Concurrent file writes can make a later chunk visible before an earlier chunk > - The remote process can then parse a tail fragment and wait forever for the missing head > - This pull request serializes host writes and makes an unexpected file gap a loud error > - The benefit is ordered input with a bounded failure path for sandbox ACP sessions ## Linked Issues or Issue Description No public GitHub issue exists for this change. The description below follows `.github/ISSUE_TEMPLATE/bug_report.yml`. **What happened?** A sandbox ACP process-session bridge could stall after its handshake when a run sent a large prompt. The host sent one un-awaited file write for each standard input chunk. A small later chunk could finish before a large earlier chunk. The remote poller then sent the tail bytes first. The agent parser raised an error on the tail fragment, and the head bytes stayed buffered without a newline. **Expected behavior** The bridge must expose standard input files in sequence. The remote poller must report a clear error when an earlier file remains missing beyond the retry budget. **Steps to reproduce** 1. Start an ACP session through a sandbox process-session bridge. 2. Send a prompt that produces multiple standard input file chunks. 3. Delay finalization of an earlier chunk while a later chunk completes. 4. Observe that the remote parser can receive the later chunk first and the session can stop without a clear error. **Paperclip version or commit** The change targets the current `master` branch at the submitted commit. **Deployment mode** The bug affects sandbox execution. **Agent adapter(s) involved** The failure affects the ACP process-session bridge. **Database mode** Not database-related. **Additional context** The fix keeps the existing per-file atomic write behavior. It adds ordering at the host write boundary and a bounded ordering check in the shared wrapper poll tail. ## What Changed - Add a per-session promise chain for host standard input file writes. - Keep a failed write from blocking later chain entries. - Track the next expected sequence number in the shared wrapper poll tail. - Hold later files while an earlier file is missing within the existing retry budget. - Emit a loud error and advance after the retry budget expires. - Add regression tests for host ordering, gap holding, and the loud error path. ## Verification - `npx vitest run packages/adapter-utils/src/execution-target-stdin-race.test.ts` — 9 tests passed. - `npx vitest run packages/adapter-utils/src/execution-target-sandbox.test.ts` — 43 tests passed. - `pnpm --filter @paperclipai/adapter-utils typecheck` — clean. - With the source fix reverted, the 3 new tests fail and the 6 original tests pass. - CI must pass on the pull request before merge. ## Risks Low risk. - The host now serializes writes for each session, which can reduce write parallelism. - A failed write still emits one error and destroys the socket, as before. - The wrapper can emit a loud error after the existing retry budget when a file gap persists. - The change does not alter the atomic per-file write behavior. ## Model Used OpenAI GPT-5, model ID `gpt-5`, with tool use and code execution. The context window and internal reasoning details are not disclosed. ## 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>canary/v2026.815.0-canary.1 |
||
|
|
3526b82e2b |
test(server): expect transactional review transition
Align the watchdog in-review assertion with the atomic update contract. Co-Authored-By: Codex <noreply@openai.com> |
||
|
|
277c13529a |
fix(server): persist review requester atomically
Commit both bound and unbound in-review transition activity in the same transaction as the issue update. Co-Authored-By: Codex <noreply@openai.com> |
||
|
|
3a87b143a2 |
test(server): support locked review policy updates
Keep terminal-update route harnesses aligned with the transactional issue service contract. Co-Authored-By: Codex <noreply@openai.com> |
||
|
|
6f26f2a450 |
fix(adapter-utils): add per-iteration timeout and watchdog to the sandbox callback bridge poll loop (#11341)
## Thinking Path > - Paperclip runs AI agents through adapters and sandboxed execution paths > - The sandbox callback bridge carries file requests between the host and a sandbox > - The poll loop waited forever when a sandbox call stopped responding > - A permanent wait stranded queued requests and hid the run failure > - This pull request adds bounded timeouts, abort handling, recovery backstops, and trace reporting > - The benefit is prompt request failure, safe mutation outcomes, run-level error reporting, and trace visibility ## Linked Issues or Issue Description **What happened?** The sandbox callback bridge could wait forever when a client call stopped responding without a rejection. **Expected behavior** The bridge should fail queued requests and report a run-level error when the sandbox channel stops responding. **Steps to reproduce** 1. Start a sandbox callback bridge. 2. Queue a request. 3. Make the sandbox call stop responding. 4. Observe that the request does not receive a failure response. **Paperclip version or commit** Commit `edc4f71b460c600f97cf44cb486d5cac72ca2db9`. **Deployment mode** Built from source. **Installation method** Built from source with pnpm. **Agent adapter(s) involved** Custom or external sandbox callback bridge. **Database mode** Not database-related. ## What Changed - Add a per-iteration timeout for `listJsonFiles` and `processRequestFile`. - Add a watchdog that fails pending requests when the loop makes no progress. - Abort a hung handler and use a non-retryable 504 backstop when its outcome can be indeterminate. - Retry recovery writes and keep queued requests when a recovery write fails. - Forward the indeterminate-outcome header through the execution target. - Record worker failures through the `sandbox.callbackBridge.workerFailed` trace span. - Add tests for timeout, watchdog, recovery, mutation safety, header forwarding, and fast-request behavior. ## Verification - Run `pnpm exec vitest run packages/adapter-utils/src/sandbox-callback-bridge.test.ts packages/adapter-utils/src/execution-target-sandbox.test.ts`. - Confirm that the PR test, typecheck, build, end-to-end, serialized test, and security checks pass. - Confirm that the current PR head is `edc4f71b460c600f97cf44cb486d5cac72ca2db9`. - Confirm that the PR changes four files: the callback bridge, its tests, the execution target, and its tests. ## Risks The default timeout can fail a slow but valid sandbox call. The defaults remain configurable, and the iteration timeout stays below the sandbox response deadline. A mutation that may have committed returns a non-retryable 504 outcome so the caller does not apply it twice. ## Model Used OpenAI Codex, GPT-5 current runtime, with extended reasoning and tool use. The exact context window is not exposed by the runtime. ## 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>canary/v2026.815.0-canary.0 |
||
|
|
991f40bb2e |
fix(server): serialize review policy verdict authorization
Recheck terminal verdict and policy mutations under a row lock, and scope interaction verdict enforcement to the review confirmation itself. Co-Authored-By: Codex <noreply@openai.com> |
||
|
|
373b675f94 |
fix(server): prevent review policy verdict downgrade bypass
Authorize verdicts and policy changes against the stored restrictive review policy, remove downgrade guidance, and cover both restrictive policies with route regressions. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
37fde84abd |
fix(server): enforce review policy on interaction verdicts
Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
8ee1fb21a6 |
feat(ui): badge the review policy when it constrains approval (#10938)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents move their work into review, and a reviewer must then give a verdict on it > - By default anyone with write access can give that verdict, including the agent that did the work > - The server can constrain that default per issue with a `reviewPolicy` column, but no screen showed the value > - A reviewer could therefore press Approve on a review that the server refuses, and get a 403 > - This pull request shows the policy as a badge on the two surfaces where a person gives a verdict > - It also makes an agent verdict read as a verdict in the activity timeline > - The benefit is that a reviewer sees who can approve before they try ## Linked Issues or Issue Description No public GitHub issue exists for this change. The description below follows `.github/ISSUE_TEMPLATE/enhancement.yml`. **What existing behavior does this improve?** The issue review flow. A reviewer cannot see the approval constraint on an issue before they give a verdict. **Subsystem affected** Web UI (`ui/`), with one supporting change in the server attention service. **Current behavior** The server stores an optional approval constraint for each issue in a `reviewPolicy` column. The column has three meaningful states: the default (`NULL` or `anyone`), `not_creator`, and `human_only`. The server enforces the constraint when it receives a verdict. No screen shows the value. Two problems follow: 1. A reviewer presses Approve on a review that the server refuses. The server answers 403, and the reason is not visible on the card. 2. An agent that accepts or rejects a review renders in the activity timeline as the raw action id, for example "issue thread interaction accepted". A person who reads the timeline cannot tell that a verdict was given. **Proposed behavior** Show the constraint as a read-only badge on the two surfaces where a person gives a verdict. Show no pixels for the default state, because the default is what every issue already does. Make an agent verdict read as a verdict in the timeline. Only agents set the column today, so this change adds no control to set it. **Reason and benefit** A reviewer sees the constraint before they act. This prevents the 403, and it removes the need to explain the 403 afterwards. The timeline also becomes complete, because it now shows agent verdicts and human verdicts in the same way. **Breaking changes** None. The change adds a badge and changes copy. It adds no column, no endpoint, and no request. **Additional context** The server-side column and the verdict enforcement landed earlier in #10931. This pull request is the user interface for that column. The default state stays unchanged on screen, so the badge appears on a small number of issues. ## What Changed - **A read-only "Approvals" row** in the issue Execution properties. The row renders *only* for a constrained policy: "Anyone else" (`not_creator`) or "Human only" (`human_only`). A `NULL` or `anyone` column adds no row, so the panel is untouched on the overwhelming majority of issues. - **The same badge on the stalled-review card** in `/decisions`, above the three review verbs. A reviewer now sees the constraint before they press Approve. The condition is the same, so the default card is unchanged. - **Agent verdicts read as verdicts in the activity timeline.** An agent that accepted or rejected a review request previously rendered the raw action id ("issue thread interaction accepted"). It now reads "approved the request". A stalled-review decision names the verb that the actor chose. - **A cleared policy reports as "anyone", not "none",** in the field-change receipt. The `reviewPolicy` column is nullable by default, so an absent value is a real setting rather than a missing one. - **All copy comes from `ui/src/lib/review-policy.ts`.** Its badge lookup returns `null` for the default. This makes "no pixels for the default" one enforced decision instead of a condition repeated at each call site. It also keeps the badge, the activity line, and the receipt reading alike. - **The server attention service carries the policy** on the review attention subject, so the stalled-review card can read it. ## Verification Automated tests: - `ui/src/lib/review-policy.test.ts` — the default returns no badge, however the column spells it (`null`, `undefined`, `"anyone"`). An unrecognised policy from the wire shows nothing rather than leaking an enum value. - `ui/src/components/AttentionQueueRow.test.tsx` — no badge on the default card, and the verbs still render. Suppression of the badge must not suppress the card. - `ui/src/components/IssueProperties.test.tsx` — no Approvals row on the default. The constrained row contains no `button`, so nothing there can PATCH. - `server/src/__tests__/attention-service.test.ts` — the review attention subject carries the policy, and subjects built from narrower selects do not claim one. Run them with: ```sh pnpm vitest run ui/src/lib/review-policy.test.ts \ ui/src/components/AttentionQueueRow.test.tsx \ ui/src/components/IssueProperties.test.tsx \ server/src/__tests__/attention-service.test.ts ``` Manual steps: 1. Open an issue that has no `reviewPolicy`. Confirm that the Execution properties panel shows no Approvals row. 2. Set the column to `not_creator`. Reload the issue. Confirm that the Approvals row reads "Anyone else", and that the row has no control. 3. Move that issue into review. Open `/decisions`. Confirm that the stalled review card shows the same badge above the review verbs. 4. Let an agent approve the review. Confirm that the activity timeline reads "approved the request" and not "issue thread interaction accepted". Screenshots were captured at 1440x900 and 390x844, in light mode and dark mode, with the three policy states side by side. The leftmost column in each capture is the default. It carries no badge and no extra row. ## Risks Low risk. - The change is additive on screen. Every new surface is behind a constrained policy, so the default path renders exactly as before. - The badge is read-only. It has no control and sends no request, and a test asserts that the row contains no `button`. - An unknown policy value from the wire renders nothing. It does not render the raw enum. - No migration, no schema change, and no endpoint change. ## Model Used Claude Opus 5 (Anthropic), model id `claude-opus-5[1m]`, 1M context window, with extended thinking and tool use enabled. Used through Claude Code for the implementation, the tests, and this description. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
69027cbaae |
fix(workspaces): reopen archived git worktree for managed_checkout projects (#11395)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Execution workspaces give agent tasks isolated Git worktrees > - Archived isolated workspaces must reopen against a live project checkout > - A managed_checkout project has no project workspace directory in its row > - The reopen path used the removed archived worktree as the Git working directory > - This pull request resolves the live managed checkout and reports a clear error when it is unavailable > - The benefit is reliable workspace reopen behavior after archive cleanup ## Linked Issues or Issue Description Related public pull request: [#6164](https://github.com/paperclipai/paperclip/pull/6164) clears archive state during un-archive. This pull request fixes the separate reopen failure that occurs after archive cleanup. **What happened?** An archived isolated `git_worktree` workspace under a `managed_checkout` project failed to reopen after cleanup. The route attempted to run Git in the removed archived worktree and returned a generic service error. **Expected behavior** The reopen path should use the live managed checkout as the Git base directory and should return a clear error when that directory is unavailable. **Steps to reproduce** 1. Create a project with `managed_checkout` source control. 2. Create and archive an isolated `git_worktree` execution workspace. 3. Let archive cleanup remove the worktree. 4. Reopen the workspace for an issue. **Paperclip version or commit** `cab0c31dc61310106caef42ca244e9f7b0f19460` **Deployment mode** Local dev with the default embedded database. **Agent adapter(s) involved** Not adapter-specific. This issue affects core workspace handling. ## What Changed - Resolve the live managed checkout when a managed project reopens an archived Git worktree. - Keep local-folder projects on their project workspace directory. - Validate the Git base directory before `git rev-parse` and return a scrubbed error. - Add nine regression tests for workspace reopen behavior. ## Verification - `server` TypeScript check passes with `tsc --noEmit`. - `server/src/__tests__/execution-workspace-reopen.test.ts` passes with 9 tests. - GitHub Actions must pass all required PR checks. ## Risks Low risk. The change affects only archived isolated workspace reopen behavior. It reuses the existing managed checkout and Git authentication helpers. It adds no new credential path, endpoint, or telemetry. ## Model Used OpenAI GPT-5 assisted with review and GitHub operations. The implementation author supplied the code and test results. ## 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>canary/v2026.814.0-canary.7 |
||
|
|
35a9b98733 |
fix(ui): theme the onboarding wizard decorative panel instead of hardcoding dark (#11379)
The onboarding wizard's decorative right-hand panel, which holds the ASCII paperclip illustration, hardcoded a near-black surface. `ThemeContext` supports light and dark and follows `prefers-color-scheme`, so in light mode a new customer met the product as a pale form beside a solid black rectangle — on the one screen meant to introduce it. The glyphs inside already used `text-muted-foreground`, so the panel was the only part ignoring the theme. It now uses the paired `bg-muted` surface, which is defined in both themes (`oklch(0.97 0 0)` light, `oklch(0.269 0 0)` dark), so the illustration reads as ink on a surface either way and follows any future theme without another fix. The guard for it asserts the complete set of `bg-` classes on the panel is `["bg-muted"]`, anchored to the `<AsciiArtAnimation />` wrapper rather than scanning the file. Forbidding specific spellings is what failed here originally: the first version checked `bg-[#rrggbb]` and silently stopped guarding anything once master migrated the class to `bg-(--hex-1d1d1d)`. Naming what is allowed cannot decay that way, and it catches named colours like `bg-black` that no spelling list covered. Lands the work from #8982 by @stubbi, whose two commits are included unchanged with their authorship. The rebase and the guard are mine. Tested: ui typecheck clean; both theme cases fail against four spellings of the regression — `bg-(--hex-1d1d1d)`, `bg-[#1d1d1d]`, `bg-black`, `bg-zinc-900` — where the original caught only one and my first widening caught two. Full ui suite 3958 pass, with one timezone-dependent IssueProperties failure present on master. All CI gates green; Greptile 5/5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>canary/v2026.814.0-canary.6 |
||
|
|
a53cc8819b |
fix(claude-local): pipe print prompt via stdin (#9500)
Fixes #2444. Refs #4947. The `claude_local` adapter launched Claude Code as `claude --print - --output-format stream-json --verbose`. Paperclip writes the rendered task prompt to Claude's stdin, but current Claude Code releases can treat the stale `-` positional marker as the prompt itself, so Claude received the literal string `"-"` instead of the issue body. The customer's task ran against no content at all. The fix keeps `--print` mode and stdin delivery, and removes the stale `-`. Adds regression coverage on both sides of the delivery path: a `claude_local` assertion that `--print` is present, `"-"` is absent and the prompt still reaches stdin, and an adapter-utils case proving the sandbox run-log command wrapper preserves stdin while streaming logs. Authored by @elJayAdvisor, whose commit is included unchanged with their authorship. The branch had gone stale and was showing CONFLICTING; the conflict was in `execution-target-sandbox.test.ts`, where their new test was added at the same point as master's `creates the process session directories only in the launch exec` case and git interleaved the two into one hunk. Resolved by taking master's file and re-inserting their test whole, after checking every helper it needs still exists there. Verified: the bug was still live on master at `execute.ts:838`; the regression test genuinely catches it — restoring the stale `-` fails `expect(captured.argv).not.toContain("-")`; `@paperclipai/adapter-claude-local` and `@paperclipai/adapter-utils` typecheck clean; 67 pass across the two test files. All CI gates green; Greptile 5/5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8cb0ce0de5 |
fix(ui): restore queued message interrupt action (#11374)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The task thread lets an operator add guidance while an agent run is active > - A new message can wait behind that active run as a queued message > - The classic task view lets the operator interrupt the target run from that queued message > - The redesigned task view did not expose the same action > - This pull request restores the action and keeps it bound to the exact target run > - The benefit is that operators can apply urgent guidance without switching task views ## Linked Issues or Issue Description **What happened?** The redesigned task view showed `Queued` for a queued operator message, but it did not show the existing interrupt action. **Expected behavior** The queued message must show `Interrupt` next to `Queued`. The action must stop the exact run that the message is waiting behind. **Steps to reproduce** 1. Open a task in the redesigned task view while an agent run is active. 2. Send a new operator message so it enters the queued state. 3. Observe that the queued message has no interrupt action. **Paperclip version or commit** `bc0b5a1642` **Deployment mode** All deployment modes that use the redesigned task view. ## What Changed - Preserve persisted queued state and the target run ID in the redesigned thread model. - Render a token-compliant `Interrupt` action beside the queued state. - Reuse the existing exact-run interrupt callback and show a disabled `Interrupting…` state during the request. - Keep an assigned queue target immutable so an in-flight comment cannot rebind its interrupt action to a replacement run. - Add regression tests for persisted queued messages, replacement-run races, and the in-progress action state. - No documentation update was required because this restores existing behavior. ## Verification - `pnpm exec vitest run ui/src/components/TaskChatThread.test.tsx` - `pnpm exec vitest run ui/src/components/TaskChatThread.test.tsx ui/src/components/task-chat/task-chat-adapter.test.ts ui/src/pages/IssueDetail.test.tsx -t 'queued message actions|queues messages against a queued live run and interrupts that exact run|commentsToTaskChatItems'` - `pnpm exec vitest run ui/src/pages/IssueDetail.test.tsx -t 'queued message|queues messages'` - `pnpm exec vitest run ui/src/pages/IssueDetail.test.tsx` (52 passed) - `pnpm -r typecheck` - `pnpm test:run` (all server and UI groups passed; the CLI group passed after inherited static AWS credential variables were omitted) - `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm exec vitest run cli/src/__tests__/secrets.test.ts` - `pnpm build` - `pnpm check:token-gates` ## Risks Low risk. The change only adds an action to queued messages that have a target run and an interrupt callback. Messages without both values keep the current rendering. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used OpenAI Codex with GPT-5. The runtime does not expose a more specific deployment ID or context-window size. The model used reasoning, repository tools, code execution, and 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 - [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>canary/v2026.814.0-canary.5 |
||
|
|
66515582e4 |
fix(onboarding): do not restore wizard state for a company the user does not own (#11370)
Onboarding persists a draft to `localStorage`, including `createdCompanyId`. That key is scoped per browser origin, not per account, so a browser that has already run onboarding hands the stored company id to the next session on the same origin — whoever is signed in. Every downstream call then targets a company that account may not own: goals, agents and issues are created there, and requests fail with authorization errors. `restoreOnboardingState` now returns a saved draft only when the signed-in account owns the company it names. Otherwise the draft is discarded and the stale blob removed. The wizard splits into a gate and an inner component because the inner one has ~20 `useState(saved?.x ?? default)` initializers, and an initializer runs only on the first render. Mounting before the restored draft is final locks every field to its default with no way back, so the gate waits for the company list while it is loading. Ownership is judged only against a list that actually answered. Any company query error makes it undecidable, whatever the list contains — the companies cache is not account-scoped and survives sign-out, so a failed refetch after an account switch can leave the previous account's companies in hand, and trusting a non-empty list there would hand one account's draft to the next. Nothing is restored and nothing is deleted in that state; the next successful load decides. Judging the draft and mounting the wizard are separate questions. The gate withholds the wizard only while the list is *loading*, never on error: the companies query sets `retry: false`, and with no companies the dashboard offers a "Get Started" button wired to onboarding, so blocking there would make that button do nothing at all. Mounting costs the draft nothing, because the persist effect is itself gated on the wizard being open. All four draft-storage call sites — read, write, cleanup, reset — go through one guarded helper. Storage access throws outright where a browser denies it, and each site sits in a render, an effect or a close handler, so an escaping exception took down something the customer was using. Lands the work from #9900 by @stubbi, whose two commits are included unchanged with their authorship. The rebase, the error-path handling and the storage guards are mine. Follow-up filed separately: sign-out should remove account-scoped cached data rather than invalidating two keys. This change is defensive and protects onboarding only. Tested: ui typecheck clean; 73 pass across the seven onboarding suites; full ui suite 3952 pass, with one timezone-dependent IssueProperties failure present on master in a file this does not touch. All CI gates green; Greptile 5/5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>canary/v2026.814.0-canary.4 |
||
|
|
bc0b5a1642 |
fix(ui): onboarding wizard keeps an invisible disabled adapter selected (#11371)
The wizard defaults `adapterType` to `claude_local`, and a saved draft can name any adapter. The grid only renders adapters the server has enabled, so on an instance where the held adapter is disabled nothing appears selected while the wizard still holds it — and the first agent is hired on an adapter the deployer turned off, which can never acquire a lease. The selection now snaps to the first enabled, non-coming-soon adapter whenever the held one is not visible, and adapter-specific model defaults follow it. The snap waits for the adapter registry to load. External adapter types are registered into the UI registry only once the adapters query resolves, so before that a saved external adapter is indistinguishable from a disabled one — snapping on that transient list would replace the customer's choice with a built-in and the persist effect would write it down. This gate fails closed, unlike the fail-open gates in onboarding, because the directions of harm are opposite: acting early silently rewrites a saved answer, while waiting merely leaves the selection alone, which is the behaviour that existed before the snap did. The test file is named `OnboardingWizard.adapters.test.tsx` rather than `OnboardingWizard.test.tsx`, which is the name #11370 uses for its restore gate. Both merged cleanly onto master alone but collided with each other on add/add, and nothing in either status showed it. Lands the work from #9900's sibling, #9501, by @stubbi, whose commit is included unchanged with their authorship. The rename and the registry gate are mine. Tested: ui typecheck clean; 51 pass across the adapter, hook, dialog, config-form and wizard-step suites, including the other callers of the adapter hook since that module changed. All CI gates green; Greptile 5/5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>canary/v2026.814.0-canary.3 |
||
|
|
d2665ff6b4 |
fix(ui): align the mobile task chat composer with the thread (#11296)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The task thread is where people read work and guide agents. > - The mobile composer should use the same content width as the thread. > - The composer kept the desktop 80% width on mobile, so its edges did not align with the thread. > - Long assignee-aware placeholder text could also clip inside the mobile editor. > - Some extracted style tokens used legacy HSL wrappers around complete semantic colors, which made those declarations invalid. > - This pull request makes the composer full width on mobile, preserves the narrower desktop layout, wraps the placeholder, and repairs the invalid color compositions. > - The benefit is a stable mobile composer that aligns with the task thread and keeps its intended visual styles. ## Linked Issues or Issue Description Related work: Refs #11263. **What happened?** At mobile widths, the task chat composer used the same 80% width as the desktop composer. Its horizontal edges did not align with the full task thread. A long assignee-aware placeholder could clip on one line. The composer's extracted shadow also used a legacy `hsl(var(...))` wrapper around complete semantic color values, so the browser could reject the declaration. **Expected behavior** The composer must match the task thread width on mobile. It must stay narrower on larger screens. Long placeholder text must wrap inside the editor. Semantic color tokens must form valid shadows and gradients. **Steps to reproduce** 1. Open a task with the chat-style thread on a mobile viewport. 2. Compare the composer edges with the task thread edges. 3. Select an assignee whose placeholder text wraps to two lines. 4. Inspect the computed composer shadow and the extracted semantic color styles. **Paperclip version or commit** The change is based on `dc6fcd1ff1` from `master`. **Deployment mode** Local build from source. The behavior also applies to packaged web builds. ## What Changed - Made the task chat composer full width below the medium breakpoint and kept the 80% desktop width. - Matched the composer dock padding to the task thread padding. - Allowed long composer placeholders to wrap and reserved enough mobile editor height for two lines. - Replaced invalid legacy HSL wrappers around full semantic colors in extracted shadows, gradients, and approval styles. - Added a token gate that prevents legacy `hsl(var(--token))` wrappers from returning. - Added focused regression tests for responsive width, padding, placeholder wrapping, mobile height, and semantic shadow validity. ## Verification - `pnpm check:token-gates` — all four gates pass. - `pnpm --filter @paperclipai/ui exec vitest run src/components/TaskChatThread.test.tsx src/components/task-chat/TaskChatComposer.test.tsx src/components/task-chat/TaskChatComposerStyles.test.ts` — 37 tests pass. - `pnpm --filter @paperclipai/ui typecheck` — passes. - `pnpm --filter @paperclipai/ui build` — passes. The build prints existing CSS optimizer and bundle-size warnings. ## Risks - Low risk. The width change is limited to the mobile breakpoint. The desktop 80% layout remains in place. - The semantic token fixes can affect shadows and gradients that were previously invalid. The new gate prevents the invalid wrapper pattern from returning. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - OpenAI Codex with `gpt-5.6-sol`. The context-window size is not exposed in this environment. The model used reasoning, repository tools, code execution, and GitHub tools. ## 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>canary/v2026.814.0-canary.2 |
||
|
|
d95340b0b8 |
feat(ui): send a company with no agent into onboarding, at the right step (#11352)
A company with no agent cannot do anything: no runs, no tasks, nothing to show. The dashboard says so in a banner with a link, which asks the customer to notice a problem the product can fix for them. It is worse for a company created by Paperclip Cloud: Cloud creates the company before the tenant boots, so the companyless redirect never runs, and the customer arrives on an empty dashboard straight out of a signup flow that already asked for a mission. The dashboard now opens onboarding when the agent list has loaded and is empty, and onboarding opens on the agent step when the company already has its mission — read from the company-level goal the seed writes, under the query key the launch path already uses, so it shares a cache entry rather than adding a fetch. The step is decided once. `initialStep` is derived from the company list and the goal list, so it changes on any retry, refetch or cache invalidation. An effect that took it as a dependency called `setStep` on every one of those and moved a customer who was already mid-flow. Gating the input only narrowed that window; it could not close it. The step now belongs to the request that opened the wizard: the effect reads it through a ref and is keyed on the wizard opening or the company changing. `createdCompanyIdRef` beside it already used this pattern for the same reason. That exposed a path nothing had ever taken. A company reached the mission step only by creating itself on step 1, so opening an existing company there found code that had never run: `companyName` is only typed on step 1, and both ways forward require it, so the step could not be completed at all; and confirming advanced without writing anything, so the mission the customer typed was discarded. Both fixed, and the write now reconciles against the goal list rather than adding a second company goal, since the mission lookup fails open and can send a company that has one back to that step. Company-scoped state now stays with its company. `clearCompanyScopedState` runs when the route replaces a company and when it withdraws one — the same event, and clearing half of it left a goal id that made the next company skip a mission it had never given. `stillTheSameCompany` guards all five async writes, after the server work rather than before it, so a company switch mid-flight cannot hand the new company the old one's goal, project, issue or agent, and cannot leave a hired agent without its instructions file. The keyboard path honours `loading` like every button already did. `claimOnboardingOffer` makes onboarding an offer that stays declined for the visit. Route ownership is now recorded whenever the route names a company, including one the wizard already holds. This changes a documented rule deliberately: without it a self-created company was never withdrawn, so `/onboarding` would show "create a company" while still holding the previous one and write the customer's new mission into it. Tested at the seam, because every defect on this branch lived between a value and its consumer and the predicate tests passed at every stage. `OnboardingWizard.step.test.tsx` renders the real wizard against the real resolver and the real mission hook across 18 cases, and each was fault-injected against the code it replaces rather than trusted on a green run. That caught a case that passed against the broken code, and a race in one of the guards. ui typecheck clean; full ui suite 3923 pass, with one timezone-dependent IssueProperties failure present on this branch's base in a file this change does not touch. All CI gates green; Greptile 5/5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>canary/v2026.814.0-canary.1 |
||
|
|
dc6fcd1ff1 |
fix(ui): move agent secret access to searchable secrets tab (#11283)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The agent configuration UI controls each agent and its allowed secrets. > - The environment variable editor already has a secret selector with search and folder navigation. > - The secret access editor used a basic list and made large secret stores hard to use. > - The secret access controls also occupied the main Configuration tab. > - This pull request reuses the rich selector and moves secret access to a dedicated Secrets tab. > - The benefit is one consistent secret selection workflow with clearer agent configuration navigation. ## Linked Issues or Issue Description **What existing behavior does this improve?** The agent detail configuration view and its secret access editor. **Subsystem affected** `ui/` — React and Vite board UI. **Current behavior** The secret access editor uses a basic select control. It does not provide the search and folder navigation available in the environment variable editor. The editor also appears inside the Configuration tab. **Proposed behavior** The secret access editor uses the shared secret picker. Users can search secrets and browse slash-delimited folders. Agent details provide a dedicated Secrets tab for this editor. **Reason and benefit** Large secret stores are slow to scan in a flat list. Reusing one selector reduces UI differences and makes scoped secret access easier to manage. **Breaking changes** None. The API and saved secret access data do not change. ## What Changed - Reused the environment variable secret picker in the agent secret access editor. - Preserved secret version selection and the create-secret action, including nested-popover focus handling. - Added a route-backed Secrets tab to agent details and removed secret access controls from Configuration. - Guarded unsaved configuration across tab, link, browser-history, and action-triggered navigation. - Rechecked dirty state when navigation-producing agent actions finish, covering edits made while a request is pending. - Added component, page, and Storybook coverage for the workflow. ## Verification - `pnpm --filter @paperclipai/ui exec vitest run src/components/AgentActionButtons.test.tsx src/components/AgentConfigForm.render.test.tsx src/components/AgentSecretAccessEditor.test.tsx src/components/environment-variables-editor/EnvironmentVariablesEditor.test.tsx src/pages/AgentDetail.progress.test.ts` — 82 tests passed. - `pnpm --filter @paperclipai/ui typecheck` - `pnpm check:token-gates` - All GitHub PR checks passed on `5209c5b787`, including build, canary, general and serialized tests, and all three e2e shards. - Greptile completed at 5/5 with zero unresolved review threads. ## Risks - Low risk. The API and persisted binding format are unchanged; this changes agent configuration navigation and secret selection UI. - Dirty-state guards now cover direct navigation, Back/Forward history, and navigation-producing agent actions, including pending-request races. - Tests cover tab separation, secret access updates, search, folder navigation, focus restoration, and navigation rejection. - No documentation change is required because commands, contracts, and setup steps do not change. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used OpenAI Codex with GPT-5. This runtime did not expose a more specific model ID or context window. The model used agentic reasoning, 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> |
||
|
|
aac6ce82e1 |
fix(ui): read the onboarding company prefix from the path, not the route match (#11351)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - New users meet the product through an onboarding wizard that creates
their company, their first agent, and a starter task
> - The wizard also serves an existing company, at
`/{PREFIX}/onboarding`, to add another agent to it
> - On that route the wizard ignores the company in the URL and opens at
"create a company" instead
> - It reads the prefix with `useParams()`, but it renders beside
`<Routes>` rather than inside it, so there is no route match to read
> - This pull request reads the prefix from the pathname, which is
available without a match
> - The benefit is that the URL a user follows decides what the wizard
asks them
## Linked Issues or Issue Description
No public issue exists for this. The problem follows.
**What happened?**
Open `/{PREFIX}/onboarding` for a company that already exists. The
wizard opens at step 1 and asks the user to create a company. The
company named in the URL is ignored.
**Expected behavior**
The wizard recognises the company in the URL and opens at step 2, so the
user adds an agent to that company instead of creating a second one.
**Steps to reproduce**
1. Create a company, so it has an issue prefix.
2. Go to `/{PREFIX}/onboarding`.
3. Read the first screen. It asks for a company name.
**Paperclip version or commit**
`master` at `5ca7b4c1f`.
**Deployment mode**
Any. This is client-side routing and does not depend on the server.
## What Changed
- `ui/src/lib/onboarding-route.ts` — adds
`companyPrefixFromOnboardingPath()`, which reads the prefix from the
pathname.
- `ui/src/components/OnboardingWizard.tsx` — uses that value when the
route match supplies none. One line, plus the import.
- `ui/src/lib/onboarding-route.test.ts` — six cases for the new
function.
`OnboardingWizard` renders beside `<Routes>` in `App.tsx`, so
`useParams()` returns nothing and `companyPrefix` was always
`undefined`. `resolveRouteOnboardingOptions` then took its no-prefix
branch every time. `useLocation()` needs only the router, not a match,
and the wizard already calls it.
The route match is still read first. If the wizard later moves inside
the route tree, this code does not need to change.
The new parser accepts the same shape as `isOnboardingPath()`: the
prefix is the first of exactly two segments. One test asserts the two
agree, because a disagreement would either open the wizard where no
company resolves, or resolve a company where onboarding is not served.
### Why the change is this small
Three pull requests are open against `OnboardingWizard.tsx` — #9900,
#9501 and #8982. A larger change there would collide with all three.
Almost all of this lands in `onboarding-route.ts`, a small file of pure
functions with existing tests.
## Verification
- `npx tsc --noEmit -p ui/tsconfig.json` — clean.
- `npx vitest run ui/src/lib/onboarding-route.test.ts` — 18 pass.
- `npx vitest run ui/src` — 3883 pass, 445 files.
One test shows the defect and the fix together. With `companyPrefix:
undefined`, which is what the wizard supplied before,
`resolveRouteOnboardingOptions` returns `{ initialStep: 1 }`. With the
parsed prefix it returns `{ initialStep: 2, companyId: "c1" }`.
**Pre-existing failures, unrelated:** `IssueProperties.test.tsx` and
`StatusCards/format.test.ts` fail on clean `origin/master` with these
changes stashed. Both look date-dependent.
**Not done:** no manual browser check. The behaviour is covered by unit
tests at the function boundary, and the wizard's own suite passes.
## Risks
Low. The route match is still preferred, so behaviour changes only where
`useParams()` gave nothing — which today is every render of this
component.
The parser returns a prefix only for a two-segment path ending in
`onboarding`, so no other route can start matching. An unknown prefix
already falls back to step 1 in `resolveRouteOnboardingOptions`, and
that path is unchanged.
To revert, remove the fallback in the wizard. The new function has no
other caller.
## Model Used
Claude Opus 5 (`claude-opus-5`), through Claude Code. Extended thinking
enabled. Tool use enabled: file read and edit, shell command execution
for typecheck and the test runs, and the 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
- [x] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.814.0-canary.0
|
||
|
|
5ca7b4c1fe |
fix(security): standardize paperclipai CLI guidance on safe npx path (#11343)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Paperclip provides CLI guidance to agents and operators through documentation and runtime messages. > - Content-bearing `pnpm paperclipai` examples send arguments through a shell. > - Shell evaluation can execute command substitutions in untrusted argument content. > - Runtime hostname guidance can also place request-derived content inside a shell command. > - This pull request uses `npx paperclipai` for content-bearing guidance and uses a static hostname placeholder. > - The benefit is safer copy-paste guidance for agents and operators. ## Linked Issues or Issue Description **Issue type** Incorrect information **Where is the issue?** CLI guidance in `doc/CLI.md`, `skills/paperclip/SKILL.md`, documentation, and runtime-generated hints. **What's wrong?** Content-bearing `pnpm paperclipai` commands can pass argument text through `/bin/sh`. Shell command substitution in an argument can execute before the CLI receives the value. **Suggested fix** Use `npx paperclipai` for content-bearing commands. Use a static `<host>` placeholder when runtime guidance displays the allowed-hostname command. ## What Changed - Replace content-bearing `pnpm paperclipai` examples with `npx paperclipai` across the documentation and agent-facing guidance. - Update runtime-generated CLI hints to use a static `<host>` placeholder. - Add safety notes to `doc/CLI.md` and `skills/paperclip/SKILL.md`. - Add scans and regression tests for unsafe invocation and hostile hostname headers. - Keep fixed lifecycle commands and `pnpm --filter @paperclipai/*` build commands unchanged. ## Verification - Run `tsc --noEmit` for the changed server files. - Run `cli-invocation-safety.test.ts`. - Run `private-hostname-guard.test.ts`. - Confirm that hostile hostname headers do not enter shown shell commands. - Confirm that the three commits contain the required Paperclip co-author trailer. ## Risks - This change updates documentation and diagnostic text across many surfaces. - Fixed lifecycle and setup commands remain unchanged. - The tests fail if content-bearing `pnpm paperclipai` guidance returns. - The change does not alter the CLI argument parser. ## Model Used OpenAI Codex, GPT-5, tool use, code execution, and repository review assistance. ## 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>canary/v2026.813.0-canary.18 |
||
|
|
05d58cd884 |
fix(tool-gateway): keep unsigned ask-first requests out of the review queue without cancelling them (#11338)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The tool gateway creates approval requests and the review queue reads them > - The gateway creates a request row before it adds the signature > - A review-queue read can see the row during that short unsigned state > - The old read path cancels the unsigned row, so approval returns `409 action_not_pending` > - This pull request hides unsigned in-flight rows and keeps them pending until signing finishes > - The benefit is that approval succeeds while invalid signed requests remain cancelled ## Linked Issues or Issue Description **What happened?** A review-queue read cancelled a pending tool action request when the request had no signature yet. The next approval call returned `409 action_not_pending`. **Expected behavior** The review queue must hide an unsigned in-flight request and keep its state as `pending`. A request with an invalid signature must remain cancelled. **Steps to reproduce** 1. Create a require-approval tool action request. 2. Read the review queue while the request signature is still null. 3. Approve the request after the creator adds the signature. 4. Observe that the old code cancels the request and the approval call fails. **Paperclip version or commit** Commit `720aa0a494bbaa1711bc7a3d795f810765915bfe`. **Deployment mode** Local dev with the embedded PGlite database. **Installation method** Built from source with pnpm. **Agent adapter(s) involved** Not adapter-specific. This is a core tool access service bug. **Database mode** Embedded PGlite. **Access context** Board and agent tool approval flow. ## What Changed - Keep a pending request with a null signature out of `listActionRequests` results. - Cancel a request when its non-null signature fails verification. - Add a permanent regression test for the unsigned request transition. - Update the contract test for unsigned and invalid-signature requests. ## Verification - Run the tool access service, tool gateway service, tool gateway, and tool access policy service tests. - Confirm 227 tests pass. - Run the `@mcp-runnable` Playwright end-to-end suite in CI. - Run the US-9 loop 30 times in CI. ## Risks The change alters review-queue filtering for unsigned requests. A null signature now means that signing remains in progress. Invalid signed requests keep the existing cancellation behavior. The change has no database migration. ## Model Used OpenAI Codex, GPT-5, with tool use and code execution. The model reviewed the handoff, repository rules, and pull request state. The implementation author supplied the code and 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] 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>canary/v2026.813.0-canary.17 |
||
|
|
0819cac4c6 |
feat(secrets): add agent-readable /secrets/catalog endpoint (#9530)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents can be configured with env bindings that reference company
secrets — they specify which secret by UUID in `adapterConfig.env`
> - But there is no API endpoint agents can call to look up a secret
UUID by name — `GET /companies/:companyId/secrets` is board-only, and
the internal `secrets.resolve` handler only accepts UUIDs
> - So when an agent needs to wire a new secret (e.g. an API key for a
new skill), it has no way to discover the UUID from a known name like
`HOMEBOX_API_KEY` — the user must find it by inspecting browser network
traffic
> - The fix is a read-only catalog endpoint that agents can call to get
the `id`/`name`/`key`/`status` mapping — no values, no provider config —
just enough to resolve a name to a UUID
> - This PR adds `GET /companies/:companyId/secrets/catalog`, guarded by
`assertBoardOrAgent` + `assertCompanyAccess`, so agents can discover the
UUID they need without board-level access and without any secret value
being exposed
## Linked Issues or Issue Description
No pre-existing public issue. Describing inline per the feature request
template:
**Subsystem affected:** `server/` — REST API & orchestration services
**Problem or motivation:**
Agents that configure env bindings must reference secrets by UUID
(`secretId`). There is no agent-accessible API to resolve a secret name
to its UUID. `GET /companies/:companyId/secrets` requires board access;
the internal `secrets.resolve` handler rejects anything that is not
already a UUID. Agents and their operators are forced to find UUIDs by
inspecting browser network requests, which is friction that should not
exist.
**Proposed solution:**
Add a read-only catalog endpoint — `GET
/companies/:companyId/secrets/catalog` — that agents can call. It
returns only non-sensitive metadata (`id`, `name`, `key`, `status`) for
each active company secret, stripped of values, provider configuration,
and version history. Board callers get the same response. The existing
full-detail list endpoint (`GET /companies/:companyId/secrets`) remains
board-only and is unchanged.
**Alternatives considered:**
- Allow agents to call the existing `/secrets` list — rejected because
it returns full rows including provider metadata; narrowing the response
is safer.
- Add a name-to-UUID lookup by query param — simpler but less useful; a
full catalog means the agent can do the resolution locally without a
second round-trip.
**Roadmap alignment:** Does not duplicate anything in `ROADMAP.md`.
## What Changed
- `server/src/routes/secrets.ts` — new `GET
/companies/:companyId/secrets/catalog` route registered before the
board-only `GET /companies/:companyId/secrets` route. Uses
`assertBoardOrAgent` + `assertCompanyAccess`. Calls `svc.list()` then
projects each row to `{ id, name, key, status }` before responding.
- `server/src/__tests__/secrets-routes.test.ts` — adds `list` to the
shared mock service object (it was missing); adds a `describe` block
with four test cases: board caller receives stripped metadata, agent
caller in the same company receives stripped metadata, unauthenticated
request gets 401, agent from a different company gets 403.
## Verification
**Automated:**
```bash
pnpm --filter @paperclipai/server test --run secrets-routes
```
All four new test cases (board access, agent access, unauthed rejection,
cross-company rejection) should pass.
**Manual:**
1. Start the Paperclip server locally.
2. Create a company and a secret via the UI.
3. Call the endpoint as a board user:
```bash
curl http://localhost:3100/api/companies/<companyId>/secrets/catalog \
-H "Authorization: Bearer <board-session-token>"
```
Expect a JSON array with `id`, `name`, `key`, `status` fields — no
`provider`, no `referenceCount`, no version data.
4. Call the same endpoint with an agent API key:
```bash
curl http://localhost:3100/api/companies/<companyId>/secrets/catalog \
-H "Authorization: Bearer <agent-api-key>"
```
Expect the same response.
5. Call with an agent API key scoped to a *different* company — expect
403.
## Risks
Low risk. This is a purely additive, read-only endpoint. No existing
behavior changes. The only new capability is that agents can discover
the UUIDs of secrets in their own company — metadata they already need
to do their job. Secret values are never returned. Authorization reuses
the existing `assertBoardOrAgent` and `assertCompanyAccess` guards
already used throughout the codebase.
## Model Used
Claude Sonnet 4.6 (`claude-sonnet-4-6`) — Anthropic, extended context,
tool use enabled. The entire change (route, tests, PR description) was
produced by the model operating as a Paperclip CEO agent assigned to the
task.
## 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
---------
Co-authored-by: Austin Pilz <austinpilz@users.noreply.github.com>
Co-authored-by: root <root@paperclip.pilz.dev>
Co-authored-by: Internet Historian <agent@paperclip.internal>
Co-authored-by: Andrew Aymeloglu <aaymeloglu@gmail.com>
canary/v2026.813.0-canary.16
|
||
|
|
eabecc6f77 |
feat(annotations): include issue document annotations in agent review context (#11332)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Reviewers annotate plans and issue documents with inline comments, and assigned agents act on that feedback > - The server already builds a bounded review context from open plan annotations and includes it in agent wake payloads > - Non-plan issue documents did not get the same treatment: their open annotation threads never reached the agent, and the properties pane did not surface their annotations > - This pull request extends the review-context path and the properties-pane UI to issue documents, at parity with plans > - The benefit is that agent feedback on any issue document reaches the assigned agent, not only feedback on the plan ## Linked Issues or Issue Description **What existing behavior does this improve?** The review-context pipeline that delivers inline annotation feedback to assigned agents, and the properties pane that surfaces those annotations to reviewers. **Subsystem affected** The server review-context path (`server/src/services/plan-review-context.ts`, wake payload assembly in `server/src/services/heartbeat.ts`, `server/src/routes/issues.ts`), shared wake-payload types (`packages/shared`, `packages/adapter-utils`), and the issue properties pane (`ui/src/components/issue-properties/`). **Current behavior** A reviewer can annotate any issue document, not only the plan. The agent wake payload includes open annotation threads for the plan document only. Feedback left on other issue documents is invisible to the assigned agent. In the properties pane, the Artifacts tab also gives no way to see or open a document's annotations. **Proposed behavior** Add `buildDocumentReviewContext` beside the existing plan builder. It collects open annotation threads for all non-plan issue documents, applies the same thread, comment, and character budgets across documents, and reports truncation. Include the result as a new `documentReviewContext` field in agent wake payloads and in the issue wake-context route. Keep the plan context on its legacy builder and field so plan-only wakes stay byte-for-byte compatible. Render the new context in the adapter wake-payload text, and surface annotation counts and the annotation panel for documents in the properties pane's Plans and Artifacts tabs. **Reason and benefit** The floating annotation popover and persistent highlight UI landed earlier; this change completes the loop so agent feedback on any issue document reaches the assigned agent, not only feedback on the plan. **Breaking changes** None. The wake payload gains a new optional `documentReviewContext` field; the existing plan context field and its legacy builder are unchanged, so plan-only wakes stay byte-for-byte compatible. ## What Changed - Add `buildDocumentReviewContext` in `server/src/services/plan-review-context.ts`: bounded review context (shared thread/comment/character budgets, per-document legacy limits) over all non-plan issue documents - Include `documentReviewContext` in agent wake payloads (`server/src/services/heartbeat.ts`) and in the issue wake-context response (`server/src/routes/issues.ts`) - Add shared `DocumentReviewContext` / `DocumentReviewContextDocument` types in `packages/shared` - Normalize and render the new context in adapter wake-payload text (`packages/adapter-utils/src/server-utils.ts`), with tests - Show a `DocumentAnnotationsCountChip` and the annotation panel for documents in the properties pane Plans and Artifacts tabs, with tests - Extend server document-annotations service tests to cover the new context builder ## Verification - Run `npx vitest run packages/adapter-utils/src/server-utils.test.ts server/src/__tests__/document-annotations-service.test.ts` from the repo root — 104 tests pass - Run `TZ=UTC npx vitest run ui/src/components/issue-properties/IssuePropertiesDocumentAnnotations.test.tsx ui/src/components/IssueProperties.test.tsx ui/src/components/IssueDocumentAnnotations.test.tsx ui/src/components/DocumentAnnotationPopover.test.tsx` from the repo root — 75 tests pass (one pre-existing monitor-row case asserts UTC timestamps, so use `TZ=UTC` locally; CI runs in UTC) - `pnpm run typecheck` in `server/` passes - Manual: annotate a non-plan issue document, then wake the assigned agent with a comment — the wake payload lists the open document annotation threads; the Artifacts tab shows the annotation count chip and opens the panel ## Risks - The wake payload gains a new optional `documentReviewContext` field; consumers that ignore unknown fields are unaffected, and the plan context field is unchanged - The context is new input to agent wakes; shared budgets (same limits as the plan context) bound token cost across all documents - Low UI risk: the properties-pane changes reuse the existing annotation components > 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 (Anthropic), model ID `claude-fable-5` (Claude Fable 5), with extended thinking and agentic tool use (Claude Code harness) ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [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>canary/v2026.813.0-canary.15 |
||
|
|
9b1fd42ac1 |
test(grok-local): isolate billing env in usage cost test (#11285)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Local adapters report run output, token use, and cost data. > - The Grok local adapter now reports real token use and cost data. > - Its new billing test must prove the no-key path and the API-key path. > - The no-key assertion used the caller environment without isolation. > - This made the test fail when `XAI_API_KEY` was already set. > - This pull request isolates that environment state in the test. > - The benefit is stable coverage for the cost gate from #10433. ## Linked Issues or Issue Description Refs #10433 **What happened?** The Grok local usage and cost test asserted subscription billing while it still used the ambient process environment. If `XAI_API_KEY` was set before the test ran, the adapter selected API billing instead. The subscription assertion could then fail on a developer machine or a CI runner with provider credentials. **Expected behavior** The test should prove the subscription path with no `XAI_API_KEY`. It should also prove the API billing path with a test key. **Steps to reproduce** 1. Start from `master` after #10433. 2. Set `XAI_API_KEY` in the shell environment. 3. Run `vitest` for `packages/adapters/grok-local/src/server/execute.test.ts`. 4. Observe that the subscription half can take the API billing branch without test isolation. **Paperclip version or commit** `master` after #10433. **Deployment mode** Built from source. ## What Changed - Isolated `XAI_API_KEY` with save, delete, set, and restore logic around both billing assertions. - Gave the subscription and API billing checks separate run ids and temp roots. ## Verification - `XAI_API_KEY=ambient-test-key corepack pnpm exec vitest run packages/adapters/grok-local/src/server/execute.test.ts` - `corepack pnpm --filter @paperclipai/adapter-grok-local typecheck` ## Risks Low risk. This changes test setup only. It does not change Grok local adapter runtime behavior. > 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-5 Codex local coding agent. The agent used shell tools, GitHub CLI, and local test execution. The context window size was not exposed in this run. ## 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: Claude <noreply@paperclip.ing>canary/v2026.813.0-canary.14 |
||
|
|
534c1a9630 |
chore(lockfile): refresh pnpm-lock.yaml (#11339)
Auto-generated lockfile refresh after dependencies changed on master. This PR only updates pnpm-lock.yaml. Co-authored-by: lockfile-bot <lockfile-bot@users.noreply.github.com>canary/v2026.813.0-canary.13 |
||
|
|
04432f82c6 |
build(deps): align Lexical packages at 0.49.0 (#10728)
Bumps [@lexical/link](https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link) from 0.46.0 to 0.49.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/facebook/lexical/releases">@lexical/link's releases</a>.</em></p> <blockquote> <p>v0.49.0 is a monthly release headlined by the completion of the <code>$config()</code> protocol migration for Lexical's built-in node classes (<a href="https://redirect.github.com/facebook/lexical/pull/8640">#8640</a>), a breaking change that replaces per-node <code>getType()</code>/<code>clone()</code>/<code>importJSON()</code>/<code>importDOM()</code>/<code>transform()</code> boilerplate with runtime-synthesized behavior. It ships with a cluster of follow-up fixes hardening the synthesized <code>getType()</code> and <code>clone()</code> (including a stack overflow in compiled builds), an opt-in sticky horizontal scrollbar for overflowing tables, and a broad batch of fixes across tables, Markdown, HTML, code highlighting, and selection.</p> <h2>Breaking Changes</h2> <ul> <li><a href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a> — Built-in node classes are ported to the <a href="https://lexical.dev/docs/concepts/nodes"><code>$config()</code></a> protocol. The static <code>importJSON()</code>, <code>importDOM()</code>, <code>clone()</code>, and <code>transform()</code> methods are no longer present on ported nodes; use the higher-level equivalents instead (<code>LexicalEditor.parseEditorState</code>/<code>$generateNodesFromDOM</code>, the <code>$cloneWithProperties</code> helper, and extensions or <code>$transform</code> in <code>$config</code>). <code>getType()</code> is unchanged and safe to keep using, <code>__type</code> is now <code>readonly</code>, and node constructors require zero-argument defaults. Custom nodes may keep their static methods, but adopting <code>$config()</code> is recommended (<a href="https://redirect.github.com/facebook/lexical/pull/8640">#8640</a>)</li> <li><a href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a> — <code>LexicalCommand<T></code>'s payload type is now invariant to prevent unsafe structural compatibility between commands. Previously correct code (including explicit generics) needs no changes; only unsafe <code>LexicalCommand<unknown></code> usage must switch to the newly exported <code>AnyLexicalCommand</code> alias. <code>dispatchCommand</code>'s payload argument is now optional when a command's payload type is <code>undefined</code> or <code>void</code>, and redundant explicit type parameters were dropped from the <code>registerCommand</code>/<code>registerNodeTransform</code> call sites (<a href="https://redirect.github.com/facebook/lexical/pull/8877">#8877</a>)</li> </ul> <h2>New APIs & Features</h2> <ul> <li><a href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a> — Opt-in sticky horizontal scrollbar for tables wider than their container, so the scrollbar stays anchored at the viewport bottom instead of only being reachable at the end of the table. Enable it with <code>hasStickyScrollbar: true</code> in the table config; style it via the new <code>tableStickyScrollbar</code> theme key (<a href="https://redirect.github.com/facebook/lexical/pull/8790">#8790</a>)</li> <li><a href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a> — The <code><colgroup></code> element is now omitted from the table DOM when column widths are undefined, producing cleaner markup and letting the browser auto-size columns; it is created or removed dynamically as widths change (<a href="https://redirect.github.com/facebook/lexical/pull/8850">#8850</a>)</li> <li><a href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a> — Added an editor operation benchmark suite (paragraph split, bold formatting, range deletion, paste, select-all format) measured through full DOM reconciliation, to help contributors track core editing performance (<a href="https://redirect.github.com/facebook/lexical/pull/8856">#8856</a>)</li> </ul> <h2>Deprecations</h2> <ul> <li><a href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a> — <code>$createTableSelection</code> is deprecated in favor of <code>$createTableSelectionFrom</code>, which takes the table node plus anchor/focus cells and validates them, instead of returning a blank selection with placeholder <code>'root'</code> keys that must be overwritten (<a href="https://redirect.github.com/facebook/lexical/pull/8855">#8855</a>)</li> <li><a href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a> — <code>getShape()</code> is deprecated because it computes incorrect bounds for merged cells; use the now-exported <code>$computeTableCellRectBoundary</code> (with <code>$computeTableMap</code>) instead (<a href="https://redirect.github.com/facebook/lexical/pull/8853">#8853</a>)</li> </ul> <h2>Notable Fixes</h2> <p><strong><code>$config()</code> follow-ups (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8640">#8640</a>)</strong></p> <ul> <li>The synthesized <code>clone()</code> now applies <code>afterCloneFrom()</code> when called directly (e.g. <code>NodeClass.clone(node)</code>), fixing silent property loss across 25+ core nodes (<a href="https://redirect.github.com/facebook/lexical/pull/8864">#8864</a>)</li> <li>The synthesized <code>getType()</code> no longer returns the superclass type when inherited, fixing node-type collisions during editor registration (<a href="https://redirect.github.com/facebook/lexical/pull/8867">#8867</a>)</li> <li>The synthesized <code>getType()</code> no longer recurses infinitely when a bundler copies it onto a subclass as an own static, fixing stack overflows in compiled/minified builds (<a href="https://redirect.github.com/facebook/lexical/pull/8869">#8869</a>)</li> </ul> <p><strong>Tables</strong></p> <ul> <li><code>DELETE_LINE_COMMAND</code> (Cmd/Ctrl-based delete-line shortcuts) now works inside table cells instead of being silently swallowed (<a href="https://redirect.github.com/facebook/lexical/pull/8851">#8851</a>)</li> <li>Table alignment now works when cells are selected in any direction, not just top-left→bottom-right (<a href="https://redirect.github.com/facebook/lexical/pull/8883">#8883</a>)</li> </ul> <p><strong>Selection & editing</strong></p> <ul> <li>Editor updates dispatched from a read-only context (e.g. inside <code>editor.read()</code>) now run in a fresh writable update instead of being silently dropped, with a dev warning (<a href="https://redirect.github.com/facebook/lexical/pull/8863">#8863</a>)</li> <li>Firefox now creates a selection and shows the block cursor when clicking in the gap between block decorators (<a href="https://redirect.github.com/facebook/lexical/pull/8862">#8862</a>)</li> <li>Pressing ArrowUp before a leading non-inline decorator no longer moves the selection out of the editor (<a href="https://redirect.github.com/facebook/lexical/pull/8887">#8887</a>)</li> </ul> <p><strong>Markdown, HTML & code</strong></p> <ul> <li>Typing a list marker at the start of a heading no longer converts the heading into a list (<a href="https://redirect.github.com/facebook/lexical/pull/8879">#8879</a>)</li> <li>HTML import now evaluates unrestricted CSS selector groups (e.g. <code>p, .foo</code>) against all elements instead of only tag-matched ones (<a href="https://redirect.github.com/facebook/lexical/pull/8873">#8873</a>)</li> <li>Concurrent async language/theme loads in the Shiki highlighter are deduplicated and merged into a single history entry, avoiding spurious undo states (<a href="https://redirect.github.com/facebook/lexical/pull/8854">#8854</a>)</li> <li><code>QuoteNode.updateDOM</code> now accepts the <code>EditorConfig</code> argument the reconciler passes to every other node, for API consistency (<a href="https://redirect.github.com/facebook/lexical/pull/8882">#8882</a>)</li> </ul> <p><strong>Playground & website</strong></p> <ul> <li>Playground scroll padding now accounts for the sticky toolbar so selections scrolled into view from above aren't hidden behind it (<a href="https://redirect.github.com/facebook/lexical/pull/8849">#8849</a>)</li> <li>Fixed a homepage crash caused by a minified dev build, and isolated each embedded example in its own error boundary so one failure no longer takes down the page (<a href="https://redirect.github.com/facebook/lexical/pull/8861">#8861</a>)</li> </ul> <h2>What's Changed</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/facebook/lexical/blob/main/CHANGELOG.md">@lexical/link's changelog</a>.</em></p> <blockquote> <h2>v0.49.0 (2026-07-29)</h2> <ul> <li>lexical-markdown Bug Fix Preserve headings when typing list shortcuts (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8879">#8879</a>) Steven Dang</li> <li>lexical-rich-text Bug Fix Forward editorConfig to QuoteNode.updateDOM (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8882">#8882</a>) Sha Halimi</li> <li>lexical-table Bug Fix Table alignment fails when selecting in non-TL-BR direction (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8883">#8883</a>) sahir</li> <li>Breaking Changelexical Chore Remove redundant registerCommandregisterNodeTransform generics (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8877">#8877</a>) mayrang</li> <li>lexical-html Bug Fix Dispatch unrestricted CSS selector groups (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8873">#8873</a>) Madan kumar</li> <li>lexical-playground Bug Fix Deterministic history coalescing for flaky webkit undo tests (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8874">#8874</a>) Bob Ippolito</li> <li>lexical Performance Skip redundant selection restoration in removeTextFromCaretRange (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8872">#8872</a>) mayrang</li> <li>lexical Refactor Simplify RangeSelection.insertText via removeText decomposition (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8870">#8870</a>) mayrang</li> <li>lexical-list Chore Remove redundant decorator-adjacent backspace handler (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8871">#8871</a>) mayrang</li> <li>lexical-table Feature Skip colgroup element in table DOM with undefined col widths (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8850">#8850</a>) Zachary Gallafent</li> <li>lexical Bug Fix Create selection when clicking between block decorators on Firefox (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8862">#8862</a>) mayrang</li> <li>fix config() synthesized getType() recurses infinitely when inherited as own static (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8867">#8867</a> follow-up) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8869">#8869</a>) Sherry</li> <li>lexicallexical-website Bug Fix Homepage crash from optimized dev build (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8861">#8861</a>) Bob Ippolito</li> <li>lexical Bug Fix run editor updates dispatched from a read-only context in a fresh writable update (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8863">#8863</a>) Sherry</li> <li>lexical Feature Editor operation benchmarks (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8856">#8856</a>) mayrang</li> <li>lexical Chore Migrate LexicalSelection tests to buildEditorFromExtensions (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8865">#8865</a>) mayrang</li> <li>fix config() synthesized getType() inherited by subclasses causes node-type collision (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8640">#8640</a>) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8867">#8867</a>) Sherry</li> <li>fix config() auto-synthesized clone() loses properties when called directly (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8640">#8640</a>) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8864">#8864</a>) Sherry</li> <li>lexical-tablelexical Feature Sticky scrollbar for overflowing tables (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8790">#8790</a>) mayrang</li> <li>lexical-code-shiki Bug Fix Deduplicate async loads and merge into history (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8854">#8854</a>) Alexis</li> <li>lexical-table Chore Remove dead code in table command handlers (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8857">#8857</a>) mayrang</li> <li>lexical-tablelexical-playground Chore Deprecate getShape() and migrate playground (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8853">#8853</a>) mayrang</li> <li>lexical-list Chore Rename isNestedListNode to isNestedListNode (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8843">#8843</a>) Bob Ippolito</li> <li>lexical-playground Bug Fix account for sticky toolbar in scroll padding (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8849">#8849</a>) Bob Ippolito</li> <li>Breaking Changeslexical Refactor Port node classes to the config() protocol (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8640">#8640</a>) Bob Ippolito</li> <li>lexical-table Chore Deprecate createTableSelection in favor of createTableSelectionFrom (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8855">#8855</a>) mayrang</li> <li>lexical-table Bug Fix Enable DELETELINECOMMAND in table cells (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8851">#8851</a>) mayrang</li> <li>lexical Bug Fix Scope bench vitest projects to exclude regular test files (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8852">#8852</a>) mayrang</li> <li>v0.48.0 (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8847">#8847</a>) Bob Ippolito</li> <li>v0.48.0 Lexical GitHub Actions Bot</li> </ul> <h2>v0.48.0 (2026-07-16)</h2> <ul> <li>lexical-reactlexical-table Bug Fix Enable table copy in read-only mode (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8845">#8845</a>) mayrang</li> <li>lexical-extensionlexical-mdastdev-mdast-editor-example Feature Add MdastHtmlExtension and Markdown custom-construct examples (collapsible, kbd, alerts, footnotes) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8826">#8826</a>) Bob Ippolito</li> <li>Fix fail closed in LinkNode.sanitizeUrl() on unparseable URLs (XSS) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8846">#8846</a>) xiezhenjia-meta</li> <li>lexical Chore Fix serialize-javascript package dependency vulnerability (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8803">#8803</a>) vijay ojha</li> <li>lexical-react Bug Fix Count block separators in character limit overflow wrapping (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8840">#8840</a>) mayrang</li> <li>lexical-yjslexical-react Feature Customizable Yjs shared-type root name (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8841">#8841</a>) mayrang</li> <li>lexical-list Bug Fix Backspace at start of list item outdents or converts to paragraph (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8829">#8829</a>) mayrang</li> <li>lexical-table Feature Add moveTableRow function Add missing export for unmergeCellNode (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8833">#8833</a>)</li> <li>lexical-link Bug Fix disable link opening for disabled autolink in (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8839">#8839</a>) Olivier Chevallier</li> <li>lexical-rich-textlexical-plain-text Bug Fix dont cancel dragover for text drags so native drops work again (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8842">#8842</a>) Bob Ippolito</li> <li>Open playground links in a new tab (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8837">#8837</a>) Sherry</li> <li>lexical-react Bug Fix Merge adjacent OverflowNodes in useCharacterLimit (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8831">#8831</a>) mayrang</li> <li>lexical-code-shiki Bug Fix force re-tokenize after async language load (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8830">#8830</a>) Olivier Chevallier</li> <li>lexical-tablelexical-playground Bug Fix Auto-scroll while drag-selecting cells past the visible edge (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8822">#8822</a>) Oleksandr Trukhnii</li> <li>lexical-mdastlexical-markdown Bug Fix Roundtrip overlapping inline formats (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8825">#8825</a>) Bob Ippolito</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/facebook/lexical/commit/ffe90924bd55b5d450c88de0f9f1c8b228c4a221"><code>ffe9092</code></a> v0.49.0</li> <li><a href="https://github.com/facebook/lexical/commit/5fc8eae3cdbd934ec3d0422ecaef7037771e38c2"><code>5fc8eae</code></a> [Breaking Changes][lexical] Refactor: Port node classes to the $config() prot...</li> <li><a href="https://github.com/facebook/lexical/commit/ba4d6d0f9b99268c58df853542e309bf4f2021ac"><code>ba4d6d0</code></a> v0.48.0 (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8847">#8847</a>)</li> <li><a href="https://github.com/facebook/lexical/commit/365516c5fcdcc141561dcbb2eb43b707b48dd5b8"><code>365516c</code></a> Fix: fail closed in LinkNode.sanitizeUrl() on unparseable URLs (XSS) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8846">#8846</a>)</li> <li><a href="https://github.com/facebook/lexical/commit/71562324c7d2154f64d79f6d20803f67b9bd9c11"><code>7156232</code></a> [lexical-link] Bug Fix: disable link opening for disabled autolink in… (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8839">#8839</a>)</li> <li><a href="https://github.com/facebook/lexical/commit/e4b7cc3f420226059c8aa30df6e89bd5fadbea90"><code>e4b7cc3</code></a> v0.47.0 (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8821">#8821</a>)</li> <li><a href="https://github.com/facebook/lexical/commit/a7666ab11f5e8c674a3f5ca8a83d2e92f1b171d0"><code>a7666ab</code></a> [*][lexical-devtools][lexical-playground] Chore: Update flow, hermes, and bab...</li> <li><a href="https://github.com/facebook/lexical/commit/e649ab28b7e2dd58c1b4798c446e611f54356518"><code>e649ab2</code></a> [lexical][lexical-eslint-plugin] Feature: Add $getDocument() API and Shadow D...</li> <li><a href="https://github.com/facebook/lexical/commit/7b76175cc96d99489c2f3c792db193cf2d9bc127"><code>7b76175</code></a> [lexical-playground] Bug Fix: Support Unicode URLs in autolink matcher (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8787">#8787</a>)</li> <li><a href="https://github.com/facebook/lexical/commit/62a4b30f382b4dc60cacd1a9d753a2d1f44d9f5e"><code>62a4b30</code></a> [lexical][*] Feature: registerEventListener / registerEventListeners DOM help...</li> <li>Additional commits viewable in <a href="https://github.com/facebook/lexical/commits/v0.49.0/packages/lexical-link">compare view</a></li> </ul> </details> <br /> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Cody <noreply@paperclip.ing> |
||
|
|
031003c5e1 |
fix(ui): search parent-issue picker on the server (#11334)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The board UI lets an operator choose a parent issue for an issue > - The parent picker loads a priority-first page and filters that page in the browser > - A medium-priority or low-priority issue past the page limit never enters the picker > - This pull request sends typed parent-picker text to the server and keeps the picker exclusions > - The benefit is that the operator can select valid parent issues beyond the default page ## Linked Issues or Issue Description This pull request supersedes [#6193](https://github.com/paperclipai/paperclip/pull/6193), whose old file path no longer matches the current component tree. **What happened?** The parent picker fetched one default issue page and filtered it in the browser. The default page sorts by priority and caps the result at 500 issues. Valid medium-priority and low-priority parent issues beyond that page stayed hidden. **Expected behavior** The parent picker must search the server when the operator types text. It must show matching issues beyond the default page while it keeps the current issue and descendant exclusions. **Steps to reproduce** 1. Open an issue in a company with more than 500 issues. 2. Open the parent picker and type the name of a medium-priority or low-priority issue beyond the default page. 3. Observe that the picker does not show the matching issue. **Paperclip version or commit** Commit `c6965bd0237fd9536b41f1495e2a4bb252afcde7`. **Deployment mode** Local dev (`pnpm dev`). ## What Changed - Send parent-picker searches to the issue list endpoint with `q` and a bounded `limit` of 50. - Keep the empty-search list, cycle exclusions, and current sort behavior. - Add a component test for a low-priority match hidden by the default page. ## Verification - Run `pnpm vitest run ui/src/components/IssueProperties.test.tsx`. - Confirm that all 53 tests pass. - Confirm that the new test checks `{ q, limit: 50 }` and the matching issue. ## Risks - Low risk. The change affects only parent-picker search requests. - The server search uses the existing issue list query and does not change stored data. ## Model Used Codex, GPT-5, with tool use and code execution. The model assisted with the change and 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 - [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>canary/v2026.813.0-canary.12 |
||
|
|
403fcefb97 |
build(deps-dev): bump vite from 6.4.1 to 6.4.3 (#11317)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.4.1 to 6.4.3. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/vitejs/vite/releases">vite's releases</a>.</em></p> <blockquote> <h2>v6.4.3</h2> <p>Please refer to <a href="https://github.com/vitejs/vite/blob/v6.4.3/packages/vite/CHANGELOG.md">CHANGELOG.md</a> for details.</p> <h2>v6.4.2</h2> <p>Please refer to <a href="https://github.com/vitejs/vite/blob/v6.4.2/packages/vite/CHANGELOG.md">CHANGELOG.md</a> for details.</p> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/vitejs/vite/blob/v6.4.3/packages/vite/CHANGELOG.md">vite's changelog</a>.</em></p> <blockquote> <h2><!-- raw HTML omitted -->6.4.3 (2026-06-01)<!-- raw HTML omitted --></h2> <ul> <li>fix: backport <a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22572">#22572</a>, reject windows alternate paths (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22576">#22576</a>) (<a href="https://github.com/vitejs/vite/commit/96b0c10162e9c55485d922db2cfc6b8227cbc176">96b0c10</a>), closes <a href="https://redirect.github.com/vitejs/vite/issues/22572">#22572</a> <a href="https://redirect.github.com/vitejs/vite/issues/22576">#22576</a></li> <li>fix(deps): backport <a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22571">#22571</a>, reject UNC paths for launch-editor-middleware (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22575">#22575</a>) (<a href="https://github.com/vitejs/vite/commit/8fed5cf540c0d475266787f52072f258478cd42f">8fed5cf</a>), closes <a href="https://redirect.github.com/vitejs/vite/issues/22571">#22571</a> <a href="https://redirect.github.com/vitejs/vite/issues/22575">#22575</a></li> </ul> <h2><!-- raw HTML omitted -->6.4.2 (2026-04-06)<!-- raw HTML omitted --></h2> <ul> <li>fix: apply server.fs check to env transport (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22159">#22159</a>) (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22163">#22163</a>) (<a href="https://github.com/vitejs/vite/commit/fe28e47e9463e4c9619f94bfa06d2f8f1411b44b">fe28e47</a>), closes <a href="https://redirect.github.com/vitejs/vite/issues/22159">#22159</a> <a href="https://redirect.github.com/vitejs/vite/issues/22163">#22163</a></li> <li>fix: avoid path traversal with optimize deps sourcemap handler (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22161">#22161</a>) (<a href="https://github.com/vitejs/vite/commit/ca4da5d1fb45c9cfdce606aa30825095791b164b">ca4da5d</a>), closes <a href="https://redirect.github.com/vitejs/vite/issues/22161">#22161</a></li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/vitejs/vite/commit/6c2c881f15495738ff03bc1d67cc052c07e0cac4"><code>6c2c881</code></a> release: v6.4.3</li> <li><a href="https://github.com/vitejs/vite/commit/96b0c10162e9c55485d922db2cfc6b8227cbc176"><code>96b0c10</code></a> fix: backport <a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22572">#22572</a>, reject windows alternate paths (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22576">#22576</a>)</li> <li><a href="https://github.com/vitejs/vite/commit/8fed5cf540c0d475266787f52072f258478cd42f"><code>8fed5cf</code></a> fix(deps): backport <a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22571">#22571</a>, reject UNC paths for launch-editor-middleware (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/2">#2</a>...</li> <li><a href="https://github.com/vitejs/vite/commit/6b3fad02abd550bd7b79934ff92c58dbd7f33045"><code>6b3fad0</code></a> release: v6.4.2</li> <li><a href="https://github.com/vitejs/vite/commit/ca4da5d1fb45c9cfdce606aa30825095791b164b"><code>ca4da5d</code></a> fix: avoid path traversal with optimize deps sourcemap handler (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22161">#22161</a>)</li> <li><a href="https://github.com/vitejs/vite/commit/fe28e47e9463e4c9619f94bfa06d2f8f1411b44b"><code>fe28e47</code></a> fix: apply server.fs check to env transport (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22159">#22159</a>) (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22163">#22163</a>)</li> <li><a href="https://github.com/vitejs/vite/commit/5487f4f641f70c47ea05fd101a4319897df048b3"><code>5487f4f</code></a> release: v6.4.1</li> <li><a href="https://github.com/vitejs/vite/commit/1114b5d7ea03e26572708715343bec69db4536e8"><code>1114b5d</code></a> fix(dev): trim trailing slash before <code>server.fs.deny</code> check (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20968">#20968</a>) (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20969">#20969</a>)</li> <li><a href="https://github.com/vitejs/vite/commit/f12697c0f64b9a37196b9ab218a0911829d5b103"><code>f12697c</code></a> release: v6.4.0</li> <li><a href="https://github.com/vitejs/vite/commit/ca6455ee9eb6111a9caa9810506a1b9ac96a520a"><code>ca6455e</code></a> feat: allow passing down resolved config to vite's createServer (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20932">#20932</a>)</li> <li>Additional commits viewable in <a href="https://github.com/vitejs/vite/commits/v6.4.3/packages/vite">compare view</a></li> </ul> </details> <br /> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
44694328a3 |
fix(issues): make DELETE /api/issues/:id succeed for issues with dependents (#11331)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The server provides issue APIs and the database stores issue child rows > - The issue delete endpoint removes the parent issue before dependent rows > - Several issue foreign keys had no delete policy, so PostgreSQL returned a foreign-key error > - This pull request adds safe cascade and set-null policies and a clear conflict response > - The benefit is reliable issue deletion with a useful error when a restricted audit row still blocks deletion ## Linked Issues or Issue Description Fixes #7728 Fixes #4660 Fixes #7991 Fixes #4627 Fixes #5086 **What happened?** `DELETE /api/issues/:id` returned HTTP 500 when dependent comments, thread interactions, read states, inbox archives, feedback votes, or ledger rows referenced the issue. The database raised SQLSTATE 23503 because several foreign keys had no delete policy. **Expected behavior** The endpoint must remove dependent rows that have no meaning without the issue. It must keep ledger rows with a null issue reference. It must return HTTP 409 when a restricted decision audit row still references the issue. **Steps to reproduce** 1. Create an issue. 2. Add a comment or thread interaction that references the issue. 3. Send `DELETE /api/issues/:id`. 4. Observe the HTTP 500 response. **Paperclip version or commit** Commit `1f8f456f8340823fe2bd891ae8933d942f190b7b`. **Deployment mode** Local dev with embedded PGlite or external PostgreSQL. ## What Changed - Add `CASCADE` to five issue child foreign keys. - Add `SET NULL` to the finance and cost event issue foreign keys. - Keep decision audit references restricted. - Map SQLSTATE 23503 from the issue delete service to HTTP 409. - Add migration 0217 for the seven changed tables. - Add regression tests for cascade deletion and restricted decision references. ## Verification - Run `pnpm --filter @paperclipai/db typecheck`. - Run `pnpm --filter @paperclipai/server typecheck`. - Run `npx vitest run src/__tests__/issue-remove-cascade.test.ts` from `server/`. - The regression test applies migration 0217 to a fresh embedded PostgreSQL database. ## Risks - Migration 0217 changes only seven foreign keys that reference `issues.id`. - Cascade deletion removes child rows that cannot exist without the parent issue. - Set-null preserves finance and cost ledger rows. - Decision audit rows remain protected, so the endpoint can return HTTP 409. ## Model Used Codex, based on GPT-5, with tool use and code-review support. The implementation author used an AI coding agent. This PR handoff uses the same model family to validate the commit and manage the pull request. ## 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 --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.813.0-canary.11 |
||
|
|
062271e934 |
build(deps): bump mermaid from 11.16.0 to 11.16.1 (#11306)
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.16.0 to 11.16.1. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/mermaid-js/mermaid/releases">mermaid's releases</a>.</em></p> <blockquote> <h2>mermaid@11.16.1</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a> <a href="https://github.com/mermaid-js/mermaid/commit/12d472c9ed43f94814b110da8d7a9ae6dd5266ed"><code>12d472c</code></a> Thanks <a href="https://github.com/aloisklink"><code>@aloisklink</code></a>! - fix: handle CSS sibling combinators in compileCSS</p> </li> <li> <p><a href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a> <a href="https://github.com/mermaid-js/mermaid/commit/2cd6dcf735533b323507e3e889ffdea870540b43"><code>2cd6dcf</code></a> Thanks <a href="https://github.com/aloisklink"><code>@aloisklink</code></a>! - fix: increase protections against prototype pollution</p> <p>User-controlled input already has protections against prototype pollution.</p> <p>Fixes: GHSA-c4c3-pg64-4m4v</p> </li> <li> <p><a href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a> <a href="https://github.com/mermaid-js/mermaid/commit/99af3fc35ef0a9a9c8c6314521344d67523ddccf"><code>99af3fc</code></a> Thanks <a href="https://github.com/aloisklink"><code>@aloisklink</code></a>! - fix(architecture): use <code>Map</code>s and <code>Set</code>s to store groups/services</p> <p>Services are now rendered in the order they are defined and more service IDs are now supported.</p> </li> <li> <p><a href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a> <a href="https://github.com/mermaid-js/mermaid/commit/2cd6dcf735533b323507e3e889ffdea870540b43"><code>2cd6dcf</code></a> Thanks <a href="https://github.com/aloisklink"><code>@aloisklink</code></a>! - deprecate: Deprecate the <code>mermaidAPI.setConfig()</code> function</p> <p>Calling this function has no observable effect, as the next time a <code>render()</code> or <code>parse()</code> is called, the <code>currentConfig</code> is cleared.</p> </li> <li> <p><a href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a> <a href="https://github.com/mermaid-js/mermaid/commit/630aa7e5dd417e1f56bff2a1ce8df2c5ad08d289"><code>630aa7e</code></a> Thanks <a href="https://github.com/aloisklink"><code>@aloisklink</code></a>! - fix(xychart): support zero-width x-axis ranges</p> </li> <li> <p><a href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a> <a href="https://github.com/mermaid-js/mermaid/commit/59b22fad2b3bb04f87a476c84a8a2b24679e607e"><code>59b22fa</code></a> Thanks <a href="https://github.com/aloisklink"><code>@aloisklink</code></a>! - fix(radar): limit number of ticks to 32</p> <p>Setting a ticks value higher than this would only show 32 ticks.</p> </li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/mermaid-js/mermaid/commit/7ecca0cd7f1658ef74f4e7e91f925724ef403bbf"><code>7ecca0c</code></a> Version Packages (<a href="https://redirect.github.com/mermaid-js/mermaid/issues/8023">#8023</a>)</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/95b1b9cdba40e7954aee7d8a265577fd3839710c"><code>95b1b9c</code></a> docs: change <code>mermaidAPI.setConfig()</code> changeset (<a href="https://redirect.github.com/mermaid-js/mermaid/issues/8024">#8024</a>)</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/acc69f172b5ad5405ca8de8f4f79a80505092b3f"><code>acc69f1</code></a> Merge pull request <a href="https://redirect.github.com/mermaid-js/mermaid/issues/8022">#8022</a> from mermaid-js/release/11.16.1</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/eba72879e94755433403e571d724ca7a448800af"><code>eba7287</code></a> docs: point changesets to correct commit hashes</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/12d472c9ed43f94814b110da8d7a9ae6dd5266ed"><code>12d472c</code></a> Merge commit from fork</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/2cd6dcf735533b323507e3e889ffdea870540b43"><code>2cd6dcf</code></a> Merge commit from fork</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/630aa7e5dd417e1f56bff2a1ce8df2c5ad08d289"><code>630aa7e</code></a> Merge commit from fork</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/59b22fad2b3bb04f87a476c84a8a2b24679e607e"><code>59b22fa</code></a> Merge commit from fork</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/99af3fc35ef0a9a9c8c6314521344d67523ddccf"><code>99af3fc</code></a> Merge commit from fork</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/2337f7ecd629c8e777684a2edff341976230ad67"><code>2337f7e</code></a> Merge branch 'test/improve-example.html' into release/11.16.1</li> <li>Additional commits viewable in <a href="https://github.com/mermaid-js/mermaid/compare/mermaid@11.16.0...mermaid@11.16.1">compare view</a></li> </ul> </details> <br /> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |