fix(server/execute): unwrap Paperclip {type,value} env entries before spawn#102
Closed
tamermina wants to merge 1 commit into
Closed
fix(server/execute): unwrap Paperclip {type,value} env entries before spawn#102tamermina wants to merge 1 commit into
tamermina wants to merge 1 commit into
Conversation
… spawn
Paperclip's adapter contract stores env entries as
{type: "plain"|"secret", value: string} objects. The current
Object.assign(env, userEnv) spreads these directly into the spawned
process env, where Node coerces each object to the literal string
"[object Object]". Every consumer of these env vars (PAPERCLIP_API_KEY,
PAPERCLIP_RUN_ID, custom user env) sees the broken value and either
fails auth or makes the wrong API call.
Fix: unwrap the {value} before assigning, falling back to the raw
string if the value isn't wrapped (preserves the legacy-shape path).
Verified by replacing the equivalent in-container patch we've run on
production for ~3 weeks (sentinel: PAPERCLIP-UNWRAP-ENV-PATCH).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
pinsleepe
pushed a commit
to Pixel-Nomad-Global/hermes-paperclip-adapter
that referenced
this pull request
May 27, 2026
… block The NousResearch#118 PAPERCLIP_API_URL prompt fix and the NousResearch#102 env-unwrap fix both declared userEnv in the same function scope. Renamed the prompt-resolution variable to configEnv to resolve the TS2451 redeclaration error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pinsleepe
pushed a commit
to Pixel-Nomad-Global/hermes-paperclip-adapter
that referenced
this pull request
May 27, 2026
Rebuilds dist/ after merging 10 upstream PRs: NousResearch#138 gate session persistence on success + drop legacy regex NousResearch#116 session-codec format validation NousResearch#102 unwrap Paperclip {type,value} env entries NousResearch#99 wire onSpawn for PID tracking NousResearch#96 add missing providers + prepare script (skip -Q disable + env dup) NousResearch#139 default model fallback to auto NousResearch#141 read wake metadata from ctx.context not ctx.config NousResearch#135 swap python3 pipes for jq in DEFAULT_PROMPT_TEMPLATE NousResearch#118 use PAPERCLIP_API_URL from adapter env in prompt NousResearch#122 make task closeout conditional on genuine completion Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
config.envvalues from Paperclip arrive as{type: "plain"|"secret", value: string}objects, not plain stringsObject.assign(env, userEnv)atsrc/server/execute.ts:425(HEAD937ea71) spreads these objects directly into the spawned process env"[object Object]", breaking every downstream consumer:PAPERCLIP_API_KEYauth fails,PAPERCLIP_RUN_IDis corrupted, all custom user env vars are uselessReproduce
Set an env var in a Paperclip agent's
adapterConfig.env(any key/value). Spawn the adapter andconsole.log(process.env.YOUR_KEY)inside the Hermes process — you'll see"[object Object]"instead of the string value. The same applies to every var Paperclip stores in the wrapped shape.Fix
Replace
Object.assign(env, userEnv)with a for-loop that unwraps{type, value}entries before assigning:The
typeof raw === "string"branch preserves the legacy path for any caller already passing plain strings.The type annotation is widened from
Record<string, string>toRecord<string, string | {type: "plain"|"secret"; value: string}>— this accurately reflects the actual runtime shape Paperclip passes and is a strictly safer cast than the existing one (which silently lies to the compiler).Verification
npx tsc --noEmit— clean, zero errorsnpm run build(i.e.tsc) — clean, zero errorseslintis not indevDependenciesand there is noeslint.config.*file in the repo — thelintscript is broken upstream independent of this PRPAPERCLIP-UNWRAP-ENV-PATCHin our deploy script). 21 agents across 3 companies (UNOA, CHAA, REVĪV) depend on the fixed behavior for every heartbeat run. Zero regressions observed in that period.Risk
Low. The unwrap path only activates when
rawis an object with avaluekey — which is exactly the shape Paperclip produces. Plain-string values fall through to the existing assignment. The fix cannot affect callers that already pass strings correctly.