npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@becos-ai/becos-opencode-v2

v0.28.9

Published

becos for OpenCode V2: default-on live capture and automatic context, append-only lifecycle facts, opt-in tools, and explicit non-fork backfill.

Readme

becos for OpenCode V2

TODO — entity history (file history) is NOT enabled. ⚠ Enabling it causes the prompt-cache-miss bug: it still rides the system prompt, so each newly touched file rewrites the cached conversation. It must move to session instruction entries before release (PENDING_V2.md). VONIC_CODECOLLAB_ENTITY_HISTORY stays off by default and must not be turned on, by default or in an install, until thorough testing and code evaluation are done. See the checklist at the top of PENDING_V2.md.

TODO — server-side recall dedup is required. Recall repeats facts already sent in the session; the backend should exclude them and use the budget for new items. See PENDING_V2.md.

0.28.8: fixes a prompt-cache miss: recall and static context are now session instruction entries, so later prompts read the conversation from cache instead of rewriting it (one measured Claude session had rewritten ~340k tokens per prompt). See CHANGELOG.md.

Local file-persistence correction: OpenCode's tool executed marker means provider-executed, not “the host ran this tool.” Completed native local calls use false; only those contribute tool-derived local file operations. Errors, unfinished/uncalled operations, provider-hosted calls and missing markers do not. This is verified with an isolated OpenCode 2.2.5 fake-model probe; the SDK target remains 2.0.32. No filesystem scan, backfill, upload-owner or core screening change.

becos_status.collector.persisted now counts files in validated core sealing summaries, not zero-exit subprocesses. persistInvocations, persistUnchanged, persistSkipped (files), persistPolicySkips (invocations), persistPartial and persistFailures distinguish outcomes. Partial results count their saved files and one partial failure; empty/malformed output is a failure, not proof of storage. Mirror refresh remains separate. shared_handoff.py persist-turn is the existing screened path for an explicitly authorized, path-scoped manual repair; do not duplicate its uploads with conversational save tools.

Run the no-backend native regression with:

OPENCODE_V2_BIN=/absolute/path/to/opencode bun run test:persistence:v2

0.27.0 rebrand: commands are /becos-*, tools and permission actions are becos_*, and client RPCs are becos.ops / becos.notices. The npm package is @becos-ai/becos-opencode-v2, with CLI becos-oc-v2 (bin/becos-oc.mjs). Re-vendored the shared core at 77acf91, matching OpenCode V1 c5d6be5. Existing directory-based installs need no configuration change. Reload the server plugin and reopen the terminal UI to replace its already-registered commands; do not restart the shared service. Environment variables, oc-codecollab.env, ~/.cache/codecollab/, backend vonic_* tools, and evidence/context tags keep their existing names for compatibility. No legacy aliases are registered. If you configured explicit permission rules, rename their codecollab_* keys to becos_*; mutations still require confirmation and configured denials stay final.

Generated-file ownership (0.26.4): native context (on by default; options.context=false opts out) carries a fresh local upload-owner declaration on each request; project tools also carry it. Only enabled nocode pipelines declare client_hooks; options.capture=false, file opt-outs, Git scope and Coding Mode declare none. Do not duplicate hook uploads using conversational save tools or claim success without a receipt.

Capture defaults ON when this plugin loads. Set options.capture=false to opt out, or VONIC_CODECOLLAB_DISABLED=1 to stand down. Capture covers only fully observed new live turns and append-only fork/revert facts. Fork history is never backfilled. See PLAN.md for remaining acceptance work.

Independent port of @becos-ai/[email protected], preserving its Git history and vendored Python core. Targets OpenCode 2.0.32. Package: @becos-ai/[email protected].

Not a production replacement for V1. Includes a native status tool, opt-in tool/command preview, verified V2 normalization and moved-session segmentation, authenticated paginated backfill, and offline/native-host contract tests.

