@adhdev/daemon-core 0.9.82-rc.369 → 0.9.82-rc.370

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.
@@ -1754,98 +1754,35 @@ export function shouldForceInjectMeshEvent(eventName: unknown): boolean {
1754
1754
  return typeof eventName === 'string' && MESH_FORCE_INJECT_EVENTS.has(eventName);
1755
1755
  }
1756
1756
 
1757
- function injectMeshSystemMessage(components: DaemonComponents, args: {
1758
- meshId: string;
1759
- sourceInstanceId?: string;
1760
- nodeId?: string;
1761
- nodeLabel: string;
1762
- event: string;
1763
- metadataEvent: Record<string, unknown>;
1764
- }) {
1765
- const eventSessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
1766
- const eventNodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1767
-
1768
- // EVTTRACE correlation context for this event's coordinator-side lifecycle (queue /
1769
- // dedup / suppress). Observation only — never read by any decision below.
1770
- const traceCtx = {
1771
- taskId: args.metadataEvent.taskId,
1772
- sessionId: eventSessionId,
1773
- nodeId: eventNodeId,
1774
- meshId: args.meshId,
1775
- event: args.event,
1776
- };
1777
-
1778
- const sourceSession = args.sourceInstanceId
1779
- ? components.instanceManager.getInstance(args.sourceInstanceId)
1780
- : undefined;
1781
- const workerCoordinatorDaemonId = readNonEmptyString(
1782
- (sourceSession?.getState()?.settings as Record<string, unknown>)?.meshCoordinatorDaemonId,
1783
- );
1784
- // Session-level routing anchor (multi-coordinator). Prefer the LIVE worker session's
1785
- // stamp; fall back to a relayed value carried in metadataEvent.meshCoordinatorSessionId
1786
- // (a remote worker's completion arrives via handleMeshForwardEvent with no local
1787
- // sourceSession, so the stamp can only ride in the relayed metadata). Empty on legacy /
1788
- // version-skewed dispatches → the event stays daemon-broadcast (no regression).
1789
- const workerCoordinatorSessionId = readNonEmptyString(
1790
- (sourceSession?.getState()?.settings as Record<string, unknown>)?.meshCoordinatorSessionId,
1791
- ) || readNonEmptyString(args.metadataEvent.meshCoordinatorSessionId);
1792
-
1793
- // T2: a summary-less completion (and any non-completion status-sync event) carries no
1794
- // assistant text on the event, so resolveMeshSurfacedSessionPreview had nothing to surface
1795
- // and the coordinator's inbox mirror stayed stuck on the first dispatched user task. When
1796
- // THIS daemon hosts the live worker instance (sourceSession present), derive the worker's
1797
- // latest display message straight from its transcript and attach it to the event as
1798
- // lastMessagePreview/lastMessageRole/lastMessageAt. resolveMeshSurfacedSessionPreview reads
1799
- // these as an assistant-only fallback; they also ride the pending-queue + P2P relay
1800
- // (handleMeshForwardEvent whitelist) so a remote coordinator can surface them. A remote
1801
- // coordinator has no local instance and keeps relying on the relayed fields — unchanged.
1802
- const enrichedMetadataEvent = ((): Record<string, unknown> => {
1803
- const last = sourceSession ? getLastDisplayMessage(sourceSession.getState()) : null;
1804
- if (!last || !last.preview) return args.metadataEvent;
1805
- return {
1806
- ...args.metadataEvent,
1807
- lastMessagePreview: last.preview,
1808
- lastMessageRole: last.role,
1809
- ...(last.receivedAt > 0 ? { lastMessageAt: last.receivedAt } : {}),
1810
- };
1811
- })();
1812
-
1813
- // R2: cloud P2P dashboard metadata sync. The cloud daemon used to do this from its own
1814
- // relay listener; now the single core forwarder invokes the injected hook (no-op on
1815
- // standalone) so the event path stays single-listener and the local code path is identical
1816
- // across standalone and cloud.
1817
- if (components.onMeshCoordinatorEventForwarded) {
1818
- try {
1819
- // T: the coordinator surfaces a remote worker's session but holds no local
1820
- // instance for it, so the status snapshot can't derive a preview and the
1821
- // mirror would stay stuck on the first dispatched user task. Resolve the
1822
- // worker's latest assistant reply (carried on the completion event's
1823
- // finalSummary / workerResult) into a preview the mirror can stamp, so the
1824
- // mobile inbox reflects the assistant response. Completion events carry assistant
1825
- // text as finalSummary; a summary-less completion / status sync falls back to the
1826
- // worker's latest assistant display message (enrichedMetadataEvent.lastMessage*).
1827
- // For a mid-turn user-only event this is undefined and the prior surfaced preview
1828
- // is preserved downstream (no clobber).
1829
- const surfacedPreview = resolveMeshSurfacedSessionPreview(enrichedMetadataEvent);
1830
- components.onMeshCoordinatorEventForwarded({
1831
- event: args.event,
1832
- meshId: args.meshId,
1833
- nodeId: eventNodeId || undefined,
1834
- ...enrichedMetadataEvent,
1835
- // Ensure a `workspace` field reaches updateMeshOwnedSession even when the
1836
- // worker provider event only carried `workspaceName`. The merge spread of
1837
- // metadataEvent above wins when it already has a non-empty `workspace`.
1838
- workspace: readNonEmptyString(args.metadataEvent.workspace)
1839
- || readNonEmptyString(args.metadataEvent.workspaceName)
1840
- || undefined,
1841
- ...(surfacedPreview ? {
1842
- meshSessionLastMessagePreview: surfacedPreview.preview,
1843
- meshSessionLastMessageRole: surfacedPreview.role,
1844
- meshSessionLastMessageAt: surfacedPreview.receivedAt || undefined,
1845
- } : {}),
1846
- });
1847
- } catch { /* dashboard metadata sync is best-effort */ }
1848
- }
1757
+ // Coordinator-side suppression/reconcile gate for an incoming mesh event. Each clause is a
1758
+ // closed dedup/suppression concern that only inspects the event + already-resolved context and
1759
+ // either (a) returns a `suppress` result the caller forwards verbatim, (b) returns a `reconcile`
1760
+ // signal carrying the rewritten metadataEvent for the caller to re-inject as
1761
+ // agent:generating_completed, or (c) returns null to let the event fall through to the
1762
+ // terminal/ledger machinery. Extracted verbatim from injectMeshSystemMessage — no behavior
1763
+ // change; the only side effects (best-effort remote-idle cleanup, LOG, trace) fire on the same
1764
+ // paths as before.
1765
+ function evaluateMeshEventSuppression(
1766
+ args: {
1767
+ meshId: string;
1768
+ sourceInstanceId?: string;
1769
+ nodeId?: string;
1770
+ nodeLabel: string;
1771
+ event: string;
1772
+ metadataEvent: Record<string, unknown>;
1773
+ },
1774
+ ctx: {
1775
+ traceCtx: Parameters<typeof traceMeshEventDrop>[1];
1776
+ eventSessionId: string;
1777
+ eventNodeId: string;
1778
+ eventTimestamp: number | null;
1779
+ workerCoordinatorDaemonId: string | undefined;
1780
+ },
1781
+ ):
1782
+ | { kind: 'suppress'; result: { success: true; forwarded: 0; suppressed: true; [extra: string]: unknown } }
1783
+ | { kind: 'reconcile'; metadataEvent: Record<string, unknown> }
1784
+ | null {
1785
+ const { traceCtx, eventSessionId, eventNodeId, eventTimestamp, workerCoordinatorDaemonId } = ctx;
1849
1786
 
1850
1787
  const intentionalCleanupStop = shouldSuppressIntentionalCleanupStop({
1851
1788
  event: args.event,
@@ -1862,7 +1799,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
1862
1799
  }
1863
1800
  LOG.info('MeshEvents', `Suppressed ${args.event} for intentionally cleanup-stopped session ${eventSessionId || '(unknown session)'}`);
1864
1801
  traceMeshEventDrop('intentional_cleanup_stop', traceCtx);
1865
- return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
1802
+ return { kind: 'suppress', result: { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true } };
1866
1803
  }
1867
1804
 
1868
1805
  if (args.event === 'monitor:no_progress') {
@@ -1875,21 +1812,20 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
1875
1812
  });
1876
1813
  if (reconciledCompletion?.source === 'no_progress_reconciliation') {
1877
1814
  LOG.info('MeshEvents', `Reconciled no-progress monitor to completion for session ${eventSessionId || '(unknown session)'}`);
1878
- return injectMeshSystemMessage(components, {
1879
- ...args,
1880
- event: 'agent:generating_completed',
1881
- metadataEvent: reconciledCompletion,
1882
- });
1815
+ return { kind: 'reconcile', metadataEvent: reconciledCompletion };
1883
1816
  }
1884
1817
  if (reconciledCompletion?.source === 'no_progress_terminal_ledger_suppression') {
1885
1818
  LOG.info('MeshEvents', `Suppressed no-progress monitor because terminal ledger evidence already exists for session ${eventSessionId || '(unknown session)'}`);
1886
1819
  traceMeshEventDrop('no_progress_terminal_ledger_suppression', traceCtx, `terminalKind=${reconciledCompletion.terminalLedgerKind}`);
1887
1820
  return {
1888
- success: true,
1889
- forwarded: 0,
1890
- suppressed: true,
1891
- terminalLedgerEvidence: true,
1892
- terminalLedgerKind: reconciledCompletion.terminalLedgerKind,
1821
+ kind: 'suppress',
1822
+ result: {
1823
+ success: true,
1824
+ forwarded: 0,
1825
+ suppressed: true,
1826
+ terminalLedgerEvidence: true,
1827
+ terminalLedgerKind: reconciledCompletion.terminalLedgerKind,
1828
+ },
1893
1829
  };
1894
1830
  }
1895
1831
  }
