@oh-my-pi/pi-coding-agent 17.3.4 → 17.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/dist/{CHANGELOG-trcc215s.md → CHANGELOG-1hmwwt45.md} +88 -0
  3. package/dist/cli.js +3156 -3163
  4. package/dist/docs-index.generated.txt +1 -1
  5. package/dist/types/cli/stats-cli.d.ts +1 -6
  6. package/dist/types/commands/stats.d.ts +4 -0
  7. package/dist/types/config/model-discovery.d.ts +2 -0
  8. package/dist/types/config/model-registry.d.ts +6 -1
  9. package/dist/types/config/settings-schema.d.ts +10 -0
  10. package/dist/types/debug/report-bundle.d.ts +7 -2
  11. package/dist/types/extensibility/extensions/loader.d.ts +2 -0
  12. package/dist/types/extensibility/extensions/runner.d.ts +24 -6
  13. package/dist/types/extensibility/extensions/types.d.ts +60 -0
  14. package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +4 -0
  15. package/dist/types/launch/presence.d.ts +11 -0
  16. package/dist/types/lsp/client.d.ts +22 -0
  17. package/dist/types/lsp/workspace-diagnostics.d.ts +2 -0
  18. package/dist/types/mcp/config.d.ts +3 -1
  19. package/dist/types/modes/components/tool-execution.d.ts +3 -1
  20. package/dist/types/registry/agent-lifecycle.d.ts +11 -0
  21. package/dist/types/registry/agent-registry.d.ts +4 -0
  22. package/dist/types/session/agent-session-types.d.ts +0 -2
  23. package/dist/types/session/agent-session.d.ts +20 -0
  24. package/dist/types/session/date-cwd-reminder.d.ts +22 -0
  25. package/dist/types/session/irc-bridge.d.ts +2 -0
  26. package/dist/types/session/messages.d.ts +4 -0
  27. package/dist/types/session/session-loader.d.ts +18 -11
  28. package/dist/types/session/session-maintenance.d.ts +6 -2
  29. package/dist/types/session/session-manager.d.ts +19 -3
  30. package/dist/types/session/session-tools.d.ts +0 -1
  31. package/dist/types/session/turn-recovery.d.ts +7 -7
  32. package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +1 -0
  33. package/dist/types/tools/browser/attach.d.ts +16 -0
  34. package/dist/types/tools/browser/launch.d.ts +2 -2
  35. package/dist/types/tools/browser.d.ts +1 -1
  36. package/dist/types/tools/builtin-names.d.ts +1 -1
  37. package/dist/types/tools/file-write-fallback.d.ts +124 -0
  38. package/dist/types/tools/hub/jobs.d.ts +6 -0
  39. package/dist/types/tools/hub/types.d.ts +6 -0
  40. package/dist/types/tools/index.d.ts +1 -0
  41. package/dist/types/tools/path-utils.d.ts +23 -0
  42. package/dist/types/vibe/runtime.d.ts +3 -3
  43. package/package.json +13 -13
  44. package/src/auto-thinking/classifier.ts +37 -23
  45. package/src/cli/models-cli.ts +1 -1
  46. package/src/cli/stats-cli.ts +6 -72
  47. package/src/commands/stats.ts +7 -4
  48. package/src/commit/analysis/conventional.ts +15 -9
  49. package/src/commit/analysis/summary.ts +15 -9
  50. package/src/commit/changelog/generate.ts +15 -9
  51. package/src/commit/map-reduce/map-phase.ts +3 -19
  52. package/src/commit/map-reduce/reduce-phase.ts +15 -9
  53. package/src/config/model-discovery.ts +3 -3
  54. package/src/config/model-registry.ts +29 -16
  55. package/src/config/settings-schema.ts +13 -0
  56. package/src/debug/report-bundle.ts +25 -59
  57. package/src/discovery/claude-plugins.ts +2 -1
  58. package/src/discovery/claude.ts +4 -2
  59. package/src/discovery/helpers.ts +5 -0
  60. package/src/edit/hashline/filesystem.ts +9 -3
  61. package/src/edit/modes/patch.ts +31 -5
  62. package/src/edit/renderer.ts +48 -19
  63. package/src/extensibility/extensions/loader.ts +20 -5
  64. package/src/extensibility/extensions/runner.ts +239 -24
  65. package/src/extensibility/extensions/types.ts +62 -0
  66. package/src/extensibility/extensions/wrapper.ts +26 -11
  67. package/src/extensibility/legacy-pi-ai-shim.ts +4 -0
  68. package/src/extensibility/plugins/marketplace/manager.ts +15 -18
  69. package/src/irc/bus.ts +1 -1
  70. package/src/launch/broker.ts +8 -1
  71. package/src/launch/presence.ts +77 -1
  72. package/src/lsp/client.ts +35 -3
  73. package/src/lsp/clients/biome-client.ts +47 -93
  74. package/src/lsp/servers.ts +17 -11
  75. package/src/lsp/tool.ts +2 -4
  76. package/src/lsp/workspace-diagnostics.ts +24 -2
  77. package/src/lsp/writethrough.ts +20 -10
  78. package/src/mcp/config.ts +50 -6
  79. package/src/memories/index.ts +29 -25
  80. package/src/mnemopi/backend.ts +13 -11
  81. package/src/modes/components/ask-dialog.ts +25 -5
  82. package/src/modes/components/tool-execution.ts +10 -6
  83. package/src/modes/components/welcome.ts +4 -1
  84. package/src/modes/controllers/extension-ui-controller.ts +52 -32
  85. package/src/modes/controllers/selector-controller.ts +6 -0
  86. package/src/modes/controllers/tan-command-controller.ts +1 -0
  87. package/src/modes/interactive-mode.ts +49 -10
  88. package/src/modes/theme/defaults/birch.json +2 -2
  89. package/src/prompts/system/checkpoint-active-notice.md +5 -0
  90. package/src/prompts/system/date-cwd-reminder.md +3 -0
  91. package/src/prompts/system/project-prompt.md +0 -1
  92. package/src/prompts/system/rewind-report.md +1 -3
  93. package/src/prompts/tools/browser.md +1 -0
  94. package/src/prompts/tools/rewind.md +1 -13
  95. package/src/registry/agent-lifecycle.ts +34 -0
  96. package/src/registry/agent-registry.ts +27 -2
  97. package/src/registry/persisted-agents.ts +40 -20
  98. package/src/sdk.ts +32 -2
  99. package/src/session/agent-session-types.ts +0 -2
  100. package/src/session/agent-session.ts +178 -46
  101. package/src/session/date-cwd-reminder.ts +77 -0
  102. package/src/session/irc-bridge.ts +5 -0
  103. package/src/session/messages.ts +5 -1
  104. package/src/session/session-loader.ts +106 -64
  105. package/src/session/session-maintenance.ts +189 -115
  106. package/src/session/session-manager.ts +142 -35
  107. package/src/session/session-persistence.ts +4 -4
  108. package/src/session/session-storage.ts +18 -5
  109. package/src/session/session-tools.ts +5 -10
  110. package/src/session/turn-recovery.ts +102 -20
  111. package/src/session/unexpected-stop-classifier.ts +33 -21
  112. package/src/slash-commands/builtin-session.ts +1 -1
  113. package/src/slash-commands/helpers/stats-dashboard.ts +23 -9
  114. package/src/system-prompt.ts +0 -5
  115. package/src/task/executor.ts +4 -15
  116. package/src/task/persisted-revive.ts +3 -6
  117. package/src/tools/ask.ts +10 -3
  118. package/src/tools/browser/attach.ts +80 -25
  119. package/src/tools/browser/launch.ts +44 -15
  120. package/src/tools/browser/relay/daemon.ts +3 -7
  121. package/src/tools/browser/render.ts +1 -1
  122. package/src/tools/browser/shared-daemon.ts +3 -7
  123. package/src/tools/browser.ts +5 -5
  124. package/src/tools/builtin-names.ts +7 -3
  125. package/src/tools/checkpoint.ts +1 -7
  126. package/src/tools/file-write-fallback.ts +467 -0
  127. package/src/tools/hub/index.ts +1 -1
  128. package/src/tools/hub/jobs.ts +20 -3
  129. package/src/tools/hub/types.ts +6 -0
  130. package/src/tools/index.ts +1 -0
  131. package/src/tools/path-utils.ts +79 -0
  132. package/src/tools/render-utils.ts +53 -21
  133. package/src/tools/think.ts +15 -3
  134. package/src/tts/speech-enhancer.ts +19 -14
  135. package/src/utils/commit-message-generator.ts +14 -12
  136. package/src/utils/title-generator.ts +23 -19
  137. package/src/vibe/runtime.ts +32 -17
