Repository navigation
Add Opencode hooks to nitro agent - #10366
Merged
Merged
Conversation
The first-prompt announcement and idle-push-per-transition gate no longer reserve reserved sentinel ids on the mail delivery ledger (session_deliveries stays real-mail-only). Both are now atomically armed/claimed via two new agent_sessions columns and IAgentSessionRegistry methods, so a Nitro-pushed chat message can skip rearming without touching the ledger at all.
Nitro's push side is expected (hc-10-5n6.2) to prepend a reserved text prefix to a delivered message instead of tagging it with metadata; the generated shim now detects and strips that exact prefix from output.parts (opencode delivers the model-bound message there, not input.parts) and sets nitroPushed on the hook payload. Adds a generated-JavaScript regression, executed under Node, guarding the output.parts requirement.
… command group
Wires the opencode hook handler landed in hc-10-nds.1 into a process
entry point: OpencodeHookExecutor mirrors ClaudeHookExecutor and
CodexHookExecutor's fail-open envelope (stdin/stdout JSON, 10s entry
timeout, NITRO_HOOK_SUPPRESS kill switch, neutral {} on every failure
except a schema mismatch, which reports to stderr with exit 1), and
the hidden `nitro agent hook opencode` command group adds the
session-created, chat-message, session-idle, and session-deleted
leaf commands the generated shim invokes.
Adds the opencode-server branch to ActorWakeDispatcher and PingSessionExecutor: wake pushes the mail digest as a real prompt (prefixed with the reserved nitro push marker so the shim recognizes and skips it) via OpencodeServerClient.PushMessageAsync, guarded by the session registry's idle-push claim so at most one push fires per idle transition and mail during a nitro-pushed turn waits for the next one; when there is nothing left to deliver, a health-only ping via PushAsync's sibling verifies the endpoint instead of a blind ok. Registers IOpencodeServerClient additively in ServiceCollectionExtensions. Existing claude-peer and codex-thread branches are untouched. Ref: hc-10-5n6.2
Move the opencode idle-push claim so it only spends the session's one-shot flag once the gate reservation is already held, instead of before it: a busy gate or a dropped capacity slot no longer burns the claim for nothing. Rearm the claim whenever an attempt did not actually deliver (a terminal failure, an access-denied offer, or a health-only ping that found no digest left to push), so a transport failure or a benign mail-already-read race no longer strands the session waiting on a human prompt. PingSessionExecutor now tags a health-only ping's outcome with a dedicated detail constant so the dispatcher can tell it apart from a delivered push. Name the opencode switch arm explicitly instead of falling through a wildcard. Covers: a busy session gate never spends the idle-push claim, an outright transport failure rearms it, and a health-only ping (no mail left to push) rearms it too.
…ation DispatchTargetAsync could strand an OpencodeServer session's one-shot idle-push claim when the dispatch was cancelled between a successful ClaimIdlePushAsync and the outcome-based rearm (a lost lease renewal or caller shutdown mid-transport). Track the claim with idlePushClaimed/ idlePushSettled flags and hand it back from the inner finally whenever a claimed attempt never reached the existing outcome-based decision.
SessionDeliveryLedger's reserving INSERT is already conditioned on the session row existing, so the FK-violation catch in OpencodeHookHandler.ReserveAsync (and the SessionRemovedDuringDeliveryException it fed) could never fire; a deleted session already reserves nothing via the normal empty-result path. Also documents that dryRun has no effect in this handler (opencode has no session-file side channel to skip). Findings F1 and F6 from hc-10-nds.1's review comment.
…stract Adds back ISessionDeliveryLedger.ReserveAsync(harness, sessionId, ...) as an additive overload with its original byte-identical SQL, and reverts ClaudeHookHandler/CodexHookHandler and their test doubles to call it, so the opencode host-aware overload no longer touches the shipped Claude/Codex paths beyond the additive abstraction. Also makes IAgentSessionRegistry's Arm/Claim announcement and idle-push members abstract instead of default no-ops, and implements them on the fake registry and every test decorator, so an implementation that does not support the markers fails to compile rather than silently doing nothing. Findings F4 and F5 from hc-10-nds.1's review comment.
Seeds a raw v12-shaped agent_sessions table, predating the announcement_pending and idle_push_armed columns, and asserts InitializeAsync adds both defaulted to 0 without losing the existing row. Finding F2 from hc-10-nds.1's review comment.
Adds a chat-message-pushed.json fixture with nitroPushed: true and a test asserting OpencodeHookExecutor deserializes it onto OpencodeHookPayload.NitroPushed. Finding F7 from hc-10-nds.1's review comment.
OpencodeHookHandler.HandleSessionIdleAsync no longer calls ClaimIdlePushAsync or reserves a gate-channel delivery: the dispatcher already claims the flag after its own gate reservation and rearms it on every non-delivering path, so an idle event that beats the daemon no longer spends the one-shot claim with nothing pushed. The event now only touches presence/heartbeat, since the shim discards its response either way. OpencodeHookOutcome.IdleDelivery is removed as dead plumbing along with it. Per the hc-10-5n6.2 planner ruling (option A).
Four minors from the hc-10-5n6.2 review cycle 3: - Split the two ActorWakeDispatcherTests exceeding the 5-Assert limit into separate push/suppress and offer/deliver tests. - Classify 'idle-not-armed' as a transient offer reason in MailWakeDaemonRetryPolicy.IsTransientOffer, matching busy/ capacity-dropped/access-denied. - Deduplicate PingSessionExecutor.ExecuteOpencodeServerAsync against ExecuteAsync via a shared helper, keeping every branch's existing behavior byte-identical. - Persist the opencode health-only ping detail only when it actually changed from the row's own last recorded state, instead of rewriting it on every idle poll.
ActorWakeDispatcher minted a fresh pingAttemptId for every dispatch but never stamped it onto agent_sessions.last_ping_attempt, so the executor's later WritePingResultAsync call (conditioned on that column) always matched zero rows: no ping result was ever durably recorded via the wake path, for any harness. Stamp the attempt through IAgentSessionRegistry.TryClaimPingCooldownAsync right before invoking the executor, mirroring the retired manual ping command's own sequence, so the write actually lands.
Adds a regression proving ActorWakeDispatcher, wired to the real PingSessionExecutor, durably stamps last_ping_attempt and writes last_ping_result on the agent_sessions row - the gap the wake-path fix closes.
Drops the persist-only-when-changed gate in PingSessionExecutor: the prior TryClaimPingCooldownAsync claim already nulls last_ping_result and last_ping_detail before every attempt, so skipping the write for a repeated health-only outcome left the row NULL on alternate wakes instead of restoring it. Every health-only ping now writes back like any other attempt. Also corrects the ActorWakeDispatcher comment describing TryClaimPingCooldownAsync's false-return condition to match its actual predicate (row missing or last_ping_at ahead of now).
This was referenced Oct 26, 2026
This was referenced Nov 2, 2026
Closed
[nuget][SUI_Matcher]- Bump the sui-package-updates group with 21 updates
DFE-Digital/SUI_Matcher#424
Open
Bump HotChocolate.AspNetCore.Pipeline and HotChocolate.Fusion.AspNetCore
feddi-dev/feddi-gateway#109
Open
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.
No description provided.