Add resilient OpenCode tool activity#3851
Merged
Merged
Conversation
Keep indentation and blank lines intact when compatibility falls back to plain OpenCode output, while continuing to filter the known permission control notice. Add route-level coverage for an unsupported future JSON format. Co-authored-by: Timothy Wayne Gregg <5861166+CompleteDotTech@users.noreply.github.com>
Reject overfull local trust-anchor journals before enumeration can consume unbounded memory, preserving a fail-closed compatibility fallback. Co-authored-by: Timothy Wayne Gregg <5861166+CompleteDotTech@users.noreply.github.com>
Avoid reusing help-derived JSON and session capability evidence across familiars, whose scoped launch environments can select different OpenCode contracts even when the launcher path and version match. Co-authored-by: Timothy Wayne Gregg <5861166+CompleteDotTech@users.noreply.github.com>
CompleteDotTech
force-pushed
the
agent/issue-3846-opencode-tool-activity
branch
from
July 26, 2026 03:41
6ec0853 to
a9afef6
Compare
BunsDev
marked this pull request as ready for review
July 26, 2026 07:35
Contributor
There was a problem hiding this comment.
Pull request overview
Implements a capability-driven OpenCode integration that preserves tool activity/resume across evolving JSONL protocols, backed by a signed compatibility-schema registry with offline fallback/quarantine behavior, plus new safety bounds for tool payload capture and persistence.
Changes:
- Added OpenCode compatibility resolution (capability probing, signed schema bundle selection/rollback/quarantine) and expanded JSONL parsing/dispatch for text/tool/error frames.
- Hardened tool activity tracking (bounded payload caps, reordered progress/result reconciliation, settled-id dedupe) and persisted user-visible compatibility diagnostics (
progress) through conversation APIs. - Added release-time guardrails (registry config validation) and expanded test coverage + suite wiring for OpenCode routing/probing/registry behavior.
Reviewed changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/lib/server/stuck-created-sweep.test.ts | Adjusts route-shape assertion for turn spawn timing change. |
| src/lib/server/conversation-write-guards.ts | Treats progress as harness-owned assistant field (client-forgery stripped). |
| src/lib/server/conversation-write-guards.test.ts | Adds coverage ensuring forged progress is removed. |
| src/lib/server/cave-home-reconciliation.ts | Adds injectable clock (lockNow) for deterministic lock timing in tests. |
| src/lib/server/cave-home-reconciliation-types.ts | Extends reconciliation options with lockNow test override. |
| src/lib/opencode-stream.ts | Major expansion of OpenCode event parsing + schema-driven envelopes + JSONL handler. |
| src/lib/opencode-stream.test.ts | Adds extensive fixture coverage for schema validation/quarantine behaviors and JSONL dispatch. |
| src/lib/opencode-compatibility.test.ts | Adds comprehensive tests for signed registry bundle validation, caching, monotonicity, quarantine, redaction. |
| src/lib/opencode-bin.ts | Prefers user launch PATH while retaining scoped fallback; updates spawn env. |
| src/lib/opencode-bin.test.ts | Tests PATH preference merge behavior across platforms. |
| src/lib/chat-turn-state.ts | Includes progress in persisted turn mapping/cloning. |
| src/lib/chat-tool-events.ts | Adds payload caps, reordered tool result/progress buffering, bounded dedupe windows. |
| src/lib/chat-tool-events.test.ts | New tests for reordered frames, caps, dedupe windows, and bounded persistence. |
| src/lib/cave-conversations.ts | Extends persisted turn schema to include progress. |
| src/lib/cave-conversations.test.ts | Ensures progress round-trips through persistence/mapping. |
| src/app/api/chat/send/route.ts | Integrates OpenCode compatibility probing/launch selection, structured JSONL handling, diagnostics persistence, privacy bounds. |
| src/app/api/chat/send/route-opencode.integration.test.ts | New end-to-end route test with a shim opencode executable (argv/probe/SSE/persistence). |
| src/app/api/chat/send/harness-routing-opencode.test.ts | Updates routing assertions to match new capability/schema-driven behavior. |
| src/app/api/chat/send/harness-routing-host-session.test.ts | Updates host-session identity assertions for OpenCode unrecorded native resume behavior. |
| src/app/api/chat/send/harness-routing-copilot-jsonl.test.ts | Updates harness session id routing assertions to include OpenCode path. |
| src/app/api/chat/send/chat-send-capabilities.ts | Adds robust OpenCode run --help / --version probing + parsing for capability selection. |
| src/app/api/chat/send/chat-send-capabilities.test.ts | New tests for probing behavior, option parsing, and bounded help-derived capability evidence. |
| src/app/api/chat/conversation/[id]/route.ts | Normalizes and returns persisted progress entries via conversation API. |
| src/app/api/chat/conversation/[id]/route.test.ts | Adds tests for stripping forged progress on PUT and preserving stored diagnostics on GET. |
| scripts/run-tests.mjs | Wires new tests into suites and alias-loader set. |
| scripts/git-hooks-pre-commit.test.mjs | Prefers Git Bash on Windows to avoid WSL bash launcher failures. |
| scripts/check-opencode-registry-release.test.mjs | Adds tests ensuring release workflow + guard script + docs stay in sync and don’t leak credentials. |
| scripts/check-opencode-registry-release.mjs | New release-only guard validating registry URL/keyring/checkpoint inputs. |
| scripts/cave-home-migration-windows.test.ts | Uses injected monotonic clock for deterministic retry timing; tweaks timeout. |
| next.config.ts | Excludes src/**/* from output tracing to avoid unintended NFT checkout capture. |
| docs/opencode-compatibility-registry.md | Documents registry config, publishing, canonicalization, rotation. |
| .github/workflows/release.yml | Enforces registry configuration validation during release packaging. |
Comments suppressed due to low confidence (1)
src/app/api/chat/send/route.ts:1042
- This comment line is misindented (starts at column 0) and should be aligned with the surrounding block to avoid formatting/lint issues.
await ensureAdapterManifestScaffold(binding.harness);
// The Responses API does not expose a documented, enforceable equivalent of
// Cave's read-only sandbox. Do not downgrade that security promise to a
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // harness uses coven run's capability probe. | ||
| const hermesDirect = !sshRuntime && binding.harness === "hermes"; | ||
| const openCodeDirect = !sshRuntime && binding.harness === "opencode"; | ||
| // Cave's Read-only control is a security promise, not a prompt hint. |
| resolveOpenCodeCompatibility, | ||
| } from "@/lib/opencode-compatibility"; | ||
| import { handleOpenCodeJsonLine } from "@/lib/opencode-stream"; | ||
| import { parseOpenCodeRunEvent } from "@/lib/opencode-stream"; |
Comment on lines
+239
to
+244
| function hasRunOption(help: string, flag: string): boolean { | ||
| // Only option-definition lines count. Mentions in examples, migration notes, | ||
| // or another command's help text are not evidence that `opencode run` takes | ||
| // this flag. | ||
| return new RegExp(`^\\s*(?:-[A-Za-z],?\\s+)?${flag}\\b(?:\\s|=|,|$)`, "m").test(help); | ||
| } |
| - `OPENCODE_SCHEMA_REGISTRY_URL` — canonical HTTPS URL for the signed OpenCode bundle. | ||
| - `OPENCODE_SCHEMA_REGISTRY_PUBLIC_KEY` — PEM-encoded Ed25519 public key that verifies that bundle. | ||
|
|
||
| - `OPENCODE_SCHEMA_REGISTRY_CHECKPOINT` — compact JSON with the current bundle's `sequence` and lowercase SHA-256 `payloadHash` of its canonical unsigned payload. |
Comment on lines
+50
to
+63
| function utf8Bytes(value: string | undefined): number { | ||
| return value === undefined ? 0 : new TextEncoder().encode(value).byteLength; | ||
| } | ||
|
|
||
| export function capLiveToolPayload(value: string | undefined, cap: number): string | undefined { | ||
| if (value === undefined || utf8Bytes(value) <= cap) return value; | ||
| const suffix = "\n[tool payload truncated]"; | ||
| const budget = Math.max(0, cap - utf8Bytes(suffix)); | ||
| let end = Math.min(value.length, budget); | ||
| // UTF-16 code-unit offsets are not byte offsets. Trim to a UTF-8 boundary | ||
| // so a hostile unicode payload cannot exceed the advertised byte cap. | ||
| while (end > 0 && utf8Bytes(value.slice(0, end)) > budget) end -= 1; | ||
| return `${value.slice(0, end)}${suffix}`; | ||
| } |
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.
Fixes #3846
Beads:
cave-trt.6· Milestone: #3 Runtime Tool-Activity CompatibilityCompatibility architecture
opencode --versionandopencode run --helpwith the same safe direct-launch path used for chats; version is retained for support diagnostics, while observed capabilities select behavior.--format json,--session,--model) and decoding from that compatibility result, not a semantic-version threshold.text, terminaltool_use, anderrorJSONL events plus a retained legacy fixture; preserve native stable IDs, tool input/output/error state, chronological placement, persistence, resume, duplicate suppression, and bounded reordering reconciliation.Support matrix
opencode-run-json-v1; structured tool activity and native-session resumeEvidence and sources
run,--session,--model, and--format jsonraw events.runimplementation confirms current JSON output emits rootsessionIDwithtext, terminaltool_use, anderrorenvelopes.run --autochanges only prompt behavior, so Cave continues to refuse unsupported Read-only enforcement.The development host has no
opencodeexecutable, so its real-client version/help capture is recorded as unavailable rather than fabricated. A signed canonical registry URL and public key are external deployment prerequisites; until configured, the reviewable built-in profile is the offline baseline.Validation
pnpm check:tests-wired: passpnpm lint: passpnpm typecheck: passnode scripts/run-tests.mjs api: blocked before OpenCode tests by the unrelated host WSL errorexecvpe(/bin/bash) failed: No such file or directoryinscripts/git-hooks-pre-commit.test.mjspnpm build: an existing build process owns this worktree's.next/lock; it was left undisturbed rather than terminated