Commit Graph

63 Commits

Author SHA1 Message Date
Chris Farhood 7fbfe8592b chore: remove committed tarballs; gitignore *.tgz
Adapter releases are distributed via the Paperclip adapter plugin
system, not tarballs in git. Removes legacy 0.1.22/0.1.23/0.1.26
tarballs and a stray screenshot, and adds *.tgz to .gitignore so
future npm pack output is not committed.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 21:40:59 +00:00
Chris Farhood af6d3e811a chore: bump version to 0.1.26
Includes PVC verification fix (FAR-84) and prior unbumped fixes
(serviceAccountName config schema, log reconnect backoff stop).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 21:35:30 +00:00
Chris Farhood 6accb424d4 fix: verify PVC exists after creation in ensureAgentDbPvc
Before creating a PVC, ensureAgentDbPvc checks if it exists and creates
it if not. However, the Kubernetes API may return a Success response
without actually creating the resource. This commit adds a verification
step after createPvc to confirm the PVC actually exists before returning.

Fixes FAR-84.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 21:28:29 +00:00
Chris Farhood 1de9b67fd3 fix: make log reconnect backoff interruptible by stop signal
The exponential backoff sleep in streamPodLogs used a single setTimeout
for the full delay (3s, 6s, 12s...). When logStopSignal.stopped was set
mid-backoff (e.g. by external cancel), the loop body could not check the
signal until the timer expired — causing the cancel test to time out when
the 12s backoff overlapped with the 15s cancel window.

Sleep in 200ms chunks so a stop signal can exit the backoff immediately.
Fixes the pre-existing CI timeout in execute.test.ts.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 20:37:49 +00:00
Chris Farhood cc445461e0 fix: remove duplicate serviceAccountName field in config schema
Prior commit from remote + this branch both added the field; deduplicate,
keeping the entry at the top of the Kubernetes group.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 20:32:00 +00:00
Chris Farhood 55984d6fdf feat: surface serviceAccountName in adapter UI config (FAR-77)
Adds serviceAccountName field to the Kubernetes group in getConfigSchema()
so operators can specify a dedicated SA (e.g. paperclip-developer) for Job
pods that need k8s API access. The field was already consumed in job-manifest.ts;
this makes it visible in the UI. Bumps to 0.1.25.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 20:31:03 +00:00
Chris Farhood a6e95c1e64 Add serviceAccountName to Kubernetes config schema
Surfaces the serviceAccountName field in the adapter UI under the
Kubernetes group. The job manifest builder already reads this field;
this change makes it configurable via the UI.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 20:05:49 +00:00
Chris Farhood 1005b6f3f2 chore: bump version to 0.1.24
Surface PVC settings (agentDbMode, agentDbStorageClass,
agentDbStorageCapacity) in adapter UI config (FAR-73).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 20:01:39 +00:00
Chris Farhood 38b3916e11 fix: support PAPERCLIP_DEV_API_KEY for external cancel polling
External cancel polling in execute.ts used PAPERCLIP_API_KEY which is
a short-lived run JWT for the main Paperclip instance. In multi-instance
setups (dev vs main), the agent runs on the dev instance but the run JWT
is only valid on the main instance, causing 401 on every poll.

Now polls with PAPERCLIP_DEV_API_KEY if set, falling back to
PAPERCLIP_API_KEY. The dev key is inherited through job-manifest.ts
from the pod's inherited env.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 14:36:11 +00:00
Chris Farhood 4a95fc0f2d fix: exponential backoff on log stream reconnect (FAR-70)
Replace fixed 3s reconnect delay with exponential backoff (3s → 6s → 12s → 24s → capped at 30s) to avoid hammering the K8s API server during prolonged network blips while remaining responsive during brief disconnects.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 14:31:58 +00:00
Chris Farhood 139a387508 test: add ensureAgentDbPvc unit tests (FAR-63)
Seven direct unit tests for ensureAgentDbPvc covering ephemeral mode,
existing PVC (no create), PVC creation with storage class/capacity,
missing storage class error, default mode, and agent ID slug derivation.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 12:41:42 +00:00
Chris Farhood 46ce5cc599 feat: dedicated PVC per agent for OPENCODE_DB (FAR-63, Option B)
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>
2026-04-25 12:38:54 +00:00
Chris Farhood 908880a25b feat: implement OPENCODE_DB per-agent path on shared PVC (FAR-62)
- 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>
2026-04-25 12:29:38 +00:00
Chris Farhood 5fa9e1396e fix: poll issue status instead of heartbeat-run for cancel detection (FAR-60)
The cancel poller was calling GET /api/heartbeat-runs/{runId} which
returned 401 because the adapter key lacks access to the internal
heartbeat-runs endpoint. Switch to GET /api/issues/{issueId}, which
the adapter key can read. Also tighten the trigger condition from
status !== "running" to status === "cancelled" so that other terminal
states (done, blocked, etc.) do not abort the K8s job.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 12:25:41 +00:00
Chris Farhood 80d18005f9 fix: wait for concurrent job to finish instead of returning permanent blocked error (FAR-61)
When multiple tasks are assigned simultaneously, only one K8s job can run
at a time (shared PVC/session guard). Previously, all other tasks received
k8s_concurrent_run_blocked immediately and stayed blocked forever.