@@ -196,7 +196,7 @@ export interface SessionMaintenanceHost {
196
196
  planReferencePath(): string;
197
197
  nonMessageTokenSource(): NonMessageTokenSource;
198
198
  memoryBackendSession(): MemoryBackendOperationContext["session"];
199
- emitSessionEvent(event: AgentSessionEvent): Promise<void>;
199
+ emitSessionEvent(event: AgentSessionEvent, options?: { detachExtensions?: boolean }): Promise<void>;
200
200
  emitNotice(level: "info" | "warning" | "error", message: string, source?: string): void;
201
201
  schedulePostPromptTask(
202
202
  task: (signal: AbortSignal) => Promise<void>,
@@ -247,7 +247,7 @@ export interface SessionMaintenanceHost {
247
247
  dropImages(): Promise<{ removed: number }>;
248
248
  runHandoff(customInstructions?: string, options?: SessionHandoffOptions): Promise<HandoffResult | undefined>;
249
249
  removeAssistantMessageFromActiveContext(message: AssistantMessage): void;
250
- dropPersistedAssistantTurn(message: AssistantMessage): Promise<void>;
250
+ dropPersistedAssistantTurn(message: AssistantMessage): Promise<string | undefined>;
251
251
  runRecoveryCompactionWithRollback(
252
252
  reason: "overflow" | "incomplete",
253
253
  message: AssistantMessage,
@@ -313,6 +313,15 @@ export class SessionMaintenance {
313
313
  set skipPostTurnMaintenanceAssistantTimestamp(timestamp: number | undefined) {
314
314
  this.#skipPostTurnMaintenanceAssistantTimestamp = timestamp;
315
315
  }
316
+
317
+ /**
318
+ * Emit a compaction lifecycle event. Mid-turn callers detach only extension
319
+ * fan-out for post-commit events; ordered subscriber delivery still completes
320
+ * before maintenance returns.
321
+ */
322
+ #emitLifecycleEvent(event: AgentSessionEvent, detach: boolean): Promise<void> {
323
+ return this.#host.emitSessionEvent(event, detach ? { detachExtensions: true } : undefined);
324
+ }
316
325
  /**
317
326
  * Append plan-read protection to a prune/shake config so the active plan
318
327
  * file survives compaction alongside skill reads (the config defaults
@@ -1150,6 +1159,7 @@ export class SessionMaintenance {
1150
1159
  suppressHandoff: true,
1151
1160
  triggerContextTokens: contextTokens,
1152
1161
  phase: "mid_turn",
1162
+ detachPostCommit: true,
1153
1163
  });
1154
1164
  if (result.automaticContinuationBlocked) {
1155
1165
  this.#midTurnCompactionDeadEnds.add(activeMessages);
@@ -2166,6 +2176,8 @@ export class SessionMaintenance {
2166
2176
  suppressHandoff?: boolean;
2167
2177
  phase?: CodexCompactionContext["phase"];
2168
2178
  terminalTextAnswer?: boolean;
2179
+ /** Mid-turn: splice history then return; do not await UI/extension fan-out. */
2180
+ detachPostCommit?: boolean;
2169
2181
  } = {},
2170
2182
  ): Promise<CompactionCheckResult> {
2171
2183
  const compactionSettings = this.#host.settings.getGroup("compaction");
@@ -2191,6 +2203,7 @@ export class SessionMaintenance {
2191
2203
  terminalTextAnswer,
2192
2204
  options.triggerContextTokens,
2193
2205
  suppressContinuation,
2206
+ options.detachPostCommit === true,
2194
2207
  );
2195
2208
  if (outcome !== "fallback") return outcome;
2196
2209
  fallbackFromShake = true;
@@ -2252,7 +2265,8 @@ export class SessionMaintenance {
2252
2265
  // for any listener — and for input routed during this emit's event-loop yield:
2253
2266
  // a message typed as the compaction loader appears must land in the compaction
2254
2267
  // queue, not the core steering queue (which handoff's agent.reset() would wipe).
2255
- await this.#host.emitSessionEvent({ type: "auto_compaction_start", reason, action });
2268
+ const startEvent = { type: "auto_compaction_start" as const, reason, action };
2269
+ await this.#emitLifecycleEvent(startEvent, false);
2256
2270
  if (action === "handoff") {
2257
2271
  let handoffSwitchCancelled = false;
2258
2272
  const handoffFocus = AUTO_HANDOFF_THRESHOLD_FOCUS;
@@ -2266,13 +2280,16 @@ export class SessionMaintenance {
2266
2280
  if (!handoffResult) {
2267
2281
  const aborted = autoCompactionSignal.aborted || handoffSwitchCancelled;
2268
2282
  if (aborted) {
2269
- await this.#host.emitSessionEvent({
2270
- type: "auto_compaction_end",
2271
- action,
2272
- result: undefined,
2273
- aborted: true,
2274
- willRetry: false,
2275
- });
2283
+ await this.#emitLifecycleEvent(
2284
+ {
2285
+ type: "auto_compaction_end",
2286
+ action,
2287
+ result: undefined,
2288
+ aborted: true,
2289
+ willRetry: false,
2290
+ },
2291
+ options.detachPostCommit === true,
2292
+ );
2276
2293
  return COMPACTION_CHECK_NONE;
2277
2294
  }
2278
2295
  logger.warn("Auto-handoff returned no document; falling back to context-full maintenance", {
@@ -2281,13 +2298,16 @@ export class SessionMaintenance {
2281
2298
  action = "context-full";
2282
2299
  }
2283
2300
  if (handoffResult) {
2284
- await this.#host.emitSessionEvent({
2285
- type: "auto_compaction_end",
2286
- action,
2287
- result: undefined,
2288
- aborted: false,
2289
- willRetry: false,
2290
- });
2301
+ await this.#emitLifecycleEvent(
2302
+ {
2303
+ type: "auto_compaction_end",
2304
+ action,
2305
+ result: undefined,
2306
+ aborted: false,
2307
+ willRetry: false,
2308
+ },
2309
+ options.detachPostCommit === true,
2310
+ );
2291
2311
  const continuationScheduled =
2292
2312
  !autoCompactionSignal.aborted &&
2293
2313
  this.#host.scheduleCompactionContinuation({
@@ -2304,27 +2324,33 @@ export class SessionMaintenance {
2304
2324
  }
2305
2325
 
2306
2326
  if (!this.#model) {
2307
- await this.#host.emitSessionEvent({
2308
- type: "auto_compaction_end",
2309
- action,
2310
- result: undefined,
2311
- aborted: false,
2312
- willRetry: false,
2313
- skipped: true,
2314
- });
2327
+ await this.#emitLifecycleEvent(
2328
+ {
2329
+ type: "auto_compaction_end",
2330
+ action,
2331
+ result: undefined,
2332
+ aborted: false,
2333
+ willRetry: false,
2334
+ skipped: true,
2335
+ },
2336
+ options.detachPostCommit === true,
2337
+ );
2315
2338
  return COMPACTION_CHECK_NONE;
2316
2339
  }
2317
2340
 
2318
2341
  const availableModels = this.#host.modelRegistry.getAvailable();
2319
2342
  if (availableModels.length === 0) {
2320
- await this.#host.emitSessionEvent({
2321
- type: "auto_compaction_end",
2322
- action,
2323
- result: undefined,
2324
- aborted: false,
2325
- willRetry: false,
2326
- skipped: true,
2327
- });
2343
+ await this.#emitLifecycleEvent(
2344
+ {
2345
+ type: "auto_compaction_end",
2346
+ action,
2347
+ result: undefined,
2348
+ aborted: false,
2349
+ willRetry: false,
2350
+ skipped: true,
2351
+ },
2352
+ options.detachPostCommit === true,
2353
+ );
2328
2354
  return COMPACTION_CHECK_NONE;