Automatic capture is on by default, with options.capture=false as the explicit opt-out. Automatic context injection is also on by default, with options.context=false as the explicit opt-out. Shared-handoff mutations and project switching are implemented behind the host's own confirmation prompt (options.tools=true). Per-turn file persistence for projects without a git repository runs with capture. A ./tui export provides the terminal surface: status, sign-in, the project picker and the shared inbox. The inherited V1 implementation remains as migration input and regression tests; it is not the V2 entrypoint. Published to public npm as @becos-ai/becos-opencode-v2 by CI on a version tag (npm Trusted Publishing, same as V1); see docs/RELEASING.md. Install with { "plugins": [{ "package": "@becos-ai/becos-opencode-v2" }] }, then bunx @becos-ai/becos-opencode-v2 login.

Development

Requires Bun, Node 22+, and Python 3.

bun install --frozen-lockfile
bun run typecheck
bun run build
env -u OPENCODE -u CLAUDE_AGENT_SDK_VERSION -u CURSOR_VERSION -u VONIC_CODECOLLAB_CLIENT_TAG bun run test
bun run check-vendor

To load the plugin in a separate V2 profile, point plugins at this repository's directory, not dist/plugin.js. Do not replace your working V1 config:

{
  "$schema": "https://opencode.ai/config.json",
  "plugins": [{ "package": "/home/amitoj/oc-v2-codecollab-plugin", "options": { "capture": false } }]
}

The native-host probe creates and removes its own disposable XDG profile, imports artificial history, and calls no model or production CodeCollab service:

OPENCODE_V2_BIN=/absolute/path/to/v2/opencode bun run test:smoke:v2
OPENCODE_V2_BIN=/absolute/path/to/v2/opencode bun run test:package:v2
OPENCODE_V2_BIN=/absolute/path/to/v2/opencode bun run test:history:v2

The history probe additionally runs real executions, mid-turn compaction, interruption, restart and moves against a local fake model. CodeCollab tool execution in that probe uses a mocked Python transport. It contacts neither real model providers nor production CodeCollab services.

Opt-in tool/command preview

In a separate V2 profile, configure the directory entry with "options": { "tools": true }:

{
  "$schema": "https://opencode.ai/config.json",
  "plugins": [{ "package": "/home/amitoj/oc-v2-codecollab-plugin", "options": { "tools": true } }]
}

This registers tools without making startup network calls. Invoking them uses your real CodeCollab login/backend unless a test injects a mock; don't invoke them as production smoke tests.

| Tool | Preview actions | |---|---| | becos_recall | Standalone query, or omit it to search this turn's own prompt (V1 parity) | | becos_team | Team-activity query | | becos_rulebook | list, or decide only for the user's explicit choices | | becos_project | list, or use only for the user's explicit choice | | becos_handoff | Reports this session's automatic handoff, or fetches on demand with budget_chars (unavailable with context: false) | | becos_handles | me, search, resolve; claim behind a confirmation prompt | | becos_share | draft inspects selected files and writes a local review manifest; confirm sends it behind a confirmation prompt | | becos_shared | list, get, seen; accept, decline, download behind a confirmation prompt | | becos_backfill | Dry run by default; deliver: true ingests past sessions behind a confirmation prompt |

Native commands: /becos-recall, /becos-team, /becos-rulebook, /becos-project, /becos-share, /becos-shared, /becos-backfill. No global command files are written. Rulebook/project changes retain the explicit-request convention. Shared/project opt-outs are checked per session on each invocation; commands/tools remain visible but report disabled.

Confirmed mutations

Five shared actions and the project switch change something outside this session, so each one calls permission.assert before any subprocess runs and performs nothing if the user declines:

| Action | Permission action | Resource shown | |---|---|---| | becos_handles action=claim | becos_shared_handoff | handles claim @ada | | becos_share action=confirm | becos_shared_handoff | share confirm @ada (addressee, else the draft id) | | becos_shared action=accept | becos_shared_handoff | shared accept <handoff_id> | | becos_shared action=decline | becos_shared_handoff | shared decline <handoff_id> | | becos_shared action=download | becos_shared_handoff | shared download <handoff_id> <artifact_id> | | becos_project action=use | becos_project_switch | project use <name> | | becos_backfill deliver=true | becos_backfill_deliver | backfill deliver <scope> |