Now the guard retries once: wait for all blocking jobs to complete via
waitForJobCompletion, then re-check before proceeding to create a new job.
If the re-check still shows a running job, the error is returned as before.

The agentCreationMutex already serializes guard-check + job-create, so
tasks naturally queue up and execute one at a time without concurrent jobs.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 11:10:27 +00:00
Chris Farhood 2bd8107f1d fix: skills not bundled and resumeLastSession ignored (FAR-56, FAR-57)
Two bugs prevented skill content from reaching K8s Job prompts, and
resumeLastSession: false was silently ignored.

Skills fix (execute.ts, FAR-57):
- Add /paperclip/.claude/skills as additional candidate to
  readPaperclipRuntimeSkillEntries — the relative candidates in
  adapter-utils don't resolve to the PVC-mounted skills home
- Read entry.source/SKILL.md instead of entry.source (which is a
  directory path); fall back to source directly for file-based entries
- Mock readPaperclipRuntimeSkillEntries in execute.test.ts to prevent
  real SKILL.md reads from delaying fake-timer registration

Session fix (job-manifest.ts, FAR-56):
- Gate --session flag on asBoolean(config.resumeLastSession, true)
  so setting resumeLastSession: false actually stops session resumption
- Default true preserves existing behaviour for agents without config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 10:11:47 +00:00
Chris Farhood 0e94e84e2c fix: grace poller checks pod liveness before giving up on log stream (FAR-52)
The K8s log client v1.x closes the follow-stream prematurely due to a
known upstream bug — causing the grace timer to fire 30 s after log
stream exit even when the container is still running.  The old behaviour
(`waitForPodTermination` with a hardcoded 120 s timeout) was too short
for agents whose opencode runs take several minutes, leading to premature
failure and issues stuck in `blocked`.

Fix: the grace poller now calls `readNamespacedPod` before resolving the
completion promise.  If the pod is still Running/Pending, it resets
`logExitTime` to defer the grace deadline.  A `graceCheckPending` guard
prevents concurrent checks.  A `graceMaxWaitMs` cap (= completionTimeoutMs
when set, 20 min otherwise) ensures we never wait forever for unlimited
jobs.  Version bumped to 0.1.21.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 01:40:21 +00:00
Chris Farhood 2625b8ffb3 fix: wait for pod termination before readPodLogs fallback (FAR-52)
The @kubernetes/client-node v1.x Log.follow stream closes prematurely
(known upstream TODO). Combined with Node.js buffering stdout to pipes,
the live log stream always returns empty. When the 30s grace timer fires
and the stream is empty, the container may still be running.

Add waitForPodTermination() to block in the empty-stdout fallback path
until the container actually exits (up to 120s), then read its complete
output with readNamespacedPodLog. This makes runs complete successfully
instead of looping indefinitely in in_progress.