2329
2355
  }
2330
2356
 
@@ -2411,17 +2437,20 @@ export class SessionMaintenance {
2411
2437
  // compaction entry — surface it as a real (non-skipped) result so
2412
2438
  // the TUI rebuilds the transcript instead of treating the pass as
2413
2439
  // a benign no-op.
2414
- await this.#host.emitSessionEvent({
2415
- type: "auto_compaction_end",
2416
- action,
2417
- result: frameRescueResult && {
2418
- ...frameRescueResult,
2419
- preserveData: snapcompact.stripPreservedArchive(frameRescueResult.preserveData),
2440
+ await this.#emitLifecycleEvent(
2441
+ {
2442
+ type: "auto_compaction_end",
2443
+ action,
2444
+ result: frameRescueResult && {
2445
+ ...frameRescueResult,
2446
+ preserveData: snapcompact.stripPreservedArchive(frameRescueResult.preserveData),
2447
+ },
2448
+ aborted: false,
2449
+ willRetry: false,
2450
+ skipped: frameRescueResult === undefined,
2420
2451
  },
2421
- aborted: false,
2422
- willRetry: false,
2423
- skipped: frameRescueResult === undefined,
2424
- });
2452
+ options.detachPostCommit === true,
2453
+ );
2425
2454
  let continuationScheduled = false;
