@adhdev/daemon-core 0.9.82-rc.550 → 0.9.82-rc.552

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.550",
3
+ "version": "0.9.82-rc.552",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -47,8 +47,8 @@
47
47
  "author": "vilmire",
48
48
  "license": "AGPL-3.0-or-later",
49
49
  "dependencies": {
50
- "@adhdev/mesh-shared": "0.9.82-rc.550",
51
- "@adhdev/session-host-core": "0.9.82-rc.550",
50
+ "@adhdev/mesh-shared": "0.9.82-rc.552",
51
+ "@adhdev/session-host-core": "0.9.82-rc.552",
52
52
  "@agentclientprotocol/sdk": "^0.16.1",
53
53
  "ajv": "^8.20.0",
54
54
  "ajv-formats": "^3.0.1",
@@ -96,6 +96,14 @@ export interface DaemonInitConfig {
96
96
  getMeshPeerConnectionStatus?: (daemonId: string) => Record<string, unknown> | null;
97
97
  /** Cloud-only: P2P dashboard metadata sync after the core forwarder handles a mesh event. */
98
98
  onMeshCoordinatorEventForwarded?: (payload: Record<string, unknown>) => void;
99
+ /**
100
+ * Cloud-only: refresh THIS daemon's own coordinator mirror for a self-hosted mesh session
101
+ * (coordinator == this daemon), used by the SELF-DIAL branch of set_conversation_prefs so a
102
+ * locally coordinated session updates its mirror in-process instead of P2P-dialing its own id
103
+ * (refused as SELF_DIAL). Injected by daemon-cloud (updateMeshOwnedSession + dashboard flush);
104
+ * absent on standalone (no coordinator mirror).
105
+ */
106
+ updateLocalMeshOwnedSession?: (payload: Record<string, unknown>) => void;
99
107
  }
100
108
 
101
109
  // ─── Result ───
@@ -378,6 +386,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
378
386
  statusVersion: config.statusVersion,
379
387
  getMeshPeerConnectionStatus: config.getMeshPeerConnectionStatus,
380
388
  dispatchMeshCommand: config.dispatchMeshCommand,
389
+ updateLocalMeshOwnedSession: config.updateLocalMeshOwnedSession,
381
390
  getCdpLogFn: config.getCdpLogFn || ((ideType: string) => LOG.forComponent(`CDP:${ideType}`).asLogFn()),
382
391
  });
383
392
 
@@ -8,7 +8,7 @@
8
8
  * + ctx.launchIde — no executeDaemonCommand recursion) or CLI/ACP restarts.
9
9
  * Extracted verbatim from executeDaemonCommand.
10
10
  */
11
- import { meshNodeIdMatches } from '@adhdev/mesh-shared';
11
+ import { meshNodeIdMatches, daemonIdsEquivalent } from '@adhdev/mesh-shared';
12
12
  import { supportsExplicitSessionResume } from '../cli-manager.js';
13
13
  import { loadState } from '../../config/state-store.js';
14
14
  import { getRecentActivity } from '../../config/recent-activity.js';
@@ -19,8 +19,13 @@ import { LOG } from '../../logging/logger.js';
19
19
  import { readStringValue } from '../router.js';
20
20
  import type { MedFamilyContext, MedFamilyHandler } from './types.js';
21
21
 
22
- /** Outcome of the coordinator-mirror refresh: not a mesh worker (no-op), refreshed, or failed. */
23
- type MirrorForwardOutcome = 'skipped' | 'refreshed' | 'failed';
22
+ /**
23
+ * Outcome of the coordinator-mirror refresh: not a mesh worker (no-op), refreshed via the remote
24
+ * relay, refreshed locally (self-hosted coordinator == this daemon — no P2P dial), or failed.
25
+ * 'refreshed-local' and 'refreshed' both mean the mirror is now fresh; the caller reports them
26
+ * identically (mirrorRefreshed, no mirrorStale) so a self-hosted toggle never rolls back.
27
+ */
28
+ type MirrorForwardOutcome = 'skipped' | 'refreshed' | 'refreshed-local' | 'failed';
24
29
 
25
30
  /**
26
31
  * MESH-WORKER-PREFS Fix B: when a mesh WORKER session's per-conversation Hide/Mute is toggled
@@ -41,6 +46,17 @@ type MirrorForwardOutcome = 'skipped' | 'refreshed' | 'failed';
41
46
  * once on failure, and reports the outcome so the handler can flag a stale mirror to the caller
42
47
  * instead of returning an unqualified success. It never rejects — a failure is reported, not thrown,
43
48
  * so the local write (already applied) still returns success with a mirrorStale marker.
49
+ *
50
+ * SELF-DIAL (mission fix/cloud-local-hide-self-dial): a self-hosted mesh session — one this daemon
51
+ * both coordinates AND hosts — carries a meshCoordinatorDaemonId equal to this daemon's own id.
52
+ * Dispatching the mirror refresh over P2P to that id self-dials, which the mesh manager REFUSES
53
+ * (SELF_DIAL) → the old code reported that refusal as 'failed' → mirrorStale → the dashboard
54
+ * dropped its optimistic overlay and the toggle appeared to do nothing (the exact CLOUD-local
55
+ * hide/mute "무반응" defect). The remote-worker relay path (coordinatorDaemonId != this daemon)
56
+ * is unchanged. When the coordinator IS this daemon we skip the dial entirely and refresh the
57
+ * local coordinator mirror directly via updateLocalMeshOwnedSession (the cloud runtime wires it to
58
+ * the same updateMeshOwnedSession + dashboard flush the remote mesh_forward_event consumer runs),
59
+ * reporting 'refreshed-local' so no rollback occurs.
44
60
  */
