3
43
Fork
You've already forked opencode.el
16

PR to add transient based permission prompts #30

Manually merged
sczi merged 7 commits from nineluj/opencode.el:wip/transient-permission into main 2026年04月23日 01:32:06 +02:00
Contributor
Copy link

Overview

Hi, first of all I want to thank you for developing this package - I've really enjoyed using it over the terminal based opencode UI. Over time I have added a few changes of my own to my locally forked version, so I figured that I would try to upstream some of them to make my life easier and hopefully bring some improvements to other users.

Changes

This PR adds the following changes:

  1. A transient based permission prompt that can be used by setting (setopt opencode-transient-permission t)
  2. Indicators showing permission requests in the session buffer, alongside the approval or denial decision for each

Here are some additional changes that I could open PRs for, please let me know if any of these are of interest. Thanks!

1. opencode-todo.el — Todo sidebar

  • Self-contained new file
  • Requires only the todo.updated SSE event hook wired in opencode.el
  • Right-side sidebar, live SSE-driven updates, grouped by status with priority indicators

2. Hooks for external integration

  • Small additions to opencode.el
  • opencode-permission-asked-functions, opencode-question-asked-functions, opencode-session-idle-functions, opencode-session-retry-functions
  • High value for users who want to integrate with notification systems, org-mode, etc.

3. Session header line

  • opencode--header-line, opencode--header-line-face, opencode--session-waiting-p, related faces
  • Color-coded by state: blue=busy, yellow=waiting, red=idle
  • Shows title, directory, token count, cost
  • Touches opencode-session--set-status and opencode-open-session to wire up header-line-format
  • Medium complexity but visible quality-of-life improvement

4. Tool elapsed timers

  • opencode--start-tool-timer, opencode--stop-tool-timer
  • 1-second overlay timer on tool headers, shown after 5s, uses server timestamps for final display

5. Agent session files

  • A buffer local list of files opencode-session-files that the agent has modified
  • External code can read or hook into this variable to integrate with other Emacs libraries (e.g. consult, project.el, embark)
## Overview Hi, first of all I want to thank you for developing this package - I've really enjoyed using it over the terminal based opencode UI. Over time I have added a few changes of my own to my locally forked version, so I figured that I would try to upstream some of them to make my life easier and hopefully bring some improvements to other users. ## Changes This PR adds the following changes: 1. A transient based permission prompt that can be used by setting `(setopt opencode-transient-permission t)` 2. Indicators showing permission requests in the session buffer, alongside the approval or denial decision for each --- Here are some additional changes that I could open PRs for, please let me know if any of these are of interest. Thanks! **1. `opencode-todo.el` — Todo sidebar** - Self-contained new file - Requires only the `todo.updated` SSE event hook wired in `opencode.el` - Right-side sidebar, live SSE-driven updates, grouped by status with priority indicators **2. Hooks for external integration** - Small additions to `opencode.el` - `opencode-permission-asked-functions`, `opencode-question-asked-functions`, `opencode-session-idle-functions`, `opencode-session-retry-functions` - High value for users who want to integrate with notification systems, org-mode, etc. **3. Session header line** - `opencode--header-line`, `opencode--header-line-face`, `opencode--session-waiting-p`, related faces - Color-coded by state: blue=busy, yellow=waiting, red=idle - Shows title, directory, token count, cost - Touches `opencode-session--set-status` and `opencode-open-session` to wire up `header-line-format` - Medium complexity but visible quality-of-life improvement **4. Tool elapsed timers** - `opencode--start-tool-timer`, `opencode--stop-tool-timer` - 1-second overlay timer on tool headers, shown after 5s, uses server timestamps for final display **5. Agent session files** - A buffer local list of files `opencode-session-files` that the agent has modified - External code can read or hook into this variable to integrate with other Emacs libraries (e.g. consult, project.el, embark)
Adds opencode-permission.el with a transient-based popup for responding
to permission requests (accept once / accept always / deny). Activated
by setting opencode-transient-permission to non-nil; the existing
x-popup-dialog path remains the default so existing users are unaffected.
Output each permission request to the session buffer as it arrives,
with a marker saved to the output line. After the user responds,
append [accepted], [accepted always], or [denied] inline on that line.
Introduces opencode-session-pending-permission (buffer-local queue),
opencode--send-permission-response (marker append + API call), and
opencode-respond-permission (interactive entry point, used by both
the transient and x-popup-dialog paths). The window-selection hook
now also auto-prompts pending permissions when a buffer is revisited.
Author
Contributor
Copy link
Author
Contributor
Copy link