A permission.hook("evaluate") registered with the tools escalates both actions to ask, so a configured blanket allow still prompts; a configured deny stays final and never reaches the hook. Nothing is saved for next time (save: []), and a host rejection propagates as the host's own error rather than becoming tool output the model could ignore. seen stays ungated: it is an idempotent receipt that discloses no content and transfers nothing. The native acceptance probe proves the prompt under a blanket allow, and that a decline leaves the shared core uncalled.

Python calls have bounded input/output, timeout/cancellation and unload cleanup. Scope/environment is resolved from the invoking session, never the plugin instance's directory. The global env file respects XDG_CONFIG_HOME; repository env files cannot override the interpreter, process PATH/HOME/XDG roots, capture opt-ins or body caps. Core auth, screening and delivery logic stays vendored unchanged.

Context channels (options.context)

The whole request-context surface is on by default; options.context: false turns all of it off. Each channel can also be narrowed independently, under V1's flag names and V1's reading of them, so "instructions but not recall injection" is expressible here as it is there:

| Channel | Flag | Default | |---|---|---| | Recalled memory | VONIC_CODECOLLAB_RECALL_INJECT | on; 0 skips the backend query entirely, not just the block | | Accepted rules | VONIC_CODECOLLAB_RULEBOOK | off; 1 enables | | Project line | VONIC_CODECOLLAB_PROJECTS | on; 0 disables | | Startup handoff | VONIC_CODECOLLAB_HANDOFF_AUTO, VONIC_HANDOFF_SESSION_START | on; either set to 0 disables | | File history | VONIC_CODECOLLAB_ENTITY_HISTORY | off; not enabled pending evaluation (PENDING_V2.md) | | Subagent recall | VONIC_CODECOLLAB_RECALL_SUBAGENTS | on; 0 excludes dispatched subagents from both the tool and injection |

One deliberate divergence: V1 defaults RECALL_INJECT off, because there the silent injection is itself the opt-in. V2 defaults it on, like the Claude Code plugin. Instructions have no adapter-side switch — the unchanged core composes them from VONIC_CODECOLLAB_PROVENANCE, ..._RECALL_FEEDBACK, ..._RESOLVE_TOOL, ..._ENTITY_HISTORY and ..._TEAM_ACTIVITY, and turning all of those off already yields an empty section; a second answer to the same question would only drift. becos_status reports the resolved channels.

Terminal surface (./tui)

The package exports ./tui beside the server plugin, so OpenCode loads it automatically with the plugin; a CLI-only install can list the package in cli.json to keep it active against a remote server. A directory entry in opencode.json is resolved by physical path (<dir>/server, <dir>/tui) rather than through exports, which is why server.js and tui.js shims sit at the repository root. CLI plugins load when the terminal starts: unlike the server plugin, which the running server hot-reloads, a new or changed terminal surface needs the TUI restarted.

It adds /becos-status, /becos-login, /becos-project and /becos-inbox (also in the command palette, under "becos"). The terminal's inbox read has its own name because /becos-shared is the server plugin's model-facing route; one slash name never resolves to two different surfaces.

The surface owns no logic: every action is one becos.ops RPC call into the server plugin, which owns the Python core, the environment and the screening. It spawns no process, reads no credential and contacts no backend itself. If no CodeCollab server plugin answers, the commands report that and the terminal keeps working.

  • Sign-in runs the unchanged connect.py browser flow through the server: the core's own message (link plus code) is shown verbatim, the code is pasted back into a prompt, and the token is written by the core into this profile's cache. The adapter never asks for a raw token, and an invalid code is refused locally before any process starts.
  • Project picker lists the user's projects for this folder and records only the chosen one. A folder whose project comes from its git origin is explained instead of offered.
  • Shared inbox is read-only here. Accepting, declining or downloading a package stays with the gated tools so it always passes the host's confirmation prompt.
  • Notices are pushed by the server, not polled here. The server plugin emits an RPC event on becos.notices at the moment it knows something: how much recall it injected on a turn, how much prior-session evidence the startup handoff added (once per session), and the session's project line (again only if the session is re-filed). The terminal subscribes and draws them; it decides nothing. That restores V1's arrangement, where the server plugin called showToast itself — V2's server context has no ui domain and a subscribe-only event domain, so an RPC event is the equivalent. A missing sign-in is still read from status (no request produces it), and waiting shared handoffs are still polled once per idle turn, because the server has no arrival event to push from.
  • A client with no terminal — headless, desktop, a remote session — renders no notices. The pull surfaces are becos_status and becos.ops status. Notices are never routed into the model's request context as a substitute.

