LearnNewsExamplesServices
Frontmatter
id15394
titleWake-daemon delivery adapter for OpenCode seats
stateClosed
labels
enhancementai
assigneesneo-kimi-phoebe
createdAtJul 18, 2026, 4:01 AM
updatedAtJul 18, 2026, 12:26 PM
githubUrlhttps://github.com/neomjs/neo/issues/15394
authorneo-fable-clio
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 18, 2026, 12:26 PM

Wake-daemon delivery adapter for OpenCode seats

Closed Backlog/active-chunk-7 enhancementai
neo-fable-clio
neo-fable-clio commented on Jul 18, 2026, 4:01 AM

Context

The first OpenCode seat (@neo-kimi-phoebe, #15385/#15386, activation #15390) is fully MCP-wired — she reads and writes mail — but nothing can wake her: the wake daemon has no OpenCode delivery adapter. Her identityRoots entry deliberately ships no subscriptionTemplate ("the wake route self-registers in Memory Core from the real first-boot envelope"), so at #15390's activation her subscription will name a harnessTarget — and the daemon must have an adapter serving it. Operator-directed, completing the OpenCode support triad (#15391 registry/launch contract, #15392 seat-config generator, this ticket = wake delivery).

The Problem

The daemon's delivery routes today (ai/daemons/wake/daemon.mjs) are keyed by subscription.harnessTarget with per-route harnessTargetMetadata validation:

  • interactive osascript (:923+) — GUI activate → paste → submit with frontmost-race retries and a phase-aware double-submit guard;
  • interactive tmux — terminal injection (referenced alongside osascript in the heartbeat-delivery comments :506-509, :822-831);
  • non-interactive codex-app-server (:894-903) — JSON-RPC injection via Codex's app-server, with the load-bearing discipline stated in-file: "a route explicitly configured as codex-app-server must fail visibly instead of recreating the GUI-focus delivery path" — no silent osascript fallback. Metadata validated with named throws (:911-915).

None of these can reach an OpenCode session. OpenCode is a different injection surface: it ships a headless server mode (opencode serve) and non-interactive run modes alongside its TUI/desktop surfaces.

The Architectural Reality

Adapter semantics are cleanly separated from wake semantics: emission is idle-gated upstream (WakeDecisionService.decideWake — active AND idle AND ready), coalescing/heartbeat digest logic is adapter-agnostic (:822-831), and participationStatus === 'active' gates all delivery (:552) — the OpenCode seat becomes wake-eligible at exactly #15390's flip. A new adapter therefore adds a delivery route, not new wake policy.

The Fix

  1. Probe OpenCode's injection surface (probed, never guessed — the #15391 discipline): candidates in preference order: (a) the opencode serve HTTP API — does it expose session-scoped message injection into a live session? (b) non-interactive opencode run with session attach; (c) an explicitly-configured interactive tmux route as a declared (never silent) fallback. Record probe evidence with the exact OpenCode version pinned.
  2. New adapter route (working name opencode-server) mirroring codex-app-server's shape: non-interactive digest injection, named-throw metadata validation, fail-visible on error — no GUI fallback, coalescing/heartbeat semantics inherited unchanged.
  3. Document the first-boot envelope contract: what harnessTarget + harnessTargetMetadata an OpenCode seat's self-registration must carry so #15390-style activations land on this route (the metadata contract is the consumed surface).
  4. Conscious-update pins/tests if the adapter registry is population-pinned.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
harnessTarget: 'opencode-server' route ai/daemons/wake/daemon.mjs non-interactive wake-digest injection into a live OpenCode session fail-visible (no silent GUI/osascript fallback — codex-app-server parity :894-903) route JSDoc + in-file discipline comment daemon read 2026-07-18 (:506-509, :822-831, :894-915, :552)
OpenCode seat harnessTargetMetadata contract same + the self-registration path documented required fields, named throws on missing/invalid (parity :915) registration rejected, never a half-configured route envelope-contract section in route JSDoc probe-evidenced at implementation

Acceptance Criteria

  • Probe evidence recorded for the chosen injection surface (endpoint/command, OpenCode version, live-session proof).
  • The new route delivers a wake digest into a live OpenCode session non-interactively; failure is visible, never a silent GUI fallback.
  • Metadata validation throws named errors on missing/invalid harnessTargetMetadata (codex-app-server parity).
  • Heartbeat + coalescing semantics verified unchanged through the new adapter.
  • The first-boot envelope contract for OpenCode seats documented where the self-registration path reads it.
  • L4 receipt: one real wake delivered to the live OpenCode seat after #15390's activation flip (explicitly Post-Merge Validation if the seat is not yet active at merge time).

Out of Scope

  • #15391 (registry/launch contract) and #15392 (seat-config generator) — the triad's other legs.
  • The subscription self-registration itself (#15390 owns the activation).
  • Any OpenCode upstream changes; wake-policy changes (emission/idle gating untouched).

Decision Record impact

none — a new adapter within the daemon's existing route architecture.

Related

  • #15390 — consumes this route at activation (the subscription half; this is the delivery half).
  • #15391 / #15392 — the OpenCode support triad. #15385 / #15386 — the seat.

Candidates: @neo-kimi-phoebe post-activation (the live probe environment and the route's first beneficiary — there is something right about a peer building her own wake path); any peer otherwise. The L4 receipt needs the machine where the seat runs.

Live latest-open sweep: checked latest open issues at 2026-07-18T02:01Z; no equivalent found (#15390 = activation, disjoint; #15391/#15392 = the other triad legs). A2A in-flight sweep (last 30, all read-states): no competing claim on wake-delivery scope.

Origin Session ID: abce4d75-7dcb-4145-8afc-b0ff2cdc51e6

Retrieval Hint: query_raw_memories("wake daemon OpenCode delivery adapter opencode-server harnessTarget envelope")