@adhdev/daemon-core 1.0.28-rc.16 → 1.0.28-rc.18

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.
package/dist/index.mjs CHANGED
@@ -786,10 +786,10 @@ function readInjected(value) {
786
786
  }
787
787
  function getDaemonBuildInfo() {
788
788
  if (cached) return cached;
789
- const commit = readInjected(true ? "3a48f660f00c3bf2e42ba36df7789edbc654b99c" : void 0) ?? "unknown";
790
- const commitShort = readInjected(true ? "3a48f660" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
791
- const version = readInjected(true ? "1.0.28-rc.16" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
792
- const builtAt = readInjected(true ? "2026-07-26T16:40:47.778Z" : void 0);
789
+ const commit = readInjected(true ? "2ab22815548d9465b9f6a28a058049ba230dfb61" : void 0) ?? "unknown";
790
+ const commitShort = readInjected(true ? "2ab22815" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
791
+ const version = readInjected(true ? "1.0.28-rc.18" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
792
+ const builtAt = readInjected(true ? "2026-07-27T05:51:47.959Z" : void 0);
793
793
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
794
794
  return cached;
795
795
  }
@@ -16439,6 +16439,31 @@ var init_debug_trace = __esm({
16439
16439
  function recordSynthCompletionGateTrace(stage, payload) {
16440
16440
  recordDebugTrace({ category: "completion-gate", stage, level: "debug", payload });
16441
16441
  }
16442
+ function evaluateTranscriptSynthAdmission(admission) {
16443
+ if (!admission) return { admitted: true };
16444
+ if (admission.trailingToolActivityAfterFinalAssistant === true) {
16445
+ return { admitted: false, reason: "trailing_tool_activity_after_final_assistant" };
16446
+ }
16447
+ if (!admission.boundedBackstop && typeof admission.liveTurnPendingEvidence === "function") {
16448
+ let pending = false;
16449
+ try {
16450
+ pending = admission.liveTurnPendingEvidence() === true;
16451
+ } catch {
16452
+ }
16453
+ if (pending) return { admitted: false, reason: "live_turn_pending_evidence" };
16454
+ }
16455
+ return { admitted: true };
16456
+ }
16457
+ function resolveLiveTurnPendingEvidence(components, sessionId) {
16458
+ try {
16459
+ const instance = components?.instanceManager?.getInstance?.(sessionId);
16460
+ if (typeof instance?.hasLiveTurnPendingEvidence !== "function") return void 0;
16461
+ const probe = instance.hasLiveTurnPendingEvidence.bind(instance);
16462
+ return () => probe();
16463
+ } catch {
16464
+ return void 0;
16465
+ }
16466
+ }
16442
16467
  function findRecentTerminalLedgerEvidence(args) {
16443
16468
  if (!args.sessionId && !args.nodeId) return null;
16444
16469
  const entries = readLedgerEntries(args.meshId, { tail: 200 });
@@ -16559,6 +16584,17 @@ function reconcileDirectDispatchCompletionFromTranscript(args) {
16559
16584
  })) {
16560
16585
  return { reconciled: false, alreadyTerminal: true, reason: "terminal_ledger_entry_exists" };
16561
16586
  }
16587
+ const admission = evaluateTranscriptSynthAdmission(args.causalAdmission);
16588
+ if (!admission.admitted) {
16589
+ LOG.info("MeshEvents", `Transcript synth vetoed for task ${args.taskId} session ${args.sessionId} (source ${args.source || "direct_task_transcript_reconciliation"}): ${admission.reason} \u2014 holding the completion; it re-evaluates on the next reconcile pass`);
16590
+ recordSynthCompletionGateTrace("synth-veto", {
16591
+ producer: "transcript_reconcile",
16592
+ source: args.source || "direct_task_transcript_reconciliation",
16593
+ taskId: args.taskId,
16594
+ reason: admission.reason
16595
+ });
16596
+ return { reconciled: false, reason: admission.reason };
16597
+ }
16562
16598
  const nodeId = readNonEmptyString(args.nodeId) || dispatch?.nodeId;
16563
16599
  const providerType = readNonEmptyString(args.providerType) || dispatch?.providerType || readNonEmptyString(dispatch?.payload.providerType);
16564
16600
  const completedAt = args.completedAt || (/* @__PURE__ */ new Date()).toISOString();
@@ -16720,6 +16756,7 @@ var init_mesh_events_stale = __esm({
16720
16756
  init_mesh_events_pending();
16721
16757
  init_mesh_events_utils();
16722
16758
  init_debug_trace();
16759
+ init_logger();
16723
16760
  init_dist();
16724
16761
  DIRECT_DISPATCH_RECONCILE_GRACE_MS = 6e4;
16725
16762
  DIRECT_DISPATCH_IDLE_SESSION_RECONCILE_GRACE_MS = 12e4;
@@ -23093,6 +23130,14 @@ function evaluateMeshEventSuppression(args, ctx) {
23093
23130
  };
23094
23131
  }
23095
23132
  }
23133
+ if (!readNonEmptyString(args.metadataEvent.taskId) && !sessionHasActiveAssignment(args.meshId, eventSessionId) && !findRecentTerminalLedgerEvidence({ meshId: args.meshId, sessionId: eventSessionId, nodeId: eventNodeId || void 0 }) && isWeakCompletionEvidence(args.metadataEvent)) {
23134
+ LOG.info("MeshEvents", `Suppressed agent:generating_completed for session ${eventSessionId} (mesh ${args.meshId}): no taskId, no active assignment, and no prior terminal ledger evidence \u2014 a pre-dispatch startup/greeting artifact, not a real task completion`);
23135
+ traceMeshEventDrop("no_dispatch_native_completion", traceCtx);
23136
+ return {
23137
+ kind: "suppress",
23138
+ result: { success: true, forwarded: 0, suppressed: true, noDispatchNativeCompletion: true }
23139
+ };
23140
+ }
23096
23141
  const inWindowTask = findInWindowUnclaimedAutoLaunchTask(args.meshId, eventSessionId, Date.now());
23097
23142
  if (inWindowTask) {
23098
23143
  const causalEvidence = MeshRuntimeStore.getInstance().taskDeliveryConsumed(args.meshId, inWindowTask.id) || hasMatchingTaskDispatchedLedgerEntry(args.meshId, inWindowTask.id, eventSessionId);
@@ -24849,6 +24894,18 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
24849
24894
  }
24850
24895
  const messages = Array.isArray(payload.messages) ? payload.messages : [];
24851
24896
  const evidence = extractFinalAssistantSummaryEvidence(messages);
24897
+ if (hasTrailingToolActivityAfterFinalAssistant(messages)) {
24898
+ setHoldState(synthKey, mesh.id, { liveConfirmedSinceAck: true, consecutiveReadFailures: 0 });
24899
+ LOG.info("MeshReconcile", `Mid-turn causal admission: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) \u2014 the latest final-looking assistant bubble is followed by trailing tool/terminal activity (interim narration; the turn is still executing); holding the transcript synth`);
24900
+ traceMeshEventDrop("reconcile_synth_veto_trailing_tool_activity", {
24901
+ taskId,
24902
+ sessionId,
24903
+ nodeId,
24904
+ meshId: mesh.id,
24905
+ event: "agent:generating_completed"
24906
+ });
24907
+ continue;
24908
+ }
24852
24909
  if (isAcked) {
24853
24910
  const ackedAtMs = Date.parse(readNonEmptyString(dispatch.updatedAt));
24854
24911
  const sinceAckMs = Number.isFinite(ackedAtMs) ? nowMs - ackedAtMs : Number.POSITIVE_INFINITY;
@@ -24923,6 +24980,17 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
24923
24980
  // The ledger-recovered dispatching-coordinator daemon (inside the reconcile fn)
24924
24981
  // takes PRIORITY over this arg; this remains the best-available fallback.
24925
24982
  ...coordinatorDaemonId ? { targetCoordinatorDaemonId: coordinatorDaemonId } : {},
24983
+ // MID-TURN-CAUSAL-ADMISSION (rc.16): pass the LOCAL live adapter's synchronous
24984
+ // turn-state probe into the unified choke point — a pending verdict vetoes this
24985
+ // eager synth (never-acked first-idle / acked fast-track). The death-deadline
24986
+ // backstop is the bounded max-wait net and overrides the veto (genuine-final
24987
+ // fail-open preserved); a remote/missing live source resolves to undefined and
24988
+ // fails open onto the bounded transcript evidence above. The trailing-tool veto
24989
+ // already ran at the top of this tick.
24990
+ causalAdmission: {
24991
+ liveTurnPendingEvidence: resolveLiveTurnPendingEvidence(components, sessionId),
24992
+ boundedBackstop: backstopKind === "ackedHoldDeathDeadlineFired"
24993
+ },
24926
24994
  source: "daemon_reconcile_transcript_completion"
24927
24995
  });
24928
24996
  if (result.reconciled) {
@@ -25368,10 +25436,10 @@ async function evaluateEarlyIdleTranscriptArm(components, mesh, store, row, loca
25368
25436
  if (profile) return profile.emitsPtyTurnEvents === false;
25369
25437
  return verdict === "UNKNOWN";
25370
25438
  }
25371
- function propagateWatchdogTranscriptCompletion(meshId, row, evidence, source) {
25439
+ function propagateWatchdogTranscriptCompletion(components, meshId, row, evidence, source, opts) {
25372
25440
  const sessionId = readNonEmptyString(row.assignedSessionId) || evidence.sessionId;
25373
25441
  if (!sessionId || !readNonEmptyString(evidence.finalSummary)) {
25374
- return false;
25442
+ return "unavailable";
25375
25443
  }
25376
25444
  try {
25377
25445
  const result = reconcileDirectDispatchCompletionFromTranscript({
@@ -25387,11 +25455,26 @@ function propagateWatchdogTranscriptCompletion(meshId, row, evidence, source) {
25387
25455
  // The watchdog poll already enforced idle + post-dispatch + no-trailing-tool + streak;
25388
25456
  // skip the reconcile's own grace/transcript_not_proven gates (dedup backstops remain).
25389
25457
  preValidatedTranscriptEvidence: true,
25458
+ // MID-TURN-CAUSAL-ADMISSION (rc.16): the transcript poll's structural evidence can
25459
+ // still straddle a genuinely mid-turn LOCAL worker (the incident shape: transcript
25460
+ // reads idle-with-final-assistant while the raw PTY is mid-tool). Route the live
25461
+ // adapter's synchronous probe through the unified choke point. The EARLY-IDLE
25462
+ // caller is an eager path (no opts) → a pending verdict defers; the redrive-deadline
25463
+ // caller passes boundedBackstop (the max-wait net) → the veto yields, preserving the
25464
+ // genuine-final fail-open semantics.
25465
+ causalAdmission: {
25466
+ liveTurnPendingEvidence: resolveLiveTurnPendingEvidence(components, sessionId),
25467
+ boundedBackstop: opts?.boundedBackstop === true
25468
+ },
25390
25469
  source
25391
25470
  });
25392
- return result.reconciled || result.alreadyTerminal === true;
25471
+ if (result.reconciled || result.alreadyTerminal === true) return "propagated";
25472
+ if (result.reason === "live_turn_pending_evidence" || result.reason === "trailing_tool_activity_after_final_assistant") {
25473
+ return "deferred";
25474
+ }
25475
+ return "unavailable";
25393
25476
  } catch {
25394
- return false;
25477
+ return "unavailable";
25395
25478
  }
25396
25479
  }
25397
25480
  async function recoverStrandedAssignedDispatches(components, mesh, store, localDaemonId, selfIds = []) {
@@ -25425,14 +25508,28 @@ async function recoverStrandedAssignedDispatches(components, mesh, store, localD
25425
25508
  minFinalAssistantAgeMs: ASSIGNED_IDLE_TRANSCRIPT_COMPLETE_MS
25426
25509
  });
25427
25510
  if (terminalEvidence) {
25428
- assignedIdleFinalAssistantSince.delete(idleTranscriptStreakKey);
25429
- updateTaskStatus(meshId, row.id, terminalEvidence.outcome);
25430
- const propagated = propagateWatchdogTranscriptCompletion(
25511
+ const propagation = propagateWatchdogTranscriptCompletion(
25512
+ components,
25431
25513
  meshId,
25432
25514
  row,
25433
25515
  terminalEvidence,
25434
25516
  "early_idle_transcript_evidence"
25435
25517
  );
25518
+ if (propagation === "deferred") {
25519
+ assignedIdleFinalAssistantSince.delete(idleTranscriptStreakKey);
25520
+ LOG.info("MeshReconcile", `Deferred early transcript completion for task ${row.id} on mesh ${meshId} (node=${row.assignedNodeId ?? "?"} session=${row.assignedSessionId ?? "?"}): the live adapter still reports the turn pending (mid-tool / streaming / modal) \u2014 holding; the completion re-evaluates next tick`);
25521
+ traceMeshEventDrop("early_idle_completion_deferred_live_pending", {
25522
+ taskId: row.id,
25523
+ sessionId: row.assignedSessionId,
25524
+ nodeId: row.assignedNodeId,
25525
+ meshId,
25526
+ event: "agent:generating_completed"
25527
+ });
25528
+ continue;
25529
+ }
25530
+ assignedIdleFinalAssistantSince.delete(idleTranscriptStreakKey);
25531
+ updateTaskStatus(meshId, row.id, terminalEvidence.outcome);
25532
+ const propagated = propagation === "propagated";
25436
25533
  if (!propagated && !findTerminalLedgerEvidenceForTask({ meshId, taskId: row.id })) {
25437
25534
  try {
25438
25535
  appendLedgerEntry(meshId, {
@@ -25607,12 +25704,15 @@ async function recoverStrandedAssignedDispatches(components, mesh, store, localD
25607
25704
  if (terminalEvidence) {
25608
25705
  deliveredNoTurnUnknownStreak.delete(streakKey);
25609
25706
  updateTaskStatus(meshId, row.id, terminalEvidence.outcome);
25610
- const propagated = propagateWatchdogTranscriptCompletion(
25707
+ const propagation = propagateWatchdogTranscriptCompletion(
25708
+ components,
25611
25709
  meshId,
25612
25710
  row,
25613
25711
  terminalEvidence,
25614
- "redrive_deadline_transcript_evidence"
25712
+ "redrive_deadline_transcript_evidence",
25713
+ { boundedBackstop: true }
25615
25714
  );
25715
+ const propagated = propagation === "propagated";
25616
25716
  if (!propagated && !findTerminalLedgerEvidenceForTask({ meshId, taskId: row.id })) {
25617
25717
  try {
25618
25718
  appendLedgerEntry(meshId, {
@@ -29455,7 +29555,10 @@ var init_cli_state_engine = __esm({
29455
29555
  const session = this.runParseSession(snap);
29456
29556
  if (!session) return;
29457
29557
  const { status, messages } = session;
29458
- const modal = session.activeModal ?? session.modal ?? null;
29558
+ const rawModal = session.activeModal ?? session.modal ?? null;
29559
+ const modal = rawModal && Array.isArray(rawModal.buttons) && rawModal.buttons.some(
29560
+ (button) => typeof button === "string" && button.trim().length > 0
29561
+ ) ? rawModal : null;
29459
29562
  const parsedStatus = session.parsedStatus ?? null;
29460
29563
  const parsedMessages = normalizeCliParsedMessages(messages, {
29461
29564
  scope: null,
@@ -39665,7 +39768,7 @@ async function handleReadChat(h, args) {
39665
39768
  });
39666
39769
  }
39667
39770
  }
39668
- if (isGeneratingLikeStatus(selectedStatus) && selectedTranscriptAuthority === "provider" && !hasNonEmptyModalButtons(activeModal) && hasFinalVisibleAssistantMessage(selectedMessages)) {
39771
+ if (isGeneratingLikeStatus(selectedStatus) && selectedTranscriptAuthority === "provider" && !hasNonEmptyModalButtons(activeModal) && hasFinalVisibleAssistantMessage(selectedMessages) && !hasTrailingToolActivityAfterFinalAssistant(selectedMessages) && !hasTrailingToolActivityAfterFinalAssistant(returnedMessages)) {
39669
39772
  selectedStatus = "idle";
39670
39773
  selectedMessages = finalizeStreamingMessagesWhenIdle(selectedMessages, selectedStatus);
39671
39774
  messageSource = {
@@ -51187,9 +51290,36 @@ var CliProviderInstance = class _CliProviderInstance {
51187
51290
  * OR isModalParked() (a live approval/choice modal) — so a session this method reports
51188
51291
  * pending is, by construction, one the local finalization gate would also refuse to
51189
51292
  * finalize right now.
51293
+ *
51294
+ * NATIVE-TRAILING-TOOL-GATE (rc.16 follow-up): the three adapter-state discriminators
51295
+ * above are blind to a background shell tool that keeps a turn alive without the adapter
51296
+ * reporting a pending response — e.g. a backgrounded `sleep 40 &` between narration
51297
+ * bubbles on a write-lag native-source provider (claude-cli), which is deliberately
51298
+ * un-floored (noExternalTranscriptSource omitted, mission f2f6da1b owner decision) so its
51299
+ * transcript write-lag emits promptly. That un-flooring is correct for the ordinary
51300
+ * fraction-of-a-second trail, but it also means the growth-hold / busy-lease protections
51301
+ * in getCompletedFinalizationBlock never engage for claude-cli, so an interim final-
51302
+ * LOOKING bubble followed by continuing tool calls can still finalize as a completion
51303
+ * while the transcript demonstrably shows the turn still executing. Close that gap here,
51304
+ * narrowly: for a native-source provider only, reuse the SAME bounded transcript read the
51305
+ * class's own completion judgment already performs (probeNativeTranscriptSignals) and
51306
+ * apply the SAME trailing-tool-activity veto the transcript-synth admission choke point
51307
+ * uses (hasTrailingToolActivityAfterFinalAssistant) — the latest final-looking assistant
51308
+ * bubble followed by tool/terminal activity is interim narration, not a turn end. Fail-open
51309
+ * by construction: a non-native-source class (probe returns null), an unresolved
51310
+ * transcript, or a read error never reaches the veto, so a missing/unavailable transcript
51311
+ * can never wedge a session as "pending" forever.
51190
51312
  */
51191
51313
  hasLiveTurnPendingEvidence() {
51192
- return this.hasAdapterPendingResponse() || this.isModalParked();
51314
+ if (this.hasAdapterPendingResponse() || this.isModalParked()) return true;
51315
+ try {
51316
+ const probe = this.probeNativeTranscriptSignals();
51317
+ if (probe?.snapshot?.available === true && Array.isArray(probe.messages)) {
51318
+ if (hasTrailingToolActivityAfterFinalAssistant(probe.messages)) return true;
51319
+ }
51320
+ } catch {
51321
+ }
51322
+ return false;
51193
51323
  }
51194
51324
  /**
51195
51325
  * PTY-OVERTRUST-DRAIN (Defect B). The deliverability/drain status the mesh
@@ -78413,6 +78543,7 @@ export {
78413
78543
  handleGitCommand,
78414
78544
  hasCdpManager,
78415
78545
  hasPendingDependents,
78546
+ hasTrailingToolActivityAfterFinalAssistant,
78416
78547
  hashSignatureParts,
78417
78548
  initDaemonComponents,
78418
78549
  insertDirectDispatch,