Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .agents/notes/implemented/bug-fix/2026-09-08-grok-plan-approval.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion apps/cli/src/agent/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
10 changes: 10 additions & 0 deletions apps/cli/src/agent/lody-acp-extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions apps/cli/src/agent/lody-acp-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions specs/independent-plan-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading