Commit Graph
1169 Commits
Author SHA1 Message Date
Nicky LeachandPaperclip 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>
2026-08-27 15:54:48 -07:00
Nicky LeachandPaperclip 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>
2026-08-27 13:56:30 -07:00
Nicky LeachandPaperclip 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: bdd8f1bed. Change head:
93e85d2ba1.

**Deployment mode**

Local dev with the embedded PostgreSQL test fixture.

**Installation method**

Built from source with pnpm.

**Agent adapter(s) involved**

Not adapter-specific. This change covers database test infrastructure.

**Database mode**

Embedded PGlite.

**Additional context**

The change keeps client references weak and keys them by host and port.
It does not retain credentials. It also handles connection URLs that the
driver accepts when the URL parser rejects them.

## What Changed

- Add a registry for live database clients in the database package.
- Close registered clients before the embedded PostgreSQL fixture stops
its cluster.
- Add a regression test for the teardown race.
- Handle driver-compatible URLs that the standard URL parser rejects.
- Add cleanup for the shared route test harness.

## Verification

- Run the full `packages/db` suite.
- Run `tsc --noEmit` in `packages/db`.
- Run the server suite that uses `route-test-harness.ts`.
- Run the teardown regression test five times.
- Confirm that the negative control fails three times.
- Confirm that no shard reports green tests and exits with an error.

## Risks

The registry changes client cleanup for embedded test fixtures. Weak
references limit retained memory in long-lived processes. The registry
uses host and port only, so it does not retain credentials. No
migration, schema, API, telemetry, authentication, or cryptography
change exists.

## Model Used

OpenAI Codex, GPT-5, tool use and code review support, standard
reasoning mode. The implementing engineer supplied the code and
verification 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>
2026-08-27 13:39:42 -07:00
Devin Foley 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 merge
2026-08-27 12:56:07 -07:00
Devin Foley 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
2026-08-27 12:11:05 -07:00
Devin Foley 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
2026-08-27 11:31:43 -07:00
Devin Foley 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
2026-08-27 11:29:05 -07:00
Devin Foley 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
2026-08-27 11:28:15 -07:00
Devin Foley 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 eb86fcd49.

**Deployment mode**

Any deployment whose user ids come from an external identity layer. The
instructions-editor case reproduces on a plain self-hosted install too.

**Agent adapter(s) involved**

Not adapter-specific (core bug).

## What Changed

- `packages/shared/src/validators/asset.ts`: widen the namespace pattern
to `/^[a-zA-Z0-9\/_.:@|-]+$/`, and reject any "/"-separated segment
equal to "." or "..". A traversal attempt now gets a clean 400 from the
validator instead of an error from the storage provider.
- `packages/shared/src/validators/asset.ts`: add
`sanitizeAssetNamespace()`, which maps any string to a namespace the
schema accepts. It works per segment: it keeps the accepted characters,
turns the others into "-", collapses repeated dashes, drops empty and
dot-only segments, and caps the result at 120 characters. It returns
`undefined` when no segment survives, and the caller then sends no
namespace.
- `packages/shared/src/validators/asset.ts`: export
`ASSET_NAMESPACE_MAX_LENGTH` and `ASSET_NAMESPACE_RULE`, so the rule
text and the API error cannot drift apart.
- `ui/src/api/assets.ts`: run the namespace through
`sanitizeAssetNamespace()` in `uploadImage`. This is one choke point for
all callers, so no caller has to know the rule.
- `server/src/routes/assets.ts`: name the field in the 400 message —
`Invalid image metadata: "namespace" must be 1-120 characters of
letters, numbers, or / _ - . : @ |, and cannot contain "." or ".." path
segments`. The zod issue details stay in the response. The UI shows
`body.error`, so the toast is now actionable.
- Tests: a new `packages/shared/src/validators/asset.test.ts`
accept/reject matrix for the schema and the sanitizer; three cases in
`server/src/__tests__/assets.test.ts`; one case in
`ui/src/pages/ProfileSettings.test.tsx`.

## Verification

Targeted runs:

```
npx vitest run packages/shared/src/validators/asset.test.ts        # 22 passed
npx vitest run server/src/__tests__/assets.test.ts                 # 11 passed
npx vitest run ui/src/pages/ProfileSettings.test.tsx               # 2 passed
```

New cases:

- Schema: accepts identity-provider ids that hold ":", "|", "." and "@";
accepts `agents/<id>/instructions/SKILL.md`; rejects `profiles/bad
name!`, over-length input, and `.` or `..` segments.
- Sanitizer: passes identity-provider ids through unchanged, replaces
and collapses the other characters, drops the `.` and `..` segments
while keeping a segment of three or more dots, caps at 120 characters
without leaving a dot segment behind at the cut, and returns `undefined`
when nothing survives. One case asserts the sanitizer output always
parses.
- Route: 201 for `profiles/oidc:example|jane.example@example.com`, and
the storage service receives that namespace; 400 naming `namespace` for
`profiles/bad name!`; 400 for `profiles/../secrets`.
- UI: a session user id holding ":" and "|" uploads, and the namespace
reaches the API unchanged.

Typecheck:

```
pnpm --filter @paperclipai/shared typecheck   # clean
pnpm --filter @paperclipai/ui typecheck       # clean
cd server && npx tsc --noEmit -p tsconfig.json # clean
```

Package suites:

```
npx vitest run --project @paperclipai/shared --exclude "**/dist/**"   # 586 passed, 8 pre-existing failures in src/worktree-seed-source.test.ts
npx vitest run --project @paperclipai/ui --exclude "**/dist/**"       # 4402 passed
```

CI runs the server suite as ten shards (five general, five serialized),
which is the authoritative full run for this package. All shards pass on
this branch.

The `worktree-seed-source` failures reproduce on an unmodified checkout
of the same base commit and are unrelated to this change. The UI
failures seen in that run were 5-second test timeouts caused by running
two suites at once on one machine; each file passes when it runs alone.

No document states the namespace character rule — I checked `docs/` and
`doc/`, where the asset upload endpoint appears only in an OpenAPI
registry entry and a smoke-lab note, neither of which describes the
metadata fields. The rule now lives in one exported constant that the
API error reuses.

## Risks

Low risk.

- The wider character set does not widen what a caller can write to
disk. `server/src/storage/service.ts` already replaces every character
outside `[a-zA-Z0-9._-]` in each path segment, and
`server/src/storage/local-disk-provider.ts` already rejects "." and ".."
segments and any key that resolves outside the base directory. This
change moves the "." and ".." refusal earlier, to the validator, so the
caller gets a clear 400.
- The API is more permissive than before, so no request that used to
succeed can start failing.
- Namespaces stored before this change keep working. The namespace is
not a key that is looked up; it is a prefix under which new objects are
filed.
- One behavior change worth noting: the UI now cleans a namespace
instead of sending it as typed, so a caller that passes an unusable
namespace gets a cleaned prefix rather than a failed upload.

