## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - ACP agents can request permission before reads, process execution, and workspace mutation. > - The runner must apply the configured policy without allowing provider display text to grant authority. > - Runner-owned semantic tools already have a separate run-scoped authorization catalog. > - This pull request defines the local permission decision and the narrow metadata needed to recognize those authorized tools. > - The benefit is a fail-closed permission boundary before an ACPX process can use it. ## Linked Issues or Issue Description **Agent or provider** Qualified Pi, Claude, and Codex ACP servers through the internal ACPX driver. **Why this adapter is useful** ACP providers use permission requests for both ordinary provider operations and runner-owned semantic operations. Paperclip must apply `approve-all`, `approve-reads`, or `deny-all` consistently while keeping semantic authorization bound to structural MCP metadata. **How the agent is invoked** A later pull request will install this policy in the private ACPX runtime host. This pull request does not launch a provider, add a dependency, register an adapter, or change runtime selection. **Additional context** This pull request is stacked on #12390. Pi uses a different bridge and never receives semantic auto-approval through this ACP permission path. ## What Changed - Map each ACPX permission mode to a closed runtime policy. - Decide local allow, reject, or coordinator delegation outcomes. - Auto-approve only runner-owned semantic MCP calls identified by structural metadata. - Ignore provider display titles when determining semantic authority. - Limit Codex blanket MCP approval to sessions where every configured MCP server is runner-owned. - Add table-driven tests for all modes, agents, metadata shapes, spoofed titles, and non-runner servers. ## Verification - Runner TypeScript typecheck — passed. - Runner TypeScript tests — passed, including 10 new permission-policy assertions. - `pnpm -r typecheck` — passed for all applicable workspaces. - `pnpm build` — passed, including runner binary, server, UI, and workspace packages. - Prettier and `git diff --check` — passed. - The diff contains 2 files and does not change `pnpm-lock.yaml`, a workflow, a dependency, a public export, server selection, or UI behavior. ## Risks The main risk is mistaking a provider-controlled label for an authorized semantic tool. The implementation ignores display titles and requires a runner-owned MCP server name, a transport tool name, or provider metadata. All other `approve-reads` mutations delegate to the coordinator, and the caller must reject them when no delegate exists. ## Model Used OpenAI Codex with GPT-5 and repository tool use. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either linked an existing public item or described the issue in this PR - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal task identifier - [x] I have run the affected tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have documented the permission and semantic-authorization boundary - [ ] All applicable GitHub Actions are green - [ ] Greptile is 5/5 with every actionable comment resolved - [x] I will address all review findings before requesting merge |
||
|---|---|---|
| .. | ||
| generated | ||
| protocol | ||
| runner | ||
| scripts | ||
| src | ||
| test | ||
| .gitignore | ||
| README.md | ||
| SEMANTIC_ACTIONS.md | ||
| package.json | ||
| rust-toolchain.toml | ||
| tsconfig.json | ||
| vitest.config.ts | ||
README.md
Paperclip Runner
This private workspace package contains the staged Paperclip Runner work.
The package currently exposes the language-neutral PRP v1 TypeScript contract, provider-neutral structured questions and responses, deterministic fixture validation/replay, structured-result normalization, and the session reducer oracle. It also contains a package-local Rust runner, scripted fake harness, bounded process supervisor, cross-language replay oracle, and durable PRP transport. The transport authenticates and encrypts loopback WebSocket sessions, persists an ACK-driven outbox and command journal, and reconnects with a short-lived lease. The Rust runner now includes a Codex-only app-server provider bridge with durable thread resume, cancellation, structured questions, and provider-neutral event normalization. The root surface now also exposes an authenticated durable PRP authority for server-side use. It stores only bootstrap and reconnect credential digests, validates immutable run identity on every connection and event, and persists commands and cumulative event ACK state across server restarts. The package also publishes the canonical semantic action declarations and their input and output schemas. Its package-local dispatcher projects only bound, run-authorized actions and emits redacted semantic receipts.
The first and only installed provider is Codex. Dynamic semantic tools remain
undiscoverable unless the hidden server coordinator projects one of the five
same-task read bindings for an already persisted native Codex run. Catalog
membership alone does not grant authority. The server can now create and start
a Codex-backed native run only through the default-off paperclip_runner
adapter. See
SEMANTIC_ACTIONS.md for the catalog boundary.
The package has two initial public surfaces:
@paperclipai/paperclip-runnercontains runtime contracts, validation, replay/reducer logic, the semantic catalog, the authorization dispatcher, and the Node-only durable server authority.@paperclipai/paperclip-runner/testingadds Node-only fixture loading and a provider-neutral semantic conformance kit for deterministic test adapters.
No SDK, browser, React, eval, live-console, lab, or provider-experiment entry
point is exported. The package remains private in this wave. The server route
at /api/runner/v1/connect/:runId has no authority until the hidden coordinator
registers an exact existing run binding. Fresh native starts are rejected
unless the instance enableNativeRunner flag is enabled. Existing direct
adapters keep their original execution path.
The package build compiles the release paperclip-runnerd executable and
stages it under dist/bin. The normal server build vendors that directory, so
an installed server does not depend on a separate system Rust installation or
a manually copied binary. pnpm-lock.yaml remains under the repository's
existing lockfile process.
Run the complete contract gate with:
pnpm --filter @paperclipai/paperclip-runner check:protocol
Run the Rust runner gate with:
pnpm --filter @paperclipai/paperclip-runner check:runner
This command checks Rust formatting, builds and tests the minimal workspace in
release mode, verifies bounded process cleanup, launches the real
paperclip-runnerd binary through the fake harness, and compares the Rust
conformance and replay summaries with the shared fixtures. The checked-in Cargo
lock and pinned Rust toolchain keep this verification reproducible.
Durability and failure semantics are documented in
runner/DURABLE_TRANSPORT.md. The fault suite
drops a connection before its event ACK, reconnects with the bound lease,
replays the same event, and proves the duplicated command effect ran once.
Codex launch, resume, cancellation, and normalization behavior is documented in
runner/CODEX_PROVIDER.md.
Use generate:protocol-manifest after a schema or fixture change,
generate:protocol-types after a schema change, and
generate:replay-goldens after an intentional reducer change. Commit generated
outputs with their sources; do not edit them by hand.
Use generate:semantic-action-catalog after changing a semantic action
declaration. Its checked-in JSON inventory must land with the source change.
The gate compiles every schema with AJV 2020-12, validates accepted fixtures, rejects unsupported required versions, checks generated TypeScript schema drift, runs the TypeScript contract tests, and compares reducer snapshots and parity summaries byte-for-byte with their checked-in golden files.