When the Paperclip pod restarts mid-run, the in-process setInterval
keepalive dies, `updatedAt` goes stale, and the server's orphan reaper
fails the run with the (misleading) "child pid 1 is no longer running"
message. Paperclip then dispatches a continuation run, whose execute()
finds the previous run's K8s Job still happily running and deletes it
as an "orphan" — throwing away work and producing the transcript/run
cascade reported on FAR-124.
Changes:
- job-manifest: add `paperclip.io/task-id` and `paperclip.io/session-id`
labels (sanitized via new `sanitizeLabelValue` helper) so a later
execute() can identify an orphan as the continuation of the same
logical unit of work.
- execute: in the concurrency guard, when `reattachOrphanedJobs` is on
(default) and an orphan matches agent + task + session + is not
terminal, pick it as the reattach target; delete only the other
orphans. Branch the build/create/waitForPod block so the reattach
path skips manifest building, Secret creation, Job creation, and
scheduling wait — it jumps straight to streaming logs and waiting
for the existing pod's completion.
- config-schema: expose `reattachOrphanedJobs` toggle (default true).
- Tests: `sanitizeLabelValue`, `isReattachableOrphan`, new label
presence/absence, config default.
No server-side changes; the misleading reaper message and lack of a
non-local retry path will be addressed in a follow-up upstream PR.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Replace the init-container RTK binary approach with a self-contained
Node.js implementation. When `enableRtk: true` is set in adapter config,
the job's main container startup:
1. Writes a Node.js filter script to /tmp/.rtk-filter.js (base64-encoded
inline — no curl, no wget, no external binary download required).
2. Merges a PostToolUse hook into ~/.claude/settings.json so Claude Code
runs the filter after every tool call.
3. The filter truncates tool_response/tool_result content that exceeds
`rtkMaxOutputBytes` (default: 50 000 B), handling both string and
array (text-block) content formats.
New config fields:
enableRtk toggle — off by default
rtkMaxOutputBytes number — truncation threshold (default 50 000)
9 new tests cover: command shape, ordering, no-external-binary guarantee,
threshold injection, PostToolUse hook presence, and filter-script logic.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
When enableRtk is set in adapter config, the adapter:
- Adds an init container (curlimages/curl) to download the RTK binary
- Mounts RTK binary in the main container via shared emptyDir volume
- Runs `rtk install claude-code` before invoking Claude to set up hooks
- Disables RTK telemetry (RTK_NO_TELEMETRY=1) for automated environments
- Supports optional rtkVersion config for pinning specific versions
RTK filters CLI command output before it reaches the LLM context,
reducing token consumption by ~80%.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Keep adapter-specific maxTurnsPerRun (default 1000) in the config
schema since the platform UI does not provide it for external adapters.
Platform-provided fields (model, effort, instructionsFilePath,
timeoutSec, graceSec) remain excluded to avoid duplication.
Add config-schema.test.ts with assertions that platform-provided
fields are absent and adapter-specific fields have correct defaults.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Fields like model, reasoning effort, instructions file path, max turns,
timeout, and grace period are either surfaced elsewhere in the platform
UI or are internal operational settings that shouldn't be user-facing
in the adapter config panel. These values remain functional when set
via the API/backend — only the UI exposure is removed.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- CI publish job failed because it tried to re-publish existing versions
(npm returns 404 for scoped packages on duplicate version). Added a
version-exists check before npm publish to skip gracefully.
- Also fixed the auth env var from NPM_TOKEN to NODE_AUTH_TOKEN which
is what actions/setup-node's registry-url option expects.
- Added missing core and operational fields to getConfigSchema() so the
Paperclip UI surfaces model, effort, maxTurnsPerRun, skipPermissions,
instructionsFilePath, timeoutSec, and graceSec alongside existing K8s
infrastructure fields.
- Bumped version to 0.1.10.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The Paperclip AdapterConfigSchema type expects a flat fields array, not
nested sections. Also maps description -> hint per the schema type.
Defines types locally since @paperclipai/adapter-utils@0.3.1 on npm
does not yet export AdapterConfigSchema/ConfigFieldSchema (those exist
in the monorepo but aren't released to npm yet).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds AdapterConfigSchema with three sections (Kubernetes, Resource Limits,
Scheduling) exposing: namespace, image, imagePullPolicy, kubeconfig,
resources.{requests,limits}.{cpu,memory}, nodeSelector, tolerations,
labels, ttlSecondsAfterFinished, retainJobs.
Paperclip's server fetches GET /api/adapters/:type/config-schema and
caches the result, automatically assigning ConfigFields to external
adapters. The adapter now wires getConfigSchema into createServerAdapter().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>