@adhdev/daemon-core 1.0.18-rc.20 → 1.0.18-rc.4

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 (74) hide show
  1. package/dist/cli-adapters/provider-cli-shared.d.ts +0 -33
  2. package/dist/commands/cli-manager.d.ts +0 -9
  3. package/dist/commands/upgrade-helper.d.ts +0 -1
  4. package/dist/commands/windows-atomic-upgrade.d.ts +0 -5
  5. package/dist/config/mesh-json-config.d.ts +0 -62
  6. package/dist/index.d.ts +2 -4
  7. package/dist/index.js +3998 -6112
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +3855 -5966
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/mesh/coordinator-prompt.d.ts +0 -76
  12. package/dist/mesh/mesh-active-work.d.ts +1 -1
  13. package/dist/mesh/mesh-ledger.d.ts +1 -28
  14. package/dist/mesh/mesh-node-identity.d.ts +0 -23
  15. package/dist/mesh/mesh-refine-gates.d.ts +0 -89
  16. package/dist/mesh/mesh-remote-event-pull.d.ts +0 -3
  17. package/dist/mesh/mesh-runtime-store.d.ts +0 -11
  18. package/dist/mesh/mesh-work-queue.d.ts +1 -24
  19. package/dist/providers/chat-message-normalization.d.ts +0 -22
  20. package/dist/providers/cli-provider-instance-types.d.ts +0 -4
  21. package/dist/providers/cli-provider-instance.d.ts +0 -78
  22. package/dist/providers/contracts.d.ts +0 -17
  23. package/dist/providers/provider-schema.d.ts +0 -1
  24. package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +0 -14
  25. package/dist/providers/types/interactive-prompt.d.ts +0 -18
  26. package/dist/repo-mesh-types.d.ts +6 -38
  27. package/dist/shared-types.d.ts +2 -4
  28. package/package.json +3 -3
  29. package/src/boot/daemon-lifecycle.ts +0 -10
  30. package/src/cli-adapters/cli-state-engine.ts +1 -17
  31. package/src/cli-adapters/provider-cli-adapter.ts +6 -2
  32. package/src/cli-adapters/provider-cli-shared.ts +0 -48
  33. package/src/commands/cli-manager.ts +8 -72
  34. package/src/commands/high-family/mesh-coordinator-launch.ts +2 -17
  35. package/src/commands/high-family/mesh-events.ts +2 -13
  36. package/src/commands/med-family/mesh-crud.ts +0 -155
  37. package/src/commands/med-family/mesh-queue.ts +1 -74
  38. package/src/commands/router-refine.ts +4 -71
  39. package/src/commands/router.ts +0 -8
  40. package/src/commands/upgrade-helper.ts +15 -78
  41. package/src/commands/windows-atomic-upgrade.ts +40 -194
  42. package/src/config/mesh-json-config.ts +0 -111
  43. package/src/index.ts +1 -21
  44. package/src/mesh/coordinator-prompt.ts +11 -258
  45. package/src/mesh/mesh-active-work.ts +1 -11
  46. package/src/mesh/mesh-completion-synthesis.ts +1 -12
  47. package/src/mesh/mesh-event-classify.ts +0 -19
  48. package/src/mesh/mesh-event-forwarding.ts +6 -64
  49. package/src/mesh/mesh-events-utils.ts +0 -42
  50. package/src/mesh/mesh-ledger.ts +0 -77
  51. package/src/mesh/mesh-node-identity.ts +0 -49
  52. package/src/mesh/mesh-queue-assignment.ts +11 -210
  53. package/src/mesh/mesh-reconcile-loop.ts +3 -306
  54. package/src/mesh/mesh-refine-gates.ts +0 -300
  55. package/src/mesh/mesh-remote-event-pull.ts +47 -68
  56. package/src/mesh/mesh-runtime-store.ts +0 -21
  57. package/src/mesh/mesh-work-queue.ts +2 -85
  58. package/src/providers/chat-message-normalization.ts +0 -53
  59. package/src/providers/cli-provider-instance-types.ts +0 -53
  60. package/src/providers/cli-provider-instance.ts +15 -497
  61. package/src/providers/contracts.ts +1 -25
  62. package/src/providers/provider-schema.ts +0 -83
  63. package/src/providers/sdk/v1/builders/cli/detect-status.ts +0 -23
  64. package/src/providers/sdk/v1/builders/cli/parse-approval.ts +0 -20
  65. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +0 -44
  66. package/src/providers/types/interactive-prompt.ts +0 -77
  67. package/src/repo-mesh-types.ts +12 -112
  68. package/src/shared-types.ts +1 -9
  69. package/src/status/reporter.ts +0 -1
  70. package/src/status/snapshot.ts +0 -2
  71. package/dist/mesh/mesh-disk-retention.d.ts +0 -105
  72. package/dist/providers/auto-approve-modes.d.ts +0 -14
  73. package/src/mesh/mesh-disk-retention.ts +0 -370
  74. package/src/providers/auto-approve-modes.ts +0 -103
@@ -12,12 +12,11 @@ import * as fs from 'fs';
12
12
  import { normalizeInputEnvelope, type ProviderModule, flattenContent, type InputEnvelope } from './contracts.js';
13
13
  import { assertProviderSupportsDeclaredInput, getEffectiveMessageInputSupport } from './provider-input-support.js';
14
14
  import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext, ProviderErrorReason, HotChatSessionState, SessionModalState } from './provider-instance.js';
15
- import { normalizeInteractivePrompt, normalizeInteractivePromptResponse, resolveInteractivePromptResponse, type InteractivePrompt } from './types/interactive-prompt.js';
15
+ import { normalizeInteractivePrompt, normalizeInteractivePromptResponse, type InteractivePrompt } from './types/interactive-prompt.js';
16
16
  import { ProviderCliAdapter } from '../cli-adapters/provider-cli-adapter.js';
17
17
  import { shortHash } from '../system/hash.js';
18
18
  import type { CliProviderModule } from '../cli-adapters/provider-cli-adapter.js';
19
19
  import type { MeshSendKeyItem, MeshSendKeyName } from '../cli-adapters/provider-cli-shared.js';
20
- import { isPurePtyTranscriptProvider } from '../cli-adapters/provider-cli-shared.js';
21
20
  import { createCliAdapter } from './spec/route.js';
22
21
  import type { PtyRuntimeMetadata, PtyTransportFactory } from '../cli-adapters/pty-transport.js';
23
22
  import { StatusMonitor } from './status-monitor.js';
