@adhdev/daemon-core 1.0.18-rc.12 → 1.0.18-rc.14

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
@@ -437,10 +437,10 @@ function readInjected(value) {
437
437
  }
438
438
  function getDaemonBuildInfo() {
439
439
  if (cached) return cached;
440
- const commit = readInjected(true ? "8cce29137e6b62781be7645411736bd8d1a9e7a8" : void 0) ?? "unknown";
441
- const commitShort = readInjected(true ? "8cce2913" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
442
- const version = readInjected(true ? "1.0.18-rc.12" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
443
- const builtAt = readInjected(true ? "2026-07-22T12:25:26.100Z" : void 0);
440
+ const commit = readInjected(true ? "a44f4a2814c4db171a9ebb0f6fcfb0798203aebb" : void 0) ?? "unknown";
441
+ const commitShort = readInjected(true ? "a44f4a28" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
442
+ const version = readInjected(true ? "1.0.18-rc.14" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
443
+ const builtAt = readInjected(true ? "2026-07-22T17:37:52.683Z" : void 0);
444
444
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
445
445
  return cached;
446
446
  }
@@ -22229,34 +22229,37 @@ async function pullRemoteNodeQueues(components, mesh, localDaemonId, candidateDa
22229
22229
  if (!dispatchMeshCommand) return;
22230
22230
  const meshId = mesh.id;
22231
22231
  const pulls = candidateDaemonIds.length > 0 ? candidateDaemonIds.map((id) => ({ meshId, coordinatorDaemonId: id })) : [{ meshId }];
22232
- await Promise.allSettled(mesh.nodes.map(async (node) => {
22233
- const nodeDaemonId = readNonEmptyString(node.daemonId);
22234
- if (!nodeDaemonId) return;
22235
- if (daemonIdsEquivalent(nodeDaemonId, localDaemonId)) return;
22236
- if (daemonIdListIncludes(candidateDaemonIds, nodeDaemonId)) return;
22237
- const getPeerStatus = components.getMeshPeerConnectionStatus;
22238
- if (getPeerStatus) {
22239
- const peerSnapshot = getPeerStatus(nodeDaemonId);
22240
- if (!peerSnapshot || String(peerSnapshot.state) !== "connected") return;
22241
- }
22242
- for (const pendingEventArgs of pulls) {
22243
- let events;
22232
+ await Promise.allSettled(mesh.nodes.map((node) => pullPendingEventsFromNode(components, meshId, node, localDaemonId, candidateDaemonIds, pulls)));
22233
+ }
22234
+ async function pullPendingEventsFromNode(components, meshId, node, localDaemonId, candidateDaemonIds, pulls) {
22235
+ const dispatchMeshCommand = components.dispatchMeshCommand;
22236
+ if (!dispatchMeshCommand) return;
22237
+ const nodeDaemonId = readNonEmptyString(node.daemonId);
22238
+ if (!nodeDaemonId) return;
22239
+ if (daemonIdsEquivalent(nodeDaemonId, localDaemonId)) return;
22240
+ if (daemonIdListIncludes(candidateDaemonIds, nodeDaemonId)) return;
22241
+ const getPeerStatus = components.getMeshPeerConnectionStatus;
22242
+ if (getPeerStatus) {
22243
+ const peerSnapshot = getPeerStatus(nodeDaemonId);
22244
+ if (!peerSnapshot || String(peerSnapshot.state) !== "connected") return;
22245
+ }
22246
+ for (const pendingEventArgs of pulls) {
22247
+ let events;
22248
+ try {
22249
+ events = await dispatchMeshCommand(nodeDaemonId, "get_pending_mesh_events", pendingEventArgs);
22250
+ } catch {
22251
+ break;
22252
+ }
22253
+ const list = extractPendingEvents(events).filter((e) => readNonEmptyString(e?.meshId) === meshId);
22254
+ for (const event of list) {
22255
+ const payload = buildForwardPayloadFromPending(event);
22256
+ if (!payload.event || !payload.meshId) continue;
22244
22257
  try {
22245
- events = await dispatchMeshCommand(nodeDaemonId, "get_pending_mesh_events", pendingEventArgs);
22258
+ handleMeshForwardEvent(components, payload);
22246
22259
  } catch {
22247
- break;
22248
- }
22249
- const list = extractPendingEvents(events).filter((e) => readNonEmptyString(e?.meshId) === meshId);
22250
- for (const event of list) {
22251
- const payload = buildForwardPayloadFromPending(event);
22252
- if (!payload.event || !payload.meshId) continue;
22253
- try {
22254
- handleMeshForwardEvent(components, payload);
22255
- } catch {
22256
- }
22257
22260
  }
22258
22261
  }
22259
- }));
22262
+ }
22260
22263
  }
22261
22264
  function unwrapReadChatPayload(raw) {
22262
22265
  let cursor = raw;
@@ -23164,7 +23167,7 @@ function assignedRowLiveStatusIsAwaitingApproval(mesh, nodeId, sessionId) {
23164
23167
  return false;
23165
23168
  }
23166
23169
  }
23167
- async function recoverStrandedAssignedDispatches(components, mesh, store) {
23170
+ async function recoverStrandedAssignedDispatches(components, mesh, store, localDaemonId, selfIds = []) {
23168
23171
  const meshId = mesh.id;
23169
23172
  const assigned = getQueue(meshId, { status: ["assigned"] });
23170
23173
  if (!assigned.length) return;
@@ -23188,6 +23191,24 @@ async function recoverStrandedAssignedDispatches(components, mesh, store) {
23188
23191
  updateTaskStatus(meshId, row.id, status);
23189
23192
  continue;
23190
23193
  }
23194
+ const assignedNode = row.assignedNodeId ? mesh.nodes?.find((n) => meshNodeIdMatches(n, row.assignedNodeId)) : void 0;
23195
+ if (assignedNode?.daemonId) {
23196
+ try {
23197
+ await pullPendingEventsFromNode(
23198
+ components,
23199
+ meshId,
23200
+ assignedNode,
23201
+ localDaemonId,
23202
+ selfIds,
23203
+ selfIds.length > 0 ? selfIds.map((id) => ({ meshId, coordinatorDaemonId: id })) : [{ meshId }]
23204
+ );
23205
+ } catch {
23206
+ }
23207
+ if (store.taskDeliveryConsumed(meshId, row.id)) {
23208
+ deliveredUnconsumedUnknownStreak.delete(`${meshId}::${row.id}`);
23209
+ continue;
23210
+ }
23211
+ }
23191
23212
  const shortStreakKey = `${meshId}::${row.id}`;
23192
23213
  const verdict = row.assignedSessionId ? resolveSessionBusyVerdict(components, row.assignedSessionId) : "IDLE_CONFIRMED";
23193
23214
  if (verdict === "GENERATING") {
@@ -23446,7 +23467,7 @@ async function runMeshReconcileTick(components) {
23446
23467
  const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
23447
23468
  if (!daemonHostsMesh(mesh, selfIds)) continue;
23448
23469
  try {
23449
- await recoverStrandedAssignedDispatches(components, mesh, store);
23470
+ await recoverStrandedAssignedDispatches(components, mesh, store, localDaemonId, selfIds);
23450
23471
  } catch (e) {
23451
23472
  LOG.warn("MeshReconcile", `Assigned-stranded watchdog failed for mesh ${mesh.id}: ${e?.message || e}`);
23452
23473
  }
@@ -49113,6 +49134,18 @@ var CliProviderInstance = class _CliProviderInstance {
49113
49134
  // MESH_WORKER_STALL_REFIRE_COOLDOWN_MS between successive emissions across
49114
49135
  // anchor re-arms (the per-anchor guard only covers a single continuous stall).
49115
49136
  meshStallLastFiredAt = -1;
49137
+ // KIMI-MESH-COMPLETION-EMIT (axis 1): the native-source transcript progress
49138
+ // fingerprint (record count + source mtime) observed the LAST time the stall
49139
+ // watchdog checked a native-source provider (transcriptAuthority=provider — e.g.
49140
+ // kimi wire.jsonl). A pure-PTY native-source worker running a long, screen-quiet
49141
+ // tool (no viewport bytes for minutes) looks stalled by the lastOutputAt clock
49142
+ // even though its transcript file is still growing. Before firing the stall, we
49143
+ // compare the current transcript fingerprint against this snapshot; if the
49144
+ // transcript advanced, the "stall" is a false positive of PTY-render stasis, so
49145
+ // we re-arm the anchor instead of paging the coordinator (whose no_progress
49146
+ // handling can lead to the worker being stopped mid-work → completion never
49147
+ // emitted). null = not yet sampled for this session/anchor.
49148
+ meshStallNativeTranscriptSample = null;
49116
49149
  // FALSE-IDLE continuity epoch: monotonically bumped on EVERY entry into a busy
49117
49150
  // phase (→generating or →waiting_approval). The completedDebouncePending snapshots
49118
49151
  // this value at arm time (busyEpochAtArm); the flush guard requires it UNCHANGED
@@ -49922,6 +49955,14 @@ var CliProviderInstance = class _CliProviderInstance {
49922
49955
  * once at completion). Reset on the next turn's start.
49923
49956
  */
49924
49957
  lastCompletionSummary = null;
49958
+ // KIMI-MESH-COMPLETION-EMIT (axis 2, double-emit guard): the (taskId, wall-clock)
49959
+ // of the most recent agent:generating_completed this instance emitted, stamped by
49960
+ // emitGeneratingCompleted. The pre-cleanup completion flush
49961
+ // (flushMeshCompletionBeforeCleanup, driven by cli-manager's exit monitor) reads
49962
+ // this to refuse a SECOND completion for a turn whose completion already fired —
49963
+ // so a worker that finished cleanly and is simply being auto-cleaned never emits a
49964
+ // duplicate. taskId '' covers an ad-hoc (no-task) turn. null = none emitted yet.
49965
+ lastEmittedCompletion = null;
49925
49966
  async enforceFreshSessionLaunchIfNeeded() {
49926
49967
  const scriptName = getForcedNewSessionScriptName(this.provider, this.launchMode);
49927
49968
  if (!scriptName) return;
@@ -50547,6 +50588,24 @@ var CliProviderInstance = class _CliProviderInstance {
50547
50588
  const threshold = turnActive ? _CliProviderInstance.MESH_WORKER_STALL_TURN_THRESHOLD_MS : _CliProviderInstance.MESH_WORKER_STALL_IDLE_THRESHOLD_MS;
50548
50589
  const stalledMs = now - this.meshStallAnchorAt;
50549
50590
  if (stalledMs < threshold) return;
50591
+ const nativeSample = this.sampleNativeTranscriptProgress();
50592
+ if (nativeSample) {
50593
+ const prev = this.meshStallNativeTranscriptSample;
50594
+ const advanced = !prev || nativeSample.msgCount > prev.msgCount || nativeSample.sourceMtimeMs > prev.sourceMtimeMs;
50595
+ this.meshStallNativeTranscriptSample = nativeSample;
50596
+ if (advanced) {
50597
+ if (this.isMeshWorkerSession()) {
50598
+ traceMeshEventDrop(
50599
+ "mesh_worker_stall_transcript_advancing",
50600
+ this.meshTraceCtx("monitor:no_progress"),
50601
+ `msgCount=${nativeSample.msgCount} sourceMtime=${nativeSample.sourceMtimeMs} (PTY quiet ${Math.round(stalledMs / 1e3)}s but transcript advancing)`
50602
+ );
50603
+ }
50604
+ this.meshStallAnchorAt = now;
50605
+ this.meshStallEmittedForAnchor = false;
50606
+ return;
50607
+ }
50608
+ }
50550
50609
  this.meshStallEmittedForAnchor = true;
50551
50610
  if (this.meshStallLastFiredAt >= 0 && now - this.meshStallLastFiredAt < _CliProviderInstance.MESH_WORKER_STALL_REFIRE_COOLDOWN_MS) {
50552
50611
  return;
@@ -50583,6 +50642,99 @@ var CliProviderInstance = class _CliProviderInstance {
50583
50642
  this.meshStallEmittedForAnchor = false;
50584
50643
  this.meshStallTurnActiveLast = void 0;
50585
50644
  this.meshStallLastFiredAt = -1;
50645
+ this.meshStallNativeTranscriptSample = null;
50646
+ }
50647
+ /**
50648
+ * KIMI-MESH-COMPLETION-EMIT (axis 1). For a native-source provider
50649
+ * (transcriptAuthority=provider — its authoritative history is an on-disk
50650
+ * transcript file, e.g. kimi's wire.jsonl), sample the transcript's current
50651
+ * progress fingerprint (record count + source-file mtime) WITHOUT parsing the
50652
+ * PTY. Used by the stall watchdog to distinguish "PTY render is quiet but the
50653
+ * worker is still doing long tool work (transcript growing)" from a genuine
50654
+ * wedge. Returns null for a pure-PTY provider (no native source) or when the
50655
+ * source cannot be resolved this tick — the caller then falls back to the
50656
+ * unchanged lastOutputAt-only judgment.
50657
+ *
50658
+ * Generalized on the provider's native-source flag, NOT a hardcoded provider
50659
+ * type, so every current and future pure-PTY long-tool native-source provider
50660
+ * benefits. Cheap enough for the stall path: it runs only at the stall threshold
50661
+ * (≥180s of PTY stasis), never on the routine 5s tick.
50662
+ */
50663
+ sampleNativeTranscriptProgress() {
50664
+ if (!isNativeSourceCanonicalHistory(this.provider?.nativeHistory)) return null;
50665
+ let messages = null;
50666
+ try {
50667
+ messages = this.readExternalCompletionMessages();
50668
+ } catch {
50669
+ return null;
50670
+ }
50671
+ const probe = this.lastExternalCompletionProbe;
50672
+ if (!probe) return null;
50673
+ return {
50674
+ msgCount: typeof probe.msgCount === "number" ? probe.msgCount : Array.isArray(messages) ? messages.length : 0,
50675
+ sourceMtimeMs: typeof probe.sourceMtimeMs === "number" ? probe.sourceMtimeMs : 0
50676
+ };
50677
+ }
50678
+ /**
50679
+ * KIMI-MESH-COMPLETION-EMIT (axis 2). Last-chance completion emit for a mesh
50680
+ * DELEGATED worker whose PTY has already exited (e.g. killed by a false stall)
50681
+ * and is about to be auto-cleaned (cli-manager.startCliExitMonitor →
50682
+ * removeInstance closes the event-emit window forever). If the worker actually
50683
+ * FINISHED its assigned turn — its authoritative native transcript holds a final
50684
+ * assistant message for the injected task — but the completion event never fired
50685
+ * (the stall-kill happened before the FSM's idle transition), emit it now so the
50686
+ * coordinator learns the task completed instead of waiting ~180s for the reconcile
50687
+ * transcript-poll to reclaim it.
50688
+ *
50689
+ * Scope & guards (must all hold to emit):
50690
+ * • mesh worker session only — a normal standalone session's ordinary exit is
50691
+ * never synthesized (isMeshWorkerSession()).
50692
+ * • DOUBLE-EMIT guard — refuse if this turn's completion already fired
50693
+ * (lastEmittedCompletion matches the current taskId). A worker that completed
50694
+ * cleanly and is merely being cleaned up never double-emits.
50695
+ * • evidence gate — reuse the same final-assistant/turn-scoped summary machinery
50696
+ * as the normal completion path (completionFinalSummary over the native
50697
+ * transcript). No in-turn assistant summary ⇒ no proof of completion ⇒ leave
50698
+ * the reclaim path to handle a genuinely-unfinished worker.
50699
+ *
50700
+ * Returns true when a synthetic completion was emitted, false otherwise. Safe to
50701
+ * call unconditionally from the cleanup path.
50702
+ */
50703
+ flushMeshCompletionBeforeCleanup() {
50704
+ if (!this.isMeshWorkerSession()) return false;
50705
+ const taskId = this.completingTurnTaskId();
50706
+ if (this.lastEmittedCompletion && this.lastEmittedCompletion.taskId === (taskId ?? "")) {
50707
+ return false;
50708
+ }
50709
+ if (!this.injectedTaskHasStartedGenerating()) return false;
50710
+ const turnStartedAt = typeof this.adapter?.currentTurnStartedAt === "number" ? this.adapter.currentTurnStartedAt : this.meshTaskInjectedAt || void 0;
50711
+ let parsedMessages;
50712
+ try {
50713
+ parsedMessages = this.adapter?.getScriptParsedStatus?.()?.messages;
50714
+ } catch {
50715
+ parsedMessages = void 0;
50716
+ }
50717
+ let finalSummary;
50718
+ try {
50719
+ finalSummary = this.completionFinalSummary(parsedMessages, turnStartedAt);
50720
+ } catch {
50721
+ finalSummary = void 0;
50722
+ }
50723
+ if (!finalSummary) return false;
50724
+ LOG.warn("CLI", `[${this.type}] emitting pre-cleanup mesh completion for session ${this.instanceId} task=${taskId ?? "(none)"} \u2014 PTY exited before the completion event fired but the native transcript shows a finished turn; synthesizing completion so the coordinator is not left waiting for reclaim.`);
50725
+ if (this.isMeshWorkerSession()) {
50726
+ traceMeshEventStage("fired", this.meshTraceCtx(), "pre_cleanup_transcript_completion");
50727
+ }
50728
+ this.emitGeneratingCompleted({
50729
+ chatTitle: "",
50730
+ duration: void 0,
50731
+ timestamp: Date.now(),
50732
+ taskId,
50733
+ finalSummary,
50734
+ evidenceLevel: "transcript",
50735
+ completionDiagnostic: { source: "pre_cleanup_transcript_completion" }
50736
+ });
50737
+ return true;
50586
50738
  }
50587
50739
  /**
50588
50740
  * AUTOAPPROVE-FLAP-RECUR (Fix A+B): how long a busy blip / modal scroll-out may
@@ -50984,6 +51136,7 @@ var CliProviderInstance = class _CliProviderInstance {
50984
51136
  if (summary) {
50985
51137
  this.lastCompletionSummary = { content: summary, receivedAt: opts.timestamp };
50986
51138
  }
51139
+ this.lastEmittedCompletion = { taskId: typeof opts.taskId === "string" ? opts.taskId : "", at: Date.now() };
50987
51140
  this.pushEvent({
50988
51141
  event: "agent:generating_completed",
50989
51142
  chatTitle: opts.chatTitle,
@@ -53780,6 +53933,15 @@ var DaemonCliManager = class {
53780
53933
  clearInterval(checkStopped);
53781
53934
  setTimeout(() => {
53782
53935
  if (this.adapters.has(key2)) {
53936
+ try {
53937
+ const inst = instanceManager?.getInstance(key2);
53938
+ if (typeof inst?.flushMeshCompletionBeforeCleanup === "function") {
53939
+ const emitted = inst.flushMeshCompletionBeforeCleanup();
53940
+ if (emitted) LOG.info("CLI", `Emitted pre-cleanup mesh completion for ${cliType} session ${key2} before auto-clean`);
53941
+ }
53942
+ } catch (e) {
53943
+ LOG.warn("CLI", `pre-cleanup mesh completion flush failed for ${key2}: ${e?.message || e}`);
53944
+ }
53783
53945
  this.adapters.delete(key2);
53784
53946
  this.deps.removeAgentTracking(key2);
53785
53947
  sessionRegistry?.unregisterByInstanceKey(key2);