becos.ops methods (status, login, project, inbox) are user-initiated from a client of this server and are not reachable by the model, whose path is the gated tools above. Identifiers are passed as arguments the core parses, never as option fragments, and a failing child is reported as becos unavailable (<status>) so stderr, paths and tokens never cross the boundary.

Explicit CLI

node bin/becos-oc.mjs --help
node bin/becos-oc.mjs backfill                         # dry run; reads session history
node bin/becos-oc.mjs backfill --project myrepo         # directory substring filter
node bin/becos-oc.mjs backfill --session ses_example    # exact id or prefix

Backfill discovers an already-running, compatible V2 service in the current XDG profile and uses its authentication. It never starts an arbitrary opencode from PATH, stops your service, or reads the private database. Select the intended XDG profile explicitly if V1 and V2 coexist. Discovery fails before any upload for unreadable/changing history, malformed pagination, unsettled messages, mid-turn/inconsistent moves, and staged reverts/forks (the selected policy is not wired yet). Native failed/interrupted terminal markers settle partial replies as error turns; running/streaming tool inputs are excluded. A failed/unexecuted edit is an attempted tool event, not an applied change. Shell working directories are attributed independently. Settled moves are split into stable segments: the first retains the original session ID, later segments use a suffix derived from the move message ID. Each segment uses its own cwd/environment and upload-manifest key. Remote-server selection is pending.

--deliver is an explicit opt-in to the inherited Python upload pipeline; production delivery has not been acceptance-tested for this V2 alpha. --watch additionally waits for positive delivery and distillation receipts. Do not run production delivery as a smoke test.

becos-oc-v2 login [authorization-code-UUID] delegates PKCE directly to the shared Python core. It never installs command files or rewrites V1's environment/mint scripts. Login uses the shared CodeCollab credential cache; it is not isolated automatically and must be requested explicitly.

Incremental capture (default on, explicit opt-out)

Optional automatic context

Automatic recall is on by default, independently of capture and tools, so each new prompt queries memory once. Set "options": { "context": false } on the plugin entry to opt out. VONIC_CODECOLLAB_DISABLED=1, VONIC_RECALL_ENABLED=0 or VONIC_RECALL_SCOPE=off stand down.

Carrier (0.28.8): a session instruction entry. The prompt hook queries the unchanged recall.py core once per user prompt, before admission, and stores the result as OpenCode's session.instructions.entry under key becos-recall. The host shows the first value at the start of the session's instructions and each later value as a chronological "changed and supersedes" update, so the request prefix stays the same between prompts and the prompt cache holds. Recall previously changed the system prompt on every prompt, which invalidated the whole cached conversation (about 340k tokens rewritten per prompt in one measured Claude session; see docs/PLAN_cache_miss_fix.md).

The static sections (instructions, upload ownership, project line, accepted rules and the startup handoff) are entries too (becos-instructions, becos-persistence, becos-project, becos-rulebook, becos-handoff). They are put at admission, so the first request has them, and written again only when their bytes change; a change becomes one update message. A failed section lookup or scope check leaves the last good values in place. Opt-in file history stays a system part.

A prompt repeated verbatim in the same scope and configuration keeps the recall already in place instead of repeating an identical lookup (recallReused). A continuation prompt (exactly "continue", "go on", "yes", "ok" or "proceed", ignoring case and trailing punctuation) keeps the previous prompt's recall without a lookup (recallContinued); anything longer is looked up.