Bump version to 0.1.20.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 01:21:40 +00:00
Chris Farhood bf32e81aa6 chore: bump version to 0.1.19
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 00:42:29 +00:00
Chris Farhood 38ed261063 fix(test): widen capturedHandler cast to resolve TS2349 never narrowing (FAR-40)
TypeScript CFA does not trace the assignment inside the vi.spyOn
mockImplementation callback, so it narrows capturedHandler to null at
the if-check, making the body unreachable (never). Cast at the call
site breaks the false narrowing without changing runtime behaviour.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 00:31:27 +00:00
Chris Farhood d858df7cf2 feat: external-cancel polling via heartbeat-runs endpoint (FAR-42)
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>
2026-04-25 00:22:28 +00:00
Chris Farhood 2b4049464c feat: per-agent mutex, fail-closed guard, SIGTERM cleanup (FAR-40)
- Add agentCreationMutex (Map<agentId, Promise>) that serializes
  guard-check + job-create per agent, eliminating the TOCTOU race where
  two concurrent execute() calls both pass the list-then-create check.
- Change catch {} on listNamespacedJob errors to return
  errorCode: "k8s_concurrency_guard_unreachable" (fail-closed) instead
  of silently bypassing the concurrency guard.
- Add ensureSigtermHandler() which tracks active Jobs in activeJobs Map
  and deletes all of them (plus prompt Secrets) on SIGTERM before exit.
- Track orphaned-job reattaches in activeJobs for consistent cleanup.
- Update execute.test.ts: change "proceeds on list error" test to assert
  k8s_concurrency_guard_unreachable; add mutex serialization test and
  SIGTERM handler registration tests.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 00:22:17 +00:00
Chris Farhood 0a3d9c4172 feat: sanitize K8s label values + collision-resistant job names (FAR-43)
- 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>
2026-04-25 00:08:33 +00:00
Chris Farhood 0df00f7d95 test(execute): large-prompt Secret path coverage
- Add describe block "execute — large-prompt Secret path" with 5 cases:
  buildJobManifest called twice (promptSecretName on second call),
  Secret created before Job, ownerReference patched after Job creation,
  Secret deleted in finally block, Secret cleaned up on Job create failure
- Update vi.mock for job-manifest to export LARGE_PROMPT_THRESHOLD_BYTES
- Add createNamespacedSecret/deleteNamespacedSecret/patchNamespacedSecret
  to makeCoreApi for completeness
- Update makeBatchApi to return { metadata: { uid } } so ownerRef tests work

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-24 22:16:34 +00:00
Chris Farhood c05d1d7515 feat: log stream reconnect, dedup, bail, keepalive (FAR-38)
- Split streamPodLogs into streamPodLogsOnce (with bail timer + stopSignal)
  and streamPodLogs (reconnect loop, up to MAX_LOG_RECONNECT_ATTEMPTS=50)
- LogLineDedupFilter suppresses replayed JSONL events on reconnect, keyed
  by type+sessionID+part.id (OpenCode shape)
- Bail timer (LOG_STREAM_BAIL_TIMEOUT_MS=3s) forces writable.destroy() +
  promise resolution when stopSignal fires and logApi.log hangs
- Keepalive: emits '[paperclip] keepalive — job X running (Ns since last output)'
  every 15s during silent phases, with 2-consecutive-reading latch to avoid
  false-positive terminal detections
- completionGraced uses logExitTime + grace poller so log stream stop signal
  is set immediately when job condition resolves
- All 235 tests pass, tsc clean

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-24 22:14:36 +00:00
Chris Farhood 61d2a42a66 feat: inherit valueFrom/envFrom env from Deployment; prefer paperclip container
- 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>
2026-04-24 22:12:31 +00:00
Chris Farhood 84dc0f5930 fix: merge parsedError + podFailureDescription so OOMKilled surfaces in errorMessage
When both a JSONL error (e.g. "killed") and a pod terminated reason (e.g. "OOMKilled")
are present, join them with "; " so the richer pod classification is never silently
dropped by the parsedError short-circuit.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-24 22:10:42 +00:00
Chris Farhood d60afaebcd feat: pod-failure classification, partial stdout fallback, llm_api_error
- Replace getPodExitCode with getPodTerminatedInfo to capture exit code
  and reason (OOMKilled, Error, etc.) from terminated container state;
  pod failure description now surfaces in returned errorMessage
- Add partial-stdout fallback: readPodLogs is triggered when stdout is
  non-empty but contains no sessionId (missing session result), not just
  when stdout is fully empty
- Detect empty LLM response: when a session ran but produced 0 output
  tokens and no messages, return errorCode "llm_api_error"
