@adhdev/daemon-core 0.9.82-rc.361 → 0.9.82-rc.363

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 (33) hide show
  1. package/dist/cli-adapters/cli-state-engine.d.ts +1 -1
  2. package/dist/cli-adapters/provider-cli-shared.d.ts +10 -0
  3. package/dist/commands/low-family/coordinator-prompt.d.ts +9 -0
  4. package/dist/commands/low-family/daemon-lifecycle.d.ts +2 -0
  5. package/dist/commands/low-family/diagnostics.d.ts +2 -0
  6. package/dist/commands/low-family/mesh-ledger.d.ts +10 -0
  7. package/dist/commands/low-family/mesh-node-logs.d.ts +2 -0
  8. package/dist/commands/low-family/notification.d.ts +2 -0
  9. package/dist/commands/low-family/status-meta.d.ts +2 -0
  10. package/dist/commands/low-family/types.d.ts +17 -0
  11. package/dist/index.js +2290 -2177
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.mjs +2296 -2183
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/mesh/mesh-events-utils.d.ts +1 -0
  16. package/dist/providers/cli-provider-instance.d.ts +2 -0
  17. package/package.json +2 -2
  18. package/src/cli-adapters/cli-state-engine.ts +7 -1
  19. package/src/cli-adapters/provider-cli-adapter.ts +1 -0
  20. package/src/cli-adapters/provider-cli-shared.ts +10 -0
  21. package/src/commands/low-family/coordinator-prompt.ts +72 -0
  22. package/src/commands/low-family/daemon-lifecycle.ts +107 -0
  23. package/src/commands/low-family/diagnostics.ts +57 -0
  24. package/src/commands/low-family/index.ts +14 -0
  25. package/src/commands/low-family/mesh-ledger.ts +62 -0
  26. package/src/commands/low-family/mesh-node-logs.ts +81 -0
  27. package/src/commands/low-family/notification.ts +116 -0
  28. package/src/commands/low-family/status-meta.ts +112 -0
  29. package/src/commands/low-family/types.ts +20 -0
  30. package/src/commands/router.ts +8 -522
  31. package/src/mesh/mesh-events-coordinator.ts +37 -0
  32. package/src/mesh/mesh-events-utils.ts +28 -2
  33. package/src/providers/cli-provider-instance.ts +46 -0
@@ -195,6 +195,23 @@ function readEventTimestampValue(value: unknown): number {
195
195
  return 0;
196
196
  }
197
197
 