2426
2455
  if (frameRescueCreatedHeadroom) {
2427
2456
  continuationScheduled = this.#host.scheduleCompactionContinuation({
@@ -2469,13 +2498,16 @@ export class SessionMaintenance {
2469
2498
  })) as SessionBeforeCompactResult | undefined;
2470
2499
 
2471
2500
  if (hookResult?.cancel) {
2472
- await this.#host.emitSessionEvent({
2473
- type: "auto_compaction_end",
2474
- action,
2475
- result: undefined,
2476
- aborted: true,
2477
- willRetry: false,
2478
- });
2501
+ await this.#emitLifecycleEvent(
2502
+ {
2503
+ type: "auto_compaction_end",
2504
+ action,
2505
+ result: undefined,
2506
+ aborted: true,
2507
+ willRetry: false,
2508
+ },
2509
+ options.detachPostCommit === true,
2510
+ );
2479
2511
  return COMPACTION_CHECK_NONE;
2480
2512
  }
2481
2513
 
@@ -2652,6 +2684,11 @@ export class SessionMaintenance {
2652
2684
  providerSessionState: this.#host.providerSessionState,
2653
2685
  preferWebsockets: this.#host.preferWebsockets,
2654
2686
  codexCompaction,
2687
+ // This loop already retries the whole compaction attempt on
2688
+ // transient errors, so the summarization oneshots must not
2689
+ // retry too — the budgets would multiply and each outer
2690
+ // wait would stack on top of an inner backoff.
2691
+ oneshotRetry: false,
2655
2692
  },