- Add 13 new unit tests covering all three new paths

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-24 22:09:33 +00:00
Chris Farhood 0fcc6f01c1 feat: instructionsFilePath config field + skill bundle materialization
- config-schema: add instructionsFilePath UI field (Core group, text type)
- server/index.ts: set supportsInstructionsBundle=true, instructionsPathKey="instructionsFilePath"
- execute.ts: read instructionsFilePath file + desired skill markdown files from PVC; pass to buildJobManifest as instructionsContent / skillsBundleContent
- job-manifest.ts: accept instructionsContent + skillsBundleContent in JobBuildInput; prepend both to prompt via joinPromptSections; add instructionsChars + skillsBundleChars to promptMetrics
- index.ts: document instructionsFilePath and skills injection in agentConfigurationDoc
- CLAUDE.md: document skill materialization (ephemeral mode) and instructionsFilePath field
- Bump version to 0.1.18

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-24 22:06:07 +00:00
Chris Farhood 13c2a3032b feat: UI parser kinds, nodeSelector textarea, step-limit session clear, per-line path redaction
- ui-parser: add thinking kind + handler for standalone thinking events, thinking
  blocks in assistant content arrays, and user-turn tool_result blocks
- job-manifest: parseKeyValueOrObject helper so nodeSelector (and labels) accept
  key=value textarea lines in addition to JSON objects
- parse: isOpenCodeStepLimitResult detects step_finish with max_turns / max_steps /
  step_limit reason
- execute: return clearSession:true when step limit reached so next run starts fresh;
  redactHomePathUserSegments moved to per-line to prevent paths split across chunks
- tests: ui-parser.test.ts (new), extended parse.test.ts and job-manifest.test.ts

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-24 22:01:35 +00:00
Chris Farhood 3ed6e95085 feat: RBAC checks, ./cli export, adapter-utils range, models.ts
- Add secrets (create/delete/get) and persistentvolumeclaims (get) RBAC checks to testEnvironment
- Add ./cli export and picocolors dependency to package.json
- Change @paperclipai/adapter-utils peer dep to >= range
- Add src/server/models.ts with listK8sModels() returning OpenCode provider-prefixed model IDs
- Wire listModels into ServerAdapterModule (replaces static undefined models field)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-24 21:58:00 +00:00
Paperclip ea4db3ba9b chore: unscope package name to paperclip-adapter-opencode-k8s
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-21 10:26:43 +00:00
Chris Farhood f728db0d1a Bump version to 0.1.16
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 23:45:26 -04:00
Chris Farhood 7c9fc77d48 Add listSkills and syncSkills support to opencode_k8s adapter
Implement skill sync handlers that were missing, matching the approach
used in the claude_k8s adapter. The adapter now surfaces available,
configured, and external skills from /paperclip/.claude/skills in K8s
pods, resolving desired skills from config and reporting missing ones.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 23:42:44 -04:00
Chris Farhood 0837ee4255 Add CLAUDE.md for Claude Code guidance
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 20:58:40 -04:00
Chris Farhood 832ead94a3 Bump version to 0.1.15 for capability flag release
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 20:24:40 -04:00
Chris Farhood 4b6da9b17b Update adapter-utils to canary with capability flag types
Upgrade from ^2026.411.0-canary.8 to 2026.415.0-canary.7 to get
ServerAdapterModule capability flag fields (supportsInstructionsBundle,
instructionsPathKey, requiresMaterializedRuntimeSkills).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 20:20:04 -04:00
Chris Farhood 85575a93a2 Add adapter capability flags for opencode_k8s
Declare supportsInstructionsBundle, instructionsPathKey, and
requiresMaterializedRuntimeSkills on ServerAdapterModule. opencode_k8s
does not support instructions bundles (instructions are piped via init
container) and does not require materialized runtime skills (bundled in
container image).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 20:10:52 -04:00
Pawla Abdul e53bcf2501 Replace local utility stubs with fork's adapter-utils imports
- Replace joinPromptSections, stringifyPaperclipWakePayload, and
  renderPaperclipWakePrompt with imports from adapter-utils/server-utils
  (the fork's renderPaperclipWakePrompt adds execution stage routing,
  resume delta sections, and full comment batch rendering)
- Replace local inferOpenAiCompatibleBiller with import from adapter-utils
- Declare sessionManagement using getAdapterSessionManagement("opencode_local")
  with fallback defaults for proper session compaction policy