45
61
  async function forwardConversationPrefsToCoordinator(
46
62
  ctx: MedFamilyContext,
@@ -74,6 +90,29 @@ async function forwardConversationPrefsToCoordinator(
74
90
  sessionSettings: { ...patch },
75
91
  };
76
92
 
93
+ // SELF-DIAL guard: the coordinator anchor resolves to THIS daemon. Refresh the local mirror
94
+ // in-process instead of dispatching a P2P command to our own id (which the mesh manager
95
+ // refuses as SELF_DIAL, formerly surfacing as mirrorStale → a false rollback). Uses the
96
+ // machine-core-canonicalizing daemonIdsEquivalent — a raw === would miss a same-machine anchor
97
+ // that arrives in a different id form (daemon_mach_X vs mach_X), the repeatedly-regressed
98
+ // daemon-id form-mismatch class in this repo, and self-dial anyway.
99
+ const selfDaemonId = readNonEmptyString((ctx.deps as any).statusInstanceId);
100
+ if (selfDaemonId && daemonIdsEquivalent(coordinatorDaemonId, selfDaemonId)) {
101
+ const refreshLocal = ctx.deps.updateLocalMeshOwnedSession;
102
+ if (refreshLocal) {
103
+ try {
104
+ refreshLocal(payload);
105
+ return 'refreshed-local';
106
+ } catch (e: any) {
107
+ LOG.warn('Mesh', `[Mesh] set_conversation_prefs local coordinator-mirror refresh failed: ${e?.message || e}`);
108
+ return 'failed';
109
+ }
110
+ }
111
+ // No local mirror hook (e.g. standalone) — the live instance write already applied is the
112
+ // single source of truth, so treat the self-hosted case as refreshed rather than dialing.
113
+ return 'refreshed-local';
114
+ }
115
+
77
116
  // Await the mirror refresh (Fix 4). One retry: a transient P2P blip on the coordinator
78
117
  // channel shouldn't strand the mirror stale when the worker's own state already moved.
79
118
  for (let attempt = 0; attempt < 2; attempt++) {
@@ -174,10 +213,12 @@ export const cliAgentHandlers: Record<string, MedFamilyHandler> = {
174
213
  success: true,
175
214
  sessionId,
176
215
  ...patch,
177
- // Only surface the mirror status for a genuine mesh worker session (refreshed/failed);
216
+ // Only surface the mirror status for a genuine mesh session (refreshed/failed);
178
217
  // a non-mesh session ('skipped') carries no mirror field, so nothing changes for it.
218
+ // A self-hosted session ('refreshed-local' — coordinator == this daemon) refreshed its
219
+ // own mirror in-process and is reported as fresh so the dashboard never rolls back.
179
220
  ...(mirror === 'failed' ? { mirrorStale: true } : {}),
180
- ...(mirror === 'refreshed' ? { mirrorRefreshed: true } : {}),
221
+ ...(mirror === 'refreshed' || mirror === 'refreshed-local' ? { mirrorRefreshed: true } : {}),
181
222
  };
182
223
  },
183
224
 
@@ -158,6 +158,16 @@ export interface CommandRouterDeps {
158
158
  getMeshPeerConnectionStatus?: (daemonId: string) => Record<string, unknown> | null;
159
159
  /** Dispatch a command to a remote mesh node via P2P/relay. Injected by cloud runtime; absent in standalone. */
160
160
  dispatchMeshCommand?: (daemonId: string, cmd: string, args: Record<string, unknown>) => Promise<unknown>;
161
+ /**
162
+ * Refresh THIS daemon's own coordinator mirror (adhdev-daemon meshOwnedSessions) for a
163
+ * self-hosted mesh session, applying the same `mesh_forward_event`-shaped payload the remote
164
+ * relay would carry — used by the SELF-DIAL branch of set_conversation_prefs so a locally
165
+ * coordinated session (coordinatorDaemonId == this daemon) updates its mirror directly instead
166
+ * of dispatching a P2P command to its own id (which the mesh manager refuses as SELF_DIAL).
167
+ * Injected by the cloud runtime (calls updateMeshOwnedSession + flushes the dashboard
168
+ * subscription); absent in standalone (no coordinator mirror there).
169
+ */
170
+ updateLocalMeshOwnedSession?: (payload: Record<string, unknown>) => void;
161
171
  }
162
172
 
163
173
  export interface CommandRouterResult {