I'd also like to add in some logic to hide and also re-open the permission prompt so that users can go back and review the context before deciding on whether to approve or deny the request. I'll throw in a new commit for that.

I'd also like to add in some logic to hide and also re-open the permission prompt so that users can go back and review the context before deciding on whether to approve or deny the request. I'll throw in a new commit for that.
Dismissing the transient (q or C-g) without responding leaves the
permission request pending. The user can re-open the prompt at any
time with `opencode-respond-permission' (C-c C-p) after reviewing
the session context.
Adds opencode-permission--handled flag to prevent double-handling, and
an opencode-permission--on-exit transient-exit-hook that removes itself
on dismissal. Binds `opencode-respond-permission' to C-c C-p.
nineluj changed title from (削除) WIP: PR to add transient based permission prompts (削除ここまで) to PR to add transient based permission prompts 2026年04月20日 18:50:52 +02:00
Owner
Copy link

Thanks! Those all sound like welcome improvements, I'm happy to accept PRs for the others. Some comments on the others first:

  • opencode-todo.el — Todo sidebar
    In a sidebar there is probably room to display more than the todolist, maybe we could include the other information shown in the TUI sidebar (context, $ spent, LSP info, modified files)
  • Session header line
    I think the modeline should be the main place such information is displayed, there we already show the title, agent, model & variant, context % used, and idle/busy status. But I've been meaning to improve it because if the window width isn't wide enough a bunch of that information isn't shown. Maybe if the window width isn't wide enough to show it all in the modeline, then a session header line can be shown with the rest?
    IMO one great thing about emacs compared to VSCode/typical IDEs is that it avoids filling the screen with a bunch of information you're not always interested in, and the full UI space is given to stuff you're actually working with. So if all the most important information that we're always interested in seeing can fit in the modeline, it should just be there, and we only display a header if it can't fit in the modeline? And then extra information that may be useful sometimes but I don't think we always need to see (directory, cost, token count (when we already see the context % used)), could go in the sidebar that the user can toggle display of?
  • Tool elapsed timers
    I'm not sure what this is but I assume with a screenshot it'd make sense.