@@ -57,11 +56,8 @@ import {
57
56
  STATUS_HYDRATION_TAIL_LIMIT,
58
57
  COMPLETED_FINALIZATION_RETRY_MS,
59
58
  COMPLETED_FINALIZATION_MAX_WAIT_MS,
60
- CANON_C_MISSING_ASSISTANT_MIN_ELAPSED_MS,
61
59
  NATIVE_HISTORY_MESH_IDLE_SETTLE_MS,
62
60
  PTY_PARSED_FINAL_ASSISTANT_QUIET_DWELL_MS,
63
- ANTIGRAVITY_HOLD_QUIET_DWELL_MS,
64
- ANTIGRAVITY_HOLD_HARD_CAP_MS,
65
61
  BACKGROUND_TASK_HOLD_MAX_MS,
66
62
  USER_INPUT_ACK_DEDUP_WINDOW_MS,
67
63
  STARTUP_GRACE_IDLE_COLLAPSE_WINDOW_MS,
@@ -75,7 +71,6 @@ import type {
75
71
  } from './cli-provider-instance-types.js';
76
72
  import { mergeConversationMessages, buildExternalTranscriptProbe } from './cli-provider-transcript-merge.js';
77
73
  import { getEffectDedupKey, formatApprovalRequestMessage, formatMarkerTimestamp } from './cli-provider-effect-format.js';
78
- import { resolveProviderAutoApproveMode, type ResolvedAutoApproveMode } from './auto-approve-modes.js';
79
74
 
80
75
  // Re-export moved public symbols so existing importers (index.ts, tests) keep
81
76
  // their `./cli-provider-instance.js` path. Pure move — no behavior change.
@@ -334,18 +329,6 @@ export class CliProviderInstance implements ProviderInstance {
334
329
  // MESH_WORKER_STALL_REFIRE_COOLDOWN_MS between successive emissions across
335
330
  // anchor re-arms (the per-anchor guard only covers a single continuous stall).
336
331
  private meshStallLastFiredAt = -1;
337
- // KIMI-MESH-COMPLETION-EMIT (axis 1): the native-source transcript progress
338
- // fingerprint (record count + source mtime) observed the LAST time the stall
339
- // watchdog checked a native-source provider (transcriptAuthority=provider — e.g.
340
- // kimi wire.jsonl). A pure-PTY native-source worker running a long, screen-quiet
341
- // tool (no viewport bytes for minutes) looks stalled by the lastOutputAt clock
342
- // even though its transcript file is still growing. Before firing the stall, we
343
- // compare the current transcript fingerprint against this snapshot; if the
344
- // transcript advanced, the "stall" is a false positive of PTY-render stasis, so
345
- // we re-arm the anchor instead of paging the coordinator (whose no_progress
346
- // handling can lead to the worker being stopped mid-work → completion never
347
- // emitted). null = not yet sampled for this session/anchor.
348
- private meshStallNativeTranscriptSample: { msgCount: number; sourceMtimeMs: number } | null = null;
349
332
  // FALSE-IDLE continuity epoch: monotonically bumped on EVERY entry into a busy
350
333
  // phase (→generating or →waiting_approval). The completedDebouncePending snapshots
351
334
  // this value at arm time (busyEpochAtArm); the flush guard requires it UNCHANGED
@@ -386,18 +369,6 @@ export class CliProviderInstance implements ProviderInstance {
386
369
  private generatingDebounceTimer: NodeJS.Timeout | null = null;
387
370
  private generatingDebouncePending: { chatTitle: string; timestamp: number } | null = null;
388
371
  private lastApprovalEventFingerprint = '';
389
- // INTERACTIVE-PROMPT-PUSH: edge-trigger key for the AskUserQuestion / waiting_choice
390
- // notification. An AskUserQuestion prompt is surfaced only as a display-only
391
- // `waiting_choice` overlay in getState(); the raw adapter status stays idle/generating,
392
- // so detectStatusTransition()'s status-keyed arms never fire and no push-worthy
393
- // agent:* event is emitted — the owner misses the "ACTION REQUIRED" prompt when the
394
- // app is backgrounded. We emit one agent:waiting_choice on ENTRY into the prompt
395
- // state (its own coordinator event, NOT the approval channel — a multi-choice
396
- // question is answered with mesh_answer_question, never mesh_approve) carrying the
397
- // FULL InteractivePrompt payload, and dedupe on this key so repeated status ticks
398
- // with the same prompt do not re-fire. '' means no prompt is currently active
399
- // (cleared when the prompt is answered/gone).
400
- private lastInteractivePromptEventKey = '';
401
372
  private autoApproveBusy = false;
402
373
  private autoApproveBusyTimer: NodeJS.Timeout | null = null;
403
374
  private lastAutoApprovalSignature = '';
@@ -1289,16 +1260,7 @@ export class CliProviderInstance implements ProviderInstance {
1289
1260
  }
1290
1261
  } else if (event === 'interactive_prompt_response' && data) {
1291
1262
  try {
1292
- // mesh_answer_question (mission f1d25e11) sends a coordinator-friendly answer
1293
- // form (per-question select by label/index) that must be resolved against the
1294
- // AUTHORITATIVE active prompt held here. When the active prompt is present and
1295
- // the promptId matches, resolve it; otherwise fall back to the strict keyed
1296
- // form (dashboard local answers already send that shape).
1297
- const response = (this.activeInteractivePrompt
1298
- && this.activeInteractivePrompt.promptId === (data as { promptId?: unknown })?.promptId
1299
- && Array.isArray((data as { answers?: unknown })?.answers))
1300
- ? resolveInteractivePromptResponse(this.activeInteractivePrompt, data)
1301
- : normalizeInteractivePromptResponse(data);
1263
+ const response = normalizeInteractivePromptResponse(data);
1302
1264
  if (this.activeInteractivePrompt?.promptId === response.promptId) {
1303
1265
  this.activeInteractivePrompt = null;
1304
1266
  }
@@ -1432,15 +1394,6 @@ export class CliProviderInstance implements ProviderInstance {
1432
1394
  */
1433
1395
  private lastCompletionSummary: { content: string; receivedAt: number; sourceTimestampMs?: number } | null = null;
1434
1396
 
1435
- // KIMI-MESH-COMPLETION-EMIT (axis 2, double-emit guard): the (taskId, wall-clock)
1436
- // of the most recent agent:generating_completed this instance emitted, stamped by
1437
- // emitGeneratingCompleted. The pre-cleanup completion flush
1438
- // (flushMeshCompletionBeforeCleanup, driven by cli-manager's exit monitor) reads
1439
- // this to refuse a SECOND completion for a turn whose completion already fired —
1440
- // so a worker that finished cleanly and is simply being auto-cleaned never emits a
1441
- // duplicate. taskId '' covers an ad-hoc (no-task) turn. null = none emitted yet.
1442
- private lastEmittedCompletion: { taskId: string; at: number } | null = null;
1443
-
1444
1397
  private async enforceFreshSessionLaunchIfNeeded(): Promise<void> {
1445
1398
  const scriptName = getForcedNewSessionScriptName(this.provider, this.launchMode);
1446
1399
  if (!scriptName) return;
@@ -1944,33 +1897,6 @@ export class CliProviderInstance implements ProviderInstance {
1944
1897
  return false;
1945
1898
  }
1946
1899
 
1947
- // (ANTIGRAVITY-30S-CAP-PREMATURE) Discriminator gating the 30s-cap release of an antigravity
1948
- // `holdForTranscript` block. Antigravity's idle verdict is PTY-screen-derived but its assistant
1949
- // answer lands in native-history, which can legitimately lag past COMPLETED_FINALIZATION_MAX_WAIT_MS
1950
- // (30s) on a long turn. The cap releases on elapsed time, not proof-of-idle, so it force-emitted a
1951
- // premature weak completion WHILE THE PTY WAS STILL GENERATING. Returns true when the PTY is still
1952
- // active — i.e. the adapter reports a pending response OR raw PTY output arrived within the last
1953
- // ANTIGRAVITY_HOLD_QUIET_DWELL_MS — meaning the 30s cap must KEEP HOLDING (the turn is not proven
1954
- // over). Returns false when the PTY is genuinely quiescent (no pending response AND no recent
1955
- // output), so a real tool-only turn with no assistant bubble still force-emits a weak completion
1956
- // rather than wedging. The absolute ANTIGRAVITY_HOLD_HARD_CAP_MS bound is enforced at the call site
1957
- // so a runaway PTY that never falls quiet still eventually releases. Fails OPEN (returns false =
1958
- // allow release) when lastOutputAt is unreadable, so the gate can never wedge a session.
1959
- private antigravityHoldPtyStillActive(): boolean {
1960
- if (this.hasAdapterPendingResponse()) return true;
1961
- try {
1962
- const outStatus = this.adapter.getStatus({ allowParse: false }) as any;
1963
- const lastOutputAt = typeof outStatus?.lastOutputAt === 'number' && Number.isFinite(outStatus.lastOutputAt)
1964
- ? outStatus.lastOutputAt as number
1965
- : undefined;
1966
- if (typeof lastOutputAt === 'number') {
1967
- const quietMs = Date.now() - lastOutputAt;
1968
- if (quietMs < ANTIGRAVITY_HOLD_QUIET_DWELL_MS) return true;
1969
- }
1970
- } catch { /* defensive: dwell read is best-effort — fall through to allow release */ }
1971
- return false;
1972
- }
1973
-
1974
1900
  private shouldSuppressStaleParsedBusyStatus(parsedStatus: any, adapterStatus: any): boolean {
1975
1901
  const parsedRawStatus = typeof parsedStatus?.status === 'string' ? parsedStatus.status.trim() : '';
1976
1902
  const adapterRawStatus = typeof adapterStatus?.status === 'string' ? adapterStatus.status.trim() : '';
@@ -2119,11 +2045,6 @@ export class CliProviderInstance implements ProviderInstance {
2119
2045
  reason: 'missing_final_assistant',
2120
2046
  terminal: (this.provider as any).requiresFinalAssistantBeforeIdle === true,
2121
2047
  allowTimeout: allowMissingAssistantTimeout,
2122
- // PTY-parsed provider (codex-cli): no external transcript trails the idle
2123
- // transition, so the CANON-C decoupled emit must observe the min-elapsed floor
2124
- // rather than fire at the first-poll waitedMs. (claude-cli's external-native
2125
- // branch above is exempt — its transcript legitimately lands moments later.)
2126
- noExternalTranscriptSource: true,
2127
2048
  };
2128
2049
  }
2129
2050
  }
@@ -2427,52 +2348,6 @@ export class CliProviderInstance implements ProviderInstance {
2427
2348
  const stalledMs = now - this.meshStallAnchorAt;
2428
2349
  if (stalledMs < threshold) return;
2429
2350
 
2430
- // KIMI-MESH-COMPLETION-EMIT (axis 1): transcript-aware stall for native-source
2431
- // providers. A pure-PTY native-source worker (transcriptAuthority=provider,
2432
- // e.g. kimi) running a long, screen-quiet tool emits no viewport bytes for
2433
- // minutes, so the lastOutputAt clock above reads it as stalled — but its
2434
- // authoritative transcript (wire.jsonl) is still being appended. Firing
2435
- // monitor:no_progress here surfaces a false stall to the coordinator whose
2436
- // downstream handling can stop the worker mid-work, killing the emit window
2437
- // before the real completion propagates. So before firing, sample the native
2438
- // transcript's progress fingerprint: if it advanced (more records OR a fresher
2439
- // source mtime) since the last sample, the worker is demonstrably alive — treat
2440
- // it as progress. Re-arm the anchor to `now` and reset the episode so the clock
2441
- // restarts from this proven-live moment; only a transcript that is ALSO static
2442
- // falls through to the genuine-stall fire below. No-op for pure-PTY providers
2443
- // (sample is null → unchanged behavior).
2444
- const nativeSample = this.sampleNativeTranscriptProgress();
2445
- if (nativeSample) {
2446
- const prev = this.meshStallNativeTranscriptSample;
2447
- const advanced = !prev
2448
- || nativeSample.msgCount > prev.msgCount
2449
- || nativeSample.sourceMtimeMs > prev.sourceMtimeMs;
2450
- this.meshStallNativeTranscriptSample = nativeSample;
2451
- if (advanced) {
2452
- if (this.isMeshWorkerSession()) {
2453
- traceMeshEventDrop('mesh_worker_stall_transcript_advancing', this.meshTraceCtx('monitor:no_progress'),
2454
- `msgCount=${nativeSample.msgCount} sourceMtime=${nativeSample.sourceMtimeMs} (PTY quiet ${Math.round(stalledMs / 1000)}s but transcript advancing)`);
2455
- }
2456
- this.meshStallAnchorAt = now;
2457
- this.meshStallEmittedForAnchor = false;
2458
- return;
2459
- }
2460
- }
2461
-
2462
- // KIMI-PURE-PTY-COMPLETION-EMIT (Fix 3): before firing a false stall, check the
2463
- // pure-PTY class (no native transcript → nativeSample was null above). A finished
2464
- // pure-PTY worker sits at a static idle prompt with an in-turn final assistant
2465
- // message but never emitted generating_completed — the status-agnostic watchdog
2466
- // would misread that quiet as a wedge. If the PTY transcript proves a finished
2467
- // turn, emit the missing completion and SUPPRESS the stall (mark the anchor
2468
- // emitted so this static idle is not re-checked every tick). No-op for
2469
- // native-source providers and for a genuinely mid-turn / wedged worker with no
2470
- // final assistant → the real stall fires below unchanged.
2471
- if (this.tryReconcilePurePtyCompletionForStall(observedStatus)) {
2472
- this.meshStallEmittedForAnchor = true;
2473
- return;
2474
- }
2475
-
2476
2351
  // FALSE-STALL-WATCHDOG-OVERFIRE (fix E): per-session refire cooldown. Mark
2477
2352
  // this anchor emitted regardless so a still-static anchor does not re-check
2478
2353
  // every tick, but suppress the actual coordinator notification (and the
@@ -2523,193 +2398,6 @@ export class CliProviderInstance implements ProviderInstance {
2523
2398
  this.meshStallEmittedForAnchor = false;
2524
2399
  this.meshStallTurnActiveLast = undefined;
2525
2400
  this.meshStallLastFiredAt = -1;
2526
- this.meshStallNativeTranscriptSample = null;
2527
- }
2528
-
2529
- /**
2530
- * KIMI-MESH-COMPLETION-EMIT (axis 1). For a native-source provider
2531
- * (transcriptAuthority=provider — its authoritative history is an on-disk
2532
- * transcript file, e.g. kimi's wire.jsonl), sample the transcript's current
2533
- * progress fingerprint (record count + source-file mtime) WITHOUT parsing the
2534
- * PTY. Used by the stall watchdog to distinguish "PTY render is quiet but the
2535
- * worker is still doing long tool work (transcript growing)" from a genuine
2536
- * wedge. Returns null for a pure-PTY provider (no native source) or when the
2537
- * source cannot be resolved this tick — the caller then falls back to the
2538
- * unchanged lastOutputAt-only judgment.
2539
- *
2540
- * Generalized on the provider's native-source flag, NOT a hardcoded provider
2541
- * type, so every current and future pure-PTY long-tool native-source provider
2542
- * benefits. Cheap enough for the stall path: it runs only at the stall threshold
2543
- * (≥180s of PTY stasis), never on the routine 5s tick.
2544
- */
2545
- private sampleNativeTranscriptProgress(): { msgCount: number; sourceMtimeMs: number } | null {
2546
- if (!isNativeSourceCanonicalHistory(this.provider?.nativeHistory)) return null;
2547
- // readExternalCompletionMessages resolves this session's OWN native-source
2548
- // conversation (providerSessionId / persisted pin / floor claim) and, as a
2549
- // side effect, refreshes this.lastExternalCompletionProbe with the source
2550
- // path, mtime, and message count. Reusing it keeps the resolution logic in
2551
- // one place and immune to the antigravity-style session-id quirks.
2552
- let messages: unknown[] | null = null;
2553
- try {
2554
- messages = this.readExternalCompletionMessages();
2555
- } catch {
2556
- return null; // best-effort: an unresolved transcript → fall back to PTY clock
2557
- }
2558
- const probe = this.lastExternalCompletionProbe;
2559
- if (!probe) return null;
2560
- return {
2561
- msgCount: typeof probe.msgCount === 'number' ? probe.msgCount : (Array.isArray(messages) ? messages.length : 0),
2562
- sourceMtimeMs: typeof probe.sourceMtimeMs === 'number' ? probe.sourceMtimeMs : 0,
2563
- };
2564
- }
2565
-
2566
- /**
2567
- * KIMI-MESH-COMPLETION-EMIT (axis 2). Last-chance completion emit for a mesh
2568
- * DELEGATED worker whose PTY has already exited (e.g. killed by a false stall)
2569
- * and is about to be auto-cleaned (cli-manager.startCliExitMonitor →
2570
- * removeInstance closes the event-emit window forever). If the worker actually
2571
- * FINISHED its assigned turn — its authoritative native transcript holds a final
2572
- * assistant message for the injected task — but the completion event never fired
2573
- * (the stall-kill happened before the FSM's idle transition), emit it now so the
2574
- * coordinator learns the task completed instead of waiting ~180s for the reconcile
2575
- * transcript-poll to reclaim it.
2576
- *
2577
- * Scope & guards (must all hold to emit):
2578
- * • mesh worker session only — a normal standalone session's ordinary exit is
2579
- * never synthesized (isMeshWorkerSession()).
2580
- * • DOUBLE-EMIT guard — refuse if this turn's completion already fired
2581
- * (lastEmittedCompletion matches the current taskId). A worker that completed
2582
- * cleanly and is merely being cleaned up never double-emits.
2583
- * • evidence gate — reuse the same final-assistant/turn-scoped summary machinery
2584
- * as the normal completion path (completionFinalSummary over the native
2585
- * transcript). No in-turn assistant summary ⇒ no proof of completion ⇒ leave
2586
- * the reclaim path to handle a genuinely-unfinished worker.
2587
- *
2588
- * Returns true when a synthetic completion was emitted, false otherwise. Safe to
2589
- * call unconditionally from the cleanup path.
2590
- */
2591
- flushMeshCompletionBeforeCleanup(): boolean {
2592
- if (!this.isMeshWorkerSession()) return false;
2593
- const taskId = this.completingTurnTaskId();
2594
-
2595
- // DOUBLE-EMIT guard: this turn's completion already fired — never re-emit.
2596
- if (this.lastEmittedCompletion && this.lastEmittedCompletion.taskId === (taskId ?? '')) {
2597
- return false;
2598
- }
2599
-
2600
- // Evidence gate: the injected task's own turn must have genuinely started
2601
- // (guards against synthesizing a completion off a reused session's stale tail
2602
- // before this task ran) and the native transcript must hold an in-turn final
2603
- // assistant summary. turnStartedAt anchors the turn-scoped read.
2604
- if (!this.injectedTaskHasStartedGenerating()) return false;
2605
- const turnStartedAt = typeof (this.adapter as any)?.currentTurnStartedAt === 'number'
2606
- ? (this.adapter as any).currentTurnStartedAt as number
2607
- : this.meshTaskInjectedAt || undefined;
2608
- let parsedMessages: unknown;
2609
- try {
2610
- parsedMessages = this.adapter?.getScriptParsedStatus?.()?.messages;
2611
- } catch { parsedMessages = undefined; }
2612
- let finalSummary: string | undefined;
2613
- try {
2614
- finalSummary = this.completionFinalSummary(parsedMessages, turnStartedAt);
2615
- } catch { finalSummary = undefined; }
2616
- // No in-turn final assistant evidence → not a proven turn-end. Defer to the
2617
- // coordinator's reclaim/reconcile path rather than fabricate a completion.
2618
- if (!finalSummary) return false;
2619
-
2620
- LOG.warn('CLI', `[${this.type}] emitting pre-cleanup mesh completion for session ${this.instanceId} `
2621
- + `task=${taskId ?? '(none)'} — PTY exited before the completion event fired but the native transcript `
2622
- + `shows a finished turn; synthesizing completion so the coordinator is not left waiting for reclaim.`);
2623
- if (this.isMeshWorkerSession()) {
2624
- traceMeshEventStage('fired', this.meshTraceCtx(), 'pre_cleanup_transcript_completion');
2625
- }
2626
- this.emitGeneratingCompleted({
2627
- chatTitle: '',
2628
- duration: undefined,
2629
- timestamp: Date.now(),
2630
- taskId,
2631
- finalSummary,
2632
- evidenceLevel: 'transcript',
2633
- completionDiagnostic: { source: 'pre_cleanup_transcript_completion' },
2634
- });
2635
- return true;
2636
- }
2637
-
2638
- /**
2639
- * KIMI-PURE-PTY-COMPLETION-EMIT (Fix 3): stall-path completion reconcile for the
2640
- * PURE-PTY transcript class (kimi and kin — no native transcript, no provider
2641
- * authority; see isPurePtyTranscriptProvider). Such a worker whose
2642
- * generating_completed never emitted (the onTurnStarted idle→idle collapse Fix 1
2643
- * fixes at the source) sits at a STATIC idle prompt: its PTY output stops the
2644
- * instant the answer is rendered, so the status-agnostic no-progress watchdog
2645
- * (checkMeshWorkerStall) reads the finished-but-quiet session as a stall and would
2646
- * false-fire monitor:no_progress. The native-transcript reconcile
2647
- * (sampleNativeTranscriptProgress) does NOT cover this class (no native source →
2648
- * null sample), so the stall path needs its own guard.
2649
- *
2650
- * Called from checkMeshWorkerStall just before the fire. Returns true when the
2651
- * session is a finished pure-PTY turn — idle, no pending response, and a PTY-parsed
2652
- * in-turn final assistant summary — in which case it emits the missing
2653
- * generating_completed (idempotent: a real/late emit writes the terminal ledger and
2654
- * the coordinator's reconcile makes any duplicate a no-op) and the caller SUPPRESSES
2655
- * the stall. Returns false for every other class/state (native-source provider, a
2656
- * genuinely mid-turn or wedged worker with no final assistant), so a real stall still
2657
- * fires unchanged.
2658
- *
2659
- * Evidence bar is identical to flushMeshCompletionBeforeCleanup: injected task's turn
2660
- * genuinely started + an in-turn final assistant summary. Conservative by
2661
- * construction — no summary ⇒ no proof of completion ⇒ return false and let the real
2662
- * stall fire.
2663
- */
2664
- private tryReconcilePurePtyCompletionForStall(observedStatus: string): boolean {
2665
- // Only the pure-PTY class — native-source providers are handled by
2666
- // sampleNativeTranscriptProgress above.
2667
- if (!isPurePtyTranscriptProvider(this.provider)) return false;
2668
- // A finished turn is idle with nothing pending. A generating/waiting session is
2669
- // mid-turn (the real stall path should evaluate it), so never complete it here.
2670
- if (observedStatus !== 'idle') return false;
2671
- if (this.hasAdapterPendingResponse()) return false;
2672
-
2673
- const taskId = this.completingTurnTaskId();
2674
- // DOUBLE-EMIT guard: this turn's completion already fired — never re-emit.
2675
- if (this.lastEmittedCompletion && this.lastEmittedCompletion.taskId === (taskId ?? '')) {
2676
- return false;
2677
- }
2678
- // The injected task's own turn must have genuinely started (guards against a
2679
- // reused session's stale tail before this task ran).
2680
- if (!this.injectedTaskHasStartedGenerating()) return false;
2681
-
2682
- const turnStartedAt = typeof (this.adapter as any)?.currentTurnStartedAt === 'number'
2683
- ? (this.adapter as any).currentTurnStartedAt as number
2684
- : this.meshTaskInjectedAt || undefined;
2685
- let parsedMessages: unknown;
2686
- try {
2687
- parsedMessages = this.adapter?.getScriptParsedStatus?.()?.messages;
2688
- } catch { parsedMessages = undefined; }
2689
- let finalSummary: string | undefined;
2690
- try {
2691
- finalSummary = this.completionFinalSummary(parsedMessages, turnStartedAt);
2692
- } catch { finalSummary = undefined; }
2693
- // No in-turn final assistant evidence → not a proven turn-end. Let the real
2694
- // stall fire so a genuinely-wedged worker is still surfaced.
2695
- if (!finalSummary) return false;
2696
-
2697
- LOG.warn('CLI', `[${this.type}] reconciling pure-PTY mesh completion from the stall path for session ${this.instanceId} `
2698
- + `task=${taskId ?? '(none)'} — PTY is idle-quiet with an in-turn final assistant message but the completion `
2699
- + `event never fired (pure-PTY provider); emitting it instead of a false monitor:no_progress.`);
2700
- if (this.isMeshWorkerSession()) {
2701
- traceMeshEventStage('fired', this.meshTraceCtx(), 'stall_pure_pty_transcript_completion');
2702
- }
2703
- this.emitGeneratingCompleted({
2704
- chatTitle: '',
2705
- duration: undefined,
2706
- timestamp: Date.now(),
2707
- taskId,
2708
- finalSummary,
2709
- evidenceLevel: 'transcript',
2710
- completionDiagnostic: { source: 'stall_pure_pty_transcript_completion' },
2711
- });
2712
- return true;
2713
2401
  }
2714
2402
 
2715
2403
  /**
@@ -2781,7 +2469,7 @@ export class CliProviderInstance implements ProviderInstance {
2781
2469
  * resolveModal, so a plain turn that never saw an approval always returns false.
2782
2470
  */
2783
2471
  private inApprovalResumeGrace(now = Date.now()): boolean {
2784
- if (!this.isAutonomousMeshSession() || !this.shouldUsePtyAutoApprove()) return false;
2472
+ if (!this.isAutonomousMeshSession() || !this.shouldAutoApprove()) return false;
2785
2473
  const resolvedAt = typeof (this.adapter as any)?.lastApprovalResolvedAt === 'number'
2786
2474
  ? (this.adapter as any).lastApprovalResolvedAt as number
2787
2475
  : 0;
@@ -2898,7 +2586,7 @@ export class CliProviderInstance implements ProviderInstance {
2898
2586
  }
2899
2587
 
2900
2588
  const latestStatus = this.adapter.getStatus({ allowParse: false });
2901
- const latestAutoApproveActive = latestStatus.status === 'waiting_approval' && this.shouldUsePtyAutoApprove();
2589
+ const latestAutoApproveActive = latestStatus.status === 'waiting_approval' && this.shouldAutoApprove();
2902
2590
  const latestVisibleStatus = latestAutoApproveActive || this.autoApproveBusy ? 'generating' : latestStatus.status;
2903
2591
  LOG.debug('CLI', `[${this.type}] flush attempt: adapterStatus=${latestStatus.status} latestVisible=${latestVisibleStatus} generatingStartedAt=${this.generatingStartedAt} isWaitingForResponse=${!!(this.adapter as any)?.isWaitingForResponse} hasPartial=${!!this.adapter.getPartialResponse?.()}`);
2904
2592
  if (latestVisibleStatus !== 'idle') {
@@ -3063,79 +2751,6 @@ export class CliProviderInstance implements ProviderInstance {
3063
2751
  this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
3064
2752
  return;
3065
2753
  }
3066
- // (CANON-C EARLY-EMIT FLOOR) The transcript-evidence gate would otherwise fire its
3067
- // decoupled-immediate completion at ANY waitedMs — including the ~13s a codex PLAIN
3068
- // terminal `missing_final_assistant` block reaches on the first completion-gate poll,
3069
- // with NO final assistant ever observed. That stamps a weak evidenceLevel=insufficient
3070
- // completion the coordinator marks reviewRecommended but does not auto-hold, racing the
3071
- // transcript before it can land and pre-empting the 180s mesh-worker stall watchdog.
3072
- // Impose a minimum dwell — but ONLY for the no-external-transcript case
3073
- // (noExternalTranscriptSource): a PTY-parsed provider has no separate transcript that
3074
- // will land to upgrade the weak emit, so firing immediately is a pure timing guess. A
3075
- // native-source block (claude-cli external-native) is deliberately NOT floored: its
3076
- // transcript legitimately trails the idle transition by a write and the CANON-C
3077
- // immediate emit is correct (upgraded by the reconcile). Keep holding (re-probe each
3078
- // retry — the block clears for a GENUINE emit the moment the assistant turn lands) until
3079
- // either the floor is met or the 30s cap force-releases the weak completion below.
3080
- // Scoped to mesh worker sessions (the only place allowTimeout is set), so interactive
3081
- // completion timing is untouched.
3082
- if (isTranscriptEvidenceGate
3083
- && blockReason === 'missing_final_assistant'
3084
- && block.noExternalTranscriptSource === true
3085
- && waitedMs < CANON_C_MISSING_ASSISTANT_MIN_ELAPSED_MS) {
3086
- if (pending.loggedBlockReason !== 'canon_c_min_elapsed_floor') {
3087
- LOG.info('CLI', `[${this.type}] holding CANON-C decoupled emit until min-elapsed floor (waitedMs=${waitedMs} floor=${CANON_C_MISSING_ASSISTANT_MIN_ELAPSED_MS}); no final assistant yet (${blockReason})`);
3088
- if (this.isMeshWorkerSession()) {
3089
- traceMeshEventDrop('completion_gate_hold', this.meshTraceCtx(), `canon_c_min_elapsed_floor waited=${waitedMs}ms`);
3090
- }
3091
- if (this.completionTraceOn()) this.recordCompletionGateTrace('hold', {
3092
- blockReason,
3093
- latestVisibleStatus,
3094
- terminal: block.terminal === true,
3095
- canonCMinElapsedFloor: true,
3096
- waitedMs,
3097
- });
3098
- pending.loggedBlockReason = 'canon_c_min_elapsed_floor';
3099
- }
3100
- this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
3101
- return;
3102
- }
3103
- // (ANTIGRAVITY-30S-CAP-PREMATURE) The 30s cap has been reached for an antigravity
3104
- // `holdForTranscript` block. The cap releases on ELAPSED TIME, not proof-of-idle — but
3105
- // antigravity is native-source (assistant answer in native-history) with a PTY-derived
3106
- // idle verdict, so the transcript's final assistant bubble can legitimately land past 30s
3107
- // on a long turn. Releasing here would fire a premature weak completed/idle to the
3108
- // coordinator WHILE THE PTY IS STILL GENERATING (the live-reproduced defect). Gate the
3109
- // release on the PTY being genuinely quiet: if it is still active (adapter pending
3110
- // response OR raw output within ANTIGRAVITY_HOLD_QUIET_DWELL_MS) and we are under the
3111
- // absolute ANTIGRAVITY_HOLD_HARD_CAP_MS bound, KEEP HOLDING (re-probe next retry — the
3112
- // transcript branch above clears the block for a genuine emit once the answer lands).
3113
- // A genuinely quiescent tool-only turn (no assistant bubble, PTY stable) falls through
3114
- // to the existing weak force-emit; a runaway PTY that never quiets releases at the hard
3115
- // cap so it cannot wedge forever. Scoped to antigravity holdForTranscript so
3116
- // claude-cli/codex-cli completion timing is unchanged.
3117
- if (this.type === 'antigravity-cli'
3118
- && block.holdForTranscript === true
3119
- && waitedMs < ANTIGRAVITY_HOLD_HARD_CAP_MS
3120
- && this.antigravityHoldPtyStillActive()) {
3121
- if (pending.loggedBlockReason !== 'antigravity_hold_pty_active') {
3122
- LOG.info('CLI', `[${this.type}] 30s cap reached but PTY still generating; holding antigravity completion past cap (waitedMs=${waitedMs} hardCap=${ANTIGRAVITY_HOLD_HARD_CAP_MS}) (${blockReason})`);
3123
- if (this.isMeshWorkerSession()) {
3124
- traceMeshEventDrop('completion_gate_hold', this.meshTraceCtx(), `antigravity_hold_pty_active waited=${waitedMs}ms`);
3125
- }
3126
- if (this.completionTraceOn()) this.recordCompletionGateTrace('hold', {
3127
- blockReason,
3128
- latestVisibleStatus,
3129
- terminal: block.terminal === true,
3130
- holdForTranscript: true,
3131
- antigravityPtyStillActive: true,
3132
- waitedMs,
3133
- });
3134
- pending.loggedBlockReason = 'antigravity_hold_pty_active';
3135
- }
3136
- this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
3137
- return;
3138
- }
3139
2754
  const emittedAfterFinalizationTimeout = waitedMs >= COMPLETED_FINALIZATION_MAX_WAIT_MS;
3140
2755
  const completionDiagnostic = this.buildCompletedFinalizationDiagnostic({
3141
2756
  blockReason,
@@ -3253,10 +2868,6 @@ export class CliProviderInstance implements ProviderInstance {
3253
2868
  if (summary) {
3254
2869
  this.lastCompletionSummary = { content: summary, receivedAt: opts.timestamp };
3255
2870
  }
3256
- // KIMI-MESH-COMPLETION-EMIT (axis 2, double-emit guard): record that THIS turn's
3257
- // completion has now been emitted, keyed by its taskId, so the pre-cleanup
3258
- // completion flush never fires a duplicate for the same turn.
3259
- this.lastEmittedCompletion = { taskId: typeof opts.taskId === 'string' ? opts.taskId : '', at: Date.now() };
3260
2871
  this.pushEvent({
3261
2872
  event: 'agent:generating_completed',
3262
2873
  chatTitle: opts.chatTitle,
@@ -3301,7 +2912,7 @@ export class CliProviderInstance implements ProviderInstance {
3301
2912
  private stabilizeFlappingApprovalStatus(adapterStatus: any, now = Date.now()): any {
3302
2913
  // Only autonomous auto-approving mesh sessions are subject to the delegated flap;
3303
2914
  // never overlay for attended/foreground/non-mesh sessions.
3304
- if (!this.isAutonomousMeshSession() || !this.shouldUsePtyAutoApprove()) return adapterStatus;
2915
+ if (!this.isAutonomousMeshSession() || !this.shouldAutoApprove()) return adapterStatus;
3305
2916
 
3306
2917
  const rawStatus = adapterStatus?.status;
3307
2918
  const resolvedAt = typeof (this.adapter as any)?.lastApprovalResolvedAt === 'number'
@@ -3348,13 +2959,6 @@ export class CliProviderInstance implements ProviderInstance {
3348
2959
  }
3349
2960
 
3350
2961
  private maybeAutoApproveStatus(adapterStatus: any, now = Date.now()): boolean {
3351
- // launch-args modes grant approval in the CLI process itself and therefore
3352
- // must never enter the PTY modal parser/fire/settle/mask/nudge subsystem.
3353
- // post-boot-command is reserved and resolves inactive in v1.
3354
- if (!this.shouldUsePtyAutoApprove()) {
3355
- this.resetPtyAutoApproveState();
3356
- return false;
3357
- }
3358
2962
  // Manual-attendance suppression (provider-common): when a human is
3359
2963
  // Manual-attendance suppression (provider-common): when a human is
3360
2964
  // actively driving this session from the dashboard, hold auto-approve so
@@ -3366,7 +2970,7 @@ export class CliProviderInstance implements ProviderInstance {
3366
2970
  // would otherwise never re-drive this decision. Background mesh workers
3367
2971
  // are never attended, so their delegated auto-approve is untouched.
3368
2972
  if (adapterStatus?.status === 'waiting_approval'
3369
- && this.shouldUsePtyAutoApprove()
2973
+ && this.shouldAutoApprove()
3370
2974
  && this.manualAttendance.isAttended(now)) {
3371
2975
  this.lastAutoApprovalSignature = '';
3372
2976
  this.pendingAutoApprovalSignature = '';
@@ -3384,7 +2988,7 @@ export class CliProviderInstance implements ProviderInstance {
3384
2988
  }, this.manualAttendance.remainingMs(now) + 20);
3385
2989
  return false;
3386
2990
  }
3387
- const autoApproveActive = adapterStatus?.status === 'waiting_approval' && this.shouldUsePtyAutoApprove();
2991
+ const autoApproveActive = adapterStatus?.status === 'waiting_approval' && this.shouldAutoApprove();
3388
2992
  // Guard re-entry: onStatusChange/getState can observe the same modal multiple
3389
2993
  // times while the PTY absorbs the approval key. Without this flag, repeated
3390
2994
  // snapshots would write stray keys into the input once the modal dismisses.
@@ -4281,68 +3885,6 @@ export class CliProviderInstance implements ProviderInstance {
4281
3885
  this.lastStatus = newStatus;
4282
3886
  }
4283
3887
 
4284
- // INTERACTIVE-QUESTION-EMIT: fire a coordinator/push-worthy notification when the
4285
- // session ENTERS an AskUserQuestion / waiting_choice state. This is orthogonal to
4286
- // the status-change block above: an AskUserQuestion prompt is surfaced only as a
4287
- // display-only `waiting_choice` overlay in getState() (mirrored here off
4288
- // adapterStatus.activeInteractivePrompt, the exact signal getState uses), while
4289
- // the raw adapter status stays idle/generating — so none of the status-keyed
4290
- // arms above ever emit an agent:* event for it and the owner gets no web-push.
4291
- //
4292
- // MESH-QUESTION-ANSWER-PATH (mission f1d25e11): this used to REUSE
4293
- // agent:waiting_approval (question as modalMessage, choice labels as modalButtons).
4294
- // That mis-classified a multi-choice QUESTION as an approval — the coordinator saw
4295
- // a task_approval_needed ledger row and tried mesh_approve, which cannot answer a
4296
- // question (it resolves a yes/no modal). We now emit a DISTINCT
4297
- // agent:waiting_choice event carrying the FULL InteractivePrompt payload
4298
- // (promptId + every question's header/question/multiSelect and each option's
4299
- // label/description) so the coordinator can render the choices and answer with the
4300
- // dedicated mesh_answer_question tool. The rich payload survives the cross-machine
4301
- // relay (see buildRelayMetadataEvent) so a REMOTE worker's question reaches the
4302
- // coordinator with its options intact.
4303
- //
4304
- // We keep modalMessage/modalButtons on the event too so the existing server
4305
- // web-push path (which reads those two fields) still fires with no cloud change —
4306
- // but the payload's `interactivePrompt` is the authoritative, structured signal.
4307
- //
4308
- // Edge-triggered: emit exactly once on entry, keyed on promptId + question text,
4309
- // and reset the key when the prompt clears so a fresh prompt re-fires and a later
4310
- // real completion still flows through the idle arm normally. We do NOT emit when
4311
- // the session is ALSO in a genuine approval state (newStatus === 'waiting_approval'):
4312
- // that arm already emits agent:waiting_approval. A question (waiting_choice) and a
4313
- // real approval (waiting_approval) therefore NEVER both fire for the same tick —
4314
- // the approval arm wins and this arm yields (owner requirement: the two are
4315
- // mutually exclusive).
4316
- const interactivePrompt = adapterStatus.activeInteractivePrompt ?? null;
4317
- if (interactivePrompt && newStatus !== 'waiting_approval') {
4318
- const firstQuestion = interactivePrompt.questions?.[0];
4319
- const promptKey = `${interactivePrompt.promptId}::${firstQuestion?.question ?? ''}`;
4320
- if (promptKey !== this.lastInteractivePromptEventKey) {
4321
- this.lastInteractivePromptEventKey = promptKey;
4322
- const modalMessage = firstQuestion
4323
- ? (firstQuestion.header
4324
- ? `${firstQuestion.header}: ${firstQuestion.question}`
4325
- : firstQuestion.question)
4326
- : undefined;
4327
- const modalButtons = firstQuestion?.options?.map((option: { label: string }) => option.label);
4328
- this.pushEvent({
4329
- event: 'agent:waiting_choice', chatTitle, timestamp: now,
4330
- // Structured, authoritative payload — the coordinator renders these and
4331
- // answers via mesh_answer_question. Carried whole (all questions, all
4332
- // options) so multi-question / multi-select prompts round-trip fully.
4333
- interactivePrompt,
4334
- promptId: interactivePrompt.promptId,
4335
- multiSelect: firstQuestion?.multiSelect === true,
4336
- // Push-notification-friendly projection (server push path reads these).
4337
- modalMessage,
4338
- modalButtons,
4339
- });
4340
- }
4341
- } else if (!interactivePrompt && this.lastInteractivePromptEventKey) {
4342
- // Prompt answered / gone — reset so the next AskUserQuestion re-fires.
4343
- this.lastInteractivePromptEventKey = '';
4344
- }
4345
-
4346
3888
  // GENERATING-BOUNDARY idle-stayed collapse (R4b): the starting→idle
4347
3889
  // fast-collapse arm above only fires when the FIRST turn itself drives the
4348
3890
  // starting→idle transition. When the launch settle already drained
@@ -4660,37 +4202,15 @@ export class CliProviderInstance implements ProviderInstance {
4660
4202
  get cliType(): string { return this.type; }
4661
4203
  get cliName(): string { return this.provider.name; }
4662
4204
 
4663
- private resolveAutoApproveMode(): ResolvedAutoApproveMode {
4664
- return resolveProviderAutoApproveMode(this.provider, this.settings);
4665
- }
4666
-
4667
- /** Legacy boolean view retained for internal/test compatibility. */
4668
4205
  private shouldAutoApprove(): boolean {
4669
- return this.resolveAutoApproveMode().active;
4670
- }
4671
-
4672
- private shouldUsePtyAutoApprove(): boolean {
4673
- const resolved = this.resolveAutoApproveMode();
4674
- return this.shouldAutoApprove() && resolved.strategy === 'pty-parse-default';
4675
- }
4676
-
4677
- private resetPtyAutoApproveState(): void {
4678
- this.lastAutoApprovalSignature = '';
4679
- this.pendingAutoApprovalSignature = '';
4680
- this.pendingAutoApprovalSince = 0;
4681
- this.autoApproveInactiveSince = 0;
4682
- this.autoApproveMaskSince = 0;
4683
- this.stalledApprovalNudgeEpisode = 0;
4684
- this.autoApproveLastModalSeenAt = 0;
4685
- this.autoApproveBusy = false;
4686
- if (this.autoApproveSettleTimer) {
4687
- clearTimeout(this.autoApproveSettleTimer);
4688
- this.autoApproveSettleTimer = null;
4206
+ if (typeof this.settings.autoApprove === 'boolean') {
4207
+ return this.settings.autoApprove;
4689
4208
  }
4690
- if (this.autoApproveBusyTimer) {
4691
- clearTimeout(this.autoApproveBusyTimer);
4692
- this.autoApproveBusyTimer = null;
4209
+ const providerDefault = this.provider.settings?.autoApprove?.default;
4210
+ if (typeof providerDefault === 'boolean') {
4211
+ return providerDefault;
4693
4212
  }
4213
+ return false;
4694
4214
  }
4695
4215
 
4696
4216
  /** @see ProviderInstance.noteManualInteraction */
@@ -4717,7 +4237,7 @@ export class CliProviderInstance implements ProviderInstance {
4717
4237
  */
4718
4238
  private autoApproveEffectivelyActive(status: string | undefined, now = Date.now()): boolean {
4719
4239
  return status === 'waiting_approval'
4720
- && this.shouldUsePtyAutoApprove()
4240
+ && this.shouldAutoApprove()
4721
4241
  && !this.manualAttendance.isAttended(now);
4722
4242
  }
4723
4243
 
@@ -4729,8 +4249,7 @@ export class CliProviderInstance implements ProviderInstance {
4729
4249
  // maybeAutoApproveStatus (driven by getState + the recheck timer during a waiting episode);
4730
4250
  // this read is side-effect-free so getStatusMetadata can consult it too.
4731
4251
  private autoApproveMaskStalled(now = Date.now()): boolean {
4732
- return this.shouldUsePtyAutoApprove()
4733
- && this.autoApproveMaskSince > 0
4252
+ return this.autoApproveMaskSince > 0
4734
4253
  && now - this.autoApproveMaskSince > CliProviderInstance.AUTO_APPROVE_MASK_STALL_MS;
4735
4254
  }
4736
4255
 
@@ -4756,7 +4275,6 @@ export class CliProviderInstance implements ProviderInstance {
4756
4275
  * mask-clock value) so a modal that flaps between parsed/unparsed states is announced once.
4757
4276
  */
4758
4277
  private maybeEmitStalledApprovalNudge(adapterStatus: any, now: number): void {
4759
- if (!this.shouldUsePtyAutoApprove()) return;
4760
4278
  if (!this.isMeshWorkerSession()) return;
4761
4279
  if (adapterStatus?.status !== 'waiting_approval') return;
4762
4280
  if (!this.autoApproveMaskStalled(now)) return;