opencode-reviewer 0.7.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,13 +1,21 @@
1
- # OpenCode reviewer
1
+ # OpenCode Reviewer
2
2
 
3
3
  Reviews pending permissions in the OpenCode sidebar using a separate LLM. Covers
4
- shell commands, file edits, MCP calls, custom tools, and external-directory access.
5
- Supports streaming explanations, optional one-time auto-approval, and
6
- reviewer-aware Linux desktop notifications with distinct sounds.
4
+ shell commands, file edits, skill loading, MCP calls, custom tools, and external-directory access.
5
+ Supports streaming explanations, saved report history, auto-approval, and Linux
6
+ desktop notifications with distinct sounds.
7
7
 
8
- Requires OpenCode 1.18.35 on Linux.
8
+ This plugin is meant to enable you to run sessions with less frequent input in a safer way. Also giving the reviewer context of specific files the agent might want to run/see/... The review prompts are freely configurable, so you can determine the reviewers behavior very accurately.
9
9
 
10
- ## Install
10
+ > ⚠️ **Disclaimer**
11
+ >
12
+ > This does not make running agents foolproof and safe! An actor running intrusive commands on your machine will always create attack surface! It just reduces risk, and enables you to get more insight into what your agent is doing.
13
+
14
+ ![](./assets/recording.gif)
15
+
16
+ Requires OpenCode 1.18.35 or newer on Linux.
17
+
18
+ ## Install and config
11
19
 
12
20
  Add this entry to `~/.config/opencode/tui.json` or `.opencode/tui.json`.
