Agent Notifications adds a global OpenCode plugin for root-session completion,
questions, permission requests and terminal errors. Desktop completion uses
✅ [Native session title] when available, and questions show their actual text
as ❓ Actual question? with the session title as context. macOS uses a native
subtitle; Linux and Windows include that context above the body. Missing context
falls back to generic OpenCode copy. Alerts remain silent by default;
the unreleased source offers an explicit opt-in to request the OS's
built-in notification sound where supported. The published v1.49.11 platform
channels remain silent. This is not the Claude/Codex custom-sound/volume
engine. Clicking does not navigate to a terminal or OpenCode session, and
plan/review events are outside this integration.
Agent Notifications uses
one installed plugin for OpenCode V1 and
V2: V1 calls server, V2 calls setup. Historical release
qualification targets include V1 1.18.33, V1 1.18.34 and V2 2.0.21. The installer
does not reject a CLI based on its version. Tested releases are evidence of
specific observed behavior, not a version allowlist or a guarantee for every release.
Setup installs notifications, never OpenCode itself, and does not start an agent session.
The self-contained plugin incorporates
universal-agent-plugins-opencode-events@0.3.0 from the reviewed vendored tarball.
Release preparation verifies its exact archive, lock integrity and an identical
rebuild of the embedded bundle. The SDK is separately unpublished; publishing it
or installing it from the registry is not a gate for this consumer release.
Existing installations must run update and restart OpenCode to load the new bundle.
See the platform channels for current versions on macOS arm64/amd64, Linux arm64/amd64 and Windows amd64, and the release notes for exact validation scope and retained failures. The current 1.48.6 release uses basic five-platform artifact checks and separate macOS E2E; the eleven-cell OpenCode installed-business suite was not run. Full native E2E, source-epoch/time-policy, cold-start and visible OpenCode desktop qualification are not established by those checks. Older observations below do not qualify new release bytes. For native Windows shell installation use Git Bash, not WSL or PowerShell. Desktop delivery uses the signed macOS helper, the Linux desktop notification service or Windows toasts. Linux needs an available desktop session/D-Bus service.
- Historical macOS arm64 evidence includes a user-confirmed visible completion banner.
- Historical Linux amd64 X11/dunst evidence rendered all four real OpenCode events; see the captured banners.
- Historical V1 lifecycle/webhook checks covered all five targets; retained V2 native completion observations provide corroboration. They do not qualify new release bytes by themselves.
- The historical 1.48.1 release gate covered seven native Linux/Windows cells and three artifact canaries, as detailed below. Exact release reports establish which checks passed; configured workflow lanes alone are not evidence.
- Historical macOS 1.48.3 evidence has four installed basic-business/lifecycle checks: V1 1.18.33 and V2 2.0.21 with Apple Silicon binaries run natively and Intel binaries run under Rosetta. Physical Intel execution, full native E2E and visible OpenCode banners were not proved for that release. Historical desktop observations do not extend that scope.
Windows V1 limitation: stock OpenCode V1 events do not always allow the original event age to be verified independently. A delayed completion can notify once, and the same completion can notify again after its 24-hour deduplication claim expires. Root-session and workspace filters, origin-bound provenance, deduplication within that claim lifetime, and lookup and IPC limits still apply. This accepted limitation does not qualify every Windows V1 version.
The installed fixture in scripts/opencode-native-e2e.py retains eleven custody
cells: V1 1.18.33 and V2 2.0.21 on all five platform pairs, plus Linux amd64 V1
1.18.34. Historical Linux/Windows 1.48.1 release checks covered seven native cells: both API
versions on Linux amd64/arm64 and Windows amd64, plus Linux amd64 V1 1.18.34.
Artifact canaries cover the three Linux/Windows native targets.
Historical macOS 1.48.3 separately checked four installed basic-business/lifecycle cells, using V1 1.18.33 and V2 2.0.21 on native Apple Silicon and Intel binaries under Rosetta. That scope does not prove execution on physical Intel hardware, full native E2E or visible OpenCode banners. Custody of eleven cells is not evidence that all eleven passed the same business suite. These checks cover managed lifecycle, completion delivery and revocation; they do not replace the broader semantic matrix. See the exact-source reports linked from each platform release and the release procedure.
Exact bundle/SDK/image custody, authoritative managed configuration, production parent/profile and compiled clock qualification precede delivery cases. Missing prerequisites report unqualified and stop business phases. Historical observations below retain their original source and scope; they do not qualify a newer binary or V2 by themselves.
Install OpenCode first. In the guided installer, select OpenCode. The guided command enables desktop notifications; webhook destinations and delivery can be configured later. You can also select Claude or Codex CLI in the same setup. The copied command uses one loader, for example:
(set -o pipefail; curl -fsSL https://agent-notifications.com/install.sh | bash -s -- --products claude,opencode --desktop)Channel flags grant consent only for OpenCode; webhook URLs still need separate configuration. Claude/Codex setup runs first. If a later setup fails, earlier successful installations remain installed; fix the error and rerun the command. The bootstrap requires Agent Notifications v1.46.0 or newer and acquires the checksum-verified executable for your platform from the accepted release. On Mac, desktop setup also acquires the signed native helper and its attestation sidecar from the same release. The loader pins installer source to the release's exact commit.
(set -o pipefail; curl -fsSL https://agent-notifications.com/install.sh | bash -s -- --product opencode --desktop)Use --webhook instead of --desktop for webhook-only consent, or supply both.
Webhook consent alone does not configure a destination: add your endpoint to the
installed OpenCode settings, enable the desired
webhook and status channel, and restart OpenCode. Saved settings can further
restrict authorized delivery; setup does not enable portable MCP notifications.
The OpenCode installer does not register Claude marketplace plugins or Codex hooks.
Run the same command to update. It uses the idempotent setup-opencode install
action and reuses the runtime recorded in an existing ownership ledger. Explicit channel flags replace the OpenCode channel choices; --webhook alone turns desktop consent off. With no channel flags, fresh public setup enables desktop only and updates preserve saved choices. Restart OpenCode
after installation or update to load the current global plugin bytes.
The managed control directory is ~/Library/Application Support/agent-notifications
on macOS, $XDG_CONFIG_HOME/agent-notifications (otherwise
~/.config/agent-notifications) on Linux, and %APPDATA%\agent-notifications on
Windows. A fresh public install puts the runtime under its runtime directory;
an existing shared component keeps its recorded runtime location. Check
ownership.json in the control directory for the actual runtime; do not publish
this file unredacted.
The plugin goes to OPENCODE_CONFIG_DIR/plugins/agent-notifications.js when set,
otherwise $XDG_CONFIG_HOME/opencode/plugins/agent-notifications.js, otherwise
~/.config/opencode/plugins/agent-notifications.js. Use the same environment when
setting up and starting OpenCode. The CLI also supports --opencode-config-dir,
--xdg-config-home, --home, --control-root and --runtime-root for explicit
placement. On Windows, OpenCode placement paths must be clean native absolute
paths with backslashes, without .. or a trailing separator. In Git Bash use
cygpath -w and quote the result, especially for OPENCODE_CONFIG_DIR,
XDG_CONFIG_HOME, --opencode-config-dir and --home; cygpath -m emits forward
slashes and does not satisfy this strict placement contract. For example:
export OPENCODE_CONFIG_DIR="$(cygpath -w /absolute/path/to/opencode-profile)"For manual/offline setup, obtain the native claude-notifications-OS-ARCH release
binary (.exe on Windows) and verify it against that release's checksums.txt.
The executable embeds the plugin; no npm installation or project dependency is
required. Below, NOTIFICATIONS_BIN means that verified binary or the executable
in your managed runtime:
"$NOTIFICATIONS_BIN" setup-opencode install \
--binary /absolute/path/to/verified-native-binary \
--runtime-root /absolute/path/to/new-managed-runtime \
--desktopOn macOS desktop, also extract the checksum-verified ClaudeNotifier.app.zip
and pass --native-app /absolute/path/ClaudeNotifier.app. Keep the adjacent
ClaudeNotifier.app.managed-runtime.json sidecar. Setup verifies the helper's
sealed protocol, attestation and code signature before enabling desktop consent.
Webhook-only setup does not require the native helper. Existing installations
may omit --runtime-root to use their authoritative recorded location.
Use the executable from the managed runtime to select OpenCode explicitly:
"$NOTIFICATIONS_BIN" config path --target opencode --json
"$NOTIFICATIONS_BIN" config inspect --target opencode --jsonThis selects agent-notifications.json in the managed control directory, the
same file the installed event consumer reads. Selection verifies the existing
ownership ledger, platform command, origin-bound plugin and its control-root
binding. The default control location follows the platform paths above. If setup
used --control-root, pass that same existing directory to every config
command, for example config inspect --target opencode --control-root /absolute/path/to/control --json. A control root is accepted only with matching
installed metadata; it is not an arbitrary config-file path.
Copy the opaque revision from that inspection. Submit only supported config
leaf edits through private stdin. For example, configure the webhook destination
without persisting an expanded secret:
printf '%s\n' '{"set":{"/notifications/webhook/enabled":true,"/notifications/webhook/url":"${AGENT_NOTIFICATIONS_WEBHOOK_URL}"}}' |
"$NOTIFICATIONS_BIN" config edit --target opencode --stdin --expect-revision 'REVISION_FROM_INSPECT'Replace REVISION_FROM_INSPECT with the inspected revision and make
AGENT_NOTIFICATIONS_WEBHOOK_URL available to OpenCode's environment before
starting it. Managed OpenCode webhook URLs support only this environment token
(or a literal URL); other URL tokens are rejected with
ConfigOpenCodeWebhookEnvUnsupported. The value is passed only to event delivery,
never clock or profile helpers, and the saved document retains the token. Keep any saved patch and
inspection private. For a custom control root, include --control-root on the
edit as well. Inspect again after success. Settings are read on subsequent events;
a settings-only edit does not replace the loaded plugin or require a restart.
Release boundary: The public installer currently selects v1.49.11, which does not contain this option. The following configuration applies only to source builds carrying this feature; do not expect it to work in a published installation until a new qualified platform release is promoted. No installer version restriction or opt-in is changed by this source patch.
The managed OpenCode observer is silent by default, including on upgrades.
It never inherits the global sound: true default as permission to start
audio. To request the operating system's default notification cue (not
Claude/Codex MP3 sounds), first inspect the managed revision as above, then
apply this single existing config leaf through the managed editor:
printf '%s\n' '{"set":{"/notifications/desktop/systemSound":true}}' |
"$NOTIFICATIONS_BIN" config edit --target opencode --stdin --expect-revision 'REVISION_FROM_INSPECT'The existing notifications.desktop.sound master switch must also be
enabled; systemSound starts as absent/off. Set it to false to opt out
without disabling banners. The native macOS notification helper, Windows toast
and Linux notification daemon decide whether an audible default cue is actually
played; OS Do Not Disturb and notification permissions may silence it. This
setting does not enable custom sound files, volume/device selection or
Click-to-Focus, and success from the OS notification API does not prove audible
playback. Do not enable OpenCode's own V2 attention.sound in parallel if a
single cue per event is desired. Keep the installed OpenCode desktop channel
consented; setting sound does not grant notification channel consent.
The editor uses the existing config leaf validation and raw-value preservation,
then the managed transaction's policy CAS, component/config locks and generation
publication. Unedited policy fields, other agents' route settings, setup channel
consent and the OpenCode origin remain intact. No-op edits retain the generation.
A setup/update/removal or byte change invalidates the revision; inspect again and
review the intended edit after ConfigConflict. If a commit is uncertain, inspect
and resolve any reported recovery before retrying. Interrupted managed transactions
use the existing setup-opencode recover lifecycle.
OpenCode channel consent still comes from the explicit setup --desktop and
--webhook flags. Config edits can restrict delivery and configure endpoints;
they do not grant setup consent or change registrations. Missing, corrupt,
unrecognized, recovery-pending or differently bound installations fail instead
of creating a policy or falling back to shared settings. Use the matching installed
executable; a plugin from a different embedded release must be updated through
setup first. Managed config init, imports and preflight-update are unsupported.
The managed policy retains its existing schema 1; schema 2 agent-profile migration
is outside this command.
Omitting --target, or using --target shared, preserves ordinary config
selection and its AGENT_NOTIFICATIONS_CONFIG override. The OpenCode target
ignores that override and AGENT_NOTIFICATIONS_CONTROL_ROOT; use the verified
control-root selection above.
Setup prepares the managed helper; event delivery never requests permission. Use the installed executable for these explicit actions:
"$NOTIFICATIONS_BIN" setup-opencode permission-status
"$NOTIFICATIONS_BIN" setup-opencode request-permissionAllow ClaudeNotifier in the macOS prompt and in System Settings > Notifications. Select banners or alerts and check Focus/Do Not Disturb if no banner is visible. Permission or an accepted delivery receipt alone is not proof that the OS displayed a banner. OpenCode notifications remain silent.
The lower-level update action requires an existing OpenCode registration and a verified local executable source. Choose the channels on every update:
"$NOTIFICATIONS_BIN" setup-opencode update --binary /absolute/path/to/verified-native-binary --webhookOn macOS/Linux, use "$NOTIFICATIONS_BIN" setup-opencode remove. On Windows,
use the installer's printed Remove command: it runs a temporary copy because
a running managed .exe cannot delete itself. The equivalent Git Bash command
is below; set NOTIFICATIONS_BIN to the installed executable:
(
set -eu
stage=$(mktemp -d "${TMPDIR:-/tmp}/agent-notifications-remove.XXXXXX")
trap 'status=$?; rm -rf "$stage"; exit "$status"' EXIT
cp "$NOTIFICATIONS_BIN" "$stage/remover.exe"
"$stage/remover.exe" setup-opencode remove
)A trusted release executable outside the managed runtime may also run
setup-opencode remove directly on Windows. Temporary cleanup preserves the
remover's exit status; a reported ownership conflict must still be resolved.
To enable macOS desktop on an update, supply the verified --native-app source
unless the existing managed helper already meets the required protocol. Removal
first revokes OpenCode consent, then removes owned registration and artifacts.
Other consumers and saved shared settings are preserved. Restart OpenCode after
removal to unload the old plugin. A loaded old plugin cannot admit new delivery
after revocation; a delivery already admitted before removal can still finish.
If interruption leaves a transaction journal, run setup-opencode recover,
then repeat the intended action. Recovery does not create channel consent.
Foreign, symlinked or edited plugin files are preserved and cause setup to stop;
resolve the reported ownership conflict explicitly rather than deleting blindly.
The shared observer sends content-free facts. The product plugin adds an optional
bounded desktop display envelope with a freshly verified native session title
and an immutable snapshot of the exact request's question text. Multiple questions
appear in their original order. Headers, answer options, prompts, paths and native
error bodies are excluded. Invalid or oversized metadata falls back to generic
copy. Turning off notifications.desktop.showSessionLabel hides session names
while keeping concrete question text. Webhook messages and receipts remain
generic; configuring a webhook intentionally sends those generic events to your
selected endpoint. Each request rechecks current
consent, registration and owned plugin/executable identities. See
configuration for channel and status restrictions.
OpenCode's own desktop notifications and other notification plugins may create duplicates. In OpenCode 1.18.33's desktop app, Settings > General > Notifications has Agent, Permissions and Errors switches: disable overlapping native notifications if Agent Notifications should be your notification source. Also check other plugins in your OpenCode profile.
One-shot opencode run may exit before asynchronous delivery completes. Delivery
at host shutdown is best effort; notification delivery after process exit is not
guaranteed. Root-session events only are covered; nested subagent events,
custom audio controls, click-to-focus and plan/review alerts remain outside this
integration. The optional OS default sound is subject to platform settings.
V2 completion means one positively verified final answer per native busy period. Queued or steered inputs can share that period. A tool step, retry, compaction summary or interrupted execution does not itself produce completion. The observer checks the root session and its owning directory/workspace before delivery, since V2 plugin event subscriptions also receive events from other locations.
Lookup and IPC limits apply per plugin instance. Soft lookup timeouts retain their capacity until the native promise settles, including on 2.0.0 where abort signals are ignored. Cleanup suppresses late verification without waiting indefinitely; there is no delivery replay after an uncertain result. V2 2.0.0 needs a server restart to reload the plugin; 2.0.21 supports location reload.
On 2026-09-29, a fresh disposable Linux amd64 project and isolated OpenCode
profile loaded the bundle from Notifications commit
2da9d192b1863f9d2220e760ad3fc47177ae15d2.
The test used OpenCode 1.18.33 and a locally built Linux amd64 binary
(sha256:5a83226dedb208c80c2597b5fa48de745bbbf6ddb5f7afdf4b7d95f7c111ee54).
The bundle source hash was
sha256:0148e31bbd1b9bca49c674689a5f621a26d72142974b9cc663b8a96f72461b81.
The operator retained test data and logs in the disposable sandbox. No agent
command ran in a product checkout.
- A loopback webhook received
task_complete,question,permission_request, andopencode_errorfrom the loaded OpenCode plugin. The question came from a nativequestion.askedtriggered through the OpenCode SDK with the question tool enabled. The error came from a test request for a nonexistent model. Captured payloads contained none of the test prompt markers. - With a private D-Bus session and test implementation of
org.freedesktop.Notifications, the loaded plugin made oneNotifycall for a completed turn and one for a terminal error. Each call used generic product copy; no duplicate call was observed in those runs. install,updatebetween desktop and webhook consent, andremovecompleted. While the OpenCode server still held the old plugin in memory, a new error afterremoveproduced no webhook request.
One-shot opencode run can exit after printing an answer before asynchronous
notification delivery finishes. The persistent OpenCode server runs above
verified both loaded desktop outcomes. This MVP is best effort at process
shutdown; it does not promise delivery after the host exits.
A second disposable, Git-initialized test project used the merged Notifications
binary (sha256:a1a70f35a6fbdd40ac50123dca5582c6fb4fe6e88724427bfc5f7e8d0460e7aa)
and OpenCode 1.18.33. setup-opencode install --desktop created a mode-0755
managed executable and a mode-0600 global plugin. The loaded OpenCode config
listed only that plugin. In a private D-Bus session with a real dunst daemon
and Xvfb display, native completion, question, permission, and error events each
rendered one visible system banner (dunstctl count displayed was 1 at each
capture). The banners used generic text and omitted the test prompt markers.
The four captured banners
show the actual X11 pixels, in that order. This qualifies the Linux X11/dunst
path, not every desktop environment or the OpenCode desktop app's own switches.