feat(gateway): accept opaque paperclip envelope on agent params#75901
feat(gateway): accept opaque paperclip envelope on agent params#75901bzanghi wants to merge 1 commit into
Conversation
External orchestrators (currently Paperclip's `openclaw_gateway` adapter) attach an opaque `paperclip` envelope at the root of every `agent` request. The gateway never reads the field, but the strict `AgentParamsSchema` (`additionalProperties: false`) was rejecting the whole request with `at root: unexpected property 'paperclip'`, blocking every wake. Declare `paperclip: Type.Optional(Type.Unknown())` so the field is accepted (and ignored) without weakening strictness for any other unknown field. Mirrors the forward-compat stance already taken by `WakeParamsSchema` (`additionalProperties: true`). Adds three `validateAgentParams` tests covering the minimal shape, the new envelope, and that other unknown root fields still fail. AI-assisted (Claude Code), fully tested locally: - pnpm exec vitest run src/gateway/protocol/index.test.ts (42 passed) - pnpm exec vitest run src/gateway/server-methods/agent.test.ts (164 passed)
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. Close as duplicate/superseded. The Paperclip So I’m closing this here and keeping the remaining discussion on the canonical linked item. Review detailsBest possible solution: Consolidate review on #74974 or the underlying Paperclip report, then land one approved Gateway protocol change with schema, generated client artifacts, tests, and changelog synchronized. Do we have a high-confidence way to reproduce the issue? Yes. A Gateway Is this the best way to solve the issue? No for this PR as the merge vehicle. The schema tolerance may be the narrow mechanical fix, but this branch duplicates #74974 and omits generated protocol model and changelog updates required for a complete Gateway protocol change. Security review: Security review cleared: The diff widens validation for one ignored opaque field and adds tests, with no CI, dependency, credential, code execution, or supply-chain surface changed. What I checked:
Likely related people:
Codex review notes: model gpt-5.5, reasoning high; reviewed against d2f623d5604c. |
…tadata The first commit on this branch added `paperclip: Type.Optional(Type.Unknown())` to AgentParamsSchema as a vendor-named root-level tolerance field. The review on openclaw#74974 (and the surrounding history of openclaw#62102, openclaw#74635, openclaw#72565, openclaw#69139, openclaw#75901 — three earlier PRs proposing the same vendor-named shape, all closed without merge) makes the underlying request clear: the right contract is a generic opaque-metadata namespace any external agent or plugin adapter can write under, not a name-per-vendor field that grows the gateway protocol surface every time a new adapter ships. This commit lands that namespace alongside the existing alias: - AgentParamsSchema gains `adapterMeta: Type.Optional(Type.Record( Type.String(), Type.Unknown()))`. Each adapter writes its own top-level key under it (`adapterMeta: { paperclip: {...}, greptile: {...} }`); the gateway accepts the bag as opaque and never inspects it. - The existing `paperclip: Type.Optional(Type.Unknown())` field is kept as a backward-compatibility alias, with a comment documenting that new adapter metadata should use `adapterMeta` and that this field can be removed once the published Paperclip adapter cuts a release that nests its payload under `adapterMeta.paperclip`. Heartbeats from current Paperclip releases keep dispatching during the transition. - `additionalProperties: false` on AgentParamsSchema is left intact, so a typo'd root field (e.g. `idempotnecyKey`) still fails validation before dispatch — the fix doesn't widen the schema, it just adds two named landing pads. Tests in src/gateway/protocol/agent-params-validator.test.ts now cover: - adapterMeta with multiple adapter keys (paperclip + greptile) accepted - empty adapterMeta and adapterMeta with primitive values accepted - both surfaces side by side (paperclip + adapterMeta.paperclip) accepted - adapterMeta with a non-object value rejected - existing paperclip-only path unchanged - truly unknown root properties still rejected - canonical message + idempotencyKey still required Swift bindings in apps/shared/OpenClawKit/Sources/OpenClawProtocol/ GatewayModels.swift mirror the new field as `adaptermeta: [String: AnyCodable]?` in the four codegen sites (declaration, init signature, init body, CodingKeys = "adapterMeta"), matching the pattern used for `inputprovenance` and the existing `paperclip` mirror. CHANGELOG.md gets a `Gateway/protocol:` entry under `## Unreleased` describing the namespace + alias and citing openclaw#74635 and openclaw#62102 as the issues being closed. Refs openclaw#74974, openclaw#74635, openclaw#62102
… alias on AgentParamsSchema
Paperclip and other external agents inject a root-level metadata blob
when invoking agents via the openclaw_gateway adapter, but
AgentParamsSchema is declared with additionalProperties: false so the
gateway rejects the entire payload before dispatch:
invalid agent params: at root: unexpected property 'paperclip'
Reported by openclaw#74635 (Paperclip rev 3494e84 vs OpenClaw 2026.4.26
be8c246) and openclaw#62102. Three earlier PRs proposed a vendor-named root
field (openclaw#69139, openclaw#72565, openclaw#75901) - all closed without merge - so this
lands the fix as a generic namespace plus a backward-compatibility
alias instead.
Schema (src/gateway/protocol/schema/agent.ts)
- AgentParamsSchema gains 'adapterMeta: Type.Optional(Type.Record(
Type.String(), Type.Unknown()))'. Each external agent or plugin
adapter writes under its own top-level key here
(adapterMeta: { paperclip: {...}, greptile: {...} }); the gateway
accepts the bag as-is and never inspects it. New adapters can land
protocol-side without growing the schema.
- AgentParamsSchema also gains 'paperclip: Type.Optional(Type.Unknown())'
as a backward-compatibility alias, with a comment marking it for
removal once the published Paperclip adapter cuts a release that
nests under adapterMeta.paperclip. Heartbeats from current Paperclip
releases keep dispatching during the transition.
- additionalProperties: false on AgentParamsSchema is intentionally
untouched. Truly unknown root fields (e.g. typo'd 'idempotnecyKey')
still fail validation before dispatch.
Swift bindings (apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift)
Mirror the two new fields on the AgentParams struct in all four
codegen positions (declaration, init signature, init body, CodingKeys),
following the existing 'inputprovenance' and 'cleanupbundlemcponrunend'
pattern:
public let adaptermeta: [String: AnyCodable]?
public let paperclip: AnyCodable?
The macOS-side mirror was deleted upstream so only the shared one needs
to be regenerated.
Tests (src/gateway/protocol/agent-params-validator.test.ts, new)
- minimal canonical payload accepted, missing message/idempotencyKey rejected
- root-level paperclip alias accepts object, string, number, boolean, null
- adapterMeta with one or more adapter keys accepted
- empty adapterMeta and adapterMeta with primitive entry values accepted
- both surfaces side by side (paperclip + adapterMeta.paperclip) accepted
- adapterMeta with non-object value rejected (string, number, boolean, null, array)
- truly unknown root properties still rejected (additionalProperties guard intact)
Verification
- pnpm exec oxfmt --check --threads=1 on the touched .ts files: clean.
- Standalone tsx reproducer importing the schema body and compiling it
with the same Ajv configuration as src/gateway/protocol/index.ts:
19/19 cases pass (covering the 8 PR test scenarios above plus their
primitive variants).
- pnpm test src/gateway/protocol/agent-params-validator.test.ts is
expected to pass on CI; local run was blocked by registry access
while preparing the github-pinned @openclaw/fs-safe workspace dep.
Changelog
Added one bullet under ## Unreleased -> ### Changes describing the
namespace + alias and citing openclaw#74635 and openclaw#62102.
Fixes openclaw#74635.
Fixes openclaw#62102.
Refs openclaw#69139, openclaw#72565, openclaw#75901.
… alias on AgentParamsSchema
Paperclip and other external agents inject a root-level metadata blob
when invoking agents via the openclaw_gateway adapter, but
AgentParamsSchema is declared with additionalProperties: false so the
gateway rejects the entire payload before dispatch:
invalid agent params: at root: unexpected property 'paperclip'
Reported by openclaw#74635 (Paperclip rev 3494e84 vs OpenClaw 2026.4.26
be8c246) and openclaw#62102. Three earlier PRs proposed a vendor-named root
field (openclaw#69139, openclaw#72565, openclaw#75901) - all closed without merge - so this
lands the fix as a generic namespace plus a backward-compatibility
alias instead.
Schema (src/gateway/protocol/schema/agent.ts)
- AgentParamsSchema gains 'adapterMeta: Type.Optional(Type.Record(
Type.String(), Type.Unknown()))'. Each external agent or plugin
adapter writes under its own top-level key here
(adapterMeta: { paperclip: {...}, greptile: {...} }); the gateway
accepts the bag as-is and never inspects it. New adapters can land
protocol-side without growing the schema.
- AgentParamsSchema also gains 'paperclip: Type.Optional(Type.Unknown())'
as a backward-compatibility alias, with a comment marking it for
removal once the published Paperclip adapter cuts a release that
nests under adapterMeta.paperclip. Heartbeats from current Paperclip
releases keep dispatching during the transition.
- additionalProperties: false on AgentParamsSchema is intentionally
untouched. Truly unknown root fields (e.g. typo'd 'idempotnecyKey')
still fail validation before dispatch.
Swift bindings (apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift)
Mirror the two new fields on the AgentParams struct in all four
codegen positions (declaration, init signature, init body, CodingKeys),
following the existing 'inputprovenance' and 'cleanupbundlemcponrunend'
pattern:
public let adaptermeta: [String: AnyCodable]?
public let paperclip: AnyCodable?
The macOS-side mirror was deleted upstream so only the shared one needs
to be regenerated.
Tests (src/gateway/protocol/agent-params-validator.test.ts, new)
- minimal canonical payload accepted, missing message/idempotencyKey rejected
- root-level paperclip alias accepts object, string, number, boolean, null
- adapterMeta with one or more adapter keys accepted
- empty adapterMeta and adapterMeta with primitive entry values accepted
- both surfaces side by side (paperclip + adapterMeta.paperclip) accepted
- adapterMeta with non-object value rejected (string, number, boolean, null, array)
- truly unknown root properties still rejected (additionalProperties guard intact)
Verification
- pnpm exec oxfmt --check --threads=1 on the touched .ts files: clean.
- Standalone tsx reproducer importing the schema body and compiling it
with the same Ajv configuration as src/gateway/protocol/index.ts:
19/19 cases pass (covering the 8 PR test scenarios above plus their
primitive variants).
- pnpm test src/gateway/protocol/agent-params-validator.test.ts is
expected to pass on CI; local run was blocked by registry access
while preparing the github-pinned @openclaw/fs-safe workspace dep.
Changelog
Added one bullet under ## Unreleased -> ### Changes describing the
namespace + alias and citing openclaw#74635 and openclaw#62102.
Fixes openclaw#74635.
Fixes openclaw#62102.
Refs openclaw#69139, openclaw#72565, openclaw#75901.
Problem
External orchestrators (today: Paperclip's
@paperclipai/adapter-openclaw-gateway) attach an opaquepaperclipenvelope at the root of everyagentrequest —runId,taskId,wakeReason,workspace, etc., used for upstream observability and future correlation.AgentParamsSchemais declared withadditionalProperties: false, so the gateway rejects the entire wake with:This blocks every Paperclip → OpenClaw heartbeat. Today users work around it by hot-patching the installed
protocol-Hjar_s3V.jsafter everybrew upgrade openclaw.Fix
Declare
paperclip: Type.Optional(Type.Unknown())onAgentParamsSchemaso the field is accepted and silently ignored. The gateway code never reads it (nopaperclipreferences anywhere undersrc/gateway/server-methods/), so this is a pure pass-through.additionalProperties: falsestays in place — this only opens a single named, well-understood field, not a wildcard.This mirrors the forward-compat stance already taken by
WakeParamsSchema(additionalProperties: true) and the existing test that validatespaperclipon wake params (src/gateway/protocol/index.test.tsline 167).Tests
Added three
validateAgentParamscases:paperclipenvelopeAI-assisted disclosure