13
21
  OpenCode installs the [npm package](https://www.npmjs.com/package/opencode-reviewer).
@@ -27,16 +35,28 @@ OpenCode installs the [npm package](https://www.npmjs.com/package/opencode-revie
27
35
  "stream": false,
28
36
  "reviewBash": true,
29
37
  "reviewEdits": true,
38
+ "reviewSkills": true,
30
39
  "reviewMcp": false,
31
40
  "reviewCustomTools": false,
32
41
  "reviewExternalDirectories": false,
33
42
  "autoApprove": false,
43
+ "fastMode": false,
34
44
  "extraCareful": true,
35
45
  "autoApproveDelaySeconds": 15,
36
46
  "notify": true,
37
47
  "notifySound": true,
48
+ "staleReminderSeconds": 60,
49
+ "notifications": {
50
+ "attention": { "banner": true, "sound": true },
51
+ "unsafe": { "banner": true, "sound": true },
52
+ "question": { "banner": true, "sound": true },
53
+ "approved": { "banner": true, "sound": true },
54
+ "error": { "banner": true, "sound": true },
55
+ "ended": { "banner": true, "sound": true }
56
+ },
38
57
  "notificationSoundDirectory": "/absolute/path/to/notification-sounds",
39
58
  "formatRetries": 1,
59
+ "maxOutputTokens": 4096,
40
60
  "timeoutMs": 30000,
41
61
  "maxFiles": 6,
42
62
  "maxEvidenceBytes": 131072
@@ -47,7 +67,7 @@ OpenCode installs the [npm package](https://www.npmjs.com/package/opencode-revie
47
67
  ```
48
68
 
49
69
  Only `baseURL` and `model` are required. Use any OpenAI-compatible Chat Completions
50
- endpoint; `/chat/completions` is appended to the base URL. Choose a model available
70
+ endpoint: `/chat/completions` is appended to the base URL. Choose a model available
51
71
  at that endpoint.
52
72
 
53
73
  Replace `apiKey`, or remove it and set the variable named by `apiKeyEnv` before
@@ -55,68 +75,169 @@ launching OpenCode. The inline key takes precedence. Omit both for an unauthenti
55
75
  endpoint. Remove `instructions` to use the built-in prompts and
56
76
  `notificationSoundDirectory` to use the bundled sounds.
57
77
 
58
- The remaining values above are the defaults. Restart OpenCode after changing
59
- configuration. Reviews run when OpenCode asks for permission, so set the relevant
60
- rules to `ask` in `opencode.json`. Existing `allow` rules skip review.
61
-
62
- ## Options
63
-
64
- | Option | Behavior |
65
- | --- | --- |
66
- | `stream` | Show the rating and explanation as they arrive. Requires SSE support from the endpoint. |
67
- | `reviewBash` | Review native shell commands. |
68
- | `reviewEdits` | Review native edit, write, and apply-patch requests. |
69
- | `reviewMcp` | Review identifiable MCP tool and resource permissions. |
70
- | `reviewCustomTools` | Review permissions requested by registered custom tools. |
71
- | `reviewExternalDirectories` | Review directory access independently of the other switches. Directory approval can resume the operation without another prompt. |
72
- | `autoApprove` | Allow completed Safe reviews once after the visible countdown. |
73
- | `extraCareful` | Include the extra-careful prompt in auto-mode reviews. Defaults to `true`; set `false` to omit it. |
74
- | `autoApproveDelaySeconds` | Countdown duration, 0–3600 seconds. |
75
- | `notify` | Linux desktop notifications and sounds. Defaults to `true`; set `false` to disable both. |
76
- | `notifySound` | Play notification sounds. Defaults to `true`; set `false` to keep banners silent. |
77
- | `notificationSoundDirectory` | Absolute custom sound directory. Use `attention`, `approved`, `error`, and `ended` basenames with `.wav` or `.mp3`; WAV takes precedence. Missing or unusable files fall back to bundled sounds. |
78
- | `formatRetries` | Additional attempts to correct malformed assessment JSON, 0–100. |
79
- | `timeoutMs` | Total review deadline, 1–3,600,000 ms. |
80
- | `maxFiles` | File limit per review, 1–1,000. |
81
- | `maxEvidenceBytes` | Evidence limit, 1–16,777,216 bytes. Whole files or diffs may be omitted; oversized mandatory arguments fail review. |
82
- | `instructions` | Absolute directory containing overrides for the [prompt templates](https://github.com/mightykatun/opencode-reviewer/tree/main/prompts). Missing templates use the built-ins. |
83
-
84
- ## Usage
78
+ The connection, credential and directory values above are examples, not defaults.
79
+ All boolean, numeric and per-type notification values shown are the defaults.
80
+ Restart OpenCode after changing configuration. Reviews run when OpenCode asks for
81
+ permission, so set the relevant rules to `ask` in `opencode.json`. Existing `allow`
82
+ rules skip review.
83
+
84
+ ## Options reference
85
+
86
+ These are options inside the plugin's configuration object in `tui.json`.
87
+ Defaults apply only to omitted options; `null` is invalid. Boolean and integer
88
+ options require JSON booleans and numbers, not quoted strings. Integer ranges are
89
+ inclusive, fractions are invalid, and the maximum safe integer is
90
+ 9,007,199,254,740,991. Unknown option names are rejected.
91
+
92
+ | Option | Type | Default | Accepted values and behavior |
93
+ | --- | --- | --- | --- |
94
+ | `baseURL` | string | Required | HTTP(S) API base URL; `/chat/completions` is appended after removing trailing slashes. Embedded credentials, query strings and fragments are rejected. |
95
+ | `model` | string | Required | Nonempty model identifier accepted by the configured endpoint. |
96
+ | `apiKey` | string | Unset | Nonempty Bearer token. Takes precedence over `apiKeyEnv`. |
97
+ | `apiKeyEnv` | string | Unset | Environment-variable name matching `[A-Za-z_][A-Za-z_0-9]*`. Without an inline key, its value must be set and nonblank when a review runs. Omit both key options for no Authorization header. |
98
+ | `instructions` | string | Unset (built-ins) | Absolute, NUL-free directory path containing overrides for the [prompt templates](https://github.com/mightykatun/opencode-reviewer/tree/main/prompts), not inline instructions. Missing named files use built-ins; supplied files must be readable, regular, non-symlink, nonempty UTF-8 text up to 64 KiB. Fixed contracts cannot be overridden. |
99
+ | `stream` | boolean | `false` | Request SSE and show provisional rating/text updates. Normal automatic approval waits for a completed, validated and rendered report; fast mode can approve on an early Safe rating. |
100
+ | `reviewBash` | boolean | `true` | Review native shell-command permissions. |
101
+ | `reviewEdits` | boolean | `true` | Review native `edit`, `write` and `apply_patch` permissions. |
102
+ | `reviewSkills` | boolean | `true` | Review native skill-load permissions for dangerous guidance and relevance to the current task. Supplies the host's skill instructions and bounded, directly referenced supporting files within the skill directory. |
103
+ | `reviewMcp` | boolean | `false` | Review identifiable MCP tool and resource permissions. |
104
+ | `reviewCustomTools` | boolean | `false` | Review permissions requested by registered custom tools. |
105
+ | `reviewExternalDirectories` | boolean | `false` | Review directory access independently of operation-review switches. Directory approval can resume the operation without another prompt. |
106
+ | `autoApprove` | boolean | `false` | Allow Safe reviews once automatically. Normally waits for completion, validation, rendering and the countdown, including behind this plugin's report history. |
107
+ | `fastMode` | boolean | `false` | Requires `autoApprove: true`. Approve a visible, front-of-queue request as soon as a Safe rating is parsed, bypassing the countdown. Respects `stream`; with `stream: false`, waits for the complete validated response. Finish the report and save it in the background after approval. |
108
+ | `extraCareful` | boolean | `true` | Include extra-careful guidance when `autoApprove` is enabled. Setting `false` omits that guidance without disabling automatic approval. |
109
+ | `autoApproveDelaySeconds` | integer | `15` | `0`–`3600` seconds. Positive values have an extra one-second initial hold; `0` skips the wait but retains rendering and eligibility checks. Ignored in fast mode. |
110
+ | `notify` | boolean | `true` | Master switch for desktop banners, sounds and reminders. `false` overrides all per-type controls. |
111
+ | `notifySound` | boolean | `true` | Master switch for notification audio. `false` silences every type and reminder without disabling enabled banners. |
112
+ | `staleReminderSeconds` | integer | `60` | `0` through the maximum safe integer, in seconds. `0` disables reminders; otherwise eligible pending human interactions repeat at this interval, limited to the native front-of-queue blocker per conversation. |
113
+ | `notifications` | object | All types: `banner: true`, `sound: true` | Keys: `attention`, `unsafe`, `question`, `approved`, `error`, `ended`. Each entry is an object with optional `banner` and `sound` booleans. Omitted types/fields remain enabled; unknown types/fields are invalid. Controls also apply to reminders. |
114
+ | `notificationSoundDirectory` | string | Unset (bundled sounds) | Absolute, NUL-free path, at most 4096 string code units. Use the six notification type names as basenames: usable `.wav` first, then `.mp3`, then the corresponding bundled sound. |
115
+ | `formatRetries` | integer | `1` | `0`–`100` additional assessment-format correction attempts. `0` disables format corrections, not independent transport recovery. |
116
+ | `maxOutputTokens` | integer | `4096` | `1` through the maximum safe integer. Sent as `max_tokens` on every reviewer POST, including retries/corrections. Your provider/model enforces its supported output limit. |
117
+ | `timeoutMs` | integer | `30000` | `1`–`3600000` milliseconds shared across evidence collection, HTTP requests, retries and corrections; not a fresh budget per POST. |
118
+ | `maxFiles` | integer | `6` | `1`–`1000` distinct file candidates per shell/edit/skill review, including unavailable candidates. Skill reviews count the main instructions as one file. |
119
+ | `maxEvidenceBytes` | integer | `131072` | `1`–`16777216` bytes for command/source content, edit diffs or category-specific JSON evidence. Skill reviews share this budget between mandatory skill JSON and UTF-8 supporting-file content. Optional items are omitted whole; oversized mandatory evidence fails analysis. This is not a cap on the full prompt or HTTP request. |
120
+
121
+ Notification channels are independent: `banner: false, sound: true` is sound-only;
122
+ `banner: true, sound: false` is banner-only; both `false` disable that type.
123
+ `notify` and `notifySound` take precedence. Invalid notification settings disable
124
+ notification work without invalidating otherwise valid review settings.
85
125
 
126
+ My LLMs insist on writing a bunch of useless text to my README, so I collapsed them here in case anyone wants to inflict themselves (or more likely their agent) the pain of reading it.
127
+
128
+ <details>
129
+ <summary>Usage</summary>
86
130
  The sidebar shows Safe, Unsafe, or Analysis unavailable. Explanations support
87
131
  Markdown and scrolling. If the sidebar is hidden, use OpenCode's Show sidebar
88
132
  command.
89
133
 
134
+ Skill-load review is enabled by default; set `skill` permissions to `ask` in
135
+ `opencode.json` to receive reviews. Its prompt requires both bounded risk and clear
136
+ relevance to the current work. A harmless but irrelevant skill is rated Unsafe.
137
+ The reviewer receives the exact main instructions from the host's skill catalog,
138
+ plus bounded snapshots of directly referenced local files within the skill directory.
139
+ It does not execute scripts, follow references in supporting files, expand directory
140
+ listings, fetch external URLs, or read supporting files outside that directory.
141
+ Built-in skills without a local directory receive instruction-only review.
142
+ Customize this guidance with `SKILL-REVIEW-PROMPT.md` in your prompt directory.
143
+
144
+ For every review category, subagent requests include the latest immediate delegation
145
+ linked to the requesting assistant invocation, alongside the genuine root-user prompt.
146
+ Resumed subagents use the current delegation; nested subagents include only their
147
+ immediate delegation. Missing or over-64-KiB delegation text is noted rather than
148
+ replaced with an older task. Both prompts are context for the reviewer, not instructions
149
+ that can override its review contract.
150
+
90
151
  With streaming enabled, Evaluating and its spinner disappear when a rating arrives.
91
- The rating remains provisional until the full response is validated. A format retry
92
- clears the preview and restores the loading indicator. Auto-approval
152
+ The rating remains provisional until the full response is validated. A retry
153
+ clears the preview and restores the loading indicator. Normal auto-approval
93
154
  starts only after the full response is validated and rendered.
94
155
 
95
- Transient connection failures and HTTP 408/429/500/502/503/504 responses get up to
96
- two internal retries within `timeoutMs`, honoring server cooldowns. A stream that
97
- has already delivered assessment text is not restarted. This is separate from
98
- `formatRetries`; no additional setting is needed. Retried requests can incur
99
- additional provider charges, and unreported usage remains unknown.
156
+ With `autoApprove: true` and `fastMode: true`, a parsed Safe rating approves the
157
+ current visible permission immediately, without a countdown or waiting for the
158
+ rest of a streamed report. Hidden panels and covering dialogs or history prevent
159
+ fast approval. Without streaming, fast mode waits for the full validated response
160
+ and then skips the countdown.
161
+
162
+ After confirmed fast approval, the live panel shows **Auto-approved; finishing
163
+ report…** and stays ahead of newer requests until its report finishes. Hiding the
164
+ panel, changing conversations or disabling review does not stop that already-approved
165
+ assessment. Normal retries and the original deadline still apply. The panel closes
166
+ when the assessment finishes; history saves continue in the background. Only a
167
+ completed validated report is saved, including a final Unsafe rating after a retry.
168
+ A failed remainder creates no report entry and cannot undo the earlier approval.
169
+
170
+ Transient connection failures, HTTP 408/429/500/502/503/504 responses, and rejected
171
+ assessment streams share up to two internal retries within `timeoutMs`, honoring
172
+ server cooldowns. Stream protocol failures and output-token truncation regenerate
173
+ the report from a fresh, identical request; previous previews are cleared and never
174
+ joined to the next response. Socket failures after text starts, provider error/refusal
175
+ events, tool calls, invalid JSON/UTF-8, incomplete SSE framing and resource-limit
176
+ failures remain terminal. This recovery is separate from `formatRetries`.
177
+ If token-limit errors persist, increase `maxOutputTokens` within your model's limits.
178
+ Providers enforce this limit and may include reasoning tokens; the plugin's byte
179
+ limits and overall timeout still apply. Retried requests can incur additional
180
+ provider charges, and unreported usage remains unknown.
100
181
 
101
182
  During an auto-approval countdown, click the countdown to allow once immediately,
102
183
  or Cancel to leave the request manual. Hiding or covering the panel also cancels
103
- that request's countdown. Cancel before using native Allow always or rejection
184
+ that request's countdown, except when covered by this plugin's report history.
185
+ Cancel before using native Allow always or rejection
104
186
  forms; those forms alone do not stop the countdown.
105
187
  Positive countdowns hold their configured starting number for one extra second
106
188
  before counting down. A zero-second setting still approves without that hold.
107
189
 
108
190
  `/reviewer-disable` and `/reviewer-enable` control the current conversation and its
109
191
  descendants. The setting is saved for resume. Both commands are also in the command
110
- palette. Disabling stops current reviews and countdowns while native permission
111
- controls remain available.
192
+ palette. Disabling stops unapproved reviews and countdowns while native permission
193
+ controls remain available. Already-approved fast-mode reports finish in the background.
194
+
195
+ ### Report history
196
+
197
+ Use `/reviewer-history` or **Reviewer: Report history** in the command palette to
198
+ open the newest saved report for the current conversation and its descendants.
199
+ Left/Right or the arrow buttons select older/newer entries; Up/Down and
200
+ PageUp/PageDown scroll. Close or Escape closes history. Repeating the command
201
+ returns to the newest report. History remains available with review disabled or
202
+ invalid reviewer configuration. The history command preserves a hidden sidebar;
203
+ clicking an approval notification reveals it and selects that permission's live or saved report.
204
+
205
+ Each entry shows the original report, available usage, model, provider and outcome:
206
+ **Auto approved**, **Manually approved**, **Cancelled**, or **Rejected**. Cancelled
207
+ records a linked interruption or removed request after automation was cancelled;
208
+ it does not prove that the tool never ran. Ambiguous outcomes are omitted, including
209
+ native Allow once replies whose submitting client cannot be identified. History
210
+ keeps the latest completed report, which can differ from the report that caused
211
+ approval in another window.
212
+
213
+ History can cover an ongoing review without stopping its countdown. A newly
214
+ completed Safe review can also start its countdown beneath history after its full
215
+ report is rendered. Native dialogs, including the command palette, still cancel an
216
+ active countdown; opening history afterward cannot restart it. Browsing saved
217
+ reports never approves a request or replays notifications.
218
+
219
+ Reports are saved only after a qualifying permission resolution. Saved reports
220
+ can contain private text and are retained in OpenCode's local state directory
221
+ until their conversation is deleted. Deleting a root removes its descendants'
222
+ details; deletion missed while OpenCode was closed is reconciled when that host
223
+ scope is visited again. Windows using the same state directory share this history,
224
+ partitioned by host directory and root conversation. Uncommitted reports can be
225
+ lost on shutdown or prolonged storage failure.
226
+
227
+ ### Usage totals
112
228
 
113
229
  Token and cost totals appear below completed reports when available. Open
114
- Reviewer: Lifetime usage in the command palette for cumulative totals. OpenRouter
230
+ **Reviewer: Statistics** in the command palette for **Lifetime** and **Conversation**
231
+ views. Use Tab or click a scope to switch. The dialog opens on Lifetime;
232
+ Conversation uses the session selected when opened, resolving its root and all
233
+ descendants, including subagents, within the current host-directory scope.
234
+ Opening from a descendant shows the same combined conversation totals.
235
+ OpenRouter
115
236
  costs use reported charges; other endpoints use available catalog estimates.
116
237
  Received usage counts even if a review fails or is interrupted. Unreported charges
117
238
  are missing from the totals.
118
239
 
119
- The lifetime dialog shows completed reviews, retries, tokens, cost, Safe/Unsafe
240
+ Both views show completed reviews, retries, tokens, cost, Safe/Unsafe
120
241
  percentages, confirmed auto-approvals, and average time to a rating and full report.
121
242
  Percentages use recorded completed reviews as their denominator. Each validated
122
243
  review counts once, even without usage data; a fresh review after re-enabling counts
@@ -125,49 +246,115 @@ corrections and transport recovery. Manual approvals are not auto-approvals.
125
246
  Timings run from evaluation start, including evidence and retries, to the accepted
126
247
  attempt's first rating and final validated response, excluding rendering/countdown
127
248
  time. Non-streaming reviews use the final response time for both measurements.
128
- Only running averages and sample counts are stored, not individual timings.
129
- Totals persist across restarts. New metrics show partial history when older records
130
- lack them; averages include measured reviews only. Earlier unrecorded values cannot
131
- be reconstructed.
249
+ Averages use running means and sample counts; stored review details also retain
250
+ their individual timings. Both scopes persist in the existing history database
251
+ across restart/resume. Conversation totals keep independent roots separate;
252
+ Lifetime combines activity across conversations and host directories using the
253
+ same state directory. Browsing and switching views do not add usage.
254
+
255
+ Older retained history can provide a **partial** conversation baseline. Previously
256
+ deleted details and usage predating history tracking cannot be attributed or
257
+ reconstructed from legacy lifetime aggregates. Once recorded in conversation totals,
258
+ deleting report details does not reduce those totals or Lifetime. Storage failures
259
+ show unavailable, not zero.
260
+
261
+ Tokens describe received provider usage, not just the visible explanation. One
262
+ report may sum several reviewer POSTs, including format corrections and transport
263
+ retries with received usage. Each POST can resend the prompt and evidence;
264
+ corrections also include the failed response and feedback. Cached input remains
265
+ part of the provider's input-token count. Lifetime and Conversation also retain
266
+ received usage from unsuccessful or interrupted reviews, which do not count as
267
+ completed reviews. Averages include measured reviews only.
132
268
 
133
269
  Reviews send the pending request, latest user prompt, project context, and relevant
134
270
  file snapshots, diffs, or tool arguments to your endpoint before approval. Shell
135
271
  file snapshots can follow symlinks outside the project. Missing evidence is noted
136
272
  in the report; ratings are advice based on the supplied evidence.
273
+ </details>
137
274
 
138
- ### Desktop notifications
139
-
275
+ <details>
276
+ <summary>Desktop notifications</summary>
140
277
  Transient banners have an **Opencode (Session name)** heading and a small status icon: green
141
- checkmark for approvals, orange exclamation mark for attention, red X
278
+ checkmark for approvals, orange exclamation mark for attention, red exclamation mark
279
+ for Unsafe reviews, purple question mark for questions, red X
142
280
  for errors, and a neutral code mark for completed responses. GNOME controls the
143
281
  heading's font weight. The event message appears beneath it, even while the
144
282
  terminal is focused:
145
283
 
146
- - **Session needs attention:** questions and manual permissions. Reviewed requests
147
- wait for a final validated assessment; failures, canceled automation, and a Safe
148
- review blocked from starting its countdown also notify. Unreviewed requests
149
- notify immediately, including when conversation review is disabled.
284
+ - **Session needs attention:** manual permissions. Reviewed requests
285
+ stay silent during identification, report generation and retries. Final Safe
286
+ reports notify when auto-approval is disabled or canceled, or after failed approval
287
+ is confirmed to remain pending. Waiting for rendering, queue position or an
288
+ automatic countdown to start does not trigger attention. Disabled/unsupported
289
+ reviews and terminal analysis failures can notify without a report.
290
+ - **Unsafe permission needs human approval:** a completed, validated Unsafe review,
291
+ with its own sound. Provisional streamed ratings never notify.
292
+ - **Agent has a question:** a pending agent question at the front of the native
293
+ input queue, with its own sound.
150
294
  - **Reviewer approved a permission:** sent with the approval sound after confirmed
151
- automatic approval, for both positive and zero delays. The countdown is silent.
295
+ automatic approval, including fast mode while its report is still streaming.
296
+ Positive and zero delays also notify on success; the countdown is silent.
152
297
  Approval sounds are limited to
153
298
  one every two seconds; every eligible banner is retained for delivery.
154
299
  - **Session error:** an unrecovered session/provider failure, not a review failure.
155
300
  - **Session ended:** a completed root-agent response, not a question/permission
156
301
  pause or an explicit user interruption.
157
302
 
303
+ Permission attention, Unsafe and question notifications, including their reminders,
304
+ apply only to the current native input blocker in each visited root conversation.
305
+ Requests queued behind it stay silent and are re-evaluated from their current
306
+ state when they become actionable. Questions wait behind pending permissions and
307
+ earlier questions. A request resolved while queued never sends a delayed alert.
308
+ A sequence of permissions that proceeds
309
+ through automatic approval stays free of attention notifications. Losing the
310
+ actionable position, restarting analysis or entering automatic approval cancels
311
+ pending attention delivery and reminders.
312
+
313
+ Each type's `banner` and `sound` can be controlled independently. For example,
314
+ `notifications.approved.banner: false` keeps approval sounds without banners;
315
+ `notifications.question.sound: false` keeps question banners silent. Set both to
316
+ `false` to disable that type. `notify: false` overrides all types and disables
317
+ reminders too; `notifySound: false` silences all types and their reminders.
318
+ Sound-only delivery does not require a desktop banner or delivery acknowledgement.
319
+ Invalid notification settings disable notification work without disabling reviews.
320
+
321
+ Pending permissions and questions repeat after `staleReminderSeconds`, measured
322
+ from initial dispatch, even if desktop delivery fails. Reminders replace their
323
+ previous banner and append **(Reminder)** to the event text, reusing the same
324
+ sound, icon, session heading and click target. Only the native front-of-queue
325
+ interaction per root conversation repeats: permissions precede questions, with
326
+ the host's session/request ordering. Queued interactions stay silent. Handoff sends
327
+ the next eligible request's initial notification, then waits a full interval before
328
+ its first reminder. Independent visited conversations
329
+ can each remind. The supported host presents input for roots and direct children;
330
+ deeper descendants cannot become a native root-input notification or reminder target.
331
+
332
+ Answering/dismissing a question in OpenCode, resolving a permission, deleting its
333
+ session or closing the plugin stops its reminders. Closing/clicking a desktop
334
+ banner, activating the terminal or viewing a conversation does not. Countdown
335
+ and approval submission remain silent. Approval, error and completed-response
336
+ notifications never repeat. Setting `staleReminderSeconds: 0` disables all reminders.
337
+
158
338
  Notifications cover conversations visited in this terminal and their descendants.
159
- Existing pending requests at startup/resume are not replayed. Clicking can select
339
+ Existing pending requests at startup/resume are not replayed or reminded, but still
340
+ participate in queue ordering. Clicking can select
160
341
  the originating GNOME Terminal tab and root conversation. Native input prompts
161
342
  cover root/direct-child requests in the supported host. Open dialogs are
162
343
  left intact; other terminals still receive banners and sounds. Desktop policies
163
344
  control expiry/history and whether activation actually brings a window forward.
345
+ Clicking **Reviewer approved a permission** opens that permission's unfinished
346
+ fast-mode report, or its matching saved report if it has completed, revealing the
347
+ sidebar if hidden. If a dialog is open, navigation waits until it closes and then
348
+ checks whether the report is still live. A clicked live panel closes on completion
349
+ like any fast-mode report. A pending save gets up to five seconds; if the report is
350
+ unavailable or deleted, the click just opens its conversation rather than a different report.
164
351
  GNOME Terminal clicks use the desktop activation token to bring the correct tab
165
352
  forward across workspaces. GNOME may attribute the notification source to Terminal;
166
353
  the banner heading remains Opencode.
167
354
 
168
355
  Linux delivery uses `notify-send`, `stdbuf`, and `gdbus`; audio uses `paplay` or `pw-play`.
169
356
  On Ubuntu, `libnotify-bin`, `coreutils`, `libglib2.0-bin`, and `pulseaudio-utils` provide these
170
- utilities. The MP3 decoder and four default sounds are bundled; FFmpeg is not
357
+ utilities. The MP3 decoder and six default sounds are bundled; FFmpeg is not
171
358
  required. MP3 and mono/stereo PCM/float WAV files up to 4 MiB and 10 seconds are
172
359
  normalized toward -20 dBFS RMS with a -3 dBFS peak ceiling before playback.
173
360
  Custom sounds are loaded on first use; restart after replacing them.
@@ -176,6 +363,7 @@ with a low-latency buffer; preparation never blocks permission approval.
176
363
  `stdbuf` makes delivery acknowledgements immediate instead of waiting for
177
364
  `notify-send` to flush its output when the banner closes.
178
365
  Disable overlapping notification plugins to avoid duplicate alerts.
366
+ </details>
179
367
 
180
368
  ## Development
181
369
 
@@ -29,7 +29,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
29
 
30
30
  # Bundled audio decoder and supplied sounds
31
31
 
32
- The four notification MP3 files were supplied by the maintainer with explicit
32
+ The six notification MP3 files were supplied by the maintainer with
33
33
  permission to redistribute them with this plugin. Their original bytes are
34
34
  embedded in the bundle. Normalization occurs during playback preparation.
35
35