@@ -1897,10 +1833,9 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
1897
1833
  if (isDuplicateRefineTerminalEvent(args.meshId, args.event, args.metadataEvent)) {
1898
1834
  LOG.info('MeshEvents', `Suppressed duplicate ${args.event} for refine job ${readRefineJobId({ metadataEvent: args.metadataEvent })}`);
1899
1835
  traceMeshEventDrop('duplicate_refine_terminal', traceCtx);
1900
- return { success: true, forwarded: 0, suppressed: true, duplicateRefineTerminalEvent: true };
1836
+ return { kind: 'suppress', result: { success: true, forwarded: 0, suppressed: true, duplicateRefineTerminalEvent: true } };
1901
1837
  }
1902
1838
 
1903
- const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
1904
1839
  if (args.event === 'agent:waiting_approval' && eventSessionId) {
1905
1840
  const duplicateApproval = isDuplicateMeshApprovalEvent({
1906
1841
  meshId: args.meshId,
@@ -1913,7 +1848,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
1913
1848
  if (duplicateApproval) {
1914
1849
  LOG.info('MeshEvents', `Suppressed duplicate approval event for mesh ${args.meshId} session ${eventSessionId}`);
1915
1850
  traceMeshEventDrop('duplicate_approval', traceCtx);
1916
- return { success: true, forwarded: 0, suppressed: true, duplicateApproval: true };
1851
+ return { kind: 'suppress', result: { success: true, forwarded: 0, suppressed: true, duplicateApproval: true } };
1917
1852
  }
1918
1853
  }
1919
1854
  if (args.event === 'agent:generating_completed' && eventSessionId) {
@@ -1965,7 +1900,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
1965
1900
  ) {
1966
1901
  LOG.info('MeshEvents', `Suppressed duplicate completion with existing terminal ledger evidence for mesh ${args.meshId} session ${eventSessionId}`);
1967
1902
  traceMeshEventDrop('duplicate_completion_terminal_ledger', traceCtx);
1968
- return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true, terminalLedgerEvidence: true };
1903
+ return { kind: 'suppress', result: { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true, terminalLedgerEvidence: true } };
1969
1904
  }
1970
1905
  }