## 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 merge
2026-08-27 11:27:31 -07:00
Nicky LeachandPaperclip 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>
2026-08-27 10:54:11 -07:00
Dotta 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 merge
2026-08-27 12:12:21 -05:00
Nicky LeachandPaperclip 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>
2026-08-27 09:25:06 -07:00
Nicky LeachandPaperclip 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>
2026-08-26 21:22:49 -07:00
Nicky LeachandPaperclip 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>
2026-08-26 17:51:36 -07:00
Nicky LeachandPaperclip 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>
2026-08-26 17:34:53 -07:00
Nicky LeachandPaperclip 4277ecbb2e fix(adapter-utils): terminate the remote process-session wrapper deterministically on bridge stop (#12244)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Adapter utilities run remote process-session wrappers for sandbox
work.
> - A wrapper can outlive its host run when the host removes its session
directory during shutdown.
> - A failed directory read can look like an empty queue, so the wrapper
can poll forever.
> - This pull request adds an explicit shutdown acknowledgement and
fail-closed identity checks.
> - The benefit is deterministic wrapper cleanup without killing an
unrelated session.

## Linked Issues or Issue Description

Refs: #11916

**What happened?**

Remote process-session wrappers could remain after a host run ended. The
host could remove the session directory before the wrapper read the
shutdown marker. The wrapper then treated directory errors as an empty
queue and continued to poll.

**Expected behavior**

The host must receive an explicit shutdown acknowledgement before it
treats the wrapper as stopped. The wrapper must stop when its session
identity becomes invalid or untrusted.

**Steps to reproduce**

1. Start a remote process-session wrapper.
2. Stop the bridge while the wrapper polls its session directory.
3. Remove the session directory during the poll.
4. Observe that the wrapper must terminate with its child.

**Paperclip version or commit**

`7cfbd1ecbe4a40261ba51fed07f624524352ada2`

**Deployment mode**

Built from source with the adapter-utils test suite.

## What Changed

- Add a shutdown control file and wait for a bounded `shutdownAck`
before session cleanup.
- Require `shutdownAck` as proof of host-side shutdown.
- Capture and verify session and stdin directory identity before each
poll.
- Terminate and latch the wrapper on missing, changed, linked,
non-directory, or untrusted paths.
- Reject unusable creation times and treat all identity-check `lstat`
errors as terminal.
- Add focused regression coverage for shutdown races and identity
failures.

## Verification

- `npx vitest run
packages/adapter-utils/src/execution-target-stdin-race.test.ts` passes.
- The full execution-target set passes: 175 tests across three files.
- The `packages/adapter-utils` typecheck passes with `tsc --noEmit`.
- CI will run on this pull request.
- Greptile will review the pull request.

## Risks

- A platform with unreliable directory creation times can stop a wrapper
earlier than before. This fail-closed result prevents an orphan.
- A transient identity-check error now stops the wrapper. This favors
cleanup over continued polling when the session identity cannot be
trusted.
- Session cleanup remains unconditional after the bounded
acknowledgement wait.

## Model Used

OpenAI Codex — GPT-5. Context window size is not exposed in this run.
The model used tool calls and code execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-26 15:12:49 -07:00
Nicky LeachandPaperclip 039a547962 fix(adapter-utils): make SSH env-lab fixture teardown deterministic (#12238)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent adapters use SSH environment fixtures to test process behavior
> - The SSH fixture detached its listener process from the test process
> - Teardown removed the fixture directory without stopping and awaiting
that listener
> - This pull request validates fixture state, stops the listener with
bounded escalation, and waits before directory removal
> - The benefit is deterministic test cleanup without orphan listeners
or unsafe signals

## Linked Issues or Issue Description

**What happened?**

The SSH environment fixture detached its listener process. Test teardown
removed the temporary fixture directory without stopping and awaiting
the listener. Repeated test runs left orphan listeners that held
loopback ports.

**Expected behavior**

The fixture teardown stops its listener, waits for exit, and then
removes the fixture directory. A forged state file must not signal an
unrelated process.

**Steps to reproduce**

1. Run the SSH fixture test repeatedly.
2. Inspect listener processes after each run.
3. Observe orphan listeners or ports that remain held.

**Paperclip version or commit**

Commit 324e1331f8.

**Deployment mode**

Not deployment-related.

**Installation method**

Built from source.

**Agent adapter(s) involved**

Custom / external plugin adapter.

**Database mode**

Not database-related.

**Access context**

Unclear / not applicable.

**Node.js version**

Node.js 24.

**Operating system**

Linux.

**Relevant logs or output**

A diagnostic found orphan listeners with parent process ID 1. Each
orphan held a loopback port.

**Relevant config (if applicable)**

Not applicable.

**Additional context**

The change keeps the process identifier reuse check and limits signals
to fixture-owned processes.

## What Changed

- Add one teardown owner for each SSH fixture.
- Stop the detached listener and wait for exit before removing the
fixture root.
- Add bounded SIGTERM and SIGKILL escalation with ESRCH guards.
- Validate the state file before any signal call.
- Require a positive safe-integer PID and safe absolute paths rooted at
the fixture directory.
- Require sshdConfigPath to equal the fixture root sshd_config path.
- Add regression coverage for listener cleanup and forged state files.

## Verification

- `pnpm --filter @paperclipai/adapter-utils typecheck`
- `pnpm exec vitest run packages/adapter-utils/src/ssh-fixture.test.ts`
- Confirm the fixture listener count stays at zero before and after the
test run.

## Risks

The teardown now sends signals to a fixture-owned process. State
validation and the existing PID reuse check limit the target. The
escalation has bounded waits.

## Model Used

OpenAI GPT-5 Codex. Exact model ID: GPT-5 Codex. Context window: not
exposed in this run. Capabilities used: tool use, repository inspection,
GitHub operations, and code review workflow management.

## 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 have addressed all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-26 14:03:47 -07:00
Nicky LeachandPaperclip 8f1e3cfe24 feat(observability): add opt-in Sentry error monitoring for the server and the browser (#12190)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The server and the browser need clear error reports when an operator
enables external monitoring.
> - Paperclip already uses an opt-in OpenTelemetry pattern for server
traces.
> - Sentry can provide error reports for both runtime paths when the
operator sets one data source name.
> - This pull request adds one opt-in Sentry gate for the server and the
browser.
> - The benefit is faster diagnosis while the default setup sends no
Sentry data.

## Linked Issues or Issue Description

**What is improved?**

Paperclip gains optional error monitoring for server and browser
failures.

**Subsystem affected**

Cross-cutting (server, UI, and shared authentication data).

**Current behavior**

Paperclip has no built-in Sentry error capture for server failures or
browser boundary failures. Operators must inspect local logs and browser
tools.

**Proposed behavior**

When the operator sets `SENTRY_DSN`, the server and authenticated
browser use the same Sentry project. When the variable is absent, both
paths stay inactive. The server loads Sentry dynamically and fails open
when the optional package is absent.

**Reason and benefit**

Operators can inspect runtime errors in one Sentry project. The default
setup remains local and sends no monitoring data.

**Breaking changes**

None when `SENTRY_DSN` remains unset. Authenticated session responses
add the optional `sentryDsn` field.

**Additional context**

The implementation uses built-in Sentry privacy options. It disables
default HTTP context and breadcrumb integrations and keeps
`sendDefaultPii` false.

## What Changed

- Add an opt-in server Sentry gate with dynamic package loading and
fail-open behavior.
- Add the Sentry data source name to the authenticated session response.
- Add an authenticated browser Sentry gate and React error boundary
capture.
- Add tests for server, browser, route, and application error paths.
- Document activation, installation, privacy settings, capture behavior,
and operator controls.

## Verification

- Run `npx vitest run server/src/__tests__/sentry.test.ts`.
- Run `npx vitest run ui/src/lib/sentry.test.ts`.
- Run `npx vitest run server/src/__tests__/auth-routes.test.ts
server/src/__tests__/shutdown.test.ts`.
- Confirm that the full continuous integration suite passes on this pull
request.
- Leave `SENTRY_DSN` unset and confirm that the server and browser gates
stay inactive.
- Set `SENTRY_DSN` and install the optional Sentry packages before a
manual capture check.

## Risks

The operator controls the Sentry project and accepts the data risk when
the operator enables the feature. Error objects can contain messages,
stacks, or cause chains with private values. The default configuration
sends no data because the feature stays off without `SENTRY_DSN`. A
missing optional server package does not stop server boot.

## Model Used

OpenAI Codex, GPT-5, with tool use, repository inspection, GitHub CLI
operations, and code review support.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-26 11:15:45 -07:00
Devin Foley 8ef39febd7 refactor(db): drop the vendored postgres teardown patch (reverts #12227) (#12234)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - #12227 vendored a pnpm patch of the `postgres` driver to stop a
teardown race (`nextWrite` firing after the socket is nulled) from
crashing the process and failing green CI shards
> - Maintainer call: carrying a vendored driver patch is not worth it
for a CI flake — the patch adds a maintenance obligation on every future
driver upgrade
> - The race is an upstream bug in `postgres@3.4.9`; the plan is to wait
for an upstream release that fixes it and bump the dependency instead
> - This pull request reverts #12227 in full: the patch file, its
`package.json` registration, and the regression test that exercised the
patched behavior
> - The benefit is an unmodified dependency graph; the known flake
signature returns and is retried when it bites

## Linked Issues or Issue Description

Reverts #12227.

**What existing behavior does this improve?**

Dependency hygiene: `postgres@3.4.9` is consumed unmodified again, with
no `pnpm.patchedDependencies` entry to re-evaluate on every driver
upgrade.

**Current behavior**

The repo carries `patches/postgres@3.4.9.patch` (null-socket guard in
the driver's deferred write flush, plus an `execute()` refusal on
socketless connections) and a regression test for it.

**Proposed behavior**

Plain upstream `postgres@3.4.9`. The teardown race stays an upstream
bug: a green test shard can occasionally fail with `Vitest caught 1
unhandled error` and `TypeError: Cannot read properties of null (reading
'write')` at `Immediate.nextWrite`; the remedy is retrying the shard
until an upstream driver release fixes the race and we bump.

**Reason and benefit**

A vendored driver patch is a standing maintenance cost that outweighs
the flake it suppressed.

## What Changed

- Reverts #12227 (`6c7c0fd1f`) in full: removes
`patches/postgres@3.4.9.patch`, its `pnpm.patchedDependencies`
registration in root `package.json`, and
`packages/db/src/postgres-driver-teardown.test.ts`. No lockfile
involvement — the merged commit never touched `pnpm-lock.yaml` and the
refresh bot had not yet recorded the patch.

## Verification

- `pnpm install` on the reverted tree is coherent; the full
`packages/db` suite passes (26 files / 100 tests).
- `git revert` applied cleanly with no conflicts.

## Risks

- Low. This restores the exact pre-#12227 state. The known flake
signature returns; it fails jobs whose tests all passed and is cleared
by retrying the shard.

## Model Used

Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — agentic
coding session with tool use.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-26 11:10:44 -07:00
Devin Foley 6c7c0fd1f2 fix(db): stop the postgres driver from crashing the process on a write/close race (#12227)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The server and its test suites talk to PostgreSQL through the
`postgres` (postgres.js) driver, and tests routinely tear their
databases down while connections still carry traffic
> - The driver flushes small buffered frames from a `setImmediate`, and
that deferred flush calls `socket.write()` without checking that the
socket still exists; a reserved connection whose backend died keeps
accepting queries, so the flush can fire with a null socket
> - The resulting `TypeError` escapes from a timer callback with no
try/catch above it, crashing the process — in CI this fails suites whose
tests all passed ("Vitest caught 1 unhandled error"), and the same crash
is reported against the driver in the wild after ECONNRESET
> - The latest driver release (3.4.9) still has the bug, so this pull
request adds a pnpm patch guarding the flush and normalizing timer state
on close, plus a deterministic regression test
> - The benefit is CI that no longer fails randomly on a teardown race,
and production processes that survive a database connection dying at the
wrong moment

## Linked Issues or Issue Description

No public issue exists; the underlying problem follows the bug-report
template.

**What happened?**

CI jobs fail with all tests passing: vitest reports `Vitest caught 1
unhandled error during the test run` with `TypeError: Cannot read
properties of null (reading 'write')` at `postgres/src/connection.js`
`Immediate.nextWrite`. The attribution points at whichever test file
happened to be running (e.g. `native-codex-runner.integration.test.ts`),
because the throw comes from a process-level timer callback, not from a
test. The identical crash is reported against the upstream driver by
other projects after `ECONNRESET` (e.g. immich-app/immich#25098).

**Expected behavior**

A connection dying between a write being scheduled and its deferred
flush must settle the affected queries through the driver's normal
connection-error path, never throw from a bare timer callback.

**Steps to reproduce**

Run the new `packages/db/src/postgres-driver-teardown.test.ts` with the
patch removed: reserve a connection (`sql.reserve()` — the same surface
`sql.begin()` uses), destroy the backend socket, wait for the client to
process the close, then issue one query on the reserved connection. The
deferred flush fires one tick later with `socket === null` and crashes
the process with exactly the CI signature.

**Paperclip version or commit**

master `198fc8b28`, `postgres@3.4.9` (latest release; bug still present
on the driver's master branch).

## What Changed

- `patches/postgres@3.4.9.patch` (new, wired via
`pnpm.patchedDependencies`): `nextWrite` returns without writing when
`socket === null`, dropping the buffered bytes — the close path has
already settled every in-flight query, so those bytes have nowhere to
go. The `closed()` and `terminate()` handlers additionally reset
`nextWriteTimer`/`chunk` after `clearImmediate`, so a stale cleared
handle cannot silently block a future reconnect's first flush. All three
shipped builds (`src`, `cjs`, `cf`) get the identical change.
- `packages/db/src/postgres-driver-teardown.test.ts` (new):
deterministic reproduction against a minimal in-process fake wire server
(startup auth + an empty result for the `fetch_types` bootstrap).
Asserts the late query settles with `CONNECTION_DESTROYED` through
`sql.end()` instead of crashing the process.

## Verification

- The regression test fails against unpatched `postgres@3.4.9` with the
exact CI signature (verified by running the same scenario against an
unpatched checkout) and passes with the patch.
- Full `packages/db` suite: 27 files / 101 tests pass.
- Spot-checked server suites that exercise the database through the
patched driver.

## Risks

- Low. The behavioral change activates only in a state that previously
crashed the process (write flush with no socket). Dropping the buffered
bytes matches what the connection's close path already promised callers:
every in-flight query has been settled with a connection error.
- The timer/chunk reset in `closed()`/`terminate()` prevents a
theoretical stale-handle hang after reconnect; on the normal path both
were already reset by `nextWrite`.
- The patch pins to `postgres@3.4.9`; a future driver upgrade will
surface the patch for re-evaluation (pnpm fails loudly on version
mismatch), and the guard can be dropped if the fix lands upstream.

## Model Used

Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — agentic
coding session with tool use (driver source analysis, wire-protocol fake
server, local test execution).

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-26 10:19:19 -07:00
Nicky LeachandPaperclip 198fc8b281 fix(adapter-utils): harden the referenced-project ignore scan (#12214)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent adapters stage referenced projects into controlled sandboxes
> - The ignore scan must preserve exact Git path bytes and fail closed
on unsafe input
> - Unbounded ignored-path data and raw diagnostics can harm resource
use or expose host details
> - This pull request adds exact path parsing, input bounds, fixed
failure categories, and saturation-only retry
> - The benefit is safer and more predictable referenced-project staging

## Linked Issues or Issue Description

**What happened?**

The referenced-project ignore scan trimmed NUL-delimited Git paths. It
also accepted a large ignored-path set and exposed raw failure details
through staging errors and warnings.

**Expected behavior**

The scan must preserve leading and trailing whitespace in Git paths. It
must reject oversized ignored-path data and expose only fixed failure
categories.

**Steps to reproduce**

1. Run the referenced-project ignore scan with paths that start or end
with whitespace.
2. Provide more than 10,000 ignored entries or more than 2 MiB of path
bytes.
3. Trigger a scan failure and inspect the reported reason.

**Paperclip version or commit**

d560bc2ae2

**Deployment mode**

Built from source.

**Installation method**

Built from source.

**Agent adapter(s) involved**

Not adapter-specific.

**Database mode**

Not database-related.

**Additional context**

This change covers the overlay diff, untracked, deleted, and ignored Git
paths. It also retries only typed scheduler saturation failures.

## What Changed

- Preserve all bytes in NUL-delimited Git path records.
- Bound ignored-entry count and total UTF-8 path bytes during parsing.
- Redact scan failure details to three fixed reason categories.
- Retry only the typed scheduler saturation error, with three total
attempts and 1 second then 2 second waits.
- Add tests for path whitespace, limits, diagnostics, retry behavior,
and scheduler code parity.

## Verification

- `npx tsc --noEmit` in `packages/adapter-utils` passed.
- `npx vitest run packages/adapter-utils` passed with 977 tests and 4
skipped.
- Continuous integration must run the server suite and the full
repository gates.

## Risks

The scan now rejects ignored-path data above fixed limits. Saturation
retries add up to 3 seconds before a final failure. The resolver still
fails closed for all other errors.

## Model Used

OpenAI GPT-5. The model used tool calls, code inspection, and command
execution. The exact context window 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>
2026-08-26 08:30:42 -07:00
DottaandPaperclip a9d0927fe8 fix(adapters): restore Paperclip skill for legacy runners (#12225)
<!-- Write all pull request text in Simplified Technical English
(ASD-STE100): short sentences, one instruction per sentence, simple
approved vocabulary, and the active voice. -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Legacy local adapters run agents that use the Paperclip skill for
the control-plane workflow.
> - PR #7029 removed the required-skill fallback and made runtime skill
selection depend only on stored preferences.
> - No migration or runtime fallback replaced that behavior for existing
agents or non-CEO agents.
> - PR #12138 added core skills to new CEOs, and PR #12147 added Claude
skill discovery. These changes did not mount the operational skill for
all legacy agents.
> - This pull request makes the operational skill a legacy adapter
runtime invariant. It keeps all other skills configurable.
> - The native runner stays unchanged because its protocol supplies the
control-plane contract.
> - The benefit is that new and existing legacy agents can always
operate through Paperclip.

## Linked Issues or Issue Description

Refs #7029

Refs #12138

Refs #12147

**What happened?**

A skill-capable legacy local agent could start without
`paperclipai/paperclip/paperclip`. This happened when the agent had no
stored skill preference. An explicit empty preference also removed the
skill. The agent then reported that the Paperclip skill was not
available.

**Expected behavior**

Every skill-capable legacy local adapter must mount the Paperclip
operational skill when the runtime inventory contains it. Optional
skills must remain configurable. The native runner must keep its current
protocol-based behavior.

**Steps to reproduce**

1. Create a non-CEO `codex_local` agent without `paperclipSkillSync`
preferences.
2. Start a legacy heartbeat.
3. Inspect the managed `CODEX_HOME/skills` directory.
4. Observe that the Paperclip skill is absent before this change.

**Paperclip version or commit**

The problem reproduces on `master` before this pull request. PR #7029
introduced the configured-only selection behavior.

**Deployment mode**

Local development and self-hosted legacy local adapters.

## What Changed

- Added a shared legacy skill resolver that always selects the canonical
Paperclip operational skill when it is available.
- Applied the resolver to direct adapter execution, ACPX execution,
skill snapshots, and persistent skill sync.
- Added Hermes skill materialization at sync and run boundaries.
- Aligned Cursor, Gemini, and OpenCode execution-time injection with the
configured child `HOME`.
- Made Hermes stop execution when another installation blocks the
required operational skill.
- Kept optional skills controlled by `paperclipSkillSync.desiredSkills`.
- Kept `paperclip_runner` on the configurable-only resolver.
- Added regression coverage for missing preferences, empty preferences,
each skill-capable legacy adapter, ACPX, Hermes, and native runner
isolation.
- Documented the legacy runtime invariant.

## Verification

- `pnpm -r typecheck` passed on the pushed commit.
- `pnpm build` passed on the pushed commit.
- The adapter utility regression suites passed: 236 tests.
- The changed server adapter suites passed: 48 tests across 12 files.
- The OpenCode adapter suite passed: 8 tests.
- The Hermes adapter suite passed: 7 tests.
- `git diff --check` passed.
- `pnpm test:run` is not clean on this macOS host. The command reported
failures in unchanged workspace and filesystem suites. An isolated rerun
of `company-skills.test.ts` and `company-skills-service.test.ts`
reproduced 11 failures because macOS resolved `/var/...` paths as
`/private/var/...`. The changed adapter suites pass independently.

## Risks

- This change deliberately makes the operational skill non-removable for
skill-capable legacy local adapters.
- Existing agents receive the skill on their next list, sync, or run
boundary. No database migration is required.
- The resolver does not create a skill when the runtime inventory does
not contain the canonical entry.
- Hermes aborts a run if another installation occupies the required
operational skill target.
- Hermes removes only an undesired Paperclip-owned symlink that still
points to the known Paperclip source.
- The native runner does not receive the legacy default.

> 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 were not exposed. The agent used reasoning, tool use, and code
execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-26 09:45:24 -05:00
Nicky LeachandPaperclip 821573ede8 refactor(adapter-utils): extract the shared workspace-restore teardown factory (#12196)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent adapters run workspace restore steps when an ACP run ends.
> - Claude, Codex, and Gemini each kept a near-identical teardown
closure.
> - Duplicate closures require the same defect fix in three files.
> - This pull request adds one shared workspace-restore teardown factory
and keeps each adapter's message strings.
> - The benefit is one tested restore-failure path with the same output
and outcome for all three adapters.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The Claude, Codex, and Gemini ACP adapters restore the workspace during
teardown and report restore failures with an allowlisted message.

**Subsystem affected**

`packages/adapters/` and `packages/adapter-utils/`.

**Current behavior**

Each adapter keeps a near-identical closure. The closure logs a start
line, restores the workspace, classifies errors, and logs a fixed
failure line.

**Proposed behavior**

A shared `createWorkspaceRestoreTeardown` factory owns the common steps.
Each adapter passes its staged runtime, log sink, start line, and
failure prefix.

**Reason and benefit**

The shared factory removes duplicate error handling. One tested
implementation now preserves the existing output and outcome for all
three adapters.

**Breaking changes**

None. The refactor preserves the emitted lines and returned outcomes.

**Additional context**

This pull request contains no public issue reference because no related
public issue was found.

## What Changed

- Add `createWorkspaceRestoreTeardown` to `packages/adapter-utils`.
- Move the shared restore, classify, and allowlisted log flow into the
factory.
- Update the Claude, Codex, and Gemini ACP adapters to call the factory.
- Add a table-driven test for all three message pairs.
- Keep one end-to-end restore-failure regression test per adapter.

## Verification

- `pnpm --filter @paperclipai/adapter-claude-local typecheck`
- `pnpm --filter @paperclipai/adapter-codex-local typecheck`
- `pnpm --filter @paperclipai/adapter-gemini-local typecheck`
- `npx vitest run
packages/adapter-utils/src/workspace-restore-teardown.test.ts`
- `npx vitest run
packages/adapter-utils/src/workspace-restore-merge.test.ts`
- `npx vitest run packages/adapters/claude-local/src/server/acp.test.ts`
- `npx vitest run packages/adapters/codex-local/src/server/acp.test.ts`
- `npx vitest run packages/adapters/gemini-local/src/server/acp.test.ts`
- Continuous integration must pass before merge, except for the known
pre-existing failures listed in the handoff.

## Risks

Low risk. This change moves shared code without changing behavior. The
adapter-specific message strings remain unchanged.

## Model Used

OpenAI GPT-5, exact model ID `gpt-5`, tool use and code review
assistance. The context window size was not provided 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>
2026-08-25 21:38:27 -07:00
Devin Foley 9c03443c48 feat: hideable company settings pages, with import floored on cloud-managed instances (#12199)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The app can run self-hosted or as a cloud-managed instance, where a
hosting platform provisions the instance with its company already
materialized (the existing `isCloudManagedInstance()` predicate and
`cloud_managed` floors)
> - The company Import/Export surface lets an operator materialize whole
companies from an export bundle; on a cloud-managed instance this
bypasses the existing `cloud_managed` company-creation floor and
conflicts with platform-owned provisioning
> - Importing should be disabled on cloud-managed instances, while
export stays open as the data-portability escape hatch
> - This pull request floors every import route with 403
`code=cloud_managed` on cloud-managed instances and hides the Import UI
there, using the existing predicate and the established floor pattern
> - It also extends the operator-hidden settings registry with keys for
every top-level company settings page, so a hosting operator can hide
any of them with `PAPERCLIP_HIDDEN_SETTINGS` alone next time
> - The benefit is one consistent managed-instance policy: cloud-managed
instances cannot import companies, self-hosted installs keep the full
import surface unchanged

## Linked Issues or Issue Description

No public issue exists; the underlying problem follows the enhancement
template.

**What existing behavior does this improve?**

The company import surface (`/api/companies/import*`,
`/api/companies/:companyId/imports/*`) and its UI entry points on
cloud-managed instances.

**Subsystem affected**

Server routes (`server/src/routes/companies.ts`) and UI navigation/pages
(settings sidebar, settings tabs, org chart, `/company/import` route).

**Current behavior**

A cloud-managed instance floors direct company creation (`POST
/api/companies` answers 403 `cloud_managed`), but the import routes
still accept company bundles, so an import can materialize companies the
hosting platform did not provision. The UI offers Import entry points
that lead to a surface that is not available on cloud-managed instances.

**Proposed behavior**

On instances where `isCloudManagedInstance()` is true, every import
route answers 403 `code=cloud_managed` before auth and body work, and
the Import UI (sidebar entry, settings tab, org-chart button,
`/company/import` route) is hidden or redirected. Export remains fully
available. Self-hosted instances are unchanged.

**Reason and benefit**

Cloud-managed instances keep one consistent provisioning authority, and
users do not see an Import surface that dead-ends in a 403.

## What Changed

- `server/src/routes/companies.ts`: a router-level floor mounted at the
`/import` and `/:companyId/imports` prefixes. It covers the single-shot
upload, preview, job polling, chunked transfer
declare/part-upload/status/preview/apply, and the agent-safe per-company
import routes. It throws `forbidden(..., { code: "cloud_managed" })` on
cloud-managed instances, or `403 settings_operator_managed` when the
operator hides `company.import` — both before auth and body validation,
mirroring the company-creation floor.
- `packages/shared/src/settings-visibility.ts`: new
`HIDEABLE_COMPANY_PAGES` registry group — `company.members`,
`company.invites`, `company.secrets`, `company.export`, `company.import`
— with a `hidesCompanyPage` helper. The company General page stays
non-hideable (settings root). `company.import` floors its API; the other
keys are UI-visibility only, as documented in the registry, so
membership/invite/secret/export APIs stay live for agents.
- `ui/src/components/CloudManagedPageGate.tsx` (new): route gate that
redirects cloud-managed instances to `/company/settings`, modeled on
`HiddenSettingsPageGate`.
- `ui/src/App.tsx`: wraps the `company/import` route in
`CloudManagedPageGate`.
- `ui/src/components/CompanySettingsSidebar.tsx`,
`ui/src/components/access/CompanySettingsNav.tsx`,
`ui/src/pages/OrgChart.tsx`: hide the Import entry points when
`useCloudInstance()` reports a managed instance, and honor the new
`company.*` hidden-settings keys for every company page entry (sidebar
item, tab, org-chart buttons).
- `ui/src/App.tsx`: `HiddenSettingsPageGate` route gates for the members
(incl. the legacy access route), invites, secrets, export, and import
pages under their `company.*` keys.
- `docs/deploy/environment-variables.md`: documents the new keys and
their semantics; the CLI and board-operator guides note that import is
unavailable on cloud-managed instances.
- Tests: new `server/src/__tests__/company-import-cloud-floor.test.ts`
and `ui/src/components/CloudManagedPageGate.test.tsx`, registry cases in
`packages/shared/src/settings-visibility.test.ts`, plus cloud and
hidden-key cases in the sidebar, settings-nav, and org-chart suites.

## Verification

- TypeScript typechecks pass for every workspace package (`tsc` in
shared, server, ui; the runner's Rust leg needs a local cargo toolchain
and is covered by CI).
- `pnpm test` on this branch fails only in 9 files that also fail on a
clean `origin/master` checkout on the same machine
(environment-dependent suites: live-listener probes,
workspace/native-runtime spawns, skill materialization). Zero
branch-only failures against that baseline; every suite touched by this
change passes.
- `server/src/__tests__/company-import-cloud-floor.test.ts` asserts:
every import route answers 403 `cloud_managed` under the server-token
signal; the managed-config signal alone also floors; every import route
answers 403 `settings_operator_managed` when `company.import` is hidden;
hiding other company pages leaves import open; the floor applies before
auth and body validation; export stays open on cloud-managed instances;
self-hosted import preview and job polling still work.
- `packages/shared/src/settings-visibility.test.ts` covers the new
`company.*` keys and `hidesCompanyPage`.
- UI suites assert the Import tab, sidebar entry, and org-chart button
disappear on a cloud-managed instance while Export stays, that
`/company/import` redirects through the gate, and that the `company.*`
keys hide their sidebar entries and tabs.

## Risks

- Low risk for self-hosted installs: the floor is inert unless a cloud
signal (`PAPERCLIP_CLOUD_TENANT_SERVER_TOKEN` or
`PAPERCLIP_MANAGED_CONFIG`) is present, and the self-hosted paths are
regression-tested.
- On cloud-managed instances this is a deliberate behavioral removal:
import (including agent-driven safe imports and resumable transfers)
stops working the moment an instance runs this build. In-flight chunked
transfers on such instances cannot be applied afterward; they answer
403.
- CLI import commands against a cloud-managed instance now fail with the
`cloud_managed` error; the message names the reason.
- The new `company.*` keys change nothing unless an operator sets them:
`PAPERCLIP_HIDDEN_SETTINGS` unset keeps behavior identical, and older
images ignore unknown keys by design. The four non-import company keys
hide UI only; their APIs stay live, which the registry documents
explicitly.

## Model Used

Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — agentic
coding session with tool use (code search, editing, local test
execution).

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-25 16:06:35 -07:00
Devin Foley a5c2add7be fix(adapter-utils): repair referenced-source ignore resolution type break and symlink descendant check (#12201)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Referenced projects are staged into run sandboxes, and a recent
change made each staging site resolve the project's Git-ignored paths
explicitly (`ignoreResolution` on `SandboxAdditionalSource`)
> - One test call site was left without the now-required property, so
`tsc` fails in adapter-utils and every Build/Typecheck CI job on master
is red (surfaced when the TypeScript 7 bump landed in the same window)
> - Separately, the new descendant check compares a logical caller path
against the physical toplevel git prints, so any symlinked path fails
ignore resolution spuriously — three of the suite's own tests fail on
macOS because temp dirs live under the `/var` → `/private/var` symlink
> - This pull request supplies the missing property at the test call
site and makes the descendant comparison symlink-safe via realpath on
both sides
> - The benefit is a green master again, plus referenced-project staging
that works from symlinked checkouts and temp directories

## Linked Issues or Issue Description

No public issue exists; the underlying problem follows the bug-report
template.

**What happened?**

`packages/adapter-utils` fails `tsc` on master:
`src/sandbox-managed-runtime.test.ts(2284,29): error TS2741: Property
'ignoreResolution' is missing in type '{ localPath: string; projectId:
string; }' but required in type 'SandboxAdditionalSource'.` Every
Build/Typecheck CI job is red. Independently,
`resolveReferencedSourceIgnore` returns `{ kind: "failed", reason:
"referenced project path is not a descendant of its own Git top level:
/var/... under /private/var/..." }` for any symlinked project path, and
three tests in the suite fail on macOS.

**Expected behavior**

Master typechecks. A referenced project whose path reaches git through a
symlink (macOS temp dirs, symlinked checkouts) resolves its ignore set
normally, and the descendant check still fails closed for genuinely
foreign paths.

**Steps to reproduce**

1. `pnpm --filter @paperclipai/adapter-utils exec tsc --noEmit` on
master — TS2741 at `sandbox-managed-runtime.test.ts:2284`.
2. On macOS: `pnpm vitest run
packages/adapter-utils/src/sandbox-managed-runtime.test.ts` — three
`resolveReferencedSourceIgnore` tests fail with the "not a descendant"
reason above.

**Paperclip version or commit**

master `29d12045f`.

## What Changed

- `sandbox-managed-runtime.test.ts:2284`: the one call site missing
`ignoreResolution` now passes `{ kind: "other" }`, matching every
sibling call site from the same change.
- `sandbox-managed-runtime.ts`: `resolveReferencedSourceIgnore` resolves
both the git toplevel and the caller's `localPath` through a new
`physicalPath` helper (realpath with a resolve fallback) before the
descendant comparison. Git prints physical toplevels, so both sides must
be physical; the fallback keeps the check failing closed when a path
vanishes mid-run.

## Verification

- `pnpm vitest run
packages/adapter-utils/src/sandbox-managed-runtime.test.ts` — 61/61 pass
on macOS (previously 58 passing, 3 failing, plus the typecheck break).
- `tsc --noEmit` in `packages/adapter-utils` is clean.

## Risks

- Low. The behavioral change is confined to path normalization before an
existing comparison; a realpath failure falls back to the prior string
comparison, so the fail-closed property is preserved.

## Model Used

Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — agentic
coding session with tool use (code search, editing, local test
execution).

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-25 15:48:24 -07:00
Nicky LeachandPaperclip 29d12045f4 perf(gateway): remove the whole-body string round trip on the HTTP/2 send path (#12189)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Sandbox agents send callback requests through a generated gateway.
> - The HTTP/2 callback path converts each full request body to a UTF-8
string.
> - The same path converts that string back to a buffer before it sends
the request.
> - This pull request sends the body buffer directly to the HTTP/2
forwarder.
> - The benefit is less copying and unchanged queue payload behavior.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The sandbox callback gateway HTTP/2 send path copies the full request
body through a UTF-8 string before it sends the body.

**Subsystem affected**

The affected subsystem is `packages/adapter-utils`, which contains
sandbox gateway and HTTP/2 adapter utilities.

**Current behavior**

The gateway reads the body into a buffer, converts the complete body to
a UTF-8 string, and converts that string back to a buffer for the HTTP/2
path. The queue path stores the string payload.

**Proposed behavior**

The gateway keeps a byte reader for the HTTP/2 path. A thin text wrapper
keeps the queue path behavior. The HTTP/2 path sends the original body
buffer.

**Reason and benefit**

The extra conversions add work and memory use without changing the
HTTP/2 body bytes. Direct buffer forwarding removes that work and
preserves the size limit and reject behavior.

**Breaking changes**

None. The queue payload remains a string. The body size limit, content
type check, and reject point remain unchanged.

**Additional context**

This change has no public issue link. The repository roadmap search
found no duplicate planned work. The implementation also adds tests for
non-ASCII JSON, malformed UTF-8, size limits, and queue payload shape.

## What Changed

- Add `readBodyBytes(req)` for byte-preserving body reads.
- Keep `readBody(req)` as a string wrapper for the queue path.
- Send the byte buffer directly on the HTTP/2 path.
- Add tests for byte identity, malformed UTF-8, size limits, and queue
payload shape.

## Verification

- `pnpm --filter @paperclip/adapter-utils test
src/sandbox-callback-bridge.test.ts` passed with 51 tests.
- `pnpm --filter @paperclip/adapter-utils exec tsc --noEmit` passed.
- The tests spawn the generated gateway and the real host HTTP/2 bridge.
- The tests verify byte identity, malformed UTF-8, pre-forward size
rejection, queue file protection, and string queue payloads.
- Full repository CI must pass before merge.

## Risks

Low risk. The HTTP/2 path changes its internal body conversion only. The
queue path keeps the prior string payload. The size limit and reject
point stay unchanged.

## Model Used

OpenAI Codex, GPT-5, tool use and code review assistance. The exact
runtime context window is managed by the Codex service.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` /
`Closes: #` / `Refs: #` OR (b) described the issue in-PR following the
relevant issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 14:56:17 -07:00
0cedb45df3 build(deps-dev): bump typescript from 5.9.3 to 7.0.2 (#11880)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3
to 7.0.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/microsoft/TypeScript/releases">typescript's
releases</a>.</em></p>
<blockquote>
<h2>TypeScript 7.0.2</h2>
<p><a
href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/">https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/</a></p>
<p>This tag was originally released at: <a
href="https://github.com/microsoft/typescript-go/releases/tag/typescript%2Fv7.0.2">https://github.com/microsoft/typescript-go/releases/tag/typescript%2Fv7.0.2</a></p>
<h2>TypeScript 6.0.3</h2>
<p>For release notes, check out the <a
href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/">release
announcement blog post</a>.</p>
<ul>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.0%22">fixed
issues query for TypeScript 6.0.0 (Beta)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.1%22">fixed
issues query for TypeScript 6.0.1 (RC)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.2%22">fixed
issues query for TypeScript 6.0.2 (Stable)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.3%22">fixed
issues query for TypeScript 6.0.3 (Stable)</a>.</li>
</ul>
<p>Downloads are available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/typescript">npm</a></li>
</ul>
<h2>TypeScript 6.0</h2>
<p>For release notes, check out the <a
href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/">release
announcement blog post</a>.</p>
<ul>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.0%22">fixed
issues query for TypeScript 6.0.0 (Beta)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.1%22">fixed
issues query for TypeScript 6.0.1 (RC)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.2%22">fixed
issues query for TypeScript 6.0.2 (Stable)</a>.</li>
</ul>
<p>Downloads are available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/typescript">npm</a></li>
</ul>
<h2>TypeScript 6.0.1 RC</h2>
<p>For release notes, check out the <a
href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-rc/">release
announcement blog post</a>.</p>
<ul>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.0%22">fixed
issues query for TypeScript 6.0.0 (Beta)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.1%22">fixed
issues query for TypeScript 6.0.1 (RC)</a>.</li>
</ul>
<p>Downloads are available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/typescript">npm</a></li>
</ul>
<h2>TypeScript 6.0 Beta</h2>
<p>For release notes, check out the <a
href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-beta/">release
announcement</a>.</p>
<ul>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.0%22+is%3Aclosed+">fixed
issues query for Typescript 6.0.0 (Beta)</a>.</li>
</ul>
<p>Downloads are available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/typescript">npm</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/microsoft/TypeScript/commit/1e4744d68260a7cb91b62b12edc3f6a2187faaf1"><code>1e4744d</code></a>
Merge branch 'main' into ts7-release</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/a5a219c3b5da0db4fa0ecf6c0b1f588c9af9c669"><code>a5a219c</code></a><code>microsoft/typescript-go#4558</code></li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/ecfe30dce91368d52c9a49b6095bb0b673a238f8"><code>ecfe30d</code></a>
Update status localization</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/5de25b5f8fec2ca35eadaed041f1f06d2e214895"><code>5de25b5</code></a>
Hide executable name in TypeScript status</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/d7ce74a75da2b80e8201506a1599c06549432b93"><code>d7ce74a</code></a>
Show bundled TypeScript version for packaged servers</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/29be66a607707f90d7a53103a4469bb3015a4d54"><code>29be66a</code></a>
Correct TS 7 release version to 7.0.2</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/ed2bd1bfa4aac5211ce4bc58fcd1313c7eddc8ff"><code>ed2bd1b</code></a>
Merge branch 'main' into ts7-release</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/887307575c58ea640dbeba3b4e8fdb6347cd3044"><code>8873075</code></a>
Bump the github-actions group across 1 directory with 3 updates
(microsoft/ty...</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/9427131ae2d4e230a90ee8a09daac4e75da3e311"><code>9427131</code></a>
Set up stable / nightly extension split, other prep
(microsoft/typescript-go#...</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/d4eaca5460a1f5f02a829e62706794b0a6fb903e"><code>d4eaca5</code></a><code>microsoft/typescript-go#4549</code></li>
<li>Additional commits viewable in <a
href="https://github.com/microsoft/TypeScript/compare/v5.9.3...v7.0.2">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~microsoft1es">microsoft1es</a>, a new
releaser for typescript since your current version.</p>
</details>
<br />

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Priya Raman <priya@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 14:49:05 -07:00
Nicky LeachandPaperclip 802f2af154 refactor(adapter-utils): delete the dead duplex body-chunk protocol code (#12186)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The adapter utilities package provides transport code for sandbox
agents
> - The retired `duplex_v1` broker no longer produces or consumes
body-chunk frames
> - Dead protocol code remains in the host codec, gateway copy, bridge
options, and tests
> - This pull request removes that dead code and keeps the READY
handshake unchanged
> - The benefit is a smaller transport surface with fewer unused paths
to maintain

## Linked Issues or Issue Description

**What existing behavior does this improve?**

This change improves the adapter utilities code that supports sandbox
duplex readiness and frame handling.

**Subsystem affected**

`packages/adapter-utils/` — sandbox transport codecs, execution targets,
and callback bridge tests.

**Current behavior**

The repository keeps body-chunk frame types, validators, a body spool,
decoder limits, and tests after the `duplex_v1` broker removal. No live
producer or consumer uses this code.

**Proposed behavior**

Remove the unused body-chunk protocol code and retain the READY
handshake, its strict checks, and its size limits.

**Reason and benefit**

The removal reduces dead code and keeps the host and embedded gateway
paths easier to inspect. It adds no new behavior.

**Breaking changes**

The removed frame types now decode as `unknown_type`. The live readiness
gate already ignores those frames. The READY handshake stays
byte-for-byte compatible.

**Additional context**

This cleanup follows [PR
#12171](https://github.com/paperclipai/paperclip/pull/12171), which
removed the duplex broker.

## What Changed

- Remove `duplex-body-spool.ts` and its test.
- Remove unused body-chunk frame types, validators, decoder code,
vectors, and limits.
- Remove the unused `reassembledBody` option and decoder limit
environment entry.
- Remove the embedded gateway decoder copy and the unused frame type
map.
- Keep the READY handshake and its existing boundary tests unchanged in
behavior.

## Verification

- `pnpm -F @paperclip/adapter-utils typecheck` passes.
- The duplex frame codec test passes with 30 tests.
- The sandbox execution-target test passes with 136 tests.
- The sandbox callback bridge test passes with 46 tests.
- CI must confirm all required checks after it starts.

## Risks

Low risk. The change removes code only. The READY handshake, HTTP/2 body
path, and byte-ledger path remain unchanged.

## Model Used

OpenAI Codex, 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 (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 14:33:38 -07:00
Nicky LeachandPaperclip 822e0aed93 fix(adapter-utils): move the workspace-restore merge lock to an instance-scoped root and surface restore failures on the run (#12187)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent adapters restore sandbox work into project workspaces after a
run
> - The restore lock used the target workspace parent, which can reject
writes
> - The teardown then hid restore errors, so a run could report success
with lost work
> - This pull request moves the lock into an instance-scoped root and
reports safe restore failure codes
> - The benefit is reliable restore coordination and visible failure
evidence without changing run success semantics

## Linked Issues or Issue Description

Refs: #10914

## What Changed

- Move the workspace-restore merge lock into a private, instance-scoped
root.
- Derive the lock key from the canonical target path with SHA-256.
- Resolve the lock root from the caller environment and reject unsafe
root types.
- Classify restore failures with three allowlisted codes.
- Add the failure code to run result JSON without exposing a host path
or process identifier.
- Keep restore failure fail-open for the run exit code and run status.

## Verification

- Run `npx vitest run
packages/adapter-utils/src/workspace-restore-merge.test.ts`.
- Run `npx vitest run
packages/adapter-utils/src/acpx-engine/run-fault-matrix.test.ts`.
- Run the four Codex credential suites.
- Confirm the branch includes the current `master` commit and no manual
lockfile edit.
- Confirm all pull request checks and the Greptile review reach a
terminal green state.

## Risks

- The lock path changes for workspace restore and removes the
sibling-directory fallback.
- A misconfigured or inaccessible instance home can still stop lock
setup.
- Restore remains fail-open, so callers must inspect the result evidence
when a restore fails.

## Model Used

OpenAI GPT-5. The model used tool calls and code execution to validate
and route an author-provided change. The implementing engineer authored
the 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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 14:32:14 -07:00
Nicky LeachandPaperclip d866ff374e fix(adapter-utils): report real transferred bytes for project sync, git-history export, and workspace restore (#12180)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent adapters move files between the host and sandbox during a run
> - The sync transport reports transferred bytes, but some progress
lines discard this value
> - Discarded byte totals make large transfers display as `0.0 MB`
> - This pull request passes the transport total to the affected
progress lines
> - The benefit is accurate transfer progress without changing file
movement or confinement checks

## Linked Issues or Issue Description

**What happened?**

Three file-sync progress lines displayed `0.0 MB` when the transport
moved data. The affected paths cover referenced-project staging, native
git-history export, and native workspace restore.

**Expected behavior**

Each progress line should display the bytes that the sync transport
transfers. A provider that reports zero bytes should preserve the known
host-side value for inbound workspace sync.

**Steps to reproduce**

1. Run a sandbox task that stages a referenced project.
2. Run a task that uses native git-history export or native workspace
restore.
3. Inspect the file-sync progress lines during each transfer.

**Paperclip version or commit**

Commit `8062612baa20036a1defce8bbd683c038ba187d5`.

**Deployment mode**

Built from source with the adapter-utils Vitest suite.

## What Changed

- Add a helper that sums valid `bytesTransferred` values from a
`SandboxSyncResult`.
- Use the transport total for referenced-project staging, native
git-history export, and native workspace restore.
- Preserve the caller count when referenced-project staging reports zero
bytes.
- Add tests for non-zero progress and the zero-byte fallback.

## Verification

- Run `npx vitest run
packages/adapter-utils/src/sandbox-managed-runtime.test.ts` from the
repository root.
- Run the TypeScript check for `packages/adapter-utils`.
- Confirm that the new tests cover referenced-project staging, native
workspace restore, native git-history export, and the zero-byte
fallback.

## Risks

This change affects progress reporting only. It does not change
transferred files, transfer order, provider behavior, or confinement
checks.

## Model Used

OpenAI Codex, GPT-5, with tool use and code execution. The model
reviewed and routed the author-provided change. The implementing
engineer authored the 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 have addressed all Greptile and reviewer comments before
requesting merge

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 14:24:13 -07:00
Nicky LeachandPaperclip 6880213de5 fix(adapter-utils): honor .gitignore for referenced-project staging (#12184)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Sandbox adapters stage project files before an agent starts.
> - Referenced projects ignored Git-ignored paths and copied large local
directories.
> - This behavior increased staging time and disk use, and it differed
from anchor workspaces.
> - This pull request resolves Git-ignored paths once and shares that
result across all referenced-project consumers.
> - The benefit is smaller, faster, and consistent project staging.

## Linked Issues or Issue Description

No public GitHub issue exists for this bug.

**What happened?**
Referenced-project staging copied Git-ignored paths, except for a fixed
list of heavy directory names. A large repository therefore used much
more time and disk space than the same repository in an anchor
workspace.

**Expected behavior**
Referenced-project staging should exclude the same Git-ignored paths
that the workspace staging path excludes.

**Steps to reproduce**
1. Create a referenced project with a large Git-ignored directory.
2. Start a sandbox or SSH run that stages the referenced project.
3. Observe that the ignored directory enters the staged content.

**Paperclip version or commit**
Commit `9964b034bbff24e700c8eccf5a8b1fc3daa44bf2`.

**Deployment mode**
Built from source.

## What Changed

- Resolve each referenced project's Git-ignored paths once before
staging.
- Carry the resolved paths as a required field on
`SandboxAdditionalSource`.
- Reuse the resolved paths in sandbox staging, SSH staging, and
content-signature code.
- Harden the read-only Git helper with a bounded process, a reduced
environment, and disabled system and global configuration.
- Fail closed on Git errors, timeouts, and invalid path relations.
- Escape tar glob metacharacters in ignore-derived exclude entries.
- Add and update unit tests for the resolver and its three consumers.

## Verification

- `pnpm vitest run --config packages/adapter-utils/vitest.config.ts`
passes 266 tests locally.
- `pnpm exec tsc --noEmit -p packages/adapter-utils/tsconfig.json`
passes locally.
- CI must pass on this pull request.
- Greptile must report 5/5 with no unresolved comments before merge.

## Risks

- A Git error or timeout now prevents staging for the affected
referenced project.
- The resolver uses a bounded read-only Git process and fails closed by
design.
- The change stays inside `packages/adapter-utils` and does not change
the database schema.

## Model Used

Claude Sonnet 5 (Anthropic) assisted the implementation with code
execution and tool use. The exact context window and reasoning mode are
not recorded.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` /
`Closes: #` / `Refs: #` OR (b) described the issue in-PR following the
relevant issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 14:22:47 -07:00
Dotta 397de98193 feat(runner): add flagged Codex execution adapter (#12188)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Paperclip Runner now has protocol, provider, tool, package,
persistence, and hidden server boundaries.
> - The server still cannot select that path for a real agent heartbeat.
> - A new runtime must not change any existing direct adapter.
> - An experimental runtime must fail closed when its rollout flag is
off.
> - This pull request adds one guarded Codex vertical slice through
runnerd.
> - The benefit is a production-built runner path that users cannot
start by default.

## Linked Issues or Issue Description

Refs #11962

Refs #12111

Refs #12169

Refs #12176

**Subsystem affected**

Cross-cutting. The change affects the runner package, server
orchestration, shared settings, and adapter configuration UI.

**Problem or motivation**

The hidden PRP coordinator cannot execute a real heartbeat. The
application also needs an explicit rollout boundary before it can expose
the experimental runner. Existing direct adapters must keep their
current execution and finalization behavior.

**Proposed solution**

Add `paperclip_runner` as a Codex-only adapter behind the default-off
`enableNativeRunner` instance flag. Select the native runtime only for
that adapter. Persist the run binding before runnerd starts. Wait for
the durable PRP result and terminal event. Resume the real Codex
provider thread on later heartbeats. Keep persisted native runs readable
and recoverable after the flag changes.

**Alternatives considered**

The server could route `codex_local` through runnerd. That option would
change an existing adapter and weaken rollback safety. The server could
expose all providers now. That option would add unreviewed provider
behavior. The build could depend on a prebuilt runner binary. That
option would make source builds architecture-dependent and difficult to
verify.

**Roadmap alignment**

This work supports the shipped enforced-outcomes, governed-tool, and
self-healing-run milestones. It does not add a new roadmap surface. It
is the guarded execution step after the merged hidden runner boundaries.

**Additional context**

This is the next replacement for the closed large runner pull request.
Task-thread presentation remains a separate follow-up so this change can
preserve the current direct-adapter UI.

## What Changed

- Add `paperclip_runner` as an explicit Codex-only adapter.
- Add the default-off `enableNativeRunner` instance flag.
- Reject fresh create, hire, import, switch, and execution requests
while the flag is off.
- Allow edits to persisted runner agents while the flag is off.
- Recover an already persisted native run even after the flag is
disabled.
- Keep every built-in direct adapter on its existing runtime path.
- Persist an immutable native run binding and revisioned completion
contract before runnerd starts.
- Execute server to PRP to runnerd to Codex to server through the hidden
coordinator.
- Validate the durable result against the terminal event and exact
completion criteria before finalization.
- Preserve the Codex provider thread ID and use `thread/resume` on the
next heartbeat.
- Strip unsupported Codex configuration fields from the experimental
adapter.
- Build a target-native release runner binary from source and vendor it
into the server distribution.
- Install Rust only in the Docker build stage. Do not add a workflow or
lockfile change.
- Stop the runner process group on completion, cancellation, and forced
shutdown.

## Verification

- Run `pnpm --filter @paperclipai/paperclip-runner check:all`. All 69
TypeScript tests and 58 Rust tests pass. Protocol, conformance, replay,
formatting, and generated-file checks pass.
- Run the 12 focused adapter, settings, runtime-selection, coordinator,
direct-isolation, and real Codex integration test files. All 186 tests
pass.
- The real integration test uses PostgreSQL, HTTP, WebSocket, runnerd,
and a fake Codex app server. It proves one `thread/start` followed by
one `thread/resume`.
- Run `pnpm -r typecheck`.
- Run `pnpm build`.
- Run `pnpm check:token-gates`.
- Build the Docker `build` target from a clean context. Confirm that the
server distribution contains an executable `paperclip-runnerd` built
with Debian Rust 1.85.
- Start the server through the source-mode tsx entry point with the
package `dist` directory absent. Confirm the vendor shim resolves source
exports and the server boots.
- Run `pnpm test:run` twice. On this macOS host, 405 files pass and 1
file skips. Eight untouched workspace and loopback tests fail because
macOS resolves `/tmp` and `/var` through `/private` and because
PID-derived test ports exceed 65535. Linux CI must pass the full suite.
- Confirm that the diff contains 52 files. Confirm that it contains no
`.github` or `pnpm-lock.yaml` change.

## Risks

- The feature flag is off by default. A fresh native start fails with a
stable error while the flag is off.
- A persisted native run remains recoverable after the flag changes.
This prevents rollout changes from corrupting recorded work.
- Only local Codex execution is accepted. Other providers and remote
work modes fail closed.
- Existing direct adapters do not start runnerd, create native rows, use
native status arbitration, or enter native finalization.
- The runner receives its one-use bootstrap ticket through the child
environment. The server does not put the ticket in command arguments or
logs.
- The server validates the company, task, agent, run, runner, session,
completion contract, result, and terminal binding before it accepts
completion.
- The build compiles a target-native Rust binary. Cross-platform release
packaging remains a later concern. Source builds and Docker builds
compile for their current target.
- Docker needs enough build memory for the existing server TypeScript
compile. The Docker build stage sets a 4 GB V8 heap limit.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- OpenAI Codex with GPT-5. The exact deployment ID and context-window
size are not exposed. The model used agentic reasoning, repository
tools, code execution, and test execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and applicable tests 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
2026-08-25 16:03:41 -05:00
Devin Foley 243430f76e feat: agents see the company skill library at runtime (#12147)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - An agent's runtime mounts only its own enabled skills; nothing tells
the model what else the company skill library holds
> - From inside a sandbox, "installed but not enabled for me" and "does
not exist" look identical, so agents tell users freshly installed skills
are not installed
> - This pull request renders the library as a deterministic markdown
section appended to claude-local agent instructions, and adds a
paperclipListSkills MCP tool
> - The benefit is that agents report the true state ("installed, not
enabled for me — ask an operator to enable it") instead of a false
negative

## Linked Issues or Issue Description

**What existing behavior does this improve?**

How agents reason about the company skill library at runtime.

**Subsystem affected**

`packages/adapter-utils` (new pure builder),
`packages/adapters/claude-local` (instructions append),
`packages/mcp-server` (new tool).

**Current behavior**

The runtime hands adapters the full library list, but only the agent's
enabled skills are mounted, and no prompt content or MCP tool describes
the rest. Agents inspect their sandbox, find nothing, and report
installed skills as not installed.

**Proposed behavior**

A "Company skill library" markdown section lists every skill as
`enabled`, `installed, not enabled for you`, or `enabled but
unavailable: <cause>`, with instructions to report the not-enabled state
accurately and ask an operator to enable it. claude-local appends it to
the agent instructions text. A `paperclipListSkills` MCP tool exposes
the same list on demand.

**Breaking changes**

None. Other adapters are untouched (they can adopt the builder later);
the manifest is deterministic, so the claude-local prompt-bundle cache
only busts when the library actually changes.

## What Changed

- New `packages/adapter-utils/src/skill-library-manifest.ts` with
`buildSkillLibraryManifestMarkdown` (pure, key-sorted, deterministic;
renders the missing-cause detail from #12146).
- `packages/adapters/claude-local/src/server/execute.ts` appends the
manifest to `combinedInstructionsContents` (creating it when no
instructions file is configured).
- `packages/mcp-server/src/tools.ts` adds `paperclipListSkills` hitting
`GET /companies/:companyId/skills`.

## Verification

- `npx vitest run
packages/adapter-utils/src/skill-library-manifest.test.ts` (from repo
root) — 3 tests: byte-identical output for shuffled input, state
rendering incl. the unavailable cause, change detection.
- `cd packages/mcp-server && npx vitest run` — new tool routing test
passes (13 passed; 1 pre-existing failure on my machine reproduces
unchanged at the branch base).
- `cd packages/adapters/claude-local && npx vitest run` — 244 passed, 1
skipped.
- `pnpm run typecheck` clean in adapter-utils, mcp-server, claude-local.

## Risks

- Prompt growth is one line per installed skill plus a five-line header
— bounded and only present when the library is non-empty. Stacked on
#12146 so the manifest's "enabled but unavailable" state reflects real
materialization failures.

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic) with extended thinking
and 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
2026-08-25 14:01:52 -07:00
Devin Foley 79b464bf9d fix(server): surface skill materialization failures instead of dropping the skill (#12146)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Runtime skill listing materializes each company skill's files before
handing them to the agent's adapter
> - A materialization failure was swallowed with catch-to-null, and the
skill silently vanished from the runtime while the library still showed
it installed
> - Operators saw "installed", agents saw nothing, and nobody saw the
cause; on claude-local a missing desired skill could even crash the
prompt-bundle hasher
> - This pull request turns both failure paths into structured "missing"
entries with the real error and makes every adapter skip unmountable
entries explicitly
> - The benefit is that a broken skill shows up as broken, with its
cause, instead of not existing

## Linked Issues or Issue Description

**What happened?**

A company skill whose runtime files fail to materialize (deleted source,
missing stored SKILL.md copy, failed version snapshot) disappears from
`listRuntimeSkillEntries` with no trace. Agent skill snapshots report a
generic "not available" with no cause. On claude-local, a desired skill
whose source path does not exist reaches the prompt-bundle hasher, whose
`fs.lstat` throws and can fail the whole run.

**Expected behavior**

The skill appears with `sourceStatus: "missing"` and a `missingDetail`
carrying the underlying error, snapshots and the UI show it as broken,
and adapters skip it at mount time with a logged warning instead of
crashing or dangling-symlinking.

**Steps to reproduce**

Install a local-path skill referenced by an agent, delete its source
directory contents so the stored SKILL.md copy cannot be recovered, and
start a run: before this change the skill vanishes from the runtime set
silently; on claude-local a pinned-but-unmaterializable version can fail
bundle preparation.

## What Changed

- `server/src/services/company-skills.ts` `resolveRuntimeSkillSource`:
both `.catch(() => null)` sites (version snapshot, runtime
materialization) now return the structured `{status: "missing", source,
detail}` shape the deliberate missing branch already used, with the
underlying error message in `detail`.
- `packages/adapter-utils/src/server-utils.ts`:
`isPaperclipSkillSourceMissing` is exported with a doc comment.
- `packages/adapters/claude-local/src/server/execute.ts`: missing
desired skills are filtered out of the prompt bundle and each one logs a
`[paperclip] Warning` with its detail to the run output.
- `cursor-local`, `gemini-local`, `kimi-local`, `opencode-local`,
`pi-local` `execute.ts`: mount loops (and the cursor/gemini injection
calls) skip missing entries instead of symlinking a nonexistent path.

## Verification

- `cd server && npx vitest run
src/__tests__/company-skills-service.test.ts` — new test pins the
missing-with-cause entry for a failed materialization. Nine pre-existing
project-workspace tests in this file fail on my machine at clean
`master` too (environment-specific); their count is unchanged by this
PR.
- `cd server && npx vitest run
src/__tests__/heartbeat-runtime-skills.test.ts
src/__tests__/claude-local-skill-sync.test.ts
src/__tests__/cursor-local-skill-sync.test.ts
src/__tests__/cursor-local-skill-injection.test.ts
src/__tests__/gemini-local-skill-sync.test.ts` — 12 tests pass.
- `cd packages/adapters/claude-local && npx vitest run` — 244 passed, 1
skipped.
- `pnpm run typecheck` clean in server, adapter-utils, and all six
touched adapters.

## Risks

- Runtime skill entry lists grow by the previously dropped entries (now
flagged missing). All shipped consumers either intersect with desired
sets, already handle `sourceStatus: "missing"`, or now skip missing
entries at mount time. The snapshot layer already understood the missing
shape via the `materializeMissing: false` path, so downstream contracts
are unchanged.

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic) with extended thinking
and 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
2026-08-25 13:51:57 -07:00
Devin Foley fcb84d472d feat: already-imported transfer error names the landed company (#12144)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Chunked company-import transfers are deduplicated by content: a
byte-identical zip that already finished an apply is rejected
> - The rejection said only "this exact package was already imported by
a completed transfer" without saying where that import went
> - Users who could not find the earlier import read the rejection as
data loss and kept retrying, or exported again and created duplicate
companies
> - This pull request makes the declaration response carry the company
the completed apply created, and both clients name it in the error
> - The benefit is that the dedupe rejection now points at the existing
import instead of implying it vanished

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The `alreadyCompleted` rejection when re-declaring a chunked
company-import transfer.

**Subsystem affected**

Shared transfer contract
(`packages/shared/src/company-import-transfer.ts`), transfer declaration
route (`server/src/routes/companies.ts`), web import page, CLI import
command.

**Current behavior**

`POST /api/companies/import/transfers` returns `alreadyCompleted: true`
with no pointer to the earlier import. Web and CLI raise "This exact
package was already imported by a completed transfer. Re-export the
package to import it again."

**Proposed behavior**

The response includes an optional `company` field (`{id, name,
issuePrefix} | null`) resolved from the completed run's company link.
Web and CLI raise a shared message: `… It created the company
"Paperclip" (PAPA) — open it from the company switcher. Re-export the
package to import it again.` A company that was deleted since (or a link
that was never written) degrades to `null` and the original message.

**Breaking changes**

None. The new response field is optional; old clients ignore it.

## What Changed

- `CompanyImportTransferCreated` gains optional `company`, plus a shared
`buildAlreadyImportedMessage` used by both clients.
- The declaration route's `alreadyCompleted` branch resolves the landed
company null-safely via `companyService.getById`.
- Web (`ui/src/pages/CompanyImport.tsx`) and CLI
(`cli/src/commands/client/company.ts`) raise the shared message.

## Verification

- `cd packages/shared && npx vitest run
src/company-import-transfer.test.ts` — 3 tests (named company, id
fallback, no-company original message).
- `cd server && npx vitest run
src/__tests__/company-import-transfer-routes.test.ts` — 24 tests; the
re-declaration test now asserts the company payload and the
deleted-company null path.
- `cd cli && npx vitest run
src/__tests__/company-import-transfer.test.ts` — 17 tests; new test pins
the named-company message.
- `cd ui && npx vitest run src/pages/CompanyImport.test.tsx` — 23 tests.
- `pnpm run typecheck` clean in shared, server, ui, cli.

## Risks

- Low risk. The lookup runs only on the `alreadyCompleted` branch and is
null-safe; the transfer run is already scoped to the requesting actor
(user + instance context in the actor key), so the response never names
a company the caller did not import.

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic) with extended thinking
and 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
2026-08-25 13:51:50 -07:00
Devin Foley 11f6c754c9 feat: dedicated import pause reason with visible paused-assignee notices (#12140)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Company import parks every imported agent as a safety default, and
issue assignment wakes are dropped for paused agents
> - The pause was recorded as the generic reason "system" and was almost
invisible: the chat-style task thread showed nothing, the legacy notice
had no action, and the new-task dialog gave no hint
> - Users assigned tasks in an imported company, nothing ran, and there
was no explanation — the imported company looked broken
> - This pull request records a dedicated "import" pause reason and
makes the paused state visible and fixable where the user is looking
> - The benefit is that a silent no-op becomes an explained state with a
one-click resume

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Working with a company whose agents arrived paused from a company
import.

**Subsystem affected**

Shared constants (`PAUSE_REASONS`), company import service
(`server/src/services/company-portability.ts`), task thread and new-task
dialog UI.

**Current behavior**

Imported agents get `pauseReason: "system"`, the same value
plugin-managed and built-in agent pauses use. Assigning an issue to a
paused agent silently drops the wake. The chat-style task thread renders
no paused notice; the legacy thread's notice says "It was paused by the
system." with no action and only renders when the composer is shown.

**Proposed behavior**

Import writes `pauseReason: "import"`. The paused-assignee notice
explains the import pause, offers an inline "Resume agent" button
(suppressed for budget pauses, which clear on their own), and renders
for read-only viewers. The chat-style task thread shows the same notice
above the composer. The new-task dialog warns when the selected assignee
is paused.

**Breaking changes**

None. `PAUSE_REASONS` is widened, not changed; the column already stores
free-text values in other paths, and every consumer is an equality check
with a manual fallback, so an older client shows the generic fallback
copy for the new value.

## What Changed

- `packages/shared/src/constants.ts`: `"import"` added to
`PAUSE_REASONS`.
- `server/src/services/company-portability.ts`: the import pause patch
writes `pauseReason: "import"`.
- `ui/src/components/IssueChatThread.tsx`: `IssueAssigneePausedNotice`
gains import copy, a Resume button, test ids, and is exported; it now
renders even when the composer is hidden. New `onResumeAssignee` /
`resumeAssigneePending` props.
- `ui/src/components/TaskChatThread.tsx`: renders the paused-assignee
notice above the composer dock (the chat-style thread previously had no
paused surface at all).
- `ui/src/pages/IssueDetail.tsx`: wires a resume mutation
(`agentsApi.resume`) through both thread variants and invalidates the
company agent list.
- `ui/src/components/NewIssueDialog.tsx`: inline note when the chosen
assignee is paused, with import-specific copy.

## Verification

- `cd server && npx vitest run
src/__tests__/company-portability.test.ts` — 82 tests pass (pause pin
updated to `"import"`).
- `cd ui && npx vitest run src/components/IssueChatThread.test.tsx
src/components/NewIssueDialog.test.tsx
src/components/TaskChatThread.test.tsx` — 120 tests pass (new: notice
copy per reason, resume click, budget suppression, active-agent null
render, dialog note).
- `pnpm run typecheck` in `packages/shared`, `server`, and `ui` — clean.

## Risks

- Low risk. The resume action calls the existing `POST
/agents/:id/resume` route with its existing guards. Existing rows keep
`"system"` and fall back to the current generic copy; only new imports
write `"import"`.

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic) with extended thinking
and 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
2026-08-25 13:51:42 -07:00
Dotta 9964b034bb feat(runner): add hidden server PRP coordinator (#12176)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Paperclip Runner needs a narrow server trust boundary before an
adapter can start it.
> - The package has durable runner transport, but the server does not
host or authorize that transport.
> - Native persistence exists, but no writer connects PRP events to
those records.
> - A direct adapter must not enter this path by accident.
> - This pull request adds a hidden, run-bound PRP server coordinator.
> - The benefit is a recoverable server boundary that remains
unavailable to normal execution.

## Linked Issues or Issue Description

Refs #11962

Refs #12129

Refs #12169

**Subsystem affected**

Cross-cutting. The change affects the runner package and server
orchestration.

**Problem or motivation**

The server cannot authenticate runnerd, commit PRP events before ACK,
authorize semantic tools, or enter native finalization from a durable
runner result. The application must have this hidden boundary before a
guarded adapter can use the runner.

**Proposed solution**

Add an authenticated PRP WebSocket authority and register it only for
one exact persisted native Codex run. Bind each connection and event to
the company, issue, agent, run, runner, session, turn, item, and
verified runner identity. Commit each event before its cumulative ACK.
Project only authorized same-task read tools. Rebuild the accepted
result and finalization record from durable result and terminal events.

**Alternatives considered**

The server could expose a broad runner API key or route semantic calls
through existing adapter endpoints. Those options grant too much
authority and weaken replay recovery. The server could also add the
user-facing adapter in this pull request. That option would mix rollout
selection with the transport trust boundary and make legacy
compatibility harder to review.

**Roadmap alignment**

This work supports the shipped enforced-outcomes, governed-tool, and
self-healing-run milestones. It does not add a new roadmap surface.

## What Changed

- Add the durable PRP server authority with one-use bootstrap tickets,
reconnect leases, encrypted frames, bounded state, cumulative ACKs, and
idempotent commands.
- Add `/api/runner/v1/connect/:runId`. Derive its `ws://` or `wss://`
URL from the configured Paperclip API URL.
- Register one authority only after the coordinator verifies the
complete native Codex run binding.
- Commit validated PRP events to `heartbeat_run_events` before ACK.
Reject source gaps and conflicting replays.
- Rebuild accepted results and finalization records from durable result
and terminal events. Enforce finalization owner leases and retry times.
- Project five same-task read operations. Recheck run, agent, task, and
company authority for each call.
- Keep the route hidden. No adapter selects this coordinator, and no
code starts runnerd.
- Vendor the compiled runner TypeScript runtime into the server package
while keeping the workspace package development-only for the server.
- Document the package, database writer, run-log payload, and credential
exclusions.

## Verification

- Run `pnpm --filter @paperclipai/paperclip-runner check:all`. All
TypeScript protocol checks and 69 Vitest tests pass, including
commit-before-ACK crash recovery. All 43 Rust unit tests and 13 Rust
integration tests pass. Conformance and replay parity pass.
- Run the focused server WebSocket, coordinator, package-build, and
startup-wiring suites. All 26 tests pass, including a clean-checkout
reproduction with the runner `dist` directory absent.
- Run `pnpm -r typecheck`.
- Run `pnpm test:run`.
- Run `pnpm build`.
- Confirm that the diff contains 19 files. Confirm that it contains no
workflow or `pnpm-lock.yaml` change.

## Risks

- The server installs the WebSocket route at startup. An unregistered or
malformed run path fails closed and creates no native record.
- Bootstrap tickets are one use. The private state directory uses mode
`0700`, and the state file uses mode `0600`. The file stores derived
authentication verifiers and never stores raw tickets or lease tokens.
- The journal has explicit frame, command, event-window, and file-size
bounds. A bound violation closes the runner connection or rejects the
command.
- A runner event reaches the database before its ACK. A crash between
event commit and ACK causes a byte-equivalent replay, not a second
logical effect.
- The coordinator accepts only an existing queued or running native
Codex row with exact company, task, agent, runner, session, and
completion-contract ownership.
- Existing direct adapters do not call this service. They keep their
current execution, transcript, result, and finalization paths.
- The server has no production dependency on the private runner package.
Its build copies the compiled runtime into `server/dist`; the workspace
link is development-only. This adds no external package and does not
change the lockfile.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- OpenAI Codex with GPT-5. The exact deployment ID and context-window
size are not exposed. The model used agentic reasoning, repository
tools, code execution, and test execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-25 14:17:14 -05:00
c5382b36ba build(deps-dev): bump vite from 6.4.3 to 8.2.2 (#11887)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite)
from 6.4.3 to 8.2.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/releases">vite's
releases</a>.</em></p>
<blockquote>
<h2>plugin-legacy@8.2.2</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/plugin-legacy@8.2.2/packages/plugin-legacy/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.2.2</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.2.2/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>plugin-legacy@8.2.1</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/plugin-legacy@8.2.1/packages/plugin-legacy/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.2.1</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.2.1/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>create-vite@8.2.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/create-vite@8.2.0/packages/create-vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>plugin-legacy@8.2.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/plugin-legacy@8.2.0/packages/plugin-legacy/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.2.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.2.0/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.2.0-beta.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.2.0-beta.0/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.1.5</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.5/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.1.4</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.4/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.1.3</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.3/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.1.2</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.2/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.1.1</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.1/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>create-vite@8.1.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/create-vite@8.1.0/packages/create-vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>plugin-legacy@8.1.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/plugin-legacy@8.1.0/packages/plugin-legacy/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.1.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.0/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>plugin-legacy@8.1.0-beta.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/plugin-legacy@8.1.0-beta.0/packages/plugin-legacy/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md">vite's
changelog</a>.</em></p>
<blockquote>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v8.2.1...v8.2.2">8.2.2</a>
(2026-08-20)<!-- raw HTML omitted --></h2>
<h3>Features</h3>
<ul>
<li><strong>deps:</strong> widen <code>@vitejs/devtools</code> peer
range to v0.5.0 (<a
href="https://redirect.github.com/vitejs/vite/issues/23302">#23302</a>)
(<a
href="https://github.com/vitejs/vite/commit/495d9ff5a7d843ca876a9e49799947a5deb704c7">495d9ff</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>bundled-dev:</strong> handle lazy request error (<a
href="https://redirect.github.com/vitejs/vite/issues/23291">#23291</a>)
(<a
href="https://github.com/vitejs/vite/commit/3ba026dade4af56df08815310d3458fa110f5c5c">3ba026d</a>)</li>
<li><strong>bundled-dev:</strong> hot update through circular imports
instead of reloading (<a
href="https://redirect.github.com/vitejs/vite/issues/23259">#23259</a>)
(<a
href="https://github.com/vitejs/vite/commit/3dbddefaafc091a879b06f9279296f776691e455">3dbddef</a>)</li>
<li><strong>config:</strong> resolve sourcemap paths against sourcemap
location (<a
href="https://redirect.github.com/vitejs/vite/issues/23239">#23239</a>)
(<a
href="https://github.com/vitejs/vite/commit/05a003e6a17a84d75f907ea0f1598bc39b8dce6c">05a003e</a>)</li>
<li><strong>css:</strong> don't pass empty targets to lightningcss (<a
href="https://redirect.github.com/vitejs/vite/issues/23295">#23295</a>)
(<a
href="https://github.com/vitejs/vite/commit/2804636ff608d105928009d274ffba7cfbe55340">2804636</a>)</li>
<li><strong>define:</strong> fix match escaped dots to support
$-prefixed define keys (<a
href="https://redirect.github.com/vitejs/vite/issues/23249">#23249</a>)
(<a
href="https://github.com/vitejs/vite/commit/dcf88bd2ad2b1a8845f9029587cc8c825e382d42">dcf88bd</a>)</li>
<li><strong>deps:</strong> update all non-major dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/23217">#23217</a>)
(<a
href="https://github.com/vitejs/vite/commit/ba958bddfc9cabe302c6b34269dcf5c9634531e0">ba958bd</a>)</li>
<li><strong>deps:</strong> update rolldown-related dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/23218">#23218</a>)
(<a
href="https://github.com/vitejs/vite/commit/83ecb2c8059e8ce946a7cc835d4c14ef78aef4fd">83ecb2c</a>)</li>
<li><strong>module-runner:</strong> exclude completed modules from
in-flight cycle detection (fix <a
href="https://redirect.github.com/vitejs/vite/issues/22999">#22999</a>)
(<a
href="https://redirect.github.com/vitejs/vite/issues/23009">#23009</a>)
(<a
href="https://github.com/vitejs/vite/commit/d9b10a98db1c293ee64300bd75d568b44c8ae931">d9b10a9</a>)</li>
<li><strong>optimizer:</strong> close custom extension analysis bundles
(<a
href="https://redirect.github.com/vitejs/vite/issues/23207">#23207</a>)
(<a
href="https://github.com/vitejs/vite/commit/8fb76752836f61224d3095b502fa237b478a06b2">8fb7675</a>)</li>
<li>reduce Windows 8.3-short-name detection false-positives (<a
href="https://redirect.github.com/vitejs/vite/issues/23066">#23066</a>)
(<a
href="https://github.com/vitejs/vite/commit/02cffa9e2d38d5d8f12e4043ee9d0f7abb1471e2">02cffa9</a>)</li>
<li>respect <code>resolve.preserveSymlinks</code> when resolving root
(fix <a
href="https://redirect.github.com/vitejs/vite/issues/23197">#23197</a>)
(<a
href="https://redirect.github.com/vitejs/vite/issues/23198">#23198</a>)
(<a
href="https://github.com/vitejs/vite/commit/8413052731836d4aaf3eb94a0f25788dd35d2888">8413052</a>)</li>
<li><strong>ssr:</strong> rewrite computed key of destructing parameter
(<a
href="https://redirect.github.com/vitejs/vite/issues/23307">#23307</a>)
(<a
href="https://github.com/vitejs/vite/commit/9db0b61d4c9c7caad7ea1d9670b637faf2bb6c93">9db0b61</a>)</li>
<li><strong>vite:</strong> update outdated upstream file links in
license comments (<a
href="https://redirect.github.com/vitejs/vite/issues/23285">#23285</a>)
(<a
href="https://github.com/vitejs/vite/commit/c0f2fc607ee97ee4499337b04826420c00654065">c0f2fc6</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><strong>build:</strong> note cssTarget precedence (<a
href="https://redirect.github.com/vitejs/vite/issues/23200">#23200</a>)
(<a
href="https://github.com/vitejs/vite/commit/a20a35ec0685e374519864d0f41dd5f6e9ba0271">a20a35e</a>)</li>
</ul>
<h3>Miscellaneous Chores</h3>
<ul>
<li>fix ts errors in build test cases (<a
href="https://redirect.github.com/vitejs/vite/issues/23209">#23209</a>)
(<a
href="https://github.com/vitejs/vite/commit/a0cfcf72f8ef8bf0f2f11d553333b9bb31f1d316">a0cfcf7</a>)</li>
</ul>
<h3>Code Refactoring</h3>
<ul>
<li>use JSON import attributes instead of readFileSync in constants (<a
href="https://redirect.github.com/vitejs/vite/issues/23258">#23258</a>)
(<a
href="https://github.com/vitejs/vite/commit/1d9fa392a43229241f80630236f8552ce8f7cd0f">1d9fa39</a>)</li>
<li>use named regex constants over inline literals (<a
href="https://redirect.github.com/vitejs/vite/issues/22964">#22964</a>)
(<a
href="https://github.com/vitejs/vite/commit/5c1c6c609718303202832f706884192e1f1e9223">5c1c6c6</a>)</li>
</ul>
<h3>Tests</h3>
<ul>
<li><strong>define:</strong> close rolldown bundler after generate (<a
href="https://redirect.github.com/vitejs/vite/issues/23231">#23231</a>)
(<a
href="https://github.com/vitejs/vite/commit/b4d66fee14d970f45b8a6f3d7d6aee73ca9b88ab">b4d66fe</a>)</li>
<li><strong>module-runner:</strong> add TLA circular import case (<a
href="https://redirect.github.com/vitejs/vite/issues/23299">#23299</a>)
(<a
href="https://github.com/vitejs/vite/commit/4a261f242831bef92afd2f1aacfb81eab9dec371">4a261f2</a>)</li>
<li><strong>module-runner:</strong> simplify server-hmr tests (<a
href="https://redirect.github.com/vitejs/vite/issues/23300">#23300</a>)
(<a
href="https://github.com/vitejs/vite/commit/599b44b6600ec426e10cd556908d53b027b0c4fb">599b44b</a>)</li>
<li><strong>ssr:</strong> add destructing assignment case for
moduleRunnerTransform (<a
href="https://redirect.github.com/vitejs/vite/issues/23308">#23308</a>)
(<a
href="https://github.com/vitejs/vite/commit/cb77e2a93bad2a8ece00b4aa0ef507c092582c45">cb77e2a</a>)</li>
</ul>
<h3>Build System</h3>
<ul>
<li>use JSON import attributes instead of readFIleSync in rolldown
configs (<a
href="https://redirect.github.com/vitejs/vite/issues/23251">#23251</a>)
(<a
href="https://github.com/vitejs/vite/commit/d615bcdb23d96c1ca5ce1ee45e21d8d87381106f">d615bcd</a>)</li>
</ul>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v8.2.0...v8.2.1">8.2.1</a>
(2026-08-06)<!-- raw HTML omitted --></h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>build:</strong> make client chunkImportMap work with
<code>sharedPlugins: true</code> (<a
href="https://redirect.github.com/vitejs/vite/issues/23184">#23184</a>)
(<a
href="https://github.com/vitejs/vite/commit/15f03073c915d6ffb9a1fda447ef66b02bf5cde8">15f0307</a>)</li>
<li><strong>bundled-dev:</strong> inject client script tag before chunk
scripts (<a
href="https://redirect.github.com/vitejs/vite/issues/23161">#23161</a>)
(<a
href="https://github.com/vitejs/vite/commit/eac0cc84aa2472a85a19ee84561c1ba71e381a55">eac0cc8</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/vitejs/vite/commit/de1111ab0be00879b404e7ed3b2a80e264edddc1"><code>de1111a</code></a>
release: v8.2.2</li>
<li><a
href="https://github.com/vitejs/vite/commit/cb77e2a93bad2a8ece00b4aa0ef507c092582c45"><code>cb77e2a</code></a>
test(ssr): add destructing assignment case for moduleRunnerTransform (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23308">#23308</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/9db0b61d4c9c7caad7ea1d9670b637faf2bb6c93"><code>9db0b61</code></a>
fix(ssr): rewrite computed key of destructing parameter (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23307">#23307</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/8413052731836d4aaf3eb94a0f25788dd35d2888"><code>8413052</code></a>
fix: respect <code>resolve.preserveSymlinks</code> when resolving root
(fix <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23197">#23197</a>)
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23">#23</a>...</li>
<li><a
href="https://github.com/vitejs/vite/commit/05a003e6a17a84d75f907ea0f1598bc39b8dce6c"><code>05a003e</code></a>
fix(config): resolve sourcemap paths against sourcemap location (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23239">#23239</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/495d9ff5a7d843ca876a9e49799947a5deb704c7"><code>495d9ff</code></a>
feat(deps): widen <code>@vitejs/devtools</code> peer range to v0.5.0 (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23302">#23302</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/1d9fa392a43229241f80630236f8552ce8f7cd0f"><code>1d9fa39</code></a>
refactor: use JSON import attributes instead of readFileSync in
constants (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/2">#2</a>...</li>
<li><a
href="https://github.com/vitejs/vite/commit/2804636ff608d105928009d274ffba7cfbe55340"><code>2804636</code></a>
fix(css): don't pass empty targets to lightningcss (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23295">#23295</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/599b44b6600ec426e10cd556908d53b027b0c4fb"><code>599b44b</code></a>
test(module-runner): simplify server-hmr tests (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23300">#23300</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/4a261f242831bef92afd2f1aacfb81eab9dec371"><code>4a261f2</code></a>
test(module-runner): add TLA circular import case (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23299">#23299</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vitejs/vite/commits/v8.2.2/packages/vite">compare
view</a></li>
</ul>
</details>
<br />

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Priya Raman <priya@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 11:08:59 -07:00
DottaandPaperclip 4d2af732ae feat(runner): add native persistence contracts (#12169)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent runs need durable records so Paperclip can explain results and
final status changes.
> - The current heartbeat tables support direct adapters, but they do
not model native runner evidence.
> - The runner transport and server coordinator must share a strict
finalization contract before they write production data.
> - This pull request adds that contract and its additive database
boundary.
> - It does not select the Paperclip Runner or change any existing
adapter execution path.
> - The benefit is a reviewable persistence layer that preserves all
current behavior and supports later guarded integration.

## Linked Issues or Issue Description

Refs #11962

Refs #12129

## What Changed

- Add native run result, finalization, completion, assessment, status
decision, and status effect tables.
- Add inert native metadata to heartbeat runs and events. Keep `legacy`
as the default runtime mode.
- Bind each evidence relationship to one company, issue, run, contract,
result, assessment, and decision with composite constraints.
- Add a strict `paperclip.native_finalization.v1` shared type and
validator.
- Preserve database functions, triggers, and the unique indexes required
by foreign keys in JavaScript backups.
- Add migration, backup, mixed-owner denial, validator, and
direct-adapter compatibility tests.
- Document the new records and their ownership rules.

## Verification

- Run `pnpm -r typecheck`.
- Run `pnpm build`.
- Run `pnpm db:generate`. The schema output and migration safety checks
remain current.
- Run
`PAPERCLIP_PSQL_PATH=/Applications/Postgres.app/Contents/Versions/latest/bin/psql
pnpm exec vitest run
packages/shared/src/validators/native-finalization.test.ts
packages/db/src/client.test.ts packages/db/src/backup-lib.test.ts
server/src/__tests__/heartbeat-workspace-busy.test.ts
server/src/__tests__/heartbeat-comment-wake-batching.test.ts`. All 52
tests pass.
- The full local `pnpm test:run` run completed 4,688 tests. It found 30
existing macOS test-environment failures. A serial rerun with the
canonical `/private/tmp` path reduced those failures to six existing
listener-diagnostics and skill-browser cases. None of those suites use
files in this change.
- The full Linux GitHub Actions matrix passes. This includes all
general-server, serialized-server, workspace, browser, build, typecheck,
canary, and aggregate verification jobs.
- Greptile passes at 5/5. Contributor trust, Superagent, Socket, and
Snyk pass with no finding from this change.
- Storybook visual regression skips by path because this pull request
has no UI or Storybook change.
- Confirm that the diff contains 25 files. Confirm that it contains no
workflow or `pnpm-lock.yaml` changes.

## Risks

- The migration adds tables, columns, indexes, a function, a trigger,
and ownership constraints. It does not remove or rename existing data.
- Composite foreign keys reject mixed-company, mixed-issue, and
mixed-run evidence even when each ID exists.
- The status-version trigger runs only when an issue status changes.
Backup tests confirm that restore retains this trigger and its
dependencies.
- Native source identifiers are unique when present. Legacy event rows
remain unchanged.
- This change does not add a unique run sequence constraint. The later
native writer must allocate its sequence atomically before that
invariant can be safe.
- Existing adapters keep their current execution and finalization paths.
New heartbeat runs default to `legacy` mode.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- OpenAI Codex with GPT-5. The exact deployment ID and context-window
size are not exposed. The model used agentic reasoning, repository
tools, code execution, and test execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 13:08:39 -05:00
18ba239d85 build(deps): bump @pierre/diffs from 1.2.11 to 1.3.5 (#11875)
Bumps @pierre/diffs from 1.2.11 to 1.3.5.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Priya Raman <priya@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 10:38:42 -07:00
dependabot[bot] bef9288669 build(deps): bump @agentclientprotocol/claude-agent-acp from 0.69.0 to 0.70.0 (#11873)
Bumps
[@agentclientprotocol/claude-agent-acp](https://github.com/agentclientprotocol/claude-agent-acp)
from 0.69.0 to 0.70.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/agentclientprotocol/claude-agent-acp/releases">@​agentclientprotocol/claude-agent-acp's
releases</a>.</em></p>
<blockquote>
<h2>v0.70.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.69.0...v0.70.0">0.70.0</a>
(2026-08-17)</h2>
<h3>Features</h3>
<ul>
<li>switch providers for loaded Claude sessions (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/1002">#1002</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/50a95434e94318456f2d07c3d21aaf3595c3407d">50a9543</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/agentclientprotocol/claude-agent-acp/blob/main/CHANGELOG.md">@​agentclientprotocol/claude-agent-acp's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.69.0...v0.70.0">0.70.0</a>
(2026-08-17)</h2>
<h3>Features</h3>
<ul>
<li>switch providers for loaded Claude sessions (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/1002">#1002</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/50a95434e94318456f2d07c3d21aaf3595c3407d">50a9543</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/d0aafb1ca26427285ffaeac8d8a4452fff28e9c3"><code>d0aafb1</code></a>
chore(main): release 0.70.0 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/1010">#1010</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/50a95434e94318456f2d07c3d21aaf3595c3407d"><code>50a9543</code></a>
feat: switch providers for loaded Claude sessions (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/1002">#1002</a>)</li>
<li>See full diff in <a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.69.0...v0.70.0">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 10:10:42 -07:00
dependabot[bot] 1d3195bfcd build(deps-dev): bump esbuild from 0.28.1 to 0.28.2 (#11882)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.28.1 to 0.28.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/evanw/esbuild/releases">esbuild's
releases</a>.</em></p>
<blockquote>
<h2>v0.28.2</h2>
<ul>
<li>
<p>Fix tree shaking bug due to TypeScript import alias (<a
href="https://redirect.github.com/evanw/esbuild/issues/4507">#4507</a>)</p>
<p>This release fixes a bug that could cause esbuild to incorrectly
tree-shake imports that are used in a TypeScript type alias under
certain circumstances. Affected code uses a TypeScript-specific
<code>import</code> assignment and looks something like this:</p>
<pre lang="ts"><code>import Base from './dep.js';
import Alias = Base.SomeType;
</code></pre>
</li>
<li>
<p>Fix CSS minification bug involving <code>&amp;</code> (<a
href="https://redirect.github.com/evanw/esbuild/issues/4497">#4497</a>)</p>
<p>This release fixes a bug where esbuild's CSS minifier incorrectly
removed a <code>&amp;</code> when it was unsafe to do so. Here is an
example:</p>
<pre lang="css"><code>/* Original code */
.a .b {
  &amp; .b:not(&amp; .c) {
    color: red;
  }
}
<p>/* Old output (with --minify) */<br />
.a .b{.b:not(&amp; .c){color:red}}</p>
<p>/* New output (with --minify) */<br />
.a .b{&amp; .b:not(&amp; .c){color:red}}<br />
</code></pre></p>
<p>This should match <code>&lt;span class=&quot;a&quot;&gt;&lt;span
class=&quot;b&quot;&gt;&lt;span
class=&quot;b&quot;&gt;yes&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;</code>
but not <code>&lt;span class=&quot;a&quot;&gt;&lt;span
class=&quot;b&quot;&gt;no&lt;/span&gt;&lt;/span&gt;</code>. The old
output incorrectly matched both.</p>
</li>
<li>
<p>Avoid overwriting input files without <code>--allow-overwrite</code>
(<a
href="https://redirect.github.com/evanw/esbuild/issues/4484">#4484</a>)</p>
<p>For example: <code>esbuild input.js --outfile=input.js</code> tells
esbuild to overwrite <code>input.js</code> with the output of running
esbuild on it. This was supposed to already be prevented by default, but
it accidentally regressed in version 0.17.0 and apparently didn't have
any test coverage. The error message was being printed but the input
file was still being overwritten. Oops.</p>
<p>This release puts the original behavior back. With this release,
esbuild should now actually avoid overwriting input files unless
<code>--allow-overwrite</code> is explicitly present. This is done by
not writing out any files when a build error is encountered.</p>
</li>
<li>
<p>Fix incorrect code generated when using top-level await (<a
href="https://redirect.github.com/evanw/esbuild/issues/4498">#4498</a>)</p>
<p>Previously esbuild could generate code containing a syntax error in
complex scenarios involving top-level await used in a dependency cycle.
The problem was a missing <code>async</code> on one or more module
wrapper closures. With this release, esbuild now uses a fixed-point
iteration algorithm to correctly annotate all dependencies in the cycle
as needing an <code>async</code> module wrapper.</p>
</li>
<li>
<p>Fix a minification bug with lowered logical assignment operators (<a
href="https://redirect.github.com/evanw/esbuild/issues/4508">#4508</a>)</p>
<p>This release fixes a bug that could cause esbuild to generate
incorrect code for logical assignment operators when lowering them to an
older target environment. Specifically the lowering process requires
duplicating the left-hand side, but esbuild incorrectly failed to count
the duplicate as a new usage when the left-hand side is an identifier.
That then caused the minifier to believe that the left-hand side was
only used once and could attempt to incorrectly inline an initializer
into the first usage. This bug has now been fixed:</p>
<pre lang="js"><code>// Original code
function foo() {
  let x
  bar(x ||= {})
</code></pre>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/evanw/esbuild/blob/main/CHANGELOG.md">esbuild's
changelog</a>.</em></p>
<blockquote>
<h2>0.28.2</h2>
<ul>
<li>
<p>Fix tree shaking bug due to TypeScript import alias (<a
href="https://redirect.github.com/evanw/esbuild/issues/4507">#4507</a>)</p>
<p>This release fixes a bug that could cause esbuild to incorrectly
tree-shake imports that are used in a TypeScript type alias under
certain circumstances. Affected code uses a TypeScript-specific
<code>import</code> assignment and looks something like this:</p>
<pre lang="ts"><code>import Base from './dep.js';
import Alias = Base.SomeType;
</code></pre>
</li>
<li>
<p>Fix CSS minification bug involving <code>&amp;</code> (<a
href="https://redirect.github.com/evanw/esbuild/issues/4497">#4497</a>)</p>
<p>This release fixes a bug where esbuild's CSS minifier incorrectly
removed a <code>&amp;</code> when it was unsafe to do so. Here is an
example:</p>
<pre lang="css"><code>/* Original code */
.a .b {
  &amp; .b:not(&amp; .c) {
    color: red;
  }
}
<p>/* Old output (with --minify) */<br />
.a .b{.b:not(&amp; .c){color:red}}</p>
<p>/* New output (with --minify) */<br />
.a .b{&amp; .b:not(&amp; .c){color:red}}<br />
</code></pre></p>
<p>This should match <code>&lt;span class=&quot;a&quot;&gt;&lt;span
class=&quot;b&quot;&gt;&lt;span
class=&quot;b&quot;&gt;yes&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;</code>
but not <code>&lt;span class=&quot;a&quot;&gt;&lt;span
class=&quot;b&quot;&gt;no&lt;/span&gt;&lt;/span&gt;</code>. The old
output incorrectly matched both.</p>
</li>
<li>
<p>Avoid overwriting input files without <code>--allow-overwrite</code>
(<a
href="https://redirect.github.com/evanw/esbuild/issues/4484">#4484</a>)</p>
<p>For example: <code>esbuild input.js --outfile=input.js</code> tells
esbuild to overwrite <code>input.js</code> with the output of running
esbuild on it. This was supposed to already be prevented by default, but
it accidentally regressed in version 0.17.0 and apparently didn't have
any test coverage. The error message was being printed but the input
file was still being overwritten. Oops.</p>
<p>This release puts the original behavior back. With this release,
esbuild should now actually avoid overwriting input files unless
<code>--allow-overwrite</code> is explicitly present. This is done by
not writing out any files when a build error is encountered.</p>
</li>
<li>
<p>Fix incorrect code generated when using top-level await (<a
href="https://redirect.github.com/evanw/esbuild/issues/4498">#4498</a>)</p>
<p>Previously esbuild could generate code containing a syntax error in
complex scenarios involving top-level await used in a dependency cycle.
The problem was a missing <code>async</code> on one or more module
wrapper closures. With this release, esbuild now uses a fixed-point
iteration algorithm to correctly annotate all dependencies in the cycle
as needing an <code>async</code> module wrapper.</p>
</li>
<li>
<p>Fix a minification bug with lowered logical assignment operators (<a
href="https://redirect.github.com/evanw/esbuild/issues/4508">#4508</a>)</p>
<p>This release fixes a bug that could cause esbuild to generate
incorrect code for logical assignment operators when lowering them to an
older target environment. Specifically the lowering process requires
duplicating the left-hand side, but esbuild incorrectly failed to count
the duplicate as a new usage when the left-hand side is an identifier.
That then caused the minifier to believe that the left-hand side was
only used once and could attempt to incorrectly inline an initializer
into the first usage. This bug has now been fixed:</p>
<pre lang="js"><code>// Original code
function foo() {
  let x
</code></pre>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/evanw/esbuild/commit/609683d892977362a0f99026cb74b96263d728a9"><code>609683d</code></a>
publish 0.28.2 to npm</li>
<li><a
href="https://github.com/evanw/esbuild/commit/11b1fe48df6859393d9469f323b5ebd17baaf989"><code>11b1fe4</code></a>
add to release notes</li>
<li><a
href="https://github.com/evanw/esbuild/commit/ab50d91559a27e54cd0a27a403389130ea10d97d"><code>ab50d91</code></a>
css: fix green/blue channel swap in oklch gamut mapping (<a
href="https://redirect.github.com/evanw/esbuild/issues/4488">#4488</a>)</li>
<li><a
href="https://github.com/evanw/esbuild/commit/04627b6cf99b4a7491bebb0268173a7c77a85030"><code>04627b6</code></a>
fix <a
href="https://redirect.github.com/evanw/esbuild/issues/4498">#4498</a>:
<code>async</code> TLA checks need a worklist</li>
<li><a
href="https://github.com/evanw/esbuild/commit/5c15177a308c7224604058a769c4abf0a66b0a36"><code>5c15177</code></a>
disable <code>gopls</code> in the <code>go</code> folder</li>
<li><a
href="https://github.com/evanw/esbuild/commit/fc2ee9babc5a2e8ea7ec7c10dd5850b71f7cec7e"><code>fc2ee9b</code></a>
css: adjust parser to allow <code>--foo: {...}</code></li>
<li><a
href="https://github.com/evanw/esbuild/commit/209db54371e62ad1c50e12e56bb93c74c53b0408"><code>209db54</code></a>
release notes for css nesting bugfix</li>
<li><a
href="https://github.com/evanw/esbuild/commit/c625d31bf08a0647ec724bf76c7115f7aec55971"><code>c625d31</code></a>
fix <a
href="https://redirect.github.com/evanw/esbuild/issues/4497">#4497</a>:
preserve nested ampersands during minification (<a
href="https://redirect.github.com/evanw/esbuild/issues/4500">#4500</a>)</li>
<li><a
href="https://github.com/evanw/esbuild/commit/34474e278528a60f58c959c0f422d2bfa6f6886d"><code>34474e2</code></a>
better isolation of current part in js parser</li>
<li><a
href="https://github.com/evanw/esbuild/commit/07f6e8c50677e0b41e5ed726c08b0ea200b14e5b"><code>07f6e8c</code></a>
fix <a
href="https://redirect.github.com/evanw/esbuild/issues/4507">#4507</a>:
<code>import</code> assignment tree-shaking bug</li>
<li>Additional commits viewable in <a
href="https://github.com/evanw/esbuild/compare/v0.28.1...v0.28.2">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 09:42:26 -07:00
dependabot[bot] 4fb0978578 build(deps): bump googleapis from 174.0.1 to 176.0.0 (#11889)
Bumps
[googleapis](https://github.com/googleapis/google-api-nodejs-client)
from 174.0.1 to 176.0.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-nodejs-client/releases">googleapis's
releases</a>.</em></p>
<blockquote>
<h2>googleapis: v176.0.0</h2>
<h2><a
href="https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v175.0.0...googleapis-v176.0.0">176.0.0</a>
(2026-08-18)</h2>
<h3>⚠ BREAKING CHANGES</h3>
<ul>
<li><strong>securityposture:</strong> This release has breaking
changes.</li>
<li><strong>compute:</strong> This release has breaking changes.</li>
<li><strong>assuredworkloads:</strong> This release has breaking
changes.</li>
</ul>
<h3>Features</h3>
<ul>
<li><strong>assuredworkloads:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/4f787ecb10d2fcc0605045096ab472c8a3c848ce">4f787ec</a>)</li>
<li><strong>bigqueryconnection:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/19d67d7998bfd284eac66cbb2649df7479c3ecaa">19d67d7</a>)</li>
<li><strong>bigquery:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/5047629259ead4fb146cf95156bd8c28d5a0eb46">5047629</a>)</li>
<li><strong>ces:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/4d674e7e4efc6826072fe92f624378f9e03d0e34">4d674e7</a>)</li>
<li><strong>compute:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/88ee28ba7c20507de837c6335980f4aa239e5b4e">88ee28b</a>)</li>
<li><strong>contactcenterinsights:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/8987bcff71f26c6a511c92833049c0b7ad86469e">8987bcf</a>)</li>
<li><strong>dialogflow:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/cb090b72b2cae5d9b2053985b12237c51dd57ff7">cb090b7</a>)</li>
<li><strong>discoveryengine:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/c9a9b98cfcc0acedf8679fd3c791c74477c64654">c9a9b98</a>)</li>
<li><strong>gkehub:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/e7356ce9c0aa7240bd69688c544e4e3b3f81138a">e7356ce</a>)</li>
<li><strong>looker:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/ce6eba99279a866be197c7eaba9a8ea2e7f1eafa">ce6eba9</a>)</li>
<li><strong>metastore:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/266b861fd1a23ea8781f03ef252cf30dec2eb1f6">266b861</a>)</li>
<li><strong>networkservices:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/71b26e6c3734b967f4c228bb5cbc6658f0e8c42b">71b26e6</a>)</li>
<li><strong>playdeveloperreporting:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/b0d0c264919b34dc6c18179113ea4195375db638">b0d0c26</a>)</li>
<li>regenerate index files (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/0eb3a957cc14024a33be3910f970651aa7ba430b">0eb3a95</a>)</li>
<li><strong>secretmanager:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/333f48fa3afeb9daa9a506b77c7ddd9cdbed8fce">333f48f</a>)</li>
<li><strong>securityposture:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/868105393dbb9148f0cc827d5895c5effa51f372">8681053</a>)</li>
<li><strong>storage:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/9974109dd49839de0083621ed9ce133f6e1c37a8">9974109</a>)</li>
<li><strong>webcontentpublisher:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/7dc05fc5f268c8a7ca5d18429fde05b50a58b29c">7dc05fc</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>datafusion:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/2c691d571a3fdc8a93926fbfcbaa50273517756f">2c691d5</a>)</li>
<li><strong>docs:</strong> run JSDoc once per documentation build (<a
href="https://redirect.github.com/googleapis/google-api-nodejs-client/issues/3958">#3958</a>)
(<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/5aaf111af860b22a55ed64da824e0444b119c007">5aaf111</a>)</li>
<li><strong>redis:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/c639065e6ab3019192384f71d95bc447fb176329">c639065</a>)</li>
<li><strong>trafficdirector:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/3331b0cd347a11ea9d8c774f61ee67029399ba73">3331b0c</a>)</li>
<li><strong>workstations:</strong> update the API (<a
href="https://github.com/googleapis/google-api-nodejs-client/commit/ee9521ce5cdb69590827c30c59e58f0f047fb70d">ee9521c</a>)</li>
</ul>
<h2>googleapis: v175.0.0</h2>
<h2><a
href="https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v174.0.1...googleapis-v175.0.0">175.0.0</a>
(2026-08-14)</h2>
<h3>⚠ BREAKING CHANGES</h3>
<ul>
<li><strong>merchantapi:</strong> This release has breaking
changes.</li>
<li><strong>discoveryengine:</strong> This release has breaking
changes.</li>
</ul>
<h3>Features</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/googleapis/google-api-nodejs-client/commit/a454f9bda019c742b835e5fd5077294ce85c7875"><code>a454f9b</code></a>
chore: release main (<a
href="https://redirect.github.com/googleapis/google-api-nodejs-client/issues/3976">#3976</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-nodejs-client/commit/0eb3a957cc14024a33be3910f970651aa7ba430b"><code>0eb3a95</code></a>
feat: regenerate index files</li>
<li><a
href="https://github.com/googleapis/google-api-nodejs-client/commit/ee9521ce5cdb69590827c30c59e58f0f047fb70d"><code>ee9521c</code></a>
fix(workstations): update the API</li>
<li><a
href="https://github.com/googleapis/google-api-nodejs-client/commit/7dc05fc5f268c8a7ca5d18429fde05b50a58b29c"><code>7dc05fc</code></a>
feat(webcontentpublisher): update the API</li>
<li><a
href="https://github.com/googleapis/google-api-nodejs-client/commit/3331b0cd347a11ea9d8c774f61ee67029399ba73"><code>3331b0c</code></a>
fix(trafficdirector): update the API</li>
<li><a
href="https://github.com/googleapis/google-api-nodejs-client/commit/9974109dd49839de0083621ed9ce133f6e1c37a8"><code>9974109</code></a>
feat(storage): update the API</li>
<li><a
href="https://github.com/googleapis/google-api-nodejs-client/commit/868105393dbb9148f0cc827d5895c5effa51f372"><code>8681053</code></a>
feat(securityposture)!: update the API</li>
<li><a
href="https://github.com/googleapis/google-api-nodejs-client/commit/333f48fa3afeb9daa9a506b77c7ddd9cdbed8fce"><code>333f48f</code></a>
feat(secretmanager): update the API</li>
<li><a
href="https://github.com/googleapis/google-api-nodejs-client/commit/c639065e6ab3019192384f71d95bc447fb176329"><code>c639065</code></a>
fix(redis): update the API</li>
<li><a
href="https://github.com/googleapis/google-api-nodejs-client/commit/b0d0c264919b34dc6c18179113ea4195375db638"><code>b0d0c26</code></a>
feat(playdeveloperreporting): update the API</li>
<li>Additional commits viewable in <a
href="https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v174.0.1...googleapis-v176.0.0">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 09:23:50 -07:00
dependabot[bot] b67dced1bf build(deps): bump @agentclientprotocol/codex-acp from 1.2.0 to 1.6.2 (#11883)
Bumps
[@agentclientprotocol/codex-acp](https://github.com/agentclientprotocol/codex-acp)
from 1.2.0 to 1.6.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/agentclientprotocol/codex-acp/releases">@​agentclientprotocol/codex-acp's
releases</a>.</em></p>
<blockquote>
<h2>v1.6.2</h2>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.6.1...v1.6.2">1.6.2</a>
(2026-08-19)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>right-size the apt timeouts so a slow mirror still finishes (<a
href="https://github.com/agentclientprotocol/codex-acp/commit/86e0772204a07d6fc4a8853c523ceb5006431f88">86e0772</a>)</li>
</ul>
<h2>v1.6.1</h2>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.6.0...v1.6.1">1.6.1</a>
(2026-08-19)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>kill stalled apt from outside and serialize the unit suite (<a
href="https://github.com/agentclientprotocol/codex-acp/commit/51e011fef27b812b238bf29c2a815f8ad149fa87">51e011f</a>)</li>
</ul>
<h2>v1.6.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.5.1...v1.6.0">1.6.0</a>
(2026-08-19)</h2>
<h3>Features</h3>
<ul>
<li>harden release pipeline against hangs and e2e flakes (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/413">#413</a>)
(<a
href="https://github.com/agentclientprotocol/codex-acp/commit/39af81c29b79a85f878db096f9cb593b6d1c7429">39af81c</a>)</li>
</ul>
<h2>v1.5.1</h2>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.5.0...v1.5.1">1.5.1</a>
(2026-08-19)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>update codex to 0.148.0 (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/410">#410</a>)
(<a
href="https://github.com/agentclientprotocol/codex-acp/commit/3616954dc0e24af83b512adb618d7acbc5b98de5">3616954</a>)</li>
</ul>
<h2>v1.5.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.4.0...v1.5.0">1.5.0</a>
(2026-08-17)</h2>
<h3>Features</h3>
<ul>
<li>switch providers for loaded Codex sessions (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/404">#404</a>)
(<a
href="https://github.com/agentclientprotocol/codex-acp/commit/47b57da5641a04df9aeeedc254a3aef53a9497da">47b57da</a>)</li>
</ul>
<h2>v1.4.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.3.0...v1.4.0">1.4.0</a>
(2026-08-16)</h2>
<h3>Features</h3>
<ul>
<li>report changed files to AIR (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/403">#403</a>)
(<a
href="https://github.com/agentclientprotocol/codex-acp/commit/e305394d3f001f21e600597f41a3bee3d4530762">e305394</a>)</li>
</ul>
<h2>v1.3.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.2.0...v1.3.0">1.3.0</a>
(2026-08-14)</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/agentclientprotocol/codex-acp/blob/main/CHANGELOG.md">@​agentclientprotocol/codex-acp's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.6.1...v1.6.2">1.6.2</a>
(2026-08-19)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>right-size the apt timeouts so a slow mirror still finishes (<a
href="https://github.com/agentclientprotocol/codex-acp/commit/86e0772204a07d6fc4a8853c523ceb5006431f88">86e0772</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.6.0...v1.6.1">1.6.1</a>
(2026-08-19)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>kill stalled apt from outside and serialize the unit suite (<a
href="https://github.com/agentclientprotocol/codex-acp/commit/51e011fef27b812b238bf29c2a815f8ad149fa87">51e011f</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.5.1...v1.6.0">1.6.0</a>
(2026-08-19)</h2>
<h3>Features</h3>
<ul>
<li>harden release pipeline against hangs and e2e flakes (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/413">#413</a>)
(<a
href="https://github.com/agentclientprotocol/codex-acp/commit/39af81c29b79a85f878db096f9cb593b6d1c7429">39af81c</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.5.0...v1.5.1">1.5.1</a>
(2026-08-19)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>update codex to 0.148.0 (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/410">#410</a>)
(<a
href="https://github.com/agentclientprotocol/codex-acp/commit/3616954dc0e24af83b512adb618d7acbc5b98de5">3616954</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.4.0...v1.5.0">1.5.0</a>
(2026-08-17)</h2>
<h3>Features</h3>
<ul>
<li>switch providers for loaded Codex sessions (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/404">#404</a>)
(<a
href="https://github.com/agentclientprotocol/codex-acp/commit/47b57da5641a04df9aeeedc254a3aef53a9497da">47b57da</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.3.0...v1.4.0">1.4.0</a>
(2026-08-16)</h2>
<h3>Features</h3>
<ul>
<li>report changed files to AIR (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/403">#403</a>)
(<a
href="https://github.com/agentclientprotocol/codex-acp/commit/e305394d3f001f21e600597f41a3bee3d4530762">e305394</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.2.0...v1.3.0">1.3.0</a>
(2026-08-14)</h2>
<h3>Features</h3>
<ul>
<li>add versioned context compaction metadata (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/396">#396</a>)
(<a
href="https://github.com/agentclientprotocol/codex-acp/commit/c4a9311f60a638e3a4b03a475afff1d7678e594f">c4a9311</a>)</li>
<li>align typed session failures with AIR protocol (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/393">#393</a>)
(<a
href="https://github.com/agentclientprotocol/codex-acp/commit/e4fb92fffd8b8b9db9b40591ccbdb375c9f3f525">e4fb92f</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/agentclientprotocol/codex-acp/commit/9780d314d34616b476b1ae451ad31089b3dce49a"><code>9780d31</code></a>
chore(main): release 1.6.2 (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/417">#417</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/codex-acp/commit/86e0772204a07d6fc4a8853c523ceb5006431f88"><code>86e0772</code></a>
fix: right-size the apt timeouts so a slow mirror still finishes</li>
<li><a
href="https://github.com/agentclientprotocol/codex-acp/commit/096f5a88501db50c4420726e84c39f60f08c457f"><code>096f5a8</code></a>
chore(main): release 1.6.1 (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/416">#416</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/codex-acp/commit/51e011fef27b812b238bf29c2a815f8ad149fa87"><code>51e011f</code></a>
fix: kill stalled apt from outside and serialize the unit suite</li>
<li><a
href="https://github.com/agentclientprotocol/codex-acp/commit/50bd611451c02868cc2b50bd6a7fc61ae5ef9b41"><code>50bd611</code></a>
chore(main): release 1.6.0 (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/414">#414</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/codex-acp/commit/39af81c29b79a85f878db096f9cb593b6d1c7429"><code>39af81c</code></a>
feat: harden release pipeline against hangs and e2e flakes (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/413">#413</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/codex-acp/commit/ad658e6ec64e8b70c455b10457ccc34f77173c9b"><code>ad658e6</code></a>
chore(main): release 1.5.1 (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/412">#412</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/codex-acp/commit/3616954dc0e24af83b512adb618d7acbc5b98de5"><code>3616954</code></a>
fix: update codex to 0.148.0 (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/410">#410</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/codex-acp/commit/3d5682722545a4b2d7cfcf8bdabbbfadbdaa37ea"><code>3d56827</code></a>
chore(main): release 1.5.0 (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/409">#409</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/codex-acp/commit/47b57da5641a04df9aeeedc254a3aef53a9497da"><code>47b57da</code></a>
feat: switch providers for loaded Codex sessions (<a
href="https://redirect.github.com/agentclientprotocol/codex-acp/issues/404">#404</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/agentclientprotocol/codex-acp/compare/v1.2.0...v1.6.2">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 09:23:47 -07:00
dependabot[bot] b858fc7248 build(deps): bump @codemirror/view from 6.43.8 to 6.43.9 (#11879)
Bumps [@codemirror/view](https://github.com/codemirror/view) from 6.43.8
to 6.43.9.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/codemirror/view/commits">compare view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 09:23:31 -07:00
Nicky LeachandPaperclip 2862e18484 refactor(adapter-utils): remove the retired duplex_v1 sandbox bridge transport (#12171)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The adapter utilities provide sandbox transport paths for agent
execution
> - The retired `duplex_v1` path remains in host, gateway, and test code
after `http2_v1` replaced it
> - Retired transport code adds maintenance cost and leaves an unsafe
fallback for unknown gateway modes
> - This pull request removes the retired path, moves shared `http2_v1`
contracts to a leaf module, and closes mode dispatch to a fixed
allowlist
> - The benefit is a smaller transport surface and explicit failure for
unsupported modes

## Linked Issues or Issue Description

Refs #12120

The `http2_v1` transport replaced `duplex_v1`, but the retired broker,
gateway, constants, and tests remain in the adapter utilities. An
unknown bridge mode can also fall through to the queue gateway when a
queue directory exists. This change removes the retired code and rejects
unsupported modes before gateway selection.

## What Changed

- Delete the host `duplex_v1` broker and its transport-only tests.
- Delete the in-sandbox duplex gateway and retired mode constants.
- Move shared `http2_v1` symbols into `bridge-transport-contract.ts`.
- Update the remaining importers and repair their focused tests.
- Validate bridge modes against `http2_v1` and `queue_v1` before queue
lookup.
- Keep `queue_v1`, `duplex-frame-codec.ts`, and duplex telemetry
dimensions unchanged.

## Verification

- [x] `npx tsc --noEmit -p packages/adapter-utils` passes.
- [x] `npx vitest run packages/adapter-utils/src` passes: 48 files and
968 tests pass, with 4 pre-existing platform skips.
- [x] Full CI is green on this pull request.
- [x] Greptile review is complete and every finding is resolved.

## Risks

The change removes an internal transport that no host path selects. The
main risk is an overlooked import or test dependency. Targeted typecheck
and tests cover the adapter utility package. Full CI must confirm
workspace-wide compatibility.

## Model Used

Anthropic Claude Sonnet 5 assisted with the implementation, as recorded
in the commit. The commit does not record a context-window size or
reasoning mode.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 08:47:43 -07:00
Nicky LeachandPaperclip 02a984068c refactor(adapter-utils): clean up the HTTP/2 bridge request-body bounds (#12166)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent adapters use the HTTP/2 bridge to carry requests and responses
> - The bridge has an idle bound and a total-lifetime ceiling for
request bodies
> - The old renewable lifetime bound re-armed with each DATA chunk and
could not act before the idle bound
> - The code also repeated the same bounds and rationale in several
places
> - This pull request removes the unreachable renewable bound, keeps the
one-shot ceiling, and simplifies the shared bounds object
> - The benefit is clearer protection logic with the same default
request-body behavior

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The HTTP/2 bridge request-body reader uses several repeated bound
parameters and comments. One renewable lifetime bound cannot act before
the idle bound under the shipped defaults.

**Subsystem affected**

`packages/adapter-utils/` — HTTP/2 bridge adapter utilities.

**Current behavior**

The idle bound and renewable lifetime bound both re-arm after each DATA
chunk. The renewable bound therefore does not act on its own. The
total-lifetime ceiling also shares timer setup with the renewable bound.

**Proposed behavior**

Remove the renewable lifetime bound. Keep the total-lifetime ceiling as
an independent one-shot timer. Pass one bounds object to the bridge call
sites and keep tests for the idle bound and total-lifetime ceiling.

**Reason and benefit**

The change removes unreachable logic and repeated rationale. It keeps
the independent total-lifetime protection and makes the bound behavior
easier to review.

**Breaking changes**

The change removes two public constant and option names that
repository-wide search found unused outside this implementation. The
shipped default behavior does not change.

## What Changed

- Remove the renewable request-body lifetime bound and its public names.
- Keep the total-lifetime ceiling as a one-shot timer that starts when
the body read starts.
- Replace repeated bound parameters with one `Http2BridgeBodyBounds`
object.
- De-duplicate bound rationale comments.
- Add shared test helpers and update tests for the idle bound and
total-lifetime ceiling.

## Verification

- Run `npx tsc --noEmit -p packages/adapter-utils`.
- Run `npx vitest run
packages/adapter-utils/src/http2-bridge-server.test.ts`.
- Wait for the pull request CI checks.
- Request the Greptile review and confirm a 5/5 verdict with no open
findings.

## Risks

The main risk is an incorrect timer lifetime after the renewable timer
removal. The one-shot ceiling remains independent, and the updated tests
cover its expiry and cleanup paths. The change does not alter the
shipped default bounds.

## Model Used

OpenAI Codex based on GPT-5. Exact runtime model version is GPT-5. The
work used tool calls and code execution for repository inspection and
GitHub operations.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 08:09:52 -07:00
Nicky LeachandPaperclip 445547c989 feat(duplex): run the Daytona sandbox callback bridge over Node HTTP/2 (#12120)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Sandbox providers carry agent work through controlled execution
channels
> - The Daytona callback bridge uses a bespoke line-framed protocol over
its duplex channel
> - The bespoke protocol adds framing work and does not use the Node
transport that already supports multiplexed streams
> - This pull request carries raw bytes across the channel, adds a Node
HTTP/2 bridge, and selects it for Daytona
> - The benefit is one authenticated, multiplexed callback session with
queue_v1 as the bounded fallback

## Linked Issues or Issue Description

**Subsystem affected**

The packages/plugins Daytona provider and the shared duplex execution
path.

**Problem or motivation**

The Daytona callback bridge uses a bespoke line-framed protocol over the
provider duplex channel. This adds protocol work and limits stream
handling.

**Proposed solution**

Carry raw bytes through the cross-layer channel. Add an authenticated
Node HTTP/2 host server and sandbox client gateway. Select http2_v1 for
Daytona and retain queue_v1 as the fallback.

**Alternatives considered**

Keep the current duplex_v1 protocol. This keeps the bespoke framing path
and does not provide one HTTP/2 session for callback streams.

**Roadmap alignment**

ROADMAP.md lists Daytona under cloud and sandbox agents. This change
improves the shipped Daytona provider path.

**Additional context**

The branch adds no dependency. Node 24 provides the http2 module. The
host token check and canonical path parser remain the single dispatch
path.

## What Changed

- Carry raw Uint8Array chunks through the adapter, plugin, worker,
runtime, and Daytona layers.
- Encode bytes as base64 only across the JSON-RPC hop, because JSON has
no binary type.
- Add the bounded host HTTP/2 server and the in-sandbox HTTP/2 client
gateway.
- Authenticate every stream with the per-run bridge token before route
work.
- Parse the path once and reuse the canonical result for route and
forwarding work.
- Select http2_v1 for Daytona and fall back once to queue_v1 when the
client preface is absent.
- Add transport, session, stream, and fallback telemetry.
- Mark HTTP/2 as the preferred transport and queue_v1 as the
soft-deprecated fallback.

## Verification

- `npx vitest run packages/adapter-utils/src` — 990 passed and 4
skipped.
- `npx vitest run
server/src/__tests__/plugin-worker-manager-duplex.test.ts` — 32 passed.
- `npx vitest run --config
packages/plugins/sandbox-providers/daytona/vitest.config.ts` — 220
passed and 6 skipped.
- `npx tsc --noEmit` in `packages/adapter-utils`, `packages/shared`,
`packages/plugins/sdk`, and `server` — clean.
- No `package.json` or `pnpm-lock.yaml` file changed.
- The live Daytona test skips when `DAYTONA_API_KEY` is absent.
- The root `npx tsc --noEmit` command has a pre-existing missing
`packages/adapters/droid-local` reference on this branch and on
`master`.

## Risks

- The transport change affects several duplex layers and could expose
byte-boundary errors.
- A missing HTTP/2 client preface falls back once to queue_v1 and
records `preface_missing`.
- The host token check and canonical path parser must remain on the
shared dispatch path.
- The live Daytona test needs `DAYTONA_API_KEY` and does not run in this
agent sandbox.

## Model Used

OpenAI GPT-5, tool-enabled coding agent with repository inspection,
GitHub CLI, and shell execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 07:35:39 -07:00
DottaandPaperclip ffff1fe6e3 feat(runner): define package API and verification boundary (#12129)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The runner package now has protocol, transport, provider, catalog,
and authorization foundations.
> - Its first upstream package boundary should expose only the
implemented runtime and test-helper surfaces.
> - Rust correctness belongs in the repository existing build
verification, without introducing a parallel release process.
> - Direct package creation must build the files declared by the package
manifest.
> - This pull request defines the minimal package API and verifies the
optimized runner binaries in the existing PR and release Build jobs.
> - The benefit is a production-ready runner package boundary with
minimal build-process change.

## Linked Issues or Issue Description

Refs #11962

This pull request replaces one bounded part of the archived large runner
change. It follows the package-local authorization change in #12126.

## What Changed

- Export only `@paperclipai/paperclip-runner` and
`@paperclipai/paperclip-runner/testing`.
- Keep Node-only fixture loading and semantic conformance helpers out of
the runtime root.
- Add a provider-neutral semantic conformance kit with stable JSON
comparison and fail-closed input checks.
- Keep deferred SDK, eval, browser, React, lab, and command surfaces
private.
- Pin the runner Rust toolchain to 1.97.1 with the minimal profile and
`rustfmt`.
- Run the Rust workspace tests in release mode.
- Launch the optimized `paperclip-runnerd` and fake-harness binaries in
process-level integration coverage.
- Add one `pnpm --filter @paperclipai/paperclip-runner check:all` step
to each existing PR and release Build job.
- Make the existing server `prepack` lifecycle run its existing build
after it prepares UI assets.
- Document that no production adapter starts runnerd yet.

This revision adds no standalone GitHub Actions job. It adds no server
runner dependency or runner vendoring. It adds no Docker bootstrap or
clean-consumer harness. It does not change `pnpm-lock.yaml`.

## Verification

- `pnpm --filter @paperclipai/paperclip-runner check:all`
  - 66 TypeScript tests
  - 8 protocol contract tests
  - 56 Rust unit and integration tests
- Release-mode integration coverage launches the optimized runnerd and
fake-harness binaries.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/server-package-build-script.test.ts` (2 tests)
- Clean `pnpm pack` from `server/` rebuilt the server and produced both
`package/dist/index.js` and `package/dist/index.d.ts`.
- `node --test scripts/__tests__/release-verify-workflow.test.mjs` (8
tests)
- `pnpm -r typecheck`
- `pnpm build`
- `pnpm check:token-gates`
- `git diff --check`
- No `pnpm-lock.yaml` diff.
- The diff changes 12 files.

## Risks

The runner adds Rust work to the existing Build jobs. These jobs can
take longer on a cold cache. The pinned toolchain makes contributor and
CI behavior reproducible. Cargo tests use `--release` to verify
optimized executables. The server prepack lifecycle now performs the
build that its published entry points require. This can make direct
server packing slower. This pull request does not wire runnerd into the
server. It does not select runnerd for any adapter. Existing application
execution and finalization paths remain unchanged.

## Model Used

OpenAI Codex with GPT-5. Agentic coding mode used repository tools, code
execution, and automated tests.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 09:31:48 -05:00
Nicky LeachandPaperclip b6854e61c7 refactor(adapter-utils): rename EffectiveSandboxCapabilities to EffectiveExecutionCapabilities (#12119)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The adapter utilities package defines shared types for agent
execution targets
> - The type name EffectiveSandboxCapabilities describes only one
transport
> - All execution target drivers return the same resolved capability
snapshot
> - This pull request gives the snapshot a general name and keeps the
old type as a deprecated alias
> - The benefit is clearer public vocabulary with source compatibility
for current consumers

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The exported capability snapshot type uses the name
`EffectiveSandboxCapabilities`, although local, SSH, sandbox, and plugin
drivers return it.

**Subsystem affected**

The change affects `packages/adapter-utils` and its server consumers.

**Current behavior**

The public type name points to the sandbox transport. The private parser
also uses the sandbox-only name.

**Proposed behavior**

Use `EffectiveExecutionCapabilities` for the public type and
`parseEffectiveExecutionCapabilities` for the private parser. Keep a
deprecated alias for the old public type.

**Reason and benefit**

The new name matches the established execution-target vocabulary. The
alias keeps existing type imports working during the migration.

**Breaking changes**

None. The runtime field, capability flags, parsed shape, and package
versions do not change.

**Additional context**

GitHub search found no duplicate or related open issue or pull request.

## What Changed

- Rename the exported interface to `EffectiveExecutionCapabilities`.
- Keep `EffectiveSandboxCapabilities` as a deprecated type alias.
- Rename the private parser and update its call site and references.
- Add a type-level test for the deprecated alias.

## Verification

- `npx tsc --noEmit -p packages/adapter-utils`
- `npx vitest run
packages/adapter-utils/src/execution-target-sandbox.test.ts`
- `npx vitest run
server/src/__tests__/environment-execution-target-capabilities.test.ts
server/src/__tests__/environment-execution-target-duplex.test.ts`
- The local checks passed with 133 adapter-utils tests and 31 server
tests.
- Reviewers can confirm that the runtime field and capability flags stay
unchanged.

## Risks

Low risk. The alias protects existing type imports. The change does not
alter runtime behavior or serialized data.

## Model Used

OpenAI Codex, GPT-5, tool use and code execution. 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>
2026-08-25 07:13:55 -07:00