mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-06 10:48:12 +02:00
d9449e636e33c4f22096019b679725bda641cefc
3944
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d9449e636e |
feat(onboarding): sign in to an agent provider during onboarding (#12440)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - New organizations create their first agent through the onboarding wizard > - The wizard does not show provider sign-in when a host credential is absent or unknown > - The create step also gives unclear feedback when the provider needs authentication > - This pull request adds a safe auth signal and a provider sign-in step for sandbox drivers > - The benefit is a clearer onboarding path with no token or account data in the signal ## Linked Issues or Issue Description **Subsystem affected** Cross-cutting (server API, shared types, and UI) **Problem or motivation** The onboarding wizard can fail when the selected provider needs authentication. It does not tell the person how to complete sign-in. **Proposed solution** Add a status-only provider auth signal. Show the sign-in panel for sandbox drivers when the signal says `absent` or `unknown`. Apply a stored Claude login to the new agent and block creation when the adapter test reports missing authentication. **Alternatives considered** The wizard could hide the sign-in panel when the signal read fails. This would hide a needed action, so this pull request shows the panel when the signal is unknown. **Roadmap alignment** The change supports the roadmap goal for scoped and audited credential bindings. **Additional context** The auth signal returns only `present`, `absent`, or `unknown`. It never returns a token, identifier, or account name. ## What Changed - Add `GET /api/companies/:companyId/adapters/:type/auth-signal` with company and permission checks. - Add shared auth-signal types and the UI query path. - Apply a stored Claude login by reference without reading its token. - Show the provider sign-in panel only for sandbox drivers with interactive terminal support. - Block agent creation when the provider test reports missing authentication. - Add route, wizard, and end-to-end test coverage. ## Verification - `pnpm --filter @paperclipai/server test adapter-auth-signal-routes` passes 50 tests. - `pnpm --filter @paperclipai/ui test OnboardingWizard` passes 69 tests. - `pnpm --filter @paperclipai/ui exec tsc --noEmit` exits with code 0. - The `e2e_shards` lane runs `tests/e2e/onboarding.spec.ts`. ## Risks The route reads a host-local readiness signal. It returns `unknown` on read errors and never exposes credential data. The UI may add a sign-in step when the signal is unavailable. ## Model Used OpenAI Codex, GPT-5, extended reasoning, tool use, and code execution. The exact context window was not provided. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes: #` / `Refs: #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run 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> |
||
|
|
66e1c0df8b |
ci: pin PR base snapshot validation (#12450)
## Summary - rotate the thin caller from `5ac66b3fdd5dc22c0c4e5fdb234ac063cf1d9ff8` to `c119c4bee6ebb9c81791d7a6994f1be06d7cc22b` - keep both exact SHAs authorized during rotation - keep AWS routing disabled until verification completes ## Validation - actionlint passes - workflow-contract tests pass - internal trust-routing harness passescanary/v2026.828.0-canary.8 |
||
|
|
c119c4bee6 |
fix(ci): validate the PR base snapshot (#12449)
## Summary - validate the event base ref/SHA against the live PR state instead of requiring the moving `master` branch tip to remain unchanged while a hosted gate queues - retain exact event/live merge parent and tree validation, plus author/sender/rerun checks ## Canary finding A seven-minute hosted-gate queue allowed `master` to advance. Requiring the live branch tip to equal the event base snapshot would route otherwise valid trusted runs back to GitHub-hosted indefinitely on a busy repository. ## Validation - actionlint and workflow-contract tests pass - internal routing harness passes - replaced PR base snapshot, stale head, changed merge parent/tree, and untrusted actors all remain fail-closed - AWS routing remains disabled during rotation |
||
|
|
5a9c06ab66 |
ci: pin workflow merge ref validation (#12448)
## Summary
- rotate the thin caller from `b88fadb0390d2113933d5d155f16b07cbd5dafee`
to `5ac66b3fdd5dc22c0c4e5fdb234ac063cf1d9ff8`
- keep both exact SHAs authorized during rotation
- keep AWS routing disabled until verification completes
## Validation
- actionlint passes
- e2e workflow-contract tests pass
- internal routing harness validates a single output and `${{ github.sha
}}` merge-ref source
|
||
|
|
5ac66b3fdd |
fix(ci): validate the workflow merge ref (#12447)
## Summary
- use `${{ github.sha }}` as the event merge commit validated by the
trusted gate
- retain exact base/head parent and identical-tree comparison against
the current live merge ref
## Canary finding
GitHub leaves `pull_request.merge_commit_sha` empty on some `opened`
payloads even though the workflow runs against a valid merge ref. The
gate safely fell back to GitHub-hosted and no EC2 instance launched.
## Validation
- `actionlint .github/workflows/pr-trusted.yml .github/workflows/pr.yml`
- `node --test ./scripts/__tests__/e2e-shard.test.mjs`
- internal routing harness passes and asserts `EVENT_MERGE_SHA` is
sourced from `${{ github.sha }}`
- AWS routing remains disabled during rotation
|
||
|
|
c75507fd3f |
ci: pin single runner route output (#12445)
## Summary - rotate the thin PR caller from `3b295b05dc8c8dd82c12e4a9c6f721446c5cb2e8` to `b88fadb0390d2113933d5d155f16b07cbd5dafee` - keep both exact SHAs authorized during the rotation - keep AWS routing disabled until the caller and boundary verify ## Validation - `actionlint .github/workflows/pr.yml .github/workflows/pr-trusted.yml` - `node --test ./scripts/__tests__/e2e-shard.test.mjs` - internal routing harness requires exactly one runner output per gate execution |
||
|
|
b88fadb039 |
fix(ci): emit one runner route (#12444)
## Summary - emit exactly one `runner` job output from the trusted gate - write `ubuntu-latest` only inside fail-closed paths - write the Fleet label only after every identity, PR-state, merge-equivalence, and rerun-actor check passes ## Canary finding The live gate reached the trusted success notice, but GitHub retained the first of two duplicate `runner=` outputs, so policy still requested `ubuntu-latest`. No EC2 instance launched. Routing was disabled immediately. ## Validation - `actionlint .github/workflows/pr-trusted.yml .github/workflows/pr.yml` - `node --test ./scripts/__tests__/e2e-shard.test.mjs` - internal routing harness passes and now requires exactly one runner output in all cases - AWS routing remains disabled during rotation |
||
|
|
47bd4d4803 |
docs: point section 11 at the renamed implementation plan (#11677)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The `doc/` tree is how a contributor learns the system
> - Section 11 of `doc/DEPLOYMENT-MODES.md` is a map to the other
documents
> - One entry on that map points at a file that is not there
> - Commit `9c7d9ded1` moved every plan into `doc/plans/` with a date
prefix, and this link kept the old name
> - This pull request updates the link to the current file name
> - The benefit is that a reader who follows the map arrives at the plan
## Linked Issues or Issue Description
No issue exists. The problem is below.
**Issue type**
Docs. A link points at a file name that changed.
**Where is the issue?**
`doc/DEPLOYMENT-MODES.md`, line 176, in section 11 "Relationship to
Other Docs".
**What's wrong?**
The line reads:
```
- implementation plan: `doc/plans/deployment-auth-mode-consolidation.md`
```
That file is not in the repository. Commit `9c7d9ded1` ("docs: organize
plans into doc/plans
with date prefixes") renamed it to
`doc/plans/2026-02-23-deployment-auth-mode-consolidation.md`.
The renamed file is there today. The link text was not updated.
The other four entries in section 11 are correct. I checked each one:
`doc/SPEC-implementation.md`, `doc/DEVELOPING.md`, `doc/CLI.md` and
`doc/spec/invite-flow.md`
all exist.
**Suggested fix**
Use the new file name in the link. That is what this pull request does.
## What Changed
- `doc/DEPLOYMENT-MODES.md` line 176 now names
`doc/plans/2026-02-23-deployment-auth-mode-consolidation.md`
## Verification
Run this command. It prints the file, so the new name is correct:
```
ls doc/plans/2026-02-23-deployment-auth-mode-consolidation.md
```
Run this command. It prints nothing, so the old name is wrong:
```
ls doc/plans/deployment-auth-mode-consolidation.md
```
## Risks
Low risk. The change is one file name in one line of documentation. No
code changes.
## Model Used
Claude (Anthropic), model ID `claude-opus-5`, 1M context window,
extended thinking, with tool
use and code execution.
The path was found by [docproof](https://github.com/melbinjp/docproof),
an open source
checker that compares what documentation claims against what the
repository contains. A
person then read the document, checked the rename in `git log`, and
confirmed the other four
links in the same section.
## 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
- [ ] 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
One box stays unticked and I would rather say so than tick it. I did not
run the test suite
locally, because the change is one line of prose and no test reads it.
The other two were unticked when this opened, because CI and Greptile
had not run yet. Both
have now. **29 checks pass and 2 report `skipping`** (Contributor trust,
Storybook visual
regression); none fail. Greptile returned **5/5** with no P2s: *"The
documentation-only change
appears safe to merge. The new path exists ... and replaces an obsolete
path that no longer
exists."*
|
||
|
|
de00d78854 |
ci: pin equivalent merge validation (#12442)
## Summary - rotate the thin PR caller from `d9fc93d8383ece6fba721881a7aba638867f4996` to `3b295b05dc8c8dd82c12e4a9c6f721446c5cb2e8` - keep both exact workflow SHAs authorized in the runner group during the rotation - keep `AWS_CI_ENABLED=false` until this caller is merged and verified ## Validation - `actionlint .github/workflows/pr.yml .github/workflows/pr-trusted.yml` - `node --test ./scripts/__tests__/e2e-shard.test.mjs` - full AWS/GitHub boundary verification passes with zero active runners |
||
|
|
3b295b05dc |
fix(ci): validate equivalent PR merge refs (#12441)
## Summary - validate the live master ref and event base SHA before AWS routing - accept GitHub synthetic merge commits only when the event and live commits have the exact expected base/head parents and identical tree - preserve fail-closed routing for malformed, stale, replaced, or untrusted events ## Canary finding A trusted reopened PR produced two synthetic merge SHAs with different timestamps but identical current base/head parents and tree. The former exact-SHA comparison safely fell back to GitHub-hosted runners, but could not route a valid event to AWS. ## Validation - `actionlint .github/workflows/pr-trusted.yml .github/workflows/pr.yml` - `node --test ./scripts/__tests__/e2e-shard.test.mjs` - real-event gate simulation selects the Fleet label for equivalent merge commits - negative simulations keep an untrusted sender and replaced head on `ubuntu-latest` - AWS routing remains disabled during rotation |
||
|
|
c916af0cc0 |
ci: call trusted PR workflow (#12439)
## Thinking Path > - Paperclip uses pull request CI to validate each proposed change > - The existing workflow defines every heavy job in a PR-controlled file > - A trusted reusable workflow now contains the synchronized CI definition > - The caller must use an immutable default-branch SHA > - This pull request replaces the duplicate job list with that pinned caller > - The benefit is automatic secure runner selection without workflow drift ## Linked Issues or Issue Description Refs #12436 Refs #12438 **What existing behavior does this improve?** This improves how the pull request workflow selects trusted CI capacity. **Subsystem affected** Cross-cutting CI automation. **Current behavior** The active workflow contains a duplicate list of all heavy jobs. It cannot use the administrator-controlled runner gate. **Proposed behavior** The active workflow calls the synchronized trusted workflow at an immutable SHA. The trusted workflow selects GitHub-hosted or isolated AWS capacity from the validated contributor identity. **Reason and benefit** The thin caller prevents pull request changes from replacing the external-runner security gate. It also keeps runner selection automatic. **Breaking changes** The check names gain the reusable workflow job prefix. AWS routing remains disabled until the canary starts. ## What Changed - Replaced the duplicated heavy CI job list with one reusable-workflow call. - Pinned the call to the reviewed default-branch commit. - Limited the caller token to actions, contents, and pull request read access. ## Verification - actionlint on both workflow files - Trusted-routing tests - Confirmed the pinned SHA contains the workflow and is an ancestor of master - Full AWS and GitHub runner-boundary verification with routing disabled ## Risks The check context names change when GitHub expands the reusable workflow. The rollout verifies the new aggregate contexts before branch rules change. The repository kill switch remains off during this pull request. ## Model Used OpenAI Codex with GPT-5, tool use, and code execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either linked a related public PR or described the issue with the matching template fields - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal Paperclip 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 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 |
||
|
|
d9fc93d838 |
ci: synchronize trusted PR policy (#12438)
## Thinking Path > - Paperclip uses pull request CI to protect changes before merge > - The trusted reusable workflow will select isolated AWS capacity > - The active workflow changed while the reusable workflow waited for merge > - The reusable policy must contain every current CI policy step before activation > - This pull request synchronizes the migration-order check and shell validation > - The benefit is one reviewed workflow version with verified job parity ## Linked Issues or Issue Description Refs #12436 **What existing behavior does this improve?** This improves the pull request CI workflow synchronization before AWS runner activation. **Subsystem affected** Cross-cutting CI automation. **Current behavior** The active workflow validates migration order. The new reusable workflow does not yet contain that check. **Proposed behavior** Both workflow definitions contain the same heavy jobs and policy steps before the active workflow becomes a thin caller. **Reason and benefit** The synchronization prevents policy drift during the two-step secure rollout. **Breaking changes** None. AWS routing remains disabled. ## What Changed - Added the current migration-order validation to the trusted workflow. - Added the existing shellcheck intent annotation to the active workflow. - Verified normalized heavy-job parity between both definitions. ## Verification - actionlint on both workflow files - Local trusted-routing and normalized workflow-parity tests - git diff --check ## Risks Low risk. The migration check already runs in active CI. This change copies it into the inactive trusted definition. AWS routing stays disabled. ## Model Used OpenAI Codex with GPT-5, tool use, and code execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either linked a related public PR or described the issue with the matching template fields - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal Paperclip 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 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 |
||
|
|
07b6816829 |
ci: add trusted reusable PR workflow (#12436)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Pull request checks protect the quality of the control plane > - The current checks depend only on the shared GitHub-hosted runner limit > - Busy periods leave many pull request jobs queued even when external capacity is available > - Public pull request code must not select or directly access private runner infrastructure > - This pull request adds an inactive reusable workflow with a fail-closed identity gate > - A later pull request can pin this workflow by its full master commit SHA > - The benefit is automatic, controlled access to isolated runner capacity without changing current CI during bootstrap ## Linked Issues or Issue Description **What existing behavior does this improve?** This improves the pull request CI workflow. It prepares the existing checks to use an administrator-controlled runner selection. **Subsystem affected** Cross-cutting GitHub Actions CI configuration. **Current behavior** Every pull request job uses `ubuntu-latest`. Jobs wait when the GitHub-hosted concurrency limit is full. **Proposed behavior** Add a reusable copy of the current PR workflow. A GitHub-hosted gate validates durable numeric user IDs and current GitHub API state. The gate emits one runner label. The default and every validation failure use `ubuntu-latest`. The AWS label is possible only when an administrator enables it and every identity check passes. This bootstrap pull request does not change the active `.github/workflows/pr.yml` caller. A follow-up change will call this workflow by the full master commit SHA. **Reason and benefit** The split bootstrap creates an immutable trust boundary before external runners are reachable. It also keeps CI automatic for contributors. Contributors do not select a runner. **Breaking changes** None in this bootstrap pull request. The active PR workflow does not change. ## What Changed - Added an inactive `workflow_call` copy of the current PR checks. - Added a GitHub-hosted routing gate that checks the repository ID, pull request author ID, event sender ID, rerun actor ID, base branch, head SHA, merge SHA, and current pull request state. - Made every validation failure select `ubuntu-latest`. - Pinned every third-party action to a full commit SHA. - Disabled persistent checkout credentials for all jobs. - Limited the workflow token to Actions read, contents read, and pull request read access. ## Verification - `actionlint .github/workflows/pr-trusted.yml` - Ran the dedicated workflow routing test harness against `.github/workflows/pr-trusted.yml`. - Compared the job keys with `.github/workflows/pr.yml`. The new workflow contains every existing job plus the gate. - Verified each pinned action commit against its current GitHub major-version tag. ## Risks The gate could route a trusted pull request to the wrong runner if an identity check is incomplete. The gate checks durable numeric IDs from the event and current GitHub API state. It checks the rerun actor separately. It defaults to GitHub-hosted capacity before any validation runs. This file is inactive in this pull request. The follow-up caller and runner-group restriction must use the exact commit that reaches `master`. > 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 based on GPT-5. The exact serving model ID and context-window size are not exposed in this environment. The model used high-reasoning, terminal, GitHub API, browser, and web-research capabilities. ## 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 |
||
|
|
5f1e25c112 |
test(server): load the company-skills route module graph once per file (#12426)
## Thinking Path > - Paperclip is an open source app that helps people manage AI agents for work. > - The server provides company-scoped routes for company skills and their test runs. > - The authorization tests for these routes fail intermittently in continuous integration. > - The failure returns HTTP 500 instead of the expected HTTP 403. > - The test file resets and rebuilds its module graph before each test. > - This rebuild imports an unmocked issue service and raises a TypeError. > - This pull request loads the module graph once per describe block. > - The change keeps the test result stable and preserves all 53 tests. ## Linked Issues or Issue Description **What happened?** The company-skill test-run authorization tests failed intermittently in continuous integration. One test returned HTTP 500 instead of HTTP 403. **Expected behavior** Each unauthorized request must return HTTP 403. The test file must keep all 53 tests and skip none. **Steps to reproduce** 1. Run `npx vitest run server/src/__tests__/company-skills-routes.test.ts` before this change. 2. Repeat the run in continuous integration. 3. Observe the intermittent HTTP 500 result in an authorization case. **Paperclip version or commit** Commit `651d26a96f6e24811d336759d3e67ff3abb5ec29`. **Deployment mode** Built from source. Continuous integration runs the test suite. **Agent adapter(s) involved** Not adapter-specific. This issue affects server test module setup. **Database mode** Not database-related. ## What Changed - Load the mocked route module graph once for each describe block. - Use the existing `hoistModuleGraph` helper, as the cost service test does. - Remove twelve per-test `vi.doUnmock` calls and the redundant module rebuild. - Keep the change in `server/src/__tests__/company-skills-routes.test.ts` only. ## Verification - Run `npx vitest run server/src/__tests__/company-skills-routes.test.ts`. - Confirm that 53 tests pass and 0 tests skip. - Confirm that the diff changes only `server/src/__tests__/company-skills-routes.test.ts`. - Confirm that all Paperclip continuous integration checks pass. ## Risks Low risk. The change affects test setup only. It does not change production code, route behavior, or assertions. ## Model Used OpenAI GPT-5 (Codex), exact model ID `gpt-5`, tool use and code execution enabled. The runtime does not expose the context window size. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
dbf052577d |
Follow the current onboarding arc in the release smoke (#12423)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - A release is gated by the release smoke: it installs the published
`paperclipai` artifact into a Docker container and drives the sign-in →
onboarding → first-agent path with Playwright
> - That suite runs only from the release pipeline, never on a pull
request, so it sees the UI only after the UI has already changed
> - The onboarding wizard was rebuilt into the agent arc. The "Name your
organization" step, the "Start Onboarding" launcher, and the agent role
picker are all gone
> - The spec still waited for those, so it failed on its first assertion
and blocked every nightly and beta release
> - The failure was also hard to read. The workflow uploaded no
container logs, because it learned the container's name only after the
harness succeeded, and the harness ran the container with `--rm` and
deleted it before anything read it
> - This pull request rewrites the spec to follow the current arc, and
repairs the log capture at both ends
> - The benefit is that nightly and beta releases are unblocked, and the
next failure arrives with the logs attached
## Linked Issues or Issue Description
No existing issue. Describing it inline, following
`.github/ISSUE_TEMPLATE/bug_report.yml`.
Refs #12274 (removed the company-naming step from the wizard).
Refs #12135 (the previous alignment of this spec, before #12274).
Refs #12316 (open; also edits `scripts/docker-onboard-smoke.sh`, in the
bootstrap helpers rather than the container lifecycle, so the two
changes do
not overlap. Whichever lands second should rebase and re-run).
**What happened?**
The release smoke fails.
`tests/release-smoke/docker-auth-onboarding.spec.ts`
never gets past its first wait:
```
✘ tests/release-smoke/docker-auth-onboarding.spec.ts:43:3 › Docker authenticated onboarding smoke › logs in, completes onboarding, and hires the lead agent
Error: expect(locator).toBeVisible() failed — element(s) not found (timeout 20000ms)
> 33 | await expect(wizardHeading.or(startButton)).toBeVisible({ timeout: 20_000 });
```
The spec waits for an `h3` reading "Name your organization" or a
"Start Onboarding" button. Neither exists. #12274 removed the
company-naming
step; the string now survives only in a code comment and in
`ui/src/components/OnboardingWizard.step.test.tsx`, which asserts it is
*absent*. The steps after the first wait are stale too: the CTA on step
1 is
"Continue" and not "Next", the organization input's placeholder changed,
and
the agent step's `#onboarding-agent-role` picker is gone, so every
onboarding
hire is filed under the neutral `general` role.
The suite runs only from the release pipeline, so nothing on a pull
request
saw the drift. Both `smoke_nightly` and `smoke_beta` call the same
reusable
workflow, so every nightly and every beta was blocked.
The failure also arrived without diagnostics. The job's "Capture Docker
logs"
step is `if: always()`, but it is guarded on `SMOKE_CONTAINER_NAME`,
which the
"Launch Docker smoke harness" step writes to `$GITHUB_ENV` only *after*
the
harness returns. On any failure before that the guard is false, the step
does
nothing, and the upload reports "No files were found". Below that,
`scripts/docker-onboard-smoke.sh` starts the container with
`docker run -d --rm`, so the `docker stop` in its EXIT trap deletes the
container and its logs together — and a container that crashes on its
own is
removed the instant its process exits.
**Expected behavior**
The spec walks the onboarding arc the app actually presents, and proves
the
company is created, the lead agent is hired, and the first task is
seeded and
dispatched. When the smoke fails, the run's artifact carries the
container's
logs.
**Steps to reproduce**
1. Run the Release Smoke workflow against a published artifact that
carries
#12274, or run it locally:
`PAPERCLIPAI_VERSION=2026.828.0-canary.3 SMOKE_DETACH=true
./scripts/docker-onboard-smoke.sh`
2. Run `pnpm run test:release-smoke` against that container.
3. The single spec fails at `openOnboarding()` after 20 seconds.
4. In CI, open the run's `release-smoke` artifact. It has no
`docker-onboard-smoke.log`.
**Paperclip version or commit**
`2026.828.0-canary.3` (commit
nightly/v2026.828.0-nightly.0
beta/v2026.828.0-beta.0
v2026.831.0
canary/v2026.828.0-canary.5
|
||
|
|
7b73b08250 |
ci: enforce migration order against PR target (#12433)
## Thinking Path > - Paperclip is the open source app that people use to manage AI agents for work. > - Paperclip applies database migrations in numeric order. > - Two branches can create the same migration number before either branch merges. > - The existing repository check can find duplicates only after both histories are present in one checkout. > - A pull request must compare its new migrations with the target branch before merge. > - This pull request adds that comparison to the existing PR policy job. > - The benefit is an early failure with exact renumbering instructions. ## Linked Issues or Issue Description **What existing behavior does this improve?** The PR policy check for files in `packages/db/src/migrations`. **Current behavior** A stale branch can add the same migration number as the target branch. The existing check does not compare PR additions with the target branch migration tip. **Proposed behavior** The policy job fails when a new PR migration number is not greater than every migration on the target branch. The error names the conflict, the next safe number, and the related files to update. **Reason and benefit** This prevents duplicate or out-of-order migration numbers from reaching `master`. It also gives contributors and agents a direct repair procedure. **Breaking changes** None. The change rejects migration numbering that is already unsafe. ## What Changed - Added a dependency-free check that compares new PR migration files with the target branch tip. - Added the check to the existing PR policy job. - Added tests for no-op, valid, duplicate, and lower-number cases. ## Verification - `node --test '.github/scripts/tests/*.test.mjs'` passed 133 tests. - `pnpm -r typecheck` passed. - `pnpm build` passed. - `pnpm test:run` passed 4,889 tests and failed 24 unrelated macOS path and wildcard-listener tests that also affect the current `master` checkout. ## Risks - Low risk. The check reads Git history and does not modify migrations. - The check permits gaps. It only requires each new migration number to follow the target branch tip. - The existing migration check continues to validate duplicate numbers, snapshots, and journal entries inside the PR. ## Model Used OpenAI Codex, GPT-5. The exact serving model ID and context-window size are not exposed in this session. Reasoning, tool use, web access, and code execution 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 - [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.828.0-canary.4 |
||
|
|
8316ceb0b9 |
Add the Better Auth issuer column so signup and sign-in work (#12396)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - A self-hosted install in `authenticated` mode signs users in with Better Auth, mounted at `/api/auth` over a hand-written Drizzle `account` table in `packages/db` > - Better Auth 1.7.0 added a required `issuer` field to that `account` model, plus a unique index on `(issuer, accountId)` > - The dependency bump in #11886 changed only `server/package.json` and the lockfile, so the Drizzle table never grew the column > - The Drizzle adapter checks the model against the schema on every write, so `linkAccount` throws and sign-up answers 500 with an empty body; a fresh install cannot create its first user, and an upgraded install locks out every existing user > - This pull request adds the `issuer` column and its unique index, and migrates the column in with a backfill that covers every existing row > - The benefit is that sign-up and sign-in work again, on a new install and after an upgrade ## Linked Issues or Issue Description No existing issue. Describing it inline, following `.github/ISSUE_TEMPLATE/bug_report.yml`. Refs #11886 (the dependency bump that introduced the required field). Refs #12269 (an earlier attempt at this fix; its backfill covers only `provider_id = 'credential'`). **What happened?** Sign-up fails on a self-hosted install. `POST /api/auth/sign-up/email` answers HTTP 500 with a zero-byte body. The server log carries: ``` [Better Auth]: The field "issuer" does not exist in the "account" Drizzle schema. # SERVER_ERROR: [BetterAuthError: The field "issuer" does not exist in the "account" Drizzle schema.] ``` The request writes the `user` row and then fails on the `account` row. The address is stuck after that: a second sign-up answers 422 `USER_ALREADY_EXISTS`, sign-in answers 401, and password reset answers 400 `RESET_PASSWORD_DISABLED` because the account that would hold the password does not exist. An upgraded install is worse. `sign-in/email` matches the credential account on `account.issuer === 'local:credential'`. Rows written before the upgrade have no issuer, so every existing user is locked out. **Expected behavior** `POST /api/auth/sign-up/email` answers 2xx and writes both the `user` row and its credential `account` row. `POST /api/auth/sign-in/email` then answers 2xx and sets a session cookie. An install that upgrades keeps its existing users. **Steps to reproduce** 1. Start a server from `master` with `PAPERCLIP_DEPLOYMENT_MODE=authenticated` against an empty database. 2. `curl -X POST http://127.0.0.1:<port>/api/auth/sign-up/email -H 'Content-Type: application/json' -H 'Origin: http://127.0.0.1:<port>' --data '{"name":"A","email":"a@example.com","password":"a-long-password"}'` 3. The response is HTTP 500 with an empty body. **Paperclip version or commit** `master` atcanary/v2026.828.0-canary.3 |
||
|
|
dc7a1a020a |
fix(adapter-utils): skip the remote session close when the duplex channel is already lost (#12394)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The adapter runtime settles each run through a duplex control
channel
> - A lost channel can leave the remote session-close call without a
usable peer
> - The call has no deadline, so run teardown can wait for the full
adapter timeout
> - This pull request skips that remote call after the runtime latches
channel loss
> - The benefit is faster run finalization while the local cleanup
effects remain
## Linked Issues or Issue Description
**What happened?**
The run teardown placed a remote session-close call over a duplex
control channel that the runtime had already latched as lost. The call
blocked until the adapter execution timeout released it.
**Expected behavior**
Run teardown should release the local warm handle and continue when the
duplex control channel has already failed.
**Steps to reproduce**
1. Start an adapter run with the duplex control channel.
2. Latch a channel-loss state before settlement.
3. Use a runtime whose close call never resolves.
4. Confirm that teardown returns without a remote close call.
**Paperclip version or commit**
Commit
canary/v2026.828.0-canary.2
|
||
|
|
7895f7f2b0 |
Install the declared Sentry server package into the hosted image (#12330)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Paperclip supports opt-in Sentry error monitoring for server and browser errors. > - The hosted image must include the server package when an operator sets SENTRY_DSN. > - The server package is an optional peer in the source tree, so the image did not include it. > - This pull request installs the declared server package in the hosted image and checks the result. > - The benefit is a hosted tenant can send server errors without a manual package install. ## Linked Issues or Issue Description No public issue exists for this change. **What happened?** The hosted image did not include the declared @sentry/node server package. A hosted tenant could set SENTRY_DSN, but the server could not load the package from the image. **Expected behavior** The hosted image must include the exact @sentry/node version from server/package.json. The self-hosted image must remain without this optional package. **Steps to reproduce** 1. Build or pull the hosted image. 2. Resolve @sentry/node from the server package path. 3. Compare its version with server/package.json. 4. Confirm that the tsx loader path still resolves. **Paperclip version or commit** Commit b6ff556a33ebdbe764b7f495951cd59009776608. **Deployment mode** Docker hosted image. ## What Changed - Add a cloud-server-deps Docker stage that installs the declared @sentry/node version in isolation. - Copy the isolated package into the cloud image without changing the production image. - Add a probe that checks the tsx loader and the resolved Sentry version. - Run the probe after the hosted image push in the Docker workflow. - Add server tests and update the observability documentation. ## Verification - Run `pnpm exec vitest run --project @paperclipai/server server/src/__tests__/cloud-image-sentry.test.ts`. - Confirm that the changed test passes in CI. - Confirm that all pull request checks pass. - Note that the Docker workflow does not run for pull requests. It runs after a push to master, for configured tags, or after manual dispatch. ## Risks - Low risk. The production image body stays unchanged. - The cloud image adds the declared Sentry package and a small dependency tree. - The workflow probe fails if the image loses the tsx loader or resolves a different Sentry version. ## Model Used OpenAI GPT-5; exact model version supplied by the execution service; tool use and code execution; context window not specified. ## 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> |
||
|
|
b7fd6c59b6 |
fix(server): load the costs-service route module graph once per file (#12375)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The server test suite checks budget and cost routes > - The costs-service test rebuilt the full route module graph before every test > - Synchronous graph rebuilds caused long stalls under CPU load > - This pull request loads the mocked graph once for each describe block and keeps per-test mock setup > - The benefit is a stable 17-test file without production code changes ## Linked Issues or Issue Description **What happened?** The costs-service route test rebuilt its full mocked module graph before every test. Under CPU load, a rebuild sometimes stalled a test past the 15-second timeout. **Expected behavior** The test file should load its mocked route graph once for each describe block while each test keeps isolated mock behavior. **Steps to reproduce** 1. Run the costs-service route test under synthetic CPU load. 2. Repeat the file test 30 times. 3. Observe intermittent test timeouts before this change. **Paperclip version or commit** The test used the current master branch at the time of this change. **Deployment mode** Built from source. ## What Changed - Add `hoistModuleGraph` to load the mocked route graph once for each describe block. - Keep per-test mock setup in `beforeEach` so test isolation stays unchanged. - Keep all 17 tests and their assertions. - Remove the module graph rebuild from the per-test path. ## Verification - Run `npx vitest run src/__tests__/costs-service.test.ts` from `server/`. - Confirm that the file reports 17 tests and zero skipped tests. - Confirm that 30 runs under the same synthetic CPU load report 0.0% failure after the change, compared with 10.0% before the change. - Confirm that mutation checks still fail when each authorization guard is broken. ## Risks This change affects test setup only. The main risk is weaker test isolation if a mock keeps state between tests. Each test still re-arms its mock behavior in `beforeEach`, and the full assertion set remains. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. This bug fix does not add a core feature. ## Model Used OpenAI GPT-5. The model used tool calls and code execution. The exact context window and reasoning configuration were 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 (the submitting engineer ran the file before handoff) - [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.828.0-canary.1 |
||
|
|
4436cf00a2 |
Render the onboarding agent arc's real steps in Storybook (#12369)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - New tenants meet it through an onboarding wizard: create your agent, connect a model, review > - Those three screens only render for a signed-in account that owns a provisioned stack, so the only way to look at one was to walk a real signup > - Round 4 redesigned all three and shipped them without anyone seeing them render; when the connect step then failed on a live stack, the review step behind it could not be reached at all > - Storybook already mounts the app's provider stack and stubs `/api`, and already has an `Onboarding/Agent arc` story — but that story previews `AgentCapsule`, which the wizard stopped using in round 4 > - This pull request mounts the real wizard in Storybook at each step, and replaces the stale capsule stories with the component the arc actually renders > - The benefit is that these screens can be reviewed, and regressions seen, without provisioning anything ## Linked Issues or Issue Description No existing issue. Describing it inline, following `.github/ISSUE_TEMPLATE/enhancement.yml`. **What existing behavior does this improve?** Reviewing the tenant onboarding wizard. Today it can only be seen by signing up for a real account and provisioning a real stack. **Subsystem affected** `ui` — the onboarding wizard and its Storybook coverage. **Current behavior** `OnboardingWizard` renders only for a signed-in account that owns a company. There is no route, harness, or story that mounts it, so no screen in the agent arc can be looked at in isolation. The existing `Onboarding/Agent arc` story previews `AgentCapsule` in `slot`/`configured`/`online` and describes it as what "the wizard holds in one tree slot" — round 4 replaced that with `PillGuy` and `dormant`/`alive`, so the story documents a component the arc no longer renders. **Proposed behavior** Stories that mount the real `OnboardingWizard` at each of the three steps against the existing Storybook API fixtures, plus stories for `PillGuy` and its transition. **Reason and benefit** Round 4 shipped three redesigned screens that nobody could see. The connect step then failed on staging, which made the review step unreachable even with an account — reviewing it required hand-editing `localStorage`. Stories remove that whole class of problem. **Breaking changes** None. Storybook-only; no product code is touched. ## What Changed - `CreateYourAgent`, `ConnectAModel`, `Review` — the real wizard, per step. - `PillStates` and `PillMorph` — the two states, and the transition on a loop with a toggle. The morph is the arc's payoff and the hardest thing to judge from a still. - Replaces the `AgentCapsule` stories in this file with `PillGuy`. `AgentCapsule` is still used by `DesignGuide` and keeps its coverage there. - Four routes added to the Storybook fetch fixtures: `/api/instance/settings`, `…/environments`, `…/adapters/:type/models`. The empty environment list is the cloud-tenant shape, and also the state that produces the "no managed sandbox environment is available" notice — worth being able to look at rather than only meeting it on a live stack. ## Three properties of the wizard the stories had to respect Each of these cost a debugging cycle, so they are documented at the call site: 1. **The draft is seeded during render, not in an effect.** Roughly twenty `useState(saved?.x ?? default)` initializers read the restored blob exactly once, so a draft written after mount arrives too late. 2. **Nothing mounts until the companies list settles.** The wizard's own mount gate waits on `isFetching`, but that query is *disabled* until the account settles, and a disabled query is not fetching. Mounting straight away gets an inner wizard that reads a null draft, falls back to `initialStep`, and then persists that back over the seed. A real session never hits this because the dashboard has already loaded the list. 3. **The review step opens with no `initialStep`.** An explicit option takes precedence over saved state by design, so passing one clamps 5 to 4 and lands on Connect. ## Verification - `npx vitest run src/components/OnboardingWizard.test.tsx src/components/OnboardingWizard.step.test.tsx` — 44 tests, all passing. - `npx tsc -p tsconfig.json --noEmit` — no new errors. (`src/lib/sentry.*` reports pre-existing missing-type errors for `@sentry/browser` on master.) - Each of the three step stories loaded in a running Storybook and read back: - `create-your-agent` → "STEP 1 OF 3 / Create your first agent / Name / Next" - `connect-a-model` → "STEP 2 OF 3 / Connect a model / Paperclip works with your existing subscription or API keys. / Claude Code / Codex / Advanced settings / Connect" - `review` → "STEP 3 OF 3 / Let's get started... / Darnold is ready to work! / Get started" One difference from a real walk, worth knowing before treating a story as ground truth: the review story has no Back button, because `entryStep` is 5 there and back-navigation is bounded by where the run entered. ## Risks Low. Storybook-only — no product code, routes, or bundles change. The four added fetch fixtures are inside the Storybook mock and cannot affect the app. The one thing to watch: these stories mount the real wizard, so a future change to how it restores drafts or decides its initial step can break them. That is arguably the point — the stories would be the first place it shows — but it does mean they are coupled to internals rather than to a prop surface, and the three notes above are what a maintainer needs. ## Model Used Claude Fable 5 (`claude-fable-5`), extended thinking, with tool use: file editing, shell, and browser automation for loading and reading back each story. ## 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: Claude Fable 5 <noreply@anthropic.com>canary/v2026.828.0-canary.0 |
||
|
|
17ebcc65b7 |
refactor(daytona): simplify the login session-home create (#12348)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Sandbox providers let agents run in remote environments > - The Daytona login flow creates a home directory for each login session > - The create path ran owner, mode, and link-type checks inside the sandbox > - These checks cannot protect the host because sandbox code can change the checked state > - This pull request uses one `mkdir -p` command and removes the unused helper scripts > - The benefit is a simpler login path with the host-side credential checks unchanged ## Linked Issues or Issue Description **What happened?** The Daytona login flow used helper scripts and inside-sandbox checks for the session-home directory. The standalone package build also copied a scripts directory that no longer existed after the helper scripts were removed. **Expected behavior** The login flow must create the session home with one `mkdir -p` command. The package build must complete without copying a removed directory. **Steps to reproduce** 1. Build the Daytona plugin package. 2. Start a Daytona device login. 3. Inspect the session-home create command and the package output. **Paperclip version or commit** Commit `dfdf5914ba37caa1e3bc380236844a7d76237e12`. **Deployment mode** Built from source. ## What Changed - Replace the session-home helper checks with one `mkdir -p` command. - Remove the two unused session-home helper scripts. - Remove the dead build copy steps for the deleted scripts directory. - Add coverage for a failed session-home create command. - Keep the host-side credential reader unchanged. - Keep the Kubernetes provider package unchanged. ## Verification - Package unit tests pass: 220 passed, 6 skipped. - Package typecheck passes. - The package build passes and emits 56 files in `dist`. - The roadmap check confirms that this change stays within the planned sandbox-provider work. - GitHub search found no open duplicate or related pull request. ## Risks The login flow no longer reports owner, mode, or link-type errors from inside the sandbox. Those checks did not protect the host. The host-side credential reader still uses no-follow path opens and accepts only a regular file with owner and exact mode `0600`. Risk is low because this change removes checks that cannot enforce the host security boundary. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. This pull request updates an existing sandbox-provider path, not a new core feature. ## Model Used OpenAI Codex, GPT-5. The runtime provides tool use and code execution. The runtime does not expose the context window size or a more specific model identifier. ## 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.827.0-canary.9 |
||
|
|
3d9be6f7fe |
refactor(daytona): simplify the inbound file-sync path (#12329)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Sandbox providers run agent work in isolated environments > - The Daytona inbound file-sync path writes selected sandbox data to host paths > - Its in-sandbox lexical and realpath guards do not protect host-write or Paperclip API authority > - This pull request removes those guards and simplifies the inbound file-sync commands > - The benefit is a smaller path with the same outbound controls and host extraction validation ## Linked Issues or Issue Description No public issue exists for this change. This pull request describes the enhancement. **What existing behavior does this improve?** It improves inbound file and directory synchronization for the Daytona sandbox provider. **Subsystem affected** `packages/plugins` — the Daytona sandbox provider. **Current behavior** The inbound path checks lexical and realpath confinement inside the sandbox. It uses file-descriptor-pinned commands for file promotion, archive extraction, and decompression. These checks do not protect host-write or Paperclip API authority. **Proposed behavior** Remove the inbound lexical and realpath checks. Use `mv -f` for file mappings, `tar -xf` for directory mappings, and `zstd -d -o` for decompression. Keep outbound source checks, atomic snapshot downloads, and tarball member validation. **Reason and benefit** The sandbox boundary protects the relevant authorities. The removed guards run inside that boundary and only produce early errors. The simpler commands reduce code and preserve the controls that protect the host boundary. **Breaking changes** The inbound path no longer rejects mappings because of sandbox-side lexical or realpath confinement. Outbound source validation and tarball member validation remain unchanged. ## What Changed - Remove lexical and realpath confinement guards from inbound file mappings, inbound directory mappings, and post-upload command working directories. - Replace file-descriptor-pinned promotion with one `mv -f` command per file mapping. - Replace file-descriptor-pinned extraction with one `tar -xf` command per directory mapping. - Replace retained-descriptor decompression with `zstd -d -o`. - Keep outbound source guards, atomic snapshot-and-download, and tarball member validation. - Update Daytona tests for the new command shapes and remove tests for the removed rejections. ## Verification - The author ran the Daytona package test suite: 232 tests passed and 6 tests skipped. - The author ran the Daytona package typecheck successfully. - Review the diff and confirm it changes only the three Daytona files named in this description. - Confirm the Storybook check may report `SKIPPED` as an expected repository state. ## Risks The inbound path now trusts the sandbox boundary for host-write protection. A later change that gives sandbox code host-write or Paperclip API authority could require new guards. Outbound source checks and archive member validation remain in place. No Kubernetes or core runtime file changes exist. ## Model Used OpenAI GPT-5; exact model version and context window were not provided; tool use and code 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.827.0-canary.8 |
||
|
|
036600d922 |
fix(db): close test database clients before the embedded Postgres cluster stops (#12335)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Paperclip uses database clients and embedded PostgreSQL test fixtures > - A fixture stopped its embedded PostgreSQL cluster while clients still held connections > - The postgres.js driver then scheduled a write on a stopped connection > - That write escaped the timer callback and caused a test process to exit with an error > - This pull request closes registered clients before the fixture stops its cluster > - The benefit is stable test teardown and clear failure reporting in continuous integration ## Linked Issues or Issue Description Refs: #10869 **What happened?** An embedded PostgreSQL test fixture stopped its cluster while database clients still held open connections. The postgres.js driver then scheduled a deferred write on a dead connection. The write caused an unhandled error after the test shard reported success. **Expected behavior** The fixture closes all live clients for its cluster before it stops the embedded PostgreSQL cluster. Tests then finish without a deferred write on a dead connection. **Steps to reproduce** 1. Run the database regression test with the embedded PostgreSQL fixture. 2. Stop the fixture while its database client still has an open connection. 3. Observe the deferred write and the process exit status. **Paperclip version or commit** Branch base: |
||
|
|
666f5a6e69 |
fix(server): make the wake-claim lease test deterministic (#12331)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The server test suite checks question response delivery and wake claims > - One test used wall-clock time and could start a second delivery under load > - The second delivery reused one promise resolver and could hang for 15 seconds > - This pull request uses the injected clock and one resolver for each wakeup > - The benefit is a deterministic test that fails at once if a second wakeup occurs ## Linked Issues or Issue Description **What happened?** The wake-claim lease test slept for 70 milliseconds before it ran the pending sweep. Under load, the lease could look stale during that interval. The sweep then started a second delivery. The second delivery reused one promise resolver, so the test hung until the 15-second suite timeout. **Expected behavior** The test must control the time used by the service. One wakeup must use one resolver. An unexpected second wakeup must fail at once. **Steps to reproduce** 1. Run the question response delivery test under CPU load. 2. Let the test sleep before the pending sweep. 3. Observe that a second delivery can start and the test can reach the 15-second timeout. **Paperclip version or commit** Commit `0dd735e53a5cc9f6d3395834b826dfb0b1da2ea9`. **Deployment mode** Built from source with the server test suite. **Installation method** Built from source. **Agent adapter(s) involved** Not adapter-specific (core test issue). **Database mode** Not database-related. **Additional context** The change affects one test file. It does not change production source code. ## What Changed - Drive the test with the service's injected clock. - Give each wakeup call its own promise resolver. - Assert that lease renewal advances the last attempt time. - Assert that the wakeup runs one time and the attempt count stays at 1. ## Verification - Run `pnpm exec vitest run server/src/services/__tests__/question-response-delivery.test.ts`. - The changed file reports 29 passing tests. - Run the changed file 25 times, including 5 runs under CPU load. ## Risks Low risk. The change affects one test file and test setup only. It does not change production behavior. ## Model Used OpenAI GPT-5, exact runtime model ID supplied by the Paperclip agent environment, tool use and code 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.827.0-canary.7 |
||
|
|
bdd8f1bedb |
Repair the drizzle snapshot so generate emits no spurious migration (#12333)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Paperclip keeps its state in PostgreSQL, and `packages/db` owns that schema through Drizzle > - `drizzle-kit generate` writes a new migration by diffing `packages/db/src/schema/` against the newest snapshot in `packages/db/src/migrations/meta/`, so the snapshot must describe the schema that the migrations produce > - Snapshot `0228` recorded the new `error_count` column on the wrong table, and snapshot `0229` inherited the error, so the newest snapshot no longer matched the schema > - Because of that, `generate` on `master` folded the drift into any new migration: it emitted an `ADD COLUMN` for a column that migration `0228` already creates, which fails on a fresh database, plus an out-of-scope `DROP COLUMN` > - This pull request moves the column entry to the correct table in both snapshots and adds a test that repeats the diff `generate` performs > - The benefit is that the next person who generates a migration gets only their own change, and CI fails if the snapshot drifts again ## Linked Issues or Issue Description No existing issue. The description below follows `.github/ISSUE_TEMPLATE/bug_report.yml`. **What happened?** `drizzle-kit generate` on `master` emits a wrong migration. The newest snapshot, `packages/db/src/migrations/meta/0229_snapshot.json`, disagrees with the schema in two places. It omits `issue_question_response_deliveries.error_count`, which `0228_nasty_grim_reaper.sql` creates. It also carries `decision_archive_notification_outbox.error_count`, which no migration ever creates and the Drizzle schema never declared. Snapshot `0228` introduced both halves of the error: it added the new `error_count` column to `decision_archive_notification_outbox` instead of the table that the same migration creates. Snapshot `0229` copied it forward. Any new migration therefore starts with two statements that do not belong to it: ```sql ALTER TABLE "issue_question_response_deliveries" ADD COLUMN "error_count" integer DEFAULT 0 NOT NULL; ALTER TABLE "decision_archive_notification_outbox" DROP COLUMN "error_count"; ``` The `ADD COLUMN` fails on a fresh database, because migration `0228` already creates that column. The `DROP COLUMN` targets a column that does not exist on any deployment. **Expected behavior** `drizzle-kit generate` reports "No schema changes, nothing to migrate" on a clean checkout of `master`, and a new migration contains only the author's own schema change. **Steps to reproduce** 1. Check out `master` at commit `bc1a21564`. 2. Run `pnpm install`. 3. Run `pnpm --filter @paperclipai/db generate`. 4. Read the emitted `packages/db/src/migrations/0230_*.sql`. It contains the two statements above, and no schema file was changed. **Paperclip version or commit** `master` at `bc1a21564`. The drift entered in #12307 (snapshot `0228`) and was carried forward by #12291 (snapshot `0229`), which worked around it by building its snapshot by hand. **Deployment mode** Not deployment specific. It affects anyone who generates a migration, and it affects any fresh database that would later run the bad migration. **Database mode** All PostgreSQL modes: embedded, local Docker, and hosted. ## What Changed - Moved the `error_count` column entry from `decision_archive_notification_outbox` to `issue_question_response_deliveries` in `packages/db/src/migrations/meta/0228_snapshot.json` and `packages/db/src/migrations/meta/0229_snapshot.json`. Both files keep their `id` and `prevId`, so the snapshot chain is unchanged. - Added `packages/db/src/migration-snapshot-drift.test.ts`. It reads the newest snapshot named by `_journal.json`, serializes the schema modules with `generateDrizzleJson`, and asserts that `generateMigration` returns no statements. This is the same diff that `generate` performs. - Documented the snapshot rule in `doc/DATABASE.md` under a new "Migration snapshots" section. No migration SQL was added, renumbered, or edited. No schema file changed. The database is correct as it is; only the snapshot was wrong. Why both snapshots and not only the newest one: `0229` is the file that `generate` reads, so repairing it is what fixes the bug. `0228` holds the same error, and `drizzle-kit drop` removes the last migration and its snapshot, which would promote `0228` back to newest and bring the drift back. Repairing both removes that trap. Snapshots are never applied to a database, so neither edit changes any deployment. ## Verification Commands run from the repository root. - `pnpm --filter @paperclipai/db generate` — "No schema changes, nothing to migrate 😴". It writes no SQL file, no snapshot, and no journal entry. `git status` stays clean. Before the fix, the same command wrote `0230_fast_caretaker.sql` with the two spurious statements. - The repaired `0229_snapshot.json` is byte-identical to the snapshot that a real `generate` run produced, except for the `id` and `prevId` that keep the chain intact. - Chain check: the repaired `0228` and `0229` snapshots now differ by exactly the two columns that `0229_drop_company_brand_color_and_attachment_max_bytes.sql` drops, `companies.brand_color` and `companies.attachment_max_bytes`, and by nothing else. - Database check: applied all 229 migrations in order to an embedded PostgreSQL, then compared the live schema with the repaired snapshot. 179 tables and 2687 columns match, with no missing column, no extra column, and no nullability difference. The same comparison against the pre-fix snapshot reports exactly two problems: `column only in snapshot: decision_archive_notification_outbox.error_count` and `column only in database: issue_question_response_deliveries.error_count`. This harness was a scratch script and is not part of the pull request. - `pnpm --filter @paperclipai/db typecheck` — pass. It runs `check:migrations`, which is `check-migration-numbering` and `check-migration-safety`. - `npx vitest run --root packages/db` — 28 files, 102 tests, all pass. This includes the new test. - New test, negative case: with the pre-fix `0229_snapshot.json` restored, `migration-snapshot-drift.test.ts` fails and prints exactly the two spurious statements, plus the instruction to run `generate`. It passes on the repaired snapshot. It takes about 1.2 seconds and needs no database. - `node scripts/check-forbidden-tokens.mjs` and `node scripts/check-no-git-push.mjs` — pass. ## Risks Low risk. A Drizzle snapshot is a build-time record for `drizzle-kit generate`. It is never applied to a database, so this change cannot alter any deployment, and no operator action is needed. Databases that already ran migrations `0228` and `0229` are correct today and stay correct. The proof is a clean `generate`: the command that produced the wrong migration now reports "No schema changes, nothing to migrate" and writes nothing. Two smaller notes: - The new test depends on `drizzle-kit/api`, which is already a dev dependency of `packages/db`. If a future `drizzle-kit` upgrade changes that surface, the test fails loudly at import rather than passing silently. - The test imports every module in `packages/db/src/schema/`, which is the same set that `drizzle.config.ts` points the CLI at. It deduplicates by object identity, because the barrel re-exports the same table objects and `drizzle-kit` rejects a table it sees twice. ## Model Used Claude (Anthropic), Claude Opus, agentic tool use via Claude Code. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [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.827.0-canary.6 |
||
|
|
bc1a21564f |
Remove the company brand color and per-company attachment limit (#12291)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - A company is the top-level container, and the company General page
holds its settings
> - Two of those settings did almost nothing: the brand color only
tinted the generated company icon, and the attachment size limit sat
under the deployment-level `PAPERCLIP_ATTACHMENT_MAX_BYTES` cap that
already bounded every upload
> - A setting that changes one icon hue, and a setting that can only
lower a limit the operator already set, are not worth the page space or
the code that carries them
> - This pull request deletes both settings from the UI, the validators,
the API contract, the server, and the database
> - With the deployment cap as the only limit left, the message a person
sees when an upload is rejected has to name that limit in terms they can
act on, so the raw byte count becomes a human-readable size
> - The benefit is a shorter company General page for every deployment,
one attachment limit instead of two, and less code between an upload and
its ceiling
## Linked Issues or Issue Description
No existing issue. The description below follows
`.github/ISSUE_TEMPLATE/enhancement.yml`.
**What existing behavior does this improve?**
The company General page (`/company/settings`), the `PATCH
/api/companies/{companyId}` and `PATCH
/api/companies/{companyId}/branding` request contracts, and the
attachment upload limit on task, case, and company-import uploads.
**Subsystem affected**
Cross-cutting: `ui/`, `server/`, `packages/shared`, `packages/db`.
**Current behavior**
The company General page shows an "Appearance" section with three
controls: Logo, Brand color, and Attachment size limit. The brand color
is a hex value that feeds one thing — the hue of the generated company
pattern icon. Companies that never set one already get a hue derived
from the company name. The attachment size limit is a per-company byte
count stored on `companies.attachment_max_bytes`. Every upload path
clamps it against the deployment-level `PAPERCLIP_ATTACHMENT_MAX_BYTES`
cap, so the per-company value can only lower a limit the operator
already chose.
**Proposed behavior**
The Appearance section keeps the Logo control only. The company pattern
icon always derives its hue from the company name. Every attachment path
reads the deployment cap directly, so `PAPERCLIP_ATTACHMENT_MAX_BYTES`
is the single limit. An upload rejected by that limit says so in human
units — "File is larger than the 10 MB limit" rather than a raw byte
count. The `companies.brand_color` and `companies.attachment_max_bytes`
columns are dropped, and both fields leave the company API contract.
**Reason and benefit**
Both settings ask an operator to make a decision that changes almost
nothing. The brand color moves one icon hue on a page that also lets you
upload a real logo, which overrides the icon entirely. The attachment
limit reads as a real control but cannot raise anything, so it is a
second place to look when an upload is rejected. Removing both shortens
the page every deployment sees, removes a company-scoped read from the
task attachment upload path, and leaves one attachment limit to reason
about instead of two.
**Breaking changes**
The company API responses no longer include `brandColor` or
`attachmentMaxBytes`, and `GET /api/invites/{token}` no longer includes
`companyBrandColor`. `PATCH /api/companies/{companyId}/branding` is
strict, so a request that sends `brandColor` now returns 400; the
non-strict `PATCH /api/companies/{companyId}` schema strips it. Company
packages exported by older versions still import: the portability
company manifest schema is non-strict, so the retired keys are stripped
and ignored rather than rejected. Companies that stored a brand color
lose it — their icon reverts to the name-derived hue that every company
without a color already used.
## What Changed
- Removed the "Brand color" and "Attachment size limit" fields from the
company General page, along with their state, dirty checks, save
payload, and Save-button gating.
- Removed `brandColor` and `attachmentMaxBytes` from
`createCompanySchema`, `updateCompanySchema`, and
`updateCompanyBrandingSchema`, and deleted the now-orphaned
`DEFAULT_COMPANY_ATTACHMENT_MAX_BYTES` and
`MAX_COMPANY_ATTACHMENT_MAX_BYTES` constants.
- Removed both fields from the `Company` type, the portability manifest
type and schema, and the `companiesApi.update` payload allowlist.
- Dropped `brandColor` from `CompanyPatternIcon` and its callers, so the
icon hue always comes from the company name. Deleted the now-unused
`hexToHue` helper and the now-unused `pickTextColorForSolidBg` export.
- Stopped emitting `brandColor` from the company service selection and
from the invite-summary and invite-branding payloads in
`server/src/routes/access.ts`.
- Replaced `normalizeIssueAttachmentMaxBytes` with the deployment cap:
task attachments, case attachments, and company import now use
`MAX_ATTACHMENT_BYTES` directly. The helper is deleted.
- Added `formatAttachmentSize()` next to `MAX_ATTACHMENT_BYTES` and
routed every over-limit message through it, so a rejected upload names
the limit in human units instead of raw bytes: `Image exceeds 10485760
bytes` becomes `Image is larger than the 10 MB limit`. Enforcement is
unchanged — the same single cap, the same multer limits, the same status
codes and response shapes.
- Added migration
`0229_drop_company_brand_color_and_attachment_max_bytes.sql` and removed
both columns from the Drizzle `companies` schema.
- Kept legacy imports working: the portability company manifest schema
is non-strict, so older packages carrying the retired keys still import
with the keys ignored.
- Updated the skill API reference and the implementation spec, and
pruned the token-extraction allowlist entries that the removed code made
stale.
## Verification
Commands run from the repository root:
- `pnpm --filter @paperclipai/shared typecheck` — pass
- `pnpm --filter @paperclipai/db typecheck` — pass (includes
`check:migrations`, which validates the new migration number and journal
entry)
- `pnpm --filter @paperclipai/ui typecheck` — pass
- server typecheck via `node_modules/.bin/tsc --noEmit` in `server/` —
pass. `pnpm --filter @paperclipai/server typecheck` could not run
locally because it builds the Rust runner first and `cargo` is not
installed on this machine; the TypeScript step it wraps is the command
above.
- `npx vitest run packages/shared/src/validators/company.test.ts` — 6
passed
- `npx vitest run server/src/__tests__/company-portability.test.ts` — 90
passed
- `npx vitest run server/src/__tests__/attachment-types.test.ts
server/src/__tests__/assets.test.ts
server/src/__tests__/issue-attachment-routes.test.ts
server/src/__tests__/company-portability.test.ts
server/src/__tests__/cases-routes.test.ts` — 165 passed (the
human-readable limit messages)
- `npx vitest run server/src/__tests__/company-branding-route.test.ts
server/src/__tests__/issue-attachment-routes.test.ts
server/src/__tests__/invite-summary-route.test.ts
server/src/__tests__/openclaw-invite-prompt-route.test.ts
server/src/__tests__/companies-route-cross-company-authz.test.ts` — all
passed
- `npx vitest run cli/src/__tests__/company.test.ts
cli/src/__tests__/company-delete.test.ts` — 27 passed
- `npx vitest run` in `ui/` — 4425 passed, 1 pre-existing failure
unrelated to this change (`OnboardingWizard.test.tsx` "renders instead
of throwing when the browser denies storage access", which also fails on
`master`)
- `npx vitest run` in `server/` — see the note below
- `node scripts/check-token-gates.mjs` — no new violations; the only
reported violations are the pre-existing `PillGuy.tsx` ones present on
`master`
New tests added:
- `packages/shared/src/validators/company.test.ts` — the create and
update schemas strip the retired keys, the strict branding schema
rejects `brandColor`, and the portability manifest schema accepts a
legacy entry carrying both keys and drops them.
- `server/src/__tests__/company-branding-route.test.ts` — `PATCH
/api/companies/{companyId}/branding` returns 400 for `brandColor` and
does not call the company service.
- `server/src/__tests__/company-portability.test.ts` — a legacy package
that declares `brandColor` and `attachmentMaxBytes` imports
successfully, and neither key reaches `companies.create`.
- `server/src/__tests__/issue-attachment-routes.test.ts` — the effective
task attachment limit is the deployment cap, and the route no longer
loads the company to size an upload.
- `server/src/__tests__/attachment-types.test.ts` —
`formatAttachmentSize()` renders the default cap as `10 MB`, keeps one
decimal place for fractional sizes and drops a trailing `.0`, falls back
to KB and bytes for small caps, steps up to GB, and never emits `NaN`
for a degenerate input.
- `server/src/__tests__/assets.test.ts` — the asset-image and
company-logo routes both return the human-readable limit message on an
over-cap upload.
## Merge with master
`master` moved while this was open, and the merge needed two
resolutions:
- **`ui/src/pages/CompanySettings.tsx`.** #12243 reworded the
user-facing
copy from "company" to "organization", and that rewording landed inside
the "Brand color" and "Attachment size limit" hints — the two fields
this change deletes. Both fields are removed, so the conflicted block is
dropped whole. The Logo field and every other copy change from #12243
are
kept.
- **Migration renumbered 0228 -> 0229.** #12307 landed
`0228_nasty_grim_reaper`, so this migration is now
`0229_drop_company_brand_color_and_attachment_max_bytes`. Its snapshot
is
rebuilt from master's `0228_snapshot.json` with only the two `companies`
columns removed, and `meta/_journal.json` is master's journal plus a
single `idx: 229` entry. `pnpm --filter @paperclipai/db
check:migrations`
passes.
The snapshot was rebuilt by hand rather than taken from `drizzle-kit
generate`, because master's `0228_snapshot.json` has drifted from
master's
own schema: `issue_question_response_deliveries.error_count` is created
by
master's 0228 SQL but missing from its snapshot, and the snapshot still
carries `decision_archive_notification_outbox.error_count`. Regenerating
folds both into this migration, and the resulting `ADD COLUMN
error_count`
would fail on a fresh database where master's 0228 already created that
column. Rebuilding from master's snapshot leaves that drift exactly
where
it is and keeps this migration to the two column drops. The drift is
pre-existing on master and is not addressed here.
## Risks
- **The migration is a destructive column drop.**
`0229_drop_company_brand_color_and_attachment_max_bytes.sql` removes
`companies.brand_color` and `companies.attachment_max_bytes`. It is safe
because both features are removed in the same change and nothing reads
either column after it. The statements use `DROP COLUMN IF EXISTS`,
matching the convention of the recent drop migrations in this
repository. The drop is not reversible: a downgrade after this migration
loses any stored values.
- **Stored brand colors are lost.** A company that had set a color now
renders the name-derived icon hue that every company without a color
already used. No other surface changes, and an uploaded logo still
overrides the icon.
- **API response shape narrows.** `brandColor` and `attachmentMaxBytes`
leave the company payloads, and `companyBrandColor` leaves the invite
summary payload. A client reading those fields now sees `undefined`. The
bundled UI and CLI are updated in this change.
- **Legacy imports are covered.** Packages exported by older versions
still carry both keys. The manifest schema is non-strict, so the keys
are stripped rather than rejected, and a test locks that in.
- **The over-limit message strings changed.** Anything matching on the
old `... exceeds N bytes` text — a test, a script, or a client that
string-matches `body.error` — needs updating. The status codes (422) and
response shapes are unchanged, so structured clients are unaffected.
- **Attachment limits can only widen.** A deployment that had lowered a
company below the deployment cap now allows uploads up to the cap for
that company. Lower `PAPERCLIP_ATTACHMENT_MAX_BYTES` if a smaller
ceiling is needed.
- **Storybook visual baselines shift** for the `CompanyPatternIcon`
matrix story, because those fixtures had brand colors. That workflow
runs only on a PR labeled `storybook-visual`, so it does not gate this
PR; regenerate the baselines if the label is added.
## Model Used
Claude (Anthropic), Claude Opus, agentic tool use via Claude Code.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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
canary/v2026.827.0-canary.5
|
||
|
|
7b91fe9ea7 |
Hide host-path and execution-engine surfaces in managed-sandbox-only mode (#12293)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - An instance can turn on the `enableManagedSandboxOnly` feature, which hides the local environment and runs every agent in the platform-managed environment > - That feature already gated the environment pickers, the onboarding wizard, and the server-side run selection, but many other screens still showed absolute paths on the execution host and still let the user pick an execution engine > - On such an instance those controls name a filesystem the user cannot reach; a path written there is stored and then ignored, which reads as a broken control > - This pull request hides the remaining host-path and execution-engine surfaces behind the same feature, adds a server rule that refuses a project-workspace path write while the feature is on, and closes a related route gap in the isolated-workspace pages > - The benefit is that a managed instance shows no host path and no folder picker anywhere, and a write that carries a path now fails with a clear message instead of being silently discarded ## Linked Issues or Issue Description No public issue exists. The description below follows `.github/ISSUE_TEMPLATE/enhancement.yml`. **What existing behavior does this improve?** The `enableManagedSandboxOnly` instance feature, and the UI surfaces that show a host filesystem path: project properties, the new-project dialog, the project workspace and execution workspace detail pages, the workspace and task cards, plugin local folders, and the agent configuration form with its per-adapter fields. It also improves route gating for `enableIsolatedWorkspaces`. **Subsystem affected** Cross-cutting (`ui/` and `server/`). **Current behavior** When `enableManagedSandboxOnly` is on, the local environment disappears from the environment pickers and the server refuses to run an agent on the local host. Everything else stays visible. A user still sees: - the project "Local folder" row, its absolute path, and the Set/Change/Clear buttons - the "Local folder" field and its "Choose" folder picker in the new-project dialog - the "Local path" field and fact row on a project workspace - the "Paths" and "Lifecycle commands" groups on an execution workspace - the working directory on workspace cards, task properties, and runtime service rows - the plugin "Local folders" section - "Working directory (deprecated)", "Command", "Execution engine", "ACP server command", "ACP state directory", and "Agent instructions file" in the agent configuration form A path typed into any of these names a filesystem no agent on the instance uses. The project workspace API also accepts a `cwd` write and stores it. Separately, `/workspaces`, `/execution-workspaces/*`, and `/projects/:projectId/workspaces/:workspaceId` render for anyone who types or bookmarks the URL, even with `enableIsolatedWorkspaces` off. Only the sidebar entry reads that flag. **Proposed behavior** With `enableManagedSandboxOnly` on, none of those surfaces render. A project whose codebase came from a managed checkout keeps its one-line "Paperclip-managed folder." label and shows no path. The non-path controls stay: repo URL, branch, service URL, port, command output, ACP session mode, ACP non-interactive permissions, Codex fast mode, and the sandbox toggles. The project-workspace create and patch routes, and the nested workspace on project create, answer `422` with "This instance runs agents only in the platform-managed environment; local folders are not configurable." when the payload carries a non-null `cwd`. A `cwd: null` write still passes, so an instance that just turned the feature on can clear a stale path. With `enableIsolatedWorkspaces` off, the three workspace route groups redirect to the dashboard. **Reason and benefit** A control that cannot do anything is worse than a missing control: the user fills it in, saves, and gets no error and no effect. The server rule turns that silent no-op into a clear refusal. The route gate stops a feature that an instance has turned off from staying reachable by URL, which is the same standard the Cases, Pipelines, and hidden-settings pages already meet. **Breaking changes** None for a default instance: both flags are off by default for self-hosted and managed instances, so nothing changes unless an operator turns them on. Stored `adapterConfig` values are never cleared, so turning the feature off restores every previous value. ## What Changed - Add `ui/src/hooks/useManagedSandboxOnly.ts`, modelled on `useAppsEnabled`, for components that do not already read the experimental settings. It exposes `hideHostPaths`, which fails closed while the settings query is in flight, so a cold cache never flashes a host path before the policy resolves. Components that keep their own settings read compute the same gate from `isFetched`. - Add `managedSandboxOnly` to `AdapterConfigFieldsProps` and populate it where `AgentConfigForm` builds the adapter field props. Resolve the effective instructions-file gate once as `hideInstructionsFile || hideHostPaths`, so every adapter hides that path field with no per-adapter edit. - Hide under the flag: the project "Local folder" block and its absolute-path edit panel (a managed checkout keeps its label, without the path); the new-project "Local folder" field; the project-workspace "Local path" field and fact row; the execution-workspace "Paths" and "Lifecycle commands" groups; the working directory on the workspace summary card, the task workspace card, the task properties "Folder" row, and the runtime service rows; the plugin "Local folders" section; "Working directory (deprecated)" and "Command" in the agent form; and the per-adapter "Execution engine", "ACP server command", and "ACP state directory" for `claude_local`, `codex_local`, and `gemini_local`. - Drop two working-directory fallbacks that had no gate to read: the close-workspace dialog now falls back to "No additional details", and the reuse-existing workspace label and picker subtitle fall back to a neutral phrase. - Refuse a non-null `cwd` with `422` on `POST /projects/:id/workspaces`, `PATCH /projects/:id/workspaces/:workspaceId`, and the nested workspace on `POST /companies/:companyId/projects`, following the `assertNoAgentHostWorkspaceCommandMutation` precedent on those routes. - Add `IsolatedWorkspacesRouteGate` and wrap the `/workspaces`, `/execution-workspaces/*`, and `/projects/:projectId/workspaces/:workspaceId` routes with it. - Leave the SSH "Remote workspace path" and the workspace file browser alone, with a comment explaining why. ## Verification Automated: - `pnpm --filter @paperclipai/ui exec vitest run` — 479 of 480 files pass (4455 of 4456 tests). The one failure is `OnboardingWizard.test.tsx > renders instead of throwing when the browser denies storage access`, which also fails on `origin/master` and is unrelated to this change. - `pnpm --filter @paperclipai/server exec vitest run project workspace instance-settings` — 45 of 50 files pass. Four files fail on macOS for reasons unrelated to this change: `workspace-instance-cleanup`, `workspace-runtime`, `execution-workspace-runtime-control-conflict`, and `workspace-runtime-exposure` compare `/var/...` against the resolved `/private/var/...` or bind real ports. The same files fail on a clean `master` checkout on the same machine. - `pnpm --filter @paperclipai/ui typecheck` - `tsc --noEmit` in `server/` (after `pnpm --filter @paperclipai/plugin-sdk ensure-build-deps`). The package `typecheck` script also builds the Rust runner, which needs `cargo`; it is not installed on the machine that ran this. New and extended tests: - `ui/src/adapters/managed-sandbox-only-config-fields.test.tsx` — the three adapters drop the execution engine, the ACP paths, the instructions-file path, and every "Choose" button when the flag is on, and keep the non-path controls. - `ui/src/components/AgentConfigForm.render.test.tsx` — flag-on and flag-off renders for the working directory, the command, the engine, the ACP paths, and the resolved adapter field props. - `ui/src/components/ProjectProperties.managed-sandbox.test.tsx`, `ui/src/components/NewProjectDialog.managed-sandbox.test.tsx`, `ui/src/pages/ProjectWorkspaceDetail.test.tsx`, `ui/src/components/ProjectWorkspaceSummaryCard.test.tsx`, `ui/src/components/WorkspaceRuntimeControls.test.tsx`. - `ui/src/components/IsolatedWorkspacesRouteGate.test.tsx` — redirect when off, render when on, and render nothing while the flag query is in flight. - "Still loading" cases for the project properties, the new-project dialog, the workspace summary card, the runtime service rows, and the agent configuration form, each asserting that no host path renders before the policy resolves. - `server/src/__tests__/project-workspace-managed-sandbox-routes.test.ts` — the `422` on all three write paths, the `cwd: null` pass-through, and the flag-off pass-through. Manual check to reproduce: turn on Managed Environment Only in instance experimental settings, then open a project, the new-project dialog, an agent's configuration, and a workspace page. No path, folder icon, or "Choose" button appears. Turn the setting off and each control returns with its stored value. No documentation change was needed. The operator-facing text for both settings lives in the feature catalog entry, which already states the contract this pull request now enforces across the UI. ## Risks - Low. Both flags default to off, so a default instance is unchanged. - The hidden fields are presentation only. No stored `adapterConfig` value is cleared, because an import carries adapter configuration written on another instance and clearing it would break that flow. Turning the setting off shows every previous value again. - The `422` is the one behavior change for an API caller, and only while the setting is on. `cwd: null` still passes so a stale path can be cleared. - The route gate renders nothing until the flag query settles, so an instance with isolated workspaces on never flashes a redirect. An instance with the feature off now redirects a bookmarked workspace URL to the dashboard. - Every host-path guard fails closed while the settings query is in flight, so a default instance shows those controls a moment later than before on a cold load. That is the safe direction: the alternative flashes a path a managed instance must never show. - Two path surfaces stay on purpose, each with a comment: the SSH "Remote workspace path" is a path on the user's own remote host, and the workspace file browser shows workspace-relative paths. The instance Adapters page also keeps its "Local path" install option, since that page is an instance-admin surface the hosting operator can already hide through the hidden-settings mechanism. ## Model Used Claude (Anthropic), Claude Opus, 1M context window, extended thinking, agentic tool use through Claude Code. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [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.827.0-canary.4 |
||
|
|
76f7019bdf |
feat(server): derive hosted-tenant issue prefixes from the company name and follow renames (#12292)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Every company has an issue prefix. It is the visible half of each task and case identifier, and a self-hosted company derives it from the name it was created with > - A hosted or managed instance does not use the create-company flow. The trusted-header auth path claims the tenant company instead > - That path minted the prefix from a hash of the stack id, and it wrote a placeholder description that nobody chose > - So a hosted company showed opaque task IDs such as `PC7F2A-14`, and a rename never changed them > - This pull request derives the prefix from the company name on that path too. It re-derives the prefix when the name changes on a managed instance, and it rewrites the stored issue and case identifiers so existing tasks follow the rename > - It also repairs each company that an earlier build claimed. The repair runs once, on the next authenticated request > - The benefit is that task IDs on a hosted instance read like the ones on a self-hosted instance, and they stay correct after a rename ## Linked Issues or Issue Description No public issue exists. The description below follows `.github/ISSUE_TEMPLATE/enhancement.yml`. **What existing behavior does this improve?** The tenant company claim in `resolveCloudTenantActor` (`server/src/middleware/auth.ts`) and the company update in `companyService.update` (`server/src/services/companies.ts`). Both decide the `issue_prefix` and the `description` of a company on a hosted or managed instance. **Subsystem affected** `server/` — REST API and orchestration services. One small hint was also added in `ui/`. **Current behavior** A self-hosted company gets its issue prefix from its name. "Acme Robotics" becomes `ACM`, and its tasks read `ACM-14`. A hosted or managed instance claims the company through the trusted-header auth path. That path wrote a different prefix: `"PC"` plus the first four hex characters of the SHA-256 of the stack id. The same path also wrote a placeholder description, `"Provisioned by ... for stack <stack id>."`. The result is a task ID such as `PC7F2A-14`. It says nothing about the company. A later rename of the company does not change it, because nothing re-derives the prefix after creation. **Proposed behavior** The claim path derives the prefix from the company name, exactly as the create-company flow does. It writes no description. On a managed instance, a rename re-derives the prefix. The stored issue and case identifiers move with it, so `ACM-14` becomes `NOR-14` when "Acme Robotics" becomes "Northwind Traders". A rename that keeps the same three-letter base keeps the current prefix, including any disambiguating suffix. A self-hosted instance is unchanged. A rename there still keeps the prefix the company was created with. Companies that an earlier build already claimed get a one-time repair on their next authenticated request. The repair re-derives the prefix from the current name, re-keys the identifiers, and clears the placeholder description. **Reason and benefit** A task ID is the primary handle for a task. People type it, paste it into chat, and read it in a URL. On a hosted instance that handle was an opaque hash, and it disagreed with the company name that the same user chose during signup. The name is the only prefix source a hosted user ever supplies, so the prefix now follows it. **Breaking changes** Yes, on hosted and managed instances only. A company rename now rewrites the stored issue and case identifiers. Links that carry an old identifier stop resolving after the rename. The company settings page states this before the user saves. The one-time repair applies the same rewrite once to companies that carry the old hash prefix. Self-hosted behavior does not change. ## What Changed - Added `server/src/services/issue-prefix.ts`. It holds the prefix helpers that used to live inside the `companyService` closure: `ISSUE_PREFIX_FALLBACK`, `deriveIssuePrefixBase`, `issuePrefixSuffixForAttempt`, and `isIssuePrefixConflict`. The companies service now imports them. - Added `pickAvailableIssuePrefix` to that module. It reads the prefixes in one base family and returns the first free candidate. A standalone `INSERT` can retry on a unique violation, because each failed statement is its own implicit transaction. A caller that already holds a transaction cannot, because the violation aborts the whole transaction. Such a caller picks first, then writes. - Added `rekeyCompanyIssueIdentifiers` to that module. It rewrites the prefix of the stored `issues.identifier` and `cases.identifier` values of one company in the caller's transaction, and it returns the two row counts. - `companyService.update` re-derives the prefix when the name changes on a managed instance, re-keys both tables in the same transaction, and writes a `company.updated` activity entry after the commit. - `resolveCloudTenantActor` claims the company with a name-derived prefix and a null description. The claim retries with the next suffix when the prefix is taken. - `resolveCloudTenantActor` also runs a one-time repair for companies that carry the old hash prefix. An exact-match fence on the update lets a concurrent rename win. The repair is idempotent, because its guards stop matching after it lands. - The rename takes a row lock on the company before it compares anything against it, and it re-keys from the prefix it reads under that lock. Only patch and environment facts gate the lock, so no stale read can steer the decision. Two overlapping updates would otherwise leave a company whose prefix disagrees with its own identifiers, in either direction: two renames, where the second re-keys from a prefix the first already moved; or a rename plus a stale form that resubmits the original name, where the second sees an unchanged name, skips re-derivation, and restores the old name on top of the first rename's prefix. Only a managed instance takes the lock, and only for an update that carries a name. - Both helpers compare an exact identifier head instead of a LIKE pattern. A stored prefix is data, so it must never be read as a pattern. - The company settings page shows a hint under the name field on a managed instance: renaming can change the task ID prefix. ## Verification Automated tests: ``` pnpm --filter @paperclipai/server exec vitest run \ src/services/issue-prefix.test.ts \ src/__tests__/companies-service.test.ts \ src/__tests__/cloud-tenant-company-provisioning.test.ts \ src/middleware/cloud-tenant-actor.test.ts \ src/__tests__/auth-session-route.test.ts \ src/__tests__/cloud-routes.test.ts \ src/__tests__/cloud-instance.test.ts \ src/__tests__/company-branding-route.test.ts \ src/__tests__/company-cloud-floor.test.ts \ src/__tests__/companies-route-cross-company-authz.test.ts \ src/__tests__/companies-route-path-guard.test.ts \ src/__tests__/company-portability.test.ts pnpm --filter @paperclipai/ui exec vitest run pnpm --filter @paperclipai/ui typecheck ``` New coverage: - `server/src/services/issue-prefix.test.ts` covers the derivation, the suffix ladder, the cause-chain walk of the unique-violation detector, and `pickAvailableIssuePrefix` against a stubbed select. - `server/src/__tests__/companies-service.test.ts` covers a managed rename against a real Postgres database: the prefix moves, both identifier tables are re-keyed, and the activity entry is written. It also covers a same-base rename, a collision that takes the suffixed candidate, a non-name patch, and a self-hosted rename that leaves the prefix alone. Two more tests drive the overlap cases: two concurrent renames of the same company, and a rename racing a stale form that resubmits the original name. Both assert that the surviving name's base matches the company prefix and that the stored identifiers sit on that prefix. - `server/src/__tests__/cloud-tenant-company-provisioning.test.ts` covers the claim path and the repair against a real Postgres database: a name-derived prefix, a null description, a suffixed prefix on collision, the full repair, a second pass that changes nothing, a description-only repair, and an operator-written description that the repair leaves alone. - `ui/src/pages/CompanySettingsRenameHint.test.tsx` covers the hint on a managed instance and its absence on a self-hosted instance. The `substring` cast in `rekeyCompanyIssueIdentifiers` is load-bearing and the database tests prove it. The driver binds the offset as text. Without the `::int` cast Postgres resolves the SQL-regex overload of `substring`, and every identifier becomes NULL. ## Risks - **Re-keying changes existing identifiers and URLs.** This is deliberate, and it happens on hosted and managed instances only. After a rename, a link that carries an old task identifier stops resolving. The settings page warns about this before the user saves. - **Identifiers inside comment text are not rewritten.** Only the `identifier` columns of `issues` and `cases` move. A task ID that someone typed into a comment, a description, or a document keeps the old prefix. - **A lost prefix race inside the rename transaction surfaces as a conflict.** The rename picks a free prefix and then writes, because a unique violation inside a transaction aborts the whole transaction. Two *different* companies renamed onto the same base at the same moment can still collide. The loser sees its PATCH fail with the unique violation. The write is retryable by the client, and the window is a single statement wide. Two renames of the *same* company no longer race: the row lock serializes them, and the second one re-keys from what the first committed. - **The rename holds a row lock.** A managed rename takes `SELECT ... FOR UPDATE` on its own company row for the rest of the transaction. It is one row, and no other path in the transaction locks a company row, so there is no lock-order cycle. A self-hosted instance and every non-rename company update never reach the lock. - **The one-time repair is best effort.** It runs inside a try/catch and logs a warning on failure, so it never blocks authentication. A failed pass is retried on the next request, because its guards still match. - No schema change and no migration. ## Model Used - Provider: Anthropic (Claude) - Model: Claude Opus, model id `claude-opus-5[1m]` - Context window: 1M - Reasoning mode: extended thinking - Capabilities used: agentic tool use through Claude Code (file edits, shell, test runs against an embedded Postgres database) ## 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 |
||
|
|
a4f1b3c533 |
Move company invites into a tab of the Members page (#12289)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Company settings include member management and invites, which are one workflow: invite someone, watch the join request, manage the membership > - Today invites sit on a standalone settings page, costing a sidebar entry and forcing people to bounce between two pages for one task > - Merging invites into the Members page keeps the workflow in one place without losing any capability > - This pull request turns the Members page into a Members/Invites tab bar, redirects the old URL, and keeps both operator-visibility keys meaningful > - The benefit is a tighter settings surface for every deployment, with `company.invites` now hiding just the tab while `company.members` hides the whole page ## Linked Issues or Issue Description No public issue exists; describing the issue inline per the enhancement template: **What existing behavior does this improve?** Company invites live on a standalone settings page separate from the Members page, even though inviting someone and managing the resulting membership are one workflow. **Subsystem affected** Web UI (company settings) **Current behavior** `/company/settings/invites` is its own page with its own sidebar entry (gated by `company.invites`); the Members page (`company.members`) is separate. The sidebar company menu's "Invite people" shortcut links to the invites page and is not gated by the hidden-settings mechanism at all. **Proposed behavior** The Members page carries a Members/Invites tab bar, addressable via `?tab=invites`. The invite creation flow, latest-link panel, and invite history move unchanged into an `InvitesSection` component. The old URL redirects to the tab (still behind its `HiddenSettingsPageGate`). `company.members` hides the whole page; `company.invites` hides just the Invites tab, and the tab bar collapses when only Members remains. The "Invite people" shortcut points at the tab and hides when either surface is operator-hidden. **Reason and benefit** One settings surface for one workflow: fewer sidebar entries and no bouncing between two pages to invite someone and then manage the membership. Operators keep the same visibility controls, with a sharper meaning for each key. **Breaking changes** None. Bookmarks to the old invites URL redirect to the tab, invite and membership APIs are unchanged, and both hidden-settings keys keep working. ## What Changed - `ui/src/pages/CompanyInvites.tsx` → `ui/src/components/access/InvitesSection.tsx` (page chrome and breadcrumbs dropped; content unchanged), with its tests moved alongside. - `ui/src/pages/CompanyAccess.tsx`: Members/Invites tabs via the shared `PageTabBar`, `?tab=invites` search param, `company.invites` gating with tab snap-back; legacy "Open Invites" button retargeted. - `ui/src/App.tsx`: the invites route becomes a gated redirect to `/company/settings/members?tab=invites`. - `CompanySettingsSidebar` / `CompanySettingsNav`: standalone Invites entry/tab removed; the old path maps to the members tab. - `ui/src/components/SidebarCompanyMenu.tsx`: "Invite people" now links to the tab and hides when `company.members` or `company.invites` is hidden (closes an existing gating gap). - Tests: moved invites tests, new tab coverage (default tab, deep link, operator-hidden tab skips the invites fetch), sidebar/nav suites updated. ## Verification - `npx vitest run ui/src/components/CompanySettingsSidebar.test.tsx ui/src/components/access/CompanySettingsNav.test.tsx ui/src/pages/CompanyAccess.test.tsx ui/src/components/access/InvitesSection.test.tsx` — 24 tests passing. - `pnpm --filter @paperclipai/ui typecheck` — clean. ## Risks - Low. Pure UI restructure: invite APIs, membership APIs, and the hidden-settings registry keys are unchanged. Bookmarks to the old invites URL redirect (and stay gated). The `company.invites` key's meaning narrows from "hide the page" to "hide the tab", which is the same effective surface. ## Model Used Claude (Anthropic), model id `claude-fable-5`, extended thinking, agentic tool use via Claude Code. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge |
||
|
|
7551b63ef2 |
Remove the instance Heartbeats settings page (#12282)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Instance settings collect deployment-wide controls; one of them was the Heartbeats page, an instance-wide list of scheduler heartbeat agents with enable/disable toggles > - The same controls live on each agent's own configuration surface, so the standalone list duplicates them, and its framing no longer matches how heartbeat agents are managed > - Keeping a settings view that no longer makes sense costs every deployment navigation noise and maintenance > - This pull request removes the page, its route, its navigation entries, and its hidden-settings key for all deployments > - The benefit is a smaller, coherent settings surface, with operator hidden-settings lists that still mention the retired key continuing to work unchanged ## Linked Issues or Issue Description No public issue exists; describing the issue inline per the enhancement template: **What existing behavior does this improve?** The instance settings surface — specifically the Settings → Heartbeats page, which listed scheduler heartbeat agents instance-wide with enable/disable toggles. The view no longer makes sense as a standalone settings page: the same controls are available on each agent's configuration surface, and the instance-wide list framing does not match how heartbeat agents are managed. **Subsystem affected** Cross-cutting: `ui/` (page, route, navigation), `packages/shared` (settings-visibility registry), docs. **Current behavior** The page renders at `/company/settings/instance/heartbeats`, appears in the settings sidebar and tab bar, and is hideable by hosting operators via the `instance.heartbeats` key of `PAPERCLIP_HIDDEN_SETTINGS`. **Proposed behavior** The page, route, and navigation entries are removed for every deployment. The `instance.heartbeats` registry key is retired; operator lists that still send it are logged and ignored, so mixed-version fleets keep working. Remembered settings paths pointing at the old page remap to the settings root. Heartbeat APIs are unchanged. **Reason and benefit** A smaller, coherent settings surface with no duplicated controls; less navigation noise and maintenance for every deployment. **Breaking changes** None functional. Bookmarks and remembered paths to the removed page land on the settings root; `PAPERCLIP_HIDDEN_SETTINGS` lists that still include `instance.heartbeats` log a warning and are otherwise honored unchanged. ## What Changed - Deleted `ui/src/pages/InstanceSettings.tsx` (the Heartbeats view) and its route in `ui/src/App.tsx`. - Removed the sidebar entry (`CompanySettingsSidebar`) and tab-bar item (`CompanySettingsNav`). - Removed `"/heartbeats"` from the remembered-settings-path allowlist; remembered heartbeats paths now remap to the settings root. - Retired the `instance.heartbeats` key from the shared settings-visibility registry and the environment-variables doc; documented that retired keys are ignored with a warning. - Dropped the now-unused UI client wrapper for the instance scheduler-agent list (`heartbeatsApi.listInstanceSchedulerAgents`); the server endpoint stays. - Removed the unused `schedulerHeartbeats` query key. ## Verification - `npx vitest run packages/shared/src/settings-visibility.test.ts ui/src/lib/instance-settings.test.ts ui/src/components/CompanySettingsSidebar.test.tsx ui/src/components/access/CompanySettingsNav.test.tsx` — 24 tests passing. - Full `ui` vitest suite: 4426 tests, 4 failures — all in files this PR does not touch; 3 were load-induced timeouts that pass on rerun, and `OnboardingWizard.test.tsx` "renders instead of throwing when the browser denies storage access" fails identically on a clean master checkout (pre-existing). - `pnpm --filter @paperclipai/ui typecheck` and `pnpm --filter @paperclipai/shared typecheck` — clean. - Merged `master` to clear a conflict (see below) and re-ran the four focused suites (24 passing), `ui/src/App.test.tsx` and `ui/src/plugins/bridge.test.ts` (22 passing), and both typechecks — all clean. Full CI is green on the merge commit. ## Merge With master `master` gained the `company` → `organization` copy pass (#12243), which reworded strings inside `ui/src/pages/InstanceSettings.tsx` — the page this branch deletes — producing a modify/delete conflict. Resolved by keeping the deletion: the page is going away, so the rewording of its copy has nothing to apply to. Every other file merged cleanly, and `master`'s rewording in `App.tsx`, `App.test.tsx`, and `CompanySettingsSidebar.tsx` sits away from this branch's structural removals, so both changes survive. The net diff against `master` is unchanged from the pre-merge review: the same 13 files, 23 insertions, 330 deletions. ## Risks - Low. Pure removal of a UI surface; heartbeat data and APIs are untouched. Operators still listing `instance.heartbeats` in `PAPERCLIP_HIDDEN_SETTINGS` get a warning log and otherwise unchanged behavior (covered by the registry's unknown-key handling). Bookmarks and remembered paths to the old page land on the settings root. ## Model Used Claude (Anthropic), model id `claude-fable-5`, extended thinking, agentic tool use via Claude Code. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [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 |
||
|
|
8f195c2f49 |
Let operators supply defaults for selected instance settings (#12285)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Hosting operators (a managed cloud, an internal shared server) tune
the settings surface with `PAPERCLIP_HIDDEN_SETTINGS`, but hiding a
control never changes its value
> - An instance whose stored feedback-sharing preference is still the
schema default ("prompt") keeps prompting users even when the operator
hid the control, leaving them no way to answer
> - More generally, operators have no supported way to change what a
setting defaults to without patching code
> - This pull request adds `PAPERCLIP_SETTING_DEFAULTS`, a generic
operator-supplied read-time default overlay for registry-listed general
settings
> - The benefit is that any hosting operator can pair "hide the control"
with "default the value", while explicit user choices and self-hosted
stock behavior stay untouched
## Linked Issues or Issue Description
No public issue exists; following the enhancement template:
**What existing behavior does this improve?**
Hosting operators need to supply the default value of selected instance
settings (first: `feedbackDataSharingPreference`) via configuration,
without patching code and without a hard-coded, opinionated constant in
the product.
**Subsystem affected**
Server (instance-settings service, feedback service, boot) and
`packages/shared` (settings schemas).
**Current behavior**
Setting defaults are fixed in the shared zod schemas.
`PAPERCLIP_HIDDEN_SETTINGS` can hide the feedback-sharing control and
floor writes, but the stored value stays "prompt", so issue-chat
surfaces keep prompting with no way to answer.
**Proposed behavior**
`PAPERCLIP_SETTING_DEFAULTS` takes a JSON object validated against a
shared registry of defaultable fields. The operator value substitutes
for the schema default at read time: a field whose effective value is
still the schema default resolves to the operator value; an explicit
non-default user choice always wins. Never persisted; unsetting the
variable restores stock behavior. Malformed JSON or an invalid value for
a known field refuses startup (fail closed); unknown field names warn
and are ignored (mixed-version fleet safe).
**Reason and benefit**
Any hosting operator can pair "hide the control" with "default the
value" without forking the product. Explicit user choices and
self-hosted stock behavior stay untouched.
**Breaking changes**
None. With the variable unset, every read path is byte-identical to
before.
## What Changed
- New `packages/shared/src/setting-defaults.ts`:
`SETTING_DEFAULTS_ENV_KEY`, `DEFAULTABLE_GENERAL_SETTINGS` registry
(currently `feedbackDataSharingPreference`), `parseSettingDefaults`
(fail-closed for policy content, warn-ignore unknown fields),
`applyOperatorGeneralDefaults` (pure read-time overlay),
`stripOperatorGeneralEchoes` (persist-time echo strip, see below),
re-exported from the package index.
- New `server/src/services/setting-defaults.ts`: parse-once accessor
mirroring `settings-visibility.ts`.
- `server/src/services/instance-settings.ts`: `toGeneralView` applies
the overlay in `get`/`getGeneral`/update responses; persisted writes
never carry operator values. Because general-settings writes materialize
every field, a stored schema-default value is treated as unchosen —
deliberate, documented, and covered by tests.
- `server/src/services/feedback.ts`: the preference-persistence branch
now checks the effective (overlaid) preference, so a stray prompt answer
cannot overwrite an operator default; its local normalize fallback now
returns full schema defaults.
- `server/src/index.ts`: boot-time fail-fast parse with a log line
naming the defaulted settings, mirroring the managed-config posture.
- The hidden-settings write floor (`assertNoHiddenSettingChanges`) keeps
comparing against effective values, so clients echoing a full GET
response keep working. To keep the overlay strictly read-time,
`updateGeneral` strips such echoes at persist time: a write of the
operator value over a field whose stored value is still the schema
default (unchosen) maps back to the schema default, so an echo cannot
promote the operator value into an explicit stored choice and later
changes to (or removal of) `PAPERCLIP_SETTING_DEFAULTS` still take
effect. A write of any other value, or over an explicit stored choice,
persists as given.
- Docs: `PAPERCLIP_SETTING_DEFAULTS` row + "Operator setting defaults"
section in `docs/deploy/environment-variables.md`.
- Tests: `packages/shared/src/setting-defaults.test.ts` (parse matrix,
overlay precedence, echo-strip matrix, immutability) and
`server/src/__tests__/instance-settings-operator-defaults.test.ts`
(accessor, substitution, explicit-choice wins, unset identity,
never-persisted, full-GET echo stays unchosen, explicit non-default
write persists).
## Verification
- `npx vitest run packages/shared/src/setting-defaults.test.ts
server/src/__tests__/instance-settings-operator-defaults.test.ts
server/src/__tests__/instance-settings-managed-overlay.test.ts` — 33
tests passing.
- `npx vitest run server/src/__tests__/instance-settings-routes.test.ts
server/src/__tests__/instance-settings-service.test.ts` — 57 passing;
`npx vitest run server/src/__tests__/feedback-service.test.ts
server/src/__tests__/issue-feedback-routes.test.ts` — 18 passing.
- `pnpm --filter @paperclipai/shared typecheck` and `pnpm --filter
@paperclipai/server typecheck` — clean.
## Risks
- Low. With the variable unset every read path is byte-identical to
before (identity overlay, covered by tests). The overlay is read-time
only and never persisted, so no migration and no data risk. Fail-closed
parsing means a bad policy value is a loud boot failure rather than
silent drift — consistent with the existing managed-config contract.
## Model Used
Claude (Anthropic), model id `claude-fable-5`, extended thinking,
agentic tool use via Claude Code.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
|
||
|
|
e34ed9801b |
Let operators hide the Provider vaults and Proposals tabs (#12284)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Hosting operators (a managed cloud, an internal shared server) tune the settings surface with `PAPERCLIP_HIDDEN_SETTINGS`, which today hides whole pages > - The Secrets page bundles four tabs, and two of them — Provider vaults and Proposals — do not apply to deployments where the operator provisions provider credentials itself > - Hiding the whole Secrets page is too coarse: the Secrets and My secrets tabs stay essential everywhere > - This pull request adds per-tab visibility keys (`company.secrets.vaults`, `company.secrets.proposals`) as a new company-section registry group > - The benefit is that any hosting operator can trim the Secrets page to what fits their deployment, with self-hosted behavior unchanged by default ## Linked Issues or Issue Description No public issue exists; following the enhancement template: **What existing behavior does this improve?** `PAPERCLIP_HIDDEN_SETTINGS` can hide the whole Secrets page (`company.secrets`) but not individual tabs. Operators of managed deployments need to hide the Provider vaults and Proposals tabs while keeping the rest of the page. **Subsystem affected** Settings visibility (`packages/shared/src/settings-visibility.ts`) and the Secrets page UI (`ui/src/pages/Secrets.tsx`). **Current behavior** The Secrets page always renders all four tabs (Secrets, My secrets, Provider vaults, Proposals), polls pending proposals for the badge, and offers "manage vaults" affordances that jump to the vaults tab. **Proposed behavior** Two new registry keys, `company.secrets.vaults` and `company.secrets.proposals`, hide the corresponding tab: the tab-bar entry disappears, an active hidden tab snaps back to Secrets, the manage-vaults affordances are suppressed, and the pending-proposals poll stops. UI visibility only — the provider-config and proposal APIs stay live for agents and integrations, matching the existing `company.*` precedent. Nothing changes when the variable is unset. **Reason and benefit** Any hosting operator (a managed cloud, an internal shared server) can trim the Secrets page to what fits their deployment — for example when the operator provisions provider credentials itself, so the vault and proposal flows do not apply — without losing the Secrets and My secrets tabs, which stay essential everywhere. **Breaking changes** None. With `PAPERCLIP_HIDDEN_SETTINGS` unset (or set to existing keys only) nothing changes; older app versions receiving the new keys ignore them with a warning by design. ## What Changed - `packages/shared/src/settings-visibility.ts`: new `HIDEABLE_COMPANY_SECTIONS` group (`company.secrets.vaults`, `company.secrets.proposals`), `HideableCompanySection` type, `hidesCompanySection()` helper, wired into `HideableSettingKey` / `HIDEABLE_SETTING_KEYS`, re-exported from the package index. - `ui/src/pages/Secrets.tsx`: tab-bar filtering, hidden-tab snap-back effect, gated pending-proposals query, conditional `onManageVaults` on both the import button and dialog (the button's "AWS vault disabled — manage" affordance renders nothing when vaults are hidden), hidden `TabsContent` blocks. - Docs: new bullet in `docs/deploy/environment-variables.md` under "Hiding settings surfaces". - Tests: registry membership/parse cases in `settings-visibility.test.ts`; new render cases in `Secrets.render.test.tsx` (hidden tabs absent + proposals poll skipped; default render keeps both tabs and the poll). ## Verification - `npx vitest run packages/shared/src/settings-visibility.test.ts ui/src/pages/Secrets.render.test.tsx` — 43 tests passing. - `pnpm --filter @paperclipai/shared typecheck` and `pnpm --filter @paperclipai/ui typecheck` — clean. ## Risks - Low. Nothing changes with `PAPERCLIP_HIDDEN_SETTINGS` unset (covered by the default-render test). The keys are UI-visibility only, so agent/integration API access is unaffected. Older app versions receiving the new keys ignore them with a warning by design. ## Model Used Claude (Anthropic), model id `claude-fable-5`, extended thinking, agentic tool use via Claude Code. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge |
||
|
|
325041cb00 |
fix(assets): accept identity-provider characters in image upload namespaces (#12288)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Humans oversee those agents in teams, so each person has a login and
a profile with an avatar
> - Avatars, logos and pasted images all go to one asset upload API,
which files each object under a namespace
> - The avatar namespace embeds the user id, and a deployment can take
user ids from an external identity layer, where a subject often holds
":", "|", "." or "@"
> - But the namespace validator accepted only letters, numbers, "/", "_"
and "-", so those users got a 400 "Invalid image metadata" error and
could not set a profile photo
> - This pull request widens the accepted characters, rejects "." and
".." path segments with a clear message, and cleans the namespace in the
upload client
> - The benefit is that profile photo upload works for every user, and a
namespace the API refuses now returns a message that says what is wrong
## Linked Issues or Issue Description
No existing issue or open pull request covers this. I searched the issue
and pull request lists for "avatar upload", "profile photo", "Invalid
image metadata" and "asset namespace" and found no duplicate. The bug
report follows.
**What happened?**
Profile photo upload fails. `ui/src/pages/ProfileSettings.tsx` sends the
namespace `profiles/${user.id}` to `POST
/api/companies/:companyId/assets/images`. When the user id comes from an
external identity layer it can contain ":", "|", "." or "@" — for
example `oidc:example|jane.example@example.com`.
`createAssetImageMetadataSchema` in
`packages/shared/src/validators/asset.ts` accepted only
`/^[a-zA-Z0-9\/_-]+$/`, so the route returned 400 "Invalid image
metadata" (`server/src/routes/assets.ts`). The image bytes were never
the problem, but the message pointed at the image, so the toast gave the
user nothing to act on.
A second case has the same cause. The agent instructions editor in
`ui/src/pages/AgentDetail.tsx` builds a namespace that ends with a
filename, such as `agents/<id>/instructions/SKILL.md`. The "." in the
filename also failed the check.
**Expected behavior**
A profile photo uploads for any user id the app itself issues, and an
image pasted into the agent instructions editor uploads for any
instruction filename. A namespace the API does refuse returns a message
that names the field and states the rule.
**Steps to reproduce**
1. Run Paperclip with an external identity provider, so `user.id` holds
an OIDC subject such as `oidc:example|jane.example@example.com`.
2. Open Settings, then Profile.
3. Choose an avatar image.
4. The upload fails and the page shows "Invalid image metadata".
Or, with no identity provider:
1. Open an agent, then the instructions editor, and select a file whose
name contains a "." such as `SKILL.md`.
2. Paste an image into the editor.
3. The upload fails with the same error.
**Paperclip version or commit**
`master` at
|
||
|
|
d27998a230 |
Check the declared Lexical range with semver rules (#12319)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - People write almost every issue, goal, document, and chat message through the rich markdown editor, which wraps `@mdxeditor/editor` on top of Lexical > - #12317 fixed a dependency split that broke that editor everywhere, and it added `ui/src/lib/lexical-single-copy.test.ts` to keep the Lexical graph honest > - Greptile raised a P2 on the last assertion in that test. It compared the range `@mdxeditor/editor` declares to a literal string, so it rejected equivalent spellings such as `>=0.48.0 <0.49.0` even when the resolved version satisfies them > - A guard that fails on a healthy tree teaches people to delete it, which would give back the protection #12317 just added > - #12317 merged before the fix landed, so this pull request carries it > - This pull request stops the test from reading semver ranges at all, and asserts the mechanism that can actually break the graph > - The benefit is a guard that fails only on a real problem, and a smaller test file than before ## Linked Issues or Issue Description Refs #12317 — this addresses the Greptile P2 left on that pull request. No public issue exists, so the problem is described below with the bug report template. **What happened?** `ui/src/lib/lexical-single-copy.test.ts` asserted the declared range with an exact string comparison: ```ts const [major, minor] = versionOf("lexical", requireFromUi).split("."); expect(declared).toBe(`^${major}.${minor}.0`); ``` That accepts one spelling only. If `@mdxeditor/editor` published `>=0.48.0 <0.49.0`, or `^0.48.2`, or `^0.47.0 || ^0.48.0`, the test would fail even though the resolved Lexical version satisfies the declared range. The test would report a dependency split that does not exist. **Expected behavior** The test fails when the Lexical graph is actually split or forced. It passes on any healthy tree, whatever range syntax the editor happens to publish. **Steps to reproduce** 1. Check out `master` at |
||
|
|
b06034d762 |
Write mode-constrained inbound files directly to their target (#12320)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Adapter utilities transfer files between the host and an agent environment > - A sandbox target already provides the security boundary for inbound files > - The generic fallback adds a temporary file and a rename that do not add protection inside that boundary > - This pull request writes a mode-constrained inbound file directly to its target and applies the mode after the write > - The benefit is a simpler transfer path while host targets keep the strict pre-write mode rule ## Linked Issues or Issue Description **What existing behavior does this improve?** The inbound file-sync fallback for a mode-constrained file stages the file under a temporary name, applies the mode, and renames the file into place. **Subsystem affected** `packages/adapter-utils` and `packages/plugins`. **Current behavior** A sandbox target uses a temporary path before it receives the file. The host then changes the mode and renames the file to the target path. **Proposed behavior** A sandbox target receives the file at its target path. The host applies the mode after the write. A host target still applies the mode before the first byte. **Reason and benefit** The sandbox boundary already protects the target. The direct write removes an unnecessary staging path and rename. **Breaking changes** None. The directory path and outbound transfer path keep their existing behavior. ## What Changed - Write a mode-constrained single-file inbound transfer directly to the sandbox target. - Apply the mode after the direct write and keep the confinement check before post-upload commands. - Scope the protocol comment by transfer direction and preserve the strict host-target rule. - Keep directory inbound transfers and outbound transfers unchanged. ## Verification - Run the targeted unit suite for the changed package. - Verify the suite covers direct target writes, post-write mode application, and confinement rejection. - Run `tsc --noEmit` for both changed packages. - Review the full GitHub Actions check set after the PR opens. ## Risks - A sandbox provider that assumes a temporary inbound path could expose a behavior mismatch. - The confinement check remains before post-upload commands, which limits escape risk. - Host targets keep the pre-write mode rule, so host permission behavior does not change. ## Model Used OpenAI GPT-5. This model assisted with Git operations, PR preparation, review coordination, and tool use. Context window size and reasoning mode are 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.827.0-canary.3 |
||
|
|
67f9867bc6 |
fix(interactions): deliver question answers durably (#12307)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Agents can pause a task and ask the user structured questions. > - The answer is durable in the issue interaction, but delivery to the next run is not durable. > - A process restart can therefore leave an answered interaction without a continuation attempt. > - Native runners also need a provider-neutral question contract before the task page can consume native events safely. > - This pull request adds a content-free delivery outbox and an optional native steering seam. > - Direct adapters keep their existing heartbeat continuation path. > - The benefit is reliable answer delivery without changing runtime selection or task-page behavior. ## Linked Issues or Issue Description Refs #12202. This pull request replaces the question-delivery foundation from that stale task-thread pull request. The task-thread projection will follow in a smaller pull request. **What happened?** Question answers were stored in the issue interaction. The server then made one in-memory continuation wake. A server stop between those operations could leave the answer stored but not delivered. The combined native task-thread pull request also made this behavior hard to review separately from UI changes. **Expected behavior** The answer and its delivery receipt must commit in one transaction. The server must retry pending receipts after a restart. Existing direct adapters must keep the current wake path. A native runtime may use the optional steering seam, but this pull request does not enable native steering in production. **Steps to reproduce** 1. Create an `ask_user_questions` interaction. 2. Answer the interaction. 3. Stop the server before the continuation wake completes. 4. Start the server again. 5. On current master, no durable record tells the server to retry the answer delivery. **Paperclip version or commit** Current `master` at `4d82f5eae`. ## What Changed - Add the `issue_question_response_deliveries` table and migration. - Store only routing state, a correlation ID, and a payload digest in the delivery row. The answer remains in the existing interaction result. - Commit an answered interaction and its pending delivery row in one transaction. - Add bounded claims, retry recovery, cumulative terminal state, and content-free activity records. - Keep every built-in direct adapter and external adapter on the existing heartbeat wake path. - Add an optional native steering seam. No production caller supplies that seam in this pull request. - Retain the provider-neutral `paperclip.question_set.v1` presentation on recovered interactions. - Run delivery immediately after an answer and sweep pending rows at startup and on the existing server interval. - Add focused database, service, route, startup, adapter-matrix, digest, and duplicate-delivery tests. ## Compatibility Boundary - This pull request does not change adapter selection. - This pull request does not start runnerd. - This pull request does not create native run records. - Direct adapters never call the native steering seam. - The existing interaction result stays authoritative for answer content. - The migration is additive and does not rewrite existing rows. - This pull request has no UI, dependency, workflow, package-manager, or lockfile changes. - The diff has 19 files. ## Verification - `pnpm exec vitest run server/src/__tests__/question-response-delivery.test.ts server/src/services/issue-thread-interactions.test.ts server/src/__tests__/issue-thread-interaction-routes.test.ts server/src/__tests__/server-startup-feedback-export.test.ts` — 4 files and 120 tests passed. - `pnpm -r typecheck` — passed for all applicable workspaces. This includes Cargo format and check, protocol drift checks, and migration safety. - `pnpm build` — passed. This includes the Rust release binary, server build, and UI production build. - `git diff --check` — passed. - Secret patterns were not present in the changed text files. - The repository token gates currently report violations from unchanged files on `master`. This pull request does not change those files. ## Risks The main risk is routing a direct-adapter answer into a native session. The service checks the persisted runtime mode, and the adapter matrix proves that all direct adapters use only the existing wake path. The new table is additive. It has foreign keys, unique correlation constraints, bounded attempts, and status checks. Activity records omit question and answer content. ## Model Used OpenAI Codex, GPT-5 family. The client does not expose the exact deployment ID or context window. Agentic reasoning, tool use, and code execution 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 or instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal Paperclip ticket ID or instance-derived details - [x] I have run the affected tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have documented the new contracts and compatibility boundary - [x] I have considered and documented compatibility and security 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 mergecanary/v2026.827.0-canary.2 |
||
|
|
c9df0e251d |
chore(lockfile): refresh pnpm-lock.yaml (#12318)
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> |
||
|
|
2a3c86f91d |
fix(ui): keep the rich editor available for angle-bracket prose (#12290)
## Thinking Path
> - Paperclip helps people manage AI agents and their work
> - People use the rich markdown editor to write task descriptions,
comments, and agent instructions
> - The editor disables HTML processing, but the markdown parser still
treats some angle-bracket text as HTML
> - A value such as `<name>` could therefore stop the rich editor and
show the raw-source fallback
> - The retry action used the same input, so it could not recover
> - This pull request escapes unsupported angle brackets only while the
editor processes the value
> - The benefit is that the rich editor works and stored markdown stays
unchanged
## Linked Issues or Issue Description
Fixes: #12197
Related prior attempt: #2696
**What happened?**
The rich markdown editor did not start when prose contained a bare angle
bracket, such as `<name>`. It showed the raw-source fallback. The retry
action repeated the same failure.
**Expected behavior**
The rich editor starts for normal prose. It stores the text in its clean
form because agent prompts can use this text.
**Steps to reproduce**
1. Open a task description, comment, or other field that uses the rich
markdown editor.
2. Enter `Rename <name> to the real name`.
3. Reload the field.
4. Observe the raw-source fallback.
**Paperclip version or commit**
`master` at
|
||
|
|
c8a136fb02 |
Restore a single Lexical copy so the rich editor renders (#12317)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - People write almost every issue, goal, document, and chat message
through the rich markdown editor, which wraps `@mdxeditor/editor` on top
of Lexical
> - On `master` that editor renders its raw-source textarea instead. It
shows "Rich editor unavailable for this markdown" on every field, for
all content, including empty content
> - Lexical throws at render time because the project holds two copies
of `LexicalBuilder`. The root `pnpm.overrides` block forced the Lexical
family past the version range the editor supports, and it missed the
packages that are reached transitively
> - Every markdown surface in the product degrades to plain text
editing, so this is a full loss of a core authoring feature and not a
cosmetic problem
> - This pull request removes the Lexical entries from `pnpm.overrides`,
pins the app to Lexical 0.48.0, and adds a test that guards the
resolution graph
> - The benefit is that the rich editor renders again, and a future
override or bump that splits Lexical fails in CI instead of in the
browser
## Linked Issues or Issue Description
No public issue exists. The problem is described below with the bug
report template.
**What happened?**
The rich markdown editor falls back to its raw-source textarea on every
markdown field. The header reads "Rich editor unavailable for this
markdown. Showing raw source instead." The fallback appears for all
content, including empty content. React catches an error during the
first render of the editor.
The cause is dependency resolution. The root `package.json`
`pnpm.overrides` block pinned the Lexical family to `0.49.0`.
`@mdxeditor/editor@4.2.1` declares `lexical: "^0.48.0"`. A caret range
on a `0.x` version pins the minor, so `^0.48.0` means `>=0.48.0
<0.49.0`. The override therefore pushed the editor past its only
supported line. Lexical 0.49.0 also carries breaking `$config()` node
changes.
The override list was also incomplete. `@lexical/extension`,
`@lexical/history`, and `@lexical/internal` are reached transitively and
were never listed. Those packages stayed on 0.48 while the listed
packages moved to 0.49. The graph mixed the two lines, and the built
browser bundle carried two
`Symbol.for("@lexical/extension/LexicalBuilder")` registrations.
Commit
canary/v2026.827.0-canary.1
|
||
|
|
949e975b0f |
docs(sandbox-providers): state the sandbox security boundary (#12286)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Sandbox providers run agent work in isolated environments > - The Daytona documents described a command wrapper that the provider no longer uses > - Those documents therefore described a control that the code does not have > - This pull request states the real sandbox boundary and the controls for paths that cross it > - The benefit is accurate security guidance for sandbox provider authors and operators ## Linked Issues or Issue Description **Issue type** Outdated (no longer matches behavior). **Where is the issue?** `packages/plugins/sandbox-providers/SANDBOX-REQUIREMENTS.md` and `packages/plugins/sandbox-providers/daytona/README.md`. **What's wrong?** The Daytona provider no longer uses the documented command wrapper, package installation commands, or sudoers rule. The requirements document also lacked a clear statement of the sandbox security boundary. **Suggested fix** State that the sandbox provides the boundary. Name outbound workspace synchronization and the application programming interface bridge as the paths that cross the boundary. State that a provider must not map a host path into a sandbox synchronization path. ## What Changed - Replace stale wrapper requirements with the actual sandbox security boundary. - State the controls that apply to outbound workspace synchronization and the application programming interface bridge. - State that this repository does not enforce the provider path-mapping duty today. - Remove obsolete Daytona package-install commands and the sudoers rule. ## Verification - Confirm the difference contains the two documentation files and the test file changed by the follow-up fix. - Confirm that no unrelated source, configuration, or fixture file appears in the difference. - Run the repository continuous integration checks and confirm that every required check passes. - Run the repository review bot and confirm its final verdict. ## Risks Low risk. This pull request changes two documents and closes a database client in one integration test. It does not change product runtime behavior or configuration. ## Model Used OpenAI Codex, GPT-5, with tool use and code execution. The model produced the documentation change and the pull request text. ## 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 the affected test locally; continuous integration provides complete test verification. - [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> |
||
|
|
1ba7b2cc71 |
chore(lockfile): refresh pnpm-lock.yaml (#12310)
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> |
||
|
|
1de105c475 |
fix(observability): pin the Sentry browser SDK and gate the optional Sentry server peer on the exact version (#12270)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip uses separate server and browser packages for runtime
services and the board.
> - Sentry integrations need an exact SDK version and safe optional
loading.
> - A version range can select an SDK that the privacy tests did not
audit.
> - Missing peer metadata does not describe the optional server SDK
contract.
> - This pull request pins the browser SDK and gates the optional server
SDK on its exact version.
> - The benefit is a clear SDK contract with fail-open startup behavior.
## Linked Issues or Issue Description
**What happened?**
The browser package used the range ^10.71.0, so a lockfile refresh could
select a newer SDK. The server loaded @sentry/node dynamically but did
not declare its optional peer contract.
**Expected behavior**
The browser package must use the audited 10.71.0 version. The server
must load @sentry/node only when the installed peer matches 10.71.0. The
server must start when the optional peer is absent.
**Steps to reproduce**
1. Install the project dependencies.
2. Inspect the browser Sentry version and the server package metadata.
3. Start the server without installing @sentry/node.
4. Confirm that the server starts and that the dynamic Sentry bootstrap
does not load an unsupported peer version.
**Paperclip version or commit**
|
||
|
|
4d82f5eaea | copy: unify user-facing "company" wording to "organization" (#12243) | ||
|
|
d785b19213 |
test(server): fix the pre-bind race in the byte-ledger ceiling test (#12280)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The server manages duplex channels that carry data between workers
and hosts.
> - The aggregate byte-ledger ceiling test can race the channel bind.
> - The race can make channel open fail before the test checks the
ceiling rejection.
> - This pull request writes one byte after the open call binds the
channel.
> - The test now checks the post-bind rejection and the retained-byte
count.
> - The benefit is a stable test that checks the intended byte-ledger
behavior.
## Linked Issues or Issue Description
**What happened?**
The duplex aggregate byte-ledger ceiling test scripted data during
channel open. Under load, the host could process the data notification
before the open continuation bound the route. The test then saw
`DUPLEX_CHANNEL_OPEN_FAILED` instead of the intended post-bind
rejection.
**Expected behavior**
The test must open the channel first. It must then write one byte and
confirm that the serialized host-to-worker frame exceeds the four-byte
ceiling. The route must reject the write and retain no bytes.
**Steps to reproduce**
1. Run the focused server test file.
2. Repeat the test several times under load.
3. Observe that the old test can fail during channel open.
4. Run the updated test and confirm the post-bind rejection.
**Paperclip version or commit**
canary/v2026.827.0-canary.0
|
||
|
|
eb86fcd498 |
The agent, drawn as itself (#12274)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - A new customer's first session ends in the tenant's agent arc: create an agent, connect a model, review > - Walking it turned up questions the arc had no business asking — a role picker using a vocabulary the customer has not been given, a model picker asking them to judge models they have not met — and chrome restating what they had just watched happen > - Each one costs a first-session customer attention at the exact moment they are deciding what this product is > - This pull request cuts the arc to what it must ask, and draws the agent as itself so the arc has a visible subject > - The benefit is three steps that each ask one thing, ending on an agent that is visibly ready ## Linked Issues or Issue Description No public issue exists. The changes come from walking the sign-up arc end to end. **What happened:** The agent step asks for a role from a fixed enum before asking for a name. The model step shows two "Recommended" badges (on both options), an "Adapter type" eyebrow, and a model picker. The review step lists a three-row checklist of work the customer just performed. The progress strip is a full-width segmented bar. **Expected behavior:** The agent step asks for a name. The model step offers the two harnesses and hides the rest behind advanced settings. The review step says the agent is ready. The strip counts three discrete steps. **Steps to reproduce:** 1. Sign up and enter the tenant wizard on the agent arc. 2. Observe the role select above the optional name field. 3. Continue to the model step: both options carry a "Recommended" badge, and a model picker sits below. 4. Continue to review: a checklist restates the organization name, agent, and model. **Additional context:** The brand pill assets (`pill-1-dormant.svg`, `pill-1-alive.svg`) are transcribed verbatim into a component rather than approximated. The role removal exposed a latent silent-failure path — see Risks. ## What Changed - `PillGuy` renders the brand pill in two states; the arc holds one instance, dormant through create and connect, alive on review. - The agent step asks for a name only. The name is required; the role picker is gone. - `DEFAULT_AGENT_ROLE` (`general`) backs every onboarding hire, and `agentRole` now defaults to it rather than empty. - The model step drops both "Recommended" badges, the "Adapter type" eyebrow, and the model picker; "More Agent Adapter Types" becomes "Advanced settings"; the sub-line becomes "Paperclip works with your existing subscription or API keys." - The review step drops its checklist; the heading becomes "Let's get started..." with "[name] is ready to work!". - The progress strip renders three left-aligned dots at the previous gap. - Five e2e specs and both wizard unit suites migrate off `#onboarding-agent-role`. ## Verification Run the tenant suite: ``` cd ui && npx vitest run ``` - 4398 tests pass across 474 files; `npx tsc --noEmit` clean. - Walked live in a local instance: agent step (dots, dormant pill, name placeholder), model step (no badges/eyebrow/picker, "Advanced settings"), review (pill alive, new copy, no checklist). - The retargeted role test asserts the hire payload carries `role: "general"` and the typed name — it is the test that catches the silent failure below. ## Risks - **A latent silent failure, now closed.** `handleGiveHeartbeat` returns early when `agentRole` is empty. With the picker removed and no default, Connect would have hired nobody and shown no error. The default closes it; the guard stays for any future path that clears the role. - **Behavioral change:** every onboarding hire is filed as `general` rather than a chosen role. The role remains editable in the app. - **Behavioral change:** the model is no longer chosen during onboarding. Every adapter offered here resolves its own default in `buildAdapterConfig`, and the model is changeable later. - **Assets:** the pill carries its own gradient fills and does not follow the theme. That is deliberate — the agent looks like itself on either ground. ## Model Used Claude Opus 5 (`claude-opus-5`) via Claude Code, with tool use and code execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal Paperclip ticket id - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [ ] 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: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
24d639abad |
feat(daytona): add transparent zstd-3 compression to the file-mapping upload path (#12271)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Sandbox providers transfer files between the host and an agent sandbox > - The Daytona file-mapping upload path sends staged tar files without compression > - Large raw uploads use more transfer bandwidth and storage > - This pull request adds transparent zstd-3 compression for eligible inbound file mappings > - The benefit is lower transfer size without changes for callers or post-upload commands ## Linked Issues or Issue Description **Subsystem affected** The change affects `packages/plugins`, in the Daytona sandbox provider. **Problem or motivation** The Daytona file-mapping upload path sends eligible staged tar files without compression. This increases transfer size and storage use. **Proposed solution** Compress eligible files on the host with zstd level 3. Upload the compressed bytes to a confined remote scratch path. Decompress them during the existing promote command, then promote the raw file. Use the raw upload path when compression cannot run or does not reduce size enough. **Alternatives considered** Keep the raw path for all uploads. This avoids compression work but does not reduce transfer size. Add a new sandbox round trip for decompression. This adds latency, so the change uses the existing promote round trip. **Roadmap alignment** `ROADMAP.md` lists Daytona under cloud and sandbox agents. This focused plugin change does not duplicate a planned core feature. **Additional context** The directory-mapping flow stays on the raw path. Callers and post-upload commands keep the same behavior. ## What Changed - Add transparent zstd-3 compression to `syncInFileMappings`. - Upload compressed artifacts to confined remote scratch paths and decompress them during promotion. - Keep the raw upload fallback when zstd is absent, compression fails, or the compressed result does not reduce size enough. - Create the raw scratch file once with atomic exclusive no-clobber open and write through the retained descriptor. - Stage compressed host artifacts in private `0700` directories with `0600` files. - Remove temporary directories on success and failure. - Add regression tests for compressed uploads, fallbacks, decompression failures, and cleanup. ## Verification - `pnpm --dir packages/plugins/sandbox-providers/daytona test` passes with 20 tests. - The compressed success path produces a byte-identical remote file. - A target without zstd uses the raw upload path. - A decompression failure does not promote a partial raw file. - Temporary files and directories do not remain after success or failure. - The newest cleanup regression test fails when the production cleanup fix is reverted and passes with the fix. ## Risks - Compression adds host CPU work for eligible file mappings. - The raw path remains available when compression is unavailable or ineffective. - Decompression runs during the existing promote command and can fail before promotion. - The change does not alter the directory-mapping flow or caller interface. ## Model Used OpenAI Codex, GPT-5, tool use and code execution enabled. The model assisted with repository review and pull request 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] 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> |
||
|
|
9c57c0f119 |
chore(lockfile): refresh pnpm-lock.yaml (#12252)
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> |
||
|
|
e628cf35da |
fix(adapter-utils): harden the wrapper birth-time probe with exclusive create and identity-aware cleanup (#12248)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Adapter utilities run process sessions in local and remote environments. > - The remote process-session wrapper uses a probe file to verify directory creation time. > - A peer could pre-create the probe path or replace it before cleanup. > - This pull request uses exclusive create and file-descriptor identity checks to protect the probe. > - The benefit is safer cleanup and fail-closed behavior at the sandbox boundary. ## Linked Issues or Issue Description **What existing behavior does this improve?** The remote process-session wrapper creates and removes a birth-time probe file. The old path-based flow did not prove that the wrapper created the path or that the path still named the same file. **Current behavior** A sandbox peer can race with the probe path. The peer can pre-create a symbolic link or replace the probe before cleanup. The wrapper can then inspect or remove an object that it did not create. **Proposed behavior** The wrapper creates the probe with exclusive create. It reads `(dev, ino, ctimeMs)` from the open file descriptor. It removes the path only when a final identity read matches the created file. **Reason and benefit** This change prevents symlink-following during creation and avoids removal of a peer's replacement object. The wrapper still fails closed when it cannot prove a real creation time. **Breaking changes** None. The wrapper keeps its existing fail-closed capture behavior. ## What Changed - Create the birth-time probe with `fs.open(path, "wx")`. - Read probe identity with `fstat` from the open descriptor. - Remove the probe only after a matching final identity read. - Add focused race tests for ordinary cleanup and file, directory, and symbolic-link replacement. ## Verification - Run `pnpm --filter @paperclipai/adapter-utils exec tsc --noEmit`. - Run the focused suite `packages/adapter-utils/src/execution-target-stdin-race.test.ts`. - Confirm that the focused suite passes all 33 tests. ## Risks The change affects shared wrapper source for local and remote process sessions. An identity read or cleanup failure leaves the probe in place and stops capture. The focused tests cover the new race paths. ## Model Used OpenAI Codex, GPT-5, tool use and code execution. The model reviewed and prepared this pull request from the supplied implementation 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> |
||
|
|
06cd21ed0f |
fix(observability): declare the optional OpenTelemetry peer dependencies (#12249)
## Thinking Path > - Paperclip manages AI agents for work. > - Paperclip includes an observability path that operators can enable for tracing. > - The server loads several OpenTelemetry packages only when tracing is enabled. > - The documentation calls these packages optional peer dependencies, but the server manifest does not declare them. > - This gap hides supported versions and stops Dependabot from maintaining the packages. > - This pull request aligns package metadata, runtime checks, and documentation with the opt-in tracing design. > - The change gives operators clear installation behavior and keeps the no-op default. ## Linked Issues or Issue Description This pull request fixes a package metadata and installation defect. Related observability work appears in [#8476](https://github.com/paperclipai/paperclip/pull/8476) and [#9672](https://github.com/paperclipai/paperclip/pull/9672). The server documentation described optional OpenTelemetry peer dependencies, but `server/package.json` did not declare them. Package managers and Dependabot could not see the supported version ranges. The UI and Claude local adapter also relied on automatic peer installation for `yjs` and `@anthropic-ai/sdk`. The package manifests now declare the optional runtime packages. A default install does not install optional tracing peers. The server keeps its no-op behavior when tracing is disabled or a peer is absent. ## What Changed - Add seven optional OpenTelemetry packages to `server/package.json` and mark each package as optional. - Keep `@opentelemetry/api` as a normal dependency for the no-op interface. - Disable automatic peer installation in `.npmrc`. - Declare `yjs` for the UI package and `@anthropic-ai/sdk` for the Claude local adapter. - Check declared peer versions before the server loads a dynamic OpenTelemetry import. - Keep the endpoint gate, dynamic imports, and fail-open behavior unchanged. - Update the observability and README documentation. - Tell Dependabot that its npm parser does not read `peerDependencies`. ## Verification - Targeted server tests pass: 34 passed and 2 skipped. - The skipped tests require the real OpenTelemetry SDK and remain pre-existing. - The pull request workflow regenerates the lockfile because manifest files and `.npmrc` changed. - The policy job confirms that the pull request does not include `pnpm-lock.yaml`. - GitHub checks pass except `security/snyk (cryppadotta)`, which remains pending after its authorized wait cap. - Greptile Review reports 5/5 with no open findings. - Server typecheck passes. ## Risks - Optional peers can produce a diagnostic when the installed version does not match the declared range. - A missing optional peer does not stop the server. - Disabling automatic peer installation can expose undeclared package use in other workspaces. - This pull request declares the affected packages and adds tests for the changed behavior. - This pull request makes no database or API changes. ## Model Used OpenAI Codex, GPT-5, with repository inspection and pull request 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 - [x] My branch name describes the change and contains no internal Paperclip 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 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> |