mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 16:27:18 +02:00
2cc79923691848fea68c2ed0b611a9ce2c00bfaa
2382
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2cc7992369 |
Share the external Runner lifecycle and add strict Muse contracts (#16444)
Add the external Runner foundation with distinct Dot/Grok V6, canonical home-mode V7 and Muse V8 contracts. Keep generated schemas and profiles synchronized. Bound default question-answer command IDs in a disjoint namespace while preserving existing IDs and durable replay. Preserve canonical migration history and add normally generated, replay-safe Muse schema milestones with literal preview-history fixtures. Refs #16444. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
4de6335c3b |
perf(server): trim saved run JSON from agent authentication (#16577)
## Thinking Path > - Paperclip coordinates AI agents that work for companies. > - Agent JWT authentication checks each request against its saved run. > - Saved runs can contain large prompts and result payloads. > - The first run lookup needs only identity fields, cancellation state, and the conversation flag. > - Reading all saved JSON transfers data that this lookup does not use. > - This PR selects the required JSON fields in PostgreSQL and preserves the existing authorization decisions. ## Linked Issues or Issue Description **What existing behavior does this improve?** The initial saved-run lookup in agent JWT authentication. **Subsystem affected** Server authentication and database queries. **Current behavior** The lookup transfers the full context and result JSON on each request. It uses only the cancellation state and conversation flag from those values. **Proposed behavior** Select the required JSON fields in PostgreSQL. Keep the run, company, and agent predicates. Read current state on every request. **Reason and benefit** A synthetic lookup with a 96 KiB prompt and 4 KiB result returns 102,520 JSON bytes before this change and 134 bytes after it. This reduces data transferred by this query. **Breaking changes** None intended. Identity fields, write revocation, read access, and cancellation response codes retain their current behavior. Related: #9280 addresses authentication round-trip counts. This PR only reduces the data returned by the existing initial run lookup. ## What Changed - Extract the initial run lookup and select only the JSON fields that authentication consumes. - Add real PostgreSQL tests for legacy JSON shapes, fresh cancellation state, payload size, and run/company/agent scope. - Add a reproducible benchmark that loads the original query from an explicit baseline commit. - Document the lookup and benchmark. ## Verification - Baseline: `06689c1b3410000bcd65ff40cb469e600209c4db`. - `pnpm -r typecheck`: passed. - `pnpm build`: passed. - Final server typecheck: passed. - Final-head GitHub checks: all green at `2be1c38e6bf51616b69993cf84cee1eef9657517`. Apex: 5/5, zero unresolved threads, conflict-free. - `pnpm check:module-boundaries`: passed. The standalone synthetic benchmark now uses the existing root benchmark directory and resolves Drizzle through the server workspace. - Focused HTTP middleware and database tests: 29 passed on real Homebrew PostgreSQL 16.14. The SQL test compares 270 combinations of status, cancellation JSON, and conversation JSON. It also checks fresh state and company/agent/run scope. - Broad native verification: 873 passed across the HTTP middleware, SQL projection, run diagnostics, and native executor suites. - The default local full command completed its general server lane with 12,090 passed and 8,885 skipped tests. It could not finish the root workflow: the host could not start the bundled PostgreSQL; a diagnostics assertion matched a directory name; and Git fixtures retained the host default branch. These failures remain in the verification record. All 68 failed files now pass on replay with disposable Homebrew PostgreSQL, an isolated checkout, and command-scoped Git defaults. The 68-file replay passed 2,588 tests and left two email skill-resolution failures. Moving the checkout outside an unrelated ancestor skills directory resolved those last failures: all 44 tests in that file passed. Product code and test assertions were unchanged for these host adjustments. Benchmark command: ```sh pnpm --filter @paperclipai/server exec tsx ../scripts/benchmark-agent-authentication.ts --baseline-ref=06689c1b3410000bcd65ff40cb469e600209c4db ``` The final benchmark ran at treatment commit `2be1c38e6bf51616b69993cf84cee1eef9657517` with a clean worktree. The local run used PostgreSQL 16.14 and the maintained test constructor seam to start disposable Homebrew clusters. The bundled PostgreSQL binary could not start on this host. Both versions use the same schema and predicates. Each case has two warmup rounds and ten measured rounds with alternating order. Random content prevents unrealistic compression. Complete authentication decisions match. | Context / result | Before JSON bytes | After JSON bytes | Before median | After median | | --- | ---: | ---: | ---: | ---: | | 1 KiB / 4 KiB | 5,240 | 134 | 0.157 ms | 0.157 ms | | 96 KiB / 4 KiB | 102,520 | 134 | 0.331 ms | 0.140 ms | These are local query round trips, including result transfer and decoding. The small control has no material latency gain. The large case improves by about 2.4x. Both plans read the same single row. The primary result is reduced response size. This does not measure HTTP latency, server CPU savings, or fleet-wide cost savings. Full samples and plans are retained with the verification evidence. ## Risks - JSON types must retain their meaning. The real database tests compare the consumed authorization contract with the original projection, including SQL null, JSON null, scalars, arrays, objects, string booleans, and cancellation states. - The change covers the first JWT run lookup. Later run identity capture keeps its existing full-run reads and transaction locks. Those reads remain a separate cost. - The change adds no schema migration or authorization cache. ## Model Used OpenAI GPT-6 through Codex, with reasoning, code editing, and shell tools. This session did not expose a more specific serving model ID or its 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> |
||
|
|
c91f7a6e5f |
perf(runner): overlap verified sandbox launch assets (#16585)
Native sandbox launch preparation stages fresh harness files and immutable runtime context in sequence. Overlap them only under the verified concurrent-sync capability, settle every started transfer before launch or cleanup, and preserve serial SSH behavior and error priority. A matched real-Daytona comparison reduces staging from 1,552 ms to 1,017 ms. The larger active-run difference includes model variation and is not attributed to this change. Add an explicit single-attempt diagnostic and synchronize stale Pi evidence metadata with its shipped profile. Validation: 764 native runner/tracing cases, 2,764 support cases, repository typecheck/build, fresh exact-head full CI/E2E/security and Apex 5/5 pass. Observability remains a silent no-op when no endpoint is configured. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
f5534717b0 |
fix(runtime): preserve unexported native work during lease cleanup (#16583)
Preserve the exact native workspace source until its export is finalized. Apply the same guard to cleanup sweeps and direct environment teardown. Keep stop-only recovery and healthy warm retention available. Verified with 317 targeted tests, server typecheck, module boundary check, all current-head CI gates, and Greptile 5/5. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
6f8ad3d77b |
Add verified session-local stdio MCP transport to Runner (#16526)
## Thinking Path > - Paperclip manages AI agent runs and their tool authority. > - Runner supplies provider sessions with assigned MCP connections. > - The current native connection binding supports an HTTP gateway only. > - Execution hosts also have private local tools with stdio MCP interfaces. > - Those tools need session-local registration and safe concurrency. > - This pull request adds verified local stdio assignments beside the existing gateway. > - Providers receive the existing tool schemas and image content. ## Linked Issues or Issue Description **Problem or motivation** Runner can supply a native provider session with an HTTP MCP gateway, but cannot also assign a verified local stdio MCP server without persistent provider configuration. Execution hosts need a session-local transport for local MCP tools, coordination for calls sharing a resource, and native schema/image delivery. The pinned native Codex client drops MCP images when `structuredContent` is also present. **Proposed solution** Add general stdio MCP transport support using host-assigned executable digests, arguments, environment and a private Unix socket identifying the shared resource. Tool implementations supply their own schemas and responses; Runner forwards their native MCP schemas and content. Register the transport for the current provider session. Serialize calls across processes, keep unknown outcomes fenced, and authorize assigned native Codex tools without a new prompt while routing explicit tool confirmations through existing PRP permission requests. Retain the HTTP gateway and applicable permission policy. **Alternatives considered** Reimplementing tools or translating their actions into provider-specific APIs would tie the transport to individual tool implementations. Global provider config would leak session assignments. Shell commands alone do not deliver native MCP images or preserve MCP approval requests. **Roadmap alignment** This completes the existing Runner transport boundary. Related: #10690 (operator config for the legacy ACPX engine), #11605 (gateway delivery of installed stdio connections), #16486 (preconfigured provider homes). This change assigns local tools on the execution host and leaves those administration paths intact. ## What Changed - Add reserved, verified stdio bindings for native Codex and through the shared ACPX runtime. ACPX clients must support stdio MCP assignments. Read-only sessions omit the binding. No home/project config is rewritten. - Run the backend with only its assigned environment through the verified executable launch path. - Serialize all calls sharing a private Unix socket across sessions. Metadata remains responsive. Queued cancellation prevents execution; active calls remain fenced until acknowledgement. Unknown outcomes require a resource restart. - Authorize host-assigned native Codex tools without a new per-call prompt, matching existing execution-host shell utilities. Preserve explicit tool deny/prompt rules. Route requested MCP tool confirmations through one-time PRP approvals, bound to the active thread and turn. Keep shell escalation disabled and existing tool deny rules effective. ACPX adapters retain their applicable ACPX permission policy. - Preserve schemas and image blocks. For native Codex 0.160/0.162, retain structured metadata as MCP text on image-bearing results to avoid the client's image-loss bug. ACPX adapters receive the original response. - Document transport authority, cancellation and recovery limits. ## Verification - `pnpm -r typecheck` and `pnpm build` passed with pnpm 9.15.4 and Rust 1.97.1 on macOS arm64. - Before Apex, full `paperclip-runner-core` Rust tests passed. After the fixes, all 379 library tests and eight subprocess tests using a generic stdio MCP fixture pass. Those subprocess tests cover schema/image passthrough, assigned-only environment, executable drift, call serialization, cancellation and unknown outcomes. The native Codex suite passed 91 tests with two intentionally ignored; `receipt_limit_rejects_the_call_and_keeps_polling_when_interrupt_fails` timed out waiting for `turn.interrupted`, including an isolated retry. The unchanged pre-fix commit `80917b301a2309f7966a466cf0c198a2112be2d5` reproduces the same timeout in isolation; this local native-suite run is not claimed as fully passing. - All 252 native controller tests pass against the final narrowed approval branch. Focused TypeScript transport/permission suites passed (298 tests); final binding, read-only host, policy and adapter checks passed (105 tests). - **Provider-client smoke coverage:** pinned Codex 0.160.0 through native Runner and Claude SDK 0.3.286 used local stdio MCP servers and loopback model API fixtures without live model credentials. These clients define the smoke-test coverage; this is not an exhaustive adapter compatibility test. Image responses reached the next model request as typed image blocks. Native Codex checks also verified unattended execution without approval requests, explicit per-tool prompt denial without backend execution, disabled-tool rules, and stale-turn approval rejection. - Two Greptile concurrency findings were reproduced with failing subprocess tests and fixed: pending cancellation under output backpressure, and a ctime-based fence reset after chmod. Initial checks exposed Codex CLI option-scope and key-quoting errors, which were corrected. The existing ordinary elicitation regression also exposed an overly broad request branch; the final branch handles explicit tool approvals only. - `pnpm test:run` was attempted. The broad local server suite hit persistent embedded PostgreSQL startup failures and unrelated local Git fixture failures; it was stopped with the failure log retained. Those local results are not claimed as passing. Hosted Linux CI gates passed on the reviewed runtime commit, as recorded below. - First Apex review found three additional defects, now fixed with regressions: keep the resource-lock socket snapshot instead of sampling the socket twice; project native permission requests with durable turn/item IDs while retaining provider IDs in details; validate provider response conversion before marking a request as settling. The retry regression failed before the fix. Focused Codex request/adapter checks pass (23 tests), server permission projection passes (16 tests), and all 379 Runner library tests pass. The broader database-backed bridge suite was attempted but its local PostgreSQL setup timed out; 29 tests did not execute. This local failure is retained, not claimed as passing. - Runtime acceptance before the documentation clarification, on `bd4fccae001157beb5bc76ff17c1920a5db3105a`: standard Greptile 5/5, then Apex 5/5 with no unresolved threads; all 60 checks complete successfully or intentionally skip. Ten CI jobs were interrupted by the same runner shutdown, including one stale job that required cancellation before retry; the same-commit retry passed. Hosted Rust tests also pass, including the native suite with the local baseline timeout noted above. - `git diff --check`, map validation, and pre-push secret/PII scan passed. ## Risks - No live model account or deployment isolation qualification was run. The loopback fixture proves the actual vendor-client request format, not model interpretation. - Stdio MCP support uses the current host-assignment contract: each binding requires a verified executable and a private Unix socket identifying the serialized resource. Other platforms retain the HTTP path. No provider version was upgraded. - A lost acknowledgement blocks subsequent calls until the host restarts the assigned resource. Direct shell calls are outside this MCP fence and retain the existing shell policy. - Preconfigured Claude remains unattended and denies interactive approval requests. Host assignment authorizes native Codex local tools; it must be supplied only for sessions already authorized to use the resource. ## Model Used OpenAI Codex, GPT-6, with reasoning and repository/tool execution. The session does not expose a more specific model identifier 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 - [ ] 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> |
||
|
|
ce0eff7d1f |
perf: reduce fresh workspace clone and sandbox staging waits (#16487)
## Thinking Path > - Paperclip manages AI agents and their work. > - Each run needs a private workspace and safe persistence. > - Fresh isolated repository sources download upstream Git history again. > - Sandbox staging also verifies an optional seed cache before it uploads the workspace. > - That verification holds a company lock during a full extraction and hash scan. > - This pull request reuses pristine Git objects through an authenticated remote clone. > - It also overlaps seed verification with upload and moves verification outside the publication lock. > - The benefit is less repeated transfer and less serial preparation work, with the same task isolation and recovery contract. ## Linked Issues or Issue Description **What existing behavior does this improve?** Fresh task-private repository preparation and inbound sandbox workspace staging. **Current behavior** Each isolated remote source clones the full upstream history. Optional seed verification then extracts and hashes another full tree before upload. One workspace's verification can hold the company cache lock while other workspaces wait. **Proposed behavior** Use a company and credential scoped cache of pristine upstream objects for GitHub HTTPS sources. Each task still clones from the current remote. It disconnects from the cache before publication. Run seed verification alongside upload. Hold the company lock only for final capacity checks and publication. **Reason and benefit** These operations lie on the workspace preparation path. Removing repeated Git pack transfer and serial verification can reduce startup time. Local measurements below cover these boundaries. They do not establish hosted agent latency. Related: #16425, #16454, #16398, #16461, #16468, #16473. The cache inspection and durable folder proposal in #13376 covers separate product work. ## What Changed - Add a pristine upstream Git object cache for fresh isolated GitHub HTTPS sources. Key it by company, repository and current credential fingerprint. Do not store credentials. - Keep online clone authorization and current remote refs. Use `--reference-if-able --dissociate` so task objects remain independent. A reference failure retries once online without the cache and resolves credentials again. - Cap cache storage at 64 entries and 4 GiB per company, with 2 GiB per entry. Skip contended publication. Bound priming by a cooperative five-second budget and a 4,096-entry scan allowance. Keep the existing free-space reserve. - Overlap optional seed verification and required workspace upload. Settle both before returning or deleting temporary files. Recheck quotas and company directory identity under the publication lock. - Keep the independent fresh upstream reproducibility proof, task commits, dirty source copies, ignored files, nested repositories and durable recovery behavior. - Suppress optional Git server-info generation during dissociation so generated storage passes the existing safe-expiration checks. This uses [the supported Git setting](https://git-scm.com/docs/git-config/2.43.0#Documentation/git-config.txt-repackupdateServerInfo). - Add bounded timing phases, regression coverage and two repeatable local benchmarks. No first-party Telemetry or dependency changes. ## Verification - `pnpm -r typecheck`: passed. - `pnpm build`: passed. - Affected adapter suites: 240 tests passed. Native workspace persistence/history and tracing: 33 tests passed. Server checkout/isolation/tracing batch: 53 tests passed. Final object-cache authority, path and capacity suite: 12 tests passed. These batches overlap. - Cache and expiration/restoration lifecycle batch: 34 tests passed. A direct generated-storage check covers cached clones. - Real HTTP Git fixture: a matching cache does not bypass revoked remote access. Other fixtures cover current refs/tags, hidden cached commits, independent objects after cache removal, corruption fallback, credential/company scope, quota limits and publication contention. - Initial restricted runs hit SQLite write and Unix socket permission failures. The adapter batch passed with disposable writable instance state and socket access. The first typecheck rejected the test use of `Promise.withResolvers`; typed deferred gates now use the supported library contract. - Repository benchmark on final head `9047368bf3b7fa2061e00d698b8b51cf4ae1da32`: five alternating samples, real local `file://` Git transport, synthetic 32 MiB history, no injected latency. Median 1143.8 ms to 240.5 ms; ranges 1085.7–1486.6 ms and 188.0–254.8 ms. Received Git pack bytes fell from 33,569,788 to zero. Initial cache priming took 1208.4 ms and is reported separately. - Sandbox benchmark, five alternating samples per mode on 10,000 files with real packing, extraction and verification: with a controlled 3-second transfer delay, median 9.450 s to 7.620 s (ranges 9.280–12.115 s and 7.296–7.983 s). With no injected delay, median 6.808 s to 6.450 s (ranges 6.255–7.029 s and 5.709–6.902 s). The no-delay ranges overlap; local copying competes with verification for disk. Both modes verify final tree contents, ignored-file exclusion and Git HEAD. These are synthetic local fixtures, not hosted agent runs. - Reproduce with `pnpm --filter @paperclipai/server exec tsx ../scripts/benchmark-repository-object-cache.ts` and `pnpm --filter @paperclipai/server exec tsx ../scripts/benchmark-workspace-staging.ts --baseline-ref=307eb16c0c --transfer-delay-ms=3000`. - Prior head `af766bc820` received Greptile 5/5 with no findings. Its E2E shard 7 failed in `plan-task-fixture` cleanup: company deletion raced an `activity_log` foreign key. That fixture has no provider run or Git workspace. The failure is retained here; final-head CI must pass. The corrected corruption fixture now forces a pack and verifies a real damaged archive. Current head `9047368bf3b7fa2061e00d698b8b51cf4ae1da32` has Greptile 5/5 with no review threads or findings. Final head has 55 passing checks and two optional Storybook skips. [The first workspace-shard attempt](https://github.com/paperclipai/paperclip/actions/runs/38802441857/job/116655485350) passed all 1,778 cases, then reported a late `EPIPE` from unchanged `server-utils.ts` while the ACP reuse test was active. That case mocks workspace preparation and passed locally in isolation. [Its single retry on the same commit](https://github.com/paperclipai/paperclip/actions/runs/38802441857/job/116659728034) passed, as did the dependent verification gate. The earlier E2E cleanup fixture passed on this final head. Both initial failures remain recorded here. A full local suite was not duplicated; the affected boundaries were run locally and the complete suite runs in CI. ## Risks - First cache population adds local copying. Cold clones, credential rotation, capacity exhaustion and a corrupt cache retain the online clone path. No eviction is added. Cached data is bounded, but many companies can each consume their allowance. - Individual filesystem calls and cleanup can exceed the cooperative priming budget. Failed reference clones make one additional online attempt. - Parallel verification and upload compete for disk and CPU. The local no-delay benchmark shows a smaller gain than a slow-transfer case. Verification still checks contents before cache publication, and required upload failures remain failures. - Local fixtures do not prove hosted throughput or the 15–30-second end-to-end target. The fresh checkout still writes its working tree, and model startup, copyback and other preparation costs remain. ## Model Used OpenAI Codex, GPT-6 family. The exact serving model ID and context window are not exposed in this session. Used reasoning, source editing, shell tools 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 and contains no internal task identifiers - [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> |
||
|
|
647116dd4b |
fix(adapter-utils): allow sandbox agents to read run diagnostics (#16575)
## Thinking Path > - Paperclip manages agent work and its execution history. > - Sandbox agents use a callback bridge to reach the control plane. > - The API already provides authorized run diagnostics. > - The bridge denied those read routes before the API could check access. > - Health scans could see task activity but could not inspect failed runs. > - This pull request admits exact GET routes on both bridge transports. > - Linked task results also apply the caller's task-read filter. > - Agents can inspect failures through their existing run identity. ## Linked Issues or Issue Description **What happened?** A sandbox health scan received `403 Route not allowed` for run history, run details, events, and task recovery records. The same agent could read task activity and connected provider state. The bridge stopped these diagnostic requests before they reached the API. **Expected behavior** The bridge should admit the existing diagnostic GET routes. The API must still enforce company and task access and redact run content. Execution and recovery writes must stay denied by these rules. **Steps to reproduce** 1. Start an agent in a sandbox with the normal callback bridge. 2. Use its injected API credential to request company run history. 3. Request a run's details or events. 4. Observe the bridge's route denial before the API handles the request. **Paperclip version or commit** Reproduced with the callback route list on master at `9f4a956b906`. **Deployment mode** Authenticated server with a managed sandbox. Both queue and HTTP/2 transports use this route list. Related work: Refs #15854 adds separate cost-read routes to this list. It does not add run diagnostics. Searches found no duplicate run-diagnostic change. ## What Changed - Admit nine existing diagnostic GET paths: company history and live runs; run details, events, logs, workspace operations and task links; task runs and recovery records. - Reject other methods, extra path segments, execution control, recovery resolution, and provider-trace administration. - Filter linked tasks by the caller's task-read predicate in both activity and original-task queries. Keep those results in the run's company. A readable run must not expose private task titles. - Test both route lists and the HTTP/2 request path. Preserve query parameters and controller denial responses. Add a database regression with public and private tasks linked to a readable company run. - Document bounded diagnostic reads, private-run stubs, access checks, and credential handling in the agent API reference. Update both affected run access maps with the sandbox entry points and verification limits. ## Verification - Passed both bridge suites: 139 tests. - Passed the server run-route, activity-route, and secret-redaction suites: 117 tests, including the new task-link filter test. The cold route-import test needed a 30-second timeout on this host. - Passed `pnpm -r typecheck`, `pnpm build`, the adapter-utils typecheck, both generated capability checks, and `node scripts/check-maps.mjs`. - Attempted `pnpm test:run`. The bundled PostgreSQL binary cannot start on this host. The native session suite reported 15 database startup failures; the privacy suite skipped its 15 cases. Stopped that full local attempt after those failures. No full local-suite pass is claimed. The final 16-case privacy suite also skipped locally for the same host limitation; Linux CI ran all 16 cases successfully. - One unchanged queue-response test hit a file-read race during the combined local run. A separate rerun of both complete bridge suites passed all 139 cases. The transient race was not independently isolated; the original failure is retained in this record. - All CI gates pass on head `97c65cec459d3fc2787083d05ae6ec51e3fd7b70`. Linux CI passed all 16 database-backed run privacy cases, including the new public/private task-link regression. These also cover private-run denial, authorized owner/reviewer reads, redacted list stubs, credential diagnostics, and deleted-task handling: [serialized shard 1](https://github.com/paperclipai/paperclip/actions/runs/38925932547/job/117057907621). - Apex reviewed the final head at 5/5. Both findings are addressed and their review threads are resolved. - After deployment, run a new sandbox audit. Confirm it can list and inspect authorized failed runs without a board credential. This has not been deployed or live-tested by this PR. ## Risks - Sandbox agents can reach more diagnostic GET routes. The existing server access checks and redaction still apply. - Linked task results now omit tasks that the caller cannot read. Authorized task links remain visible. - This change does not grant execution control or recover a failed workspace. - Existing bridge sessions may need a new run to load the updated route list. - No schema, credential, UI, workflow, or lockfile change. ## Model Used OpenAI Codex, based on GPT-6, assisted with implementation and verification. The exact serving model ID and context-window limit are not exposed in this session. Capabilities used: code editing, shell 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> |
||
|
|
06689c1b34 |
perf(skills): coalesce inventory before run cache locking (#16576)
Share each active company skill-inventory refresh before taking the version-cache lock. Read actor visibility under the lock and keep snapshot publication protected through durable run pinning. Preserve empty-selection behavior and the list API. Validation: 176 affected cases, full repository typecheck/build, fresh full CI/E2E, security and exact-head Apex 5/5 pass. The local six-reader latency experiment improves from 50.4s to 18.3s; live response latency is a separate acceptance check. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
5fbece9dcc |
Synchronize assigned skills through agent runtime hooks (#16501)
## Thinking Path > - Paperclip manages shared skills and agent assignments. > - Runtime plugins need installed skills before a task starts. > - Skill content can change without changing the agent row. > - Source access and version pins also affect delivered files. > - This pull request feeds those changes into the durable runtime stream. > - The runtime reader resolves the same immutable skill sources used for execution. ## Linked Issues or Issue Description Builds on merged PR #16499. This pull request now targets `master` and contains only the five skill synchronization commits. **Problem** Plugins receive no durable notification when an assigned skill changes. A saved assignment can also become inaccessible after private-source access changes. **Proposed solution** Invalidate referencing agents at installed skill mutations. Invalidate inherited access when source bindings, project privacy, or policy change. Return authorized skill sources and version IDs through the owning runtime reader. **Alternatives considered** Activity-only subscriptions miss service writers. Per-task synchronization adds preparation work. Copying current files would ignore version pins. **Roadmap alignment** This extends existing skills, source synchronization, and plugin runtime support. ## What Changed - Defer multi-skill source runtime hooks until commit, after publication holds all skill rows. Keep file-save journaling immediate to preserve filesystem rollback. - Add transactional invalidations for skill content, companion files, versions, rename, deletion, policy, source access, and pin-setting changes. - Recognize canonical keys and supported legacy aliases. - Reuse the execution skill resolver, current agent access, and instance version-pin rules. - Resolve and materialize sources outside the agent row lock. Recheck ownership and runtime revision afterward. - Keep task-only mentions and running-task snapshots in the execution path. - Document conservative invalidations for pinned assignments and inherited access. ## Verification - Rebased onto origin/master `9f4a956b90`, which contains merged PR #16499. All five commits are unchanged according to `git range-diff`; no conflict resolutions or source changes were needed. - On rebased head `58b065941`, all 237 focused tests passed: runtime skill hooks/source publication (41), plus content service, assignment routes, policy routes, and project source access (196). - Server typecheck passed after rebuilding Runner with the installed Rust toolchain. Migration numbering/safety and map checks passed; snapshot pruning made no changes. Migration CLI checks required local IPC permission. - Real PostgreSQL coverage includes concurrent source publication/version saves, policy-only tightening/restoration, referencing-agent fanout, company isolation, no-op counters, supporting files, rename/deletion, rollback, private-source access, pinned/latest bytes, and unassignment. - Earlier stack validation passed workspace typecheck/build. Fresh hosted CI and review must verify the rebased head before handoff. - The broad local suite was previously limited by an unchanged diagnostic test that rejects "private" in an error stack under this /private/tmp worktree; that unchanged suite passed all 108 tests in the original checkout. Hosted full-suite checks provide exact-head verification. - Diff whitespace and full PR secret/PII scan passed. - All hosted CI/security checks are green on `58b065941`; fresh standard Greptile and Apex both returned 5/5 with no findings or unresolved threads. The PR targets master and is mergeable without conflicts. ## Risks - Apply migration 0348 after the runtime-stream migration in #16499. - Content invalidations include pinned assignments. The resolved version stays pinned; consumers can avoid writes when its version/hash is unchanged. - Project access and source changes invalidate only agents assigned skills from the affected sources. Company policy still invalidates the company. Source polling bookkeeping and popularity counters do not create work. - The reader rebuilds installed caches without upstream requests. Concurrent changes cause a bounded retry and then a conflict. - Private transport integration and live remote computer qualification remain separate. ## Model Used OpenAI Codex, GPT-6, with reasoning, repository inspection, code execution, and testing. The exact deployment model ID and context window are not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes: #` / `Refs: #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub references) - [x] My branch name describes the change and contains no internal Paperclip ticket id - [x] I have run tests locally and they pass (focused suites; broad local limitation documented above) - [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> |
||
|
|
9f4a956b90 |
Add durable agent runtime synchronization hooks (#16499)
## Thinking Path > - Paperclip manages agents and their runtime configuration. > - Plugins can maintain external agent computers. > - Configuration and file saves currently miss durable plugin delivery. > - A transient activity notification cannot recover after interruption. > - This pull request adds an opt-in agent runtime resource stream. > - The existing dispatcher delivers and acknowledges its committed work. > - Plugins can prepare computers when configuration changes. ## Linked Issues or Issue Description Related: #15631 and #16203 establish agent lifecycle and native durable delivery. **Problem** Plugins cannot reliably react to agent configuration and instruction changes without polling or waiting for execution. **Proposed solution** Add an opt-in `agent_runtime` stream to the existing resource journal. Record changes at the database mutation and managed file audit boundaries. Wake the dispatcher with content-free commit notifications. Provide scoped runtime inputs to the selected lifecycle plugin. **Alternatives considered** Route-only activity events miss service and background writers. Scheduled scans delay changes and repeatedly query unchanged state. **Roadmap alignment** This extends the existing plugin and agent configuration work. ## What Changed - Add durable update/delete events for agent runtime state, including an existing-agent baseline. - Capture configuration changes, instruction edits, deletion, restore, and managed run writeback. - Add commit notifications and a reconnect scan. Register one listener for configured, enabled runtime subscribers. Release it when none remain. - Keep HTTP startup independent of queued plugin callbacks. - Add `ctx.agents.getRuntime`. Require lifecycle capability, invocation company, enabled plugin, and owning lifecycle selection. - Keep legacy agent/project subscriptions isolated. Manual inbox reads retain both legacy streams. - Resolve recovered instruction paths without reading or hashing bundle contents. Use the company reporting graph for runtime health. - Repair missed file events on identical save/deletion retries after a journal failure; preserve content tokens and conditional-write checks. - Correlate file audits with runtime revisions to deduplicate recorded retries without a new table. Bound the lookup with the existing company/timestamp index. - Document the contract, migration, access boundaries, and session-connection requirement. ## Verification - Rebased the full stack onto origin/master after #16507. - Final replayed stack: `pnpm -r typecheck` and `pnpm build` passed. - All 132 affected runtime, skill, connector, publication, instruction, managed-agent, and delivery tests passed after the latest-master rebase. Workspace typecheck and build passed. - Journal failure injection verifies recovery on identical file writes, deletions, and instruction retries from an acknowledged inbox. - Deduplication follow-up: 80 runtime/instruction/delivery tests and server typecheck passed. Repeated retries create no audit/events; old hashes returning after an intervening commit still repair delivery. Built-in/native instruction writer regressions passed all 56 tests. - PR 1 follow-up tests cover valid/cyclic reporting chains, listener registration failure, multiple subscribers, global/company disable, re-enable, unload, and startup with blocked callbacks. - Database coverage includes rollback, journal failure, no-op suppression, file saves/deletion, ordering/replay, owner/company denial, independent-client commits, and real listener loss/reconnect. - SDK/shared suites passed 1,059 tests during implementation. CLI/delivery regressions also passed. - Migration safety, map checks, diff whitespace, and local secrets/PII scan passed. - Hosted CI and security checks are fully green at `b296405e2`. Standard Greptile and Apex both returned 5/5 with no unresolved findings. - The broad local suite encountered an unchanged diagnostic test that rejects the word "private" from an error stack. This worktree lives under /private/tmp. The same unchanged diagnostic suite passed all 108 tests in the original checkout. Hosted full-suite checks provide exact-head verification. ## Risks - Apply migration 0347 before enabling a runtime subscriber. Runtime notifications require session-level PostgreSQL LISTEN. Transaction-mode poolers are unsupported for this opt-in feature. - Database triggers share the mutation transaction. Journal failure prevents configuration commits. Trigger work adds a journal insert to relevant changes. - Filesystem publication and SQL commit are separate boundaries. Direct filesystem edits do not emit hooks. External clients must fence stale revisions and retain conditional-write checks. - The reader returns only that plugin's own instance secrets. Keep them out of UI data, logs, plugin state, and agent files. - No private transport integration or live remote computer qualification is included. ## Model Used OpenAI Codex, GPT-6, with reasoning, repository inspection, code execution, and testing. The exact deployment model ID and context window are not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes: #` / `Refs: #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub references) - [x] My branch name describes the change and contains no internal Paperclip ticket id - [x] I have run tests locally and they pass (focused suites; broad local limitation documented above) - [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> |
||
|
|
b294abb9eb |
perf(server): append ordinary run events in one statement (#16569)
Ordinary run events now validate their binding, allocate a sequence and insert the decoded receipt in one atomic PostgreSQL statement. Native replay and retry-exhaustion transactions keep their existing behavior. Verification: exact-head full CI/E2E, Superagent, legacy security and Apex 5/5 passed. PostgreSQL concurrency, rollback and payload controls pass. Controlled network-delay comparison reduced median event persistence from about192ms to48ms; no staging response-time claim is made before deployment. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
43ffe8ce95 |
perf: skip conditions for unselected tool profile entries (#16556)
Check exact tool profile selectors before evaluating conditions. Matched entries retain fresh policy condition checks and existing approval, deny, binding, and decision-phase behavior. Validation: 70 affected cases pass with zero skips. Full repository typecheck and build pass. A balanced same-database comparison over 47,931 entries preserves all decision JSON and reduces 200 decisions from about 1.05 seconds to 0.40 seconds. Fresh exact-head full CI, E2E, Superagent, legacy security, and Apex 5/5 passed with no unresolved findings. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
8f1cb11a03 |
fix(workspaces): name the owner action when a shared source is restricted (#16545)
A reused task workspace in a shared project checkout keeps its source denial when another task's restricted worktree shares that checkout (#16095 deliberately does not clone over existing workspaces). The run then failed as an opaque `setup_failed` 403 and the task was blocked with a generic "no live execution path" notice. Convert that exact denial into a `configuration_incomplete` failure with reason `workspace_source_restricted`, give the task notice and the recovery action the owner step, and keep this pre-dispatch owner action out of the unexpected Sentry run-failure stream. The reason and predicate live in a dependency-free module. Access checks are unchanged. Co-Authored-By: Paperclip <Paperclip-Paperclip@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
f1d4bc1617 |
perf(slack): resolve only signing secret during webhook checks (#16548)
Resolve only the current Slack signing secret during workspace checks and authenticated setup evidence. Preserve full runtime credential resolution, company bounds, rotation, and auditing. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
78b833c495 |
Dispatch routine and monitor work at saved deadlines (#16514)
## Thinking Path > - Paperclip manages work that agents run for a company. > - Routines and issue monitors store their next run time in the database. > - The app currently waits for the heartbeat interval to find that work. > - This can delay work after a workspace wakes before its saved deadline. > - This pull request uses the existing work coordinator to schedule each known deadline. > - It also parses each cron expression once, so sparse schedules do less work on the event loop. > - Agents can start due work sooner while the existing execution checks stay in place. ## Linked Issues or Issue Description **What happened?** Routine and issue-monitor dispatch depends on the heartbeat scan interval. A workspace can wake early and still wait for the next scan. A sparse cron search also parses the same expression for every minute it checks. In a local annual-schedule test, the deadline callback ran on time but schedule calculation delayed the routine record by 13.6 seconds. **Expected behavior** Use a saved future deadline to request the existing dispatch scan. Restore deadlines at startup and update them after a producer transaction settles. Do not let future timers keep an idle workspace active. Parse a cron expression once for each search. **Steps to reproduce** 1. Set an active routine and an assigned issue monitor to the same future deadline. 2. Choose a deadline between heartbeat scans. Use an annual routine expression to exercise a sparse search. 3. Compare the saved deadline with the routine firing and agent start times. **Paperclip version or commit** Source checkout based on |
||
|
|
82a12f1a8b |
perf(skills): batch project source authorization reads (#16536)
Batch project-source read authorization through the existing decideReads path. Keep company scope, source union rules and fresh permission decisions. The local80-project fixture reduces select builders from322 to4 and complete legacy preparation from689ms to128ms. All88 affected cases and both fresh-database cases pass. Full current-head CI/E2E, security and Apex5/5 pass with no unresolved findings. This is component proof; hosted latency still needs verification. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
185483e090 |
perf(slack): keep authorization revision rows inside PostgreSQL (#16527)
Compute Slack authorization revisions in one PostgreSQL statement while retaining all existing scope and invalidation inputs. Return one ordered digest instead of materializing retained profile rows in Node. Validated with 19 affected cases, full typecheck/build, final-head CI/E2E/Superagent, and Apex 5/5. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
90924d61b3 |
fix(tools): batch profile selector validation (#16521)
Batch company-scoped profile selector validation while preserving both existing write boundaries and exact selectors. Verified with 81 affected local cases, full repository typecheck/build, current-head CI/E2E, Superagent and Apex 5/5; no unresolved findings. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
0e58f29eb7 |
fix(observability): trace pre-dispatch controller waits (#16515)
Measure usage startup, MCP handoff, dispatch admission, and budget reservation with the existing endpoint-gated tracer. Preserve outer attempt context across asynchronous provider dispatch.
Validated with 135 focused tests, full typecheck/build, fresh full CI/E2E, Superagent, and Apex 5/5 on
|
||
|
|
fa9f3f8a08 |
feat(runner): support preconfigured user homes for Codex and ACPX (#16486)
## Thinking Path > - Paperclip manages AI agents and their work. > - Runner launches the selected provider and keeps durable session state. > - Runner currently prepares private provider homes for each session. > - Operators can already have working login, skills, and MCP configuration on the execution machine. > - This PR adds an explicit mode that uses that existing setup. > - Runner keeps its task bridge and session ownership while leaving the user's configuration in place. ## Linked Issues or Issue Description **Problem or motivation:** Rebuilding a provider home is unnecessary when the execution account is already configured. **Proposed solution:** Add `homeMode: "preconfigured"` to Runner adapter config and runnerd transport options. Keep `"isolated"` as the default. Initially support native Codex and ACPX Claude. **Alternatives considered:** Continue copying user configuration into a private home, or add an approval workflow. Both add work outside this feature's scope. **Roadmap alignment:** This extends the existing cross-provider Runner. It does not add another runtime. Related work: #14056 covers managed Codex credentials. #14474 covers command environment delivery. This mode uses user-owned credentials and retains the existing launch boundaries. ## What Changed - Add a closed v7 launch contract for the selected home mode. Existing contracts retain their behavior. - Keep homeMode under operator control. Reject task overrides and agent-authenticated changes, including removal through replacements, adapter changes, or rollback. Ignore stored task values and exclude it from automatic hire inheritance. - Skip managed AI login, assigned-skill materialization, and external MCP setup in preconfigured mode. Preserve the Runner task/completion bridge. - Keep protocol state and provider lifetime fences scoped to the Runner session. Different sessions can share a home. - Fail preconfigured ACPX planning/read-only launches before provider admission until a read-only user-home launch is qualified. Use isolated mode or native Codex for planning. - Reject ACPX approval requests. Keep native Codex on `never`. Local permission rules and hooks remain effective. Initially require the default Runner ACPX `approve-all` setting; other Runner policies are unqualified in this mode and fail before launch. - Keep the internal ACPX bridge in a stable session namespace, preserving an existing user MCP server named `paperclip`. - Reject preconfigured ACPX Codex before launch until a session permission profile can enforce Runner-state denial. Remove the unprotected full-access mode switch; use native Codex or isolated mode for that model family. - Bump Pi to profile 23 because its attestation covers shared Runner source changed here. Preserve profile 22 for historical recovery, and update Docker/package verification inputs. - Fence recovery on mode and home bindings. Fail missing provider-history recovery without starting a replacement session. Exclude the external Codex home from backups and credential cleanup. - Discover remote HOME on the execution machine. Do not project controller HOME or XDG paths onto it. - Separate task instructions from Codex ACP isolation and update the generated provider digest. - Keep remote configured-environment manifests consistent when dropping controller paths. Preserve exact Claude model IDs through per-launch environment settings, without editing the user home or patching the provider. ## Verification - Targeted local checks cover native contracts and launch allowlists, user-config preservation, provider admission, permissions, session recovery, remote environment projection, heartbeat preparation, and operator-only configuration. Runner, shared, and server TypeScript checks and the Rust binary build pass; generated ACPX profiles are current. - Following the latest master integration, all 757 server configuration/executor tests and 115 Runner admission/recovery tests passed. Additional review-fix checks passed: 103 planning contract/host tests, 78 provider-backend tests, 95 route/preparation/inheritance tests, 43 task-validator tests, and 43 workspace-policy tests. Counts describe separate runs and can overlap. - The real pinned Claude ACP/SDK regression reproduces exact-model aliasing without the fix (`claude-sonnet-5-5` becomes `sonnet`) and passes with the fix. All 89 adapter tests pass. User settings remain byte-for-byte unchanged. - Localhost-only Claude SDK probes with synthetic credentials verify the private bridge runs, local `ask` reaches the existing reject callback, local `deny` blocks without a callback, and a user MCP server named `paperclip` remains available. No user model allowlist is required. - Review regressions cover fail-closed provider admission, saved-mode recovery before routing, controller-home path removal without configured-environment manifest drift, pre-launch rejection of unqualified read-only ACPX launches, and denial of task/agent changes to the operator-owned home mode. - Before rebase, `d4d4ee2aebe4cb88c4f4d9ef8e22fed4b36b87be` received standard Greptile 5/5 with no unresolved threads. Prior Apex findings are fixed and covered by regressions. No manual Greptile retrigger is requested for this rebase, per reviewer direction. - The prior PR head and master hit the same GitHub runner encoding crash in workspace-b on three PR attempts, including diagnostic logging ([prior PR CI](https://github.com/paperclipai/paperclip/actions/runs/38818474563/job/116714538405), [prior master CI](https://github.com/paperclipai/paperclip/actions/runs/38819528748/job/116712423193)). Rebased onto `origin/master` at `c3ffa3f0dd` to pull in its bridge-test index correction. The rebased head `edd153db410bea063b645e0000aa68649f287b00` passes all CI checks ([CI run](https://github.com/paperclipai/paperclip/actions/runs/38824676914)), including workspace-b. Greptile's automatic standard review is 5/5 on this exact head, with no unresolved threads; no manual review was requested. - Local workspace-b investigation passed 911 shared tests and 3,868 tests across the remaining 15 workspace projects. The catalog run separately hit an existing ignored `.agents/skills/design-guide/SKILL.md` description-budget violation in this checkout; that file is untracked, outside the PR, and left untouched. Repeating shared tests with CI reporting enabled also passed. - Rebase verification: the final Git tree exactly matches the previously reviewed branch combined with current master. All 192 Runner admission/contract tests, 61 server preparation/provider tests, and 68 bridge tests pass, as do Runner/server TypeScript and generated-profile checks. The real Claude adapter fixture hit an `ENOTEMPTY` cleanup race on its initial run; all 89 adapter tests passed on retry. - Apex on rebased head `edd153db` returned 2/5: ACPX Codex selected full access without enforcing Runner-state denial, and agent configuration replacement could remove the operator mode. Both are fixed in `26e14dbbf`. Preconfigured ACPX Codex is temporarily unqualified; re-enabling it requires session permission enforcement on thread/turn operations and a real filesystem-denial probe. The config guard covers effective replacements, adapter changes, and rollback while allowing ordinary agent merges and operator removal. All 190 focused Runner tests, 103 server tests, both typechecks, and generated-profile checks pass. Standard review and CI passed before the next Apex request. - CI on `26e14dbbf` passed 1,967 Runner-shard tests but hit the previously observed `ENOTEMPTY` fixture cleanup race after successful Claude model admission. `eb83284fb` adds bounded filesystem retries to that fixture's teardown; all 87 adapter tests pass. Assertions and production cleanup behavior are unchanged. Head `eb83284fb42ee19a15d9a93b106c1bbb07e1695e` passes all 59 CI checks ([CI run](https://github.com/paperclipai/paperclip/actions/runs/38829500857)) and standard Greptile at 5/5 with no unresolved threads. Apex also completed at **5/5** on that exact head ([Apex result](https://github.com/paperclipai/paperclip/pull/16486#issuecomment-6201559527)). All review threads are resolved, the branch has no merge conflicts, and the PR remains unmerged. - No live paid-provider or remote-machine smoke was performed. The lockfile remains unchanged under the repository's refresh-bot policy; local dependency installation resolved the changed patch hash. ## Risks This explicit opt-in grants Codex tools access to the execution home (read-only in planning), including credentials. Native Codex separately denies Runner-owned protocol state. ACPX Claude retains its existing provider trust boundary, and the isolated default is unchanged. Providers may refresh credentials and update their own metadata. User configuration can block tools or the completion reporter. Existing blocked results and failure diagnostics remain the recovery path. Edit the local settings and retry; no new approval workflow is added. External provider history remains on the execution machine and is not backed up by Runner. Remote HOME discovery requires `getent` and fails explicitly when unavailable. Other harnesses need separate qualification. The Codex ACP bundle digest changes. Existing sessions tied to the old bundle may need a fresh session. No database migration is required. ## Model Used OpenAI Codex, GPT-6, with reasoning and code execution. The exact runtime model ID 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 described the issue in-PR following the feature issue template - [x] I have not referenced internal/instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal ticket id - [x] I have run targeted 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 risks above --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
c7ea5c0216 |
perf(server): skip settled committed finalizations in unscoped passes (#16485)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - On restart, `/api/health` reports `starting` until startup recovery finishes, and deploys wait for it > - Native recovery and the orphan reap each run an unscoped native finalization reconciliation pass, and readiness waits for both > - The pass selects every committed finalization with a result, including runs that ended long ago, and spends dozens of sequential queries on each one > - On a busy instance this is about one minute per pass, so about two minutes of every restart, and the periodic recovery repeats it on every tick > - This pull request skips settled committed finalizations in unscoped passes, and keeps a small rotating upkeep page so their upkeep still runs > - The benefit is faster restarts and less steady database load, with no change for unsettled work ## Linked Issues or Issue Description Related: #16330 and #16329 (startup and reconciliation timing). **What existing behavior does this improve?** Startup recovery time and steady-state recovery load. **Current behavior** - The unscoped candidate scan in `reconcileNativeFinalizations` returns every native finalization with a result and phase other than `terminal_failure`. Nothing excludes rows that are already settled. - Each committed row then takes the full per-row path: pending-effect, assessment, evidence and decision reads, presentation repair (twice), the workspace barrier read, a `finalizeNativeRun` re-read of the committed coordinator, and workspace settlement (lease release check and agent status update). That's about 40–60 sequential round trips per row. - Startup runs the unscoped pass twice (native recovery, then the orphan reap), and readiness waits for both. The periodic orphan reap runs it again on every tick. - On one instance, all 268 candidates were committed finalizations whose runs had ended. Native recovery took 50–80 s and the orphan reap 47–80 s on every restart, with database round trips of about 3.4 ms. **Proposed behavior** An unscoped pass skips a committed finalization when: - its run is `succeeded`, `failed`, `cancelled` or `timed_out`, and - the run finished more than an hour ago, and - its decision has no `pending` or `failed` status-decision effect. Each unscoped pass also includes up to 10 such settled rows in a rotating order, so the presentation and evidence upkeep still reaches every row over time. Scoped passes (explicit run IDs) are unchanged. **Reason and benefit** These settled rows have nothing left to finish. Recent rows, unfinished runs and rows with undelivered effects stay in every pass, so crash-window repairs and effect delivery behave as before. Startup and periodic passes no longer scale with the full history of committed runs. **Breaking changes** None. A late repair on a settled row can be delayed until its turn in the upkeep rotation instead of the next pass. ## What Changed - `server/src/services/native-runtime/native-finalization-reconciler.ts`: - `settledCommittedFinalization(now)`: the settled predicate above, including a `NOT EXISTS` on undelivered `status_decision_effects` for the same company, issue and decision. - The unscoped candidate scan adds `NOT settled OR run in the upkeep page`. - `nextSettledCommittedUpkeep`: up to `NATIVE_SETTLED_COMMITTED_UPKEEP_PER_PASS` (10) settled run IDs after an in-process cursor, ordered by run ID, wrapping at the end. - New constants `NATIVE_SETTLED_COMMITTED_GRACE_MS` (1 hour) and `NATIVE_SETTLED_COMMITTED_UPKEEP_PER_PASS`, plus `resetSettledCommittedUpkeepForTests`. - New `server/src/__tests__/native-finalization-settled-candidates.test.ts`. - `doc/execution-semantics.md`: describes the settled rule and the upkeep rotation. ## Verification - `pnpm exec vitest run src/__tests__/native-finalization-settled-candidates.test.ts` (in `server/`): 4 passed. The test mocks the timing hook to capture the candidate count and stop the pass after the scan: - 25 settled rows give 10, 10, 5, then 10 again over four passes (rotation and wrap). - A row finished 10 minutes ago, a running row, and a terminal row without `finished_at` are all candidates. - A settled row whose decision has a `failed` effect is a candidate. - A scoped pass with an explicit run ID still selects a settled row. - `pnpm exec vitest run` for `native-finalization-recovery`, `legacy-finalization-regression`, `native-run-finalizer-telemetry`, `native-workspace-finalizer`, `native-finalization-reconciler-timing`, `external-chat-wait.integration` and `native-status-arbiter-corpus`: 316 passed. - `heartbeat/recovery.test.ts`: the 8 tests that do not need a database passed. The 20 database tests were skipped locally because embedded PostgreSQL could not start (the host's shared-memory segments were exhausted by other processes). CI runs them. - `pnpm typecheck` in `server/`: passed. ## Risks - A settled row that needs a late repair (for example a missing chat presentation) waits for its turn in the upkeep rotation, up to `ceil(settled rows / 10)` passes, instead of the next pass. - The upkeep cursor is in memory. After a restart the rotation starts from the beginning; the work is idempotent. - A run left `running` with a committed finalization is never considered settled, so it is still projected before the reaper's active-run loop. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`), 1M context, through Claude Code with tool use (shell, file edits, test runs, database and log analysis). A separate Claude agent mapped the recovery call tree. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [ ] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
c4a16cbe98 |
fix(plugins): follow local package path changes in dev watcher (#15247)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Local plugin development uses file changes to restart the plugin worker. > - A plugin can move to a different local package directory. > - The watcher rejected every later registration for the same plugin ID. > - This pull request replaces the watcher when the resolved package path changes. > - The benefit is that current plugin files trigger reloads and previous files stop doing so. ## Linked Issues or Issue Description Refs: #14955. This change addresses its second observation: the watcher keeps the previous local directory after its package path changes. The issue also tracks stored configuration replay as a separate problem. Related PRs: #11336 shares the lifecycle manager across routes and the dispatcher. #14381 controls when production file watching is enabled. #10100 orders configuration delivery. Those changes address other parts of plugin reload behavior. ## What Changed - Store the resolved package path with each watcher. Keep repeated registrations of the same path as a no-op. Replace the watcher for a different path. - Cancel pending changes from the previous directory. Ignore late change, error, and timer callbacks from a replaced watcher. - Apply only the latest pending directory lookup. Manual registration, unwatching, and shutdown invalidate earlier lookups. - Stop a failed watcher without cancelling its pending replacement lookup. Explicit unwatching, disable, unload, and shutdown still cancel pending registrations. - Stop the old watcher when the new directory is unavailable or the plugin no longer has a local package path. - Document the path-change behavior and add twelve regression cases. ## Verification - `pnpm exec vitest run server/src/__tests__/plugin-dev-watcher.test.ts server/src/__tests__/plugin-lifecycle-restart.test.ts`: 18 tests passed. Regressions cover path changes, stale callbacks, lookup order, explicit cancellation, and watcher errors during a pending replacement. The current-watcher error case fails before the stop-versus-cancel fix; the stale-watcher error control passes. - A local smoke check used real Chokidar watchers and two real temporary plugin directories. It confirmed that a pending old change is cancelled, later old-directory changes are ignored, and a new-directory change triggers one restart. - `pnpm -r typecheck` passed on the path and lookup guards. The final watcher-error fix passed `pnpm --filter @paperclipai/server exec tsc --noEmit`. - `pnpm build` passed on the initial path-change fix. The final source passed `pnpm --filter @paperclipai/server build`. - `git diff --check`: passed. - Greptile reviewed `bef3182` at 5/5 with no new findings. The watcher-error review thread is resolved. CI is still running. - The full `pnpm test:run` suite was not repeated for this watcher-only change. The affected suites ran locally. The broad test shards will run in CI. ## Risks - Low risk. The watcher follows the current registered path. An unavailable replacement stops watching until another valid registration arrives. - Closing a filesystem watcher is asynchronous. Callback identity checks prevent an old watcher from acting on its replacement. - No database migration, API shape, or dependency change is required. ## Model Used - OpenAI Codex based on GPT-6, with reasoning, tool use, and code execution. The runtime does not expose the exact serving model ID or context window. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [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 |
||
|
|
457d3209b1 |
fix: name the real reason a connection request expires on reassignment (#14067)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - When an agent's selected AI account is unavailable, the issue thread shows a `connection_intent` card. A person must reconnect the account or pick another connection. > - If the issue assignment changes while that card is still pending, the server expires it. This is the ownership-change path in `expireConnectionIntentsForOwnershipChange`. > - The expired card showed the reason "The task assignment changed". That text is wrong in two ways. It does not say that the connection request was never answered, and it does not say the connection is still missing. > - Worse, the reason never reached the screen at all: the expired branch of the card hard-codes "This request is no longer active.", so any guidance the server stores is discarded. My first round of this PR claimed the opposite, which was incorrect — the UI change is required for the fix to be visible. > - Issue #14040 reported the symptom: users saw a run fail with "assignment" blame and no hint that a connection needed attention. It happened on both `claude_local` and `codex_local` agents, so the message — not one adapter — is the defect. > - This pull request therefore fixes both ends: the server stores a sentence that names what happened and what to do next, per kind of request, and the card prints the stored reason instead of a generic string. > - The issue asks for a larger behavior change (hold reassignment until the card resolves). That needs a product discussion, so this PR keeps the scope to the message and where it is displayed. > - The benefit is that the card tells the truth: the blocker is the connection, not the assignment, and it says so on screen. ## Linked Issues or Issue Description - Fixes: #14040 ## What Changed - `server/src/services/issue-thread-interactions.ts`: `expireConnectionIntentsForOwnershipChange` now selects each pending row's payload before expiring it and writes a per-request reason: "The task was reassigned before the connection request was answered, so the AI account is still not connected. Reconnect it or choose an available connection." for AI-account intents, and the same sentence naming "the `<serviceName>` connection" for tool intents. The subject is chosen from `payload.purpose === "ai"`, falling back to "the requested connection" for a payload that carries neither, because this one path expires both kinds and the recovery step differs. - `ui/src/features/connections/ConnectionIntentInteractionBody.tsx`: the expired branch now renders `interaction.result?.reason` and keeps "This request is no longer active." only as the fallback for results stored before this change. The `superseded` outcome keeps its own wording. This component is the shared body for both the compact task card and the full thread card, so both views get the reason. - `server/src/__tests__/issues-service.test.ts`: the expiry test now expires a tool (`Notion`) and an AI (`claude_local`) intent in the same run and asserts both stored reasons, so neither wording can regress into the other. - `ui/src/features/connections/ConnectionIntentInteractionBody.test.tsx`: new test renders an expired card whose result carries a reason and asserts the reason is on screen and the generic fallback string is not. ## Verification - `npx vitest run server/src/__tests__/issues-service.test.ts ui/src/features/connections/ConnectionIntentInteractionBody.test.tsx` — 151/151 passed (127 server, 24 UI; 33.4s, Node 22.22.2, local). My first revision of this body cited 168 for the same set; that count came from a run on the wrong branch, and 151 is what this command prints on `fix/connection-intent-expiry-reason`. - The pinned server test drives the real path: it inserts pending `connection_intent` rows plus a live `toolOauthStates` row, reassigns the issue through `svc.update(...)`, then asserts both expired reasons and the deleted OAuth state row. - Mutation checks: forcing the AI subject for every row makes the expiry test fail on the tool-row assertion (`1 failed | 126 skipped`, `expected [ { …29 fields } ] to deeply equal [ ObjectContaining{…} ]`), and deleting the `result.reason` branch in the card fails exactly `shows the stored reason on an expired request` (`1 failed | 23 passed`). Both revert clean. The reverse wording mutation (tool subject forced onto AI rows) was not run, so that half of the test is pinned by its assertion rather than by a demonstrated mutation. - No schema, wire-format, or payload change. Only the free-text `result.reason` value differs and the card now reads the field it was already given. ## Risks - Low risk. The value is display text on a terminal interaction outcome. No code parses the old string, and no stored data is migrated; cards that expired before this change keep their old text and now render it as the stored reason they carry. - The deeper request in #14040 (do not expire a pending `connection_intent` on reassignment) is intentionally out of scope here and needs a maintainer decision. ## Model Used - Anthropic Claude (Opus 4.x, agentic CLI coding assistant with file edit and shell tool use) assisted with the code change, the test updates, and this PR text. A human reviewed the diff and the issue context before submission. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes: #` / `Refs: #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] 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 |
||
|
|
7c6d977929 |
fix(wake-queue): skip malformed queued comment ids so one bad id cannot block an issue's wakes (#13912)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The wake queue decides whether a finishing run's `@`-mention actually wakes the mentioned agent, by checking the liveness of the comment ids queued in the wake payload > - `getQueuedCommentLiveness` passes every queued comment id straight into an SQL `inArray`. Postgres rejects the whole statement on the first malformed uuid, so one bad id (for example an 8-char short form written into a wake payload) silently blocks every queued wake for that issue > - The run that triggers the check still records `succeeded`, so from the board the agent finished its work and the mentioned agent simply never responds; the reporter hit this twice on the same long-lived issue > - This pull request filters the queued ids with the existing `isUuidLike` helper before the query, and skips the query entirely when nothing valid remains > - The benefit is that a malformed id is skipped instead of poisoning the batch — it can never match a comment row anyway, so the downstream liveness semantics do not change Fixes: #13908 Related issue class (malformed ids reaching database queries): #887, #7639, #8093, #11018, #13504 — this is the first report for the wake-queue site. ## What Changed - `server/src/modules/wake-queue/adapters/postgres.ts`: in `getQueuedCommentLiveness`, filter `queuedCommentIds` through `isUuidLike` before building the `inArray` predicate; when no valid uuid remains, skip the query and return an empty row set. - The post-processing loops now iterate the filtered id list, which is equivalent because a malformed id can never match a comment row. ## Verification - New embedded-Postgres regression tests in `server/src/modules/wake-queue/adapters/postgres.test.ts`: - a batch mixing one live comment id, one malformed short-form id, and one valid-but-missing uuid returns only the live comment instead of throwing; - an all-malformed batch returns nothing live instead of generating `inArray ()`. - `./node_modules/.bin/vitest run server/src/modules/wake-queue/adapters/postgres.test.ts` — 58/58 pass. ## Risks Low risk. The only behavioral change is that a wake batch that would previously fail the whole statement now dispatches the valid entries. A malformed id previously matched no rows (the statement errored before any filtering), so treating it as not-live is exactly what the old downstream filters would have concluded had the query run. ## Model Used Codex (OpenAI GPT-5, coding agent, tool use, repo-level context) — assisted with locating the call site, authoring the guard, and the regression tests; human review before submission. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: 馨冉 <xinxincui239@gmail.com> |
||
|
|
1b2632facc |
fix: serialize concurrent issue reference syncs (#13898)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The server keeps issue references synchronized with issue title and description changes. > - Concurrent updates can rebuild the same source issue's reference mentions at the same time. > - The replacement writes can then collide on the unique mention key and return a 500 error. > - This pull request serializes each source issue's reference rebuild and adds concurrent regression coverage. > - The benefit is that repeated unchanged descriptions complete without duplicate-key failures. ## Linked Issues or Issue Description Fixes #13731 ## What Changed - Lock the source issue row before replacing title and description reference mentions. - Add a 20-way concurrent regression test with repeated `COM-2` references. ## Verification - `pnpm exec vitest run server/src/__tests__/issue-references-service.test.ts packages/shared/src/issue-references.test.ts` passed with 10 tests. - `pnpm --filter @paperclipai/server typecheck` passed under Node 22.22.1; the repository declares Node >=24.11 and emitted engine warnings. - `git diff --check` passed. ## Risks - The row lock serializes rebuilds for one source issue and may reduce concurrency for simultaneous updates to that issue. - A theoretical lock-order risk remains for concurrent syncs of different mutually referencing source issues. That path is outside this fix and is not covered by the focused regression. - Remote CI and Greptile review have not run yet. ## Model Used OpenCode runtime, model `supercodes5/gpt-5.6-luna`; context window size is not exposed. Repository tool use and reasoning 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 - [ ] 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 |
||
|
|
550e959934 |
fix(task-watchdogs): deliver board instructions and stopped leaves in the wake payload (#13771)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task watchdogs wake an agent when a watched issue subtree stops
moving.
> - An operator can write `instructions` on the watchdog record. The
wake prompt has a slot for this text in the "Task Watchdog Mandate"
block.
> - The producer writes `customInstructions` and `stoppedLeaves` at the
top level of the wake payload. The normalizer reads them only from the
nested `taskWatchdog` object. Thus the normalizer drops both fields on
every watchdog wake.
> - The agent then sees "No board-supplied watchdog instructions"
although the record holds an instruction. The operator cannot see this
failure, and the agent cannot see it either.
> - This pull request puts both fields in the nested `taskWatchdog`
object.
> - The benefit is that operator instructions and the stopped-leaf list
reach the agent again.
## Linked Issues or Issue Description
No public issue exists. This is the bug description (bug_report
template).
**What happened?**
`watchdogWakeContext()` in `server/src/services/task-watchdogs.ts`
writes `customInstructions` and `stoppedLeaves` at the top level of the
wake payload. `normalizePaperclipWakePayload()` in
`packages/adapter-utils/src/server-utils.ts` reads
`payload.taskWatchdog` only. The two fields are lost. The "Task Watchdog
Mandate" block always shows "No board-supplied watchdog instructions.
Apply the mandate above." The "Terminal / stopped leaves to verify"
section never shows.
**Expected behavior**
When the watchdog record has non-empty `instructions`, the wake prompt
shows that text. When the subtree has stopped leaves, the prompt lists
them.
**Steps to reproduce**
1. Create a task watchdog on an issue. Set a non-empty `instructions`
value.
2. Let the watched subtree stop, so that the watchdog triggers.
3. Read the wake prompt of the watchdog agent. It shows the "No
board-supplied watchdog instructions" line.
4. `GET /api/issues/{id}/watchdog` still returns the non-empty
`instructions` value.
**Paperclip version or commit**
Reproduced on `master` before this change. The new test in this PR fails
on `master`.
**Agent adapter(s) involved**
Not adapter-specific (core bug). The drop occurs in the shared
wake-payload normalizer.
## What Changed
- `server/src/services/task-watchdogs.ts`: add `customInstructions` and
`stoppedLeaves` to the nested `taskWatchdog` object. Map each stopped
leaf to the shape the normalizer expects (`issueId` becomes `id`). Keep
the top-level copies, so that other consumers do not change.
- Add a test for the full path: real service, real wake payload,
`normalizePaperclipWakePayload`, then `renderPaperclipWakePrompt`. The
test also asserts that the "No board-supplied watchdog instructions"
line is absent.
## Verification
- Run from the repo root:
```
npx vitest run server/src/__tests__/task-watchdogs-scheduler.test.ts \
server/src/__tests__/task-watchdogs-classifier.test.ts \
server/src/__tests__/task-watchdogs-incident-fixture.test.ts \
server/src/__tests__/issue-watchdogs-routes.test.ts
npx vitest run packages/adapter-utils/src/server-utils.test.ts
```
- Local result: watchdog suites 52 of 52 pass. `server-utils.test.ts`
117 of 117 pass.
- The new test fails on `master` because
`taskWatchdog.customInstructions` is absent. It passes on this branch.
- All Paperclip CI gates on this PR are green. Greptile gives 5/5.
## Risks
- Low risk. The change adds fields to a nested object. It does not
remove fields.
- No schema change, no migration, no data write.
- Behavior change: agents now receive operator instructions that they
did not receive before. This is the intended result.
- Rollback: revert the single commit.
- Note: the normalizer field name is `terminalLeafSummaries`, but the
classifier fills `stoppedLeaves` with non-terminal stopped leaves. The
renderer heading ("Terminal / stopped leaves to verify") covers both.
This PR does not rename a public payload field. Maintainers can align
the names in a separate change.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- Provider: Anthropic Claude, Opus family, used through Claude Code as
an agent with tool use and shell execution.
- This PR description revision: `claude-opus-5-5`, 1M context window.
- The model found the cause, wrote the fix and the test, and ran the
test suites. A human owns the account that opened the PR.
## 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
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes (not
applicable: no documented behavior changed)
- [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
Duplicate search: I searched open and closed PRs and issues for
"watchdog instructions" and "watchdog customInstructions". I found no
related PR or issue.
Branch name: the branch name has an internal prefix. If the maintainers
want it, I can open the same commit again from a `fix/...` branch.
|
||
|
|
92bedaac4a |
fix(server): treat a scheme-less public base URL as absent instead of crashing startup (#13727)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - At boot the server selects the origin that agents must dial for the
control plane. It exports that origin to every run as
`PAPERCLIP_API_URL`
> - The selection starts from `auth.publicBaseUrl`. An operator writes
this value by hand
> - If the operator writes `host:port` and omits the scheme, the server
does not reject the value. The value is a valid URL with an opaque
scheme. The `origin` of such a URL serializes to the *string* `"null"`
> - The string `"null"` is truthy. Thus it gets through the `if (value)`
guard. The enclosing `try/catch` does not help, because the parse did
not fail. A later `new URL("null")` then throws `TypeError: Invalid URL`
on the startup path
> - The result is that the server does not start. The error message
names neither the configuration key nor the bad value
> - This pull request parses the public base URL in one helper. The
helper treats an unusable origin as absent configuration. This is the
same treatment that input which fails to parse gets today
> - The benefit is that a missing `https://` falls back to the existing
derived candidates. It no longer stops the boot
## Linked Issues or Issue Description
Fixes: #13576 — that issue reports this defect and includes a standalone
reproduction.
**Related PRs found by the duplicate search (none fix this defect).** I
searched the open PR list
for `runtime-api`, `publicBaseUrl`, `public base url`,
`PAPERCLIP_API_URL` and `Invalid URL`. I then
read the diff of every close neighbour. Refs #10517, Refs #9916, Refs
#7342, Refs #12886,
Refs #11564, Refs #9228. No one of them contains a test for an opaque
origin, and no one of them
compares an origin against `"null"`. They change *which reachable origin
wins*. This pull request
changes *how one unusable value is rejected*. The two subjects do not
overlap.
Refs #10517 and Refs #9916 are duplicates of each other. Neither touches
`server/src/runtime-api.ts`.
**One neighbour needs attention.** Refs #11564 adds a new function
`resolveRuntimeApiUrl`. That
function contains this code:
```ts
try {
const origin = new URL(explicitPublicBaseUrl).origin;
return { url: origin, reason: "explicit-public-base-url", ... };
} catch {
// Fall through to derived candidates if config parsing drifted.
}
```
This is the same defect in a third location. A scheme-less value parses
correctly. Thus the `catch`
does not run, and the function returns the literal string `"null"` as
the URL. The lines are pure
additions, so there is no text conflict with this pull request. But if
#11564 merges after this one,
it puts the defect back in a new function. The helper in this pull
request is module-private and
available to that function. I can add the one-line change to this branch
if the maintainers prefer
that.
## What Changed
`server/src/runtime-api.ts` — one new module-private helper, used at the
two existing call sites:
```ts
function parsePublicOrigin(rawUrl: string | null | undefined): string | null {
const trimmed = rawUrl?.trim();
if (!trimmed) return null;
let origin: string;
try {
origin = new URL(trimmed).origin;
} catch {
return null;
}
return origin === "null" ? null : origin;
}
```
- `choosePrimaryRuntimeApiUrl` now calls the helper. Its inline
`try/catch` is removed.
- `buildRuntimeApiCandidateUrls` now calls the helper. Its inline IIFE
is removed.
- `server/src/__tests__/runtime-api.test.ts` gets two new cases. One
case covers the wrong `"null"`
result. The other case covers the startup exception.
Counts: **+20 / −17** in `runtime-api.ts`. Five of the added lines are
the comment that explains the
opaque scheme. The test file is **+23 / −0**. There is no new
dependency. There is no `any` type. No
exported signature changes. The duplicated parse logic at the two call
sites becomes one function.
## Verification
I ran every step below against `master` at commit
`c65fc9e3c81c41aafe421aa90a00514b84343285`. That
commit is the merge base of this branch.
- **Unit tests**, with the repository runner (`vitest`), on the two
added cases:
- Before the fix: `Tests 2 failed | 6 passed (8)`. The two failures are
exactly the two new cases.
The assertion error is
`AssertionError: expected 'null' to be
'http://runtime-host.example.test:3102'`.
- After the fix: `Tests 8 passed (8)`.
- Both new cases are pure additions. No existing expectation changes,
because no existing test
uses a scheme-less `authPublicBaseUrl`.
- **Standalone reproduction with 8 arms** (attached to #13576): **6/8**
on unpatched `c65fc9e3`, and
**8/8** with the patch. The 5 regression arms and the 1 control arm give
the same result on both
sides. The control arm uses input that truly fails to parse. Thus the
reproduction discriminates
between the two builds. It does not pass by accident.
- **Typecheck**: `tsc --noEmit --strict --noEmitOnError --target ES2023
--module NodeNext` exits 0 on
the patched file. It also exits 0 on the unmodified file.
## Risks
**Low risk.** Reasons:
- The change only affects input that makes the server crash today. No
operator can depend on the
present behaviour, because the present behaviour stops the boot.
- The new path is not new code. The helper returns `null`, and `null`
goes to the fallback that
already exists for input that fails to parse.
- The second call site previously put the string `"null"` into the
candidate list. It now omits that
entry. A `"null"` origin is not reachable, so no caller can lose a
usable candidate.
**The one behaviour change a reviewer must agree with.** A bad value now
degrades quietly instead of
failing loudly. An operator who writes `host:port` gets a server that
starts on a derived origin. The
operator does not get an error.
I did not add a warning log, for one reason: the existing parse-failure
path is also silent. A log
would change behaviour that this pull request does not target, and it
would enlarge the diff. If the
maintainers want the warning, the correct fix is one log line inside
`parsePublicOrigin`, which
covers both input classes at once. This is now possible only because the
two call sites share one
function. Tell me if you want that, and I will add it.
**Migration:** none. No schema change, no data change, no configuration
change.
## Model Used
- Provider and model: Anthropic Claude, Opus 5
- Exact model ID: `claude-opus-5[1m]`
- Context window: 1M tokens
- Mode: extended thinking
- Capabilities used: tool use, shell execution, file editing, and GitHub
API access
- The human operator reviewed and authorized this submission. A human
approved the publication of
this pull request before I opened it.
## 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 — I read
the file. It has no item for the runtime API URL, for startup URL
validation, or for
configuration parsing.
- [x] I have searched GitHub for duplicate or related PRs and linked
them above — 5 queries, and I
read the diff of each close neighbour. See the linked-issues section.
- [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 — the branch is
`fix/scheme-less-public-base-url`
- [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 — no
user-facing document
describes this internal fallback, so no document change applies.
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green — not yet true, and I do not
check a box that is not true. On
commit `13ffb649`: 43 checks pass, 7 checks still run, 2 checks fail.
Both failures are the
PR-description gate that this update repairs. I will check this box when
the gates are green.
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups —
not yet true. Greptile gave
4/5 with one P2. That P2 is "Required PR sections missing", which this
update repairs.
- [x] I will address all Greptile and reviewer comments before
requesting merge — this update is
that work for the first review round.
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
|
||
|
|
514d8738cf |
fix: keep the JWT run-mismatch audit when the claimed run is missing (#13183)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Rejected agent JWTs whose run header disagrees with the signed claim are audited into `activity_log` so operators can see spoof attempts > - The audit insert writes the claimed run id into `run_id`, which is a real foreign key to `heartbeat_runs`; a bogus claim violates the FK and the catch swallows it, so the audit row is silently lost exactly when it matters most > - This pull request verifies the claimed run exists before trusting it into the FK column; the claim itself stays in `entityId` and `details` > - The benefit is a reliable audit trail for rejected credentials ## Linked Issues or Issue Description Refs #10498 — the anonymous fall-through half was fixed on master already (every failed-credential branch now returns an explicit 401/403/422); this closes the residual FK-swallowed audit defect. ## What Changed - `server/src/middleware/auth.ts`: `auditAgentJwtRunHeaderMismatch` looks up the claimed run id and populates `runId` only when the row exists; `entityId` and `details.claimRunId` carry the claim either way. - `server/src/__tests__/agent-auth-middleware.test.ts`: a mismatch whose claimed run does not exist still produces the audit row with `runId` omitted. ## Verification - `pnpm --filter @paperclipai/server vitest run src/__tests__/agent-auth-middleware.test.ts` — 17 tests pass. - Before: a run-header mismatch whose claimed run id was absent from `heartbeat_runs` violated the `activity_log.run_id` FK, and the catch swallowed it — no audit row. After: the row lands with `runId` null and the claim preserved in `entityId`/`details`. ## Risks - Low risk. One extra indexed select on a rare rejection path; the audit payload is unchanged apart from `runId` now being conditional on existence. ## Model Used - Anthropic Claude — SWE-2 Max agent via Devin CLI, tool use and code execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [ ] 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: Waseem Ilyas <1478353+Waseemilyas@users.noreply.github.com> Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
880d4ae60f |
fix: reject done when an executionPolicy replace discards a live review cycle (#13178)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The issue PATCH route applies executionPolicy edits and status changes through one transition function, so both can arrive in a single request > - A replacement executionPolicy regenerates stage ids, which orphans the stored currentStageId; the orphaned-stage path clears executionState > - When a review cycle is live (pending or changes_requested), that clear lets a caller-supplied `status: "done"` stand although the review never ran > - This pull request rejects `done` on that combined write and leaves every other requested status — including board cancellation — on the established abandon-and-clear path > - The benefit is that a review gate cannot be bypassed by attaching a fresh policy object to a completion request ## Linked Issues or Issue Description Fixes #13176 Refs #13001 (same transition function, distinct defect) Supersedes #13164 — same fix, resubmitted on a correctly named branch with the review findings addressed: the guard no longer rejects board cancellation, and the internal ticket reference is gone. ## What Changed - `server/src/services/issue-execution-policy.ts`: in the orphaned-stage branch, reject the transition with 422 `unexecuted_review_stage` only when a live review cycle (pending or changes_requested) meets a requested status of `done`. All other requested statuses — including `cancelled`, where abandoning the cycle is the intent — fall through to `clearExecutionStatePatch` exactly as a bare policy edit does. - `server/src/__tests__/issue-execution-policy.test.ts`: regression coverage for the rejected `done`, the preserved board cancellation, a routine non-`done` status change on the same path, the unchanged-policy `done` route to the stored reviewer, and the bare policy edit. ## Verification - `pnpm --filter @paperclipai/server vitest run src/__tests__/issue-execution-policy.test.ts` — 75 tests pass locally. - `pnpm --filter @paperclipai/server typecheck` passes. - Manual reasoning check: PATCH `{executionPolicy: <new object>, status: "done"}` on an issue whose `executionState.status` is `pending` or `changes_requested` now returns 422; the same request with `status: "cancelled"` or with no status clears the orphaned stage as before. ## Risks - Low risk. The only newly rejected request is one that previously discarded a live review while claiming completion — a silent-bypass bug, not a supported workflow. Callers intending to cancel or repurpose an issue mid-review are unaffected. ## Model Used - Anthropic Claude — SWE-2 Max agent via Devin CLI, tool use and code execution; the original fix commit was authored by an earlier agent pass on the same 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 - [ ] 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: Waseem Ilyas <1478353+Waseemilyas@users.noreply.github.com> Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
becdc3b08e |
perf(slack): remove delays before mention acknowledgements (#16491)
Slack mentions now start eyes reactions after the authorized task/comment commits, independently of attachments, subscription and scheduler acceptance. Omit unused optional URL-preview polling while retaining message text, links and files. Keep receipt failures and missing-file warnings independent, including terminal cleanup of older multi-line diagnostics. Verified with full current-head CI and E2E, Superagent and legacy security checks, Apex 5/5 with no unresolved findings, 19 selected real-PostgreSQL receipt/authorization/cleanup cases, and full frozen-source typecheck/build. The first CI attempt's unchanged Cursor probe timeout is retained in the PR; the one failed-only retry passed under unchanged deadlines. Live Slack latency still requires deployed acceptance. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
67a9d5cc37 |
perf(runner): bypass skill cache waits for empty selections (#16498)
Native runs with no skill files no longer queue behind company skill-cache work. Preserve saved empty selections and the existing exclusion for selected and legacy operational skills. Verified with real PostgreSQL held-lock regressions, 20 distinct affected cases, full repository typecheck/build, fresh full CI/E2E/security and exact-head Apex 5/5. Hosted latency remains to be verified. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
ec3eb1968b |
feat(runner): expose credential updates through PRP (#16463)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The Runner uses PRP to send harness events to the host. > - A harness can replace its stored OAuth tokens during a run. > - The host has no PRP signal that tells it to read the updated credential. > - This pull request adds a durable credential update event and observers for supported stores. > - The host can react before it acknowledges the event. Token values stay outside PRP. ## Linked Issues or Issue Description **Subsystem affected** `packages/paperclip-runner`: native provider execution, PRP schemas, and host event delivery. **Problem or motivation** A Paperclip host that consumes PRP needs to react when a harness changes its refresh-token credential. Existing credential copy-back paths do not provide a common event during a warm session. **Proposed solution** Add `auth.credentials.updated` as a P0 runner event. Include only the provider, binding ID, revision, and detection method. Observe native Codex and Codex/Grok ACPX credential files. Retain each pending event across runner restart until the durable outbox accepts it. Raw PRP consumers opt in with `observeCredentials: true` before session open. The bundled transport opts in for fresh sessions. **Alternatives considered** `harness.diagnostic` would mix a runner observation with harness diagnostics. Sending token values through PRP would expose credentials to event logs. Both approaches are unnecessary. **Roadmap alignment** I checked `ROADMAP.md`. This change extends the existing Runner protocol and does not duplicate a planned core feature. **Additional context** Refs: #11094, #14056, #12696. These describe credential copy-back and staging work. This PR adds the notification contract. It does not replace those private credential channels or claim to fix their full scope. ## What Changed - Add strict credential update payload validation to PRP v1, v2, and v3. Regenerate validators, the manifest, and the event catalog. - Watch supported credential directories with `notify`. Add a periodic read as a fallback and check at terminal and close boundaries. - Persist a private baseline and pending event together. Notify on account identity changes and retain the new baseline for later refreshes. Ignore incomplete files, unsafe files, and metadata changes. - Forward the event through the host transport. Test commit-before-ACK delivery and replay. - Commit pending runner observations before suspend and shutdown commands complete. Preserve the provider event backlog. - Create the private ACPX runtime directory before configuring observation. Cover fresh session startup with observation enabled. - Fence asynchronous PRP commits to the current authenticated connection and run authority. Ignore replaced callbacks while preserving the maintenance drain of admitted work. - Deliver observations found during final provider cleanup before clearing terminal recovery state. Retain the outbox and recovery state when the host ACK is lost. - Advance the pending Copilot candidate profile to v19 for the changed shared PRP source hashes. Preserve its v18 history and pending qualification status. Include the current fixture in the Docker build context. - Commit credential updates through an awaited host callback before PRP ACK, including during idle and close. Persist them in a separate run-log source namespace with company, agent, session, and runner checks. - Preserve public event metadata through server redaction. Register the event in task run details. - Document opt-in behavior, supported stores, event semantics, and the local run-log path. ## Verification - All 45 durable runner control tests and 24 native executor integration tests pass. - Ten Rust credential observer tests pass. They cover atomic replacement, polling, recovery, account-only changes and later refreshes for Codex and Grok, unsafe files, fresh runtime creation, and the Grok close handoff. Account identity stays private. - The full Runner TypeScript suite passes: 3,565 tests in 221 files. An additional 12 tests are skipped by the suite. - All 214 focused tests pass for the contract, Codex event routing, catalog, and durable host delivery. - All 13 Node protocol contract tests pass. - The exact Docker context integrity command passes: `docker buildx build --file .github/docker-context-checks.Dockerfile .`. - Rust formatting and generated protocol checks pass. - `pnpm -r typecheck` and `pnpm build` pass. The full local `pnpm test:run` was attempted with CI-compatible `init.defaultBranch=master`, then stopped after a reproducible failure outside this change: `git-workspace-expiration.test.ts` expects one re-expired workspace but gets zero. The same case fails in isolation on this macOS checkout. Full repository validation is provided by CI; the focused Runner and host tests pass locally. - All 253 native transport and restart tests pass against the final release binary. - All 3 durable recovery tests and 139 candidate profile and native admission tests pass. - All 41 host redaction and task protocol surface tests pass. `pnpm check:token-gates` passes. - All 467 CI policy tests pass, including the 15 CI profiling tests. The upstream secret-reference test fix is retained from master. No workflow changes are included. - The Apex host-delivery fix passes 381 targeted Runner tests, six host database tests, repository typecheck, and the production build. The Runner tests cover commit-before-ACK, retry after a rejected host commit, warm attachment, and close after the turn queue ends. - Prior head `2f125cfcb` passed CI, standard Greptile 5/5, and Apex 5/5. One CI shard was interrupted by GitHub runner shutdown; its single rerun passed. Both Apex findings are addressed: idle/cleanup host delivery and late callback reconnect fencing. Current head `5db78f082` passes CI (56 successful statuses and four optional skips) and standard Greptile 5/5. Apex review of the account-change update is 5/5 on this exact head, with no actionable issues. - All 121 durable control-plane and credential callback tests pass, including account-only change delivery, subsequent refresh on the new account during close, host commit retry, late success/failure races, forward cursor delivery on a replacement connection, and the existing maintenance retirement contract. All 27 native provider and durable recovery integration tests pass after the account-change update. ## Risks - File changes signal a stored credential update. They do not prove that an OAuth refresh succeeded. Changes between reads can be combined. - Unix Codex and Codex/Grok ACPX stores are supported. Other harnesses and credentials held only in memory need their own integration. - Hosts must update their event schemas before opting in. Older hosts omit the flag and receive no new event. Older runners ignore the flag. - The host must use its existing private credential channel and account checks to read or copy the token. Account switches trigger notification but do not authorize accepting the new account. The event grants no credential access. - Tests use synthetic credentials. No live OAuth refresh has been forced. ## Model Used OpenAI Codex, exact model ID `gpt-6.1-sol`, with high reasoning effort, repository inspection, tool use, and code execution. The session does not expose its 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> |
||
|
|
23e7ee9a2d |
fix(issues): exclude terminal children from helper cap (#11542)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The issue service lets agents create bounded child work through a
helper.
> - The helper counted completed and cancelled children against its
safety limit.
> - A recurring coordinator could reach the limit even when almost all
historical children were terminal.
> - The safety limit must still bound current delegated work.
> - This pull request counts only non-terminal children when it applies
the limit.
> - The benefit is that recurring parents can keep their lineage without
losing the active-work guardrail.
## Linked Issues or Issue Description
**What happened?**
The child-creation helper rejected a new child after a parent had 25
historical children. It counted children with `done` and `cancelled`
status.
**Expected behavior**
The helper must apply its limit to non-terminal children. Terminal
children must remain attached to the parent for lineage and audit
history.
**Steps to reproduce**
1. Create one parent issue.
2. Create 25 child issues and move them to `done` or `cancelled`.
3. Use the issue-service child helper to create another child.
4. Observe that the current code rejects the new child.
**Paperclip version or commit**
`1a17cbf23`
## What Changed
- Exclude `done` and `cancelled` children from the helper cap query.
- Keep the limit at 25 non-terminal children.
- Add a regression test with terminal history and the active limit
boundary.
- Clarify the limit error so it says `active child issues`.
## Verification
- `pnpm exec vitest run server/src/__tests__/issues-service.test.ts -t
"counts only non-terminal children toward the helper child limit"`
passed: 1 test passed and 118 skipped.
- `pnpm -r typecheck` passed.
- `pnpm build` passed.
- `pnpm test:run` completed the general server and UI groups
successfully. The CLI group then reported one unrelated environment
failure because port 3199 was already in use. The failing test was
`doctor > re-runs repairable checks so repaired failures do not remain
blocking`.
```test-evidence:
sha:
|
||
|
|
7b09e3da73 |
fix: count writes from unscoped heartbeat runs against the cross-issue cap instead of rejecting them (#12621)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents act inside heartbeat runs, and the server attributes every
issue comment and task update to a run so the per-run cross-issue
influence cap can be counted
> - A timer heartbeat that wakes an agent with no checked-out issue
stores no `issueId` in the run context snapshot
> - The influence gate treats that missing source issue as "no valid
run" and rejects every comment and PATCH from the run with
`cross_issue_influence_run_context_required`, even though the run row
exists and fully attributes the write
> - Issue creation is not gated, so an unscoped run can open an issue it
then cannot describe, update, or comment on
> - This pull request counts writes from unscoped runs against the
cross-issue cap instead of rejecting them
> - The benefit is that unscoped heartbeat runs keep their write
channels while the anti-spray cap and the audit trail stay fully intact
## Linked Issues or Issue Description
No public issue exists. Description follows the bug report template.
**What happened?**
An agent woken by a plain heartbeat timer (no assigned issue) received a
403 with code `cross_issue_influence_run_context_required` on every
`POST /api/issues/{id}/comments` and `PATCH /api/issues/{id}` request.
The agent JWT was fresh and its `run_id` matched a live `heartbeat_runs`
row. The error text said the request "arrived without a valid run", but
the run was valid. `POST /api/companies/{companyId}/issues` succeeded
with the same token, so the run created an issue it could not then
update.
**Expected behavior**
A live, authenticated run must be able to comment and update issues. A
run with no source issue has no same-issue exemption, so each of its
writes must spend the per-run cross-issue cap. The
`cross_issue_influence_run_context_required` error must fire only for
malformed, unknown, or mismatched run ids.
**Steps to reproduce**
1. Let an agent wake from the heartbeat timer with no checked-out issue.
2. From that run, call `PATCH /api/issues/{id}` or `POST
/api/issues/{id}/comments` with the agent JWT.
3. The server returns 403 `cross_issue_influence_run_context_required`,
because `observeCrossIssueInfluence` throws when `readRunSourceIssueId`
returns null.
**Paperclip version or commit**
master (
|
||
|
|
89ef37d7e0 |
fix(slack): accept linear DM receipt destinations (#16494)
Accept the pinned Slack SDK linear DM address for receipt add/remove while preserving channel, source message, credential and timeout checks. SDK regressions, 106 affected tests, repository typecheck/build, full CI/E2E, Superagent, legacy Snyk and exact-head Apex 5/5 passed. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
8843887c24 |
fix(runner): preserve managed GitHub access in OpenCode (#16488)
## Thinking Path > - Paperclip manages AI agents and their authorized connections. > - Native OpenCode agents use managed Git and GitHub launchers for shell work. > - Each operation must obtain credentials from the active run's broker. > - Two OpenCode environment filters dropped that broker binding before the shell started. > - The shell therefore ran anonymously even when connector tools had authorized access. > - This pull request preserves the explicit broker environment through both filters. > - The benefit is working GitHub shell access with current identity and revocation checks. ## Linked Issues or Issue Description **What happened?** Native OpenCode shell commands reported `GitHub capability_missing` despite an authorized GitHub connection. Both runner startup and provider startup dropped the controller's broker settings. Connector MCP calls used a separate path and could still succeed. **Expected behavior** A managed shell operation obtains credentials for the current run. A later operation uses the new identity after rotation and loses access after revocation. Ambient host credentials do not supply authority. **Steps to reproduce** 1. Start the native GitHub broker with an active run binding. 2. Pass its environment through native OpenCode startup. 3. Run `gh api user` from the provider shell. 4. Observe anonymous access before this fix. **Paperclip version or commit** Reproduced on master `6943904bad` before this change. **Deployment mode** Native Paperclip Runner with OpenCode, including remote execution. Related: #16401 fixes the separate oversized OpenCode SSE failure and is already merged. The same live-driver regression was verified locally against its exact pre-fix source and current source. This PR does not duplicate that fix. Searches found no open PR for this native broker propagation defect; #15309 concerns the legacy OpenCode adapter. ## What Changed - Use the existing GitHub credential environment projection at both OpenCode launch boundaries. - Preserve explicit controller authority while excluding an ambient host broker. - Exercise the real launcher and broker from an OpenCode child process, directly and through runner projection. - Check operation-time identity rotation, revocation, and exclusion of controller API credentials and persistent GitHub tokens. - Document the managed OpenCode shell contract. ## Verification - Red: both new `native-github-access.test.ts` cases returned `anonymous` instead of `fixture-a` before production changes. - Green: both cases pass after the fix, including `fixture-a` to `fixture-b` rotation and anonymous operation after revocation. - Full native GitHub broker suite: 18 tests pass. - OpenCode, provider-event, and configured-environment suites: 141 tests pass across eight files. - Separate SSE red/green verification: the existing live-driver regression times out on `fd057f6ac4^` and passes with merged #16401. The full OpenCode group also passes after restoration. - Full repository build and typecheck pass locally. All CI jobs pass for `23d734293f6a5c6d6128cae0c8773661816daf9a`. Final GitHub rollup: 55 successful checks/statuses, two skipped checks, and one neutral check; none pending or failing. Greptile gave that commit 5/5 with no findings or unresolved threads. - Paperclip Review spent over 23 minutes preparing its runtime without a provider assessment. Its run was cancelled for recovery and ended neutral (review execution did not complete). This neutral result is not a code assessment. No check policy or branch protection was changed. - The full local Vitest attempt first hit embedded PostgreSQL startup failures. Running the unchanged `initdb --version` and `postgres --version` independently showed missing native library symlinks. The dependency had been installed with scripts disabled. Running its bundled `hydrate-symlinks.js` restored startup; all 15 affected native-session cases and all 112 AI connection tests then passed without application code changes. The original full-suite attempt was stopped after the setup failure and its log is retained. A complete local suite pass is not claimed. - The broker and executable are local fixtures. No live GitHub account, credential change, or test PR is required by these regressions. ## Risks The change affects credential delivery into OpenCode processes. It reuses the existing bounded GitHub projection and accepts broker authority only from explicit input. Each shell operation still uses the normal broker, scope, identity, and revocation rules. Unrelated controller secrets remain excluded. There are no database or permission-policy changes. ## Model Used OpenAI `gpt-6.1-sol`, through Codex, with xhigh reasoning and repository tools/code execution. The session does not report its 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 the focused regression and affected suites locally and they pass; the full local attempt and setup failure are disclosed above - [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 (Paperclip Review is neutral/incomplete, as disclosed above) - [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> |
||
|
|
0b62c736a0 |
feat(connections): add generic Daytona API and Toolbox access (#16483)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Apps connections give agents controlled access to external services. > - Daytona provides sandbox, file, command, Git, and account APIs. > - Operators need to assign this access without exposing a server credential. > - A small task-specific tool list cannot serve general Daytona work. > - This pull request adds the published HTTP actions through the existing connection gateway. > - Operators keep control of identity, agent access, and each action. ## Linked Issues or Issue Description **Problem or motivation** Paperclip has a Daytona sandbox provider for its own execution infrastructure. It lacks a normal Apps connection for agents to manage an operator's Daytona account. Reusing the server's provider credential would bypass normal connection ownership and action permissions. **Proposed solution** Add a Daytona connection with personal or shared vault credentials. Generate its action catalog from the official platform and Toolbox API specifications. Use the existing run-bound gateway, grants, action policies, approvals, revocation, and audit paths. Sandbox IDs come from Daytona; tools need no Paperclip task or lease ID. **Alternatives considered** A four-action diagnostic subset cannot support general provider work. Launching the provider CLI would expose host filesystem behavior. Native OAuth is not included because a supported third-party registration path was not verified. The native method uses an organization API key. Existing Composio setup is a separate possible OAuth route and is not qualified by this change. **Roadmap alignment** This extends the existing Apps and Connections provider coverage in `ROADMAP.md`. It uses that system's credential and permission model. GitHub searches found no existing Daytona Apps connector PR or issue. ## What Changed - Add the Daytona catalog entry, provider source override, official theme logos, permission review, and setup documentation. - Generate 274 HTTP actions: 169 platform and 105 Toolbox operations. Record two WebSocket upgrades separately; do not advertise them as HTTP tools. Record source URLs and hashes. - Add provider-focused execution for sandboxes, snapshots, volumes, files, Git, commands, computer use, and account resources. Provider authority still limits which endpoints accept an organization key. - Validate action arguments. Resolve Toolbox targets through the fixed platform API. Check sandbox identity and organization before using an approved Daytona proxy. Keep normal guarded network requests and vault resolution. - Transfer file bytes without reading or writing host files. Share one caller deadline across prerequisite reads, dispatch, and response reading. Bound requests and responses. Do not automatically retry writes with unknown outcomes. Redact the selected credential from results and errors. - Classify credential access, access changes, interactive input, command execution, file permissions, and deletion (including TTL and auto-delete) as destructive actions. Keep new and changed actions under catalog review. - Upgrade saved catalogs and retire the earlier diagnostic names. Hide retired actions from the UI and agent discovery even when provider refresh fails. Preserve existing action choices and historical records. Reject stale same-name contracts during agent discovery and dispatch before a board refresh. - Add database-backed gateway and catalog-upgrade regressions, schema generator tests, a browser setup check, and both capability map updates. ## Verification - Passed on the review-fix head: 57 focused Vitest cases (18 Daytona database/client cases, five HTTP boundary cases, and 34 shared catalog cases), four generator tests, `pnpm -r typecheck`, and `pnpm build`. The five HTTP cases use a real local server through the pinned production transport. The normal browser setup check and 17 generator/artwork cases passed on the initial PR head; those UI and artwork paths are unchanged. - Passed on this branch: brand asset validation, token gates, map structure checks, and `git diff --check`. All latest-head GitHub checks pass on `ea979ccb3a64dc9508807823540f935c4316ea35`. Apex is 5/5, with all five findings addressed and zero unresolved threads. The branch has no merge conflicts. - The initial catalog-generation command could not find the external research corpus. The supported `--definitions-only` mode regenerated the definitions successfully. Unrelated generated drift was excluded. - An earlier full local Vitest attempt on the preview branch failed when bundled PostgreSQL could not start on macOS. After replay onto current master, all three unchanged event-sequencing cases pass with bundled PostgreSQL; the earlier startup cause remains uncertain. - The first full-suite command on this branch used my external test database override. Chat fixtures require a current migrated schema and failed because `agents.lifecycle_state` was absent in that disposable database. A focused comparison reproduces failure with the override and passes with normal setup. That attempt was stopped, and the full suite restarted without the override. Focused Daytona cases use their own migrated PostgreSQL 16 databases. - The clean local full-suite attempt recorded embedded PostgreSQL startup failures in two unchanged native-runtime cases and two failures in unchanged Git fixtures. It was stopped after these failures were isolated; no full local suite pass is claimed. The 15 matching native-runtime cases pass separately. The Git cases fail with the local `init.defaultBranch=main` setting and pass with `GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=init.defaultBranch GIT_CONFIG_VALUE_0=master` limited to the test command. No global Git setting was changed. Full Linux CI is green. - Fixtures use real database persistence and run-bound gateway sessions with provider responses replaced by fixtures. They cover personal/shared vault ownership, reads and writes, file bytes, Off policies, revocation, organization mismatch, untrusted proxy rejection, output bounds, saved catalog upgrades, failed refresh, and permission preservation. - The first Apex review found five valid issues: multipart transport compatibility, destructive classification, caller deadlines, audit filters, and stale saved contracts. All five were fixed with regressions before the second review. A typecheck then found the nullable saved schema hash; admission now accepts that stored type and rejects null as stale. - The browser check covers setup controls without submitting a provider key. No real Daytona account, provider write, native OAuth flow, or agent model run is qualified. Live provider read/write proof remains pending a test account. ## Risks - Daytona keys have broad Toolbox authority, including keys with no selected resource scopes. Only assign this connection to trusted agents and review its action permissions. - Remote writes or commands can continue after a timeout. Calls do not automatically retry an uncertain outcome. - The bridge targets Daytona Cloud. Proxy hosts are restricted; an unrecognized future regional host fails closed. - Files and responses have an 8 MiB bound. Each action has one caller deadline, capped at 30 seconds across all provider requests and response reading. Interactive WebSocket sessions are not exposed as HTTP actions. - The generated JSON snapshot exceeds 1,000 lines because it holds the provider input contracts. Generator logic and execution code remain in separate small modules. Updates require explicit regeneration and catalog review. - No database migration or lockfile change is required. ## Model Used OpenAI Codex, GPT-6. The runtime identifies the model family but does not expose the exact backend model ID or context window. Used reasoning, repository inspection, code editing, shell tools, database verification, and browser tests. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
f1d925b726 |
fix(issues): keep workspace restoration failures retryable (#16489)
## Thinking Path > - Paperclip manages agent work and its saved files. > - Finished Git workspaces can expire and restore from a saved commit. > - A task comment must restore a closed workspace before the comment is saved. > - Failed restoration returned HTTP 503 and hid the restoration message. > - The composer treats 5xx writes as uncertain, so it blocked the draft instead of offering a clear retry. > - This change returns a definite rejection before the write and preserves the restoration message. > - Users can restore repository access and retry the same draft. ## Linked Issues or Issue Description Refs #16457 and #16476. Live acceptance found this gap with a disposable expired workspace and an unavailable upstream. The saved commit and unique-file control stayed intact. No fallback workspace or comment was created. The UI still showed an uncertain comment result because the route returned 503. ## What Changed - Return HTTP 409 for a rejected workspace rebuild before the consuming write. - Preserve the service's safe restoration message in the response. - Cover rejected comment, comment-update, and checkout paths. - Document the response and draft retry behavior. ## Verification - `pnpm exec vitest run server/src/__tests__/issue-closed-workspace-routes.test.ts ui/src/api/issues.test.ts`: 38 passed in 26.80s. - Route tests assert that failed restoration does not save a comment or update the task. - Existing client controls preserve 409 rejections and still treat network failures and 5xx writes as uncertain. - `pnpm -r typecheck` and `pnpm build` passed. Full PR CI passed on attempt 2: [run 38804449401](https://github.com/paperclipai/paperclip/actions/runs/38804449401). The first attempt timed out in the unrelated fresh personal-AI connection browser case while waiting for its first reply. Logs and browser evidence were inspected and retained; only failed jobs were rerun once, and that shard passed. - Retained live fixture will verify the error, retry, exact saved commit, and subsequent persisted work after deployment. ## Risks Clients now receive 409 instead of 503 for this known pre-write rejection. They must retry after repository access is restored. The route uses the service's fixed messages, not raw Git errors. This change does not alter restoration, authorization, cleanup, or genuine uncertain-write handling. ## Model Used OpenAI Codex, GPT-6 family, with code execution and browser tools. The exact deployment model ID 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 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 |
||
|
|
3898f6a45f |
fix(native): honor duplex transport for GitHub callback bridges (#16455)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Native remote runners use a session-owned broker for managed GitHub operations. > - That broker uses the existing authenticated sandbox callback bridge. > - The native caller omitted the server-selected duplex flag. An enabled and capable target still used file polling. > - Warm sessions must observe a changed flag and channel loss before the next admission. > - This pull request forwards the existing flag and retires unavailable or changed bridges before retained instruction preparation. > - The benefit is less idle remote traffic without changing credential authority or the default transport. ## Linked Issues or Issue Description **What happened?** A native GitHub callback bridge selected `queue_v1` even when its sandbox target had duplex enabled and supported it. The file bridge continues remote queue checks while the warm session remains available. **Expected behavior** An enabled, capable target should use the existing HTTP/2 bridge. Disabled or unsupported targets and failed channel startup must retain the authenticated file fallback. A changed server flag must take effect at the next normal warm-session boundary. **Steps to reproduce** 1. Create native GitHub access on a sandbox target with the server-selected duplex flag enabled, verified duplex capability and a working channel. 2. Observe the selected callback transport. 3. Before this fix, it is `queue_v1`. The added real-wire regression expects `http2_v1` and fails on the old source. ## What Changed - Forward the existing server-selected duplex flag through the native GitHub broker. - Record that transport selection on the broker. Retire the warm provider and broker through the existing cleanup path when the flag changes. - Read live channel failure when checking broker readiness. Mark normal session cleanup as an orderly completion. - Include the selected flag in instruction preparation identity. Retire a failed broker before adopting retained instructions, while preserving existing ownership and cleanup fences. - Add real local HTTP/2 credential-rotation and file-fallback controls. Add native warm-session flag-change controls. - Document selection, fallback and warm-session behavior. - Scan CI secret expressions in one forward pass, including quoted braces and escaped apostrophes. Preserve direct, bracket, multiline and whole-context references while rejecting filename and literal-string false positives. ## Verification - Current head `41440b07ea45e9e882e36ed495a66e501c8dd4df` resolves the actual CI-policy conflict with master `307eb16c0c59abd9e0626fde9201939b262e7f5f`. It retains upstream's stricter whole-expression credential guard, including quoted secret text, and uses the forward scanner instead of a backtracking regex. All **16 CI policy tests** pass, including the bounded 28- and 10,000-apostrophe controls in both policy modes. The reported old regex still has its recorded two-second timeout (14 passed, one failed). On this final merged source, **809 affected native cases** pass with zero selected skips (65.20s). Full repository typecheck (31.44s) and build (22.13s) pass. Frozen source hashes stayed unchanged. Fresh exact-head full CI, configured security and completed Apex remain pending. - Before master integration, the frozen native repair passed repository `pnpm -r typecheck` passed (33.0s) and `pnpm build` passed (32.1s). - Before the latest master integration, all **800 affected cases passed with zero selected skips**: complete native executor (725), resume (50), GitHub broker (16), environment duplex-gate (4), and heartbeat instruction-copy (5) suites. The final run took 95.3s. Source hashes stayed unchanged through these checks. - The initial head passed 788 affected cases and full CI, but Apex scored it **3/5**. It found a dead HTTP/2 broker could remain reusable and a flag change could fail after retained instruction preparation. Both findings required the new repair. Three added before-fix regressions failed on channel availability, orderly close, and preparation identity. - The first repair run failed three new warm-instruction fixtures because fake provider close left its recorded state ready. The corrected fixture writes a suspended checkpoint without weakening the production ownership guard. A separate real-wire run then failed two availability assertions because its helper spread had frozen the readiness getter. The helper now preserves the getter. All failed attempts remain in the local evidence. - One disposable real Daytona sandbox exercised both transports on the initial transport patch through the production native broker and callback bridge. The provider shell wrapper used the then-current production declarations. During equal two-second idle windows, file mode issued **9 remote commands** and HTTP/2 issued **0**. Both variants rotated synthetic credentials between two run bindings and denied idle requests. Cleanup deleted the sandbox. - Daytona component setup took 3.64s for file mode and 4.74s for HTTP/2. This is an idle-traffic reduction, not a startup-speed claim. That component run predates the availability and preparation repair; it does not qualify the repaired paths on Daytona. The new failure controls use a real local HTTP/2 channel. The credential resolver was synthetic and no model was called. Real GitHub-account grants, staging replies, and the end-to-end latency target remain unverified by this component test. - Repaired-head CI found a policy-test false positive: a broad `secrets.` regex matched an upstream filename. The unchanged test reproduced the failure, and the corrected version passed all 15 controls. Master later supplied a stronger direct/bracket check, which we retained when resolving the merge conflict. Review then found that formatted expressions could stop that check at a quoted brace. The new reported-case baseline failed (one failed, 14 passed). The final check treats quoted strings as complete tokens, respects expression boundaries, and passes all 15 cases. The later master conflict also added `toJSON(secrets)` controls. The old branch pattern failed that whole-context case. Review subsequently found exponential backtracking with escaped apostrophes; the final forward scanner retains the quoted-boundary and whole-context controls and passes all 15 tests, including the bounded long-expression regression. No workflow or credential handling changed. All failed attempts remain recorded. - The full local repository test suite was not rerun. [Prior-head full CI](https://github.com/paperclipai/paperclip/actions/runs/38775801821), including `ci / verify` and `ci / e2e`, passed on previous head `e5f8bdb425081cf08110c0f7423f96609e405920`. All 52 check runs succeeded, two optional Storybook jobs skipped, and legacy Snyk passed. Completed Apex review is **5/5** on that exact head, with no unresolved review threads. ## Risks - Only an explicit server-selected opt-in can use HTTP/2. The existing provider-capability, channel-startup and authenticated file-fallback checks remain in the shared bridge. - Toggling the flag or losing the HTTP/2 channel replaces an idle retained provider at the next run boundary. Instruction preparation must make the same retirement decision. Normal ownership, settled history and cleanup checks remain in effect. - Lower idle traffic may reduce provider/controller pressure, but no staging latency improvement is claimed. This feature remains inactive when the existing flag is off. Other preparation and finalization costs remain. - No schema, API, UI, credential-selection, tracing endpoint or default-setting changes. ## Model Used - OpenAI Codex, based on GPT-6, with tool-assisted reasoning and code execution. The exact hosted snapshot ID 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 - [ ] 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> |
||
|
|
341cfbcea8 |
fix(docker): remap the node user without walking the app home (#16475)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The container image sets the node user's home to the app home, `/paperclip`, which is the data volume in container deployments > - When `USER_UID`/`USER_GID` differ from the image, the entrypoint runs `usermod -u` and `usermod -g` before the app starts > - `usermod` also re-owns every file under the user's home, so every boot walks the whole data volume first > - On a large volume this delayed each boot by about six minutes, and platforms that stop the old container before starting the new one serve errors for that whole time > - This pull request runs the remap while the home points at a path that does not exist, then restores it > - The benefit is that a UID/GID remap takes milliseconds, and deploys no longer wait for a full volume walk ## Linked Issues or Issue Description Related: #15992 (skips the full-tree ownership probe on routine boots) and #10530 (ownership before the privilege drop). This removes the remaining per-boot full-tree walk. **What happened?** A deployment sets `USER_UID=1001`/`USER_GID=1001` on an image built with the default 1000. Every boot logs `Updating node UID to 1001`, and the next log line (`Updating node GID to 1001`) appeared six minutes later. During that time the app was not running. The volume holds many agent worktrees and dependency trees. No file had the old owner, so the walk changed nothing. **Expected behavior** Remapping the node account should not depend on the size of the data volume. Ownership repair belongs to the existing tiered ownership probe, which records a marker and skips the full walk on routine boots. **Steps to reproduce** 1. Build the image with the default `USER_UID=1000`. 2. Run it as root with `USER_UID=1001`, `USER_GID=1001` and a large app home. 3. Every boot spends the time of a full home walk in `usermod -o -u` before the app starts. Measured in the runtime image (Debian trixie) with 200,000 files under a user's home: `usermod -u` took 1,204 ms and re-owned every file. With the home moved away first, the three `usermod` calls took 124 ms and changed no file. ## What Changed - `scripts/docker-entrypoint.sh`: when a UID or GID remap is needed, read the node user's home with `getent passwd node` and point it at `/nonexistent/paperclip-uid-remap`. Then run the same UID and GID commands as before, and restore the home. Without a passwd entry, the remap runs as before. The ownership probe and repair logic are unchanged. After a real ID change, the home directory has the old owner, so the shallow probe detects the mismatch on the same boot and repairs the tree once. - `server/src/__tests__/docker-entrypoint.test.ts`: stubs `getent` and adds tests for the call order (UID+GID remap, GID-only remap), for repair after the remap, and for the fallback without a passwd entry. ## Verification - `pnpm exec vitest run src/__tests__/docker-entrypoint.test.ts` (in `server/`): 22 passed. - `sh -n scripts/docker-entrypoint.sh`: passed. - Manual check in the runtime image with a throwaway user and 200,000 files (numbers above). The UID changed, file owners were not touched, and the passwd home was restored. ## Risks - `usermod -d` without `-m` only edits the passwd entry. The home is restored right after the ID change. If the entrypoint fails between the two calls, `set -e` stops the container before the privilege drop, and the next boot sets the home again, so a partial state does not persist past a restart. - Ownership after a real remap now depends on the existing probe instead of `usermod`. The probe's tests already cover the remap, fresh-mount, deep-mismatch and marker cases. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`), 1M context, through Claude Code with tool use (shell, file edits, test runs, live measurement in a running container). ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [ ] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
2aca41464b |
fix(workspaces): preserve compaction and expire standard Git bases (#16476)
## Thinking Path > - Paperclip manages AI agents and their task workspaces. > - Finished tasks can release reproducible Git files and restore them later. > - Unique work must stay on disk, while regenerable dependencies can be removed. > - Linux Git templates include an empty legacy directory that blocked base cleanup. > - The new expiration branch also skipped the existing guarded dependency cleanup. > - This change accepts the standard empty directory and retains that cleanup order. > - Private bases can expire, and protected workspaces still shed regenerable files. ## Linked Issues or Issue Description Refs: #16457 Live acceptance of #16457 removed a worktree but retained its private base because Linux Git created an empty `.git/branches` directory. A pending-base dry run then tried to resolve the already removed worktree and hid the preservation reason. A local integration comparison also showed that enabling Git expiration skipped existing dependency compaction. It deferred pending startup compaction by an hour. Both failures were reproduced before the fix. ## What Changed - Accept `.git/branches` only when it is an empty real directory. Preserve populated entries, files, and symlinks. - Inspect pending base cleanup without resolving an absent worktree. - Run existing guarded compaction before Git expiration. Exclude already closed rows from compaction and retain its pending deadline. - Keep failed expiration checks from reaching legacy whole-workspace deletion. Existing compaction remains independent of Git expiration dry-run mode. - Extend real Git/PostgreSQL tests and retain the acceptance failures in the delivery state note. ## Verification - Linux-template baseline: private-base expiration failed before the fix. The first sandboxed attempt could not bind PostgreSQL; the authorized retry reproduced the behavior. - Compaction baseline: two regression cases failed in 7.67 seconds. They reproduced skipped dependency cleanup and the lost startup deadline. - After the fixes, `pnpm exec vitest run server/src/__tests__/execution-workspace-compaction.test.ts server/src/__tests__/git-workspace-expiration.test.ts server/src/__tests__/terminal-workspace-work.test.ts` passed all 49 cases in 43.79 seconds. - Controls cover unique work, active runs, unfinished descendants, previews, concurrent reopening, interrupted removal, exact-commit restoration, and retryable upstream failures. - Full `pnpm -r typecheck` and `pnpm build` passed for the final commit. All exact-head CI and security checks passed. Greptile is 5/5 with all findings resolved. - Final staging acceptance will let the restarted worker resume the retained base cleanup, then reopen normally at the recorded commit and persist subsequent work. The base has not been manually removed. The superseded preview build was cancelled before dispatching a replacement. ## Risks - Legacy branch definitions can hold unique data. Only an empty real directory qualifies for deletion. - Compaction preserves its existing lifecycle guard and regenerable-directory allowlist. It cannot fall through to legacy deletion after an expiration failure. - Already closed workspaces must only resume their recorded expiration; they do not enter compaction. ## Model Used OpenAI Codex, GPT-6.1-sol, with reasoning, code execution, Git, database, and browser tools. The exact context-window size is not exposed by 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> |
||
|
|
5ec93dae01 |
fix(connectors): avoid redundant run preparation discovery (#16461)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Run preparation selects connector skills and tools under current permissions. > - An assignment check can build an unrelated company tool catalog and wait for a shared discovery slot. > - Native startup repeats assignment discovery to obtain static tool definitions. > - Slack also locks and reconciles an unchanged catalog, then repeats task authorization. > - This pull request removes that redundant startup work and preserves live checks at tool execution. > - The benefit is less preparation work, less contention, and unchanged permission and approval enforcement. ## Linked Issues or Issue Description Related: #16454 (detailed preparation tracing), #16425 (workspace preparation performance). **What happened?** Connector configuration added avoidable work to run preparation. Browser Use assignment authorized the whole company catalog, including other connector families. Four connector resolvers ran in series. Native controller startup repeated all assignment resolution when any connector skill was pinned. Slack reconciled an unchanged catalog under an advisory lock and checked task authority twice. **Expected behavior** Prepare only the connector data needed by the run. Reuse static tool definitions from pinned skills. Preserve current company, assignment, identity, profile, policy, grant, and approval checks when an operation executes. **Steps to reproduce** 1. Configure Browser Use and an unrelated MCP catalog with 500 tools for one company. 2. Resolve Browser Use resources for an authorized task. The old path reads and authorizes the unrelated catalog. 3. Occupy both full discovery slots. The old assignment check waits for those slots. 4. Resolve a linked Slack task repeatedly with an existing current catalog. The old path still acquires a catalog lock and repeats authority checks. **Paperclip version or commit** Base: `68a09dcb5f50e3fb6ceaaeac2706d96db97fc9b3`. **Deployment mode** Observed in hosted native runs. The local reproduction uses a source checkout and real embedded PostgreSQL. ## What Changed - Restrict Browser Use catalog reads and policy decisions to active Browser Use connections. Preserve guest restrictions, reviewed catalog filters, live grants, and deterministic resource order. Keep assignment checks out of the full discovery queue. - Resolve the four independent connector families concurrently. Wait for outstanding work to settle before propagating a failure, and retain registration order. - Register native direct connector tool definitions from pinned connector skills. Keep live assignment resolution and governed execution on every tool call. - Compare Slack catalog hashes before reconciliation. Current catalogs skip the catalog transaction and lock. Check task authority once after current profile checks. Initial setup and upgrades retain locked reconciliation and checks before and after it. - Precompute immutable bundled Slack descriptor hashes. Preserve disabled entries, profile state, removed bindings, and review metadata. - Document the startup paths and add focused contention, revocation, catalog upgrade, and failure-settlement regressions. - Repair a pre-existing CI guard false positive: the filename `docker-compose-secrets.test.mjs` matched `secrets.`. Check GitHub secret expressions instead, with dot, bracket, and whole-context controls. No workflow changes. ## Verification - Conflict resolution: merge `b1566ca494f4af6e8d4418fa37393c5caee02582` from master into head `55140b1609ae7bd21a038eb75a48c2683299e4b4`. Combine both secret-expression guards and regression cases. Preserve current workspace, native recovery, and Daytona changes. All 467 policy tests, 787 focused native/connector/Browser Use tests, and server typecheck passed locally after integration. Fresh full CI passed all 55 checks on this head, with two optional Storybook jobs skipped. Greptile reviewed this exact head at 5/5 with no unresolved threads. - `pnpm -r typecheck` — passed. - `pnpm build` — passed. - Focused native startup, connector concurrency, and AgentMail suites — 763 tests passed. - Complete Browser Use suite — 33 tests passed. Focused Slack catalog, linked task, retained provenance/revocation, and pinned native tool tests passed. The complete runner tool authority suite passed (26 tests). The 1,337-case chat suite passed 1,335 tests and had two failures: the original new transaction-count assertion and a 15-second timeout in the publication-batch case. Both cases passed an isolated rerun. The publication timeout occurred during concurrent local checks; the passing rerun does not erase it. CI must still pass the chat shards. - The first focused run found two incorrect new assertions: policy evaluation performs a second catalog lookup, and an explicit allow policy overrides a profile default. The tests now distinguish the scoped catalog query and revoke the governing policy. The focused Browser Use rerun passed all 33 cases after these assertion fixes. - Full-suite test verification used the CI shards of `pnpm test:run`. Local runs focused on the changed boundaries; the complete repository test command was not duplicated locally. - `node --test '.github/scripts/tests/*.test.mjs'` — all 467 policy tests passed. The CI failure reproduced on the unchanged base guard before this correction. Actual GitHub secret expressions still match the guard. - CI on the final head passed all chat shards, typecheck, build, Runner checks, browser shards, and other test lanes. One existing workspace-runtime case hit `EADDRINUSE` for a randomly selected test listener in server shard 1/12 (1,162 tests passed in that shard). The shard passed its single retry on the same SHA with no source changes. The first failure remains recorded here. Previous reviewed head `7191ac40691ddb1ef38b86665c95cc14c57ea390`: all 55 checks passed, two optional Storybook jobs skipped, and no pending or failed checks. Greptile is 5/5 on this head with no unresolved review threads. The external security scan passed. Local before/after measurements use the old and new implementations against the same fixture. Both paths are warmed. Seven samples alternate execution order. The database is real embedded PostgreSQL. No latency is injected. These measurements do not establish hosted speed. | Local path | Before median (range) | After median (range) | Work reduction | | --- | --- | --- | --- | | Browser Use assignment with 500 unrelated MCP tools | 44.020 ms (33.994–107.328) | 26.502 ms (20.385–99.037) | 26 → 22 statements; unrelated catalog and discovery queue removed | | Slack assignment with current catalog and linked responsible user | 49.723 ms (43.624–80.653) | 22.141 ms (19.781–30.818) | 5 → 2 database transactions; no catalog lock on the current path | The contention regression fails against the old implementation after a bounded five-second wait, then passes against the optimized implementation. It occupies the real global discovery scheduler and verifies that Browser Use assignment completes without releasing those slots. It records catalog SQL to verify connection scope. Revocation cases remove policy, catalog availability, and grants between calls. Existing Slack integration cases exercise retained provenance, removed profile bindings, identity revocation, approved writes, and current responsible-user restrictions. ## Risks - Pinned native tool definitions can remain visible after access is revoked. They confer no authority. Invocation rechecks current assignments and fails closed before an operation executes. Newly available connector families still require a newly admitted skill selection. - Concurrent resolution can increase instantaneous database reads to four connector families per configuration call. It is a fixed bound. Outstanding work settles before a failed preparation returns. - Slack skips reconciliation only when all bundled version hashes and an existing profile match. Missing or changed definitions still use the existing locked path. Operator revocations remain effective. - This PR addresses connector preparation. Workspace transfer, provider startup, copyback, and queue delay can still keep total latency above the user's goal. Hosted improvement needs a deployed before/after trace. - No schema, lockfile, workflow, telemetry event, or authorization cache changes. ## Model Used OpenAI GPT-6 (Codex), with reasoning, repository inspection, code execution, and GitHub tools. The runtime does not expose the exact serving model ID or context window. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
db11d899cf |
fix(native): recover remote sessions while preserving warm runs (#16451)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Native agents keep remote processes and conversation history across warm turns. > - A completed turn can leave handoff authority only in controller memory. > - Losing that controller can block a follow-up even while its sandbox survives. > - This pull request verifies durable ownership and reconnects the exact prior process before handing it to the next run. > - Healthy follow-ups stay warm. Recovery preserves saved work without replaying a completed turn. ## Linked Issues or Issue Description **What happened?** A remote warm turn can finish without suspended backup authority. A replacement controller rejects that prior owner even when its exact process and completed checkpoint remain. A single unavailable identity probe can also stop an otherwise valid reattachment. **Expected behavior** Keep healthy follow-ups warm. Recover a completed session after proving that its controller stopped and that the successor holds current authority. Reuse compatible processes; otherwise save and resume the conversation. Retry unavailable identity reads within one shared deadline. **Steps to reproduce** Use `doc/testing-native-remote-continuity.md`. Its Linux cases execute real turns through PostgreSQL, TLS ingress, and the Rust runner. They kill a separate controller process, recover old checkpoint metadata, and change process configuration. The provider is deterministic. Related: #16432 and #16441 fix live warm-owner handoffs. #16341 adds recovery diagnostics. #14695 covers provider process retention. ## What Changed - Preserve warm process reuse between healthy remote turns. - Verify a committed prior result, stopped controller, matching checkpoint and sandbox, and current task/coordinator ownership before recovering an orphaned warm session. - Reconnect through the prior run route, then use the existing authenticated run attachment to change authority. - Persist a process-configuration digest. Close and save recovered sessions when old or changed configuration cannot support reuse. - Retry unavailable state, marker, and process reads within one 30-second deadline. Keep signals and definitive identity failures single-attempt. - Record closed recovery diagnostics, including unreadable or malformed saved state. - Retire the recovery-only ownership check after verified attachment, so later warm turns use their current authority. - Add real-process Linux regressions and refusal controls. - Expose the existing source-only file-recovery form for retained native holds, with the same board authority, evidence, and exact stop-receipt requirements as legacy holds. Recording recovered files does not change task status or replay a run. ## Verification - `pnpm -r typecheck` passed; the server check was repeated for the third-turn fix. - `pnpm build` passed after the final UI change. The UI typecheck, token gates, and map checks also pass. - Four Linux continuity cases passed: warm reuse, controller loss, legacy checkpoint metadata, and changed configuration. Each creates one conversation and starts exactly three turns, including a healthy follow-up after recovery. The fixture uses expired prior leases; the third turn keeps the runner and provider processes. - Greptile caught the retained recovery gate; the third-turn regression now covers it. An initial test omitted the heartbeat’s saved input and left a test process after failure; the fixture was corrected and its disposable container reset before the passing run. - The current focused executor/restart group passes all 749 tests, including the original failure classifications. - Native file-recovery and UI tests pass (52 tests), including blocked tasks and both native and legacy holds. - After merging current master and retaining its equivalent CI expression check, the CI policy tests pass (15 tests) and all four three-turn Linux continuity cases pass again on `dec418ae55ff21b4fb067132613658cce445f181`. - `pnpm test:run` completed its general server group with 20,572 passes, 103 skips, and three failures: the skill-audit child process and two HTTP test timeouts. The same three suites then passed in isolation (89 tests) without code changes. That local command stopped before later groups; the complete final-head CI, including general, serialized, Runner, build, and browser gates, is green. - Greptile reviewed the combined head `dec418ae55ff21b4fb067132613658cce445f181` at 5/5 with no actionable findings; both earlier findings are resolved. All final-head CI gates and the security scan passed. Staging deployment and retained-incident disposition remain deployment acceptance work. ## Risks - Recovery requires exact durable evidence. Live conflicting controllers, replacement sandboxes, Stop/pause, and uncertain effects must remain blocked. - After controller loss, tool-contract or process-configuration changes can restart the provider. Normal healthy follow-ups retain their processes. - The new idle-recovery path has Linux fake-provider evidence. Live Codex/OpenCode and retained quarantined-state recovery still need staging qualification. - No database schema, telemetry, or runner protocol changes. ## Model Used OpenAI GPT-6 through Codex, with reasoning, repository inspection, code execution, and browser tools. The session does not expose a more specific model version 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 (targeted checks and isolated reruns; full-run limitation recorded above) - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green on `dec418ae55ff21b4fb067132613658cce445f181` - [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> |
||
|
|
59a70dc9e2 |
fix(workspaces): expire reproducible Git files and restore exact commits (#16457)
## Thinking Path > - Paperclip manages AI agents and their task workspaces. > - Isolated Git worktrees and private repository copies remain on the host after tasks finish. > - These files can fill the disk even when every commit is available upstream. > - Cleanup must retain unique files, local Git state, live work, and recovery data. > - This pull request records and verifies an exact-commit restoration recipe before expiration. > - Normal reopening restores that commit even after upstream advances. > - The benefit is lower host disk use while finished tasks remain available for later work. ## Linked Issues or Issue Description Refs #10285 and #2022. Follows #16433 for skill-cache expiration. Related #11936 provides separate host-local cleanup tooling; this change extends the existing company-scoped lifecycle worker and normal workspace reopening. ROADMAP.md does not specify a competing implementation. ## What Changed - Add opt-in expiration for verified Paperclip-owned Git worktrees after their whole task tree has been done or cancelled for 24 hours. - Preserve active and scheduled runs, previews, pending reopening, unresolved recovery, dirty files, unknown ignored files, and unsupported or unique Git state. - Verify an authenticated fetch in a fresh object store. Save repository identity, exact commit, branch, and ownership before any deletion. - Rename eligible files into recorded managed trash. Resume interrupted cleanup under source and lifecycle locks. - Expire private base clones only after consumer, file, reference, reflog, and object checks pass. Preserve shared project base clones. - Rebuild missing bases and worktrees through normal reopening at the recorded commit. Return an explicit retryable error when repository access fails. - Add independent workspace/base switches, dry-run reports, reclaimed-byte counts, and operational documentation. No table, public API, UI, or recovery-retention change. ## Verification - Real local bare remote plus Postgres: create at A, advance upstream to B, expire the worktree and private base, reopen at A, then make and retain a subsequent commit. - Final lifecycle suite: all 20 tests passed in 46.41 seconds at `d72947a2e45be5d0c912debb03c46b15863b4798`. Review fixes cover second-cycle expiration, newer-branch recovery, normal preparation after restoration, branch adoption, and Git administration path controls. Eleven existing branch reconciliation controls also passed. Covers unique files and Git state, live descendants, scheduled runs, recovery, previews, unauthorized access, upstream failure, concurrent reopening, interrupted deletion, shared bases, and nested private worktrees. - Final guard regressions: 3 affected tests passed. Existing workspace/runtime/preparation/source controls: 328 tests passed in 238.69 seconds. - Full repository typecheck and build passed locally. The full local test attempt did not pass: initial native-session cases hit macOS Postgres shared-memory exhaustion, source changes invalidated two discovery cases, and the checkout disappeared near the end. The final-code retry of the remaining skill and unrelated controls passed all 103 tests. All checks passed on this exact head, including typecheck, general and serialized tests, Runner checks, build, canary dry-run, browser E2E, and security scans. Greptile reviewed the head at 5/5 with all findings resolved. - Fixed a blocking CI policy false positive for `docker-compose-secrets.test.mjs`. The test still rejects GitHub secret expressions, including dot, bracket, and multiline forms. Its full 15-test suite passes. - Map validation and `git diff --check` passed. - Staging dry-run inspection, measured reclamation, and ordinary product acceptance remain pending. No live acceptance is claimed. ## Risks - Git restoration depends on continued upstream availability and credentials. Cleanup skips unverified commits; restoration never substitutes the default branch. - File deletion requires multiple conservative proofs. Unknown or unsupported state is retained and can limit reclamation. - Source and lifecycle locks serialize preparation, reopening, and expiration. Remote verification has a deadline but can delay work on the same source. - Start with both expiration switches disabled. When enabled, dry-run remains the default. Rollback disables further deletion while retaining restoration support. ## Model Used OpenAI GPT-6 through Codex, with reasoning, repository inspection, tool use, and code execution. The exact backend model ID 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 (focused tests; full local limitation documented above) - [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> |
||
|
|
25b16e65fc |
fix(observability): detail workspace and connector preparation (#16454)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The server prepares a repository, sandbox workspace, and connector access before an agent can reply. > - Existing traces measure large preparation phases but leave important work inside those phases unclear. > - Repository source materialization and Browser Use discovery can do more work than their callers suggest. > - New detail must preserve access checks, run ownership, and the existing work order. > - This pull request adds fixed OpenTelemetry spans inside those operations. > - Operators can use one deployed trace to select the next performance fix. ## Linked Issues or Issue Description Refs: #16419, #16425, #16428. **What existing behavior does this improve?** Startup traces for server workspace preparation, native sandbox synchronization, and connector assignment resolution. **Current behavior** Workspace and connector aggregate spans can take many seconds. The trace does not separate private source cloning, seed reuse, archive work, progress persistence, or each connector resolver. Browser Use resource selection builds the full authorized tool catalog. The configuration and native backend can each resolve connector assignments. **Proposed behavior** Show the awaited work as fixed child spans. Separate discovery queue wait from catalog construction. Preserve the initiating run context when another run releases a scheduler slot. Add no new lifecycle event writes for workspace detail callbacks. **Reason and benefit** Operators need evidence about the hosted critical path before another performance change. These spans distinguish filesystem work, provider transfer, authorization, transaction entry, locks, and reporting overhead. **Breaking changes** None. Authorization, query order, transaction ownership, synchronization, existing progress events, and task results retain their contracts. The change adds opt-in Observability spans under the existing OTLP endpoint gate. ## What Changed - Trace private source directory setup, Git credentials, clone, requested ref, source identity, and checkout publication. - Trace sandbox source capture, seed cache/replay, shallow clone and archive, overlay copy/archive, verification, upload, warm stamps, and descriptor persistence. - Separate existing awaited progress reporting from useful workspace work. Keep new detail callbacks trace-only. - Trace each connector resolver and Browser Use catalog/grant work. Split discovery queueing, descriptor sources, and policy evaluation. - Trace Slack authority, credential reads, catalog synchronization, transaction entry/settlement, and advisory locks. - Update the real filesystem staging trace expectations and the Honeycomb investigation guide. - Add a real scheduler regression for a queued job released by another run. Assert run ownership, parent spans, and exactly one completed queue span. ## Verification - Passed: server and adapter-utils TypeScript checks (`pnpm --filter @paperclipai/server exec tsc --noEmit` and `pnpm --filter @paperclipai/adapter-utils exec tsc --noEmit`). - Passed: 252 checks across 9 focused files. These cover real filesystem staging, native workspace synchronization, trace ownership, endpoint gating, and workspace preparation. - Passed after review: 26 trace-context, tracing-failure, and discovery-scheduler checks across 3 files (these overlap the first run). The new queued-discovery regression fails when context binding is removed and passes after restoration. An initial submillisecond timestamp assertion was replaced with explicit span-completion order to avoid SDK clock precision assumptions. - Passed on implementation commit `0e68968a4bd61001893d833f281f12bb110edbe1`: `pnpm -r typecheck` and `pnpm build`. The later commit updates only an engine test expectation. - The first CI workspace lane found an old engine trace-tree expectation. It expected the earlier span set. The updated assertion now checks the new detail spans, their parents, and single completion. Its targeted executor check passes. - The full test suite, build, release registry, and end-to-end gates pass in CI on final head `40e37d89ef679109d60a96bb5731a0616efeaa43`: 55 passing checks and 2 optional Storybook skips. The full suite was not duplicated locally after the focused run. - Greptile reviewed the final head at 5/5. No unresolved review threads remain. - After deployment, filter Honeycomb by the hashed run ID and verify `service.version`. Inspect `heartbeat.prepare.workspace`, `runtime_selection.workspace_sync`, and the connector assignment parents. The guide in `doc/observability.md` explains each new family and the nested intervals. - No hosted run has used this new instrumentation yet. Local checks cannot establish hosted latency improvement. ## Risks - Tracing adds spans when an operator configures an OTLP endpoint. Detail remains capped at 16 spans per phase per execution attempt. - Parent and child durations overlap. The documentation explains how to avoid counting them twice. - A discovery job can start in another run's scheduler continuation. The bound callback preserves the initiating async context. - New spans export fixed names and the existing hashed run ID. They add no paths, repository URLs, connector resource IDs, credentials, tool schemas, or exception text. ## Model Used OpenAI Codex, GPT-6 family, with reasoning, tool use, and code execution. The exact served model ID 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> |
||
|
|
86c965ce7a |
fix(skills): expire unused version caches and restore saved contents (#16433)
Expire idle skill-version caches with durable runtime pins, bounded discovery, conservative retention, and database-backed restoration. Co-Authored-By: Dotta <cryppadotta@users.noreply.github.com> Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
60e03c002d |
fix(grok): hold new runs while connection needs recovery (#16445)
## Thinking Path > - Paperclip manages agents and their work. > - Grok Bot assignments use a managed Runner and a verified connection. > - Stop can leave the connection held for remote-work inspection. > - The queue started new runs before it checked that connection. > - Those runs allocated Runners, failed, and caused more preparation attempts. > - This pull request checks readiness at queue admission. > - Pending input waits for recovery without using managed compute. ## Linked Issues or Issue Description Refs #16427 and #16422. The open Grok changes cover the coding adapter and do not duplicate this fix. **What happened?** A real Stop test held the Grok connection for inspection. A second task was pending. Four new preparation attempts acquired managed Runners, then failed because the connection was held. No new external assignment or webhook was sent. Pause stopped the loop, and all leases completed cleanup. **Expected behavior** Keep fresh input in the durable queue until its connection is ready. Do not allocate a Runner to discover a known connection hold. Keep existing session reconciliation under its recorded authority. **Steps to reproduce** 1. Connect Grok Bot and start an assignment. 2. Queue another task for that agent. 3. Stop the first assignment and leave the inspection hold in place. 4. Run queue recovery. The pending task repeatedly starts preparation and fails. **Paperclip version or commit** Observed on packaged Core |
||
|
|
e4ce989099 |
Add opt-in Gmail archive capability (#16331)
## Thinking Path > - Paperclip separates Gmail capabilities by reviewed OAuth scope and tool policy. > - Existing Gmail connections support reading and draft creation but not bounded archiving. > - Gmail archive requires `gmail.modify`, so it must not widen existing read or draft grants. > - This change adds an opt-in archive profile and limits it to removing only the `INBOX` label. > - A final pre-dispatch check preserves that boundary for approved and replayed calls. ## Linked Issues or Issue Description **What existing behavior does this improve?** This improves the existing Gmail connection and tool-governance behavior. **Subsystem affected** Cross-cutting: `packages/shared` and `server`. **Current behavior** Paperclip can read Gmail and create drafts. It does not provide an opt-in, bounded action for archiving a thread. **Proposed behavior** Paperclip offers a separate Read & archive connection. It exposes `unlabel_thread` only for that connection and accepts only `threadId` with `labelIds: ["INBOX"]`. **Reason and benefit** Users can archive processed Gmail threads while sending, trashing, and other label changes remain disabled. **Breaking changes** None. Existing read and draft methods keep their current scopes and defaults. Related work: #15545 adds a unified Google Workspace connector and keeps Gmail draft-only. #15412 adds a separate Gmail REST server without label mutations. Neither PR adds this bounded archive capability. ## What Changed - Added the `gmail.archive` connector profile. - Added managed and customer-owned archive connection methods. - Added an Inbox-only schema and final pre-dispatch argument guard. - Classified archive as destructive and added Ask-first defaults for the archive methods. - Added permission-review records and focused tests. ## Verification - `pnpm --filter @paperclipai/server typecheck` - `pnpm --filter @paperclipai/server build` - `pnpm --filter @paperclipai/shared typecheck` - Focused Vitest suites: 42 tests passed. - App-definition ingestion: 103 definitions and 0 review flags. - `git diff --check` ## Risks The main risk is accidental scope or operation expansion. The new profile is opt-in. Existing profiles retain their scopes. The gateway validates the exact Inbox-only payload immediately before dispatch. Production rollout requires separate OAuth configuration and fresh consent. ## Model Used Unverified — reliable runtime model metadata was not available. Confirm the model attribution before publishing. ## Checklist - [x] I have included a concise design rationale - [x] I have specified that model attribution is unverified - [x] I have checked ROADMAP.md and found no Gmail archive entry - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have described the issue in this PR - [x] I have not referenced internal or instance-local issues - [x] My branch name describes the change - [x] Local tests and builds pass - [x] I have added or updated tests - [x] I have updated relevant documentation and permission records - [x] I have documented risks - [ ] All hosted CI gates are green - [ ] Greptile and human review are complete --------- Co-authored-by: Leia Marshall <leia@paperclip.ing> |
||
|
|
afe5b3418d |
fix(native): verify warm ownership before configuration rotation
Track the exact prior warm-owner run ID separately from mutable configuration. Keep configuration retirement, terminal-state checks and failed-close fences. Add current-v5 handoff regressions and document the recovery boundary. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
d5b1975621 |
fix(native): serialize warm idle expiry with turn admission (#16432)
The warm idle timer no longer closes a warm native session while a continuation has reserved or is preparing the same session scope; it re-arms instead. An idle close that has already started is registered in closingWarmNativeSessions so turn admission waits for the checkpoint, matching the other closers. Co-Authored-By: Paperclip <Paperclip-Paperclip@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |