feat(opencode): add plan mode, reasoning effort, and status telemetry#688
feat(opencode): add plan mode, reasoning effort, and status telemetry#688swear01 wants to merge 3 commits into
Conversation
Bridge OpenCode ACP usage updates into the existing token-count pipeline so the web status bar can show live context and cache information without a separate UI path. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Findings
-
[Major] OpenCode plan mode is exposed to local sessions without local enforcement — adding
planto the shared OpenCode modes makes CLI/API paths accept it for local OpenCode sessions, but the enforcement added in this PR lives in the remote launcher/ACP permission handler. The local launcher only mirrorspermission.*hook events, so local plan mode can still execute tools, defeating the mode's safety expectation. Evidenceshared/src/modes.ts:29.
Suggested fix:if (flavor === 'opencode' && mode === 'plan' && sessionResult.session.agentState?.controlledByUser === true) { return c.json({ error: 'OpenCode plan mode is only supported for remote sessions' }, 409) }
-
[Major] Failed OpenCode effort switches remain persisted — the config RPC stores the requested effort and reports it as applied before the remote launcher successfully calls
session/set_config_option; if OpenCode rejects the value or method, the catch path only mutates the current batch and sends a message, leaving hub/session state on the rejected value and causing later turns to retry it. Evidencecli/src/opencode/runOpencode.ts:147.
Suggested fix:const fallbackEffort = this.currentBackendEffort; batch.mode.modelReasoningEffort = fallbackEffort; session.setModelReasoningEffort(fallbackEffort); session.pushKeepAlive(); this.onReasoningEffortRollback?.(fallbackEffort);
Questions
- None.
Summary
- Review mode: initial
- Two issues found: remote-only plan enforcement is exposed to local OpenCode sessions, and failed OpenCode reasoning-effort switches can leave persisted state out of sync with the backend.
Testing
- Not run (automation; static review only).
HAPI Bot
| export type KimiPermissionMode = typeof KIMI_PERMISSION_MODES[number] | ||
|
|
||
| export const OPENCODE_PERMISSION_MODES = ['default', 'yolo'] as const | ||
| export const OPENCODE_PERMISSION_MODES = ['default', 'plan', 'yolo'] as const |
There was a problem hiding this comment.
[MAJOR] This exposes plan as an OpenCode mode for every path, including the default local hapi opencode --permission-mode plan flow and web permission-mode updates on controlled-by-user sessions. The PR only enforces plan in the remote launcher/ACP permission handler; opencodeLocalLauncher just mirrors permission.* hook events, so local plan mode can still run tools.
Suggested fix:
if (flavor === 'opencode' && mode === 'plan' && sessionResult.session.agentState?.controlledByUser === true) {
return c.json({ error: 'OpenCode plan mode is only supported for remote sessions' }, 409)
}Also reject terminal local startup unless --hapi-starting-mode remote:
if (options.permissionMode === 'plan' && options.startingMode !== 'remote') {
throw new Error('OpenCode plan mode is only supported in remote mode')
}| if (config.model !== undefined) { | ||
| sessionModel = config.model; | ||
| } | ||
| if (config.modelReasoningEffort !== undefined) { |
There was a problem hiding this comment.
[MAJOR] This records the requested effort as applied before the remote launcher has successfully called session/set_config_option. If OpenCode rejects the value or the method, the catch path only mutates the current batch and sends a message; the hub/session state still says the rejected effort is active, and future messages are queued with the same stale sessionModelReasoningEffort.
Suggested fix:
const fallbackEffort = this.currentBackendEffort;
batch.mode.modelReasoningEffort = fallbackEffort;
session.setModelReasoningEffort(fallbackEffort);
session.pushKeepAlive();
this.onReasoningEffortRollback?.(fallbackEffort);Wire onReasoningEffortRollback to update sessionModelReasoningEffort before the next queued turn.
Summary
ctxandcacheTest plan
bun run test -- src/agent/messageConverter.test.ts src/agent/backends/acp/AcpSdkBackend.test.tsinclibun run typecheckinclibun run test -- src/chat/normalize.test.tsinwebbun run typecheckinwebIssues
Notes
ctx/cache) so ACP usage data is visible in the existing composer/status bar without introducing an agent-specific UI path.Made with Cursor