198
+ // A completion whose evidence is WEAK: the worker FSM reached idle but the turn's
199
+ // final assistant message was never confirmed (missing_final_assistant / a
200
+ // finalAssistantPresent=false diagnostic), or the event self-declares insufficient/weak
201
+ // evidence. cli-provider-instance emits this on the CANON-C decoupled-immediate path and
202
+ // on a forced finalization timeout, and the new structural approval-resolution gate
203
+ // (FALSEIDLE-a) can also let an unconfirmed approval→idle through. Such a completion is NOT
204
+ // trustworthy terminal evidence — surface a verify hint so the coordinator confirms via
205
+ // mesh_read_chat / git status before declaring the task done. Mirrors
206
+ // isWeakCompletionMetadata in mesh-events-pending.ts.
207
+ export function isWeakCompletionMetadata(metadataEvent: Record<string, unknown>): boolean {
208
+ const evidenceLevel = readNonEmptyString(metadataEvent.evidenceLevel);
209
+ if (evidenceLevel === 'insufficient' || evidenceLevel === 'weak') return true;
210
+ if (metadataEvent.reviewRecommended === true) return true;
211
+ const diag = readRecord(metadataEvent.completionDiagnostic);
212
+ return diag?.finalAssistantPresent === false || diag?.blockReason === 'missing_final_assistant';
213
+ }
214
+
198
215
  function formatCompletionMetadata(event: Record<string, unknown>): string {
199
216
  const completionDiagnostic = event.completionDiagnostic && typeof event.completionDiagnostic === 'object'
200
217
  ? event.completionDiagnostic as Record<string, unknown>
@@ -229,6 +246,13 @@ export function buildMeshSystemMessage(args: {
229
246
  return `[System] ${args.nodeLabel} already has completion evidence${metadata}. The no-progress monitor reconciled the terminal handoff and marked the session complete; wait for the queued completion event/status refresh before doing any manual transcript check.`;
230
247
  }
231
248
  const reviewRecommended = args.metadataEvent.reviewRecommended === true;
249
+ // FALSEIDLE-b: a weak completion (missing final assistant / finalAssistantPresent=false /
250
+ // insufficient evidence) is not trustworthy terminal evidence even when reviewRecommended
251
+ // is not set. Append an explicit verify hint so the coordinator does not declare the task
252
+ // done off a false idle. Only the weak case is affected — a genuine completion (final
253
+ // assistant confirmed, no weak diagnostic) carries no extra note and is unchanged.
254
+ const weakCompletion = isWeakCompletionMetadata(args.metadataEvent);
255
+ const verifyTextNote = ' Completion evidence is weak — verify via mesh_read_chat or git status before declaring the task done; the worker may still be parked on an approval/modal.';
232
256
  // Auto-surface the worker's final summary directly into the coordinator chat so it
233
257
  // does not have to call mesh_read_chat just to see the result. The summary IS the
234
258
  // worker's final assistant message; embedding it here replaces the previous
@@ -244,12 +268,14 @@ export function buildMeshSystemMessage(args: {
244
268
  : completionSummary;
245
269
  const verifyNote = reviewRecommended
246
270
  ? ' Completion evidence is insufficient — verify via git status or provider_session_id before assuming the task is done.'
247
- : '';
271
+ : (weakCompletion ? verifyTextNote : '');
248
272
  return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. Its final summary is included below — read it directly and only call mesh_read_chat if you need the full transcript.${verifyNote}\n\n--- ${args.nodeLabel} final summary ---\n${surfaced}`;
249
273
  }
250
274
  const reviewNote = reviewRecommended
251
275
  ? ' Completion evidence is insufficient — verify via git status or provider_session_id before assuming the task is done. Use mesh_read_chat once if needed, but do not poll repeatedly.'
252
- : ' Use mesh_read_chat once to review its final progress, but do not poll repeatedly.';
276
+ : (weakCompletion
277
+ ? `${verifyTextNote} Use mesh_read_chat once if needed, but do not poll repeatedly.`
278
+ : ' Use mesh_read_chat once to review its final progress, but do not poll repeatedly.');
253
279
  return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path;${reviewNote}`;
254
280
  }
255
281
  if (args.event === 'agent:waiting_approval') {
@@ -1401,6 +1401,12 @@ export class CliProviderInstance implements ProviderInstance {
1401
1401
  }
1402
1402
  }
1403
1403
 
1404
+ // (FALSEIDLE-a) Structural approval-resolution gate. Runs BEFORE the brittle
1405
+ // screen-text heuristic below so it also catches modals whose text does not match
1406
+ // looksLikeActiveApprovalPromptText (e.g. claude-cli's cd / "untrusted hooks" prompt).
1407
+ const approvalResolutionBlock = this.approvalResolutionFinalizationBlock(pending);
1408
+ if (approvalResolutionBlock) return approvalResolutionBlock;
1409
+
1404
1410
  // Guard: if the screen still shows an approval/choice prompt as the last visible text,
1405
1411
  // the turn is not complete even if the parsed status says idle and there is an assistant
1406
1412
  // message. This catches the case where waiting_approval→idle transitions occur before
@@ -1420,6 +1426,46 @@ export class CliProviderInstance implements ProviderInstance {
1420
1426
  return null;
1421
1427
  }
1422
1428
 
1429
+ // (FALSEIDLE-a) Positive, structural proof that the latest approval entry was resolved
1430
+ // through ADHDev. resolveModal() — driven by auto-approve, dashboard/mesh_approve, and
1431
+ // dev-cli-debug alike — advances the engine's lastResolvedEntrySeq to the current
1432
+ // approvalEntrySeq. So `lastResolvedEntrySeq >= approvalEntrySeq` (with a real entry,
1433
+ // approvalEntrySeq > 0) means the modal we last saw was actually answered. Absence of this
1434
+ // evidence after a waiting_approval→idle transition means the idle is suspect: the spec's
1435
+ // text-based approval→idle rule false-tripped while the modal is still unresolved.
1436
+ // Fails OPEN (returns true) when the seq fields are unavailable, so the gate can never wedge
1437
+ // a session on a provider/adapter that does not surface the counters.
1438
+ private hasApprovalResolutionEvidence(): boolean {
1439
+ try {
1440
+ const status = this.adapter.getStatus({ allowParse: false }) as any;
1441
+ const entrySeq = typeof status?.approvalEntrySeq === 'number' ? status.approvalEntrySeq : 0;
1442
+ if (entrySeq <= 0) return true;
1443
+ const resolvedSeq = typeof status?.lastResolvedEntrySeq === 'number' ? status.lastResolvedEntrySeq : undefined;
1444
+ if (resolvedSeq === undefined) return true;
1445
+ return resolvedSeq >= entrySeq;
1446
+ } catch {
1447
+ return true;
1448
+ }
1449
+ }
1450
+
1451
+ // (FALSEIDLE-a) Hold a completion that is the anomalous DIRECT waiting_approval→idle
1452
+ // transition with no positive resolution evidence. A genuinely resolved approval routes
1453
+ // through resolveModal → setStatus('generating'), so its completion's previousStatus is
1454
+ // 'generating' (not 'waiting_approval') and this gate never fires for it. Scoped to
1455
+ // delegated mesh/coordinator sessions — whose only modal-resolution path is auto-approve /
1456
+ // mesh_approve (both advance lastResolvedEntrySeq) — so an interactive local session, where
1457
+ // a human may answer the PTY prompt directly and leave no resolveModal record, is untouched.
1458
+ // Non-terminal: the hold is bounded by COMPLETED_FINALIZATION_MAX_WAIT_MS (30s), giving a
1459
+ // settling auto-approve time to fire and advance the seq, and guaranteeing no permanent wedge
1460
+ // if resolution ever happens via a path that does not record evidence.
1461
+ private approvalResolutionFinalizationBlock(pending: CompletedDebouncePending): CompletedFinalizationBlock | null {
1462
+ if (pending.previousStatus !== 'waiting_approval') return null;
1463
+ const meshContext = !!(this.settings.meshNodeFor || this.settings.meshActiveTaskId || this.settings.launchedByCoordinator);
1464
+ if (!meshContext) return null;
1465
+ if (this.hasApprovalResolutionEvidence()) return null;
1466
+ return { reason: 'approval_resolution_unconfirmed', terminal: false };
1467
+ }
1468
+
1423
1469
  private scheduleCompletedDebounceFlush(delayMs: number): void {
1424
1470
  if (this.completedDebounceTimer) clearTimeout(this.completedDebounceTimer);
1425
1471
  this.completedDebounceTimer = setTimeout(() => this.flushCompletedDebounceIfFinalized(), delayMs);