Skip to content

Commit 61c803e

Browse files
committed
refactor: remove sync rewrite mode, always use async (non-blocking) rewrite
- Remove `async` field from MessageRewriteConfig - MessageRewriteMiddleware.flushTurn() always fires in background - nonInteractiveCli.ts main & cron paths always push to pendingRewrites - No user-facing latency from rewrite calls
1 parent d6b61de commit 61c803e

File tree

4 files changed

+64
-82
lines changed

4 files changed

+64
-82
lines changed

packages/cli/src/acp-integration/session/Session.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -587,9 +587,9 @@ export class Session implements SessionContext {
587587
}
588588

589589
if (usageMetadata) {
590-
// Flush rewrite buffer before emitting usage (marks turn boundary)
590+
// Kick off rewrite in background (non-blocking)
591591
if (this.messageRewriter) {
592-
await this.messageRewriter.flushTurn(ac.signal);
592+
this.messageRewriter.flushTurn(ac.signal);
593593
}
594594
const durationMs = Date.now() - streamStartTime;
595595
await this.messageEmitter.emitUsageMetadata(

packages/cli/src/acp-integration/session/rewrite/MessageRewriteMiddleware.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ export class MessageRewriteMiddleware {
3131
private readonly turnBuffer: TurnBuffer;
3232
private readonly rewriter: LlmRewriter;
3333
private readonly target: MessageRewriteConfig['target'];
34-
private readonly asyncMode: boolean;
3534
private turnIndex = 0;
3635

3736
constructor(
@@ -42,7 +41,6 @@ export class MessageRewriteMiddleware {
4241
this.turnBuffer = new TurnBuffer();
4342
this.rewriter = new LlmRewriter(config, rewriteConfig);
4443
this.target = rewriteConfig.target;
45-
this.asyncMode = rewriteConfig.async !== false; // default true
4644
}
4745

4846
/**
@@ -97,8 +95,7 @@ export class MessageRewriteMiddleware {
9795
/**
9896
* Flush the turn buffer: rewrite accumulated content and emit.
9997
*
100-
* In async mode (default): non-blocking, rewrite runs parallel to tool execution.
101-
* In sync mode: blocks until rewrite completes, ensures strict message ordering.
98+
* Non-blocking: rewrite runs in background, parallel to tool execution.
10299
*
103100
* Called when:
104101
* - A tool_call is about to be emitted (turn boundary)
@@ -112,7 +109,7 @@ export class MessageRewriteMiddleware {
112109
this.turnIndex++;
113110
const turnIdx = this.turnIndex;
114111

115-
const doRewrite = (async () => {
112+
this.pendingRewrite = (async () => {
116113
try {
117114
const rewritten = await this.rewriter.rewrite(content, signal);
118115
if (!rewritten) {
@@ -139,14 +136,6 @@ export class MessageRewriteMiddleware {
139136
);
140137
}
141138
})();
142-
143-
if (this.asyncMode) {
144-
// Non-blocking: rewrite runs parallel to tool execution
145-
this.pendingRewrite = doRewrite;
146-
} else {
147-
// Blocking: wait for rewrite before proceeding (strict message order)
148-
await doRewrite;
149-
}
150139
}
151140

152141
/**

packages/cli/src/acp-integration/session/rewrite/types.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ export interface MessageRewriteConfig {
2020
promptFile?: string;
2121
/** Model to use for rewriting (empty = use current model) */
2222
model?: string;
23-
/** Whether to run rewrite async (parallel with tool execution, default true).
24-
* async=true: no added latency, but rewritten messages may arrive after tool calls
25-
* async=false: rewrite blocks before tools, messages in strict order */
26-
async?: boolean;
2723
}
2824

2925
/**

packages/cli/src/nonInteractiveCli.ts

Lines changed: 60 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -338,44 +338,40 @@ export async function runNonInteractive(
338338
adapter.finalizeAssistantMessage();
339339
totalApiDurationMs += Date.now() - apiStartTime;
340340

341-
// Rewrite turn content (async by default, parallel with tool execution)
341+
// Rewrite turn content (async, parallel with tool execution)
342342
if (rewriter && turnBuffer) {
343343
const content = turnBuffer.flush();
344344
if (content) {
345345
rewriteTurnIndex++;
346346
const turnIdx = rewriteTurnIndex;
347-
const doRewrite = async () => {
348-
try {
349-
const rewritten = await rewriter.rewrite(
350-
content,
351-
abortController?.signal,
352-
);
353-
if (rewritten) {
354-
debugLogger.info(
355-
`Turn ${turnIdx}: rewritten ${rewritten.length} chars`,
347+
pendingRewrites.push(
348+
(async () => {
349+
try {
350+
const rewritten = await rewriter.rewrite(
351+
content,
352+
abortController?.signal,
353+
);
354+
if (rewritten) {
355+
debugLogger.info(
356+
`Turn ${turnIdx}: rewritten ${rewritten.length} chars`,
357+
);
358+
adapter.startAssistantMessage();
359+
adapter.processEvent({
360+
type: GeminiEventType.Content,
361+
value: rewritten,
362+
_meta: { rewritten: true, turnIndex: turnIdx },
363+
} as unknown as Parameters<
364+
JsonOutputAdapterInterface['processEvent']
365+
>[0]);
366+
adapter.finalizeAssistantMessage();
367+
}
368+
} catch (err) {
369+
debugLogger.warn(
370+
`Turn ${turnIdx}: rewrite failed: ${err instanceof Error ? err.message : String(err)}`,
356371
);
357-
adapter.startAssistantMessage();
358-
adapter.processEvent({
359-
type: GeminiEventType.Content,
360-
value: rewritten,
361-
_meta: { rewritten: true, turnIndex: turnIdx },
362-
} as unknown as Parameters<
363-
JsonOutputAdapterInterface['processEvent']
364-
>[0]);
365-
adapter.finalizeAssistantMessage();
366372
}
367-
} catch (err) {
368-
debugLogger.warn(
369-
`Turn ${turnIdx}: rewrite failed: ${err instanceof Error ? err.message : String(err)}`,
370-
);
371-
}
372-
};
373-
// Default async (parallel with tool execution), sync if configured
374-
if (rewriteConfig?.async === false) {
375-
await doRewrite();
376-
} else {
377-
pendingRewrites.push(doRewrite());
378-
}
373+
})(),
374+
);
379375
}
380376
}
381377

@@ -534,42 +530,43 @@ export async function runNonInteractive(
534530
adapter.finalizeAssistantMessage();
535531
totalApiDurationMs += Date.now() - cronApiStartTime;
536532

537-
// Flush turn buffer and append rewritten message for cron path
533+
// Flush turn buffer and rewrite for cron path (async)
538534
if (rewriter && turnBuffer) {
539535
const content = turnBuffer.flush();
540536
if (content) {
541537
rewriteTurnIndex++;
542-
try {
543-
const rewriteSignal = AbortSignal.any([
544-
abortController.signal,
545-
AbortSignal.timeout(30000),
546-
]);
547-
const rewritten = await rewriter.rewrite(
548-
content,
549-
rewriteSignal,
550-
);
551-
if (rewritten) {
552-
debugLogger.info(
553-
`Cron turn ${rewriteTurnIndex}: rewritten ${rewritten.length} chars`,
554-
);
555-
adapter.startAssistantMessage();
556-
adapter.processEvent({
557-
type: GeminiEventType.Content,
558-
value: rewritten,
559-
_meta: {
560-
rewritten: true,
561-
turnIndex: rewriteTurnIndex,
562-
},
563-
} as unknown as Parameters<
564-
JsonOutputAdapterInterface['processEvent']
565-
>[0]);
566-
adapter.finalizeAssistantMessage();
567-
}
568-
} catch (err) {
569-
debugLogger.warn(
570-
`Cron turn ${rewriteTurnIndex}: rewrite failed: ${err instanceof Error ? err.message : String(err)}`,
571-
);
572-
}
538+
const turnIdx = rewriteTurnIndex;
539+
pendingRewrites.push(
540+
(async () => {
541+
try {
542+
const rewritten = await rewriter.rewrite(
543+
content,
544+
abortController.signal,
545+
);
546+
if (rewritten) {
547+
debugLogger.info(
548+
`Cron turn ${turnIdx}: rewritten ${rewritten.length} chars`,
549+
);
550+
adapter.startAssistantMessage();
551+
adapter.processEvent({
552+
type: GeminiEventType.Content,
553+
value: rewritten,
554+
_meta: {
555+
rewritten: true,
556+
turnIndex: turnIdx,
557+
},
558+
} as unknown as Parameters<
559+
JsonOutputAdapterInterface['processEvent']
560+
>[0]);
561+
adapter.finalizeAssistantMessage();
562+
}
563+
} catch (err) {
564+
debugLogger.warn(
565+
`Cron turn ${turnIdx}: rewrite failed: ${err instanceof Error ? err.message : String(err)}`,
566+
);
567+
}
568+
})(),
569+
);
573570
}
574571
}
575572

0 commit comments

Comments
 (0)