- Add log redaction via redactHomePathUserSegments in streamPodLogs
- Bump peerDependency to >=0.3.1 and version to 0.1.14

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-14 11:00:33 +00:00
Pawla Abdul 193e68e99d Sync package-lock.json version and peerDependency with package.json
Lock file was stale at 0.1.11 with an outdated peerDependency constraint;
bring it in line with package.json (0.1.13, >=0.3.0).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-12 18:48:00 +00:00
Pawla Abdul ea9ce27e55 Remove duplicate UI fields already provided by the platform
The adapter config schema was re-declaring model, promptTemplate, env,
extraArgs, timeoutSec, and graceSec which the Paperclip platform already
surfaces as standard fields, causing duplicate controls in the agent
configuration UI.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-12 17:28:13 +00:00
Pawla Abdul b59f571e0b Fix peer dependency conflict by relaxing adapter-utils requirement
The peer dependency on @paperclipai/adapter-utils was pinned to
>=2026.411.0-canary.8 which conflicts with the stable 2026.403.0
version used by other adapters (claude-k8s, hermes-k8s). Since the
canary types (AdapterConfigSchema, getConfigSchema on ServerAdapterModule)
are only needed at compile time, we can safely relax the peer dep to
>=0.3.0 while keeping the canary as a devDependency for our own build.

Bumps version to 0.1.13.

Closes FAR-49

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-12 16:58:09 +00:00
Chris Farhood e280130f6d Update README.md 2026-04-12 11:24:20 -04:00
Chris Farhood 07fc7ab5ec Update project title in README.md 2026-04-12 11:23:29 -04:00
Pawla Abdul b286a36f1f Enhance README with RWX PVC requirements, RBAC examples, and deployment guide
Rewrites the README to clearly document all prerequisites for running the
adapter on Kubernetes: shared ReadWriteMany PVC at /paperclip, full RBAC
Role/RoleBinding with all required permissions, namespace scoping, security
context, and resource defaults. Uses deployment "paperclip" in namespace
"paperclip" as example nomenclature throughout.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-12 15:18:49 +00:00
Pawla Abdul 241da1d4f9 Use rich TranscriptEntry kinds in UI parser for pretty output
The UI parser was mapping all OpenCode JSONL events to plain stdout/stderr
transcript entries, causing the Paperclip UI to render everything as raw
terminal output. Updated parseStdoutLine to use structured kinds:

- text events → "assistant" (renders as chat bubbles)
- tool_use events → "tool_call" / "tool_result" (shows tool invocations)
- step_finish events → "result" (shows token/cost metrics)
- step_start events → "system" (shows step transitions)
- assistant events → "assistant" (nested content blocks)

Fixes FAR-43.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-12 15:15:19 +00:00
Pawla Abdul faa5f09984 Upgrade adapter-utils to canary with getConfigSchema type support
The UI wasn't surfacing config parameters because getConfigSchema wasn't
part of the ServerAdapterModule interface in adapter-utils >=0.3.0. The
canary release (2026.411.0-canary.8) adds ConfigFieldSchema,
AdapterConfigSchema, and getConfigSchema to the type. This removes the
local type augmentation workaround and the unsafe `as ServerAdapterModule`
cast, letting TypeScript properly validate the schema contract.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-12 14:58:07 +00:00
Chris Farhood a7b42da7a0 CI: skip publish if version already published
Avoids 403 E403 "cannot publish over previously published version" error
when pushing non-version-bump commits to master.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 10:37:28 -04:00
Chris Farhood 72f7950da6 Add getConfigSchema to surface adapter config fields in UI
- src/server/config-schema.ts: define schema with Core, Kubernetes,
  and Operational field groups matching agentConfigurationDoc
- src/server/config-schema.test.ts: 10 tests covering field types,
  defaults, options, and group structure
- src/server/index.ts: attach getConfigSchema to ServerAdapterModule
- Revert k8s-client.ts loadFromConfig change (not available in installed
  @kubernetes/client-node version)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 10:35:31 -04:00
Chris Farhood 6011f3e886 Fix ui-parser exports: make self-contained and export parseStdoutLine directly
- ui-parser.ts: inline all logic, zero external imports (matches Paperclip
  adapter plugin UI parser contract)
- Export parseStdoutLine as named export from index.ts (like claude_k8s
  exports printClaudeStreamEvent directly)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 10:12:59 -04:00