2656
2693
  );
2657
2694
  break;
@@ -2763,13 +2800,16 @@ export class SessionMaintenance {
2763
2800
  }
2764
2801
 
2765
2802
  if (autoCompactionSignal.aborted) {
2766
- await this.#host.emitSessionEvent({
2767
- type: "auto_compaction_end",
2768
- action,
2769
- result: undefined,
2770
- aborted: true,
2771
- willRetry: false,
2772
- });
2803
+ await this.#emitLifecycleEvent(
2804
+ {
2805
+ type: "auto_compaction_end",
2806
+ action,
2807
+ result: undefined,
2808
+ aborted: true,
2809
+ willRetry: false,
2810
+ },
2811
+ options.detachPostCommit === true,
2812
+ );
2773
2813
  return COMPACTION_CHECK_NONE;
2774
2814
  }
2775
2815
 
@@ -2804,11 +2844,20 @@ export class SessionMaintenance {
2804
2844
  | undefined;
2805
2845
 
2806
2846
  if (this.#host.extensionRunner && savedCompactionEntry) {
2807
- await this.#host.extensionRunner.emit({
2847
+ const compactEmit = this.#host.extensionRunner.emit({
2808
2848
  type: "session_compact",
2809
2849
  compactionEntry: savedCompactionEntry,
2810
2850
  fromExtension,
2811
2851
  });