A recalled item already delivered in this session is replaced by an inline pointer in its ranked place: - (still current; full text sent earlier this session: decision_…), about 77 characters against a median 396 for the item (recallPointers, recallPointerSavedChars). The wording matters: the host labels earlier recall blocks superseded, and in live tests (3 Haiku + 3 Sonnet runs) the model answered a pointed-to fact correctly every time, but said UNKNOWN every time when the repeat was simply dropped. Items are sent in full again after compaction, a revert, or a removed entry. Text in an unrecognised format passes through unchanged.

A lookup may hold a prompt for at most 20 seconds. Past that the prompt goes through and the result is applied while its turn is still running, reaching the next model step; a result whose turn has ended, or that a newer prompt replaced, is dropped. A lookup failure never rejects the prompt. An empty result, a slash command, an oversized prompt or VONIC_CODECOLLAB_RECALL_INJECT=0 removes the entry, so the previous prompt's recall never reads as current. A host without instruction entries keeps the previous per-step system-part carrier.

Retention. Recalled memory is now stored in OpenCode's local session database, appears in session.context and session exports, and (host behaviour) survives session deletion in its instruction_blob table. Treat exports as containing team memory. Live capture and backfill still exclude it.

The lookup uses only the newly submitted user prompt and the session's current cwd. It never extracts a query from model history, files/attachments, compaction summaries, synthetic messages or pre-admission drafts. No old prompts are queried after attaching/reloading mid-turn. A dispatched subagent gets recall on its own task (caller_mode: subagent, at most four at once per parent, VONIC_CODECOLLAB_RECALL_SUBAGENTS=0 turns it off); a primary fork child is treated as primary.

Returned context retains core provenance/citations and receives an explicit retrieved-evidence warning. User input is unchanged, and context is not copied into capture. Actual assistant answers still capture normally. Explicit recall-tool calls remain independent.

On the system-part fallback (hosts without instruction entries): every model step checks local scope, and recall is cached for the current delivered prompt + core repository/project identity (branch and author, not the commit) + VONIC configuration; prompt/scope/config changes requery.

Bounds: 32 observed sessions, 128 queued items, 20 KiB query text, 20,000 characters of injected core output, and a 25-second overall lookup deadline. Adapter queries/results remain in memory only; the core's explicitly enabled debug logging keeps its inherited behavior. Interruption, move, revert, shutdown or stream loss cancels obsolete work; scope/env is checked again before injection. Unavailable/malformed/oversized output fails open without raw diagnostic output. Unknown deliveries, oversized prompts and slash commands do not reuse the preceding prompt's recall.

The same context surface supplies these additional request-only channels:

  • Instructions: compact/legacy provenance, feedback and resolve guidance come from the unchanged core text selectors. Static parts are reattached on each outgoing primary request, including after compaction. Explicit instruction flags remain authoritative; unavailable team/entity tools are not advertised. Instruction copies are suppressed in the recall subprocess to avoid legacy duplication.
  • Rulebook: VONIC_CODECOLLAB_RULEBOOK=1 injects only accepted cached rules, never candidates. A plugin-owned, cancellable five-second refresh updates the shared cache (at most one per session at a time, normally at least 30 seconds apart); the next request sees fresh rules. No detached SessionStart refresh process, automatic acceptance, or project selection is launched.
  • Startup handoff: the fetch starts at the session's session.created event, before any prompt, for primary sessions only, and is never awaited. It then follows V1's lifecycle exactly (pending → ready → bound/delivered, empty, failed), defers past a turn already in flight, and is re-applied to each later request without duplicating. One logical fetch per session with at most one retry; the active session is excluded by the shared core. becos_handoff reports that state or fetches on demand with its own budget_chars. VONIC_CODECOLLAB_HANDOFF_AUTO=0 or VONIC_HANDOFF_SESSION_START=0 disables it. A resumed or attached session gets no catch-up fetch, and fork ancestry is never hydrated. The backend scopes a handoff to the authenticated tenant and the repository, so a teammate's captured work on it is included and every turn names its author; the adapter adds that attribution to the framed block because the vendored upstream preamble still describes the older caller-only scope — see docs/HANDOFF.md for the full contract and the V1/V2 parity table.
  • Project: the session's project is resolved once per repository identity (refreshed on a 60-second TTL) through the unchanged core, which pins a dotfile result so a mid-session edit cannot re-file later turns. The model is told where the conversation is saved; a line resolved for another identity is dropped rather than shown. VONIC_CODECOLLAB_PROJECTS=0 disables it. No picker, backend write or detached list refresh happens here — switching is the confirmed becos_project action or the terminal picker.
  • File history: with tools: true and VONIC_CODECOLLAB_ENTITY_HISTORY=1, native reads and edit/write/patch attempts use the unchanged entity-history core's path screening and once-per-file claims. History is queued into the next primary request, not appended to tool output. An unseen file's edit is refused once with a generic retry message; it does not put the retrieved text into a durable tool error. Current scope/cancellation checks remain fail-open. Unknown/MCP names are never inferred to be native file operations.

