@adhdev/daemon-core 0.9.76-rc.25 → 0.9.76-rc.26

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.
@@ -67,6 +67,7 @@ export declare class ProviderInstanceManager {
67
67
  onEvent(listener: (event: ProviderEvent & {
68
68
  providerType: string;
69
69
  }) => void): void;
70
+ emitProviderEvent(providerType: string, instanceId: string, event: ProviderEvent): void;
70
71
  private emitPendingEvents;
71
72
  /**
72
73
  * Forward event to specific Instance
@@ -147,6 +147,8 @@ export interface InstanceContext {
147
147
  onPtyData?: (data: string) => void;
148
148
  /** Provider configvalue (resolved) */
149
149
  settings: Record<string, any>;
150
+ /** Immediate provider-originated status/event emission. Used to avoid waiting for status polling. */
151
+ emitProviderEvent?: (event: ProviderEvent) => void;
150
152
  }
151
153
  export interface ProviderInstance {
152
154
  /** Provider type */
@@ -522,6 +522,8 @@ export interface DaemonStatusEventPayload {
522
522
  timestamp: number;
523
523
  targetSessionId?: string;
524
524
  providerType?: string;
525
+ providerSessionId?: string;
526
+ workspaceName?: string;
525
527
  duration?: number;
526
528
  elapsedSec?: number;
527
529
  modalMessage?: string;
@@ -535,6 +537,8 @@ export interface DashboardStatusEventPayload {
535
537
  daemonId?: string;
536
538
  providerType?: string;
537
539
  targetSessionId?: string;
540
+ providerSessionId?: string;
541
+ workspaceName?: string;
538
542
  duration?: number;
539
543
  elapsedSec?: number;
540
544
  modalMessage?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.76-rc.25",
3
+ "version": "0.9.76-rc.26",
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",
@@ -62,6 +62,54 @@ function resolveUpgradeChannel(args: any): ReleaseChannel {
62
62
  || normalizeReleaseChannel(loadConfig().updateChannel)
63
63
  || 'stable';
64
64
  }
65
+
66
+ function readProviderPriorityFromPolicy(policy: unknown): string[] {
67
+ const record = policy && typeof policy === 'object' && !Array.isArray(policy)
68
+ ? policy as Record<string, unknown>
69
+ : {};
70
+ const raw = record.providerPriority;
71
+ if (!Array.isArray(raw)) return [];
72
+ const seen = new Set<string>();
73
+ return raw
74
+ .map(type => typeof type === 'string' ? type.trim() : '')
75
+ .filter(Boolean)
76
+ .filter(type => {
77
+ if (seen.has(type)) return false;
78
+ seen.add(type);
79
+ return true;
80
+ });
81
+ }
82
+
83
+ async function resolveProviderTypeFromPriority(args: {
84
+ nodeId: string;
85
+ providerPriority: string[];
86
+ providerLoader: ProviderLoader;
87
+ onStatusChange?: () => void;
88
+ }): Promise<{ providerType?: string; error?: string }> {
89
+ if (!args.providerPriority.length) {
90
+ return { error: `Node '${args.nodeId}' has no providerPriority policy; pass cliType explicitly or configure node.policy.providerPriority` };
91
+ }
92
+
93
+ const failed: string[] = [];
94
+ for (const requestedType of args.providerPriority) {
95
+ const normalizedType = args.providerLoader.resolveAlias(requestedType);
96
+ if (!args.providerLoader.isMachineProviderEnabled(normalizedType)) {
97
+ failed.push(`${requestedType}: disabled`);
98
+ continue;
99
+ }
100
+ const detected = await detectCLI(normalizedType, args.providerLoader, { includeVersion: false });
101
+ args.providerLoader.setCliDetectionResults([{
102
+ id: normalizedType,
103
+ installed: !!detected,
104
+ path: detected?.path,
105
+ }], false);
106
+ args.onStatusChange?.();
107
+ if (detected) return { providerType: normalizedType };
108
+ failed.push(`${requestedType}: not detected`);
109
+ }
110
+
111
+ return { error: `No usable provider detected for node '${args.nodeId}' from providerPriority: ${failed.join('; ')}` };
112
+ }
65
113
  import * as fs from 'fs';
66
114
 
67
115
  type MeshCoordinatorConfigFormat = 'claude_mcp_json' | 'hermes_config_yaml';
@@ -1194,7 +1242,7 @@ export class DaemonCommandRouter {
1194
1242
  // ─── Mesh Coordinator Launch ───
1195
1243
  case 'launch_mesh_coordinator': {
1196
1244
  const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
1197
- const cliType = typeof args?.cliType === 'string' ? args.cliType.trim() : 'claude-cli';
1245
+ let cliType = typeof args?.cliType === 'string' ? args.cliType.trim() : '';
1198
1246
  if (!meshId) return { success: false, error: 'meshId required' };
1199
1247
 
1200
1248
  try {
@@ -1232,6 +1280,25 @@ export class DaemonCommandRouter {
1232
1280
  }
1233
1281
  const workspace = typeof coordinatorNode.workspace === 'string' ? coordinatorNode.workspace.trim() : '';
1234
1282
  if (!workspace) return { success: false, error: 'Coordinator node workspace required', meshId, cliType };
1283
+ if (!cliType) {
1284
+ const resolved = await resolveProviderTypeFromPriority({
1285
+ nodeId: String(coordinatorNode.id || coordinatorNode.nodeId || preferredCoordinatorNodeId || 'coordinator'),
1286
+ providerPriority: readProviderPriorityFromPolicy(coordinatorNode.policy),
1287
+ providerLoader: this.deps.providerLoader,
1288
+ onStatusChange: this.deps.onStatusChange,
1289
+ });
1290
+ if (!resolved.providerType) {
1291
+ return {
1292
+ success: false,
1293
+ code: 'mesh_coordinator_provider_priority_unusable',
1294
+ error: resolved.error || 'No usable provider found from node providerPriority',
1295
+ meshId,
1296
+ cliType,
1297
+ workspace,
1298
+ };
1299
+ }
1300
+ cliType = resolved.providerType;
1301
+ }
1235
1302
  const providerMeta = this.deps.providerLoader.resolve?.(cliType) || this.deps.providerLoader.getMeta(cliType);
1236
1303
  const coordinatorSetup = resolveMeshCoordinatorSetup({
1237
1304
  provider: providerMeta,
@@ -738,7 +738,29 @@ export class CliProviderInstance implements ProviderInstance {
738
738
  }
739
739
 
740
740
  private pushEvent(event: ProviderEvent): void {
741
- this.events.push(event);
741
+ const enrichedEvent: ProviderEvent = {
742
+ ...event,
743
+ instanceId: typeof event.instanceId === 'string' && event.instanceId.trim()
744
+ ? event.instanceId
745
+ : this.instanceId,
746
+ targetSessionId: typeof event.targetSessionId === 'string' && event.targetSessionId.trim()
747
+ ? event.targetSessionId
748
+ : this.instanceId,
749
+ providerType: typeof event.providerType === 'string' && event.providerType.trim()
750
+ ? event.providerType
751
+ : this.type,
752
+ workspaceName: typeof event.workspaceName === 'string' && event.workspaceName.trim()
753
+ ? event.workspaceName
754
+ : this.workingDir,
755
+ providerSessionId: typeof event.providerSessionId === 'string' && event.providerSessionId.trim()
756
+ ? event.providerSessionId
757
+ : this.providerSessionId,
758
+ };
759
+ if (this.context?.emitProviderEvent) {
760
+ this.context.emitProviderEvent(enrichedEvent);
761
+ return;
762
+ }
763
+ this.events.push(enrichedEvent);
742
764
  }
743
765
 
744
766
  private flushEvents(): ProviderEvent[] {
@@ -47,7 +47,10 @@ export class ProviderInstanceManager {
47
47
  this.instances.get(id)!.dispose();
48
48
  }
49
49
  this.instances.set(id, instance);
50
- await instance.init(context);
50
+ await instance.init({
51
+ ...context,
52
+ emitProviderEvent: (event) => this.emitProviderEvent(instance.type, id, event),
53
+ });
51
54
  }
52
55
 
53
56
  /**
@@ -237,6 +240,22 @@ export class ProviderInstanceManager {
237
240
  this.eventListeners.push(listener);
238
241
  }
239
242
 
243
+ emitProviderEvent(providerType: string, instanceId: string, event: ProviderEvent): void {
244
+ const payload = {
245
+ ...event,
246
+ providerType,
247
+ instanceId: typeof event.instanceId === 'string' && event.instanceId.trim()
248
+ ? event.instanceId
249
+ : instanceId,
250
+ targetSessionId: typeof event.targetSessionId === 'string' && event.targetSessionId.trim()
251
+ ? event.targetSessionId
252
+ : instanceId,
253
+ } as ProviderEvent & { providerType: string };
254
+ for (const listener of this.eventListeners) {
255
+ listener(payload);
256
+ }
257
+ }
258
+
240
259
  private emitPendingEvents(
241
260
  providerType: string,
242
261
  state: ProviderState,
@@ -174,6 +174,8 @@ export interface InstanceContext {
174
174
  onPtyData?: (data: string) => void;
175
175
  /** Provider configvalue (resolved) */
176
176
  settings: Record<string, any>;
177
+ /** Immediate provider-originated status/event emission. Used to avoid waiting for status polling. */
178
+ emitProviderEvent?: (event: ProviderEvent) => void;
177
179
  }
178
180
 
179
181
  export interface ProviderInstance {
@@ -641,6 +641,8 @@ export interface DaemonStatusEventPayload {
641
641
  timestamp: number;
642
642
  targetSessionId?: string;
643
643
  providerType?: string;
644
+ providerSessionId?: string;
645
+ workspaceName?: string;
644
646
  duration?: number;
645
647
  elapsedSec?: number;
646
648
  modalMessage?: string;
@@ -662,6 +664,8 @@ export interface DashboardStatusEventPayload {
662
664
  daemonId?: string;
663
665
  providerType?: string;
664
666
  targetSessionId?: string;
667
+ providerSessionId?: string;
668
+ workspaceName?: string;
665
669
  duration?: number;
666
670
  elapsedSec?: number;
667
671
  modalMessage?: string;
@@ -151,6 +151,12 @@ export class DaemonStatusReporter {
151
151
  if (providerType) {
152
152
  payload.providerType = providerType;
153
153
  }
154
+ if (typeof event.providerSessionId === 'string' && event.providerSessionId.trim()) {
155
+ payload.providerSessionId = event.providerSessionId.trim();
156
+ }
157
+ if (typeof event.workspaceName === 'string' && event.workspaceName.trim()) {
158
+ payload.workspaceName = event.workspaceName.trim();
159
+ }
154
160
  if (typeof event.duration === 'number' && Number.isFinite(event.duration)) {
155
161
  payload.duration = event.duration;
156
162
  }