2852
+ if (options.detachPostCommit) {
2853
+ void compactEmit.catch(error => {
2854
+ logger.warn("Detached session_compact emit failed", {
2855
+ error: error instanceof Error ? error.message : String(error),
2856
+ });
2857
+ });
2858
+ } else {
2859
+ await compactEmit;
2860
+ }
2812
2861
  }
2813
2862
 
2814
2863
  const result: CompactionResult = {
@@ -2910,7 +2959,10 @@ export class SessionMaintenance {
2910
2959
  }
2911
2960
  }
2912
2961
 
2913
- await this.#host.emitSessionEvent({ type: "auto_compaction_end", action, result, aborted: false, willRetry });
2962
+ await this.#emitLifecycleEvent(
2963
+ { type: "auto_compaction_end", action, result, aborted: false, willRetry },
2964
+ options.detachPostCommit === true,
2965
+ );
2914
2966
 
2915
2967
  if (retryFits) {
2916
2968
  this.#host.scheduleAgentContinue({ delayMs: 100, generation });
@@ -2931,29 +2983,35 @@ export class SessionMaintenance {
2931
2983
  return noProgressDeadEnd ? COMPACTION_CHECK_BLOCK_AUTOMATIC_CONTINUATION : COMPACTION_CHECK_NONE;
2932
2984
  } catch (error) {
2933
2985
  if (autoCompactionSignal.aborted) {
2934
- await this.#host.emitSessionEvent({
2986
+ await this.#emitLifecycleEvent(
2987
+ {
2988
+ type: "auto_compaction_end",
2989
+ action,
2990
+ result: undefined,
2991
+ aborted: true,
2992
+ willRetry: false,
2993
+ },
2994
+ options.detachPostCommit === true,
2995
+ );
2996
+ return COMPACTION_CHECK_NONE;
2997
+ }
2998
+ const errorMessage = error instanceof Error ? error.message : "compaction failed";
2999
+ await this.#emitLifecycleEvent(
3000
+ {
2935
3001
  type: "auto_compaction_end",
2936
3002
  action,
2937
3003
  result: undefined,
2938
- aborted: true,
3004
+ aborted: false,
2939
3005
  willRetry: false,
2940
- });
2941
- return COMPACTION_CHECK_NONE;
2942
- }
2943
- const errorMessage = error instanceof Error ? error.message : "compaction failed";
2944
- await this.#host.emitSessionEvent({
2945
- type: "auto_compaction_end",
2946
- action,
2947
- result: undefined,
2948
- aborted: false,
2949
- willRetry: false,
2950
- errorMessage:
2951
- reason === "overflow"
2952
- ? `Context overflow recovery failed: ${errorMessage}`
2953
- : reason === "incomplete"
2954
- ? `Incomplete response recovery failed: ${errorMessage}`
2955
- : `Auto-compaction failed: ${errorMessage}`,
2956
- });
3006
+ errorMessage:
3007
+ reason === "overflow"
3008
+ ? `Context overflow recovery failed: ${errorMessage}`
3009
+ : reason === "incomplete"
3010
+ ? `Incomplete response recovery failed: ${errorMessage}`
3011
+ : `Auto-compaction failed: ${errorMessage}`,
3012
+ },
3013
+ options.detachPostCommit === true,
3014
+ );
2957
3015
  } finally {
2958
3016
  if (this.#autoCompactionAbortController === autoCompactionAbortController) {
2959
3017
  this.#autoCompactionAbortController = undefined;
@@ -2980,6 +3038,7 @@ export class SessionMaintenance {
2980
3038
  terminalTextAnswer: boolean,
2981
3039
  triggerContextTokens?: number,
2982
3040
  suppressContinuation = false,
3041
+ detachPostCommit = false,
2983
3042
  ): Promise<CompactionCheckResult | "fallback"> {
2984
3043
  const action = "shake";
2985
3044
  this.#autoCompactionAbortController?.abort();
@@ -2987,16 +3046,19 @@ export class SessionMaintenance {
2987
3046
  this.#autoCompactionAbortController = controller;
2988
3047
  const signal = controller.signal;
2989
3048
  try {
2990
- await this.#host.emitSessionEvent({ type: "auto_compaction_start", reason, action });
3049
+ await this.#emitLifecycleEvent({ type: "auto_compaction_start", reason, action }, false);
2991
3050
  const result = await this.#host.shake("elide", { config: DEFAULT_SHAKE_CONFIG, signal });
2992
3051
  if (signal.aborted) {
2993
- await this.#host.emitSessionEvent({
2994
- type: "auto_compaction_end",
2995
- action,
2996
- result: undefined,
2997
- aborted: true,
2998
- willRetry: false,
2999
- });
3052
+ await this.#emitLifecycleEvent(
3053
+ {
3054
+ type: "auto_compaction_end",
3055
+ action,
3056
+ result: undefined,
3057
+ aborted: true,
3058
+ willRetry: false,
3059
+ },
3060
+ detachPostCommit,
3061
+ );
3000
3062
  return COMPACTION_CHECK_NONE;
3001
3063
  }
3002
3064
  const reclaimed = result.toolResultsDropped + result.blocksDropped > 0;
@@ -3039,25 +3101,31 @@ export class SessionMaintenance {
3039
3101
  const errorMessage = reclaimed
3040
3102
  ? `Auto-shake reclaimed ~${result.tokensFreed} tokens but context is still above the threshold; falling back to context-full compaction.`
3041
3103
  : "Auto-shake found nothing eligible to drop; falling back to context-full compaction.";
3042
- await this.#host.emitSessionEvent({
3104
+ await this.#emitLifecycleEvent(
3105
+ {
3106
+ type: "auto_compaction_end",
3107
+ action,
3108
+ result: undefined,
3109
+ aborted: false,
3110
+ willRetry: false,
3111
+ skipped: !reclaimed,
3112
+ errorMessage,
3113
+ },
3114
+ detachPostCommit,
3115
+ );
3116
+ return "fallback";
3117
+ }
3118
+ await this.#emitLifecycleEvent(
3119
+ {
3043
3120
  type: "auto_compaction_end",
3044
3121
  action,
3045
3122
  result: undefined,
3046
3123
  aborted: false,
3047
- willRetry: false,
3124
+ willRetry,
3048
3125
  skipped: !reclaimed,
3049
- errorMessage,
3050
- });
3051
- return "fallback";
3052
- }
3053
- await this.#host.emitSessionEvent({
3054
- type: "auto_compaction_end",
3055
- action,
3056
- result: undefined,
3057
- aborted: false,
3058
- willRetry,
3059
- skipped: !reclaimed,
3060
- });
3126
+ },
3127
+ detachPostCommit,
3128
+ );
3061
3129
 
