@adhdev/daemon-core 0.9.82-rc.121 → 0.9.82-rc.122

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.
@@ -34,7 +34,10 @@ export interface CliAdapter {
34
34
  workingDir: string;
35
35
  _acpInstance?: AcpAdapterHandle;
36
36
  spawn(): Promise<void>;
37
- sendMessage(text: string): Promise<void>;
37
+ sendMessage(text: string, options?: {
38
+ force?: boolean;
39
+ }): Promise<void>;
40
+ forceSendMessage?(text: string): Promise<void>;
38
41
  getStatus(): CliAdapterStatus;
39
42
  getScriptParsedStatus?(): unknown;
40
43
  getDebugSnapshot?(): unknown;
@@ -214,7 +214,10 @@ export declare class ProviderCliAdapter implements CliAdapter {
214
214
  private submitSendKey;
215
215
  private submitImmediatePrompt;
216
216
  private waitForEchoAndSubmit;
217
- sendMessage(text: string): Promise<void>;
217
+ sendMessage(text: string, options?: {
218
+ force?: boolean;
219
+ }): Promise<void>;
220
+ forceSendMessage(text: string): Promise<void>;
218
221
  private enqueuePendingOutboundMessage;
219
222
  private shouldQueuePendingOutboundMessage;
220
223
  private schedulePendingOutboundFlush;
package/dist/index.js CHANGED
@@ -2563,7 +2563,7 @@ function hasPendingRefineTerminalEventDuplicate(event) {
2563
2563
  if (!REFINE_TERMINAL_EVENTS.has(event.event)) return false;
2564
2564
  const jobId = readRefineJobId(event);
2565
2565
  if (!jobId) return false;
2566
- return getPendingMeshCoordinatorEvents(event.meshId).some(
2566
+ return readPendingMeshCoordinatorEventsFromDisk(event.meshId).some(
2567
2567
  (pending) => pending.event === event.event && readRefineJobId(pending) === jobId
2568
2568
  );
2569
2569
  }
@@ -2588,12 +2588,91 @@ function buildPendingEventFingerprint(event) {
2588
2588
  function hasPendingCoordinatorEventDuplicate(event) {
2589
2589
  const fingerprint = buildPendingEventFingerprint(event);
2590
2590
  if (!fingerprint.trim()) return false;
2591
- return getPendingMeshCoordinatorEvents(event.meshId).some((pending) => buildPendingEventFingerprint(pending) === fingerprint);
2591
+ return readPendingMeshCoordinatorEventsFromDisk(event.meshId).some((pending) => buildPendingEventFingerprint(pending) === fingerprint);
2592
2592
  }
2593
2593
  function getPendingEventsPath(meshId) {
2594
2594
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
2595
2595
  return (0, import_path7.join)(getLedgerDir(), `${safe}.pending-events.jsonl`);
2596
2596
  }
2597
+ function readPendingMeshCoordinatorEventsFromDisk(meshId) {
2598
+ if (!meshId) return [];
2599
+ const path28 = getPendingEventsPath(meshId);
2600
+ if (!(0, import_fs8.existsSync)(path28)) return [];
2601
+ try {
2602
+ const raw = (0, import_fs8.readFileSync)(path28, "utf-8");
2603
+ return raw.split("\n").filter(Boolean).flatMap((line) => {
2604
+ try {
2605
+ return [JSON.parse(line)];
2606
+ } catch {
2607
+ return [];
2608
+ }
2609
+ });
2610
+ } catch {
2611
+ return [];
2612
+ }
2613
+ }
2614
+ function refineTerminalEventFromLedger(meshId, pending) {
2615
+ const acceptedJobIds = new Set(
2616
+ pending.filter((event) => event.event === "refine:accepted").map((event) => readRefineJobId(event)).filter(Boolean)
2617
+ );
2618
+ if (acceptedJobIds.size === 0) return [];
2619
+ const existingTerminalJobIds = new Set(
2620
+ pending.filter((event) => REFINE_TERMINAL_EVENTS.has(event.event)).map((event) => `${event.event}:${readRefineJobId(event)}`).filter((value) => !value.endsWith(":"))
2621
+ );
2622
+ const backfilled = [];
2623
+ const entries = readLedgerEntries(meshId);
2624
+ for (let i = entries.length - 1; i >= 0; i--) {
2625
+ const entry = entries[i];
2626
+ if (entry.kind !== "task_completed" && entry.kind !== "task_failed") continue;
2627
+ const payload = readRecord2(entry.payload);
2628
+ if (payload?.source !== "refine_mesh_node_async_job") continue;
2629
+ const refineJob = readRecord2(payload.refineJob);
2630
+ const jobId = readNonEmptyString2(refineJob?.jobId);
2631
+ if (!jobId || !acceptedJobIds.has(jobId)) continue;
2632
+ const eventName = entry.kind === "task_completed" ? "refine:completed" : "refine:failed";
2633
+ if (existingTerminalJobIds.has(`${eventName}:${jobId}`)) continue;
2634
+ existingTerminalJobIds.add(`${eventName}:${jobId}`);
2635
+ const result = readRecord2(payload.result);
2636
+ const metadataEvent = {
2637
+ source: "refine_mesh_node_async_job",
2638
+ jobId,
2639
+ interactionId: readNonEmptyString2(refineJob?.interactionId),
2640
+ meshId,
2641
+ nodeId: readNonEmptyString2(refineJob?.nodeId) || entry.nodeId,
2642
+ targetDaemonId: readNonEmptyString2(refineJob?.targetDaemonId),
2643
+ workspace: readNonEmptyString2(refineJob?.workspace),
2644
+ status: eventName === "refine:completed" ? "completed" : "failed",
2645
+ startedAt: readNonEmptyString2(refineJob?.startedAt),
2646
+ completedAt: readNonEmptyString2(refineJob?.completedAt) || entry.timestamp,
2647
+ retryOfJobId: readNonEmptyString2(refineJob?.retryOfJobId) || readNonEmptyString2(payload.retryOfJobId),
2648
+ ...result ? { result } : {}
2649
+ };
2650
+ backfilled.push({
2651
+ event: eventName,
2652
+ meshId,
2653
+ nodeLabel: readNonEmptyString2(refineJob?.nodeId) || entry.nodeId || "refine job",
2654
+ nodeId: readNonEmptyString2(refineJob?.nodeId) || entry.nodeId,
2655
+ workspace: readNonEmptyString2(refineJob?.workspace),
2656
+ metadataEvent,
2657
+ coordinatorMessage: buildMeshSystemMessage({
2658
+ event: eventName,
2659
+ nodeLabel: readNonEmptyString2(refineJob?.nodeId) || entry.nodeId || "refine job",
2660
+ metadataEvent
2661
+ }),
2662
+ queuedAt: Date.now()
2663
+ });
2664
+ }
2665
+ return backfilled.reverse();
2666
+ }
2667
+ function reconcilePendingMeshCoordinatorEvents(meshId, events) {
2668
+ const backfilled = refineTerminalEventFromLedger(meshId, events);
2669
+ if (backfilled.length === 0) return events;
2670
+ const terminalJobIds = new Set(backfilled.map((event) => readRefineJobId(event)).filter(Boolean));
2671
+ return [
2672
+ ...events.filter((event) => !(event.event === "refine:accepted" && terminalJobIds.has(readRefineJobId(event)))),
2673
+ ...backfilled
2674
+ ];
2675
+ }
2597
2676
  function queuePendingMeshCoordinatorEvent(event) {
2598
2677
  try {
2599
2678
  if (hasPendingRefineTerminalEventDuplicate(event)) {
@@ -2616,38 +2695,19 @@ function drainPendingMeshCoordinatorEvents(meshId) {
2616
2695
  const path28 = getPendingEventsPath(meshId);
2617
2696
  if (!(0, import_fs8.existsSync)(path28)) return [];
2618
2697
  try {
2619
- const raw = (0, import_fs8.readFileSync)(path28, "utf-8");
2698
+ const parsed = readPendingMeshCoordinatorEventsFromDisk(meshId);
2620
2699
  try {
2621
2700
  (0, import_fs8.unlinkSync)(path28);
2622
2701
  } catch {
2623
2702
  }
2624
- return raw.split("\n").filter(Boolean).flatMap((line) => {
2625
- try {
2626
- return [JSON.parse(line)];
2627
- } catch {
2628
- return [];
2629
- }
2630
- });
2703
+ return reconcilePendingMeshCoordinatorEvents(meshId, parsed);
2631
2704
  } catch {
2632
2705
  return [];
2633
2706
  }
2634
2707
  }
2635
2708
  function getPendingMeshCoordinatorEvents(meshId) {
2636
2709
  if (!meshId) return [];
2637
- const path28 = getPendingEventsPath(meshId);
2638
- if (!(0, import_fs8.existsSync)(path28)) return [];
2639
- try {
2640
- const raw = (0, import_fs8.readFileSync)(path28, "utf-8");
2641
- return raw.split("\n").filter(Boolean).flatMap((line) => {
2642
- try {
2643
- return [JSON.parse(line)];
2644
- } catch {
2645
- return [];
2646
- }
2647
- });
2648
- } catch {
2649
- return [];
2650
- }
2710
+ return reconcilePendingMeshCoordinatorEvents(meshId, readPendingMeshCoordinatorEventsFromDisk(meshId));
2651
2711
  }
2652
2712
  function clearPendingMeshCoordinatorEvents(meshId) {
2653
2713
  if (!meshId) return;
@@ -2752,6 +2812,62 @@ function isDuplicateRefineTerminalEvent(meshId, eventName, metadataEvent) {
2752
2812
  recentCompletionFingerprints.set(fingerprint, now);
2753
2813
  return false;
2754
2814
  }
2815
+ function findRecentTerminalLedgerEvidence(args) {
2816
+ if (!args.sessionId && !args.nodeId) return null;
2817
+ const entries = readLedgerEntries(args.meshId);
2818
+ for (let i = entries.length - 1; i >= 0; i--) {
2819
+ const entry = entries[i];
2820
+ if (entry.kind !== "task_completed" && entry.kind !== "task_failed" && entry.kind !== "task_stalled") continue;
2821
+ if (args.sessionId && entry.sessionId === args.sessionId) {
2822
+ return { kind: entry.kind, payload: entry.payload || {}, timestamp: entry.timestamp };
2823
+ }
2824
+ if (!args.sessionId && args.nodeId && entry.nodeId === args.nodeId) {
2825
+ return { kind: entry.kind, payload: entry.payload || {}, timestamp: entry.timestamp };
2826
+ }
2827
+ }
2828
+ return null;
2829
+ }
2830
+ function buildLongGeneratingCompletionReconciliation(args) {
2831
+ const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
2832
+ const nodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
2833
+ const providerType = readNonEmptyString2(args.metadataEvent.providerType);
2834
+ const providerSessionId = readNonEmptyString2(args.metadataEvent.providerSessionId);
2835
+ const workerResult = readWorkerResultMetadata(args.metadataEvent);
2836
+ const completionDiagnostic = readRecord2(args.metadataEvent.completionDiagnostic);
2837
+ const finalSummary = readNonEmptyString2(args.metadataEvent.finalSummary);
2838
+ const status = readNonEmptyString2(args.metadataEvent.status).toLowerCase();
2839
+ const explicitCompletionEvidence = Boolean(
2840
+ finalSummary || workerResult || completionDiagnostic?.finalAssistantPresent === true || status === "idle" || status === "ready" || status === "completed"
2841
+ );
2842
+ if (explicitCompletionEvidence) {
2843
+ return {
2844
+ ...args.metadataEvent,
2845
+ targetSessionId: sessionId,
2846
+ providerType,
2847
+ providerSessionId,
2848
+ finalSummary,
2849
+ source: "long_generating_reconciliation",
2850
+ reconciledFromEvent: "monitor:long_generating",
2851
+ timestamp: args.metadataEvent.timestamp ?? Date.now(),
2852
+ completionDiagnostic: {
2853
+ ...completionDiagnostic || {},
2854
+ reconciliationReason: "provider_completion_evidence"
2855
+ }
2856
+ };
2857
+ }
2858
+ const terminal = findRecentTerminalLedgerEvidence({
2859
+ meshId: args.meshId,
2860
+ sessionId: sessionId || void 0,
2861
+ nodeId: nodeId || void 0
2862
+ });
2863
+ if (!terminal) return null;
2864
+ return {
2865
+ ...args.metadataEvent,
2866
+ source: "long_generating_terminal_ledger_suppression",
2867
+ terminalLedgerKind: terminal.kind,
2868
+ terminalLedgerAt: terminal.timestamp
2869
+ };
2870
+ }
2755
2871
  function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
2756
2872
  const task = claimNextTask(meshId, nodeId, sessionId);
2757
2873
  if (!task) {
@@ -2845,6 +2961,9 @@ function activeAssignedCount(meshId) {
2845
2961
  function nodeHasActiveAssignment(meshId, nodeId) {
2846
2962
  return getQueue(meshId, { status: ["assigned"] }).some((task) => task.assignedNodeId === nodeId);
2847
2963
  }
2964
+ function sessionHasActiveAssignment(meshId, sessionId) {
2965
+ return getQueue(meshId, { status: ["assigned"] }).some((task) => task.assignedSessionId === sessionId);
2966
+ }
2848
2967
  function liveSessionCountForNode(components, meshId, nodeId) {
2849
2968
  return components.instanceManager.getByCategory("cli").filter((inst) => {
2850
2969
  const state = inst.getState();
@@ -3055,6 +3174,9 @@ async function triggerMeshQueue(components, meshId) {
3055
3174
  function buildMeshSystemMessage(args) {
3056
3175
  const metadata = formatCompletionMetadata(args.metadataEvent);
3057
3176
  if (args.event === "agent:generating_completed") {
3177
+ if (args.metadataEvent.source === "long_generating_reconciliation") {
3178
+ return `[System] ${args.nodeLabel} already has completion evidence${metadata}. The long-generating monitor reconciled the terminal handoff and marked the session complete; wait for the queued completion event/status refresh before doing any manual transcript check.`;
3179
+ }
3058
3180
  return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path; use mesh_read_chat once to review its final progress, but do not poll repeatedly.`;
3059
3181
  }
3060
3182
  if (args.event === "agent:waiting_approval") {
@@ -3092,7 +3214,7 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
3092
3214
  return `[System] ${args.nodeLabel} has stopped${metadata}. Use mesh_read_chat once if you need to inspect its last output.`;
3093
3215
  }
3094
3216
  if (args.event === "monitor:long_generating") {
3095
- return `[System] ${args.nodeLabel} has been generating for a long time${metadata}. Use mesh_read_chat once for a status check, but do not poll repeatedly.`;
3217
+ return `[System] ${args.nodeLabel} is still reported as generating after a long interval${metadata}. Wait for pendingCoordinatorEvents or a completion/status event; if the user explicitly asks for status, make one bounded status check and then wait again.`;
3096
3218
  }
3097
3219
  if (args.event === "refine:accepted") {
3098
3220
  const jobId = readRefineJobId({ metadataEvent: args.metadataEvent });
@@ -3170,12 +3292,54 @@ function injectMeshSystemMessage(components, args) {
3170
3292
  LOG.info("MeshEvents", `Suppressed ${args.event} for intentionally cleanup-stopped session ${eventSessionId || "(unknown session)"}`);
3171
3293
  return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
3172
3294
  }
3295
+ if (args.event === "monitor:long_generating") {
3296
+ const reconciledCompletion = buildLongGeneratingCompletionReconciliation({
3297
+ meshId: args.meshId,
3298
+ nodeId: args.nodeId,
3299
+ nodeLabel: args.nodeLabel,
3300
+ metadataEvent: args.metadataEvent,
3301
+ sourceInstanceId: args.sourceInstanceId
3302
+ });
3303
+ if (reconciledCompletion?.source === "long_generating_reconciliation") {
3304
+ LOG.info("MeshEvents", `Reconciled long-generating monitor to completion for session ${eventSessionId || "(unknown session)"}`);
3305
+ return injectMeshSystemMessage(components, {
3306
+ ...args,
3307
+ event: "agent:generating_completed",
3308
+ metadataEvent: reconciledCompletion
3309
+ });
3310
+ }
3311
+ if (reconciledCompletion?.source === "long_generating_terminal_ledger_suppression") {
3312
+ LOG.info("MeshEvents", `Suppressed long-generating monitor because terminal ledger evidence already exists for session ${eventSessionId || "(unknown session)"}`);
3313
+ return {
3314
+ success: true,
3315
+ forwarded: 0,
3316
+ suppressed: true,
3317
+ terminalLedgerEvidence: true,
3318
+ terminalLedgerKind: reconciledCompletion.terminalLedgerKind
3319
+ };
3320
+ }
3321
+ }
3173
3322
  if (isDuplicateRefineTerminalEvent(args.meshId, args.event, args.metadataEvent)) {
3174
3323
  LOG.info("MeshEvents", `Suppressed duplicate ${args.event} for refine job ${readRefineJobId({ metadataEvent: args.metadataEvent })}`);
3175
3324
  return { success: true, forwarded: 0, suppressed: true, duplicateRefineTerminalEvent: true };
3176
3325
  }
3177
3326
  const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
3178
3327
  if (args.event === "agent:generating_completed" && eventSessionId) {
3328
+ const terminal = findRecentTerminalLedgerEvidence({
3329
+ meshId: args.meshId,
3330
+ sessionId: eventSessionId,
3331
+ nodeId: eventNodeId || void 0
3332
+ });
3333
+ if (terminal?.kind === "task_completed" && !sessionHasActiveAssignment(args.meshId, eventSessionId)) {
3334
+ const terminalProviderSessionId = readNonEmptyString2(terminal.payload.providerSessionId);
3335
+ const terminalFinalSummary = readNonEmptyString2(terminal.payload.finalSummary);
3336
+ const eventProviderSessionId = readNonEmptyString2(args.metadataEvent.providerSessionId);
3337
+ const eventFinalSummary = readNonEmptyString2(args.metadataEvent.finalSummary);
3338
+ if (terminalProviderSessionId && terminalProviderSessionId === eventProviderSessionId || terminalFinalSummary && terminalFinalSummary === eventFinalSummary || args.metadataEvent.source === "long_generating_reconciliation") {
3339
+ LOG.info("MeshEvents", `Suppressed duplicate completion with existing terminal ledger evidence for mesh ${args.meshId} session ${eventSessionId}`);
3340
+ return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true, terminalLedgerEvidence: true };
3341
+ }
3342
+ }
3179
3343
  const duplicateCompletion = isDuplicateMeshCompletionEvent({
3180
3344
  meshId: args.meshId,
3181
3345
  event: args.event,
@@ -3452,6 +3616,10 @@ function handleMeshForwardEvent(components, payload) {
3452
3616
  completedAt: readNonEmptyString2(payload.completedAt),
3453
3617
  retryOfJobId: readNonEmptyString2(payload.retryOfJobId),
3454
3618
  ...payload.result && typeof payload.result === "object" && !Array.isArray(payload.result) ? { result: payload.result } : {},
3619
+ ...payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? { completionDiagnostic: payload.completionDiagnostic } : {},
3620
+ ...payload.workerResult && typeof payload.workerResult === "object" && !Array.isArray(payload.workerResult) ? { workerResult: payload.workerResult } : {},
3621
+ ...payload.meshWorkerResult && typeof payload.meshWorkerResult === "object" && !Array.isArray(payload.meshWorkerResult) ? { meshWorkerResult: payload.meshWorkerResult } : {},
3622
+ ...payload.structuredResult && typeof payload.structuredResult === "object" && !Array.isArray(payload.structuredResult) ? { structuredResult: payload.structuredResult } : {},
3455
3623
  ...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {},
3456
3624
  intentional: payload.intentional === true,
3457
3625
  intentionalStop: payload.intentionalStop === true,
@@ -6351,9 +6519,27 @@ ${lastSnapshot}`;
6351
6519
  nextScreenChangeAt
6352
6520
  ), 50);
6353
6521
  }
6354
- async sendMessage(text) {
6522
+ async sendMessage(text, options = {}) {
6523
+ if (options.force === true) {
6524
+ await this.forceSendMessage(text);
6525
+ return;
6526
+ }
6355
6527
  await this.sendMessageNow(text, true);
6356
6528
  }
6529
+ async forceSendMessage(text) {
6530
+ if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
6531
+ const content = String(text || "");
6532
+ if (!content.trim()) return;
6533
+ this.recordTrace("force_send_message", {
6534
+ text: summarizeCliTraceText(content, 500),
6535
+ status: this.currentStatus,
6536
+ isWaitingForResponse: this.isWaitingForResponse,
6537
+ queueLength: this.pendingOutboundQueue.length
6538
+ });
6539
+ LOG.info("CLI", `[${this.cliType}] force-sending prompt while status=${this.currentStatus}`);
6540
+ await this.writeToPty(content + this.sendKey);
6541
+ this.onStatusChange?.();
6542
+ }
6357
6543
  enqueuePendingOutboundMessage(text, reason) {
6358
6544
  const content = String(text || "");
6359
6545
  const duplicate = this.pendingOutboundQueue.some((message2) => message2.content === content);
@@ -18118,8 +18304,18 @@ async function handleSendChat(h, args) {
18118
18304
  assertTextOnlyInput(provider, input);
18119
18305
  if (!text) return { success: false, error: "text required for PTY send" };
18120
18306
  await waitOnceForFreshHermesCliStart(adapter, _log);
18121
- await adapter.sendMessage(text);
18122
- return _logSendSuccess(`${transport}-adapter`, adapter.cliType);
18307
+ const forceSend = args?.force === true || args?.forceSend === true;
18308
+ if (forceSend && typeof adapter.forceSendMessage === "function") {
18309
+ await adapter.forceSendMessage(text);
18310
+ } else if (forceSend) {
18311
+ await adapter.sendMessage(text, { force: true });
18312
+ } else {
18313
+ await adapter.sendMessage(text);
18314
+ }
18315
+ return {
18316
+ ..._logSendSuccess(`${transport}-adapter`, adapter.cliType),
18317
+ ...forceSend ? { forceSent: true } : {}
18318
+ };
18123
18319
  } catch (e) {
18124
18320
  return { success: false, error: `${transport} send failed: ${e.message}` };
18125
18321
  }
@@ -20470,6 +20666,7 @@ var CliProviderInstance = class {
20470
20666
  lastApprovalEventAt = 0;
20471
20667
  autoApproveBusy = false;
20472
20668
  autoApproveBusyTimer = null;
20669
+ lastAutoApprovalSignature = "";
20473
20670
  controlValues = {};
20474
20671
  summaryMetadata = void 0;
20475
20672
  appliedEffectKeys = /* @__PURE__ */ new Set();
@@ -20979,15 +21176,26 @@ var CliProviderInstance = class {
20979
21176
  }
20980
21177
  maybeAutoApproveStatus(adapterStatus, now = Date.now()) {
20981
21178
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
20982
- if (autoApproveActive && !this.autoApproveBusy) {
21179
+ if (!autoApproveActive) {
21180
+ this.lastAutoApprovalSignature = "";
21181
+ return autoApproveActive;
21182
+ }
21183
+ const modal = adapterStatus.activeModal;
21184
+ const { index: buttonIndex, label: buttonLabel } = pickApprovalButton(modal?.buttons, this.provider);
21185
+ const signature = [
21186
+ typeof modal?.message === "string" ? modal.message.trim() : "",
21187
+ Array.isArray(modal?.buttons) ? modal.buttons.join("|") : "",
21188
+ buttonIndex
21189
+ ].join("::");
21190
+ if (!this.autoApproveBusy || signature !== this.lastAutoApprovalSignature) {
20983
21191
  this.autoApproveBusy = true;
21192
+ this.lastAutoApprovalSignature = signature;
20984
21193
  if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
20985
21194
  this.autoApproveBusyTimer = setTimeout(() => {
20986
21195
  this.autoApproveBusy = false;
20987
21196
  this.autoApproveBusyTimer = null;
21197
+ this.lastAutoApprovalSignature = "";
20988
21198
  }, 2e3);
20989
- const modal = adapterStatus.activeModal;
20990
- const { index: buttonIndex, label: buttonLabel } = pickApprovalButton(modal?.buttons, this.provider);
20991
21199
  this.recordAutoApproval(modal?.message, buttonLabel, now);
20992
21200
  setTimeout(() => {
20993
21201
  this.adapter.resolveModal(buttonIndex);
@@ -21129,7 +21337,26 @@ var CliProviderInstance = class {
21129
21337
  });
21130
21338
  const agentKey = `${this.type}:cli`;
21131
21339
  const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint);
21340
+ const monitorParsedStatus = parsedStatus;
21132
21341
  for (const me of monitorEvents) {
21342
+ if (me.type === "monitor:long_generating" && this.completionHasFinalAssistantMessage(monitorParsedStatus?.messages) && !this.hasAdapterPendingResponse() && !hasNonEmptyCliModalButtons(monitorParsedStatus?.activeModal ?? monitorParsedStatus?.modal)) {
21343
+ this.pushEvent({
21344
+ event: "agent:generating_completed",
21345
+ chatTitle,
21346
+ duration: this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : void 0,
21347
+ timestamp: me.timestamp,
21348
+ finalSummary: extractFinalSummaryFromMessages(monitorParsedStatus?.messages),
21349
+ completionDiagnostic: {
21350
+ providerType: this.type,
21351
+ sessionId: this.instanceId,
21352
+ providerSessionId: this.providerSessionId || null,
21353
+ reconciliationReason: "long_generating_monitor_final_summary",
21354
+ finalAssistantPresent: true
21355
+ }
21356
+ });
21357
+ this.generatingStartedAt = 0;
21358
+ continue;
21359
+ }
21133
21360
  this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
21134
21361
  }
21135
21362
  }
@@ -23731,11 +23958,19 @@ Run 'adhdev doctor' for detailed diagnostics.`
23731
23958
  }
23732
23959
  const message = input.textFallback;
23733
23960
  if (!message) throw new Error("message required for send_chat");
23734
- await adapter.sendMessage(message);
23961
+ const forceSend = args?.force === true || args?.forceSend === true;
23962
+ if (forceSend && typeof adapter.forceSendMessage === "function") {
23963
+ await adapter.forceSendMessage(message);
23964
+ } else if (forceSend) {
23965
+ await adapter.sendMessage(message, { force: true });
23966
+ } else {
23967
+ await adapter.sendMessage(message);
23968
+ }
23735
23969
  return {
23736
23970
  success: true,
23737
23971
  status: BUSY_AGENT_STATUSES.has(currentStatus) ? currentStatus : "generating",
23738
- ...BUSY_AGENT_STATUSES.has(currentStatus) ? { queued: true, queuedReason: "agent_runtime_busy" } : {}
23972
+ ...BUSY_AGENT_STATUSES.has(currentStatus) ? { queued: true, queuedReason: "agent_runtime_busy" } : {},
23973
+ ...forceSend ? { forceSent: true, queued: false } : {}
23739
23974
  };
23740
23975
  } else if (action === "clear_history") {
23741
23976
  if (typeof adapter.clearHistory === "function") adapter.clearHistory();