Repository navigation
fix(kimi-code): flush wire journals before print-mode exit #3531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
3511b89
463967f
84fe69b
f8fb8c8
e566535
61e34b0
a755e73
0614a7a
dda1e05
5c739e5
e3aa0a6
f4a76a4
36eb475
ed9fe38
ab7c823
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": patch | ||
| --- | ||
|
|
||
| Fix print mode (`kimi -p`) losing a failed turn's closing session records on error exit. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,6 +30,7 @@ import { | |
| IBootstrapService, | ||
| IConfigService, | ||
| IEventBus, | ||
| IEventDispatcher, | ||
| IOAuthToolkit, | ||
| ISessionIndex, | ||
| ISessionManager, | ||
|
|
@@ -196,6 +197,7 @@ export async function runV2Print( | |
| } | ||
|
|
||
| let restorePermission = async (): Promise<void> => {}; | ||
| let flushWires = async (): Promise<void> => {}; | ||
| let removeTerminationCleanup: (() => void) | undefined; | ||
| let cleanupPromise: Promise<void> | undefined; | ||
| let telemetryService: ITelemetryService | undefined; | ||
|
|
@@ -205,6 +207,13 @@ export async function runV2Print( | |
| setCrashPhase('shutdown'); | ||
| try { | ||
| await restorePermission(); | ||
| // The turn's tail records (step.end / turn.ended / prompt.completed) are | ||
| // dispatched fire-and-forget and reach the journal only through the wire | ||
| // service's async persist queue. Without an explicit flush, a cleanup | ||
| // that returns fast (e.g. telemetry disabled) lets process.exit cut off | ||
| // that queue before the records land on disk. Best-effort: a persist | ||
| // failure was already reported where the append failed. | ||
| await raceWithTimeout(flushWires(), CLI_SHUTDOWN_TIMEOUT_MS).catch(() => {}); | ||
| } finally { | ||
| if (telemetryService !== undefined) { | ||
| await raceWithTimeout(telemetryService.shutdown(), CLI_SHUTDOWN_TIMEOUT_MS); | ||
|
|
@@ -254,6 +263,7 @@ export async function runV2Print( | |
|
|
||
| const resolved = await resolveNativeSession(app, opts, workDir, defaultModel, stderr); | ||
| restorePermission = resolved.restorePermission; | ||
| flushWires = () => flushSessionWires(resolved.session, resolved.agent); | ||
|
|
||
| telemetryService.setContext({ session_id: resolved.session.id, model: resolved.telemetryModel }); | ||
| setTelemetryContext({ sessionId: resolved.session.id }); | ||
|
|
@@ -869,6 +879,26 @@ function countPendingBackgroundTasks(session: ISessionScopeHandle): number { | |
| return count; | ||
| } | ||
|
|
||
| /** | ||
| * Flush every session agent's wire journal. The main agent handle is included | ||
| * explicitly: `IAgentLifecycleService` skips `closing` agents, but a closing | ||
| * agent's already-dispatched tail records still deserve to land on disk. | ||
| */ | ||
| async function flushSessionWires( | ||
| session: ISessionScopeHandle, | ||
| mainAgent: IAgentScopeHandle, | ||
| ): Promise<void> { | ||
| const agentManager = session.accessor.get(IAgentLifecycleService); | ||
| const handles = new Set<IAgentScopeHandle>([mainAgent]); | ||
| for (const agent of agentManager.list()) { | ||
| const handle = agentManager.handleOf(agent.agentId); | ||
| if (handle !== undefined) handles.add(handle); | ||
|
Comment on lines
+903
to
+905
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a non-main agent has entered Useful? React with 👍 / 👎. |
||
| } | ||
| await Promise.all( | ||
| [...handles].map((handle) => handle.accessor.get(IEventDispatcher).flush()), | ||
| ); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When multiple agents are present and any dispatcher's Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| async function drainBackgroundTasks( | ||
| session: ISessionScopeHandle, | ||
| ceilingS: number | undefined, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the wire flush uses its 3-second allowance and both telemetry pipelines are also slow, cleanup can now take up to 9 seconds (
flushWires,telemetryService.shutdown, andshutdownTelemetryeach receiveCLI_SHUTDOWN_TIMEOUT_MS) while the outer cleanup still stops waiting after 8 seconds. On a failed run or signal, the caller then invokesprocess.exitimmediately, truncating the final telemetry shutdown and potentially preventingapp.dispose()from running. Use a shared deadline, run independent shutdowns concurrently, or increase the outer bound to cover all sequential phases.Useful? React with 👍 / 👎.