- Replaced streamPodLogs / streamPodLogsOnce / readPodLogs / waitForPodTermination
with tailPodLogFile() that polls a shared PVC file path with adaptive cadence
(250ms active, 1000ms idle after 5 consecutive empty polls)
- Added buildPodLogPath() export and podLogPath to JobBuildResult
- Added assertSafePathComponent with [a-zA-Z0-9-:] allowance for UUIDs
- Updated Job manifest to tee stdout to /paperclip/instances/default/run-logs/<companyId>/<agentId>/<runId>.pod.ndjson
- Added hasOutOfProcessLiveness: true to createServerAdapter (cast required)
- Deleted log-dedup.ts and log-dedup.test.ts entirely
- Removed all LogLineDedupFilter, Writable, and LOG_STREAM_* constants
- Removed completionResult.status workaround (completionWithGrace returns directly)
- Test infrastructure: mocked node:fs/promises to prevent unmocked fs.stat hangs
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cancel poll now uses ctx.authToken exclusively. Remove forwarding of
PAPERCLIP_DEV_API_KEY into job pods and all associated tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Locks in the existing override behavior so a future regression that
reverts to a hardcoded image is caught immediately. Closes the
investigation on FAR-90.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The env var was set to /opencode-db (the mount point directory), but sqlite
requires a file path. Changed to /opencode-db/opencode.db.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Replaces the Option A shared-PVC path implementation with a long-lived
dedicated PVC per agent, mounted at /opencode-db with OPENCODE_DB=/opencode-db.
Changes:
- k8s-client.ts: add getPvc/createPvc/deletePvc CoreV1Api helpers
- execute.ts: add ensureAgentDbPvc() that gets-or-creates a PVC named
opencode-db-<agentId> before Job creation; pass agentDbClaimName through
to buildJobManifest; return null for ephemeral mode (emptyDir used instead)
- job-manifest.ts: accept agentDbClaimName on JobBuildInput; mount dedicated
PVC or emptyDir at /opencode-db; set OPENCODE_DB=/opencode-db; revert init
container to simple form (no mkdir, no PVC mount)
- config-schema.ts: replace opencodeDbMode/opencodeDbPath with agentDbMode
(dedicated_pvc|ephemeral, default dedicated_pvc), agentDbStorageClass
(required for dedicated_pvc), agentDbStorageCapacity (default 1Gi)
- test.ts: add create/delete RBAC checks for persistentvolumeclaims
- pvc.test.ts: unit tests for ensureAgentDbPvc (7 cases incl. error paths)
- 289/289 tests pass; typecheck clean
- No agent-delete hook exists; opencode-db PVC janitor routine is a deferred
follow-up task
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- shared_pvc mode (default): sets OPENCODE_DB to /paperclip/.opencode/db/<agentId>
and prepends mkdir -p to the busybox init container when a PVC is present
- ephemeral mode: mounts an emptyDir at /opencode-db and points OPENCODE_DB there
- config-schema: adds opencodeDbMode (select, default shared_pvc) and
opencodeDbPath (optional text override for shared_pvc path)
- No agent-delete hook exists in this adapter; per-agent DB dir cleanup is
deferred to a janitor routine (follow-up work)
- 284/284 tests pass; typecheck clean
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Poll PAPERCLIP_API_URL/api/heartbeat-runs/{runId} at keepalive cadence
during log streaming. When status != "running", delete the Job with
propagationPolicy=Background and return errorCode="cancelled" as a
distinct result, matching the claude_k8s reference implementation.
Also includes: reattachOrphanedJobs config field that lets the adapter
reattach to a same-task Job left over from a prior server restart;
task-id and session-id K8s labels on Job manifests for observability.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add `sanitizeLabelValue()` export: strips [^a-z0-9._-], lowercases, truncates to 63 chars, warns on drop
- Apply sanitizer to all paperclip.io/* label values (agent-id, run-id, company-id, extra labels)
- Job name now includes 6-char sha256 hash over raw agent.id+runId for collision resistance
- Trailing hyphens stripped from final job name
- Slugs extended from 8 to 16 chars to match claude_k8s reference
- 32 unit tests covering sanitizeLabelValue, job name format, determinism, and collision avoidance
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- SelfPodInfo gains inheritedEnvValueFrom (V1EnvVar[]) and inheritedEnvFrom (V1EnvFromSource[])
- Container selection now prefers the container named "paperclip", falls back to first
- buildJobManifest appends valueFrom env vars (skipping names already overridden)
and sets envFrom on the opencode container when present
- Tests updated: mock updated, 5 new cases covering secretKeyRef forwarding,
dedup, envFrom passthrough, and empty-envFrom omission
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add vitest with 26 passing tests for parse and job-manifest
- Set models to undefined for free-text model input
- Add fsGroupChangePolicy: "OnRootMismatch" to reduce volume chown delays
- Change job name prefix to agent-opencode- for adapter identification
- Add .npmrc to .gitignore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>