1971
1906
  }
@@ -1984,7 +1919,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
1984
1919
  if (duplicateCompletion) {
1985
1920
  LOG.info('MeshEvents', `Suppressed duplicate completion for mesh ${args.meshId} session ${eventSessionId}`);
1986
1921
  traceMeshEventDrop('duplicate_completion', traceCtx);
1987
- return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true };
1922
+ return { kind: 'suppress', result: { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true } };
1988
1923
  }
1989
1924
  }
1990
1925
  if (args.event === 'agent:stopped' && eventSessionId) {
@@ -2003,8 +1938,126 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
2003
1938
  if (duplicateStopped) {
2004
1939
  LOG.info('MeshEvents', `Suppressed duplicate stopped event for mesh ${args.meshId} session ${eventSessionId}`);
2005
1940
  traceMeshEventDrop('duplicate_stopped', traceCtx);
2006
- return { success: true, forwarded: 0, suppressed: true, duplicateStopped: true };
1941
+ return { kind: 'suppress', result: { success: true, forwarded: 0, suppressed: true, duplicateStopped: true } };
1942
+ }
1943
+ }
1944
+
1945
+ return null;
1946
+ }
1947
+
1948
+ function injectMeshSystemMessage(components: DaemonComponents, args: {
1949
+ meshId: string;
1950
+ sourceInstanceId?: string;
1951
+ nodeId?: string;
1952
+ nodeLabel: string;
1953
+ event: string;
1954
+ metadataEvent: Record<string, unknown>;
1955
+ }) {
1956
+ const eventSessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
1957
+ const eventNodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1958
+
1959
+ // EVTTRACE correlation context for this event's coordinator-side lifecycle (queue /
1960
+ // dedup / suppress). Observation only — never read by any decision below.
1961
+ const traceCtx = {
1962
+ taskId: args.metadataEvent.taskId,
1963
+ sessionId: eventSessionId,
1964
+ nodeId: eventNodeId,
1965
+ meshId: args.meshId,
1966
+ event: args.event,
1967
+ };
1968
+
1969
+ const sourceSession = args.sourceInstanceId
1970
+ ? components.instanceManager.getInstance(args.sourceInstanceId)
1971
+ : undefined;
1972
+ const workerCoordinatorDaemonId = readNonEmptyString(
1973
+ (sourceSession?.getState()?.settings as Record<string, unknown>)?.meshCoordinatorDaemonId,
1974
+ );
1975
+ // Session-level routing anchor (multi-coordinator). Prefer the LIVE worker session's
1976
+ // stamp; fall back to a relayed value carried in metadataEvent.meshCoordinatorSessionId
1977
+ // (a remote worker's completion arrives via handleMeshForwardEvent with no local
1978
+ // sourceSession, so the stamp can only ride in the relayed metadata). Empty on legacy /
1979
+ // version-skewed dispatches → the event stays daemon-broadcast (no regression).
1980
+ const workerCoordinatorSessionId = readNonEmptyString(
1981
+ (sourceSession?.getState()?.settings as Record<string, unknown>)?.meshCoordinatorSessionId,
1982
+ ) || readNonEmptyString(args.metadataEvent.meshCoordinatorSessionId);
1983
+
1984
+ // T2: a summary-less completion (and any non-completion status-sync event) carries no
1985
+ // assistant text on the event, so resolveMeshSurfacedSessionPreview had nothing to surface
1986
+ // and the coordinator's inbox mirror stayed stuck on the first dispatched user task. When
1987
+ // THIS daemon hosts the live worker instance (sourceSession present), derive the worker's
1988
+ // latest display message straight from its transcript and attach it to the event as
1989
+ // lastMessagePreview/lastMessageRole/lastMessageAt. resolveMeshSurfacedSessionPreview reads
1990
+ // these as an assistant-only fallback; they also ride the pending-queue + P2P relay
1991
+ // (handleMeshForwardEvent whitelist) so a remote coordinator can surface them. A remote
1992
+ // coordinator has no local instance and keeps relying on the relayed fields — unchanged.
1993
+ const enrichedMetadataEvent = ((): Record<string, unknown> => {
1994
+ const last = sourceSession ? getLastDisplayMessage(sourceSession.getState()) : null;
1995
+ if (!last || !last.preview) return args.metadataEvent;
1996
+ return {
1997
+ ...args.metadataEvent,
1998
+ lastMessagePreview: last.preview,
1999
+ lastMessageRole: last.role,
2000
+ ...(last.receivedAt > 0 ? { lastMessageAt: last.receivedAt } : {}),
2001
+ };
2002
+ })();
2003
+
2004
+ // R2: cloud P2P dashboard metadata sync. The cloud daemon used to do this from its own
2005
+ // relay listener; now the single core forwarder invokes the injected hook (no-op on
2006
+ // standalone) so the event path stays single-listener and the local code path is identical
2007
+ // across standalone and cloud.
2008
+ if (components.onMeshCoordinatorEventForwarded) {
2009
+ try {
2010
+ // T: the coordinator surfaces a remote worker's session but holds no local
2011
+ // instance for it, so the status snapshot can't derive a preview and the
2012
+ // mirror would stay stuck on the first dispatched user task. Resolve the
2013
+ // worker's latest assistant reply (carried on the completion event's
2014
+ // finalSummary / workerResult) into a preview the mirror can stamp, so the
2015
+ // mobile inbox reflects the assistant response. Completion events carry assistant
2016
+ // text as finalSummary; a summary-less completion / status sync falls back to the
2017
+ // worker's latest assistant display message (enrichedMetadataEvent.lastMessage*).
2018
+ // For a mid-turn user-only event this is undefined and the prior surfaced preview
2019
+ // is preserved downstream (no clobber).
2020
+ const surfacedPreview = resolveMeshSurfacedSessionPreview(enrichedMetadataEvent);
2021
+ components.onMeshCoordinatorEventForwarded({
2022
+ event: args.event,
2023
+ meshId: args.meshId,
2024
+ nodeId: eventNodeId || undefined,
2025
+ ...enrichedMetadataEvent,
2026
+ // Ensure a `workspace` field reaches updateMeshOwnedSession even when the
2027
+ // worker provider event only carried `workspaceName`. The merge spread of
2028
+ // metadataEvent above wins when it already has a non-empty `workspace`.
2029
+ workspace: readNonEmptyString(args.metadataEvent.workspace)
2030
+ || readNonEmptyString(args.metadataEvent.workspaceName)
2031
+ || undefined,
2032
+ ...(surfacedPreview ? {
2033
+ meshSessionLastMessagePreview: surfacedPreview.preview,
2034
+ meshSessionLastMessageRole: surfacedPreview.role,
2035
+ meshSessionLastMessageAt: surfacedPreview.receivedAt || undefined,
2036
+ } : {}),
2037
+ });
2038
+ } catch { /* dashboard metadata sync is best-effort */ }
2039
+ }
2040
+
2041
+ const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
2042
+ // Coordinator-side dedup/suppression gate (extracted, behavior-preserving). A non-null
2043
+ // outcome either short-circuits with a forwarded result or signals a no-progress→completion
2044
+ // reconciliation that we re-inject; null lets the event fall through to the ledger machinery.
2045
+ const suppression = evaluateMeshEventSuppression(args, {
2046
+ traceCtx,
2047
+ eventSessionId,
2048
+ eventNodeId,
2049
+ eventTimestamp,
2050
+ workerCoordinatorDaemonId,
2051
+ });
2052
+ if (suppression) {
2053
+ if (suppression.kind === 'reconcile') {
2054
+ return injectMeshSystemMessage(components, {
2055
+ ...args,
2056
+ event: 'agent:generating_completed',
2057
+ metadataEvent: suppression.metadataEvent,
2058
+ });
2007
2059
  }
2060
+ return suppression.result;
2008
2061
  }
2009
2062
 
2010
2063
  function markSessionTerminal(sessionId: string, outcome: 'completed' | 'failed', occurredAtMs?: number | null, opts?: { tentativeIfDirect?: boolean }): { id?: string } | null {
@@ -15,6 +15,7 @@ import { assertProviderSupportsDeclaredInput, getEffectiveMessageInputSupport }
15
15
  import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext, ProviderErrorReason, HotChatSessionState, SessionModalState } from './provider-instance.js';
16
16
  import { normalizeInteractivePrompt, normalizeInteractivePromptResponse, type InteractivePrompt } from './types/interactive-prompt.js';
17
17
  import { ProviderCliAdapter } from '../cli-adapters/provider-cli-adapter.js';
18
+ import { shortHash } from '../system/hash.js';
18
19
  import type { CliProviderModule } from '../cli-adapters/provider-cli-adapter.js';
19
20
  import { createCliAdapter } from './spec/route.js';
20
21
  import type { PtyRuntimeMetadata, PtyTransportFactory } from '../cli-adapters/pty-transport.js';
@@ -1056,11 +1057,7 @@ export class CliProviderInstance implements ProviderInstance {
1056
1057
 
1057
1058
  const receivedAt = Date.now();
1058
1059
  this.lastAcknowledgedUserInputAt = receivedAt;
1059
- const dedupKey = `user_input_ack:${crypto
1060
- .createHash('sha256')
1061
- .update(`${this.instanceId}:${content}:${receivedAt}`)
1062
- .digest('hex')
1063
- .slice(0, 24)}`;
1060
+ const dedupKey = `user_input_ack:${shortHash(`${this.instanceId}:${content}:${receivedAt}`, 24)}`;
1064
1061
  this.appendRuntimeMessage(buildChatMessage({
1065
1062
  role: 'user',
1066
1063
  senderName: 'User',
@@ -18,6 +18,7 @@ import * as path from 'path';
18
18
  import * as os from 'os';
19
19
  import * as chokidar from 'chokidar';
20
20
  import { registerIDEDefinition } from '../detection/ide-detector.js';
21
+ import { sha256Hex } from '../system/hash.js';
21
22
  import { LOG } from '../logging/logger.js';
22
23
  import { VersionArchive } from './version-archive.js';
23
24
  import type {
@@ -1604,10 +1605,7 @@ export class ProviderLoader {
1604
1605
  });
1605
1606
 
1606
1607
  // Verify checksum
1607
- const actualChecksum = await new Promise<string>((resolve) => {
1608
- const crypto = require('crypto') as typeof import('crypto');
1609
- resolve(crypto.createHash('sha256').update(manifestBody, 'utf-8').digest('hex'));
1610
- });
1608
+ const actualChecksum = sha256Hex(manifestBody);
1611
1609
  if (actualChecksum !== checksum) {
1612
1610
  this.log(`⚠ Registry checksum mismatch for ${type}@${version} — skipping`);
1613
1611
  continue;