What happened
When an external executor turn settles but the Runtime rejects its result before publishing it, the Plugin is never told to acknowledge or abandon that turn. The ACP Session then refuses every later message with acp_busy, while the executor picker keeps showing it as ready.
Chain on 2f3220552:
AcpExecutor.execute sets awaitingAck and leaves its continuity record at prompt_pending once the prompt settles (packages/acp-executor-plugin/src/index.ts).
PluginExecutorService.executeEntry runs the result through normalizeResult, which throws for a completion over MAX_EXECUTOR_COMPLETION_TEXT_BYTES (256 KiB) (packages/runtime/src/plugin-executor-service.ts).
PluginExecutorBackend.#produce publishes a failure and returns false. The finally block in send only calls abandonExecution when returnedResult is true, so neither acknowledgeExecution nor abandonExecution runs (packages/runtime/src/plugin-executor-backend.ts:119-126).
awaitingAck is never cleared. execute rejects every later prompt as busy, but inspectConversation does not check awaitingAck and still reports ready. After a Host restart, the stored prompt_pending record surfaces as history_gap.
Expected: the turn is abandoned and the Session shows history_gap right away, as it already does when the terminal event is not consumed or the acknowledgement fails (P2 #1 in Astro-Han's review on #5670). The task still cannot continue after that, which is intended because the transcript really is missing the reply. The defect is the contradictory ready/busy state, which leaves the user retrying with no guidance until they restart.
How to reproduce
Reproduced end to end in Desktop (npm run dev:worktree). A fake ACP Agent stands in for Antigravity, so no Google sign-in is needed (script below).
- Save the fake Agent as
~/maka-fake-acp/agent. Next to it, create an executable localharness_external that just runs exit 0, then chmod +x both files.
- Settings → External Agents → Antigravity: select
~/maka-fake-acp/agent.
- New task, executor Antigravity, model Fake Model. Send
hello: you get a normal reply.
- Send
MAKA_BIG_REPLY: the Agent streams about 300 KiB, and the turn fails with Executor completion text exceeds the size limit.
- Open the executor picker: Antigravity still shows no status and lists Fake Model.
- Send
hello: it fails with Antigravity ACP Session is busy. Every later message fails the same way.
Fake ACP Agent (~/maka-fake-acp/agent)
#!/usr/bin/env node
// Fake ACP Agent for end-to-end testing Maka's Antigravity executor path.
// A prompt starting with "MAKA_BIG_REPLY" replies with ~300 KiB of text (over Maka's 256 KiB limit);
// any other prompt gets a short echo reply.
const { createInterface } = require('node:readline');
const BIG_REPLY_KIB = 300;
let model = 'fake-model';
let promptId;
const modelOption = () => ({
id: 'model',
name: 'Model',
category: 'model',
type: 'select',
currentValue: model,
options: [{ value: 'fake-model', name: 'Fake Model' }],
});
const send = (value) => process.stdout.write(JSON.stringify({ jsonrpc: '2.0', ...value }) + '\n');
const respond = (id, result) => send({ id, result });
const text = (sessionId, chunk) =>
send({
method: 'session/update',
params: { sessionId, update: { sessionUpdate: 'agent_message_chunk', content: { type: 'text', text: chunk } } },
});
createInterface({ input: process.stdin }).on('line', (line) => {
const m = JSON.parse(line);
if (m.method === undefined) return;
switch (m.method) {
case 'initialize':
return respond(m.id, {
protocolVersion: 1,
agentCapabilities: { loadSession: true, sessionCapabilities: { resume: {} } },
});
case 'session/new':
return respond(m.id, { sessionId: `fake-${Date.now()}`, configOptions: [modelOption()] });
case 'session/resume':
case 'session/load':
return respond(m.id, { configOptions: [modelOption()] });
case 'session/set_config_option':
if (m.params.configId === 'model') model = m.params.value;
return respond(m.id, { configOptions: [modelOption()] });
case 'session/cancel':
if (promptId !== undefined) respond(promptId, { stopReason: 'cancelled' });
promptId = undefined;
return;
case 'session/prompt': {
const { sessionId, prompt } = m.params;
const input = prompt.map((part) => part.text ?? '').join('');
if (input.startsWith('MAKA_BIG_REPLY')) {
const chunk = 'x'.repeat(1023) + '\n';
for (let i = 0; i < BIG_REPLY_KIB; i++) text(sessionId, chunk);
} else {
text(sessionId, `fake reply (${input.length} chars received)`);
}
return respond(m.id, { stopReason: 'end_turn' });
}
default:
if (m.id !== undefined) send({ id: m.id, error: { code: -32601, message: `Method not found: ${m.method}` } });
}
});
The same chain also reproduces with the existing test harnesses. For an oversize completion, PluginExecutorBackend records neither acknowledgeExecution nor abandonExecution. AcpExecutor then reports ready from inspectConversation, while execute returns acp_busy.
Environment
- Maka commit: 2f32205 (source,
npm run dev:worktree)
- OS: macOS 15 (Darwin 24.6.0), arm64
- Surface: Desktop + Runtime Host, Antigravity ACP executor (fake Agent)
- Node.js: v24.15.0
Logs, screenshots, or additional context
Plugin continuity record after step 6 (plugin storage under ~/Library/Caches/Maka/runtime-hosts/<rootId>/plugin-data/):
{"phase":"prompt_pending","pendingTurnId":"1d446523-…","committedPrompts":1}
committedPrompts: 1 shows the first hello was acknowledged normally. pendingTurnId is the MAKA_BIG_REPLY turn, whose stored terminal event is the size-limit error. An abandoned turn would have moved the record to history_gap and closed the Agent process, but the fake Agent process is still running.
Screenshots: expanded turn errors (size limit, then busy); executor picker after the failure still showing Antigravity ready.
Proposed fix: in PluginExecutorBackend.send, call abandonExecution whenever the execution was not acknowledged, not only when a result was published. AcpExecutor.abandonExecution ignores turns that are not awaiting acknowledgement, so the extra call does nothing for failures that happen before the prompt settles. Add a regression test in plugin-executor-backend.test.ts asserting the abandonment for an oversize completion.
Refs #5103, #5670
This issue was drafted with Claude Code. I reproduced the behavior and reviewed the text.

What happened
When an external executor turn settles but the Runtime rejects its result before publishing it, the Plugin is never told to acknowledge or abandon that turn. The ACP Session then refuses every later message with
acp_busy, while the executor picker keeps showing it as ready.Chain on
2f3220552:AcpExecutor.executesetsawaitingAckand leaves its continuity record atprompt_pendingonce the prompt settles (packages/acp-executor-plugin/src/index.ts).PluginExecutorService.executeEntryruns the result throughnormalizeResult, which throws for a completion overMAX_EXECUTOR_COMPLETION_TEXT_BYTES(256 KiB) (packages/runtime/src/plugin-executor-service.ts).PluginExecutorBackend.#producepublishes a failure and returnsfalse. Thefinallyblock insendonly callsabandonExecutionwhenreturnedResultis true, so neitheracknowledgeExecutionnorabandonExecutionruns (packages/runtime/src/plugin-executor-backend.ts:119-126).awaitingAckis never cleared.executerejects every later prompt as busy, butinspectConversationdoes not checkawaitingAckand still reportsready. After a Host restart, the storedprompt_pendingrecord surfaces ashistory_gap.Expected: the turn is abandoned and the Session shows
history_gapright away, as it already does when the terminal event is not consumed or the acknowledgement fails (P2 #1 in Astro-Han's review on #5670). The task still cannot continue after that, which is intended because the transcript really is missing the reply. The defect is the contradictory ready/busy state, which leaves the user retrying with no guidance until they restart.How to reproduce
Reproduced end to end in Desktop (
npm run dev:worktree). A fake ACP Agent stands in for Antigravity, so no Google sign-in is needed (script below).~/maka-fake-acp/agent. Next to it, create an executablelocalharness_externalthat just runsexit 0, thenchmod +xboth files.~/maka-fake-acp/agent.hello: you get a normal reply.MAKA_BIG_REPLY: the Agent streams about 300 KiB, and the turn fails withExecutor completion text exceeds the size limit.hello: it fails withAntigravity ACP Session is busy. Every later message fails the same way.Fake ACP Agent (
~/maka-fake-acp/agent)The same chain also reproduces with the existing test harnesses. For an oversize completion,
PluginExecutorBackendrecords neitheracknowledgeExecutionnorabandonExecution.AcpExecutorthen reportsreadyfrominspectConversation, whileexecutereturnsacp_busy.Environment
npm run dev:worktree)Logs, screenshots, or additional context
Plugin continuity record after step 6 (plugin storage under
~/Library/Caches/Maka/runtime-hosts/<rootId>/plugin-data/):committedPrompts: 1shows the firsthellowas acknowledged normally.pendingTurnIdis theMAKA_BIG_REPLYturn, whose stored terminal event is the size-limit error. An abandoned turn would have moved the record tohistory_gapand closed the Agent process, but the fake Agent process is still running.Screenshots: expanded turn errors (size limit, then busy); executor picker after the failure still showing Antigravity ready.
Proposed fix: in
PluginExecutorBackend.send, callabandonExecutionwhenever the execution was not acknowledged, not only when a result was published.AcpExecutor.abandonExecutionignores turns that are not awaiting acknowledgement, so the extra call does nothing for failures that happen before the prompt settles. Add a regression test inplugin-executor-backend.test.tsasserting the abandonment for an oversize completion.Refs #5103, #5670
This issue was drafted with Claude Code. I reproduced the behavior and reviewed the text.