@adhdev/daemon-core 0.9.82-rc.548 → 0.9.82-rc.549
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.js +28 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +28 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/commands/med-family/cli-agent.ts +50 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.549",
|
|
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.
|
|
51
|
-
"@adhdev/session-host-core": "0.9.82-rc.
|
|
50
|
+
"@adhdev/mesh-shared": "0.9.82-rc.549",
|
|
51
|
+
"@adhdev/session-host-core": "0.9.82-rc.549",
|
|
52
52
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
53
53
|
"ajv": "^8.20.0",
|
|
54
54
|
"ajv-formats": "^3.0.1",
|
|
@@ -19,6 +19,9 @@ 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';
|
|
24
|
+
|
|
22
25
|
/**
|
|
23
26
|
* MESH-WORKER-PREFS Fix B: when a mesh WORKER session's per-conversation Hide/Mute is toggled
|
|
24
27
|
* (set_conversation_prefs, forwarded to the worker daemon by the coordinator's
|
|
@@ -27,19 +30,25 @@ import type { MedFamilyContext, MedFamilyHandler } from './types.js';
|
|
|
27
30
|
* meshOwnedSessions) is only refreshed by a `mesh_forward_event` carrying `sessionSettings`.
|
|
28
31
|
* Without one the mirror stays stale, so when the coordinator re-emits the session metadata to
|
|
29
32
|
* the dashboard the old surfaceHidden/muted comes back and the toggle reverts after the 8s
|
|
30
|
-
* optimistic overlay expires. Push a
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
33
|
+
* optimistic overlay expires. Push a `mesh_forward_event` to the coordinator daemon with the
|
|
34
|
+
* fresh prefs so its mirror updates immediately. The coordinator's mesh_forward_event command
|
|
35
|
+
* consumer calls updateMeshOwnedSession(args) BEFORE routing, so the mirror is refreshed even
|
|
36
|
+
* though the (non coordinator-event) event name is otherwise ignored.
|
|
37
|
+
*
|
|
38
|
+
* Fix (4) — atomicity: the worker's local updateSettings and the coordinator mirror refresh were
|
|
39
|
+
* fire-and-forget, so a dropped/rejected forward left the mirror stale and the dashboard toggle
|
|
40
|
+
* silently reverted (the exact "restore does nothing" defect). This now AWAITS the dispatch, retries
|
|
41
|
+
* once on failure, and reports the outcome so the handler can flag a stale mirror to the caller
|
|
42
|
+
* instead of returning an unqualified success. It never rejects — a failure is reported, not thrown,
|
|
43
|
+
* so the local write (already applied) still returns success with a mirrorStale marker.
|
|
35
44
|
*/
|
|
36
|
-
function forwardConversationPrefsToCoordinator(
|
|
45
|
+
async function forwardConversationPrefsToCoordinator(
|
|
37
46
|
ctx: MedFamilyContext,
|
|
38
47
|
sessionId: string,
|
|
39
48
|
patch: Record<string, unknown>,
|
|
40
|
-
):
|
|
49
|
+
): Promise<MirrorForwardOutcome> {
|
|
41
50
|
const dispatch = ctx.deps.dispatchMeshCommand;
|
|
42
|
-
if (!dispatch) return;
|
|
51
|
+
if (!dispatch) return 'skipped';
|
|
43
52
|
let settings: Record<string, unknown> = {};
|
|
44
53
|
try {
|
|
45
54
|
const state = ctx.deps.instanceManager.getInstance(sessionId)?.getState?.();
|
|
@@ -51,7 +60,7 @@ function forwardConversationPrefsToCoordinator(
|
|
|
51
60
|
const coordinatorDaemonId = readNonEmptyString(settings.meshCoordinatorDaemonId);
|
|
52
61
|
const meshId = readNonEmptyString(settings.meshNodeFor);
|
|
53
62
|
// A non-delegated (non-mesh) session has no coordinator anchor — nothing to mirror.
|
|
54
|
-
if (!coordinatorDaemonId || !meshId) return;
|
|
63
|
+
if (!coordinatorDaemonId || !meshId) return 'skipped';
|
|
55
64
|
const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.meshLastNodeId);
|
|
56
65
|
|
|
57
66
|
// sessionSettings mirrors the exact keys updateMeshOwnedSession merges onto the mirror's
|
|
@@ -64,8 +73,20 @@ function forwardConversationPrefsToCoordinator(
|
|
|
64
73
|
...(nodeId ? { nodeId } : {}),
|
|
65
74
|
sessionSettings: { ...patch },
|
|
66
75
|
};
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
|
|
77
|
+
// Await the mirror refresh (Fix 4). One retry: a transient P2P blip on the coordinator
|
|
78
|
+
// channel shouldn't strand the mirror stale when the worker's own state already moved.
|
|
79
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
80
|
+
try {
|
|
81
|
+
await dispatch(coordinatorDaemonId, 'mesh_forward_event', payload);
|
|
82
|
+
return 'refreshed';
|
|
83
|
+
} catch (e: any) {
|
|
84
|
+
if (attempt === 0) continue;
|
|
85
|
+
LOG.warn('Mesh', `[Mesh] set_conversation_prefs mirror forward to coordinator ${coordinatorDaemonId.slice(0, 12)} failed after retry: ${e?.message || e}`);
|
|
86
|
+
return 'failed';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return 'failed';
|
|
69
90
|
}
|
|
70
91
|
|
|
71
92
|
export const cliAgentHandlers: Record<string, MedFamilyHandler> = {
|
|
@@ -139,12 +160,25 @@ export const cliAgentHandlers: Record<string, MedFamilyHandler> = {
|
|
|
139
160
|
// Push a fresh status snapshot so all clients see the updated
|
|
140
161
|
// surfaceHidden/muted immediately (cloud path). The standalone server has a
|
|
141
162
|
// parallel broadcast gate keyed on the command name (see daemon-standalone).
|
|
163
|
+
// Fired BEFORE the awaited mirror refresh so the local write is visible even if
|
|
164
|
+
// the coordinator mirror dispatch is slow or fails.
|
|
142
165
|
ctx.deps.onStatusChange?.();
|
|
143
|
-
// MESH-WORKER-PREFS Fix B: if this is a mesh worker session, mirror the fresh
|
|
144
|
-
// the coordinator so its stale meshOwnedSessions copy can't revert the toggle
|
|
145
|
-
// re-emits the dashboard metadata.
|
|
146
|
-
|
|
147
|
-
|
|
166
|
+
// MESH-WORKER-PREFS Fix B + Fix (4): if this is a mesh worker session, mirror the fresh
|
|
167
|
+
// prefs onto the coordinator so its stale meshOwnedSessions copy can't revert the toggle
|
|
168
|
+
// when it re-emits the dashboard metadata. AWAITED (with one retry) so a dropped mirror
|
|
169
|
+
// refresh is reported to the caller (mirrorStale) instead of silently leaving the
|
|
170
|
+
// coordinator to re-stamp the old value — the "restore does nothing" revert. No-op for
|
|
171
|
+
// non-mesh sessions. Never throws: the local write already succeeded.
|
|
172
|
+
const mirror = await forwardConversationPrefsToCoordinator(ctx, sessionId, patch);
|
|
173
|
+
return {
|
|
174
|
+
success: true,
|
|
175
|
+
sessionId,
|
|
176
|
+
...patch,
|
|
177
|
+
// Only surface the mirror status for a genuine mesh worker session (refreshed/failed);
|
|
178
|
+
// a non-mesh session ('skipped') carries no mirror field, so nothing changes for it.
|
|
179
|
+
...(mirror === 'failed' ? { mirrorStale: true } : {}),
|
|
180
|
+
...(mirror === 'refreshed' ? { mirrorRefreshed: true } : {}),
|
|
181
|
+
};
|
|
148
182
|
},
|
|
149
183
|
|
|
150
184
|
agent_command: async (ctx: MedFamilyContext, args: any) => {
|