diff --git a/.agents/notes/implemented/bug-fix/2026-09-08-grok-plan-approval.md b/.agents/notes/implemented/bug-fix/2026-09-08-grok-plan-approval.md new file mode 100644 index 000000000..8071a7ec4 --- /dev/null +++ b/.agents/notes/implemented/bug-fix/2026-09-08-grok-plan-approval.md @@ -0,0 +1,41 @@ +# Grok plan approval transport + +Status: implemented +Translation: pending + +## Abstract + +The Core boolean Plan toggle did not translate Grok's native plan approval request. +Lody ignored the private method, returned an empty response, and Grok treated it as +cancellation. The adapter now presents the plan and decision using standard ACP, +while Lody excludes mode-switch decisions from its Grok Always Approve policy. +Provider-specific request/response translation stays inside the wrapper. + +## Contract and decisions + +Grok sends `x.ai/exit_plan_mode` with `sessionId`, `toolCallId`, and nullable +`planContent`. Gateway envelopes also wrap the params and result. The bridge retains +the request id independently of client-originated requests, publishes markdown +`plan_update` when supported, and otherwise places text on a `switch_mode` tool. +Approval uses `session/request_permission`. Revision feedback uses an optional +standard form elicitation with Core metadata; clients without forms keep planning +without feedback. Abandon is explicit. Errors and cancellation never approve. +Native mode notifications update the Core boolean option without changing permission. + +The host must not auto-answer `switch_mode` decisions, including already waiting +requests when Always Approve is enabled. The adapter and host changes must roll out +together. This uses existing Core/ACP contracts; no new Core RPC is introduced. + +## Evidence and validation + +Upstream inspected at xai-org/grok-build commit +`72a61251fcffb464bcc687aeb5a998e5a98ec0c9`: exit_plan_mode/types.rs, +acp_session_impl/tool_calls.rs, and leader/server_tests.rs in crates/codegen. +Deterministic proxy tests cover decisions, envelopes, feedback, fallback clients, +empty plans, cancellation, request-id directionality, and mode synchronization. +Host tests cover new and waiting plan decisions under Always Approve. +No live authenticated Grok turn or runtime publication is included. + +Companion adapter change: https://github.com/LodyAI/acp-extension-grok/pull/14. +Validation completed: all 51 Grok tests, 13 host permission tests, ACP SDK schema +validation, full root `pnpm check`, and `pnpm format`. Documentation checks pass. diff --git a/apps/cli/src/agent/AGENTS.md b/apps/cli/src/agent/AGENTS.md index 6cc6c8806..124a9c8e1 100644 --- a/apps/cli/src/agent/AGENTS.md +++ b/apps/cli/src/agent/AGENTS.md @@ -11,7 +11,7 @@ context/acp-agent-edit-evidence.md; adapter repos: [apps/cli/AGENTS.md](../../AG - Consume Core extensions via `agentCapabilities._meta.lody`, session `_meta.lody`, and `_lody/...`. Provider/pre-Core readers stay in `lody-acp-extension.ts`; consumers stay neutral. - Grok Always Approve uses `allow_once`, never lasting grants; pending calls drain through - the durable permission flow on accepted config changes. Questions remain interactive. + the durable permission flow on accepted config changes. Questions and mode switches remain interactive. - Builtin Grok must default `clientCapabilities.terminal` to false. - Send the driving turn's config on every session establishment as `_meta.lody.sessionConfig`; provider-specific startup translation belongs in the ACP adapter. `session/set_config_option` diff --git a/apps/cli/src/agent/lody-acp-extension.test.ts b/apps/cli/src/agent/lody-acp-extension.test.ts index 06eb603f7..91d8d7e3f 100644 --- a/apps/cli/src/agent/lody-acp-extension.test.ts +++ b/apps/cli/src/agent/lody-acp-extension.test.ts @@ -70,6 +70,16 @@ describe('Grok TUI permission compatibility', () => { ).toEqual({ outcome: 'cancelled' }); }); + it.each([false, true])('keeps plan decisions interactive (pending=%s)', (pending) => { + expect( + getBuiltinToolPermissionOutcome({ + ...args, + pending, + request: { ...request, toolCall: { ...request.toolCall, kind: 'switch_mode' } }, + }) + ).toBeUndefined(); + }); + it('keeps question requests interactive even when draining', () => { const question: RequestPermissionRequest = { ...request, diff --git a/apps/cli/src/agent/lody-acp-extension.ts b/apps/cli/src/agent/lody-acp-extension.ts index 5ca5e789d..bcef77b37 100644 --- a/apps/cli/src/agent/lody-acp-extension.ts +++ b/apps/cli/src/agent/lody-acp-extension.ts @@ -27,6 +27,7 @@ export function getBuiltinToolPermissionOutcome(args: { if ( args.agentConfig?.cliType !== 'builtin' || args.agentConfig.agentType !== 'grok' || + args.request.toolCall.kind === 'switch_mode' || isAskUserQuestionPermissionRequest(args.request) ) { return undefined; diff --git a/packages/acp-extension-grok b/packages/acp-extension-grok index cd3a62777..f3f59e1a4 160000 --- a/packages/acp-extension-grok +++ b/packages/acp-extension-grok @@ -1 +1 @@ -Subproject commit cd3a62777ad6ae22c33512ef5f8d2ca79894d94b +Subproject commit f3f59e1a4528e5e201b24eccb4b3548987bbcc67 diff --git a/specs/independent-plan-mode.md b/specs/independent-plan-mode.md index f07771a61..21b97c8ed 100644 --- a/specs/independent-plan-mode.md +++ b/specs/independent-plan-mode.md @@ -21,6 +21,15 @@ current target advertises the new option; an explicit boolean takes precedence. Frozen historical Turns are not rewritten. Executing a plan sets the next Turn's Plan value to false while preserving its permission selection. +## Grok plan approval + +A completed Grok plan is shown for an explicit implement, keep planning, or abandon +decision, including under Always Approve. Keep planning can carry revision feedback +when the client supports form elicitation. Empty plans remain reviewable; errors, +dismissal, and cancellation never imply approval. Native enter/exit events refresh +the independent Plan toggle. These semantics use standard ACP plan, permission, +and elicitation messages rather than a new provider-specific client interface. + ## Evidence and validation - [Core contract](../packages/acp-extension-core/src/plan-mode.ts)