Commit Graph

271 Commits

Author SHA1 Message Date
Dotta df8ffa3934 feat(security): enforce scoped board API keys
Add the revision-4 scope contract, safe migration, fresh owner authority checks, default-deny route inventory, audit controls, and focused regression coverage.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-13 05:02:11 +00:00
Dotta 422287eecd
fix: preserve runner recovery, warm sessions, and task outcomes (#13338)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The native runner connects task messages, provider execution, and
task outcomes.
> - First-time user tests exposed gaps in recovery, completion
permissions, message delivery, and Stop behavior.
> - These gaps left usable output hidden, completed work waiting for
bookkeeping, or safe work unable to continue.
> - This pull request fixes the shared lifecycle and receipt paths while
preserving process ownership and action checks.
> - Users can continue work with accurate task state and durable
messages.

## Linked Issues or Issue Description

**What happened?**

A stopped local Codex execution could remain blocked even after its
processes had stopped and its complete transcript proved that no
external action needed replay. Claude under Conservative permissions
could fail to call task completion tools. Recovery could reuse an
assistant item ID and overwrite prior output. A delivered comment could
remain marked uncertain after navigation. Stop could look like Pause or
a new recovery incident. Workspace contention could look like
cancellation. A direct reply reopening Done could enter a clarification
loop.

**Expected behavior**

Recover automatically only with verified termination and complete action
receipts. Preserve answers and messages. Keep task completion available
under Conservative permissions without broad tool access. Show crashes
as Blocked, actual human decisions as In Review, and ordinary workspace
contention as waiting. Stop the current response and allow a new
direction.

**Steps to reproduce**

1. Create ordinary response tasks with local Codex and Claude Code, then
send follow-up messages through the task composer.
2. Interrupt a disposable local Codex runner during text-only work.
Verify automatic continuation and retained output.
3. Stop a response, send a new request, answer a clarification, and
reopen completed work with another message.
4. Navigate or reload while a comment submission is pending. Confirm the
exact persisted request receipt settles it without removing newer draft
text.
5. Run two tasks in a shared Daytona workspace. Confirm waiting does not
appear as failure.

**Paperclip version or commit**

Initial acceptance baseline: `c9021c6721f91e2c74bd9fee9d3fd41c999d17b7`.
Current integration base: `6cef9743c`. Both operator-interruption and
workspace-waiting guards are preserved; native restart and legacy
permission rules remain documented.

**Deployment mode**

An isolated source-built test-drive instance, with real local Codex and
Claude Code providers and disposable Daytona environments.

Related work: #13314, #13316, #13327, #13344, #13239, #13254, #13163.
This PR addresses additional failures from ordinary task journeys,
including controller restart handoff and repeated warm sandbox setup.
Historical task status reconciliation is excluded.

## What Changed

- Persist runner ownership immediately at spawn and resume an explicitly
adopted runner even when the controller crashed before the first driver
checkpoint. Detach the controller safely across graceful restarts,
including session startup. Prevent an old finalizer from suspending or
signaling an adopted runner. Checkpoint idle warm sessions before
shutdown. Preserve the same run and queued follow-up messages.
- Scope saved legacy queue successor checks to the queue owner while
preserving ordinary task locks, operator identity, assignment gates, and
exactly-once delivery.
- Preserve managed Codex credential files when an old session is
detached for restart; normal owned cleanup still copies refreshed auth
back and removes the scoped copy.
- Reuse the bound warm shared sandbox and fully verify an existing
staged provider pack before using it. This avoids repeated uploads when
the pack is already valid.
- Add a narrow local Codex replacement path with stopped-process proof,
a closed transcript inventory, exact completion receipts, and
fresh-session lineage. Preserve no-replay holds when evidence is
incomplete. Recovery may clear only the same run's recorded Blocked
status version; manual re-blocking and dependency changes invalidate
that receipt, while queued comments do not. Later blocks stop scheduled,
queued, and final dispatch; queued/final checks re-read dependencies
even when the task status stays In Progress.
- Permit only task delivery and human-input tools through the isolated
Claude runner's exact task bridge.
- Scope assistant item identity to the provider turn and ignore only
authority-free Codex skill-change notifications during startup.
- Reconcile composer submissions by client request ID across response
loss, navigation, and reload. Retain text typed during delivery.
- Keep acknowledged run-only Stop neutral and show workspace contention
as waiting. Project exhausted native failures as Blocked.
- Restore the guarded task-page retry action for failed legacy runs,
including the server-supported explicit new-attempt path for stopped
conversation adapters. Preserve native/process recovery holds and avoid
promising Retry while a decision or execution gate hides it.
- Refresh delivered artifacts and handle direct user replies that reopen
completed work without a clarification loop.
- Check the embedded PostgreSQL PID, data directory, and actual port
before connecting or migrating.
- Document accepted behavior and add focused regressions at lifecycle,
route, transcript, and UI boundaries.

## Verification

- Final head `fece606ac2` passes the complete GitHub CI matrix: **34
green checks, two expected Storybook skips, no failures or pending
checks**, including `ci / verify`, `ci / e2e`, full runner verification,
typecheck, build, every server/workspace shard, and all browser shards.
[CI
run](https://github.com/paperclipai/paperclip/actions/runs/34727183287).
Greptile is **5/5 with no open findings**. The final two commits only
refine test fixtures; both affected suites pass 24/24 locally and in CI,
with server typecheck green.
- Complete local Vitest coverage uses the canonical groups/shards: all
635 general server suites, all 145 serialized suites, and all workspace
packages. The aggregate began on `0a8001c18` while the final queue fix
arrived: 23,903 passed, five failed, 87 skipped. The five
port/socket/timing failures passed unchanged in follow-ups (60 tests in
the exposure/file suites and 412 tests covering the serialized failures
and unrun tails). The final queue/operator-identity suites separately
passed 52/52. This is aggregate coverage plus explicit reruns, not a
pristine single-command final-head run.
- After integration with current master,
queue/operator-identity/continuation suites passed 162/162 and affected
UI suites passed 140/140. ACP Stop/continuation and legacy
task/Inbox/message browser suites passed 9/9, including both task
recovery Retry and thread Try again, automatic saved-message delivery,
exactly one new run, Done, and retained output after reload. The default
process Stop/Pause/Resume browser case passed (the native-provider case
is opt-in and skipped by default). The complete Board attachment/receipt
browser suite passed 11/11 on a disposable instance, covering both
composers, exact receipts after lost responses, no replay, bound
attachments, and newer drafts after reload.
- Blocking-intent regressions cover pre-existing Blocked, a mismatched
run/cause, an explicit manual re-block, changed dependencies, a queued
comment after failure, and a block arriving between scheduling and
provider dispatch. The negative cases reproduced before the fix. All 478
affected executor/recovery/dispatch tests passed; both database suites
ran separately after availability-probe skips in the first combined
command. The final late-dependency check passed all 143 affected
recovery/dispatch tests (zero skips) after two new negative cases
reproduced the bug.
- Focused runtime regressions cover awaited runner ownership
publication, authenticated adoption before the first checkpoint,
old-finalizer detachment, idle and busy warm-session shutdown, rejected
checkpoint propagation, provider-pack verification, and managed-Codex
credential preservation. Four managed credential detachment cases
reproduced the bug before the fix; normal owned cleanup still succeeds
exactly once.
- Live local Claude: SIGKILL 2.6 seconds into startup recovered the same
run automatically in 53 seconds, then a normal follow-up completed in 24
seconds. SIGTERM 2.5 seconds into startup preserved the same run (54
seconds) and its queued follow-up (21 seconds). Answers remained visible
and the task reached Done.
- Live Claude Daytona: a warm follow-up retained its sandbox and fell
from 121 seconds to 44 seconds. A separate cold turn took 127 seconds;
after controller shutdown and checkpointing, its follow-up completed in
33 seconds with the same sandbox, workspace, native session, and runner.
Both answers remained visible and the task was Done.
- Other live journeys covered task completion and follow-up with local
and Daytona Codex, local Codex crash recovery, Stop then new direction,
clarification response, live artifact refresh, and shared-workspace
waiting.
- Validation limits: the opt-in native composer Stop/Pause→subtree
Resume fixture exposes terminal/result ordering and subtree-cancellation
attribution bugs that can leave a child task blocked; that new finding
is assigned to a separate follow-up and is not claimed fixed here.
Default CI skips this optional native-provider fixture. Managed-Codex
credential handoff and the queue-agent integration use automated
regression evidence. Cold custom provider-pack uploads still add startup
latency.

## Risks

- Automatic replacement remains deliberately narrow: local Codex,
verified stopped identities, unchanged retained state, and a complete
text/completion-only turn. Unknown actions, partial history, or changed
ownership remain blocked.
- Claude completion permission handling changes an upstream package
patch. The exact isolated task bridge must remain pinned; unrelated
tools keep their existing permissions.
- New task failure projection changes user-visible status. No historical
status backfill or database migration is included.
- This is a broad lifecycle fix across server and UI. Live proof covers
graceful local Claude restart during startup and idle Claude Daytona
session recovery across controller shutdown. Live abrupt SIGKILL during
local Claude startup also recovered the same run. Unknown ownership or
missing action evidence still blocks reuse. Cold custom provider-pack
uploads still add startup latency; this change avoids unnecessary repeat
uploads.

## Model Used

OpenAI GPT-6 (Codex), with reasoning, code execution, browser
automation, and tool use. The exact hosted model ID and context window
are not exposed in this task.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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-09-12 19:41:15 -05:00
Dotta ab15aff390
feat: add experimental persistent agent chat (#13284)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Conversations must use the same tasks, controls, and execution
history.
> - Users need an ongoing chat with an agent without managing task
properties.
> - Agents should clarify and plan work, then hand execution to assigned
project tasks.
> - This pull request combines the reviewed Agent Chat stack for one
squash merge.
> - The benefit is persistent conversation with normal task governance
and shared UI.

## Linked Issues or Issue Description

**Subsystem affected**

Task lifecycle, agent runtime tools, shared task UI, and browser/paid
runner tests.

**Problem or motivation**

Users need one persistent conversation with each agent. A separate chat
store or renderer would duplicate task behavior and bypass existing
controls.

**Proposed solution**

Use a task-backed chat per company, user, and agent. Reuse the task
composer and transcript. Clarify and plan in chat, then create assigned
project tasks with the relevant plan. Keep Agent Chat behind its own
disabled-by-default experimental setting.

**Roadmap alignment**

This implements the task-backed direction in [CEO
Chat](https://github.com/paperclipai/paperclip/blob/master/ROADMAP.md#-ceo-chat).
Related proposals: #2504 and #9693. Related request: #7981. The
maintainer requested one squash merge of the complete stack.

Consolidates the reviewed runtime
[#13281](https://github.com/paperclipai/paperclip/pull/13281), backend
[#13282](https://github.com/paperclipai/paperclip/pull/13282), and UI
[#13283](https://github.com/paperclipai/paperclip/pull/13283) layers
with this PR's E2E coverage. All four layers passed CI and received
Greptile 5/5 before consolidation. This PR targets master and includes
the complete feature.

## What Changed

- Add personal canonical chat tasks with ordinary company visibility,
immutable identity, idempotent first sends, and an idle waiting state.
- Process `/new` in queue order. Preserve history, release a chat pause,
and fence old provider context and delayed writes.
- Keep chat lifecycle rules across recovery, finalization, assignment,
task lists, and rollups.
- Support research and plan revision in chat. Hand plans to ordinary
assigned project tasks before execution starts. Reject new chat
subtasks.
- Add repository-aware project creation and discovery tools, including
multiple repository IDs and GitHub URLs, authorization, idempotency, and
durable project-created cards.
- Reuse task UI components for chat, with starred/recent agent
navigation and a separate `enableAgentChat` experimental flag.
- Add deterministic browser tests and 24 paid chat cells across four
Codex/Claude profiles, with validated reports and screenshots.
- Integrate current master recovery, controller lease, queued-message,
and task UI changes. Gate chat interruption and deferred promotion on
ownership/feature policy. Guarantee lease renewal and active controls
are stopped even if teardown fails.
- Preserve master's migration 0273 and generate chat migration 0274 with
idempotent replay for development databases.

## Verification

- Prior exact heads of all four PRs passed Linux CI, including build,
typecheck, general/serialized tests, and browser E2E. Each had Greptile
5/5 and no unresolved findings.
- Integrated local verification passed: full repository typecheck and
production build, Storybook build, token gates, 340 focused UI tests,
all 20 deterministic chat browser tests, two migration replay tests, 88
focused chat/queue/native/controller tests, and provider/session
regressions including real lease expiry. These include the three
lifecycle regressions for the final admission/teardown fixes; server
typecheck also passes. Current head
`1268eda16cc2af892055917e7292f068820be135` has Greptile 5/5 with no
unresolved findings and passing security scans. All final-head CI gates
passed: build, full Runner verification, typecheck/release registry,
canary, all general/serialized test shards, and all browser E2E shards
([CI
run](https://github.com/paperclipai/paperclip/actions/runs/34696739927)).
Local PostgreSQL startup contention required serialized retries; skipped
fixtures do not count as passing coverage.
- The earlier paid campaign passed all 24 chat cells and retained 32
screenshots:
[report](https://d1p6rlowie26tp.cloudfront.net/runner-e2e/campaigns/gha-34648511170-1/index.html?report=agent-chat#suite-agent-chat).
It tested `abacbdfd2f660709ec37312cdb758284c8399d04`; it is prior
evidence, not a paid run of this integrated head.
- Manual check: enable Agent Chat in Experimental settings, open an
agent, clarify and revise a plan, then hand off to an assigned project
task. Stop a reply, send `/new`, and verify fresh context with retained
history. Disable the setting and verify agent shortcuts/new chat turns
are blocked.

## Risks

- Queue/session integration can affect retries and delayed writes. Tests
cover ownership, cancellation, reset boundaries, idle recovery, and
ordinary task behavior.
- Migration 0274 adds conversation fields and constraints. Replay is
idempotent and preserves existing development chat history.
- This combines the previously reviewed stack at the maintainer's
request. Agent Chat remains off by default and is separate from
Conference Room.

## Model Used

OpenAI Codex, GPT-6 Astra (`gpt-6-astra`), with reasoning, code
execution, browser tools, and parallel review. The exact context-window
size is not exposed in this session. Codex and Claude also ran as test
subjects in the linked paid campaign.

## 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-09-12 08:56:04 -05:00
Dotta 2083bf6f9a
feat(connections): add AgentMail inboxes and email tasks (#13256)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Connections give agents controlled access to external services.
> - Experimental channels already map conversations to tasks and durable
work queues.
> - Email needs inbox ownership, recipient envelopes, delivery records,
and explicit sends.
> - This pull request adds AgentMail to that infrastructure and keeps
the provider key in the server vault.
> - Agents can receive and send email from local or sandbox execution
while the board follows each conversation in its task.

## Linked Issues or Issue Description

**Problem or motivation**

Agents need dedicated email addresses. Incoming email should become
assigned work. Internal task comments and progress must never become
outgoing email by accident.

**Proposed solution**

Add experimental AgentMail connections, an inbox assignment wizard,
durable email intake and publication, task email cards, and
authenticated API, CLI, and native runtime actions. Agents use Paperclip
credentials to request sends. Paperclip owns the provider key and
enforces access and task authority.

**Alternatives considered**

A general mailbox MCP connector does not provide durable task binding or
publication boundaries. A separate mailbox application duplicates task
collaboration. The board instead directs the agent through the normal
task conversation.

**Roadmap alignment**

This extends the existing experimental connections and task
infrastructure. Product scope and interaction design were reviewed with
the maintainer. Related connection authority work: #11831 and #11818.
The duplicate search found no competing task-based AgentMail
integration.

## What Changed

- Add AgentMail catalog data, shared contracts, company-scoped email
records, and an additive migration.
- Add vaulted setup, inbox assignment, access grants, trust guidance,
and provider-side allowlist guidance.
- Support WebSocket and signed-webhook intake through a shared durable
pipeline, deduplication, catch-up, and task wakeups.
- Queue explicit new conversations and replies with immutable send
intents, idempotency, delivery state, and uncertain-send resolution.
- Show inbound and outbound email cards in normal task conversations.
Keep internal messages internal.
- Add task-scoped CLI actions and the sandbox callback routes required
for Daytona execution.
- Provide a dedicated AgentMail skill automatically only to agents with
active authorized inbox assignments. Keep email instructions out of the
universal Paperclip skill.
- Advertise connector-owned `agentmail_inboxes`,
`agentmail_read_thread`, `agentmail_send`, and `agentmail_delivery`
tools only in eligible native sessions. Recheck live authority on
execution.
- Isolate Codex CLI connector skills by agent and skill revision.
Deliver the assigned skill in the run prompt for adapters that use
shared skill directories, including resumed turns. Keep automatic skills
out of manual persistent sync. Show them as read-only and document the
pattern in the connector playbook.
- Fix AgentMail health checks that entered local-stdio validation and
optional missing Codex credential cleanup in sandboxes.
- Add API, pipeline, authorization, sandbox, browser, and Storybook
coverage.

## Verification

- Live AgentMail testing covered WebSocket intake, signed webhooks,
restart catch-up, and a full receive → task → Daytona Codex CLI →
explicit reply → Delivered round trip. The reply was verified in the
other inbox. The normal task composer also initiated an outgoing email
child task.
- The connector-skill change was verified in the browser: AgentMail
appears once as an automatic, read-only skill with its assigned address.
Disabling experimental chat connections removes it; re-enabling restores
it. A regression test covers assignment data arriving after library
data.
- Connector regression coverage passed 178 runtime utility, email
integration, skill-route, and heartbeat tests. All 17 Codex execution
tests passed, including per-agent skill isolation, model identity,
revision changes, removal, and prompt delivery without shared skill
files.
- After rebasing onto master, all 44 focused email, heartbeat, and
native-authority tests passed. All 313 native-session executor tests
passed. The UI regression suite passed all 3 tests. These test sets
overlap earlier focused runs.
- Full workspace typecheck and build passed after the rebase. Token
gates passed. Earlier focused Playwright task/setup coverage and the
Storybook build also passed.
- Native connector tool execution uses deterministic integration tests.
Live Daytona qualification used the Codex CLI adapter; the new
shared-home prompt fallback has deterministic coverage.
- The full repository suite is run by CI. The earlier unsharded local
full-suite attempt was stopped after the equivalent CI suites passed and
is not reported as a completed local run. Greptile reviewed
`7e57dc267a8446d3c906e3cc5b8abc94fb8860eb` at 5/5 with no unresolved
threads. All server, workspace, serialized server, and browser suites
passed in CI. The build job hit a five-second timeout in a runner
transport test; both variants and the full 80-test file passed locally
with unchanged timeouts. The build passed on retry on the same commit
without code or timeout changes. All required CI gates, including the
final `ci / verify` and `ci / e2e` summaries, are green on
`7e57dc267a8446d3c906e3cc5b8abc94fb8860eb`.

## Risks

- Email from external senders can start normal agent work. Setup
recommends a low-trust agent and AgentMail sender controls. Sender
addresses never grant board membership.
- Provider timeouts can leave uncertain sends. Retries retain their
idempotency key; expired windows require reconciliation or operator
resolution.
- Connector skills and native tools are assignment-dependent and require
current access. Revocation denies retained calls; assignment changes
select a new runtime context.
- Activation remains behind the experimental-channel setting. The native
runner path has deterministic coverage; live Daytona qualification used
the Codex CLI adapter.
- Schema changes are additive. Inbox ownership is unique across
companies. Disconnect preserves provider inboxes and task history.

## Model Used

OpenAI GPT-6 (Codex). Used reasoning, repository tools, code execution,
and browser testing. The exact deployment model ID and context-window
size were not exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [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-09-11 16:56:38 -05:00
Dotta 889947c238
feat: add experimental native chat connectors (#13038)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - People also ask agents for work in their existing chat tools.
> - Each external conversation needs one task and a current authorized
source.
> - Retries, Stop, and provider failures must not duplicate work or
expose private data.
> - The first chat PR establishes the opt-in provider and data
contracts.
> - This PR adds experimental channel integration and its durable
control plane.
> - Users can request work from connected channels and inspect delivery
in Paperclip.

## Linked Issues or Issue Description

Refs #13100 and #13092. This is the second of exactly two chat PRs.
Foundation #13100 is merged and changed 143 files. Runner prerequisite
#13092 is also merged. This PR changes 400 files against master, below
the 500-file review limit. It contains no wireframe images or HTML
galleries.

## What Changed

- Add native Slack, GitHub, Microsoft Teams, Telegram, and Discord chat
connections. Keep chat disabled unless the operator enables experimental
chat connectors. Preserve the production GitHub tool connection and its
normal setup path.
- Bind each provider bot identity to one immutable Paperclip agent. Bind
each admitted external conversation to one task. Paperclip owns tasks,
runs, permissions, and audit records.
- Add durable admission, per-conversation queues, questions, task
controls, progress, final replies, images, files, and delivery receipts.
Board comments remain internal unless explicitly sent to the channel.
- Check current identity, provider reach, resource access, credentials,
runtime generation, and exact source before provider effects. Keep
private responses private. Never send raw reasoning, private logs,
credentials, or tool arguments.
- Hold uncertain sends for explicit audited resolution. Make Board
Send-to-channel atomic and idempotent. Keep reconnect and setup
credentials in Paperclip secret storage.
- Preserve current native-runner authority across retries, lost
acknowledgements, and recovery. Keep immutable input and completion
contracts separate from newer user input. Receipt reconciliation cannot
launch a provider.
- Reconcile chat close/new ordering and provider-effect lock order.
Audit resource access changes in the same transaction. Submit only the
selected resource from each UI toggle so stale pages cannot undo
unrelated access changes.
- Drain Codex stdout before certifying process exit. Bound the drain
with the existing shutdown grace. Preserve observed terminal authority
without treating an undrained process as successful or reusable.
- Incorporate master `018ca5da` with its ACP Stop, mobile task layout,
runner packaging, and official lock changes. Preserve dedicated
chat-answer continuations in both directions when ordinary queued
comments are adopted after Stop.
- Fence late adapter readiness behind an earlier Stop for the same run.
Preserve verified cleanup for registered adapters. Handle single Stop,
agent pause, duplicate Stops, and failure release without creating a
false cancellation receipt.
- Incorporate master's `6dd48cad4` wake-queue extraction. Preserve exact
failed-chat retry authorization and lineage, retired question-source
suppression, and the block on generic recovery that would discard the
admitted source. Fresh deferred input retains its separate promotion
path.
- Incorporate master `2a05b5ed3` and its queue-admission extraction,
simplified transaction ports, and separate runner CI job. Preserve exact
durable receipts, actor separation, and dedicated-answer isolation
through the new module. A failed receipt insert rolls back the
accompanying deferred-wake merge.

## Verification

Current head: `afe19299d06253cb628eb398e91d1200ea9f412a`, incorporating
master `2a05b5ed3457ea33efd6895520447d1d97fe98d8`. The conflicts are
resolved. This successor fixes two test-harness boundaries exposed by
CI: per-case route-module preparation and actual durable-save completion
before intentional runner termination. Production code and all existing
test/turn deadlines are unchanged. [Exact-head Greptile
review](https://github.com/paperclipai/paperclip/pull/13038#issuecomment-5587250594)
is **5/5**, completed September 10 at 13:20:55 UTC, with no actionable
findings or open review threads. [Fresh exact-head
CI](https://github.com/paperclipai/paperclip/actions/runs/34481724341)
passes **all 24 jobs**, including Build and both required aggregates.
Normal exact-head guarded merge was attempted and rejected by the
remaining branch approval policy: CODEOWNER review is required and no
human approval is present. Normal **squash auto-merge is enabled** as of
September 10 at 13:36:26 UTC. Requested CODEOWNERS have been notified;
no approval bypass or self-approval was used. Earlier-head results below
remain historical evidence, not qualification of this successor.

- Final exact-head Linux evidence: 995/995 chat integration cases; 36/36
agent-skills routes; 35/35 runner live-session cases, including real
process kill/resume; 1948 runner Vitest cases with three existing
benchmark/platform guards; 870/870 API-authority cases; and 104 browser
cases with four existing optional skips. Rust, conformance/replay, full
repository build, typecheck, canary, all server/workspace shards, and
both required aggregates pass with normal CI concurrency. Earlier failed
attempts remain recorded below.

- Latest test-only qualification: 141/141
route/permissions/authentication cases pass in separate cold forks, with
plain server types and independent review clear. The real-runner suite
passes 35/35, with plain runner types and independent review clear. A
controlled premature-save acknowledgement fails as expected; matching
ownership/effect/process evidence, rejected saves, real turn outcome,
test abort, and pre-kill liveness are covered. No local reproduction of
the original CI scheduling failure is claimed. The preceding [CI
run](https://github.com/paperclipai/paperclip/actions/runs/34479680858)
passes 21/24 jobs, including all 995 Linux chat cases and browser
aggregate (104 passed, four existing optional skips); only Build, the
skills serialized shard, and the required verification aggregate fail.
Its exact-head Greptile review was 5/5. Both failed job logs are
retained.

- Final fixture qualification: all eight focused Discord cases and all
995 chat integration cases pass. The exact modal statement/PID is
observed before taking the real connection lock; the test then proves
its actual blocking relationship before mutation. Original SQL
execution, provider behavior, negative assertions, and 1s/15s timeouts
remain unchanged. Independent review is clear and test/production hashes
remain frozen. The preceding [CI
attempt](https://github.com/paperclipai/paperclip/actions/runs/34477184777)
passed 22 jobs, including Build/runner, typecheck, canary, all other
test shards, and browser aggregate (104 passed, four existing optional
skips); the two fixture failures and failed verification aggregate
remain recorded, not relabeled as a pass.

- Current queue-module composition: 308/308 recovery/batching/queue/Stop
tests; 995/995 full chat integration; 89/89 module tests, including real
PostgreSQL receipt-insert rollback; 24/24 workflow/module-boundary
tests; plain server and UI types. All four actual local process/ACP
browser paths pass in 1.4 minutes. Fresh databases, no skips or retries,
stable reviewed source hashes. The initial boundary failure is retained;
its no-op service wrapper was removed without changing recovery context
or weakening the check. An exploratory standalone test-directory
typecheck fails because its new upstream transformation config is not a
standalone typechecking project; standard CI/build does not invoke it,
and no configuration was weakened to suppress those diagnostics.

- The preceding head `e02a63d462ce5d47433b0aeb632bb6fd20aab1ba` passed
[all 24 CI
jobs](https://github.com/paperclipai/paperclip/actions/runs/34436462958)
and exact-head Greptile review at 5/5. Required CODEOWNER review
prevented its normal merge before master advanced again.

- Final extracted-module composition: 307/307 recovery, batching, queue
and Stop-control tests; 995/995 full chat integration; 49/49 module
tests including eight PostgreSQL adapter cases; and 19/19 issue-update
tests. Plain server types pass. All four actual local process/ACP
browser paths pass in 1.3 minutes. Fresh databases, no skips or retries
in these cohorts, frozen source hashes, and independent review clear.

- The preceding head `3e4e1c1c` passes [all PR CI
jobs](https://github.com/paperclipai/paperclip/actions/runs/34415826820),
including Build and required `ci / verify` and `ci / e2e`. Both the
original Rust failure and the previously load-sensitive lineage fixture
pass with unchanged Linux concurrency. Master advanced afterward and
required this reconciliation.
- Final master composition: 448/448 focused UI tests, 186/186 adapter
tests, 24/24 queue/control tests, and 11/11 packaging tests. Plain UI,
server, shared, and adapter types pass. Token gates and diff checks
pass. Independent server and UI reviews are clear.
- Stop-registration regression: both real-service cases fail against
exact `a95` source and pass with the fix. The full corrected
recovery/control suite passes 265/265. Duplicate-owner and failed-Stop
controls also pass. Plain server types pass. The readiness barrier
prevents provider startup without adding an acknowledgment to an already
terminal run.
- Final qualification strengthens terminal-field equality and repeats
both affected cases successfully on a fresh database. All four actual
local process/ACP browser paths pass again in 1.3 minutes, without skips
or retries. The final screenshot shows Cancelled, a paused subtree,
retained input, and no error toast.
- Two new actual-service regressions fail before the merge fix. They
prove that queued-comment adoption could consume a dedicated chat answer
or add unrelated input to that answer. The fixed four-case cohort
passes, including ordinary upstream continuation and adapter Stop
controls. Full recovery passes 257/257. All four actual local
process/ACP Stop browser flows pass in 1.4 minutes, without skips or
retries, on a fresh database.
- The unchanged runner artifact was qualified with 171/171 transport
tests, 870/870 API-authority tests, conformance 1/1, and replay 11/11.
Six controlled reader tests prove the exit/drain repair. Its local
serial Rust workspace passed 546 top-level cases plus two invoked
helpers; the later passing Linux CI supplies default-concurrency
evidence.
- Prior exact-source full chat integration passes 995/995. Settings
regressions cover concurrent stale pages, 501 destinations, pending
state, rejected updates, and explicit retry. These deterministic tests
do not prove live provider behavior.
- Retained failed attempts and their causes are in the [qualification
log](afe19299d0/doc/plans/chat-adapters/2026-09-08-chat-queue-and-webhook-repair.md).
The first merge adapter run timed out while macOS slept for 290 seconds.
Its unchanged repeat passed with a temporary sleep guard. No assertion,
deadline, or CI gate was weakened.

Review commands include `pnpm --filter @paperclipai/server exec vitest
run src/__tests__/heartbeat-process-recovery.test.ts
src/__tests__/issue-queued-comments-routes.test.ts` and `pnpm exec
playwright test --config tests/e2e/playwright.config.ts
tests/e2e/acp-stop-continuation.spec.ts`. Database suites require fresh
disposable databases. See the [browser
runbook](afe19299d0/doc/plans/chat-adapters/2026-09-04-chat-adapters-browser-e2e-runbook.md)
for provider setup and separate live acceptance steps.

## Risks

- This remains experimental. Deterministic tests and bounded live
evidence do not establish every provider feature, tenant, permission
layout, or media shape. Teams work-tenant qualification is still open.
- Failed and uncertain provider effects remain visible and can require
operator action. A transport receipt does not prove recipient
visibility.
- Native controller and runner artifacts must remain compatible.
Preserve lease ownership, terminal authority, source binding, and
quarantine during future changes.
- Access and audit rows commit together, but activity notifications
remain best-effort. This is not a new durable event outbox.
- The PR operation does not deploy a live server, replace its runner, or
change provider permissions. Remaining live qualification is documented
in the [temporary
handoff](afe19299d0/doc/plans/chat-adapters/2026-09-08-open-qualification-followups.md).

## Model Used

OpenAI Codex assisted with implementation, tool execution, testing, and
review. The work records `gpt-6-astra` assistance. The environment does
not report a context-window size. No private reasoning traces are
included.

## 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-09-10 10:06:45 -05:00
Dotta 6abeb67334
feat: add opt-in chat provider and data foundation (#13100)
Add dormant provider contracts, qualified patched adapters, tenant-scoped persistence and lifecycle ownership without activating chat routes. Preserve the experimental integration as dependent PR #13038.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-09 13:49:12 -05:00
Dotta 2991a59b17
fix(adapters): prevent engine fallback and preserve usable runtime defaults (#13105)
## Thinking Path

> - Paperclip manages agents that must write work and report task
outcomes through its API.
> - Local adapters select an execution engine and its permission
settings.
> - A higher ACP Node requirement can make an unchanged installation
lose access to its default engine.
> - The adapter then silently selects CLI, which can change permissions
and block API access.
> - This pull request keeps the engine choice fixed and reports missing
prerequisites before work starts.
> - It also gives explicit Codex CLI runs usable defaults and keeps
managed services on a supported Node runtime.

## Linked Issues or Issue Description

Refs #12215. Related changes: #11792 raised the Node requirement; #13094
addressed separate runner networking behavior. This change fixes the
engine-selection and managed-launcher paths.

**What happened?**

An unchanged agent could switch from ACP to CLI after an upgrade. Codex
CLI then used read-only permissions with networking disabled. The run
could finish without updating its task. Repeated recovery attempts used
the same unavailable setup. Managed updates also skipped the Node check
and did not refresh old launchers.

**Expected behavior**

An unavailable engine must fail with a clear setup error. It must not
silently select another engine. Explicit CLI runs must be able to write
workspace files and call the API unless the operator configures stricter
settings. Managed updates must validate Node and keep child tools on
that runtime.

**Steps to reproduce**

1. Run an ACP-default agent under Node 22 after the ACP minimum rises to
24.11.
2. Leave the engine unset and disable the approval/sandbox bypass.
3. Observe the old adapter select CLI and fail to write task disposition
through the API.
4. Start a managed service with an old launcher and a supervisor PATH
that selects a different Node for child tools.

## What Changed

- Remove automatic engine fallback for Codex, Claude, Gemini, and Kimi.
Check prerequisites for default and explicit ACP selections.
- Return a configuration error with proof that provider work did not
start. Stop automatic continuation retries for this error.
- Enable Codex ACP workspace networking at the actual turn boundary.
Upstream mode presets otherwise force it off even when config.toml
enables it. Preserve explicit network denial and read-only mode.
- Set workspace-write and network access defaults for explicit Codex CLI
runs. Preserve explicit sandbox modes, profiles, and network
restrictions.
- Pin the validated Node directory in managed launcher PATH. Refresh
legacy launchers during installs and npm/Git updates.
- Reject updates on unsupported Node. Keep update checks, dry runs, and
rollback available.
- Synchronize the qualified Codex ACP executable identity across server,
TypeScript runner, Rust runner, and provider-pack launch paths.
- Add regression tests and update engine and installation documentation.

## Verification

- [Full CI passed on the final
head](https://github.com/paperclipai/paperclip/actions/runs/34387099695):
typecheck, build/native runner verification, all general and serialized
test shards, all browser shards, release registry, canary dry run, and
policy checks.
- Greptile: 5/5 on `2c1d6e2815830a5cd39e36c8a082cc0c4441b6c0`, with no
unresolved review findings. Security gates are green.
- Full workspace typecheck and build also passed locally. The final
deployed Linux build passed.
- Full Codex, Claude, Gemini, and Kimi source test suites: 804 passed, 2
skipped. Installer, updater, and launcher tests: 47 passed. Installed
ACP turn-boundary tests: 3 passed. ACP packaging tests: 14 passed.
Focused recovery classification tests also passed.
- Real Linux Codex CLI runs, both fresh and resumed, wrote a workspace
file and reached the control-plane health API with the new defaults.
- Explicit read-only and network-disabled control probes retained those
restrictions.
- A real ACP run on the final deployed Linux build wrote a file and
reached the control-plane API with HTTP 200, without engine fallback.
The same probe failed DNS before the turn-policy patch.
- Executable-identity and installed-policy contracts: 12 passed.
Affected native server tests: 197 passed. Runner factory tests: 21
passed. Rust qualification and native provider integration tests: 11
passed.
- Deployed the production changes to a Linux service on Node 24.20 after
a verified database backup. Health, bootstrap readiness, static UI,
executable/cwd identity, and guarded restart checks passed. The restart
lost no runs.
- Corrected stale Kimi skill-default and Gemini remote-archive fixtures;
both suites pass.

## Risks

- Default or legacy auto engine settings now fail when ACP is
unavailable. Operators who intend to use CLI must select it explicitly.
- Codex CLI now permits workspace writes and networking by default, and
ACP workspace-write turns permit networking by default. Explicit
operator sandbox settings remain authoritative.
- Old managed launchers keep their pinned Node until they are
reinstalled under a supported runtime. An old updater cannot repair
itself; the documentation gives the current installer command.
- Custom service wrappers and global/source installations must configure
their runtime PATH. No database migration is required.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, repository inspection,
shell execution, and test tools. The exact serving model identifier and
context-window size are not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 13:27:24 -05:00
Dotta 3da58b185e
fix(cli): restore test-drive credential inputs (#12898)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The CLI provides a test-drive command for a ready local test
instance.
> - That command must accept a provider credential before it creates the
first agent.
> - The command did not accept a literal key and could lose an exported
key during server startup.
> - This pull request accepts both credential paths and captures the CLI
environment before startup.
> - The benefit is a reliable one-command test drive from an existing
shell.

## Linked Issues or Issue Description

Refs #12894

**What happened?**

`paperclipai test-drive --api-key <value>` failed because the option did
not exist. An exported canonical provider variable could also become
unavailable before the post-listen bootstrap read it.

**Expected behavior**

The command must accept a literal key when the operator requests it. The
command must also use a provider variable that was present when the CLI
started.

**Steps to reproduce**

1. Export `ANTHROPIC_API_KEY` in the shell.
2. Run `pnpm paperclipai test-drive`.
3. Observe that bootstrap can report that no credential exists.
4. Run `pnpm paperclipai test-drive --api-key test-value`.
5. Observe that Commander reports an unknown option on the prior
implementation.

**Paperclip version or commit**

The problem exists on `master` after #12894.

**Deployment mode**

Local development with `pnpm` and the embedded database.

## What Changed

- Add the `--api-key <value>` test-drive option.
- Keep `--api-key` and `--api-key-env` mutually exclusive.
- Capture provider variables before in-process server startup changes
the process environment.
- Redact literal and environment-backed credentials from Paperclip
errors, including custom environment-variable names with surrounding
whitespace.
- Scrub split and joined literal-key forms from the JavaScript
`process.argv` view before telemetry, diagnostics, API work, or server
startup.
- Warn about process argument and shell history exposure without
printing the key.
- Add tests for literal keys, option conflicts, environment snapshots,
argv handling, and error redaction.
- Update the CLI and development documentation, including the remaining
external argv exposure tradeoff.

## Verification

- `pnpm exec vitest run cli/src/__tests__/test-drive.test.ts` passed 32
tests.
- `pnpm -r typecheck` passed.
- `pnpm build` passed.
- A live literal-key smoke test created one company and one CEO agent
without a provider call.
- A live exported-variable smoke test created the same clean instance
without credential flags.
- `pnpm test:run` completed locally with 5,870 passing tests and 19
host-dependent failures in six unrelated suites. The failures came from
macOS `/tmp` aliases, exhausted test ports, and existing
workspace-runtime fixture assumptions.
- GitHub CI passed the full build, typecheck, canary dry run, general
tests, serialized server tests, and e2e matrix on clean Linux runners.
- Greptile rated the exact latest head 5/5, Superagent passed, and all
review threads are resolved.

## Risks

- A raw value passed through `--api-key` can appear in operating-system
process listings, shell history, or parent-wrapper output before
Paperclip can scrub its own JavaScript argv view. The command warns
about this risk and Paperclip does not print the value.
- The environment snapshot contains the process environment only in
memory for the life of the foreground command.
- This change adds no schema migration and no REST endpoint.

> 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 version and context window
are not exposed to the agent. Extended reasoning, tool use, code
execution, and GitHub access were enabled.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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-09-05 10:48:17 -05:00
Dotta 8f0c1d4548
feat(cli): add isolated test-drive command (#12894)
Add a foreground-only test-drive workflow with isolated data, provider-backed CEO bootstrap, OpenCode/OpenRouter support, worktree execution setup, reuse safeguards, and delayed browser opening.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-05 09:33:38 -05:00
Dotta fdf8c8464d
feat(runner): add managed provider backends (#12699)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Paperclip Runner provides durable, provider-neutral agent
execution.
> - The current stack supports qualified local providers but omits the
managed provider paths from the integration branch.
> - Claude Managed Agents and AWS AgentCore need explicit profile
qualification, durable recovery, usage accounting, and cleanup controls.
> - This pull request adds those managed backends as the third part of
the Runner parity stack.
> - The benefit is managed execution without weakening the default-off
Runner rollout gate.

## Linked Issues or Issue Description

**Subsystem affected**

Cross-cutting: Runner, server orchestration, database profiles, CLI, and
adapter configuration UI.

**Problem or motivation**

The current Runner stack cannot select or execute the managed Claude
Agents API or AWS Bedrock AgentCore Harness backends. It also lacks
qualified profile storage and recovery checks for those remote
resources.

**Proposed solution**

Add qualified managed and remote profiles, API and CLI management, exact
provider selection, durable lifecycle handling, cumulative usage
accounting, bounded cleanup, and retention acknowledgement. Keep
`enableNativeRunner` default-off.

**Alternatives considered**

A direct copy of the old integration branch was rejected because its
provider contracts, model values, credential flow, and migration history
no longer match the current base. A single large parity pull request was
also rejected because stacked review keeps each subsystem bounded.

**Roadmap alignment**

This continues the existing Runner architecture and rollout work. It
does not introduce a separate execution system.

**Additional context**

This pull request is based on the merged #12691 and #12685 stack. It
also closes the delayed security-review findings reported on #12691 by
binding qualified ACPX and OpenCode launch artifacts to the bytes
actually executed. A GitHub search for managed agent, AgentCore, and
Claude managed work found no duplicate public issue or pull request.

## What Changed

- Add Claude Managed Agents and AWS AgentCore provider executors to
runnerd.
- Add qualified managed and remote profile storage, routes, OpenAPI
contracts, CLI commands, and migration 0237.
- Validate profile ownership, enabled state, exact qualified revision,
model, agent version, and secret binding before persistence and
recovery.
- Persist durable provider session and owned skill state for
restart-safe cleanup.
- Reconcile uncertain create responses and delete remote sessions before
owned skills.
- Track cumulative provider usage and enforce positive session spend
caps.
- Recover interrupted AgentCore usage at the next turn boundary by
charging the prior invocation ceiling exactly once; keep the session
gated until an explicit monotonic budget raise.
- Isolate AgentCore AWS configuration from host profiles and
credential-process/SSO configuration while preserving workload identity.
- Require OpenCode 1.18.17 and fixed build-owned provider-pack artifact
paths; remove the ambient executable override.
- Snapshot and content-verify ACPX and OpenCode commands, scripts, and
provider executables before launch. Linux executes sealed inherited
descriptors; macOS uses authenticated private snapshots with retry-safe
rematerialization at the spawn boundary.
- Persist canonical ACPX and OpenCode launch-profile digests, reject
drift across fresh recovery, and make recovery failures sticky.
- Close and journal unsafe ACPX active-turn recovery before any provider
bootstrap or reconnect.
- Add managed provider fields to the Runner configuration UI and
permission projection.
- Preserve the default-off `enableNativeRunner` experimental flag.

## Verification

- `pnpm -r typecheck`
- `pnpm build`
- Focused managed server, database, CLI, Runner TypeScript, Rust,
Claude, AgentCore, ACPX, OpenCode, process-supervisor, and
durable-recovery tests passed.
- `cargo test -p paperclip-runner-core --lib --locked` (160 tests)
- `cargo check --workspace --all-targets --locked`
- Native Codex integration tests passed (60 tests); native provider
tests passed (7 tests); server native-runtime tests passed (87 tests).
- Verified-launch replacement, nested-spawn retry, exact-version,
profile-drift, sticky-failure, and no-bootstrap active-recovery tests
passed.
- `git diff --check`
- The PR changes 91 files. `pnpm-lock.yaml` is unchanged. The Rust
workspace lockfile adds the approved `rustix` dependency used for safe
descriptor handling while `#![forbid(unsafe_code)]` remains enabled.

## Risks

- The provider APIs can change while they are in beta. Exact
qualification and fail-closed recovery checks limit drift.
- Remote cleanup can fail after a partial create. Durable ownership
inventories and retry-safe deletion preserve recovery state.
- Migration 0237 adds profile tables. The generated migration and
snapshot pass the repository migration checks.
- Managed execution can incur provider cost. Positive default spend caps
and explicit retention acknowledgement limit accidental use.
- An interrupted AgentCore invocation without final metadata is
conservatively charged to its active session ceiling. This can overstate
cost, but cannot undercount it; later work requires an explicit budget
increase.
- Linux qualified launches use sealed memory descriptors. macOS lacks
executable-descriptor APIs, so the runner uses owner-only private
snapshots and minimizes linked-path lifetime; hostile same-UID processes
remain outside the documented local-host trust boundary.
- The global Runner feature remains default-off.

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

## Model Used

OpenAI Codex, GPT-5, with tool use, code execution, and subagent review.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change 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-09-02 00:48:30 -05:00
Dotta 4b6de5327e
Remove cheap model profiles (#12683)
## Thinking Path

> - Paperclip manages agents that use different model providers and
adapters.
> - Paperclip must keep agent execution rules clear and predictable.
> - The cheap-model profile added a second execution mode across
adapters, task recovery, APIs, and the UI.
> - That mode increased configuration and recovery complexity.
> - This pull request removes the cheap-model profile as a product
feature.
> - The benefit is one model-selection path for normal work and recovery
work.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

This change simplifies model selection across agent configuration, task
execution, recovery, and adapter capabilities.

**Current behavior**

Paperclip exposes cheap-model profiles in adapter metadata, agent
runtime configuration, task overrides, recovery rules, APIs, and the
board UI. Recovery work can select a different model profile from the
agent's configured model.

**Proposed behavior**

Paperclip uses the agent's configured model for normal work and recovery
work. Status-only recovery stays limited to coordination work. The API
rejects legacy model-profile configuration. A migration removes stored
model-profile values from existing agent, issue, and historical revision
records.

**Reason and benefit**

One model path reduces configuration, API, UI, and recovery complexity.
It also prevents status recovery from becoming a separate product-level
model-routing feature.

**Breaking changes**

This change removes model-profile fields and adapter capability
metadata. Existing stored model-profile values are removed by an
idempotent migration. The validators reject new legacy profile values
with clear errors.

## What Changed

- Removed model-profile types, adapter capabilities, API fields, and
model selection logic.
- Removed cheap-model controls from agent and task UI surfaces.
- Kept status-only recovery limited to coordination context while normal
continuations use the configured agent model.
- Added an idempotent migration that removes stored model-profile values
from agents, issues, and configuration revisions without changing issue
update timestamps.
- Updated tests and product documentation for the single-model behavior.

## Verification

- `pnpm check:token-gates` passes.
- `pnpm -r typecheck` passes.
- `pnpm build` passes.
- `pnpm test:run` completed with 5,607 passing tests and 8
environment-sensitive failures in unrelated fixed-port and
database-deadlock suites. The same failures repeated in an isolated
rerun. CI is the final clean-room result.

## Risks

- This is an intentional breaking change for clients that send
model-profile fields.
- The migration changes legacy agent, issue, and configuration-revision
JSON. It is idempotent and preserves unrelated fields and issue update
timestamps.
- The change is cross-cutting because the removed feature existed in
adapters, shared contracts, the server, plugins, and the UI.

> 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`. Reasoning and tool use were enabled. The
runtime did 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)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-01 14:57:38 -05:00
Dotta 1ab159d3a7
feat(apps): consolidate connector management (#12684)
Completes the post-managed-OAuth connector lifecycle, Paperclip Cloud provisioning defaults, governed test flows, and consolidated Apps UI.\n\nCo-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-01 14:55:35 -05:00
Dotta dda4dff645
fix(onboarding): restore browser launch and gate canaries (#12667)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The onboarding command starts the local server and opens the
first-run wizard.
> - Interactive onboarding stopped opening the browser by default.
> - Organization creation could also succeed in the API while the wizard
stayed on the name step.
> - The npm canary workflow did not prove that the published package
could complete this path.
> - This pull request repairs the startup and organization transitions
and adds an exact-version canary smoke gate.
> - The benefit is a quickstart that works for users and is tested after
each canary publish.

## Linked Issues or Issue Description

Related: #12557 covers a separate final-route onboarding handoff.

**What happened?**

Interactive `paperclipai onboard` runs did not open the onboarding page.
The organization API request could succeed while a same-company context
update caused the wizard to stay on the organization step. The canary
release lane did not test the exact published npm package through this
path.

**Expected behavior**

Interactive onboarding must open the browser once. A successful
organization request must advance to the first-agent step when the
surrounding context adopts the same organization. Each published canary
must install in a clean environment and reach the model connection step.

**Steps to reproduce**

1. Run `npx paperclipai@canary onboard --data-dir "$(mktemp -d
/tmp/paperclip-canary.XXXXXX)"` in an interactive terminal.
2. Enter an organization name while the company context refreshes from
the create response.
3. Observe that the browser does not open or that the wizard can remain
on the organization step after the API creates it.
4. Inspect the canary release lane and observe that no post-publish
onboarding test runs against the exact npm version.

**Paperclip version or commit**

The issue reproduced with `2026.901.0-canary.8` and the source state
before this pull request.

**Deployment mode**

Local trusted quickstart with embedded PostgreSQL. The install source
can be npm or a source checkout.

## What Changed

- Open the browser once for interactive foreground onboarding.
- Preserve explicit browser opt-outs and restore the prior environment
value after startup.
- Accept a same-company context update after organization creation and
reject a different-company takeover with an explicit error.
- Export the exact canary version from the publish job.
- Install and test that exact npm version in a clean Playwright smoke
job through the "Connect a model" step.
- Upload server logs, traces, screenshots, and the Playwright report
when the canary smoke fails.
- Document the interactive default and headless opt-outs.

## Verification

- `pnpm exec vitest run cli/src/__tests__/onboard.test.ts
ui/src/components/OnboardingWizard.step.test.tsx --reporter=dot` passes
with 37 tests.
- `node --test scripts/__tests__/release-verify-workflow.test.mjs`
passes with 9 tests.
- `pnpm exec playwright test --config tests/e2e/playwright.config.ts
tests/e2e/onboarding.spec.ts` passes with 2 tests.
- `PAPERCLIPAI_VERSION=2026.901.0-canary.8 pnpm run
test:canary-onboarding-smoke` passes against the published npm package.
- `pnpm check:token-gates` passes.
- `pnpm -r typecheck` passes.
- `pnpm build` passes.
- A fresh interactive source run opens the browser and reaches "Connect
a model" after organization and agent naming.

## Risks

- Low risk. Automatic browser opening only applies to interactive
foreground onboarding.
- `PAPERCLIP_NO_BROWSER=1` and `PAPERCLIP_OPEN_ON_LISTEN=false` keep
headless runs silent.
- A different organization context still blocks the pending create
transition.
- The canary package is immutable before the smoke runs. A smoke failure
leaves the package published but makes the release workflow red.
- This change does not modify REST APIs, database schemas, or shared
data types.

> 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 runtime does not expose the exact
deployment snapshot or context-window size. The model used reasoning,
browser automation, repository tools, shell commands, code editing, 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-09-01 10:10:30 -05:00
Dotta e3eed3a3ae
Keep browser startup explicitly opt-in (#12435)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The CLI can save a configuration and start the local server in one
command.
> - The onboarding path set a browser-open environment variable without
an explicit user request.
> - Headless test servers use the same onboarding path.
> - Each server restart could therefore open a system browser.
> - This pull request removes the implicit browser-open request and
fixes test servers to disable it explicitly.
> - The benefit is predictable foreground and test startup without
unsolicited browser windows.

## Linked Issues or Issue Description

This is stack 11 of 11. It depends on stack 10.

**What happened?**

`paperclipai onboard --yes --run` set `PAPERCLIP_OPEN_ON_LISTEN=true`.
Headless server users, including browser test runners, opened the system
browser on each server restart.

**Expected behavior**

Server startup must not open a browser unless the caller explicitly sets
`PAPERCLIP_OPEN_ON_LISTEN=true`.

**Steps to reproduce**

1. Run `paperclipai onboard --yes --run` from a clean source checkout.
2. Wait for the server to listen.
3. Observe that the default system browser opens.

**Paperclip version or commit**

Reproduced on `dbf052577` plus the dependent stack.

**Deployment mode**

Local dev from source.

## What Changed

- Stop onboarding from setting `PAPERCLIP_OPEN_ON_LISTEN=true` for
foreground startup.
- Set `PAPERCLIP_OPEN_ON_LISTEN=false` in E2E and issue-detail
performance test servers as defense in depth.
- Preserve the existing explicit environment opt-in in the server.

## Verification

- `pnpm exec vitest run cli/src/__tests__/onboard.test.ts` — 10 tests
passed.
- `pnpm --filter paperclipai typecheck` — passed.
- `pnpm -r typecheck` — passed on the stacked head.
- `pnpm build` — passed on the stacked head.
- Playwright was not run locally by request.

## Risks

- Low risk. The only behavior change removes an unsolicited side effect.
- A caller that wants browser startup can still set
`PAPERCLIP_OPEN_ON_LISTEN=true` explicitly.
- No database or migration change exists in this layer.

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

## Model Used

- OpenAI Codex, GPT-5. The exact deployment suffix and context window
are not exposed. The model used reasoning, repository tools, code
execution, Git, and GitHub API access.

## 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-29 12:08:35 -05:00
Dotta b3343dbd64
feat(connections): add self-serve intent runtime (#12345)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents need a governed way to request app connections during issue
work.
> - The catalog now describes the available providers and setup methods.
> - A request must become a durable, company-scoped intent before an
operator acts on it.
> - This pull request adds that intent runtime across server, agent,
CLI, and shared contracts.
> - The benefit is a safe bridge from agent need to operator-approved
setup.

## Linked Issues or Issue Description

Refs #11965

This is stack 7 of 11. It depends on stack 6 and replaces another
reviewable part of #11965.

## What Changed

- Add connection intent types, validation, service logic, and routes.
- Add agent runtime tools and CLI support for connection requests.
- Add issue-thread interaction support for connection intents.
- Add runtime, route, adapter, and contract tests.
- Hold the final resolved-continuation row lock through asynchronous
adapter preparation until an actual process spawn, so parking or
reassignment cannot cross that boundary.
- Report Hermes Gateway's first remote run request through the shared
dispatch hook so the resolved-intent lock is released at the true
dispatch boundary.
- Revalidate the addressed user's live non-viewer membership and
connection-management authority for every intent mutation, including
OAuth completion.

## Verification

- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/tool-access-service.test.ts`
- Result: 176 tests passed.
- `pnpm build`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-stale-queue-invalidation.test.ts` (32 passed;
includes non-process dispatch lock-release coverage)
- `pnpm exec vitest run --project @paperclipai/server
server/src/__tests__/connection-intents-service.test.ts -t
"addressed-user mutation"` (1 passed)
- `pnpm exec vitest run --project @paperclipai/server
server/src/__tests__/tool-access-service.test.ts -t "binds OAuth
callback completion to the initiating board session"` (1 passed)
- `pnpm --filter @paperclipai/hermes-paperclip-adapter test --
src/gateway/server/execute.test.ts` (23 passed; includes dispatch-hook
ordering and exactly-once coverage)
- `pnpm --filter @paperclipai/hermes-paperclip-adapter typecheck`

## Risks

- A malformed intent could create an unusable operator request.
- Validators and company checks reject invalid or cross-company
requests.
- The final continuation gate holds the issue row lock through adapter
preparation until process or remote dispatch; later operator changes use
the normal active-run interruption path.
- The change does not add a database migration.

> I checked `ROADMAP.md`. This stack continues the existing app
connection work from #11965 and does not duplicate another planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime model ID and context window were not
exposed. The model 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 linked the public source pull request with `Refs #`
- [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 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-29 12:08:34 -05:00
Nicky Leach a3b78f9d34
fix(cli): make embedded-Postgres tests survive runner contention (#12466)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The CLI test suite uses embedded Postgres for worktree checks.
> - Several tests start a cluster and run migrations on each test.
> - Runner contention can make this cost class exceed small hand-written
time budgets.
> - A timed-out test can also leave the cluster alive while cleanup
removes its data directory.
> - This pull request gives the cost class one measured timeout and
registers cleanup for the timeout path.
> - The benefit is stable tests and fewer orphaned embedded Postgres
processes.

## Linked Issues or Issue Description

No public GitHub issue tracks this defect. Related PRs #10987 and #10961
address wider embedded-Postgres test setup issues. This pull request
addresses the CLI timeout and cleanup path described below.

**What happened?**
Five CLI tests used different hand-written time budgets while they
started embedded Postgres and ran migrations. Runner contention made one
test exceed its budget. A timeout also skipped the `finally` cleanup
path and left a Postgres process alive.

**Expected behavior**
Each embedded-Postgres test gets a budget that covers measured runner
contention. Cleanup stops the cluster before the test removes its data
directory, including after a timeout.

**Steps to reproduce**
1. Run `npx vitest run cli/src/__tests__/worktree.test.ts` on a
contended runner.
2. Observe the embedded-Postgres tests take longer than their small
hand-written budgets.
3. Inspect the process list after a timeout and observe an orphaned
Postgres process.

**Paperclip version or commit**
The defect reproduces on `master` before this pull request.

**Deployment mode**
Built from source with Vitest.

**Installation method**
Built from source with pnpm.

## What Changed

- Export `EMBEDDED_POSTGRES_TEST_TIMEOUT_MS` from the embedded-Postgres
test helper.
- Apply the shared timeout to every test defined by
`itEmbeddedPostgres`.
- Replace the five hand-written timeout values in `worktree.test.ts`.
- Register temporary-directory removal and cluster stop with
`onTestFinished` in the correct order.
- Restore the working directory during timeout cleanup.

## Verification

- `npx vitest run cli/src/__tests__/worktree.test.ts` passes 63 tests.
- `npx vitest run cli/src` passes 424 tests across 59 files.
- `tsc --noEmit` in `cli/` adds no new error in the touched files.
- CI must pass on the pull request head.
- Greptile must report 5/5 with no unresolved blocking thread.

## Risks

- Low risk. The change affects test helpers and test cleanup only.
- The shared budget can lengthen a failing test before Vitest reports
the failure.
- The cleanup order depends on Vitest callback order, which the tests
now use explicitly.

## Model Used

OpenAI Codex, GPT-5. The model used tool calls and code review support.
The context window size and internal reasoning mode were not exposed in
this run.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-28 13:51:54 -07:00
Devin Foley 8316ceb0b9
Add the Better Auth issuer column so signup and sign-in work (#12396)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - A self-hosted install in `authenticated` mode signs users in with
Better Auth, mounted at `/api/auth` over a hand-written Drizzle
`account` table in `packages/db`
> - Better Auth 1.7.0 added a required `issuer` field to that `account`
model, plus a unique index on `(issuer, accountId)`
> - The dependency bump in #11886 changed only `server/package.json` and
the lockfile, so the Drizzle table never grew the column
> - The Drizzle adapter checks the model against the schema on every
write, so `linkAccount` throws and sign-up answers 500 with an empty
body; a fresh install cannot create its first user, and an upgraded
install locks out every existing user
> - This pull request adds the `issuer` column and its unique index, and
migrates the column in with a backfill that covers every existing row
> - The benefit is that sign-up and sign-in work again, on a new install
and after an upgrade

## Linked Issues or Issue Description

No existing issue. Describing it inline, following
`.github/ISSUE_TEMPLATE/bug_report.yml`.

Refs #11886 (the dependency bump that introduced the required field).
Refs #12269 (an earlier attempt at this fix; its backfill covers only
`provider_id = 'credential'`).

**What happened?**

Sign-up fails on a self-hosted install. `POST /api/auth/sign-up/email`
answers HTTP 500 with a zero-byte body. The server log carries:

```
[Better Auth]: The field "issuer" does not exist in the "account" Drizzle schema.
# SERVER_ERROR: [BetterAuthError: The field "issuer" does not exist in the "account" Drizzle schema.]
```

The request writes the `user` row and then fails on the `account` row.
The address is stuck after that: a second sign-up answers 422
`USER_ALREADY_EXISTS`, sign-in answers 401, and password reset answers
400 `RESET_PASSWORD_DISABLED` because the account that would hold the
password does not exist.

An upgraded install is worse. `sign-in/email` matches the credential
account on `account.issuer === 'local:credential'`. Rows written before
the upgrade have no issuer, so every existing user is locked out.

**Expected behavior**

`POST /api/auth/sign-up/email` answers 2xx and writes both the `user`
row and its credential `account` row. `POST /api/auth/sign-in/email`
then answers 2xx and sets a session cookie. An install that upgrades
keeps its existing users.

**Steps to reproduce**

1. Start a server from `master` with
`PAPERCLIP_DEPLOYMENT_MODE=authenticated` against an empty database.
2. `curl -X POST http://127.0.0.1:<port>/api/auth/sign-up/email -H
'Content-Type: application/json' -H 'Origin: http://127.0.0.1:<port>'
--data
'{"name":"A","email":"a@example.com","password":"a-long-password"}'`
3. The response is HTTP 500 with an empty body.

**Paperclip version or commit**

`master` at 4436cf0. The defect starts at 69e8585 (#11886), which moved
Better Auth from 1.6.28 to 1.7.0.

**Deployment mode**

`authenticated`. `local_trusted` does not sign users in, so it is not
affected. Hosted tenants are not affected either: that path resolves the
actor from a trusted header and never reads `account`.

**Database mode**

Both. Embedded PostgreSQL and external PostgreSQL use the same Drizzle
schema.

**Relevant logs or output**

Reproduced in a test by reverting the schema change:

```
stderr | better-auth-credential-signup.integration.test.ts
[Better Auth]: The field "issuer" does not exist in the "account" Drizzle schema.
AssertionError: expected 500 to be 200
```

## What Changed

- `packages/db/src/schema/auth.ts`: adds `issuer` (text, NOT NULL) to
`authAccounts`, and the `(issuer, account_id)` unique index that mirrors
the index Better Auth declares on the model. The field name, type,
requiredness, and index all come from
`@better-auth/core/dist/db/get-tables.mjs` in 1.7.0.
- `packages/db/src/migrations/0230_better_auth_account_issuer.sql`: adds
the column, backfills every existing row, sets NOT NULL, and creates the
unique index.
- `packages/db/src/migrations/meta/0230_snapshot.json` and
`_journal.json`: regenerated with `pnpm --filter @paperclipai/db
generate`.
- `packages/db/src/better-auth-account-issuer-migration.test.ts`: new.
Asserts the schema shape, then rewinds the migration on a real database,
seeds pre-upgrade rows, and re-applies it.
-
`server/src/__tests__/better-auth-credential-signup.integration.test.ts`:
new. Real sign-up and sign-in through the Better Auth mount, against the
real Drizzle schema and a migrated PostgreSQL.
- `cli/src/__tests__/worktree.test.ts`: the worktree seed fixture writes
a credential `account` row, so it now writes `issuer` too.

`server/package.json` and `pnpm-lock.yaml` are untouched. The dependency
is correct; the schema was what was missing.

### The issuer values, and where they come from

Better Auth builds these itself, in
`@better-auth/core/src/db/schema/account.ts`:

```ts
export function createLocalAccountIssuer(providerId: string): string {
  return `local:${encodeURIComponent(providerId)}`;
}
export function createOAuthAccountIssuer(providerId: string): string {
  return `local:oauth:${encodeURIComponent(providerId)}`;
}
```

Sign-up and sign-in both call `createLocalAccountIssuer("credential")`,
so a credential account is `local:credential`. An OAuth account whose
provider declares no `accountIssuer` of its own is
`local:oauth:<providerId>` — no built-in social provider declares one.
The migration writes exactly those two forms:

```sql
ALTER TABLE "account" ADD COLUMN IF NOT EXISTS "issuer" text;
UPDATE "account"
SET "issuer" = CASE
  WHEN "provider_id" = 'credential' THEN 'local:credential'
  ELSE 'local:oauth:' || "provider_id"
END
WHERE "issuer" IS NULL;
ALTER TABLE "account" ALTER COLUMN "issuer" SET NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS "account_issuer_account_id_uq" ON "account" USING btree ("issuer","account_id");
```

Two limits are worth stating plainly. The OAuth branch reproduces
`createOAuthAccountIssuer` for provider ids that need no
percent-encoding, which covers every built-in provider id; a provider id
with a character `encodeURIComponent` would escape would get a slightly
different string. And a generic-OAuth provider that sets `accountIssuer`
explicitly (Okta, Auth0, Keycloak, Slack, Line) uses the real issuer
URL, which this migration cannot know. Neither case can arise on
Paperclip today: `createBetterAuthInstance` configures
`emailAndPassword` only and registers no social or generic-OAuth
provider, so every existing row is a credential row. The OAuth branch is
there so the backfill stays total rather than leaving a NULL that aborts
`SET NOT NULL`.

## Verification

- `pnpm --filter @paperclipai/db check:migrations` — passes.
- `pnpm --filter @paperclipai/db typecheck` — passes.
- `packages/db` suite: 30 files, 107 tests, all pass.
- `npx tsc --noEmit` in `server/` — no error in any changed file. (The
wrapped `pnpm typecheck` builds the runner vendor first, which needs
cargo; that toolchain was not available here, so the pre-existing
"cannot find module" errors from the unbuilt workspace packages remain
in the bare run.)
- `node --test scripts/__tests__/run-vitest-stable-shard.test.mjs` —
passes with the new server suite in the file list.
- The two new tests were confirmed to fail without the fix:
- Reverting `packages/db/src/schema/auth.ts` to its `master` content
makes the server test fail with the reported error and `expected 500 to
be 200`.
- Narrowing the backfill to `WHERE "issuer" IS NULL AND "provider_id" =
'credential'` makes the migration test fail with `column "issuer" of
relation "account" contains null values` — the failure mode of #12269.
- End to end against a server built from this branch, started with
`PAPERCLIP_DEPLOYMENT_MODE=authenticated` on embedded PostgreSQL:
  - `POST /api/auth/sign-up/email` → 200 with a user and token.
  - `POST /api/auth/sign-in/email` → 200 with a session cookie.
  - `GET /api/auth/get-session` → 200 with the session.
- The stored row is `issuer = 'local:credential'`, `provider_id =
'credential'`, `account_id = user_id`, and `pg_indexes` lists
`account_issuer_account_id_uq`.
- `scripts/docker-onboard-smoke.sh` was not used as proof: it installs
`paperclipai` from npm inside the container, so it exercises a published
release rather than this branch.

## Risks

- **Migration.** The migration backfills every existing row before `SET
NOT NULL`, so an install that upgrades keeps working and its users keep
signing in. `account` is one row per user per provider, so the
full-table `UPDATE` and the index build are cheap;
`packages/db/src/table-size-estimates.ts` already classes `account` as
small, and `check:migrations` passes with no new safety finding.
- **New unique index.** `(issuer, account_id)` is the key Better Auth
resolves accounts by, so a duplicate would already be a defect. Better
Auth writes one credential account per user keyed on the user id, so the
pair is unique by construction. An install that somehow holds a
duplicate would fail the index build rather than corrupt anything, and
the migration is a single transaction.
- **Orphaned users are not repaired.** An address that hit the broken
window has a `user` row and no `account` row. This migration does not
delete or repair those rows, so that address stays unusable after the
upgrade: sign-up says the user exists, and there is no credential
account to sign in as or reset. Only installs that ran a build
containing #11886 are affected, and the repair — deleting the orphaned
`user` rows — is a judgment call about live data that does not belong in
an automatic migration.
- **Not a behavior change anywhere else.** Only the `account` table
changes. Hosted tenants resolve their actor from a trusted header and
never read it.

## Model Used

Claude (Anthropic), Claude Opus, 1M context, 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 22:31:35 -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 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
Nicky Leach 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
Dotta 0f0e544317
fix(cli): open dashboard after onboarding service starts (#12164)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The CLI can install and start Paperclip as a managed user service
during onboarding.
> - Recent fixes now install the service shim and remove the redundant
foreground start prompt.
> - The service path still ends without a dashboard URL or an open
browser.
> - The server can also move to a free port when the configured port is
busy.
> - This pull request adds a health-aware handoff to the managed
service's actual endpoint.
> - The benefit is that new users can reach Paperclip without starting a
second process.

## Linked Issues or Issue Description

**What happened?**

After interactive onboarding installs and starts the managed service,
the command ends without printing the dashboard URL or opening the
browser. If the configured port is busy, the service can use a fallback
port that the onboarding process does not know.

**Expected behavior**

Onboarding must print the dashboard URL that belongs to the managed
service. An interactive terminal should open the URL after the local
health check succeeds. A non-interactive terminal should only print the
URL.

**Steps to reproduce**

1. Start from a host without an installed Paperclip service.
2. Run another process on the configured Paperclip port.
3. Run `npx paperclipai@<version> onboard` in an interactive terminal.
4. Accept the managed service installation.
5. Observe that the service starts on a fallback port, but onboarding
does not provide or open that dashboard URL.

**Paperclip version or commit**

`b6854e61c` on `master`, after #12148, #12151, and #12153.

**Deployment mode**

Local managed user service on macOS or Linux.

**Installation method**

`npx paperclipai@<version> onboard`. The same onboarding path can also
run after `install.sh`.

Related public pull requests: #12148, #12151, and #12153.

## What Changed

- Record each running CLI server's PID, selected port, and dashboard URL
in atomic per-instance runtime metadata.
- Accept runtime metadata only when its PID matches the active managed
service.
- Wait for the selected runtime endpoint to report healthy before
printing its URL.
- Open the URL in interactive terminals and keep headless runs
browser-free.
- Keep the printed configured URL as a fallback when runtime discovery
fails.
- Use browser-launch wording that only claims the URL was sent to the
opener.
- Add runtime metadata, fallback-port, health handoff, headless, and
failure-path tests.
- Document the managed service dashboard handoff.

## Verification

- `pnpm exec vitest run cli/src/__tests__/onboard-service.test.ts
cli/src/__tests__/runtime-info.test.ts cli/src/__tests__/onboard.test.ts
cli/src/__tests__/open-url.test.ts
cli/src/__tests__/service-health-check.test.ts` — 44 tests passed.
- `node --test scripts/service-onboard-smoke.test.mjs` — 4 tests passed.
- `pnpm -r typecheck` — passed on head `82920596a`.
- `pnpm build` — passed on head `82920596a`.
- `pnpm test:run` — 4,685 tests passed. The command also reported 31
failures in nine server test files outside this change. This machine
generated invalid test ports above 65,535, and some project-skill
fixtures resolved outside the worktree.

## Risks

- Risk is low because the new handoff runs only after a successful
service installation.
- Onboarding can wait up to 60 seconds when runtime metadata or the
health check does not become ready.
- Runtime metadata is matched to the supervisor PID, so stale or
foreground-process metadata is ignored.
- A non-interactive terminal does not open a browser.
- A failed health check or browser launch does not fail onboarding. The
CLI keeps a manual URL visible.

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

## Model Used

OpenAI Codex, GPT-5 family. The runtime did not expose the exact model
ID or context window. The model used reasoning, repository tools, GitHub
access, 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 09:33:09 -05:00
Devin Foley 8d714c2d84
fix(cli): skip the foreground-start prompt after the service starts (#12153)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The CLI onboarding wizard can install Paperclip as a background
service, and it offers a foreground start when nothing else will serve
> - After #12148, an interactive onboard installs and starts the
service, then still asks "Start Paperclip now?"
> - Answering yes runs the foreground start into the already-running
instance guard, so a fully successful onboard ends with an error message
> - This pull request excludes the just-installed-service case from the
foreground-start prompt
> - The benefit is that an interactive onboard that installs the service
ends cleanly instead of steering the user into a guard refusal

## Linked Issues or Issue Description

Refs #12148 — found while verifying that fix interactively. The
`shouldRunNow` flag already accounts for `serviceInstalled`, but the
interactive TTY fallback prompt did not, so only real interactive runs
hit it: `--yes` runs, CI, and container smokes all skip the prompt
branch.

**What happened?**

Interactive `onboard`, accept the background-service prompt. Output ends
with: service installed and started, then "Start Paperclip now?" → yes →
"Paperclip instance 'default' is already running as
ing.paperclip.paperclipai. Use 'paperclipai service status --instance
default' or pass --force to bypass this safety check."

**What did you expect to happen?**

Onboarding ends cleanly after "Installed and started …" — there is
nothing left to start, so no prompt.

**Steps to reproduce**

Run `npx paperclipai@2026.825.0-nightly.1 onboard --data-dir "$(mktemp
-d)"` in a terminal, accept the service prompt, then accept "Start
Paperclip now?".

## What Changed

- New `shouldOfferForegroundStart` predicate in
`cli/src/onboard-service.ts`: the foreground-start prompt is offered
only when the start was not already decided by flags, the service was
not just installed, onboarding was not invoked by `run`, and the
terminal is interactive.
- Both onboarding call sites in `cli/src/commands/onboard.ts` use the
predicate instead of the inline condition that ignored
`serviceInstalled`.
- Unit tests cover the predicate matrix in
`cli/src/__tests__/onboard-service.test.ts`.

## Verification

- `npx vitest run src/__tests__/onboard-service.test.ts` in `cli/`: 12
passed (5 new).
- `tsc --noEmit` reports no errors in the changed files (remaining
errors are pre-existing in `server/`).
- Manual reproduction of the defect on macOS with `2026.825.0-nightly.1`
before the fix: service installed, started, and healthy, then the prompt
steered into the guard refusal.

## Risks

- Low risk. The prompt still appears in every case it did before except
when the service was just installed and is already serving.
- No behavior change for `--yes`, `--run`, `--install-service` in
non-interactive runs: those paths never reached the prompt.

## Model Used

- Claude Fable 5 (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-25 01:06:20 -07:00
Devin Foley faad235aa2
fix(cli): materialize the managed install before the onboarding service install (#12148)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Interactive onboarding offers to install Paperclip as a background
service, defaulting to yes
> - The service definition targets the managed command shim, but an
ephemeral npx run never installs it, and the service step never checks
> - The result is a crash-looping service, a doctor hint about a
nonexistent port conflict, and a first run that ends with nothing
serving
> - This pull request materializes the managed install before
registering the service, or declines with the repair path
> - The benefit is that saying yes to the service prompt yields a
working service — or an honest explanation

## Linked Issues or Issue Description

**What happened?**

On a machine with no managed install, `npx paperclipai@2026.824.0
onboard` (interactive), accepting the background-service prompt,
produced: a LaunchAgent pointing at `~/.local/bin/paperclipai` (which
does not exist), launchd exit code 78 in a KeepAlive crash loop, doctor
reporting "inactive but the configured port is serving another Paperclip
process — stop the conflicting foreground process" (no such process
existed), and "Service health: fetch failed". Reproduced twice on a
clean field. `latest` has carried this path since v2026.817.0 shipped;
CI never sees it because `--yes` onboarding skips the service prompt.

**Expected behavior**

Accepting the service prompt installs a working service (materializing
the managed payload and shim first when needed), and doctor diagnoses a
missing service binary as exactly that.

**Steps to reproduce**

On macOS with no `~/.local/bin/paperclipai`: `npx paperclipai@latest
onboard`, accept the service prompt, then `launchctl print
gui/$UID/ing.paperclip.paperclipai` (exit code 78, spawn scheduled) and
`paperclipai doctor`.

**Paperclip version or commit**

`2026.824.0` (path present since #10045).

## What Changed

- `cli/src/onboard-service.ts`: after the user opts in, an
`ensureServiceShim` step checks the service shim path. Missing +
managed-store location → run `installCommand` pinned to the onboarding
version (payload, shim, PATH block), then proceed. Missing + custom
`PAPERCLIP_SHIM_PATH`, or install failure → decline with `paperclipai
install` / `paperclipai service install` guidance and install nothing.
- `cli/src/checks/service-health-check.ts`: the runtime check diagnoses
a missing service binary with the install repair hint (instead of the
port-conflict hint); an inactive service with a healthy responder gets a
`warn` attributing the foreign process instead of a plain "Healthy"
pass.
- Tests: new cases for shim materialization ordering,
decline-on-failure, missing-binary diagnosis, and foreign-responder
attribution; existing fixtures updated to inject the new dependencies.

## Verification

- `vitest run` on both touched suites: 15 pass.
- `tsc --noEmit` error count identical to the master baseline (16
pre-existing, all in `server/`, none in changed files).
- The live failure was reproduced on macOS before the fix (twice, clean
field) and the mechanism confirmed in source: `install()` writes the
definition and bootstraps launchd only; `install-store` was previously
reachable solely from the `install`/`update` commands.

## Risks

- Low: the new path runs only when the user opts into the service and
the shim is absent. The managed install resolves the pinned onboarding
version from the public registry; on failure the flow declines exactly
as it does on unsupported platforms. `--yes` quickstarts, Docker, and
managed installs are untouched.

## Model Used

Claude Fable 5 (Claude Code)

## Pre-submission checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
2026-08-24 23:30:54 -07:00
Nicky Leach 38d8f37172
fix(build): enforce Node 24 across Paperclip (#11792)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip runs across the CLI, server, adapters, plugins, CI, and
container images.
> - These surfaces declared different Node.js versions from 20 through
24.
> - A newer `@types/node` major can expose APIs that the supported
runtime does not provide.
> - Node.js 20 is no longer a suitable project baseline, and Node.js 24
is the current LTS line.
> - This pull request sets Node.js 24.11.0 as one repository-wide
baseline, adds a drift check, and gives users actionable startup
guidance when their runtime is too old.
> - The benefit is one clear runtime contract for development, release,
installation, and published packages.

## Linked Issues or Issue Description

Refs #2734

Refs #11727

Refs #739

## What Changed

- Require Node.js 24.11.0 or newer in all 42 package manifests and
runtime checks.
- Use Node.js 24 in GitHub Actions, Docker images, smoke images, sandbox
setup, portable installs, and esbuild targets.
- Align every direct `@types/node` declaration on `^24.0.0`.
- Prevent Dependabot from opening major `@types/node` upgrades without a
matching runtime decision.
- Add `.nvmrc` and a CI policy check for Node version drift.
- Update ACP version gates, tests, and user documentation for the new
minimum.
- Print a non-blocking warning on CLI and server startup when Node is
unsupported, with remediation through a version manager or the
documented downloaded `install.sh` workflow.
- Deduplicate that warning when `paperclipai run` boots the CLI and
server in the same process.

## Verification

- `node scripts/check-node-version-policy.mjs`
- `node --check scripts/check-node-version-policy.mjs`
- `node --check cli/esbuild.config.mjs`
- `node --check scripts/generate-npm-package-json.mjs`
- `bash -n scripts/install.sh scripts/test-install-sh-docker.sh
scripts/e2e-install-lifecycle.sh`
- Parsed all 42 package manifests and confirmed `engines.node` is
`>=24.11.0`.
- `git diff --check`
- `vitest run
packages/adapter-utils/src/sandbox-install-command.test.ts` passed with
3 tests.
- `vitest run cli/src/node-version.test.ts` passed with 4 tests.
- Directly exercised the shared warning helper for unsupported-version
messaging and same-process deduplication.
- The focused exe.dev suite could not resolve the locally unbuilt plugin
SDK from this isolated worktree. A full offline workspace install was
also blocked because the package-manager signature verifier requires
registry access. The full suite was not run locally; draft CI performs a
clean install and evaluates the wider impact.

## Risks

- This is a breaking runtime change for users, plugins, and deployments
that still use Node.js 20 or 22.
- Published workspace packages will now produce an engine warning or
failure in strict package managers on older Node.js releases.
- Node.js 24 can reveal dependency, native module, Playwright, or agent
CLI compatibility issues in CI.
- The bootstrap installer now installs Node.js 24 when the current
runtime is older than 24.11.0.
- The portable sandbox fallback is pinned to Node.js 24.11.0 and depends
on that upstream tarball remaining available.
- Unsupported runtimes continue booting after a warning, so a later
incompatibility can still fail at its point of use.
- The CLI and server share the warning policy through the published
`@paperclipai/shared` package; packaging checks must keep that subpath
export available.
- This PR does not commit `pnpm-lock.yaml` because repository policy
assigns lockfile generation to CI.

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

## Model Used

- OpenAI Codex based on GPT-5. The exact deployment ID and context
window are not exposed in this session. Reasoning, repository tools,
shell execution, and GitHub tools were enabled.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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-21 10:17:52 -07:00
dependabot[bot] 3d6afde104
build(deps): bump @clack/prompts from 0.11.0 to 1.7.0 (#11724)
Bumps
[@clack/prompts](https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts)
from 0.11.0 to 1.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/bombshell-dev/clack/releases">@​clack/prompts's
releases</a>.</em></p>
<blockquote>
<h2><code>@​clack/prompts</code><a
href="https://github.com/1"><code>@​1</code></a>.7.0</h2>
<h3>Minor Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/bombshell-dev/clack/pull/574">#574</a>
<a
href="8f1c380683"><code>8f1c380</code></a>
Thanks <a
href="https://github.com/dreyfus92"><code>@​dreyfus92</code></a>! - Add
<code>showInstructions</code> option to <code>select</code>,
<code>multiselect</code>, and <code>groupMultiselect</code>. Keyboard
hints remain shown by default; pass <code>showInstructions: false</code>
to hide them.</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/582">#582</a>
<a
href="4b249539e2"><code>4b24953</code></a>
Thanks <a href="https://github.com/43081j"><code>@​43081j</code></a>! -
Handle empty arrays in various prompts and utilities.</p>
</li>
<li>
<p>Updated dependencies [<a
href="4b249539e2"><code>4b24953</code></a>]:</p>
<ul>
<li><code>@​clack/core</code><a
href="https://github.com/1"><code>@​1</code></a>.4.3</li>
</ul>
</li>
</ul>
<h2><code>@​clack/prompts</code><a
href="https://github.com/1"><code>@​1</code></a>.6.0</h2>
<h3>Minor Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/568">#568</a>
<a
href="f87933fb7b"><code>f87933f</code></a>
Thanks <a
href="https://github.com/florian-lefebvre"><code>@​florian-lefebvre</code></a>!
- Updates default formatter of <code>note()</code> to note dim lines
anymore</p>
<p>If you want the old behavior, provide a <code>format()</code>
function:</p>
<pre lang="diff"><code>import { note } from '@clack/prompts';
+import { styleText } from 'node:util';
<p>note(
'You can edit the file src/index.jsx',
'Next steps.'</p>
<ul>
<li>{ format: (text) =&gt; styleText('dim', text) }
);
</code></pre></li>
</ul>
</li>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/567">#567</a>
<a
href="cc6aab5018"><code>cc6aab5</code></a>
Thanks <a
href="https://github.com/dreyfus92"><code>@​dreyfus92</code></a>! - Add
keyboard instruction footers to <code>select</code>,
<code>multiselect</code>, and <code>groupMultiselect</code> in the
active state, matching autocomplete. No option — always shown.</p>
</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies [<a
href="2f2b52f77c"><code>2f2b52f</code></a>,
<a
href="e1b6ee71a7"><code>e1b6ee7</code></a>]:
<ul>
<li><code>@​clack/core</code><a
href="https://github.com/1"><code>@​1</code></a>.4.2</li>
</ul>
</li>
</ul>
<h2><code>@​clack/prompts</code><a
href="https://github.com/1"><code>@​1</code></a>.5.1</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/548">#548</a>
<a
href="2356e97c1f"><code>2356e97</code></a>
Thanks <a href="https://github.com/43081j"><code>@​43081j</code></a>! -
Remove sourcemaps and enable pretty-ish build output.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/546">#546</a>
<a
href="56e9d67077"><code>56e9d67</code></a>
Thanks <a
href="https://github.com/ghostdevv"><code>@​ghostdevv</code></a>! -
docs: add jsdoc for <code>date</code>, <code>limit-options</code>, and
<code>messages</code></p>
</li>
<li>
<p>Updated dependencies [<a
href="2356e97c1f"><code>2356e97</code></a>]:</p>
<ul>
<li><code>@​clack/core</code><a
href="https://github.com/1"><code>@​1</code></a>.4.1</li>
</ul>
</li>
</ul>
<h2><code>@​clack/prompts</code><a
href="https://github.com/1"><code>@​1</code></a>.5.0</h2>
<h3>Minor Changes</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/bombshell-dev/clack/blob/main/packages/prompts/CHANGELOG.md">@​clack/prompts's
changelog</a>.</em></p>
<blockquote>
<h2>1.7.0</h2>
<h3>Minor Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/bombshell-dev/clack/pull/574">#574</a>
<a
href="8f1c380683"><code>8f1c380</code></a>
Thanks <a
href="https://github.com/dreyfus92"><code>@​dreyfus92</code></a>! - Add
<code>showInstructions</code> option to <code>select</code>,
<code>multiselect</code>, and <code>groupMultiselect</code>. Keyboard
hints remain shown by default; pass <code>showInstructions: false</code>
to hide them.</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/582">#582</a>
<a
href="4b249539e2"><code>4b24953</code></a>
Thanks <a href="https://github.com/43081j"><code>@​43081j</code></a>! -
Handle empty arrays in various prompts and utilities.</p>
</li>
<li>
<p>Updated dependencies [<a
href="4b249539e2"><code>4b24953</code></a>]:</p>
<ul>
<li><code>@​clack/core</code><a
href="https://github.com/1"><code>@​1</code></a>.4.3</li>
</ul>
</li>
</ul>
<h2>1.6.0</h2>
<h3>Minor Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/568">#568</a>
<a
href="f87933fb7b"><code>f87933f</code></a>
Thanks <a
href="https://github.com/florian-lefebvre"><code>@​florian-lefebvre</code></a>!
- Updates default formatter of <code>note()</code> to note dim lines
anymore</p>
<p>If you want the old behavior, provide a <code>format()</code>
function:</p>
<pre lang="diff"><code>import { note } from '@clack/prompts';
+import { styleText } from 'node:util';
<p>note(
'You can edit the file src/index.jsx',
'Next steps.'</p>
<ul>
<li>{ format: (text) =&gt; styleText('dim', text) }
);
</code></pre></li>
</ul>
</li>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/567">#567</a>
<a
href="cc6aab5018"><code>cc6aab5</code></a>
Thanks <a
href="https://github.com/dreyfus92"><code>@​dreyfus92</code></a>! - Add
keyboard instruction footers to <code>select</code>,
<code>multiselect</code>, and <code>groupMultiselect</code> in the
active state, matching autocomplete. No option — always shown.</p>
</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies [<a
href="2f2b52f77c"><code>2f2b52f</code></a>,
<a
href="e1b6ee71a7"><code>e1b6ee7</code></a>]:
<ul>
<li><code>@​clack/core</code><a
href="https://github.com/1"><code>@​1</code></a>.4.2</li>
</ul>
</li>
</ul>
<h2>1.5.1</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/548">#548</a>
<a
href="2356e97c1f"><code>2356e97</code></a>
Thanks <a href="https://github.com/43081j"><code>@​43081j</code></a>! -
Remove sourcemaps and enable pretty-ish build output.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/546">#546</a>
<a
href="56e9d67077"><code>56e9d67</code></a>
Thanks <a
href="https://github.com/ghostdevv"><code>@​ghostdevv</code></a>! -
docs: add jsdoc for <code>date</code>, <code>limit-options</code>, and
<code>messages</code></p>
</li>
<li>
<p>Updated dependencies [<a
href="2356e97c1f"><code>2356e97</code></a>]:</p>
<ul>
<li><code>@​clack/core</code><a
href="https://github.com/1"><code>@​1</code></a>.4.1</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="dc5bce8aae"><code>dc5bce8</code></a>
[ci] release (<a
href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/575">#575</a>)</li>
<li><a
href="4b249539e2"><code>4b24953</code></a>
chore: enable strict index checks (<a
href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/582">#582</a>)</li>
<li><a
href="8f1c380683"><code>8f1c380</code></a>
feat(prompts): add showInstructions opt-out (<a
href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/574">#574</a>)</li>
<li><a
href="06c16c7b65"><code>06c16c7</code></a>
chore: update license copyright to Bombshell contributors (<a
href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/572">#572</a>)</li>
<li><a
href="0e700562df"><code>0e70056</code></a>
[ci] release (<a
href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/562">#562</a>)</li>
<li><a
href="f87933fb7b"><code>f87933f</code></a>
fix(prompts): do not dim note contents (<a
href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/568">#568</a>)</li>
<li><a
href="cc6aab5018"><code>cc6aab5</code></a>
feat(prompts): add instructions footer for <code>select</code>,
<code>multi-select</code> &amp; `group-...</li>
<li><a
href="02ae19193e"><code>02ae191</code></a>
[ci] release (<a
href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/549">#549</a>)</li>
<li><a
href="56e9d67077"><code>56e9d67</code></a>
docs: add jsdoc for <code>date</code>, <code>limit-options</code>, and
<code>messages</code> (<a
href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/546">#546</a>)</li>
<li><a
href="030ba4d776"><code>030ba4d</code></a>
[ci] release (<a
href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/539">#539</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/bombshell-dev/clack/commits/@clack/prompts@1.7.0/packages/prompts">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/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for <code>@​clack/prompts</code> since your current
version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@clack/prompts&package-manager=npm_and_yarn&previous-version=0.11.0&new-version=1.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

---------

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-20 15:57:20 -07:00
David V 233c12f029
feat: add kimi-local adapter for Kimi Code CLI (CLI + ACP engines) (#9967)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Local agent adapters (`claude_local`, `gemini_local`, `grok_local`,
…) are the integration surface that lets Paperclip run coding CLIs on
the host machine
> - The Kimi Code CLI (`kimi`, Moonshot AI) has a documented
non-interactive mode, `kimi -p --output-format stream-json` with session
resume via `kimi -r`, but Paperclip has no built-in adapter for it
> - So Kimi users (especially Kimi membership / OAuth subscribers)
cannot onboard their CLI to Paperclip agent teams
> - This pull request adds a complete built-in `kimi_local` adapter
(both execution engines, session management, instructions + skills
delivery, thinking-effort control, environment test, UI and CLI modules,
docs) following the established `gemini_local`/`grok_local` package
pattern
> - Kimi Code ships an ACP server (`kimi acp`), so the adapter runs on
Paperclip's shared acpx engine by default (streaming transcript with
live tool status, like `claude_local`/`gemini_local`) and falls back to
a headless CLI lane (`kimi -p --output-format stream-json`) when ACP
prerequisites are unavailable
> - The benefit is that Kimi Code becomes a first-class Paperclip agent
lane: selectable in the UI, resumable across heartbeats, with the same
operating context (instruction bundle, skills, effort) and streaming
transcript the other local adapters get

## Linked Issues or Issue Description

- Supersedes #9880 (same branch; expanded from the CLI-only lane into a
complete adapter with the default ACP engine lane, control-plane skill
install, and live transcript wiring)
- Refs #9879 (adapter request for Kimi Code CLI, filed with this PR)
- Refs #163 (original Kimi support request)

Duplicate/related prior PRs, per the dedup search (both appear stale: no
updates or maintainer review since May 2026, and both target an older
Kimi CLI interface; calling them out for reviewer context per
CONTRIBUTING.md):

- Refs #6276 (`feat: add kimi-local adapter`): targets an older
array-based content format (`{type: think}`/`{type: text}` blocks), not
the current documented stream-json schema
- Refs #5202 (`feat(adapter): add Kimi CLI local adapter with Wire
protocol support`): builds on a `--wire` JSON-RPC interface that current
Kimi Code CLI (0.27.0) no longer documents; the current documented
headless interface is `-p --output-format stream-json`

This PR is a fresh implementation against current master and the
currently documented/verified Kimi CLI behavior (see Verification).
Happy to fold in anything useful from the earlier attempts if a reviewer
prefers.

## What Changed

- **New adapter package** `packages/adapters/kimi-local`
(`@paperclipai/adapter-kimi-local`), modeled on
`gemini-local`/`grok-local`:
- `src/server/execute.ts`: spawns `kimi -p <prompt> --output-format
stream-json` (argv array, no shell), `-m <model>` only when configured,
`-r <sessionId>` when the stored session cwd matches the run cwd,
automatic fresh-session retry on unrecoverable-session errors,
headless-safe env (`CI=1`, `NO_COLOR=1`, `KIMI_CODE_NO_AUTO_UPDATE=1`,
`TERM=dumb`; user-configured values win), full remote (ssh/sandbox)
execution lane with runtime install via `@moonshot-ai/kimi-code`
- **Instruction bundle delivery**: the prompt path directive now names
the sibling instruction files (`./HEARTBEAT.md`, `./SOUL.md`,
`./TOOLS.md`) alongside the prepended entry file, and local runs pass
`--add-dir <instructions-dir>` so Kimi can actually open them (matching
`claude_local`). Without this, only the entry file reached Kimi and
agents improvised the operating workflow that `HEARTBEAT.md` documents
- **Thinking effort**: a configured `effort` is forwarded as the
`KIMI_MODEL_THINKING_EFFORT` operational override (Kimi has no
per-invocation effort flag). It is only sent for models that advertise
`support_efforts` (currently `kimi-code/k3`) to avoid provider
rejections, and `medium` maps to `high` since Kimi has no medium tier
(`low`/`high`/`max` pass through)
- **Skills delivery**: desired Paperclip skills are delivered via Kimi's
`--skills-dir` flag from a dedicated per-run directory (a local
snapshot, or the synced snapshot on remote targets), so skills load
reliably and in isolation. Paperclip never overwrites the shared
`$KIMI_CODE_HOME/skills` home, so skills installed by the operator or
other agents are left intact. `--skills-dir` is only passed when at
least one skill is desired, so unconfigured agents keep Kimi's default
skill discovery
- **Live run status**: the adapter now forwards each streamed
stream-json line to `onEvent` (assistant `content` as an assistant
snippet, `tool_calls` as tool-name events), which drives the
issue-thread activity indicator (`currentToolName` /
`lastAssistantSnippet` / `lastEventAt`). Previously the adapter only
wrote the raw run log, so the issue thread showed a stale "no output for
N s" line with no tool or reasoning context while Kimi worked. Tool
results are omitted so the last meaningful "Using X" / snippet is not
overwritten by a generic label
- `src/server/parse.ts`: parses the verified Kimi stream-json event
shapes (`assistant` text, `assistant.tool_calls` with JSON-string
arguments, `tool` results, trailing `meta.session.resume_hint` for
session-id capture) plus failure classifiers (`kimi_auth_required`,
transient network, unrecoverable session). A signaled exit (null exit
code, not a timeout) is now reported as a failure rather than coalesced
to success, and the error message names the terminating signal
- `src/server/skills.ts`: lists/syncs Paperclip skills for the adapter's
skill-management surface
- `src/server/test.ts`: environment test covering CLI resolution + `kimi
--version`, cwd check, auth detection (OAuth credential dirs, keyed
`[providers.*]` in config.toml, or the `KIMI_MODEL_NAME` +
`KIMI_MODEL_API_KEY` env pair), and a live hello probe
- `src/ui/` (stdout-line parser for transcripts, config builder) and
`src/cli/` (stream event formatter) modules
- Root metadata: three managed model aliases
(`kimi-code/kimi-for-coding`, `kimi-code/kimi-for-coding-highspeed`,
`kimi-code/k3`), effort-capable-model metadata (`EFFORT_CAPABLE_MODELS`,
effort mapping helpers), `agentConfigurationDoc`
- Tests: 101 tests across parse, execute (args building, resume gating,
retry, auth error code, timeout, signaled-exit failure, effort
forwarding/gating/mapping, `--add-dir` instructions directive,
`--skills-dir` gating, `onEvent` runtime-event forwarding), ACP engine
(engine resolution, acpx config build, node-version gate), ACP
transcript delegation, environment test, UI parse/build-config
- **ACP engine lane (default)** (`src/server/acp.ts` + shared
`adapter-utils/acpx-engine`): Kimi Code ships an ACP server (`kimi
acp`), so `kimi_local` now runs on Paperclip's shared acpx engine by
default, matching `claude_local`/`codex_local`/`gemini_local`. The
issue-thread transcript streams live (assistant text deltas, tool calls
with a `pending`->`completed` status lifecycle) instead of the CLI
lane's bursty complete-message output. Registered `kimi_local -> "kimi"`
in `ACPX_ADAPTER_AGENT_IDS` and resolved the built-in agent command to
`kimi acp`; `execute.ts` dispatches to the ACP executor first with an
automatic CLI fallback when ACP prerequisites fail (`engine=acp`
requires ACP, `engine=cli` pins the headless lane); `index.ts` falls
back to the shared acpx session codec; the UI/CLI delegate `acpx.*`
events to the shared acpx transcript parser and event formatter. The
headless CLI lane (above) remains as the fallback
- **Registration** (one entry each, mirroring existing adapters): server
adapter registry + `BUILTIN_ADAPTER_TYPES`, `AGENT_ADAPTER_TYPES`
(shared), UI adapter registry + display registry (`Kimi Code`, Moon
icon) + capabilities defaults, CLI adapter registry, `Dockerfile`
(package copy + `npm install --global @moonshot-ai/kimi-code@latest`),
`vitest.config.ts` workspace, `scripts/release-package-manifest.json`
- **Behavioral sets** mirroring `gemini_local` (Kimi resumes sessions
the same way): `GIT_SENSITIVE_LOCAL_ADAPTER_TYPES`,
`SESSIONED_LOCAL_ADAPTERS` (heartbeat + recovery),
`REMOTE_MANAGED_ADAPTERS`, ssh/sandbox execution-target allow-lists,
`ADAPTER_DEFAULT_RULES_BY_TYPE` (`timeoutSec: 0`, `graceSec: 15`), and
`LEGACY_SESSIONED_ADAPTER_TYPES` + `ADAPTER_SESSION_MANAGEMENT` in
adapter-utils
- **UI touch-points**: New Agent default-model branch, AgentConfigForm
command map (`kimi_local: "kimi"`) + model defaults + a Kimi-specific
thinking-effort option list (`Low`/`High`/`Max`, reflecting Kimi's tiers
rather than borrowing Claude's), OnboardingWizard (command map, model
default, `kimi login` / `KIMI_MODEL_NAME + KIMI_MODEL_API_KEY` auth
hints, manual-debug command line), InviteLanding enabled adapters
- **Control-plane skill install** (`cli/src/commands/client/agent.ts`):
`paperclipai agent local-cli` seeded the Paperclip control-plane skills
into `~/.codex/skills` and `~/.claude/skills` so Codex/Claude agents
auto-discover the API reference every run. Kimi had no equivalent
target, so `kimi_local` agents began each session without the
control-plane skill and rediscovered routes (e.g. the company-scoped
`POST /api/companies/{companyId}/issues`) by trial and error. Added
`~/.kimi-code/skills` (honoring `KIMI_CODE_HOME`) as a third install
target for parity. Independent of the per-run `--skills-dir` delivery,
which only applies to explicitly configured skills.
- **Docs**: `docs/adapters/kimi-local.md` (prerequisites, auth options,
config fields including `effort`, session resume, instruction bundle,
skills delivery, control-plane skill install) + a row in
`docs/adapters/overview.md`

Out of scope (deliberately): model profiles, built-in agent
`allowedAdapterTypes` additions.

## Verification\n\nCurrent-master rebase verification (OpenAI Codex,
2026-08-03): 13 focused files / 231 tests pass; adapter-utils, server,
UI, CLI, and Kimi adapter typechecks pass; full repository build and UI
token gates pass. The branch is conflict-free against master at head
`1249df117c5e12e5771b9a570a6340866450619e`.\n\nAutomated (all from repo
root, pnpm 9.15.4, Node 22):

- `vitest run packages/adapters/kimi-local`: 89/89 pass (includes
coverage for the instruction `--add-dir` directive, effort
forwarding/gating/mapping, `--skills-dir` gating, the signaled-exit
failure path, and `onEvent` runtime-event forwarding with cross-chunk
line buffering)
- `vitest run server/src/__tests__/adapter-registry.test.ts
server/src/__tests__/adapter-routes.test.ts
server/src/services/heartbeat-stop-metadata.test.ts
ui/src/adapters/adapter-display-registry.test.ts`: 37/37 pass
- `vitest run cli/src/__tests__/skills.test.ts`: 13/13 pass (the
control-plane skill install target follows the existing Codex/Claude
install path, whose symlink logic is unchanged)
- `vitest run packages/shared`: 307/307 pass; `vitest run
packages/adapter-utils`: pass except one pre-existing, unrelated failure
(`mcp-isolation.integration.test.ts` requires Claude CLI ≥ 2.1.207; host
has 2.1.185, fails identically on unmodified master)
- `pnpm --filter @paperclipai/adapter-kimi-local typecheck|build`, plus
typecheck of `server`, `ui`, `cli`, `adapter-utils`: all clean
- `pnpm install --frozen-lockfile`: passes (the PR diff itself contains
no lockfile changes, per repo policy; verified against a locally
regenerated lockfile)
- `node scripts/check-no-git-push.mjs` and `node
scripts/check-forbidden-tokens.mjs`: pass
- CI note: the `policy` job's release-bootstrap step is expected to stay
red until a maintainer bootstraps the first npm publish of
`@paperclipai/adapter-kimi-local`; see the CI Note for Maintainers
comment. All other contributor-actionable checks are green.

Manual end-to-end (real Kimi CLI 0.27.0, OAuth login, dev server on an
isolated instance):

1. Server `GET /api/adapters` lists `kimi_local` as builtin with correct
capability flags; models endpoint returns the three Kimi models
2. `POST .../adapters/kimi_local/test-environment`: all checks pass,
including a live `kimi -p` hello probe
3. Created a `kimi_local` agent and invoked two heartbeats: run 1
spawned `kimi -p ... --output-format stream-json`, Kimi used its `Read`
tool, produced the expected answer, and the session id was captured from
the `session.resume_hint` meta event; run 2 resumed the **same** Kimi
session (`sessionIdBefore == sessionIdAfter`) via `-r`
4. UI: adapter appears in the New Agent dropdown; selecting it shows the
Kimi command placeholder, the three models, and the Kimi config fields;
the run transcript renders Kimi tool calls via the adapter's stdout
parser

The instruction-bundle, thinking-effort, and `--skills-dir` changes
landed after the manual run above. They are covered by the unit tests
listed under Automated, and the Kimi CLI flags they rely on
(`--add-dir`, `--skills-dir`, `KIMI_MODEL_THINKING_EFFORT`) were
confirmed against the installed Kimi Code CLI 0.27.0 (`kimi --help`,
config-file thinking-effort docs).

Screenshots (assets branch on the fork, not part of the diff):

![Kimi Code in the Add a new agent runtime
picker](https://raw.githubusercontent.com/hawikk/paperclip/assets/kimi-local-pr-screenshots/shots/00-kimicode.png)

![Adapter dropdown with Kimi
Code](https://raw.githubusercontent.com/hawikk/paperclip/assets/kimi-local-pr-screenshots/shots/01-adapter-dropdown-kimi.png)

![Kimi adapter selected: command, model, config
fields](https://raw.githubusercontent.com/hawikk/paperclip/assets/kimi-local-pr-screenshots/shots/02-kimi-adapter-selected.png)

![Kimi models in the model
dropdown](https://raw.githubusercontent.com/hawikk/paperclip/assets/kimi-local-pr-screenshots/shots/03-kimi-model-dropdown.png)

![Successful resumed heartbeat run (kimi_local invocation + parsed
transcript)](https://raw.githubusercontent.com/hawikk/paperclip/assets/kimi-local-pr-screenshots/shots/04-successful-resumed-run.png)

![Agents list showing the Kimi Code
label](https://raw.githubusercontent.com/hawikk/paperclip/assets/kimi-local-pr-screenshots/shots/05-agents-list.png)

## Risks

- Low risk to existing behavior: the change is additive, one new
workspace package plus single-entry registrations alongside existing
adapters; no existing adapter code paths are modified.
- The adapter invokes the locally installed `kimi` CLI; like other local
adapters, run behavior depends on the host's Kimi version. The parser is
written against the documented/verified 0.27.0 stream-json schema and
degrades gracefully (malformed lines are skipped, failures surface as
run errors).
- `--skills-dir` overrides Kimi's auto-discovery of user and project
skills for the run. This is intentional (paperclip-managed agents get a
reproducible, isolated skill set), and it is only passed when at least
one Paperclip skill is desired, so unconfigured agents keep default
discovery.
- Thinking effort is only forwarded to models that advertise
`support_efforts` (currently `kimi-code/k3`); `EFFORT_CAPABLE_MODELS`
must be extended when more Kimi models gain support, otherwise a
configured effort is silently ignored for them.
- `Dockerfile` now installs `@moonshot-ai/kimi-code@latest` globally
alongside the other agent CLIs, so image size increases slightly.
- Maintainer action needed for the npm bootstrap gate: the `policy`
job's release-bootstrap step fails until the first npm publish of
`@paperclipai/adapter-kimi-local` (the gate from #5146 that every new
adapter package has passed through). Enrollment with `publishFromCi:
true` is required by the manifest validator (dropping the entry,
`false`, or `private` are all rejected), so this is intentionally left
to a maintainer. Remaining CI lanes are expected to run once it is done.

## Model Used\n\n- **Current-master rebase, conflict adaptation, and
registry-parity coverage:** OpenAI, **GPT-5 Codex** (Codex agent; exact
serving model ID and context-window size were not exposed to the
runtime), with repository, shell, Git, and GitHub tooling. It preserved
Hawik’s commit authorship, reconciled ACPX and environment-capability
changes, added current registry tests, and ran the verification
above.\n- **Adapter implementation and initial review:** Moonshot AI,
**Kimi K3 Coding** (latest), via **Kimi Code CLI v0.27.0**
(`kimi-code/k3` alias, 1M-token context window, thinking mode, agentic
tool use). The CLI agent explored the repo, wrote the adapter
implementation (delegated to a coder sub-agent of the same model), ran
tests, and drafted the first version of this PR body. A second
model-driven review pass (read-only, same model) audited the diff for
security/correctness before submission; its findings (shell-quoting
hardening, auth-detection false positive, session-compaction
registration, test gaps) were fixed and are included.
- **Harness-context fixes and review responses:** Anthropic, **Claude
Opus 4.8** (`claude-opus-4-8`) via Claude Code. Diagnosed from run logs
that Kimi received only the entry instructions file (not the
`HEARTBEAT.md`/`SOUL.md`/`TOOLS.md` bundle) and that `effort` was never
wired, then implemented the instruction `--add-dir` delivery,
`KIMI_MODEL_THINKING_EFFORT` forwarding, and `--skills-dir` skill
delivery, added the accompanying tests and docs, and addressed the
automated review comments (preserving external skills on remote sync,
treating a signaled exit as a failure). Also extended the `paperclipai
agent local-cli` installer to seed the control-plane skills into
`~/.kimi-code/skills` for Codex/Claude parity, wired `onEvent` runtime
events so the issue-thread activity indicator reflects Kimi's tool and
reasoning output live, and built the ACP engine lane (`kimi acp` via the
shared acpx engine, default) so the transcript streams with live tool
status like the other ACP adapters. The Kimi CLI flags, subcommand, and
env var relied on here were verified against the installed Kimi Code CLI
0.27.0.
- All CLI behaviors claimed here (`-p`, `--output-format stream-json`,
`-r` resume, event shapes, `--add-dir`, `--skills-dir`,
`KIMI_MODEL_THINKING_EFFORT`) were verified empirically against the
installed Kimi CLI, not assumed.

## 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 *(only the release-bootstrap step
remains red, pending the maintainer npm publish described in Risks)*
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
*(will address all Greptile comments as they arrive)*
- [x] I will address all Greptile and reviewer comments before
requesting merge





---

## Maintainer Addendum (2026-08-20)

The shared acpx-engine and issue-chat changes (run-summary segmentation,
placeholder tool-event coalescing,
`ISSUE_CHAT_TRANSCRIPT_MAX_VISIBLE_ENTRIES` 30 → 400, live-reasoning UI)
have been **extracted to #11761** so the cross-adapter behavior changes
review and revert independently — both commits there preserve @hawikk's
authorship. This PR is now the kimi-specific adapter only (60 files,
+3,793/−8, essentially pure addition); the only shared-engine touch left
is the `kimi acp` command resolution. `publishFromCi` is `true` — the
package name is bootstrapped on npm.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Dotta <bippadotta@protonmail.com>
Co-authored-by: Devin Foley <devin@paperclip.ing>
2026-08-20 12:06:33 -07:00
Nicky Leach 5a1ce7aed8
fix(server): stamp built commit into service.version (#11748)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The server emits OpenTelemetry spans so operators can trace agent
work
> - Each span needs a service version that identifies the code that
produced it
> - The current service version comes from a static environment value
and can become stale after a rebuild
> - This pull request records the built commit and resolves the service
version from the build stamp, runtime Git, the environment, or an
unknown fallback
> - The benefit is trace data that identifies the correct built commit
during development and deployment

## Linked Issues or Issue Description

**What happened?**

The server used a static `OTEL_SERVICE_VERSION` value for every
OpenTelemetry span. Rebuilds could produce traces with an old commit
value.

**Expected behavior**

The server should report the built commit when a build stamp exists. It
should use runtime Git, the environment value, or `unknown` as fallback.

**Steps to reproduce**

1. Set `OTEL_SERVICE_VERSION` to an old commit value.
2. Build the server at a different commit.
3. Start the server and inspect the OpenTelemetry service version.
4. Confirm that the built commit takes precedence over the old
environment value.

## What Changed

- Add a build script that writes the short Git commit to
`dist/build-info.json`.
- Resolve `service.version` from the build stamp, runtime Git, the
environment, or `unknown`.
- Log the resolved service version once during server startup.
- Add tests for the resolution order and safe behavior without Git.
- Document the resolution order in `doc/observability.md`.

## Verification

- `pnpm --filter @paperclipai/server build`
- `npx vitest run server/src/__tests__/service-version.test.ts`
- `pnpm --filter @paperclipai/server typecheck`
- Confirm that the build stamp contains the short commit.
- Confirm that the stamp wins over the environment value.
- Confirm that a build without Git exits successfully without a stamp.

## Risks

The server now prefers the built commit over `OTEL_SERVICE_VERSION`. A
build without Git uses the existing environment value or `unknown`. The
change needs no schema migration and has a single-commit rollback path.

## Model Used

OpenAI Codex, GPT-5, tool use and code execution. The runtime does not
expose the 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-19 21:20:40 -07:00
Dotta bd059a073d
fix(workspaces): make managed runtimes reliable across restarts (#11740)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Execution workspaces need isolated databases, ports, and runtime
services
> - Concurrent workspaces could reuse ports or lose service ownership
after a restart
> - A markerless worktree also needed seed recovery, but normal
markerless instances still needed to boot
> - This pull request makes seed, port, and service ownership state
explicit and recoverable
> - It also checks live process and listener identity before it reclaims
shared resources
> - The benefit is reliable workspace startup, restart, adoption, and
concurrent provisioning

## Linked Issues or Issue Description

**What happened?**

Managed workspaces could lose runtime service ownership after a
control-plane restart. Concurrent worktrees could also reuse a port when
their parent paths differed. A seed recovery change made every
markerless instance resolve a worktree seed source, so normal instances
without a source could not start.

**Expected behavior**

Paperclip must preserve healthy managed services across restarts. It
must reserve unique ports across worktree parents. It must provision a
registered markerless worktree, but it must skip seed work for a normal
markerless instance.

**Steps to reproduce**

1. Start two managed worktrees under different parent paths at the same
time.
2. Restart the control plane while a managed service stays alive.
3. Start Paperclip with a config that has no seed markers and no
registered worktree source.
4. Observe duplicate port selection, lost service adoption, or a
seed-source startup error.

**Paperclip version or commit**

Current `master` plus the workspace runtime reliability changes in this
pull request.

**Deployment mode**

Local development with managed execution workspaces and embedded
Postgres.

## What Changed

- Added a shared port registry with lease heartbeats, process identity
checks, and live listener probes.
- Reserved worktree ports across custom parent paths and repaired
duplicate legacy assignments.
- Preserved and adopted healthy managed services across control-plane
restarts.
- Reconciled guest bind modes and verified listener ownership before
termination or reuse.
- Provisioned registered markerless worktree databases and kept normal
markerless instance startup as a no-op.
- Added CLI, shared, server, and shell regression tests for seed, port,
listener, restart, and adoption behavior.
- Updated the worktree development documentation.

## Verification

- `pnpm exec vitest run cli/src/__tests__/worktree.test.ts
--reporter=verbose` — 63 tests passed.
- `pnpm exec vitest run
packages/shared/src/worktree-port-registry.test.ts --reporter=verbose` —
5 tests passed.
- Focused runtime Vitest set — 199 tests passed across 37 suites.
- `node --test scripts/__tests__/provision-worktree-self-heal.test.mjs`
— 10 tests passed.
- `git diff --check` passed.

## Risks

- Port reservation now depends on lease and process identity data. The
fallback listener probe prevents early reclamation when process metadata
is incomplete.
- Runtime adoption is stricter about bind and owner identity. The tests
cover healthy adoption, stale records, PID reuse, and unrelated
listeners.
- Markerless seed detection now separates registered worktrees from
normal instances. The tests cover both paths.
- There are no database schema migrations.

> 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 the `gpt-5` model family. The serving snapshot and
context-window size are not exposed. The agent used reasoning,
repository tools, code execution, and test execution.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Dev Agent <dev@paperclip.ing>
2026-08-19 14:55:16 -05:00
Dotta e1df4c6068
fix(workspaces): keep deferred seed databases reliable (#11706)
## Thinking Path

> - Paperclip manages agent work in isolated execution workspaces.
> - A workspace depends on a valid database seed before it can run.
> - Deferred seed failures were hidden behind a successful provision
status.
> - The seed restore also had two possible owners for the embedded
PostgreSQL process.
> - That allowed the target database to stop while the restore was still
running.
> - This pull request makes seed failures visible and gives the seed
process sole lifecycle ownership.
> - The benefit is that workspace provisioning reports the real result
and does not stop its own target database.

## Linked Issues or Issue Description

Related: #11684

**What happened?**

Initial worktree provisioning could report success before its deferred
database seed completed. The seed restore could also reuse a target
embedded PostgreSQL process with another shutdown owner. This could stop
the target database during the restore.

**Expected behavior**

Workspace status must show a failed deferred seed as a failure. The seed
restore must own the target embedded PostgreSQL process until restore,
migration, and validation finish.

**Steps to reproduce**

1. Provision a worktree with deferred database seeding.
2. Make the seed manifest end in a failed state while the command exits
with code 0.
3. Observe that the provision status remains successful on `master`.
4. Start a seed restore against an already-running target embedded
PostgreSQL process.
5. Observe that another lifecycle owner can stop the target during
restore.

**Paperclip version or commit**

`51a843e135`

**Deployment mode**

Local dev with execution workspaces and embedded PostgreSQL.

## What Changed

- Add a first-class `workspace_seed` operation for deferred database
seeds.
- Require terminal, verified seed evidence before the seed operation
succeeds.
- Surface the seed phase and failure metadata in workspace status and UI
state.
- Give the seed process exclusive lifecycle ownership of the target
embedded PostgreSQL process.
- Suppress imported embedded-Postgres exit hooks without removing
existing host listeners.
- Record a credential-safe shutdown diagnostic in failed seed manifests.

## Verification

- The original deferred-seed commit passed 4 server tests, 24
workspace-status UI tests, shared/server/UI typechecks, and the UI token
gate.
- The original PostgreSQL-lifecycle commit passed 3 lifecycle tests, 3
ownership/diagnostic tests, 1 real embedded-Postgres seed integration,
and the affected package typechecks.
- No local tests were rerun after the clean cherry-pick because the
operator requested the shortest landing path.
- Review the automatic PR checks for the clean `origin/master` replay.

## Risks

- A live target database now causes an early error instead of being
reused. The error includes recovery guidance.
- Workspace consumers must handle the new `workspace_seed` operation
type. Shared types and UI state handling are updated in this pull
request.

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

## Model Used

- OpenAI Codex, GPT-5, high-reasoning mode, with repository, shell, and
GitHub tool use. The runtime does not expose a more specific deployment
suffix or context-window value.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-19 10:41:29 -05:00
Dotta 51a843e135
fix(cli): accept renumbered migration journal order (#11684)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Worktree provisioning clones a source database into an isolated
workspace
> - Source validation must accept a migration journal that matches a
prefix of the checkout journal
> - Long-lived instances can apply migrations in a different order after
migration files are renumbered
> - The validator compared application order with filename order and
rejected a valid source
> - This pull request compares the resolved migration names as a set and
records the checkout-prefix revision
> - The benefit is that valid renumbered migration histories can pass
source validation without allowing divergent histories

## Linked Issues or Issue Description

**What happened?**

Worktree seed source validation compared applied migrations in database
application order with available migration files in filename order. A
current source with the same migration set failed with `Migration
journal is not a prefix` after migration files were renumbered.

**Expected behavior**

Source validation must accept a source when its resolved applied
migration set equals a prefix of the checkout migration files. It must
still reject a source that contains a resolved migration outside that
prefix.

**Steps to reproduce**

1. Apply migrations before a migration-file renumber operation.
2. Update the checkout so the same migration files have a different
filename order.
3. Run worktree seed source validation against the long-lived source.
4. Observe that positional comparison rejects the source even though the
sets are equal.

**Paperclip version or commit**

The bug reproduces on the master-equivalent worktree-seeding
implementation before this commit.

**Deployment mode**

Local dev with embedded PostgreSQL.

## What Changed

- Compare resolved applied migration names with the expected checkout
prefix as an order-independent set.
- Derive the reported source revision from the checkout prefix instead
of database application order.
- Add unit and embedded-PostgreSQL regressions for shuffled application
order, stale unresolved rows, lagging sources, and true divergence.

## Verification

- `pnpm exec vitest run cli/src/__tests__/worktree.test.ts` — 54 tests
passed.
- `pnpm --filter paperclipai typecheck` — passed.
- The focused suite includes the real embedded-PostgreSQL seed path.

## Risks

- Low risk. The change is limited to source migration-prefix validation.
- The validator still rejects missing or unknown resolved migrations.
- Duplicate resolved names remain set-equivalent by design. Raw stale
journal rows remain tolerated.
- Existing documentation already specifies order-independent
checkout-prefix behavior, so no documentation change is required.

> 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, exact model ID `gpt-5.6-sol`. The Codex runtime manages
the context window. The model used reasoning, shell 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-19 09:53:15 -05:00
Dotta a2bf936f9a
feat(workspaces): sign the workspace login handoff and gate readiness (#11671)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Managed worktree services run isolated Paperclip instances with
cloned databases.
> - A reachable service was reported as ready even when its database,
runtime identity, or login path was not usable.
> - The first candidate added verified database seeding and managed
repair in #11665.
> - This pull request consolidates that candidate with signed login
handoff and a complete readiness contract.
> - Post-QA fixes close five defects in repair identity, repair
responses, UI retry, seed journal handling, and seed-source trust.
> - The benefit is a workspace that either opens safely or reports one
accurate recovery action.

## Linked Issues or Issue Description

No public GitHub issue exists for this work, so the problem is described
here.

**What happened**

Managed workspace URLs could return HTTP 200 and report ready while
login failed. QA also found cases where repair used the wrong instance
identity, returned a generic error, left the UI stuck, rejected a safe
journal lag, or trusted a mutable workspace manifest.

**Expected behavior**

Opening a ready workspace signs the board user in to the correct
isolated instance. Provisioning and repair use a registered source and
report a structured recovery state.

**Actual behavior**

Entry depended on a password copied into the clone. Several failure
paths could publish stale readiness, hide the repair precondition, or
trust state that the workspace could modify.

**Additional context**

This pull request includes the commits first published in #11665. That
pull request keeps the original base head for review history. This
consolidated pull request is the merge candidate. Related open readiness
work includes #11575 and #11621.

## What Changed

- Adds a short-lived, signed, single-use login ticket. It binds the
user, workspace, instance, and runtime origin.
- Exchanges the ticket through Better Auth. It creates the session and
cookie through the supported adapter path.
- Adds protected workspace readiness fields for the database, clone
data, login handoff, seed phase, and runtime identity.
- Fails readiness closed when the guest has no company or
execution-workspace binding.
- Binds ticket issuance to the exact cloned user and active company
membership selected for the handoff.
- Verifies every current active board identity through the exact-user
handoff before publication or reuse.
- Gates managed runtime publication on the readiness contract and the
recorded worktree instance identity.
- Refreshes runtime work products from the live runtime row after a port
change.
- Adds one workspace access card with ready, degraded, repairing, and
failed states.
- Uses the runtime response identity for repair. It returns structured
repair precondition errors.
- Lets a valid source journal lag converge during provisioning.
- Binds seed and repair manifests to a source registered outside the
agent-writable worktree.
- Clears recovered UI errors so a successful retry can open the
workspace.
- Makes runtime tests register canonical sources and avoid ports owned
by live host listeners.
- Keeps Vitest on source suites when compiled `dist` trees exist.
- Isolates CLI and adapter tests from ambient AWS and runtime API
environment variables.
- Preserves a 404 response for cross-company workspace ID lookups before
runtime authorization.
- Makes concurrent single-flight coverage independent of
path-canonicalization scheduling order.

## Verification

The following checks passed on the integrated head:

```sh
pnpm -r typecheck
pnpm build
pnpm check:token-gates
pnpm --filter @paperclipai/db check:migrations
```

- The server source lane passed 420 files and 4,953 tests. Five tests
were skipped.
- The CLI lane passed 57 files and 385 tests.
- The database lane passed 26 files and 97 tests.
- The shared package passed 58 files and 506 tests.
- The adapter utility lane passed 640 tests. Four tests were skipped.
- The Claude adapter passed 220 tests. One test was skipped.
- The Codex adapter passed 323 tests.
- The OpenClaw adapter passed 13 tests.
- The OpenCode adapter passed 42 tests.
- The plugin SDK passed 45 tests.
- The workspace runtime suite passed 124 tests.
- The caller-scoped readiness and handoff suite passed 52 tests.
- The workspace provisioning shell suite passed 7 tests.
- The runtime exposure suite passed 17 tests while live host mappings
occupied fixed test ports.
- `git diff --check` passed and the worktree is clean.

The serialized route lane will run in GitHub CI with its normal shards.
No deployment or active-workspace migration was performed.

## Risks

- This is a medium-risk authentication and runtime-readiness change.
- The login ticket uses exact origin, workspace, instance, and user
binding. It has a short expiry and a one-time nonce.
- Runtime publication is stricter. A real readiness, identity, per-user
handoff, or control-plane database disagreement now blocks publication.
- This pull request supersedes #11665 as the merge candidate. Close
#11665 after this pull request merges.
- No new database migration is included. The lockfile and workflow files
are unchanged.
- Deployment and active-workspace migration are intentionally outside
this pull request.

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

## Model Used

Claude Opus 5 (`claude-opus-5[1m]`), 1M context, extended thinking, tool
use, and code execution produced the main candidate. OpenAI GPT-5
(`gpt-5`) through Codex, with agentic reasoning, tool use, and code
execution, integrated the post-QA fixes and hardened the test gates. The
Codex context-window size was not exposed.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [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-19 02:37:02 -05:00
Dotta 4b968d8c05
fix(worktrees): quarantine cloned runtime services (#11653)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip can create a worktree and seed it with data from another
instance.
> - A full seed copied active runtime state and live process claims into
the new database.
> - The copied state could make the new instance restart or adopt
services that belong to the source instance.
> - A stale process identifier, port, or URL could then make the
worktree page fail to load after a restart.
> - This pull request makes cloned runtime state inactive before the new
instance starts.
> - The benefit is that each instance starts with clear service
ownership and no stale live claims.

## Linked Issues or Issue Description

- [x] I searched open and closed issues and pull requests. I found no
duplicate report.

**What happened?**

`paperclipai worktree init --full` copied project and execution
workspace runtime records without changing their active state. The new
database could contain `running` desired state, `running` service state,
and provider references that identify processes from the source
instance. After a host restart, the cloned instance could try to recover
services that it did not own. The browser then showed a load failure at
a stale or moved service URL.

**Expected behavior**

A cloned database must not claim that source-instance runtime processes
are live. Project and execution workspace services must start as stopped
in the clone. An operator can start them explicitly after the clone is
ready.

**Steps to reproduce**

1. Start a managed worktree runtime in a source Paperclip instance.
2. Create a full worktree seed from that instance.
3. Start Paperclip with the seeded database.
4. Observe that the copied database can retain active desired state and
live process, port, and URL claims.

**Paperclip version or commit**

Reproduced on `master` at `d1cd9c37f4`.

**Deployment mode**

Local development with managed worktree services.

**Installation method**

Built from source with pnpm.

**Agent adapter(s) involved**

Not adapter-specific. This is a core worktree seed bug.

**Database mode**

External PostgreSQL source data copied into the isolated worktree
database.

**Access context**

Board operator.

**Privacy checklist**

I reviewed this description and removed private instance URLs, internal
task identifiers, credentials, and user paths.

## What Changed

- Stop cloned project and execution workspace runtime desired state
during a full or minimal seed.
- Change copied service states from `running` to `stopped`.
- Clear copied process, provider, port, URL, owner, and starter claims.
- Keep unrelated runtime metadata intact.
- Add regression tests for project services, execution workspace
services, unrelated metadata, and the live-work preservation option.
- Document runtime quarantine in the worktree development guide.

## Verification

- `pnpm exec vitest run cli/src/__tests__/worktree.test.ts` passes 43
tests.
- `pnpm -r typecheck` passes.
- `pnpm build` passes.
- `pnpm test:run` passes 4,304 tests. One unrelated timing test timed
out during the loaded run and passed alone. Ten unrelated exposure tests
could not use their fixed ports because host Tailscale listeners already
owned ports 42000 and 52000.
- A managed worktree restart completes with a healthy source runtime and
one authoritative service owner.
- An authenticated clone inspection shows the copied runtime as stopped
with no live provider, process, port, or URL claim.

## Risks

- A cloned service no longer starts only because the source service was
running. An operator must start the cloned service explicitly. This is
the intended ownership boundary.
- `--preserve-live-work` keeps the old behavior for operators who
explicitly request live runtime state.
- The change does not alter schema or source-instance runtime records.

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

## Model Used

OpenAI Codex with GPT-5. The runtime does not expose a more specific
deployment ID or context-window size. The model used high-reasoning
mode, repository tools, code execution, and GitHub review tools.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-18 17:43:08 -05:00
Guatu 2ae6fa51b1
fix: warn when a worktree-mode embedded-postgres data dir is in the OS temp dir (#8283)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work, and it can run as a self-hosted instance backed by an embedded
PostgreSQL database.
> - To let agents work in isolation, Paperclip supports worktree-local
instances, gated by `PAPERCLIP_IN_WORKTREE` / `PAPERCLIP_HOME` (see
`server/src/worktree-config.ts`, `cli/src/config/home.ts`).
> - Those worktree env vars can leak into a *primary* instance's
environment (inherited from an agent/worktree shell, or persisted into
the instance env file). When they do, `paperclipai run` resolves the
data root from `PAPERCLIP_HOME` and rewrites `config.json` to point the
DB/backups/logs/storage at `$PAPERCLIP_HOME/instances/<id>/…`.
> - If `$PAPERCLIP_HOME` is a throwaway dir under the OS temp dir, the
primary instance boots a brand-new **empty** database. Every login then
fails (`better-auth` logs `User not found`; the UI returns a generic
`401`), so it looks like a *password* problem while the real data sits
untouched in `~/.paperclip`. Nothing warns that the control-plane DB is
ephemeral.
> - This pull request makes that situation non-silent: the database
preflight check (also surfaced by `doctor`) emits a `warn` when a
worktree-mode instance's embedded-postgres data dir is inside the OS
temp directory, with clear remediation.
> - The benefit is that a confusing total lockout becomes an obvious,
actionable warning the operator sees at every `run` and `doctor`.

## Linked Issues or Issue Description

Refs #8282

Related PRs (not duplicates — complementary work on the same area):
- #3030 — *stop leaking server worktree env into unrelated local adapter
heartbeats* (tackles one **leak vector** of the same root cause; this PR
adds **detection** of the resulting bad state).
- #3899 — *fix(db): refuse side-started embedded migration instances*
(adjacent embedded-postgres safety hardening).

## What Changed

- `cli/src/checks/database-check.ts`: for `embedded-postgres` mode, emit
`status: "warn"` when the resolved data dir is inside `os.tmpdir()`
**and** `PAPERCLIP_IN_WORKTREE === "true"`. The message explains the
ephemerality + likely env leak; the repair hint says to unset
`PAPERCLIP_HOME` / `PAPERCLIP_IN_WORKTREE` (or pass `--data-dir`). Added
a small `isInsideOsTmpDir()` helper.
- Intentionally gated on worktree mode so deliberate ephemeral/CI
instances that use a temp data dir without `PAPERCLIP_IN_WORKTREE` are
**not** flagged.
- `cli/src/__tests__/database-check.test.ts` (new): covers pass
(persistent dir), warn (worktree-mode temp dir), and no-warn (temp dir
without worktree mode).

## Verification

```
# in cli/
pnpm exec vitest run src/__tests__/database-check.test.ts   # 3 passed
pnpm exec vitest run src/__tests__/doctor.test.ts           # passes (no regression)
pnpm exec tsc --noEmit                                       # no new errors in changed file
```

Manual repro of the underlying bug (no warning before this change):

```bash
PAPERCLIP_IN_WORKTREE=true PAPERCLIP_HOME="$(mktemp -d)/.paperclip-worktrees" paperclipai run
# -> boots an empty DB under /tmp; logins fail with "User not found".
# With this change, run/doctor now print a Database WARN pointing at the cause + fix.
```

Note for transparency: one unrelated test (`worktree.test.ts >
pauseSeededScheduledRoutines`) fails *locally only* because it shells
out to a real `pnpm install` that times out in a sandbox — it does not
touch `database-check`. Pre-existing `tsc` errors under
`server/src/services/plugin-*` (missing `@paperclipai/plugin-sdk` build
artifact) are also unrelated to this change.

## Risks

Low risk. Additive, non-fatal `warn` only — no behavior change to
startup or existing `pass`/`fail` paths, and gated on
`PAPERCLIP_IN_WORKTREE` so it does not fire for intentional ephemeral/CI
temp data dirs. A stricter follow-up (refuse to start the primary `run`
against a temp-dir data dir unless explicitly opted in) is possible but
intentionally out of scope here.

## Model Used

- **Provider / model:** Anthropic Claude — Opus 4.8
- **Exact model ID:** `claude-opus-4-8` (1M-context variant)
- **Context window:** 1M tokens
- **Reasoning mode:** extended thinking enabled
- **Capabilities used:** agentic tool use via Claude Code (repository
exploration, file edits, local shell, and running the vitest suite
locally before pushing)

The change was authored by @futhgar with this model as an assistant; the
diagnosis, fix, and tests were reviewed and verified locally.

## 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 run tests locally and they pass (new test + affected
`doctor` suite; see Verification for one unrelated, environment-only
failure)
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots (N/A — no UI change)
- [x] I have updated relevant documentation to reflect my changes (the
warning message + repair hint are self-documenting; no separate docs
change needed)
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green (pending CI run)
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(pending review)
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: futhgar <futhgar@users.noreply.github.com>
2026-08-17 14:02:09 -07:00
Nicky Leach fdb9a4880d
fix(security): route paperclipai CLI guidance through safe npx form (CWE-78) (#11400)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip provides CLI commands and guidance for operators and
agents
> - The `pnpm paperclipai` script can pass argument values through a
shell
> - Shell re-parsing can execute command substitutions inside quoted
values
> - This pull request routes guidance through inert-argv `npx
paperclipai` commands and adds regression coverage
> - The benefit is safer operator guidance across documentation and
runtime hints

## Linked Issues or Issue Description

This pull request fixes a command-injection-class defect in Paperclip
CLI guidance.

**What happened?**

The `pnpm paperclipai <sub> --flag "$VALUE"` form can re-parse argument
values through a shell. A command substitution inside a quoted value can
execute on the host.

**Expected behavior**

Paperclip guidance must pass CLI values as inert argument values.
Host-derived values must not appear in copyable commands.

**Steps to reproduce**

1. Run a Paperclip guidance command that uses the `pnpm paperclipai`
script.
2. Provide a quoted value that contains a command substitution.
3. Observe that the shell can evaluate the substitution before the CLI
starts.
4. Compare the result with the `npx paperclipai` form.

**Paperclip version or commit**

`5670984b75d109950c968542a0111ebb6967f4da`

**Deployment mode**

All deployment modes that show or use the affected CLI guidance.

**Installation method**

Built from source and installed CLI guidance.

**Agent adapter(s) involved**

Not adapter-specific (core bug).

**Database mode**

Not database-related.

**Access context**

Both.

**Additional context**

The earlier merged PR
[#11343](https://github.com/paperclipai/paperclip/pull/11343) used the
unsafe `pnpm exec paperclipai` form. This fresh PR replaces that
guidance with the safe `npx paperclipai` form.

## What Changed

- Standardize documentation and runtime hints on `npx paperclipai`.
- Remove the broken `pnpm exec paperclipai` guidance.
- Use a static `<host>` placeholder in private-hostname guidance.
- Add regression tests for unsafe forms, continued lines, static hosts,
and offline guidance.

## Verification

- `git diff --check
origin/master...origin/fix/paperclipai-cli-npx-safe-invocation` passes.
- The branch adds `server/src/__tests__/cli-invocation-safety.test.ts`
and updates private-hostname tests.
- CI must run the new tests, typecheck, lint, and build checks.
- Local Vitest execution was not available because this worktree has no
installed Vitest binary.

## Risks

- The change affects operator and agent documentation text.
- The runtime hints now show `<host>` instead of a request-derived host
value.
- No database schema or migration changes exist.
- CI will detect any missed unsafe invocation or type error.

## Model Used

OpenAI GPT-5, exact model ID `gpt-5`, with tool use and code-review
assistance. The model used repository inspection, Git operations, and PR
preparation.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-14 22:11:16 -07:00
Devin Foley 0a95ada1be
feat(server): chunked import preview endpoint and resumable upload in the Import page and CLI (#11224)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The previous pull request added server-side chunked resumable import
transfers; without clients, large imports still ride the single fragile
upload
> - The Import page and the CLI need to slice large packages, upload
parts with retry and progress, resume after interruptions, and preview
before applying
> - Preview is the missing server piece: the browser flow is
preview-then-import, so a completed spool must be previewable without
re-uploading
> - This pull request adds the transfer preview endpoint, switches the
Import page to the chunked path for zips over 48 MB, and teaches the CLI
the same for oversized local imports
> - The benefit is that large imports get progress, per-part retry, and
resume in both clients, while small imports keep the exact single-shot
path they have today

## Linked Issues or Issue Description

**What happened?**

With only the server transfer routes in place, users still upload large
company packages as one request from the Import page and the CLI: no
progress indication, no retry below the whole file, and no resume after
a dropped connection or refresh. The preview-then-import flow also
cannot run against an uploaded transfer, forcing a second full upload.

**Expected behavior**

A large package uploads once as verified parts with visible progress;
preview and import both run against the uploaded spool; an interrupted
upload resumes with only the missing parts re-sent; packages at or below
48 MB behave exactly as before.

**Steps to reproduce**

1. Select a 500 MB zip on the Import page over an unreliable connection.
2. Watch the single upload fail near the end and restart from zero,
twice — once for preview, once for import.
3. Same story headless via the CLI.

## What Changed

- Server: `POST /import/transfers/:id/preview` runs the existing preview
logic against the completed spool (shared assembly + whole-file
verification helper with apply); preview neither completes the run nor
deletes the spool, so the subsequent apply reuses it. Missing parts
respond with the missing list.
- UI: zips over 48 MB take the chunked path in both preview and import —
the file is sliced into 32 MB parts hashed with WebCrypto (single
ArrayBuffer, no second copy), the transfer is created or resumed (the
create response's missing-parts list drives what uploads), parts upload
sequentially with three attempts each and visible progress, then
transfer preview/apply replace the multipart calls. The existing preview
pane, collision handling, adapter overrides, and async job polling are
unchanged; ≤ 48 MB keeps the single-shot path.
- CLI: oversized local `.zip` or folder imports zip/slice/hash with node
crypto, upload with resume and per-part retry and progress lines, and
use transfer preview/apply. Small packages keep the inline path
byte-identical.
- Failure honesty: adapters/API errors fail open to existing behavior; a
part failing all attempts surfaces a durable error panel with resume
intact.

## Verification

- Server: preview-then-apply on one spool (run stays open, spool intact,
then apply completes), preview with missing parts rejected — added to
the transfer route suite (embedded Postgres).
- UI suite: large file takes the chunked path (manifest shape, part
uploads, progress, apply on a resumed transfer, single-shot endpoints
never called), small file stays single-shot, part failure after three
attempts surfaces the error panel without running preview, resume
re-uploads only the missing part.
- CLI: manifest slicing/hashing, threshold behavior for zip and folder
sources, folder-zip round-trip through the real zip reader, upload
resume/retry/exhaustion/already-completed, full-command chunked and
small-zip inline flows.
- Server, ui, cli typechecks clean. Exact counts in the PR checks.

## Risks

- The 48 MB threshold only routes between two verified paths; behavior
below it is untouched.
- Chunked CLI imports use the board-scoped transfer routes, so oversized
CLI imports need board credentials (agent tokens keep the agent-safe
small-file path). No privilege change — board actors already had the
generic routes — but the two size regimes differ semantically; called
out for review.
- A CLI dry-run over the threshold uploads parts before previewing; the
spool persists (24 h sweep) and a later apply resumes without re-upload
— inherent to preview-against-spool.

Stacked on #11223 — merge that first; this PR then shows only the
preview endpoint and client changes.

## Model Used

- Claude Fable 5 (`claude-fable-5`) via Claude Code CLI, with extended
thinking and tool use (multi-agent implementation with independent
verification).

## 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-11 16:06:37 -07:00
Devin Foley 1ea2f0e2d6
feat(cli): add 'paperclipai channels' to show release lanes and the current one (#11210)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The release channels (canary → nightly → beta → stable) select by
install target, and users discover them today only through
maintainer-oriented docs
> - A user who wants to know "which lane am I on, and what else is
there" has no self-serve answer
> - The channel rollout planned a read-only CLI command for exactly this
> - This pull request adds `paperclipai channels`: every lane with the
version its dist-tag resolves to, the install command for it, and which
lane the running install follows
> - The benefit is self-serve lane discovery without reading release
documentation

## Linked Issues or Issue Description

Refs #11008 — the user-facing discovery surface for the channel model
completed there.

**Subsystem affected**

CLI: `cli/src/commands/channels.ts` (new), `cli/src/index.ts`,
`doc/CHANNELS.md`, tests.

**Problem or motivation**

Channel selection is install-based (`@latest` / `@beta` / `@nightly` /
`@canary`), but nothing in the product tells a user which channel their
install follows or what the other lanes currently resolve to. The
information lives in `doc/CHANNELS.md` and the npm registry, neither of
which a running install surfaces.

**Proposed solution**

A read-only `paperclipai channels` command: prints each channel with the
version its dist-tag currently resolves to (per-lane registry lookups
that degrade to `unavailable` individually), the install command for
each, and the running install's lane parsed from its version suffix —
source checkouts carry the repository's placeholder version and are
reported as unmapped rather than guessed. `--json` emits the same data
for scripting.

## What Changed

- `cli/src/commands/channels.ts` (new): channel table, lane parsing,
registry resolution, human and `--json` output
- `cli/src/index.ts`: registers `channels`
- `doc/CHANNELS.md`: "Seeing where you are" section
- `cli/src/__tests__/channels.test.ts` (new): lane parsing including
unknown versions, full resolution against a fake runner, per-lane
degradation, table/dist-tag sync

## Verification

- `vitest run cli/src/__tests__/channels.test.ts`: 5 pass
- `pnpm typecheck` in `cli/`
- Live run against the real registry shows all four lanes with their
current versions (`2026.722.0` / `2026.811.0-beta.0` /
`2026.811.0-nightly.0` / canary) and correctly reports a source checkout
as unmapped

## Risks

- Low. Read-only command reusing the existing `resolvePublishedVersion`
registry helper; no state, no auth, no publish surface

## Model Used

Claude Fable 5 (`claude-fable-5`, Anthropic) in Claude Code, with
extended thinking and full tool use. All changes model-authored under
human direction.

## 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 (pending — will confirm before
merge)
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(pending — will confirm before merge)
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-11 08:47:32 -07:00
Dotta b18b0fc39b
feat: refine app connections and legacy worktree startup (#11040)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Apps UI manages app discovery and app connections.
> - The managed worktree runtime starts agent work in repository
worktrees.
> - The Apps routes do not match the main discovery flow, and the
connections view lacks a delete action.
> - Legacy managed worktrees can also start before their pending seed
operation runs.
> - This pull request makes app discovery the main Apps route and makes
connection management explicit.
> - It also seeds legacy managed worktrees before runtime startup and
makes the CLI read the repository-local config.
> - The benefit is a clearer Apps workflow and a safer managed-worktree
startup path.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

This improves the Apps navigation, app connection management, managed
git-worktree startup, and CLI worktree selection.

**Subsystem affected**

Cross-cutting. The change affects `ui/`, `server/`, `cli/`, and
development documentation.

**Current behavior**

The `/apps` route opens the connections list while discovery uses a
nested route. The connections list has no delete action. Some legacy
managed worktrees can start runtime work before their pending seed
operation runs. The CLI can also read an ambient Paperclip config
instead of the repository-local config.

**Proposed behavior**

The `/apps` route opens Browse, and `/apps/connections` opens the
connection list. Users can delete a connection after confirmation.
Runtime startup seeds legacy managed worktrees when required. The CLI
resolves the current worktree from the repository-local
`.paperclip/config.json` file.

**Reason and benefit**

Users can discover apps from the canonical Apps route and can manage
existing connections from a dedicated route. Legacy worktrees receive
their required repository content before agent runtime starts. CLI
worktree selection stays scoped to the current repository.

**Breaking changes**

The `/apps` and `/apps/browse` route behavior changes. Old Browse links
redirect to `/apps`. The change does not modify an API schema or
database schema.

## What Changed

- Make Browse the canonical `/apps` page and move the connection list to
`/apps/connections`.
- Align Apps navigation, redirects, attention links, empty states, and
connection actions with the new routes.
- Add connection deletion with confirmation and clear failure feedback.
- Seed legacy managed git worktrees before runtime startup when their
seed status is pending.
- Read the CLI worktree selection from the repository-local Paperclip
config.
- Update focused UI, server, CLI, and development documentation
coverage.

## Verification

- Ran 202 focused UI, server, and CLI tests. All tests passed.
- Ran `pnpm -r typecheck`. All projects passed.
- Ran `pnpm build`. All projects built successfully.
- Ran `pnpm test:run`. The server and UI stages passed 7,168 tests. The
CLI stage found one environment-sensitive secrets test because this
workspace injects static AWS credentials. The isolated CLI file passed
all 8 tests after those injected variables were unset.
- Ran `pnpm check:token-gates`. It reports 12 existing color-token
violations in the unchanged `PaperclipOrbit3D.tsx` file from the target
branch. This pull request does not modify that file.
- Ran focused regression coverage for repository-root CLI config
resolution and connection deletion state. All tests and affected package
typechecks passed.
- Collected all 27 tests in the six changed Playwright specifications
successfully.
- GitHub Actions passed every latest-head CI gate, including all three
e2e shards and the aggregate `e2e` and `verify` jobs.
- Greptile reviewed the final commit at 5/5 with zero unresolved
threads.

## Risks

- Existing bookmarks for `/apps/browse` redirect to `/apps`.
- Connection deletion changes visible connection state and requires user
confirmation.
- The legacy seed path runs only for managed git worktrees with pending
seed state. Tests cover the startup condition.
- The rebase preserves the target branch's direct OAuth policy for the
Notion connection flow.

> 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 the GPT-5 model family assisted this change. The agent
used reasoning, repository tools, code execution, and test execution.
The runtime does not expose the exact model snapshot or context-window
size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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-07 14:26:40 -05:00
Dotta 9485ffea70
fix(config): preserve env files during managed updates (#10980)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The CLI and server both update Paperclip values in `.env` files
> - The server preserved operator content, but the CLI rebuilt the
complete file
> - A CLI rerun could remove comments, custom values, ordering, and
newline style
> - Both paths need one editor with one value encoding and duplicate key
policy
> - The final integration also needs one regression test across the
related setup and sync safety mechanisms
> - This pull request moves the editor to the shared package and adds
cross-cutting rerun-survival coverage
> - The benefit is safe setup and worktree repair reruns that preserve
operator edits

## Linked Issues or Issue Description

**What happened?**

The CLI rebuilt the complete `.env` file when it wrote a managed
Paperclip value. This action removed comments, blank lines, custom keys,
original quoting, and the original newline style.

**Expected behavior**

Paperclip must update only the managed assignments. It must preserve all
unrelated bytes. It must skip the file replacement when all managed
values are current.

**Steps to reproduce**

1. Add comments, custom keys, quoted values, and CRLF newlines to the
Paperclip `.env` file.
2. Run a CLI path that calls the agent JWT secret setup.
3. Observe that the old writer replaces the complete file.

**Paperclip version or commit**

The problem exists on `master` before this pull request.

Related public context: Refs #437.

## What Changed

- Add one shared line-preserving `.env` editor for the CLI and server.
- Define minimal and JSON value encodings in the shared helper.
- Update every stale duplicate of a managed key and preserve current
duplicate encodings.
- Preserve comments, ordering, blank lines, unknown keys, export
prefixes, trailing comments, and newline style.
- Write changed files through a same-directory temporary file and atomic
rename.
- Limit CLI updates to non-empty `PAPERCLIP_*` entries.
- Skip the write when all managed values are current.
- Add shared, CLI, and server regression coverage.
- Refresh the branch after the related config, sandbox, and skill safety
changes landed.
- Add a cross-cutting integration test for config, env-file,
managed-sandbox, and managed-instructions rerun survival.

## Verification

- `pnpm exec vitest run packages/shared/src/env-file.test.ts
packages/shared/src/config-schema.test.ts
cli/src/__tests__/agent-jwt-env.test.ts
cli/src/__tests__/config-store.test.ts
server/src/__tests__/config-file.test.ts
server/src/__tests__/worktree-config.test.ts` passes 39 tests.
- `pnpm exec vitest run
server/src/__tests__/rerun-survival.integration.test.ts` passes 4 tests.
- `pnpm -r typecheck` passes on the previous head. GitHub CI reruns it
on the refreshed head.
- The previous head passed the complete general, serialized, workspace,
and E2E matrix. GitHub CI reruns that matrix on the refreshed head.
- `pnpm build` passes on the previous head. GitHub CI reruns it on the
refreshed head.

## Risks

- Low risk. The production change only affects managed `.env`
assignments.
- Existing managed assignments can keep their original quoting when
their decoded values are current.
- Changed CLI values keep the prior minimal encoding policy. Changed
server values keep the prior JSON encoding policy.
- Duplicate managed assignments now follow one explicit rule: Paperclip
updates each stale occurrence.
- The master refresh had one import-block conflict. The resolution keeps
both the config merge imports and the env-file imports.
- The added integration file is test-only. It has no database, API, or
UI contract effect.

> 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 from the GPT-5 family produced this change with reasoning,
tool use, and code execution. The runtime did not expose the exact model
ID or context window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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-07 00:54:43 -05:00
Dotta 5da382fd59
feat(skills): require explicit merge modes (#10978)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents can select company skills and synchronize them to adapter
runtimes
> - The skill sync API replaced the complete selection without an
explicit destructive choice
> - Company package import also replaced conflicting skills by default
> - These defaults could remove operator edits during setup and import
reruns
> - This pull request adds explicit assignment merge modes and safe
package conflict handling
> - The benefit is that reruns preserve operator work unless the caller
explicitly requests replacement

## Linked Issues or Issue Description

**What existing behavior does this improve?**

This change improves agent skill synchronization and company package
import.

**Subsystem affected**

This is a cross-cutting change across the shared contracts, server, CLI,
and UI.

**Current behavior**

Agent skill synchronization replaces the full desired skill set from a
modeless request. Package import replaces a conflicting skill when the
caller does not select a conflict mode.

**Proposed behavior**

Agent skill synchronization requires `add`, `remove`, or `replace`.
Package import skips conflicts by default. Each imported skill reports
whether it was created, renamed, replaced, or skipped.

**Reason and benefit**

Setup and import reruns must preserve operator edits by default.
Explicit destructive modes make data loss less likely and make each
outcome inspectable.

**Breaking changes**

Callers of the agent skill sync API must now send `mode`. Callers that
need the former behavior must send `replace`. Package import now uses
`skip` when `onConflict` is absent.

## What Changed

- Added required `add`, `remove`, and `replace` modes to the shared
agent skill sync contract.
- Added actionable `422` validation for missing or invalid modes.
- Updated first-party UI and CLI callers with explicit modes.
- Changed package skill conflict handling to use `skip` by default.
- Kept plugin-owned and built-in stock skill imports on explicit
`replace`.
- Added created, renamed, replaced, and skipped results to company
imports.
- Added regression coverage for merge modes and package conflict
outcomes.

## Verification

- `pnpm check:token-gates`
- `pnpm -r typecheck`
- `pnpm build`
- `pnpm test:run:serialized` (128 suites passed)
- `pnpm --filter @paperclipai/skills-catalog test` (20 tests passed)
- Focused agent skill route, company skill service, portability, CLI,
and UI tests passed.
- GitHub CI passed build, typecheck, canary, all general and serialized
test shards, all browser shards, policy, security, and final
verification on commit `2cfbb3e4c5`.
- Greptile reviewed the latest commit at 5/5 with zero unresolved
threads.

## Risks

- This change intentionally rejects modeless agent skill sync requests.
- The safe package default can leave an existing skill unchanged where
the old default overwrote it.
- All first-party callers now select a mode. Regression tests cover each
outcome.

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

## Model Used

- OpenAI `gpt-5.6-sol` through Codex. The runtime used agentic
reasoning, tool use, code execution, and repository editing. The runtime
did 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-07 00:42:08 -05:00
Dotta 35132af161
fix(config): preserve extensions and guard invalid repairs (#11005)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The CLI and server share a JSON configuration contract for local
installations and worktrees.
> - Existing config writes removed extension keys because Zod stripped
unknown object properties.
> - Invalid config files could also be replaced with defaults before an
operator preserved the original bytes.
> - Configuration updates must preserve operator edits and must not
rewrite files when the effective value is unchanged.
> - This pull request adds extension-preserving merges, guarded
invalid-config repair, atomic writes, and focused regression tests.
> - The benefit is safe setup and configuration reruns without data loss
or unnecessary mtime changes.

## Linked Issues or Issue Description

**What happened?**

Known-field updates through the CLI or server removed unknown top-level
and nested config keys. Non-interactive configure and onboard paths
could replace a present but invalid config with defaults.

**Expected behavior**

Writers preserve extension keys, skip semantic no-op writes, and require
explicit interactive confirmation before an invalid config is replaced.
Repair preserves an exact collision-safe backup first.

**Steps to reproduce**

1. Add an unknown top-level key and an unknown nested provider key to
`config.json`.
2. Update a known field through the CLI or worktree config writer.
3. Observe that the extension keys are removed on the base branch.
4. Write invalid JSON and run configure or onboard without an
interactive terminal.
5. Observe that the original file can be replaced without a durable
invalid-file backup on the base branch.

**Paperclip version or commit**

`master` at the pull request base commit.

## What Changed

- Accept unknown properties at each extensible config object boundary
while keeping every known field validated.
- Merge known-field updates into the parsed source config and preserve
only unknown extension data.
- Warn about near-match key names without deleting or changing them.
- Skip writes when the effective config is unchanged, which keeps file
mtimes stable.
- Write config changes through a temporary file, file sync, rename, and
directory sync.
- Distinguish a missing config from an invalid config in configure and
onboard.
- Back up invalid bytes as `config.json.invalid-N` and verify the source
still matches that backup before repair.
- Require interactive repair confirmation and reject non-interactive
replacement with an actionable message.
- Document the config preservation and repair behavior.

## Verification

- `pnpm exec vitest run packages/shared/src/config-schema.test.ts
cli/src/__tests__/config-store.test.ts
cli/src/__tests__/configure-repair.test.ts
cli/src/__tests__/configure.test.ts cli/src/__tests__/onboard.test.ts
server/src/__tests__/config-file.test.ts
server/src/__tests__/worktree-config.test.ts`
- `pnpm -r typecheck`
- `AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= VITEST_MAX_WORKERS=1 pnpm
test:run`
- `pnpm build`
- Confirm all pull request checks are green on the latest commit.
- Confirm Greptile reports 5/5 with no unresolved comments.

## Risks

- Passthrough keeps misspelled keys. Near-match warnings make this
visible without destructive cleanup.
- Merge behavior must distinguish unknown extension keys from optional
known keys. Schema-aware regression tests cover preservation and
known-key deletion.
- Repair must not overwrite bytes that changed after backup. The writer
compares the current source with the selected backup before atomic
replacement.
- The change does not alter database schema, company scoping, or
activity logging.

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

## Model Used

OpenAI Codex, GPT-5 model family. The exact deployment model ID and
context window are not exposed. 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/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-07 00:41:19 -05:00
Dotta ded813ad6f
feat(interactions): add governed agent addressees (#10252)
## Thinking Path

> - Paperclip is the control plane that lets humans govern companies of
AI agents.
> - Issue-thread interactions are the structured handoff point for
confirmations, questions, suggested tasks, and other governed decisions.
> - Those interactions previously assumed that only board users could
resolve them, preventing one agent from explicitly addressing another
agent for a response.
> - Agent resolution needs company-level governance, auditable resolver
identity, safe terminal-state handling, and attention routing so
authorization is enforced server-side rather than inferred from UI
behavior.
> - This pull request adds governed agent resolution, withdrawal and
terminal expiry semantics, explicit agent addressees, lifecycle
reconciliation, and attention-feed filtering.
> - The benefit is that agents can participate in structured decisions
without weakening board control, company isolation, wake behavior, or
audit invariants.

## Linked Issues or Issue Description

### Subsystem affected

Issue-thread interactions across database, shared contracts, server
authorization/services, adapter callbacks, agent skill guidance, API
docs, and UI governance surfaces.

### Problem or motivation

Structured interactions were board-only, had no explicit agent
addressee, and lacked durable withdrawal/terminal-expiry semantics. That
made peer-agent decisions impossible to authorize and audit safely.

### Proposed solution

Persist requested/effective resolver policy and addressee identity,
enforce company governance and eligible agent resolution, reconcile
addressee lifecycle changes, expose withdrawal and terminal expiry, and
route attention to the intended active agent with board fallback.

### Alternatives considered

Implicitly authorizing the issue assignee or mentioned agents was
rejected as ambiguous and difficult to audit. Using comments alone was
rejected because it loses structured outcomes and continuation behavior.

### Roadmap alignment

Supports the ROADMAP direction for lightweight leadership-agent
communication that still resolves into governed decisions and work
objects.

### Additional context

Public GitHub issue/PR search found no duplicate implementation; open PR
search for interaction resolver governance and agent addressees only
returned this PR.

## What Changed

- Add company-scoped interaction resolver governance contracts and
persistence.
- Add requested/effective resolver policy, resolver identity,
withdrawal, and terminal-expiry behavior.
- Add explicit `addresseeAgentId` validation, authorization,
persistence, lifecycle reconciliation, API documentation, and skill
guidance.
- Route pending addressed interactions to the intended invokable agent
and fall back to board attention when that agent becomes ineligible or
is deleted.
- Preserve sandbox callback identity fields required by governed
resolution paths.
- Add migrations `0193` and `0194` plus route, service, attention,
adapter, CLI, and UI coverage.
- Add governance state and company settings UI, including responsive
mobile behavior and distinct withdrawn/expired audit presentation.

## Verification

- `pnpm check:token-gates` — passed.
- `pnpm -r typecheck` — passed, including migration numbering and safety
checks.
- `pnpm test:run` — feature/server and UI workspace suites passed; one
unrelated CLI AWS doctor test observed injected static AWS credentials
and warned instead of passing.
- `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm exec vitest
run cli/src/__tests__/secrets.test.ts --project paperclipai` — 8 tests
passed, confirming the failure was environment-sensitive.
- `pnpm build` — passed.
- Latest rebased head `e24cece6be9f1877bdbac7691bcb44fd583c0161`
completed all GitHub CI jobs successfully.

## Risks

- Migrations add interaction and company-governance fields; numbering is
conflict-free on current `master`, additive statements are idempotent,
and migration safety checks pass.
- Agent authorization behavior expands beyond board-only resolution, but
defaults remain board-only and coverage exercises company boundaries,
resolver eligibility, lifecycle invalidation, wake behavior, withdrawal,
expiry, and attention fallback.
- Attention routing depends on current agent invokability;
reconciliation and read-time filtering prevent stale addressees from
retaining visibility or resolution authority.

> 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 using `gpt-5.6-sol` with reasoning, terminal tool use,
code execution, Git/GitHub integration, and Paperclip control-plane
tools. Context-window metadata was not reported 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 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>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 12:51:06 -05:00
Dotta dcac49a4fd
feat(workspaces): defer isolated setup until runtime start (#10653)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Isolated workspaces give each task a safe and reproducible checkout.
> - The existing setup cloned the development database before an agent
needed to run the app.
> - This made worktree creation slower and heavier for tasks that never
start a service.
> - Runtime services already use one server start path for heartbeat,
operator, and startup recovery flows.
> - This pull request moves heavy setup to that start path and keeps
worktree creation lean.
> - The benefit is faster isolated workspace creation with the same
reliable runtime setup when a service starts.

## Linked Issues or Issue Description

Related pull request: #10652 covers the initial deferred
database-seeding slice. This pull request supersedes it with end-to-end
runtime provisioning and safe cleanup.

**What existing behavior does this improve?**

This improves isolated worktree creation, runtime service startup, and
isolated instance cleanup.

**Subsystem affected**

Cross-cutting: CLI worktree setup, server runtime orchestration, shared
workspace contracts, and development scripts.

**Current behavior**

Paperclip seeds an isolated development database during worktree
creation. It can also leave an isolated instance directory after
workspace teardown. This work happens even when no runtime service
starts.

**Proposed behavior**

Paperclip creates the worktree with a lean eager setup. It runs an
idempotent runtime provision command before the first managed service
spawn. Concurrent starts share one provision attempt. Teardown removes
the isolated instance safely.

**Reason and benefit**

Many agent tasks only edit and test code. They do not need a running
Paperclip instance. Deferring the database seed reduces workspace
startup cost while preserving automatic setup for tasks that start the
app.

**Breaking changes**

None. The new runtime provision command is optional. Existing workspace
behavior is unchanged when it is absent.

## What Changed

- Split Paperclip worktree setup into a lean eager script and an
idempotent runtime provision script.
- Added `runtimeProvisionCommand` to project, issue, realized workspace,
and persisted workspace contracts.
- Added a per-workspace provision mutex before local service spawn for
heartbeat, operator, and startup recovery flows.
- Added a persisted `provisioning` service state and the
`workspace_runtime_provision` operation phase.
- Kept provision time outside the service readiness timeout and made
failed attempts visible and retryable.
- Reclaimed isolated instance data during safe workspace teardown.
- Serialized deferred database seeding across processes and bound
teardown to the instance root captured in persisted workspace metadata.
- Added tests for config flow, concurrency, retry, no-op behavior,
readiness timing, scripts, CLI commands, and cleanup.
- Documented the eager and runtime provisioning contracts.

## Verification

- `pnpm -r typecheck`
- `pnpm build`
- `pnpm test:run` (server: 3,201 passed; UI: 3,345 passed; the CLI phase
exposed one environment-sensitive AWS doctor assertion because the agent
runtime injects static AWS credentials)
- `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm exec vitest
run cli/src/__tests__/secrets.test.ts -t 'passes AWS doctor checks when
non-secret provider config is present'`
- Focused runtime tests cover serialized provisioning, retry after
stderr failure, absent-command no-op behavior, operation logging,
persisted state order, and readiness timeout exclusion.
- Focused CLI and cleanup tests cover concurrent seed serialization,
stale-lock fail-closed behavior, persisted instance ownership, and
rewritten sibling pointers.

## Risks

- A faulty runtime provision script blocks service startup. Paperclip
records stderr, marks the service failed, and retries on the next start.
- Concurrent service requests share an in-process provision attempt,
while the seed command uses an atomic filesystem lock across processes.
A stale lock fails closed and requires an operator to verify no seed is
running before removing it.
- Isolated instance cleanup is destructive. The cleanup service
validates ownership and path containment before removal.

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

## Model Used

- OpenAI Codex, `gpt-5.6-sol`, with agentic reasoning, tool use, and
code execution. The service 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>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-02 10:37:10 -05:00
Dotta 9c1f8e7887
feat(decisions): add first-class propose mode (#10010)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents can currently perform many mutations directly, while humans
often need a durable review point before cross-issue or destructive
actions occur
> - Existing approvals and issue-thread interactions do not provide a
standalone, reusable object for presenting options, collecting typed
inputs, detecting stale targets, and auditing effect execution
> - The control plane therefore needs a first-class propose mode that
separates an agent's recommendation from the governed mutation it may
cause
> - This pull request adds Decisions v1 across the database, shared
contracts, server execution and telemetry, agent skill guidance, and
operator UI
> - The benefit is that agents can propose multi-option actions safely
while operators get explicit provenance, fail-closed execution,
per-effect results, and a focused attention workflow

## Linked Issues or Issue Description

### Subsystem affected

Cross-cutting: `packages/db`, `packages/shared`, `server`, and `ui`.

### Problem or motivation

Agents need a governed way to propose consequential work without
immediately mutating issues, especially when one choice can affect
several issue trees. Existing approvals and issue-thread interactions do
not provide a standalone object with typed options, target snapshots,
effect-level authorization, expiration, execution outcomes, and reusable
attention-feed presentation.

### Proposed solution

Add first-class Decisions that store options and typed inputs, surface
open proposals in the operator attention feed, validate target freshness
and the origin-agent/operator authorization intersection at decision
time, execute a bounded set of auditable effects, and retain terminal
outcomes. Decisions v1 supports comments, status and assignee changes,
follow-up issue creation, blocker resolution, and issue-tree
cancellation, plus bundle grouping, expiration/dismissal, rule-key
telemetry, and agent-facing API guidance.

### Alternatives considered

- Extend approvals with arbitrary effects: rejected because approvals
represent governed yes/no actions and would become an unsafe generic
mutation envelope.
- Model every proposal as an issue-thread interaction: rejected because
decisions can span several targets and need independent lifecycle,
telemetry, idempotency, and effect results.
- Let agents perform the mutation and ask for retrospective review:
rejected because it removes the pre-execution governance boundary this
feature is meant to provide.

### Roadmap alignment

Aligns with `ROADMAP.md` sections **Agent Reviews and Approvals**,
**Enforced Outcomes**, **MCP Tool Gateway & Apps (governed tool
access)**, and **Activity History** by making explicit decisions,
authorization gates, auditable execution, and terminal outcomes
first-class control-plane objects.

### Additional context

This does not replace existing approvals or issue-thread interactions,
and it does not add an unrestricted generic mutation effect.

## What Changed

- Added company-scoped decision, option, target, and effect-execution
schema plus migration and shared TypeScript/Zod contracts.
- Added decision routes and services for propose, list/get, decide,
dismiss, cancel, target freshness checks, authorization intersection,
idempotency, activity logging, and execution auditing.
- Added rule-key decision telemetry and attention-feed metadata so open
decisions are visible and measurable.
- Added agent skill documentation for proposing and resolving decisions
through the Paperclip API.
- Added the Decisions UI: API client, query keys, inline attention
resolver, bundle grouping, target-issue strip, terminal history,
destructive confirmation, and per-effect result rendering.
- Added server service coverage, DecisionCard state tests, and Storybook
stories for the supported visual states.

## Verification

- `pnpm -r typecheck` — passed.
- `pnpm test:run` — 2,876 passed, 1 skipped, with one unrelated
cross-suite cleanup-order failure in
`heartbeat-responsible-user-invariant.test.ts`; the failing file passes
in isolation (`6/6`).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-responsible-user-invariant.test.ts` — passed.
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/DecisionCard.test.tsx` — passed (`9/9`).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/authz-existence-oracle-guard.test.ts
src/__tests__/openapi-routes.test.ts` — passed (`5/5`).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/decisions-service.test.ts` — passed (`16/16`).
- `pnpm --filter paperclipai exec vitest run
src/__tests__/company-import-export-e2e.test.ts` — passed (`1/1`).
- `pnpm --filter @paperclipai/server typecheck` and `pnpm --filter
paperclipai typecheck` — passed.
- `pnpm build` — passed.
- Rebased-head focused suite — passed (`6` files, `88` tests): shared
decision contracts, Decisions service, OpenAPI routes, startup feedback
export, DecisionCard states, and attention helpers. The follow-up
stale-secondary-target regression passes in the DecisionCard suite
(`10/10`).
- Rebased-head scoped typechecks — passed for `@paperclipai/shared`,
`@paperclipai/db`, `@paperclipai/server`, and `@paperclipai/ui`.
- Rebased-head migration numbering and safety checks — passed after
renumbering the additive migration to `0193` and making it replay-safe
for environments that applied the earlier feature-branch number.
- `pnpm check:token-gates` — passed with all gates clean.
- GitHub PR workflow and Greptile review for
`1f9f7645882d05dfdd9c99377c03a1f53f20e8be` — running after the
stale-secondary-target fix and PR metadata refresh on July 27, 2026.
- `pnpm --filter @paperclipai/ui build-storybook` exposes an existing
Storybook version mismatch (`storybook` 10.4.6 vs
`@storybook/addon-docs` 10.5.0); Decisions stories were validated with
the docs addon temporarily disabled and the tracked config remains
unchanged.

## Risks

- **Migration:** Adds replay-safe migration `0193`; migration numbering
and safety checks pass. The new tables and indexes are additive.
- **Authorization:** Effect execution intersects the proposing agent's
permissions with the responsible user context and fails closed; mistakes
could reject a valid proposal rather than silently over-authorize it.
- **Concurrency:** Target snapshots and idempotency keys protect against
stale or duplicate execution, but reviewers should focus on mixed-effect
partial outcomes and retry behavior.
- **UI:** Decisions are integrated into the existing attention feed
rather than a separate navigation surface, reducing routing risk but
increasing the importance of attention-item metadata compatibility.

> 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 CLI using `gpt-5.6-sol` for final PR preparation, review
fixes, and verification; repository tools and code execution were
enabled, and context-window size is not exposed in this runtime.
- Anthropic Claude Opus 4.8 with 1M context assisted with the Decisions
UI implementation, as recorded in the relevant commits.

## 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>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 19:17:02 -07:00
Dotta fc5a30805e
feat(cli): add managed install, update, and service lifecycle (#10045)
## Thinking Path

> - Paperclip is the open source control plane people use to manage
AI-agent companies
> - Operators need a predictable installation path that survives beyond
an ephemeral `npx` process
> - A durable installation needs an owned per-user payload store, stable
command shim, safe shell integration, and supported service lifecycle
> - Updates must preserve recoverability by backing up data, installing
side-by-side, verifying the new payload, and retaining rollback state
> - Bootstrap scripts and privileged service operations must fail closed
across download, filesystem, ownership, and consent boundaries
> - This pull request integrates managed install, update, rollback,
service, uninstall, doctor, bootstrap-installer, and runtime-serving
support into one workflow
> - The benefit is a recoverable, inspectable, and documented
installation lifecycle with explicit safety boundaries across Linux,
macOS, containers, WSL, npm, npx, and source checkouts

## Linked Issues or Issue Description

### Problem

Paperclip lacks a first-class durable installation and lifecycle
workflow. Operators currently have to assemble npm/npx installation,
PATH setup, background-service management, updates, rollback,
diagnostics, and uninstall behavior themselves. That makes upgrades
harder to recover, creates inconsistent behavior across platforms, and
leaves shell/download/service trust boundaries without one documented
implementation.

### Proposed Solution

Add a managed per-user install store and stable shim, a verified shell
bootstrap installer, service lifecycle commands, install-mode-aware
update/rollback behavior, doctor checks, and documentation. Managed
updates back up the database, install and smoke-test a side-by-side
payload, atomically switch `current`, and retain prior payloads. The
shell installer pins registry/download trust boundaries and requires
explicit consent for non-interactive privileged actions.

### Alternatives Considered

- Keep recommending `npx`: simple for evaluation, but ephemeral and
unsuitable for stable services, atomic updates, or rollback.
- Require global npm installation only: familiar, but cannot provide the
owned side-by-side payload store and retained rollback semantics.
- Split the capability across multiple PRs: rejected because install,
update, service, uninstall, bootstrap, and serving behavior share
contracts and security boundaries that need review together.

### Related Pull Requests

- Supersedes #10042 and #10044 with one integrated final diff.
- Incorporates and replaces the closed preparatory work in #10032 and
#10034.

## What Changed

- Added `paperclipai install`, `update`/`upgrade`, rollback, uninstall,
service lifecycle, onboarding integration, and managed-install doctor
checks.
- Added a private managed payload store, verified manifest/marker
ownership, exclusive mutation locks, atomic manifest/current/shim
writes, retained previous payloads, and provenance validation.
- Added npm and GitHub-ref install sources with exact target resolution,
registry isolation, database backup, side-by-side verification, atomic
activation, service restart coordination, and failure rollback.
- Made managed-update backups report actionable service-start and
`--no-backup` recovery guidance for unreachable databases, while clean
never-onboarded instances skip an empty backup.
- Added systemd user and launchd service definitions, status/health/log
commands, single-instance coordination, stale-port recovery, and
explicit sudo/lingering consent handling.
- Added the `scripts/install.sh` bootstrap path with checked two-stage
downloads, pinned public npm registry usage, platform checks,
dry-run/non-interactive controls, and Docker fixtures.
- Added embedded Postgres/native bootstrap integration,
hot-restart/systemd-notify serving support, passive update notices,
configuration contracts, README/CLI/install documentation, and focused
regression tests.
- Security re-review should explicitly re-verify: (1)
`addManagedPathBlock`/`removeManagedPathBlock` reject symlinked or
non-regular rc files, assert current-user ownership, preserve
restrictive modes, and replace atomically; (2) managed shim replacement
rejects unsafe parents, foreign-owned or multiply linked files, and uses
checked atomic replacement; (3) the shell installer and sudo path
preserve explicit consent and checked downloads; and (4) installed
service/runtime serving remains bound to the validated managed shim and
instance configuration.

## Verification

- `bash -n scripts/install.sh scripts/clean-install-git.sh
scripts/clean-install-npm.sh scripts/test-install-sh-docker.sh`
- `pnpm exec vitest run cli/src/__tests__/install-store.test.ts
cli/src/__tests__/install-command.test.ts
cli/src/__tests__/managed-install-check.test.ts
cli/src/__tests__/onboard-service.test.ts
cli/src/__tests__/service-health-check.test.ts
cli/src/__tests__/service-manager.test.ts
cli/src/__tests__/update-command.test.ts
cli/src/__tests__/update-notice.test.ts
packages/db/src/embedded-postgres-native.test.ts` — 9 files, 66 tests
passed
- `pnpm --dir cli typecheck`
- `pnpm --dir cli build`
- Follow-up verification: `pnpm exec vitest run
cli/src/__tests__/update-command.test.ts` (14/14), `pnpm --dir cli
typecheck`, `pnpm --dir cli build`, and `pnpm --filter
@paperclipai/server typecheck`.
- `pnpm -r typecheck`
- `pnpm build`
- Full `pnpm test:run` exercised all suites; an injected static AWS
credential changed one unrelated doctor expectation, which passed when
those credentials were removed. A second run cleared that case and
exposed stale pre-existing adapter-utils `dist` output; rebuilding
`@paperclipai/adapter-utils` made the isolated test pass. The updated PR
CI is the authoritative clean-workspace full-suite run.

## Risks

- Installer/update code writes executable shims, symlinks, shell rc
blocks, service definitions, and managed payloads; ownership,
regular-file, symlink, hard-link, marker, and path-containment checks
fail closed before destructive changes.
- The bootstrap installer executes downloaded tooling; downloads are
staged and checked before execution, npm traffic is pinned to the public
registry, and non-interactive privileged behavior requires explicit
consent.
- Linux lingering may invoke `sudo`; the command is surfaced and
confirmed before execution, and unsupported service managers fall back
to foreground-run guidance.
- Database migrations remain forward-only; payload rollback does not
reverse migrations, so managed updates create a backup before activation
unless explicitly disabled.
- Service restart and runtime serving touch process/port ownership;
lifecycle locks, health/version checks, and stable-shim service
definitions reduce split-brain and stale-process risk.

> 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 coding agents using GPT-5.5 and GPT-5.6-sol, with
reasoning, repository/API access, shell execution, and test tooling. The
runtime did not expose a reliable 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>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:52:23 -07:00
Devin Foley 5ec7ce76e5
Upload company import packages as compressed zip uploads (fix large-company imports through Cloud) (#10531)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Company Import (#10507, hardened in #10523) lets a user upload a
company package on the Import page
> - The page expanded the user's `.zip` into a files map and POSTed it
as ONE inline JSON body — ~40MB for a real company because attachment
blobs get base64-inflated
> - On Paperclip Cloud that body travels browser → harness proxy →
tenant, where it truncated in transit → body-parser 400 → the browser
saw "Failed to fetch", and nothing imported
> - Two compounding causes: the giant inline body itself, and the board
async opt-in riding an `x-paperclip-cloud-*` header that the Cloud
harness strips as anti-spoofing (so async never engaged and the import
held one fragile synchronous connection)
> - This pull request uploads the raw compressed `.zip` as a multipart
request (about a third the size, already compressed) parsed server-side
into the same bundle the importer consumes, and moves the async opt-in
to a proxy-safe `?async=1`
> - The benefit is that a large-company import actually completes
through Cloud: a small compressed upload, a real async job that survives
dropped connections

## Linked Issues or Issue Description

- Refs #10507 / #10523 (Import/Export and its hardening). No open issue;
problem described above (large-company browser import through a proxy:
inline JSON body truncates → 400 → "Failed to fetch"; async opt-in
header stripped by the front door → async never engages).

## What Changed

- **Multipart zip transport.** The Import page uploads the raw `File` as
`multipart/form-data` (field `package`, import options in a JSON `meta`
field); the server unzips it into `{ rootPath, files }` and runs the
exact existing preview/import logic. The `application/json` inline path
is byte-identical for CLI/programmatic callers. Bare `application/zip`
(meta via `?meta=`) is also accepted for programmatic use.
- **Shared node zip reader.** `packages/shared/src/portability-zip.ts`
(node-only subpath, not re-exported to the browser bundle — same pattern
as `portability-hash.ts`); the CLI's `zip.ts` becomes a thin re-export.
Identical codec (STORE + DEFLATE via `inflateRawSync`, rejects data
descriptors/zip64).
- **Proxy-safe async signal.** `wantsAsyncImport` = `?async=1` (board
browsers, survives the harness) OR the existing
`x-paperclip-cloud-async-import` header (cloud tenants, set
server-side). The UI async client now uses `?async=1`. Backward
compatible.
- **Size + preflight.** New `PORTABLE_ZIP_UPLOAD_LIMIT_BYTES = 128MB`;
the inline 56MB preflight no longer gates the zip path (it shows the
compressed size instead). Async submit/poll/resume, the duplicate-guard
fingerprint (now over the resolved bundle), pause-on-import,
progress/error panels, and activation all apply to the multipart path.
- OpenAPI documents json + multipart + zip bodies and the `async` query
param.

## Verification

- Full typecheck chain (shared, server, ui, cli) clean.
- 152 tests across 8 files: new `portability-zip.test.ts`
(STORE/DEFLATE/base64-blob byte-exact round-trip, truncation throws,
data-descriptor rejection); `company-portability-routes.test.ts` +7
(multipart import+preview equals the inline bundle; async multipart
202→poll→success; board async via `?async=1` with no cloud header;
cloud-tenant async via header; sync fallback with neither; truncated-zip
400, nothing imported); `CompanyImport.test.tsx` asserts the local zip
sends the raw File and the inline preflight no longer blocks;
`openapi-routes.test.ts` green.
- NOT yet measured: the end-to-end browser upload through the live Cloud
harness — verified on staging after deploy before closing out.

## Risks

- Import semantics unchanged — only transport changed; the JSON inline
path is byte-identical, the cloud-tenant header async path untouched.
Multipart parsing is server-side (memory-bound: a ~13MB zip → ~30MB
files map, fine on the server).
- The bare `application/zip` path is programmatic-only and covered by
content-type dispatch but not a dedicated route test (the multipart path
is).

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic), Claude Code CLI,
extended thinking + tool use; root-caused against live logs/DB and the
harness proxy source.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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-07-30 18:48:22 -07:00
Dotta 7cfb655f60
fix(worktree): disable automatic database backups (#10520)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip creates isolated instances for linked git worktrees so
development does not affect the primary instance
> - Those instances inherited the source instance's automatic
database-backup setting and also repaired older configs without
overriding it
> - As worktrees accumulated, each isolated instance could schedule its
own backup stream, producing redundant backup churn for disposable
database clones
> - This pull request makes backup disablement an invariant of worktree
config creation and repair
> - The benefit is that automatic backups remain focused on the durable
primary instance while isolated development instances stop accumulating
redundant backup files

## Linked Issues or Issue Description

No public GitHub issue exists for this bug, so the report is included
here. The closest related open change is Refs #10266, which hardens
where worktree config repair may write; this PR changes the backup
policy applied by that repair and by worktree initialization.

### What happened?

Isolated worktree instances copied `database.backup.enabled` from their
source config. When the source instance enabled automatic backups (the
normal default), every linked worktree also enabled a scheduled backup
stream. Existing worktree configs kept that state during startup repair,
so the redundant backups continued after the policy changed.

### Expected behavior

Automatic database backups are disabled for isolated worktree instances
created by `paperclipai worktree init` or `paperclipai worktree:make`,
and legacy worktree configs are migrated to that policy during normal
startup repair. The durable primary/default instance keeps its existing
backup behavior.

### Steps to reproduce

1. Start from a Paperclip instance whose database backup setting is
enabled.
2. Create or initialize a linked worktree with `paperclipai worktree
init`.
3. Inspect the generated worktree config and environment.
4. Before this change, the config retained `database.backup.enabled:
true` and the environment had no disabling override; after this change,
the config is false and `PAPERCLIP_DB_BACKUP_ENABLED=false` is
persisted.

### Paperclip version, deployment mode, and environment

- Reproduced against `master` before commit `ea5e0a0269`.
- Deployment mode: local trusted development with linked git worktrees
and embedded PostgreSQL.
- Environment: Node.js 22, pnpm workspace install.

## What Changed

- Always generate isolated worktree configs with automatic backups
disabled.
- Persist `PAPERCLIP_DB_BACKUP_ENABLED=false` in generated worktree
environments.
- Repair existing isolated worktree configs and environments that still
enable backups.
- Add CLI and server regression coverage for creation and legacy repair
paths.
- Document the worktree-specific backup policy and primary-instance
exception.

## Verification

- `pnpm exec vitest run cli/src/__tests__/worktree.test.ts
server/src/__tests__/worktree-config.test.ts` — 52 tests passed.
- `pnpm -r typecheck` — passed.
- `pnpm build` — passed.
- All repository commands above were run with inherited worktree runtime
identity variables removed.

## Risks

- Low operational risk: the change is limited to explicitly isolated
worktree instances.
- Operators who intentionally relied on automatic backups of disposable
worktree databases will now need to run a manual backup or explicitly
manage those files outside the scheduled worktree runtime.
- No schema, migration, API, UI, lockfile, or workflow changes.

> 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 runtime does not expose a more
specific snapshot ID or context-window value), using reasoning, tool
use, local code execution, and GitHub CLI integration.

## 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-07-30 17:35:04 -07:00
Devin Foley 916c13501f
Replace host-to-host Cloud Sync with full-fidelity company Import/Export (#10507)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - A company accumulates real state — issues, labels, blockers,
documents, work products, monitors, attachments, agents, routines — and
people need to move that state between instances: self-hosted to cloud,
cloud back to self-hosted, or plain backups
> - The experimental, flag-gated Cloud Sync transport (#6548) tried to
solve this host-to-host: the source pushed into a receiver over HTTPS
with a cross-instance consent/token handshake, which required the
destination to be publicly reachable and broke for common self-hosted
topologies (plain-HTTP LAN/VPN origins); the receiver half never landed
upstream at all
> - Meanwhile the portability bundle and the existing export/import
pages already move companies offline with none of those networking
constraints — but silently dropped labels, blockers, issue documents,
work products, monitors, and every attachment
> - This pull request removes the host-to-host transport and makes
Import/Export the single data-movement path: the pages become
first-class company-settings destinations, exports declare exactly what
they do not carry, and bundle schemaVersion 6 now carries all of the
above, with attachments as content-addressed sha256 blobs verified
before a single row is written
> - The benefit is a migration and backup flow that works between any
two instances with no reachability requirements, no cross-instance auth,
and no silent data loss

## Linked Issues or Issue Description

- Refs #6548 — the original Cloud Sync sender this PR supersedes and
removes.
- Related, not duplicates: #1697 (goals in the portability manifest —
orthogonal field addition), #954 (an earlier import/export +
skill-visibility proposal predating the current portability bundle).
- No open issue describes this directly, so in brief (feature-request
shape): **Problem** — moving a company between instances silently lost
labels (imports with label references actually hard-failed), blocker
relations, issue documents, work products, monitor state, and all
attachments, and the alternative Cloud Sync transport required the
destination to be publicly reachable over HTTPS plus a consent
handshake, which failed for typical self-hosted setups. **Desired
behavior** — one Import/Export flow in company settings that produces a
portable bundle carrying all of that data, tells the operator up front
what it cannot carry, imports with automations paused, and offers real
one-click activation afterwards.

## What Changed

- New export fidelity report (`GET
/api/companies/:companyId/export/fidelity`) + an "Export fidelity" panel
on the Export page listing anything a bundle will not include (now only:
approvals, cost history, activity history)
- Imports accept `pauseAutomations`; imported agents and routines land
paused, the import result reports created routines, and the Import page
ends in an activation panel that actually resumes selected
agents/activates routines
- Export and Import pages promoted into the company-settings nav; the
Cloud Upstream wizard, ux-lab page, and API client removed; the old
settings route redirects to Export
- Host-to-host transport removed: upstream-sync/receiver-client routes
and services, CLI `cloud connect`/`cloud push` + keypair store, the
shared upstream transfer contract, and the `enableCloudSync` flag;
migration `0196` drops the two experimental `cloud_upstream_*` sender
tables
- Bundle schemaVersion 6: labels (definitions + per-task names, remapped
by name on import), blocker relations (`blockedBy` slugs,
cycle-tolerant), issue documents (`tasks/<slug>/documents/<key>.md`),
work products (system refs nulled), monitors (notes/scheduledBy
restored, imported un-armed)
- Attachments travel as content-addressed `blobs/<sha256>` entries
(deduped; comment-scoped attachments re-link via comment index); every
blob is hash-verified **before any write**, so a corrupted bundle cannot
leave a partially imported company; both zip codecs now round-trip
extensionless/binary entries byte-exactly; the Import page preflights
the inline body limit and offers continue-without-attachments
- v5 (and older) bundles still import, with an informational warning;
bundles newer than v6 are rejected cleanly
- Docs: board-operator import/export guide, CLI README, README/ROADMAP
updated

## Verification

- `pnpm -r` typechecks (shared, db incl. migration numbering/safety
checks, server, ui, cli) and `pnpm check:token-gates` — clean
- Vitest: full server + shared sweep 4,888 passed / 1 skipped, with the
only 3 failures being pre-existing on `master` (2×
heartbeat-workspace-branch-containment, 1× workspace-runtime auto-port;
reproduced identically with this change stashed); ui + cli suites green;
the embedded-Postgres export-fidelity suite applies the full migration
chain including the new `0196` against a fresh database
- Live end-to-end on a scratch instance: seeded a company with labels, a
blocker pair, an issue document, a work product, a monitor, an agent, a
routine, and two binary attachments (one comment-scoped) → export →
import into a fresh company → labels remapped to new ids, blocker edge
and document restored, monitor un-armed with notes intact, attachments
byte-identical (sha256-compared through the API), agents/routines paused
→ activation panel resumed them; a v5-shaped bundle imported with only
the info warning; flipping one byte in a blob made the import 422 with
**zero** rows created
- Reviewer repro: create a company with a labeled issue + attachment →
Settings → Export → download → Settings → Import on another
company/instance → watch the preview, apply with "start paused", then
activate

## Risks

- Migration `0196` drops
`cloud_upstream_connections`/`cloud_upstream_runs` — experimental tables
behind a default-off flag; their connection/run history is intentionally
discarded
- Breaking removals are all of experimental, flag-gated surface:
`/api/upstream-sync/*` + `/api/cloud-upstreams/*` routes, `paperclipai
cloud connect|push`, and the `enableCloudSync` flag (stale keys in
stored instance settings parse harmlessly)
- Import remains non-atomic on mid-apply errors generally (pre-existing
behavior); the new blob verification specifically moved ahead of all
writes so tampered bundles cannot create partial state
- GitHub-sourced imports do not fetch `blobs/*` and skip attachments
with a warning

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic), via Claude Code CLI with
extended thinking, tool use, and subagent orchestration; implementation
and review split across Fable 5 subagents, with live end-to-end
verification against a running instance

## Checklist

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

- `company export` writes into a target directory but aborts
non-interactively when that directory is non-empty ("already contains
files. Re-run interactively or choose an empty directory"), and there is
no override flag. Any automated caller that exports into a pre-existing
directory (for example a git clone used as a backup target) is therefore
stuck.
- The interactive confirmation is the right default for humans, but
automation needs an explicit opt-out rather than being forced to export
into a throwaway empty directory and copy the tree afterward.
- A `--force` flag that skips only the non-empty-directory confirmation
is the minimal change: it does not alter what gets written (existing
files are overwritten in place, nothing is bulk-deleted), so callers
keep full control over cleanup via their own VCS.

## What Changed

- Added a `--force` option to `company export` that skips the non-empty
output-directory confirmation for non-interactive/automated runs.
- Threaded the flag into `confirmOverwriteExportDirectory(outDir, {
force })`; behavior is unchanged when the flag is absent.
- Updated the non-interactive error message to also mention `--force`.
- Added focused unit tests covering: missing dir (resolves), empty dir
(resolves), non-empty dir without force (throws), non-empty dir with
force (resolves), and a path that exists but is a file (throws).

## Verification

- `pnpm --filter @paperclipai/cli exec vitest run
src/__tests__/company-export-force.test.ts` → 5/5 pass.
- `tsc --noEmit` over the CLI sources: no new type errors (the only
errors are pre-existing `@paperclipai/plugin-sdk` module-not-found in
`server/` from an unbuilt plugin sdk in the sandbox, unrelated to this
change).
- End-to-end against a local server: exporting into a non-empty
directory fails without `--force` and succeeds with it; a `.git`
directory and a sentinel file in the target were preserved; 128 files
written.

## Risks

- Low. The flag is opt-in and defaults to false; interactive and
empty-directory behavior is untouched. `--force` overwrites matching
files in place but never deletes unrelated files, so it cannot silently
wipe a directory.

## Model Used

Claude Opus 4.8 (claude-opus-4-8)

---

**Problem or motivation**
`company export` aborts when the `--out` directory is non-empty and
stdin/stdout are not a TTY, and there is no override flag. This makes it
impossible to run `company export` unattended into a pre-existing
directory such as a git clone.

**Proposed solution**
Add a `--force` flag that skips the non-empty-directory confirmation for
non-interactive callers. Files are still written on top of existing
content with no bulk delete, so unrelated files such as `.git` are
preserved.

**Alternatives considered**
Exporting into a fresh temp directory and copying the tree into the real
target afterward works but is clumsy and error-prone for automation;
broadening or removing the guard entirely would remove a useful safety
net for interactive users.

**Roadmap alignment**
Hardens the automated/unattended export path used by scheduled
company-backup routines.

---

- [x] I searched the repository and open pull requests for similar or
duplicate PRs and found none.

Co-authored-by: anicca <annica@MichaelacStudio.localdomain>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-22 15:36:54 -07:00
Andrew Aymeloglu 98608dd71c
fix(cli): add missing company manifest fields to import selection test fixture (#9978)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The CLI's company import/export (portability) subsystem has a typed
manifest, `CompanyPortabilityCompanyManifestEntry`, that test fixtures
must satisfy
> - PR #2379 was authored in April and merged on 2026-07-21 without a
rebase; in the interim the manifest type gained five required fields
> - Its new test fixture predates those fields, so `tsc --noEmit` now
fails on master (TS2739), which fails the Release workflow's
`verify_canary / Typecheck` job and blocks canary publishing
> - This pull request adds the five missing fields to that one fixture,
matching the sibling fixtures in the same file
> - The benefit is a green typecheck on master, unblocking the Release
pipeline

## Linked Issues or Issue Description

No open issue exists; the problem is described here (bug path):

- **What happens:** `pnpm typecheck` fails on master at
`cli/src/__tests__/company.test.ts:744` with `error TS2739: Type '{...}'
is missing the following properties from type
'CompanyPortabilityCompanyManifestEntry': attachmentMaxBytes,
feedbackDataSharingEnabled, feedbackDataSharingConsentAt,
feedbackDataSharingConsentByUserId, feedbackDataSharingTermsVersion`.
See the failing Release run:
https://github.com/paperclipai/paperclip/actions/runs/29855224743/job/88718044096
- **Expected:** master typechecks cleanly and the Release workflow
publishes the canary
- **Cause:** semantic conflict — Refs #2379 (merged with pre-existing
green checks from April, before the manifest type gained the five
required fields)

## What Changed

- Added `attachmentMaxBytes: null`, `feedbackDataSharingEnabled: false`,
`feedbackDataSharingConsentAt: null`,
`feedbackDataSharingConsentByUserId: null`, and
`feedbackDataSharingTermsVersion: null` to the company manifest fixture
in the test "includes extension file even when all entities are
deselected" (`cli/src/__tests__/company.test.ts`), using the same values
and field order as the two sibling fixtures in the same file

## Verification

- `cd cli && pnpm typecheck` — fails on master with TS2739 at
`src/__tests__/company.test.ts:744`, passes with this change
- `cd cli && pnpm vitest run src/__tests__/company.test.ts` — all tests
pass (runtime behavior unchanged; the fixture only gains fields the code
under test does not read)
- CI: the `verify / Typecheck` job on this PR exercises the same gate
that is currently red on master

## Risks

- Low risk — a 5-line, test-only fixture change; no runtime code
touched. Worst case is a still-failing typecheck, which CI on this PR
verifies before merge.

## Model Used

- Claude Fable 5 (Anthropic, model ID `claude-fable-5`), via Claude Code
with extended thinking and tool use (GitHub CLI/API for investigation,
diff authored by the model)

## Checklist

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

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01T5W8yjizAewpLtmsBHKPuA

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 14:41:59 -05:00
Kevin Manase f4b65c122a
fix(cli): preserve extension file when import selections are cleared (#2379)
## Summary
- always include the extension file in interactive import selection when
it exists
- add a regression test for the all-deselected case

## Why
In the interactive company import flow, clearing every entity selection
also drops `.paperclip.yaml`, which makes configuration-only imports
impossible even when the extension file is present.

## Testing
- pnpm test:run cli/src/__tests__/company.test.ts
2026-07-21 10:58:28 -07:00