Commit Graph

32 Commits

Author SHA1 Message Date
Chris Farhood 4c956cc039 chore: bump version to 0.1.31 2026-04-26 00:25:06 +00:00
Chris Farhood 1bad618b29 chore: bump version to 0.1.30
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 23:25:17 +00:00
Chris Farhood 798b80f2f2 test: push coverage to 90%+ on lines for all files except execute.ts (FAR-85)
Overall before: 80.36% lines / 79.06% statements
Overall after:  94.65% lines / 93.30% statements

Per-file lines coverage (all targets ≥90% except execute.ts):

| File              | Before | After  |
|-------------------|--------|--------|
| ui-parser.ts      | 93.63% | 99.09% |
| cli/format-event  | 59.85% | 99.27% |
| server/execute    | 81.47% | 89.64% |
| server/job-mfst   | 90.30% | 98.78% |
| server/k8s-client | 37.50% | 95.83% |
| server/log-dedup  | 97.77% | 97.77% |
| server/parse      | 89.85% | 98.55% |
| server/skills     | 100%   | 100%   |

New tests added:

- k8s-client.test.ts: getSelfPodInfo (env-var inheritance, secret volumes,
  PVC discovery, dnsConfig, all error paths) + kubeconfig file branch
- format-event.test.ts: parseStdoutLine (cli) — full event-type matrix,
  tool_use status branches, errorText fallback paths
- ui-parser.test.ts: errorText edge cases, empty event paths
- parse.test.ts: errorText fallback to data.message, name, code, JSON
- job-manifest.test.ts: workspace context env wiring, linkedIssueIds,
  paperclipWorkspaces/RuntimeServices JSON, authToken, inherited URLs,
  prompt-secret + data PVC + secret-volume mount paths
- execute.test.ts: parseModelProvider, completionWithGrace,
  instructionsFilePath read failure, ensureAgentDbPvc throw paths,
  large-prompt secret create failure, step-limit detection,
  waitForPod no-pod messaging, init-container ImagePullBackOff /
  CrashLoopBackOff, main-container CrashLoopBackOff, all-inits-done
  happy path, skill bundle source loading (SKILL.md + flat-file
  fallback), SIGTERM handler full body via vi.resetModules()

execute.ts remains at 89.64% lines — the residual gap is deep async/timer
paths inside streamAndAwaitJob (grace poller, keepalive ticker, log-stream
stop-signal/bail timer). Those need fake-timer scaffolding heavier than
this batch warrants; tracking separately.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 22:27:04 +00:00
Chris Farhood 693016d1ab chore: add @vitest/coverage-v8 to enable coverage reports
So we can answer "what's coverage?" without re-installing each time.
Run with: \`npx vitest run --coverage --coverage.provider=v8 --coverage.reporter=text-summary\`

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 22:09:55 +00:00
Chris Farhood c71e224b43 test: cover k8s-client 404 detection against real ApiException shape (FAR-85)
There was no test file for k8s-client.ts. Existing pvc.test.ts mocked
`getPvc` directly and never exercised the underlying isNotFound predicate,
so the v1.x ApiException `code` vs `statusCode` regression had nothing to
catch it.

Add k8s-client.test.ts that mocks @kubernetes/client-node, throws errors
shaped exactly like the real ApiException (status under `code`), and
verifies:

- getPvc returns null on code=404 (the FAR-85 case)
- getPvc still handles legacy statusCode=404 and response.statusCode=404
- getPvc re-throws non-404 errors (500, 403)
- deletePvc swallows 404, re-throws others
- createPvc forwards spec to the SDK

Confirmed the new tests fail when k8s-client.ts is reverted to the
pre-fix predicate (2 failures), and pass with the fix in place.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 22:05:48 +00:00
Chris Farhood 3daf2dd676 fix: detect 404 from @kubernetes/client-node v1.x ApiException (FAR-85)
The v1.x ApiException exposes the HTTP status as `code`, not `statusCode`.
Both `isNotFound` (k8s-client) and `isK8s404` (execute) only checked
`statusCode`/`response.statusCode`, so 404s were never recognized:

- `getPvc` re-threw the 404 instead of returning null, which bubbled up
  through `ensureAgentDbPvc` as `k8s_job_create_failed` with the raw
  "persistentvolumeclaims X not found" body — the symptom in FAR-85.
- The PVC was never actually created, because the existence check threw
  before reaching `createPvc`.

Add `code === 404` to both predicates and a regression test for `isK8s404`.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 21:53: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 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 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 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 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 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
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 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
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 2927d3e47e Bump version to 0.1.10
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 09:52:09 -04:00
Chris Farhood 1b3d4b6dfe Bump version to 0.1.9
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 09:28:33 -04:00
Chris Farhood bbd579e7e2 Bump version to 0.1.8
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 08:58:45 -04:00
Chris Farhood 66893dc286 Bump version to 0.1.7
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 08:33:25 -04:00
Chris Farhood ce666f6b54 Read all env vars from pod spec instead of static allowlist
- Remove INHERITED_ENV_KEYS; read directly from mainContainer.env
- Any Deployment env var is now forwarded automatically to Job pods
- Layer ordering preserved: pod spec → paperclip vars → agent config env

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 08:31:43 -04:00
Chris Farhood 6866da42bf Add tests, free-text model field, and K8s job improvements
- 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>
2026-04-12 08:14:57 -04:00
Chris Farhood 75f54b1edc Update package name to @farhoodliquor scope and refresh README
- Rename to @farhoodliquor/paperclip-adapter-opencode-k8s
- Add .gitignore
- Emphasize RWX PVC requirement in README

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 23:18:12 -04:00
Chris Farhood be7c525063 Initial commit
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 23:08:05 -04:00