Carrier. Context is added as a tagged SystemPart by default. @openchamber/opencode-claude replaces the system prompt with a Claude Code preset and never reads the request's system array, so for claude-code models the same content is prefixed onto the request's own copy of the latest user text part instead (<codecollab-context>), copy-on-write and idempotent. The stored conversation is untouched either way, and capture never reads model messages. Without this guard the whole context channel is silently dropped for that provider — V1 made the same distinction with receivesSystemTransform.

Native Claude Agent (claude-agent, OpenCode 2.2.5). This is not the external claude-code bridge. Its built-in hook snapshots system instructions before external plugins run. Becos keeps its own recall and appends all its request context to the native claudeCodeInstructions option, preserving the host snapshot. Only Becos's tagged suffix is replaced on reused requests; user messages and stored history are untouched. Claude hooks remain disabled by the native provider.

Delivery diagnostics. becos_status.context.attached counts outgoing request attachments, with systemAttached, claudeInstructionsAttached, and promptAttached identifying the route; cacheHits counts reused lookups. injected is a compatibility alias for attached, not proof of model receipt. modelReceipt: "unverified" is explicit: this adapter has no provider acknowledgement or model-side receipt. Recall notices say “attached to request.” Run OPENCODE_V2_BIN=/path/to/opencode bun run test:context:v2 for the isolated real-host snapshot-order/cache regression (fake model/core, no live Claude or backend). A real-Claude end-to-end receipt check remains outstanding.

The host may start a turn's first model request before the plugin's event stream reports that turn's delivery. The context hook therefore waits up to 1.5 seconds for that live boundary (never reading model messages instead); if it still has not arrived, that request proceeds without CodeCollab context.

Additional channels are capped at 48,000 characters per request (instructions 12k, accepted rules 6k, handoff 8k, then newest file history within the remainder), separate from the 20k recall cap. At most 16 file-history sections of 15k characters are retained per observed session; concurrent file lookups are capped at eight, with a ten-second deadline. The core owns existing rulebook/entity claim caches; the adapter writes no raw context journal. File-history text is not reconstructed after a plugin reload: core once-per-file claims retain their existing lifecycle behavior. Local repository identity is rechecked before injection: branch/commit/project or environment changes cannot reuse stale sections. Legacy Claude SessionStart hooks are not loaded; the adapter-owned oc_context.py calls core functions directly.

The native acceptance probe (bun run test:acceptance:v2, with OPENCODE_V2_BIN set) uses only a loopback fake model/MCP server. It verifies direct MCP/Code Mode settlement, opaque argument exclusion, shared-core result screening, request-only recall, actual plugin hot reload and a fault-injected subscription disconnect. The pending provider response is explicitly gated during reload/disconnect; partial turns are skipped and fresh turns resume without replay.