3062
3130
  let continuationScheduled = false;
3063
3131
  if (willRetry) {
@@ -3096,25 +3164,31 @@ export class SessionMaintenance {
3096
3164
  };
3097
3165
  } catch (error) {
3098
3166
  if (signal.aborted) {
3099
- await this.#host.emitSessionEvent({
3167
+ await this.#emitLifecycleEvent(
3168
+ {
3169
+ type: "auto_compaction_end",
3170
+ action,
3171
+ result: undefined,
3172
+ aborted: true,
3173
+ willRetry: false,
3174
+ },
3175
+ detachPostCommit,
3176
+ );
3177
+ return COMPACTION_CHECK_NONE;
3178
+ }
3179
+ const message = error instanceof Error ? error.message : "shake failed";
3180
+ await this.#emitLifecycleEvent(
3181
+ {
3100
3182
  type: "auto_compaction_end",
3101
3183
  action,
3102
3184
  result: undefined,
3103
- aborted: true,
3185
+ aborted: false,
3104
3186
  willRetry: false,
3105
- });
3106
- return COMPACTION_CHECK_NONE;
3107
- }
3108
- const message = error instanceof Error ? error.message : "shake failed";
3109
- await this.#host.emitSessionEvent({
3110
- type: "auto_compaction_end",
3111
- action,
3112
- result: undefined,
3113
- aborted: false,
3114
- willRetry: false,
3115
- errorMessage: message,
3116
- skipped: false,
3117
- });
3187
+ errorMessage: message,
3188
+ skipped: false,
3189
+ },
3190
+ detachPostCommit,
3191
+ );
3118
3192
  // Overflow still needs recovery even if shake threw.
3119
3193
  return reason === "overflow" ? "fallback" : COMPACTION_CHECK_NONE;
3120
3194
  } finally {