forked from farhoodlabs/paperclip
1d8c7a09b85b80eaa1616e302914b3d18bdc197f
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
24232078fd |
fix(adapters/registry): honor module-provided sessionManagement for external adapters (#4296)
## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies > - Adapters are how paperclip hands work off to specific agent runtimes; since #2218, external adapter packages can ship as npm modules loaded via `server/src/adapters/plugin-loader.ts` > - Each `ServerAdapterModule` can declare `sessionManagement` (`supportsSessionResume`, `nativeContextManagement`, `defaultSessionCompaction`) — but the init-time load at `registry.ts:363-369` hard-overwrote it with a hardcoded-registry lookup that has no entries for external types, so modules could not actually set these fields > - The hot-install path at `routes/adapters.ts:179` → `registerServerAdapter` preserves module-provided `sessionManagement`, so externals worked after `POST /api/adapters/install` — *until the next server restart*, when the init-time IIFE wiped it back to `undefined` > - #2218 explicitly deferred this: *"Adapter execution model, heartbeat protocol, and session management are untouched."* This PR is the natural follow-up for session management on the plugin-loader path > - This PR aligns init-time registration with the hot-install path: honor module-provided `sessionManagement` first, fall back to the hardcoded registry when absent (so externals overriding a built-in type still inherit its policy). Extracted as a testable helper with three unit tests > - The benefit is external adapters can declare session-resume capabilities consistently across cold-start and hot-install, without requiring upstream additions to the hardcoded registry for each new plugin ## What Changed - `server/src/adapters/registry.ts`: extracted the merge logic into a new exported helper `resolveExternalAdapterRegistration()` — honors module-provided `sessionManagement` first, falls back to `getAdapterSessionManagement(type)`, else `undefined`. The init-time IIFE calls the helper instead of inlining an overwrite. - `server/src/adapters/registry.ts`: updated the section comment (lines 331–340) to reflect the new semantics and cross-reference the hot-install path's behavior. - `server/src/__tests__/adapter-registry.test.ts`: new `describe("resolveExternalAdapterRegistration")` block with three tests — module-provided value preserved, registry fallback when module omits, `undefined` when neither provides. ## Verification Targeted test run from a clean tree on `fix/external-session-management`: ``` cd server && pnpm exec vitest run src/__tests__/adapter-registry.test.ts # 1 test file, 15 tests passed, 0 failed (12 pre-existing + 3 new) ``` Full server suite via the independent review pass noted under Model Used: **1,156 tests passed, 0 failed**. Typecheck note: `pnpm --filter @paperclipai/server exec tsc --noEmit` surfaces two errors in `src/services/plugin-host-services.ts:1510` (`createInteraction` + implicit-any). Verified by `git stash` + re-run on clean `upstream/master` — they reproduce without this PR's changes. Pre-existing, out of scope. ## Risks - **Low behavioral risk.** Strictly additive: externals that do NOT provide `sessionManagement` continue to receive exactly the same value as before (registry lookup → `undefined` for pure externals, or the builtin's entry for externals overriding a built-in type). Only a new capability is unlocked; no existing behavior changes for existing adapters. - **No breaking change.** `ServerAdapterModule.sessionManagement` was already optional at the type level. Externals that never set it see no difference on either path. - **Consistency verified.** Init-time IIFE now matches the post-`POST /api/adapters/install` behavior — a server restart no longer regresses the field. ## Note This is part of a broader effort to close the parity gap between external and built-in adapters. Once externals reach 1:1 capability coverage with internals, new-adapter contributions can increasingly be steered toward the external-plugin path instead of the core product — a trajectory CONTRIBUTING.md already encourages ("*If the idea fits as an extension, prefer building it with the plugin system*"). ## Model Used - **Provider**: Anthropic - **Model**: Claude Opus 4.7 - **Exact model ID**: `claude-opus-4-7` (1M-context variant: `claude-opus-4-7[1m]`) - **Context window**: 1,000,000 tokens - **Harness**: Claude Code (Anthropic's official CLI), orchestrated by @superbiche as human-in-the-loop. Full file-editing, shell, and `gh` tool use, plus parallel research subagents for fact-finding against paperclip internals (plugin-loader contract, sessionCodec reachability, UI parser surface, Cline CLI JSON schema). - **Independent local review**: Gemini 3.1 Pro (Google) performed a separate verification pass on the committed branch — confirmed the approach & necessity, ran the full workspace build, and executed the complete server test suite (1,156 tests, all passing). Not used for authoring; second-opinion pass only. - **Authoring split**: @superbiche identified the gap (while mapping the external-adapter surface for a downstream adapter build) and shaped the plan — categorising the surface into `works / acceptable / needs-upstream` buckets, directing the surgical-diff approach on a fresh branch from `upstream/master`, and calling the framing ("alignment bug between init-time IIFE and hot-install path" rather than "missing capability"). Opus 4.7 executed the fact-finding, the diff, the tests, and drafted this PR body — all under direct review. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work (convention-aligned bug fix on the external-adapter plugin path introduced by #2218) - [x] I have run tests locally and they pass (15/15 in the touched file; 1,156/1,156 full server suite via the independent Gemini 3.1 Pro review) - [x] I have added tests where applicable (3 new for the extracted helper) - [x] If this change affects the UI, I have included before/after screenshots (no UI touched) - [x] I have updated relevant documentation to reflect my changes (in-file comment reflects new semantics) - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge |
||
|
|
8d0c3d2fe6 |
fix(hermes): inject agent JWT into Hermes adapter env to fix identity attribution (#3608)
## Thinking Path > - Paperclip orchestrates AI agents and records their actions through auditable issue comments and API writes. > - The local adapter registry is responsible for adapting each agent runtime to Paperclip's server-side execution context. > - The Hermes local adapter delegated directly to `hermes-paperclip-adapter`, whose current execution context type predates the server `authToken` field. > - Without explicitly passing the run-scoped agent token and run id into Hermes, Hermes could inherit a server or board-user `PAPERCLIP_API_KEY` and lack a usable `PAPERCLIP_RUN_ID` for mutating API calls. > - That made Paperclip writes from Hermes agents risk appearing under the wrong identity or without the correct run-scoped attribution. > - This pull request wraps the Hermes execution call so Hermes receives the agent run JWT as `PAPERCLIP_API_KEY` and the current execution id as `PAPERCLIP_RUN_ID` while preserving explicit adapter configuration where appropriate. > - Follow-up review fixes preserve Hermes' built-in prompt when no custom prompt template exists and document the intentional type cast. > - The benefit is reliable agent attribution for the covered local Hermes path without clobbering Hermes' default heartbeat/task instructions. ## What Changed - Wrapped `hermesLocalAdapter.execute` so `ctx.authToken` is injected into `adapterConfig.env.PAPERCLIP_API_KEY` when no explicit Paperclip API key is already configured. - Injected `ctx.runId` into `adapterConfig.env.PAPERCLIP_RUN_ID` so the auth guard's `X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID` instruction resolves to the current run id. - Added a Paperclip API auth guard to existing custom Hermes `promptTemplate` values without creating a replacement prompt when no custom template exists. - Documented the intentional `as unknown as` cast needed until `hermes-paperclip-adapter` ships an `AdapterExecutionContext` type that includes `authToken`. - Added registry tests for JWT injection, run-id injection, explicit key preservation, default prompt preservation, and the no-`authToken` early-return path. ## Verification - [x] `pnpm --filter "./server" exec vitest run adapter-registry` - 8 tests passed. - [x] `pnpm --filter "./server" typecheck` - passed. - [x] Trigger a Hermes agent heartbeat and verify Paperclip writes appear under the agent identity rather than a shared board-user identity, with the correct run id on mutating requests. ## Risks - Low migration risk: this changes only the Hermes local adapter wrapper and tests. - Existing explicit `adapterConfig.env.PAPERCLIP_API_KEY` values are preserved to avoid breaking intentionally configured agents. - `PAPERCLIP_RUN_ID` is set from `ctx.runId` for each execution so mutating API calls use the current run id instead of a stale or literal placeholder value. - Prompt behavior is intentionally conservative: the auth guard is only prepended when a custom prompt template already exists, so Hermes' built-in default prompt remains intact for unconfigured agents. - Remaining operational risk: the identity and run-id behavior should still be verified with a live Hermes heartbeat before relying on it in production. ## Model Used - OpenAI Codex, GPT-5 family coding agent, tool use enabled for local shell, GitHub CLI, 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 run tests locally and they pass - [x] I have added or updated tests where applicable - [x] If this change affects the UI, I have included before/after screenshots (not applicable: backend-only change) - [x] I have updated relevant documentation to reflect my changes (not applicable: no product docs changed; PR description updated) - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> Co-authored-by: Dotta <bippadotta@protonmail.com> |
||
|
|
50cd76d8a3 |
feat(adapters): add capability flags to ServerAdapterModule (#3540)
## Thinking Path > - Paperclip orchestrates AI agents via adapters (`claude_local`, `codex_local`, etc.) > - Each adapter type has different capabilities — instructions bundles, skill materialization, local JWT — but these were gated by 5 hardcoded type lists scattered across server routes and UI components > - External adapter plugins (e.g. a future `opencode_k8s`) cannot add themselves to those hardcoded lists without patching Paperclip source > - The existing `supportsLocalAgentJwt` field on `ServerAdapterModule` proves the right pattern already exists; it just wasn't applied to the other capability gates > - This pull request replaces the 4 remaining hardcoded lists with declarative capability flags on `ServerAdapterModule`, exposed through the adapter listing API > - The benefit is that external adapter plugins can now declare their own capabilities without any changes to Paperclip source code ## What Changed - **`packages/adapter-utils/src/types.ts`** — added optional capability fields to `ServerAdapterModule`: `supportsInstructionsBundle`, `instructionsPathKey`, `requiresMaterializedRuntimeSkills` - **`server/src/routes/agents.ts`** — replaced `DEFAULT_MANAGED_INSTRUCTIONS_ADAPTER_TYPES` and `ADAPTERS_REQUIRING_MATERIALIZED_RUNTIME_SKILLS` hardcoded sets with capability-aware helper functions that fall back to the legacy sets for adapters that don't set flags - **`server/src/routes/adapters.ts`** — `GET /api/adapters` now includes a `capabilities` object per adapter (all four flags + derived `supportsSkills`) - **`server/src/adapters/registry.ts`** — all built-in adapters (`claude_local`, `codex_local`, `process`, `cursor`) now declare flags explicitly - **`ui/src/adapters/use-adapter-capabilities.ts`** — new hook that fetches adapter capabilities from the API - **`ui/src/pages/AgentDetail.tsx`** — replaced hardcoded `isLocal` allowlist with `capabilities.supportsInstructionsBundle` from the API - **`ui/src/components/AgentConfigForm.tsx`** / **`OnboardingWizard.tsx`** — replaced `NONLOCAL_TYPES` denylist with capability-based checks - **`server/src/__tests__/adapter-registry.test.ts`** / **`adapter-routes.test.ts`** — tests covering flag exposure, undefined-when-unset, and per-adapter values - **`docs/adapters/creating-an-adapter.md`** — new "Capability Flags" section documenting all flags and an example for external plugin authors ## Verification - Run `pnpm test --filter=@paperclip/server -- adapter-registry adapter-routes` — all new tests pass - Run `pnpm test --filter=@paperclip/adapter-utils` — existing tests still pass - Spin up dev server, open an agent with `claude_local` type — instructions bundle tab still visible - Create/open an agent with a non-local type — instructions bundle tab still hidden - Call `GET /api/adapters` and verify each adapter includes a `capabilities` object with the correct flags ## Risks - **Low risk overall** — all new flags are optional with backwards-compatible fallbacks to the existing hardcoded sets; no adapter behaviour changes unless a flag is explicitly set - Adapters that do not declare flags continue to use the legacy lists, so there is no regression risk for built-in adapters - The UI capability hook adds one API call to AgentDetail mount; this is a pre-existing endpoint, so no new latency path is introduced ## Model Used - Provider: Anthropic - Model: Claude Sonnet 4.6 (`claude-sonnet-4-6`) - Context: 200k token context window - Mode: Agentic tool use (code editing, bash, grep, file reads) ## 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 run tests locally and they pass - [x] I have added or updated tests where applicable - [ ] If this change affects the UI, I have included before/after screenshots - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Pawla Abdul (Bot) <pawla@groombook.dev> Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
d9476abecb |
fix(adapters): honor paused overrides and isolate UI parser state
Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
2a2fa31a03 |
feat(adapters): allow external plugins to override built-in adapters
Previously external adapters matching a built-in type were skipped with a warning. Now they override the built-in, so plugin developers can ship improved versions of existing adapters (e.g. hermes-paperclip-adapter) without removing the built-in fallback for users who haven't installed the plugin. |
||
|
|
14d59da316 |
feat(adapters): external adapter plugin system with dynamic UI parser
- Plugin loader: install/reload/remove/reinstall external adapters from npm packages or local directories - Plugin store persisted at ~/.paperclip/adapter-plugins.json - Self-healing UI parser resolution with version caching - UI: Adapter Manager page, dynamic loader, display registry with humanized names for unknown adapter types - Dev watch: exclude adapter-plugins dir from tsx watcher to prevent mid-request server restarts during reinstall - All consumer fallbacks use getAdapterLabel() for consistent display - AdapterTypeDropdown uses controlled open state for proper close behavior - Remove hermes-local from built-in UI (externalized to plugin) - Add docs for external adapters and UI parser contract |