Native Claude Agent (claude-agent, OpenCode 2.2.5). This is not the external claude-code bridge. Its built-in hook snapshots system instructions before external plugins run, including instruction entries, so all Becos context reaches it through the host. Only opt-in file history, and the fallback carrier on hosts without entries, still append to the native claudeCodeInstructions option, preserving the host snapshot. Only Becos's tagged suffix is replaced on reused requests; user messages and stored history are untouched. Claude hooks remain disabled by the native provider.

Delivery diagnostics. becos_status.context.attached counts outgoing request attachments, with systemAttached, claudeInstructionsAttached, and promptAttached identifying the route; cacheHits counts reused lookups on the fallback carrier. recallCarrier names the recall carrier; entryPuts, entryRemoves, budgetExpired and lateDropped count instruction-entry activity.

Prompt-cache diagnostics. becos_status.context.cache reads the host's own per-step token counts (session.step.ended). A step whose cache read falls below half of the previous request's prompt size counts as missSuspected, unless more than 5 minutes passed (coldAfterIdle) or the session was just compacted (afterCompaction); last holds the latest step's read/write/input. missSuspected should stay 0. bun run test:context:v2 asserts that every request starts with the previous request's messages byte for byte (and fails on the old system-prompt carrier). bun run test:cache:live [model] is a manual probe with real Claude and real recall (uses quota): it requires every request to read at least 90% of the previous one from cache, across a tool step, an interrupt and a restart. injected is a compatibility alias for attached, not proof of model receipt. modelReceipt: "unverified" is explicit: this adapter has no provider acknowledgement or model-side receipt. Recall notices say “attached to request.” Run OPENCODE_V2_BIN=/path/to/opencode bun run test:context:v2 for the isolated real-host snapshot-order/cache regression (fake model/core, no live Claude or backend). A real-Claude end-to-end receipt check remains outstanding.

Observed capture and routing

V2 2.0.32 does have incremental APIs: the prompt hook, accepted/enqueued/delivered inbox events, finalized assistant text, tool calls/results and execution-terminal events. A validated adapter can collect these as they happen and submit assembled turns to the existing shared delivery flow without re-reading the full transcript. It must not rely on session.context(), which compaction can truncate.

Loading the plugin without a capture option enables capture independently of preview tools. The example above explicitly disables it. capture: true also enables it; capture: false disables both live observation and startup retry. tools: true is still required for preview tools/commands. src/v2/collector.ts correlates enqueued/delivered IDs, execution boundaries, finalized text, tool settlement and steps. Draft prompt hooks, deltas, attachments and provider state are not journaled. Execution boundaries may omit location; only a previously observed session supplies that scope. Compaction does not clear collected evidence. Unknown deliveries, disconnects and bounds violations discard incomplete executions; only new fully observed executions resume.

Per-turn file persistence

With capture on and shared handoffs enabled, each completed turn's created/modified paths are handed to the unchanged shared_handoff.py persist-turn. Projects without a git repository only — but the core decides that, not this adapter: it resolves the project, owns VONIC_CODECOLLAB_PERSIST_FILES and the host stand-downs, screens the bytes, applies the 10-file / 25 MiB caps and always exits 0. The adapter checks only the shared-handoff flag and "there is nothing to persist", selects paths from the same normalized turn it just delivered (no second transcript parse), and never awaits the child: hashing, screening and uploads cannot delay observing the next turn. At most eight run concurrently; a crashed or failing child only increments a local counter. Persisted files stay private to the user until a shared handoff attaches them by reference.

Exactly one runtime owns a conversation. Under OpenCode's Claude bridge (OPENCODE=1 plus CLAUDE_AGENT_SDK_VERSION), the cc runtime stands down — capture.hosted_by_opencode() — while this adapter keeps capturing; a plain claude started from an OpenCode terminal has no SDK marker and keeps its own capture. A regression test pins both directions.

