paperclip/packages/adapters/openclaw-gateway
Joonyoung Park c54936e2e9
fix(openclaw-gateway): use per-agent claimedApiKeyPath in wake text (#4668)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The `openclaw-gateway` adapter wakes a remote agent over a WebSocket
gateway. It sends a wake prompt. That prompt tells the agent which
environment variables to set and which file holds its Paperclip API key.
> - Each agent stores its claimed key in its own JSON file. The adapter
already exposes a `claimedApiKeyPath` config field for this. The field
is documented in `src/index.ts`. It also has an input in the agent
settings UI.
> - `buildWakeText` ignored that field. It hardcoded the shared default
path into the wake prompt text.
> - Every agent therefore read the same key file at wake time. Agents
authenticated as the wrong identity. The first API call failed.
> - This pull request passes `ctx.config.claimedApiKeyPath` into
`buildWakeText`. It uses the existing `resolveClaimedApiKeyPath` helper.
That helper falls back to the documented default.
> - The benefit is that each agent reads its own claimed-key file. Each
agent authenticates as itself.

## Linked Issues or Issue Description

Fixes #10071
Fixes #4976
Fixes #3098
Fixes #8076

These four open issues report the same defect. Earlier duplicates are
already closed: Refs #2561, Refs #2592, Refs #930.

Related pull requests that address the same root problem (duplicate
search):

- #3396 — same core change, no tests
- #3370 — heavier approach, injects `PAPERCLIP_CLAIMED_API_KEY_PATH`
into the wake env and adds server onboarding defaults
- #5970 — renames the config field to `paperclipApiKeyPath`
- #8072 — same core change, bundled with an unrelated protocol-version
change
- #784 — adds shell quoting and preflight instructions
- #3296 — bundled with an unrelated Claude hello-probe fix

## What Changed

- `packages/adapters/openclaw-gateway/src/server/execute.ts`
- `buildWakeText` now accepts `claimedApiKeyPath` as a parameter. It no
longer hardcodes the path.
- The `execute` call site passes
`resolveClaimedApiKeyPath(ctx.config.claimedApiKeyPath)`. That helper
returns the documented default
`~/.openclaw/workspace/paperclip-claimed-api-key.json` when the agent
sets no override.
  - `resolveClaimedApiKeyPath` is now exported so tests can call it.
- `packages/adapters/openclaw-gateway/src/server/execute.test.ts` — adds
`resolveClaimedApiKeyPath` cases: a configured value, an empty string, a
whitespace-only string, `undefined`, `null`, and non-string input.
- `packages/adapters/openclaw-gateway/vitest.config.ts` (new) —
package-level vitest config. It matches the config used by sibling
adapters such as `opencode-local`.
- `vitest.config.ts` (root) — adds the adapter to the workspace project
list.
- `scripts/run-vitest-stable.mjs` — adds
`@paperclipai/adapter-openclaw-gateway` to `nonServerProjects`.
**Maintainer-added during rebase.** The CI test lanes do not run a bare
`vitest`. They call `run-vitest-stable.mjs`, which invokes vitest with
an explicit `--project` allowlist. Without this entry the CI lanes skip
this package, and the root project-list entry alone has no effect on CI.

## Verification

Run the package suite directly:

```
pnpm install --frozen-lockfile
pnpm exec vitest run --project @paperclipai/adapter-openclaw-gateway
```

The suite covers `resolveSessionKey`, `buildAgentParams`, and the new
`resolveClaimedApiKeyPath` cases. The first two already existed in this
file but never executed in CI before this change.

Typecheck the package:

```
pnpm --filter @paperclipai/adapter-openclaw-gateway typecheck
```

Behavioural check, which no automated test covers:

1. Set `claimedApiKeyPath` to a per-agent value such as
`~/.openclaw/workspace/paperclip-keys/<agent>.json` in the agent's
gateway adapter settings.
2. Trigger a wake for that agent.
3. Confirm the rendered wake text names that file. It must not name the
shared default.

Maintainer note: this branch was rebased onto current `master` by a
maintainer. The original branch was two months stale. Only two conflicts
occurred, both additive: the import line and the tail of
`execute.test.ts`, and the project list in the root `vitest.config.ts`.
The `execute.ts` change applied without conflict. CI and Greptile re-run
against the rebased head.

## Risks

- Low for existing deployments. `resolveClaimedApiKeyPath` preserves the
default path exactly. Any agent that never set `claimedApiKeyPath`
receives the same wake text as before.
- The behaviour changes only for agents that already set a per-agent
path. Those agents previously received the wrong instruction. They now
receive the correct one.
- No database, schema, or API surface changes.
- CI now runs this package's test file for the first time. That file
includes the pre-existing `resolveSessionKey` and `buildAgentParams`
tests, which were never executed before.
- Five other adapters (`cursor-cloud`, `cursor-local`, `gemini-local`,
`grok-local`, `pi-local`) sit in the root project list but remain absent
from the CI allowlist. This pull request does not change them. That gap
is tracked separately.

## Model Used

- Contributor's change: Anthropic Claude, model ID `claude-opus-4-7`,
approximately 200K context, extended thinking. Used for triage, patch
authoring, and the original description.
- Rebase, the `run-vitest-stable.mjs` entry, and this description:
Anthropic Claude, model ID `claude-opus-5`, tool use enabled. Run by a
Paperclip maintainer.

## 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
— the branch name carries an internal ticket id. A fork branch cannot be
renamed without opening a new pull request, so this is left as-is. The
internal reference has been removed from the description.
- [ ] I have run tests locally and they pass — the contributor verified
the pre-rebase branch. The rebased head is verified by CI on this pull
request.
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes —
`claimedApiKeyPath` is already documented in `src/index.ts` and exposed
in the agent settings UI, so no documentation change is needed
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green — pending the post-rebase run
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups —
pending re-review of the rebased head
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Pieter (CTO) <pieter@openclaw.local>
Co-authored-by: Andrew Aymeloglu <aaymeloglu@gmail.com>
2026-08-05 00:53:43 -05:00
..
doc Introduce bind presets for deployment setup 2026-04-11 07:09:07 -05:00
src fix(openclaw-gateway): use per-agent claimedApiKeyPath in wake text (#4668) 2026-08-05 00:53:43 -05:00
CHANGELOG.md chore: release v0.3.1 2026-03-12 13:09:22 -05:00
README.md PAPA-430: workspace finalize gates + no-remote-git enforcement (#6969) 2026-05-29 08:25:29 -07:00
package.json build(deps-dev): bump @types/node from 22.19.21 to 22.20.1 (#10304) 2026-07-28 10:48:45 -07:00
tsconfig.json Fix root TypeScript solution config 2026-03-09 14:09:30 -05:00
vitest.config.ts fix(openclaw-gateway): use per-agent claimedApiKeyPath in wake text (#4668) 2026-08-05 00:53:43 -05:00

README.md

OpenClaw Gateway Adapter

This document describes how @paperclipai/adapter-openclaw-gateway invokes OpenClaw over the Gateway protocol.

Transport

This adapter always uses WebSocket gateway transport.

  • URL must be ws:// or wss://
  • Connect flow follows gateway protocol:
  1. receive connect.challenge
  2. send req connect (protocol/client/auth/device payload)
  3. send req agent
  4. wait for completion via req agent.wait
  5. stream event agent frames into Paperclip logs/transcript parsing

Auth Modes

Gateway credentials can be provided in any of these ways:

  • authToken / token in adapter config
  • headers.x-openclaw-token
  • headers.x-openclaw-auth (legacy)
  • password (shared password mode)

When a token is present and authorization header is missing, the adapter derives Authorization: Bearer <token>.

Device Auth

By default the adapter sends a signed device payload in connect params.

  • set disableDeviceAuth=true to omit device signing
  • set devicePrivateKeyPem to pin a stable signing key
  • without devicePrivateKeyPem, the adapter generates an ephemeral Ed25519 keypair per run
  • when autoPairOnFirstConnect is enabled (default), the adapter handles one initial pairing required by calling device.pair.list + device.pair.approve over shared auth, then retries once.

Session Strategy

The adapter supports the same session routing model as HTTP OpenClaw mode:

  • sessionKeyStrategy=issue|fixed|run
  • sessionKey is used when strategy is fixed

Resolved session key is sent as agent.sessionKey.

Payload Mapping

The agent request is built as:

  • required fields:
    • message (wake text plus optional payloadTemplate.message/payloadTemplate.text prefix)
    • idempotencyKey (Paperclip runId)
    • sessionKey (resolved strategy)
  • optional additions:
    • all payloadTemplate fields merged in
    • agentId from config if set and not already in template

Timeouts

  • timeoutSec controls adapter-level request budget
  • waitTimeoutMs controls agent.wait.timeoutMs

If agent.wait returns timeout, adapter returns openclaw_gateway_wait_timeout.

Log Format

Structured gateway event logs use:

  • [openclaw-gateway] ... for lifecycle/system logs
  • [openclaw-gateway:event] run=<id> stream=<stream> data=<json> for event agent frames

UI/CLI parsers consume these lines to render transcript updates.

No-remote-git contract

Like every Paperclip adapter, this one must treat the local execution-workspace cwd as the only persistence boundary across runs — no git push from runtime code, no assuming a git remote exists. The gateway transport here doesn't touch the workspace directly, but if you extend the adapter to ship code to the OpenClaw side, use the round-trip helpers in @paperclipai/adapter-utils (prepareWorkspaceForSshExecution → restoreWorkspaceFromSshExecution) rather than reaching for a git remote. See packages/adapters/AUTHORING.md for the full contract and the pinning test at packages/adapter-utils/src/ssh-fixture.test.ts.