Extract subprocess execution and process contracts into @orca/process-host with explicit public entry points, independent checks, and one Node 18 compatible CommonJS runtime. Migrate application consumers and integrate package builds, dependency selection, import enforcement, and runtime resource staging.
Preserve daemon startup ownership and profile isolation, and qualify development rebuild recovery and concurrent output publication. Remove unnecessary aliases, test forwarding exports, and a duplicate output scan.
Validation: full typechecks, changed-code quality, package and artifact checks, native Windows builds/types plus 306 runtime tests, and independent native reviews.
Matched full E2E controls on original main and unchanged reviewed head both pass all 17 applicable tests (nine skips). Earlier intermittent E2E failures are documented in the PR; no assertions were weakened. The final combined-tree audit preserves both branches with no import-boundary violations.
* fix(opencode2): only treat the question tool's form as a pane blocker
OpenCode 2 has one form primitive and several producers, and Orca's setup
bridge mapped every `form.created` to `question.asked` — the un-evictable
"the pane owner must answer this" blocker. Against opencode v2.0.12 only
`metadata.kind === "question"` is the agent's question tool; `websearch.provider`
is a provider picker and `mcp-elicitation` is an MCP server prompt raised on
sessionID "global", which is not a session and so can never be retired by that
session going idle.
Admit only the question kind, remember the admitted form ids, and drop
`form.replied`/`form.cancelled` for forms that were never admitted so an
ignored form's resolution cannot retire a live blocker.
Evidence (live v2.0.12 capture, real TUI in a PTY against `opencode serve`)
in docs/bug-reproductions/opencode2-form-created-kinds. That capture also
shows the reported Subagents/Shell/Terminals dock and the agent picker emit
no server event at all, so they were never the `form.created` source.
Refs #22371
* refactor(opencode2): drop the unreachable form-resolution guard
Review was right that the admitted-form-id set defended against nothing.
`clearAttentionForResolution` builds the exact key
[factoryID, "AskUserQuestion", form.id, sourceSessionID] and returns null on a
miss, with no session-wide fallback, and form ids are unique — so a resolution
for a form Orca ignored already matches no live blocker. The guard's comment
claimed a collision the key structure rules out, which is worse than no comment.
Removes the set, its FIFO eviction helper, and the claim; the kind check on
form.created is the whole fix. The end-to-end test stays: it pins the behavior
that an MCP form raised and cancelled leaves a live question blocker standing,
which is the property worth holding regardless of how it is achieved.
* docs: allow-list opencode tool-readout follow-up note
* fix(opencode): attribute shared-server sessions to their panes
The v2 shared server stamps every hook post with its own frozen pane,
so all panes' status lands on the starter pane (#21359).
- shared: session->pane registry plus ingest-time envelope rewrite;
bound sessions resolve to their real pane, tab and live launch token
before disposition, unbound sessions keep the stamped identity.
- main: binder poll (SQLite session store, PTY-registry pane snapshots,
argv-aware client sweep) with directory-containment plus
client-lifetime correlation; 60s loop plus debounced SessionStart kick,
wired into the hook server lifecycle.
* fix(opencode): newest-wins pane dedupe, macOS private/tmp normalization
Live verification against the dev instance found two binder gaps: remint
rows for one pane counted as an ambiguous tie, and /tmp vs /private/tmp
spellings never met on macOS.
* fix(opencode): review fixes — newest-wins worktree, drop dead constant
- applyBinderOwnerships now overwrites per-pane worktree, matching the
round's newest-wins pane dedupe; a remint's live row wins over a stale
row (pinned by test).
- remove the unused OPENCODE_CLIENT_PRE_CREATE_WINDOW_MS export and the
nowMs residue from clientCouldCreate.
- give the per-pane launch-token cache its own named cap constant.
* fix(opencode): address thread review — cursor, native table, tokens, lifecycle
- composite (time_created, id) store cursor advanced past handled rows
only, so same-millisecond pagination and full unbound maps no longer
drop sessions silently.
- Windows sweep reads the native process table instead of forking
powershell.exe; quote-aware argv parsing on both platforms.
- directory keys via normalizeRuntimePathForComparison (Windows
case-fold, POSIX backslash literals) plus narrow macOS /tmp|/var|/etc
aliases and lexical dot-segment resolution.
- bound sessions always take the stored pane token (never the frozen
stamp); token tracking runs after resolution.
- binder generation guard discards post-stop rounds; first round runs
immediately at loop start.
- unbind/move use exact pane-key match; pane launch-token cache gets its
own cap constant.
- move the tool-readout note out of this PR for its own branch.
* fix(opencode): second review round — executable field, worktree scope, round lifecycle
- POSIX sweep reads comm= alongside args= and classifies on the
kernel executable name, so unquoted install paths with spaces no
longer split argv[0] and reject the client; Windows rows carry the
native table name. Degrades to argv[0] when comm is unavailable.
- bound sessions take only the binding's worktree (never the stamped
pane's), so a worktree-less binding cannot file a row under the
wrong worktree.
- the binder generation is captured before the round body and the
running flag clears only for the current generation, so an obsolete
post-stop round cannot admit an overlapping round.
---------
Co-authored-by: orca-agent <orca-agent@local>