Raw evidence stays in bounded memory (32 sessions, 512 KiB/session, 8 MiB total; delivery queue 16 jobs/8 MiB). Overflow skips work and increments local status counters, never triggers backfill. The adapter-owned deliver/oc_incremental.py bridge serializes deltas into the unchanged shared core's cumulative snapshot boundary. Body persistence occurs only after core screening. Stable source IDs deduplicate appends, including a retry after recording but before the adapter receipt was written. On attachment, only this adapter's already-screened pending buffers for the exact location are retried. VONIC_CODECOLLAB_DISABLED=1 stands down. Unscreened handoff failures receive three bounded in-memory attempts, then are counted as rejected; they are not recovered from history after a crash.

This is not production acceptance. Moves discard any in-flight partial execution, never relabel prior evidence. Observed committed moves now produce structural checkpoints shared by location instances through the existing local cache. Their canonical message ID is derived from the moved event (evt_ → msg_, the pinned host reducer's SessionMessage.ID.fromEvent rule), not the move inbox ID. Each finalized live turn carries its terminal durable sequence; the Python bridge selects the corresponding observed boundary and exact cwd. The first segment retains the source session ID; later segments use :oc-location:<16-hex SHA-256(move-message-ID)>, identical to explicit history normalization. Moving back creates another segment, not a merge into the original buffer.

deliver/oc_locations.py stores only validated IDs, sequence numbers and local directory references (10,000 source-session checkpoints, 1,024 moves/session). It reads no transcript and ships no paths in a new wire field. Location instances use their own environment for delivery. A separate bounded structural lane prevents move checkpoints waiting behind backend calls; an observed move hint must match the durable checkpoint before body recording. Duplicate checkpoints are idempotent; stale or conflicting controls and cwd mismatches fail closed. Conflicting backfill buffers remain refused. Reload can reuse already observed structural checkpoints to route new complete turns; it does not reconstruct missed moves. A later newly observed move can establish a fresh anchor after a gap. Shared-core retention rules still apply to pending turns.

No historical catch-up. Deliberately ignored: conversations before activation; the partial turn when attaching mid-turn; events missed while unloaded/disconnected; and unobserved intervals after a crash. Skip incomplete turns and resume with new fully observed prompts/turns. Do not launch history/replay/backfill jobs to fill those intervals. Retries/deduplication for already-observed, privacy-screened pending records remain in scope.

The native context lacks message.list() while the HTTP client exposes it. That does not block this prototype: it neither rereads V1-style transcripts nor reconstructs unobserved intervals. The existing backfill CLI stays explicit/user-requested and must not be dispatched for a forked session. No implicit server discovery or private-DB access is added to the collector.

For forks, src/v2/lifecycle.ts records the observed child/parent and before/through boundary as a reference-only lifecycle fact. Subsequent child turns link through the same session_id; inherited text/tool evidence is never replayed. Nested forks form immediate-parent links, without hydrating ancestors. Bulk explicit backfill reports and excludes forked sessions before transcript reads; explicitly selecting a fork is refused. Staged-revert backfill remains refused separately.

Reverts append distinct staged, cleared and committed facts. Staging is not described as a completed history rollback; cleared events do not include a target, so none is invented. Previously captured turns are never deleted or rewritten. Collection resumes with subsequent complete observed executions.

deliver/oc_lifecycle.py validates only structural IDs/enums/timestamps before persistence and emits vonic_remember(mode="fact") with event_type="other", other_type="opencode_<type>" and the reference under metadata.event.metadata.lifecycle. These records are not fabricated dialogue. Lifecycle facts use a separate bounded local spool (10,000 records including dedupe tombstones), immutable arguments and source-event-derived IDs. Existing core repository scope/auth/receipt helpers are reused; unscoped writes are rejected. Pending facts retry at attachment (bounded batch) and on subsequent turns; accepted receipts retain tombstones, terminal rejections are parked. No backend contract or vendored-core file changed. See PLAN.md for remaining acceptance work.

See the investigation and future-only upstream proposal. No OpenCode core or installed binary has been modified.

See PLAN.md, DECISIONS.md, and the adapter reference. The original V1 README is archived, not current V2 guidance.