For this PR:

  • IMO the transient UI is nicer and more emacsy than the x-popup-dialog. I would just simplify the code and users' configuration choices by removing the opencode-transient-permission config option and the old x-popup-dialog and just have this be the new permissions UI
  • The only advantage x-popup-dialog has over transient, is that if a user is outside emacs, they will be able to see and respond to it immediately. So I think if an emacs frame is not active (frame-focus-state (selected-frame)), then we should use opencode--toast-show to show a notification that a permissions request is awaiting response, maybe with just the title of the session, or with the description of the permission request.
  • fix flycheck errors:
  1. the function ‘opencode-permission--prompt’ is not known to be defined.
    need to require opencode-permission in `opencode.el
  2. Open parenthesis in column 0 should be escaped
    checkdoc wants the ( escaped in docstring of opencode-transient-permission
  • code organization: move the opencode--permission-request, opencode--send-permission-response, and opencode-respond-permission functions into the new opencode-permission.el
  • change author and copyright information in opencode-permission.el to you
  • This is may be beyond the scope of the PR but if you want it'd be a welcome improvement to display better permission messages, copying what the TUI is doing in packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx where for external_directory type requests they turn it into a message "Access external directory..." with information from parentDir or filepath from the metadata of the permission event, and similar nicer messages and info for the other types.

Personally I just use opencode with all permissions except doom_loop allowed, in a sandbox as described in the security section of the readme, so improving the permission handling isn't a priority for me to work on, but I'm happy to merge improvements to it and I'm sure other users will benefit so please submit improvements and don't just keep them in a local fork!

Thanks! Those all sound like welcome improvements, I'm happy to accept PRs for the others. Some comments on the others first: * opencode-todo.el — Todo sidebar In a sidebar there is probably room to display more than the todolist, maybe we could include the other information shown in the TUI sidebar (context, $ spent, LSP info, modified files) * Session header line I think the modeline should be the main place such information is displayed, there we already show the title, agent, model & variant, context % used, and idle/busy status. But I've been meaning to improve it because if the window width isn't wide enough a bunch of that information isn't shown. Maybe if the window width isn't wide enough to show it all in the modeline, then a session header line can be shown with the rest? IMO one great thing about emacs compared to VSCode/typical IDEs is that it avoids filling the screen with a bunch of information you're not always interested in, and the full UI space is given to stuff you're actually working with. So if all the most important information that we're always interested in seeing can fit in the modeline, it should just be there, and we only display a header if it can't fit in the modeline? And then extra information that may be useful sometimes but I don't think we always need to see (directory, cost, token count (when we already see the context % used)), could go in the sidebar that the user can toggle display of? * Tool elapsed timers I'm not sure what this is but I assume with a screenshot it'd make sense. For this PR: * IMO the transient UI is nicer and more emacsy than the `x-popup-dialog`. I would just simplify the code and users' configuration choices by removing the `opencode-transient-permission` config option and the old `x-popup-dialog` and just have this be the new permissions UI * The only advantage `x-popup-dialog` has over transient, is that if a user is outside emacs, they will be able to see and respond to it immediately. So I think if an emacs frame is not active `(frame-focus-state (selected-frame))`, then we should use `opencode--toast-show` to show a notification that a permissions request is awaiting response, maybe with just the title of the session, or with the description of the permission request. * fix flycheck errors: 1. `the function ‘opencode-permission--prompt’ is not known to be defined. ` need to require `opencode-permission` in `opencode.el 2. `Open parenthesis in column 0 should be escaped ` checkdoc wants the ( escaped in docstring of opencode-transient-permission * code organization: move the `opencode--permission-request`, `opencode--send-permission-response`, and `opencode-respond-permission` functions into the new `opencode-permission.el` * change author and copyright information in `opencode-permission.el` to you * This is may be beyond the scope of the PR but if you want it'd be a welcome improvement to display better permission messages, copying what the TUI is doing in `packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx` where for `external_directory` type requests they turn it into a message "Access external directory..." with information from parentDir or filepath from the metadata of the permission event, and similar nicer messages and info for the other types. Personally I just use opencode with all permissions except `doom_loop` allowed, in a sandbox as described in the security section of the readme, so improving the permission handling isn't a priority for me to work on, but I'm happy to merge improvements to it and I'm sure other users will benefit so please submit improvements and don't just keep them in a local fork!
Moved permission request handling logic from opencode.el to
opencode-permission.el to improve separation of concerns. This includes
opencode--permission-request, opencode--send-permission-response, and
opencode-respond-permission functions.
Moved toast notification functions (opencode--toast-show,
opencode--default-toast-show) and buffer activity check
(opencode--buffer-active-p) to opencode-common.el for better code
organization and reusability.
Removed opencode-transient-permission custom variable and its fallback
logic, making transient UI the standard permission response handler.
Updated copyright and author information in opencode-permission.el.
Added necessary module requires and fixed transient setup calls.
Derive human-readable titles from permission type and metadata fields
(filepath, pattern, url, query, description, etc.) mirroring the TUI's
permission.tsx logic. Also wire through always-patterns so the transient
correctly shows what would be permanently approved.
Add after-focus-change-function hook that re-triggers pending permission
and question prompts when Emacs gains focus and the session buffer is
already selected. Previously, arriving back at Emacs required switching
windows internally to fire window-selection-change-functions.
Replace the formatted string concatenation with a simpler fallback chain
that returns the title when available, then the type, and "permission"
as the final default. This reduces redundancy and improves readability
by avoiding unnecessary string formatting when the title alone is
sufficient for display purposes.
Author
Contributor
Copy link

Thanks for the feedback and for sharing your thoughts! For the permission side of things - I have updated the PR to incorporate the requested changes as well as the better permission messages based on what the TUI was doing.

I'll think over the other points, in the meantime I've attached a screenshot for how I've got things looking:
image

I'm not sure if I love the emojification of everything, but I do like having most of my tool outputs be shown (using opencode-show-tool-output) while hiding some more verbose tool calls (like read, skill, todowrite). I'm currently using this workaround when using the main version:

 (define-advice opencode--maybe-insert-tool-output
 (:around (orig-fn message) skip-noisy-tools)
 "Skip output display for certain tools."
 (let-alist message
 (unless (member .tool '("read" "todowrite"))
 (funcall orig-fn message))))
Thanks for the feedback and for sharing your thoughts! For the permission side of things - I have updated the PR to incorporate the requested changes as well as the better permission messages based on what the TUI was doing. I'll think over the other points, in the meantime I've attached a screenshot for how I've got things looking: ![image](/attachments/294073bb-3514-4d32-affd-f4f86643cc51) I'm not sure if I love the emojification of everything, but I do like having most of my tool outputs be shown (using `opencode-show-tool-output`) while hiding some more verbose tool calls (like `read`, `skill`, `todowrite`). I'm currently using this workaround when using the main version: ``` (define-advice opencode--maybe-insert-tool-output (:around (orig-fn message) skip-noisy-tools) "Skip output display for certain tools." (let-alist message (unless (member .tool '("read" "todowrite")) (funcall orig-fn message)))) ```
137 KiB
sczi manually merged commit 808c168e33 into main 2026年04月23日 01:32:06 +02:00
Owner
Copy link

The screenshot looks nice! I'm cool with the emojification, it helps visually distinguish skill loading vs running a subagent vs tool calls etc, which all look similar at the moment.

On tool outputs, I want to copy the collapsible tool outputs of agent-shell, but also have what is collapsed configurable. so you could do:
(setopt opencode-collapsed-tools '(read todowrite))
and those would be collapsed by default, or since I almost never read tool outputs I could set it to only show bash tool outputs if exited with a non-zero status:
(setopt opencode-collapsed-tools '(everything-but bash-non-zero))

With the header and modeline that could also be configurable, so ie to get it as your screenshot you could set:

(setopt opencode-modeline-format '("%I %i %a / %m" mode-line-format-right-align "%c")
 opencode-header-format '("%t — %d" mode-line-format-right-align "%t tokens"))

where:

format specifier meaning
%I busy/idle emoji
%i busy/idle
%a agent (build/plan)
%m model
%c Context % used
%t session title
%d session directory
%t tokens used
The screenshot looks nice! I'm cool with the emojification, it helps visually distinguish skill loading vs running a subagent vs tool calls etc, which all look similar at the moment. On tool outputs, I want to copy the collapsible tool outputs of agent-shell, but also have what is collapsed configurable. so you could do: `(setopt opencode-collapsed-tools '(read todowrite))` and those would be collapsed by default, or since I almost never read tool outputs I could set it to only show bash tool outputs if exited with a non-zero status: `(setopt opencode-collapsed-tools '(everything-but bash-non-zero))` With the header and modeline that could also be configurable, so ie to get it as your screenshot you could set: ```elisp (setopt opencode-modeline-format '("%I %i %a / %m" mode-line-format-right-align "%c") opencode-header-format '("%t — %d" mode-line-format-right-align "%t tokens")) ``` where: | format specifier | meaning | |---------|---------| | %I | busy/idle emoji | | %i | busy/idle | | %a | agent (build/plan) | | %m | model | | %c | Context % used | | %t | session title | | %d | session directory | | %t | tokens used |
nineluj deleted branch wip/transient-permission 2026年04月23日 17:56:49 +02:00
Author
Contributor
Copy link

That sounds good! I'll start working on the tool call improvements and aim to create a new PR tomorrow.

That sounds good! I'll start working on the tool call improvements and aim to create a new PR tomorrow.
Sign in to join this conversation.
No reviewers
Labels
Clear labels
No items
No labels
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sczi/opencode.el!30
Reference in a new issue
sczi/opencode.el
No description provided.
Delete branch "nineluj/opencode.el:wip/transient-permission"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?