@adhdev/daemon-core 0.9.82-rc.6 → 0.9.82-rc.8

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.
@@ -213,14 +213,135 @@ export interface RepoMeshStatus {
213
213
  repoIdentity: string;
214
214
  refreshedAt: string;
215
215
  nodes: RepoMeshNodeStatus[];
216
+ queue?: RepoMeshQueueStatus;
217
+ ledger?: RepoMeshLedgerStatus;
218
+ }
219
+ export interface RepoMeshSessionStatus {
220
+ sessionId: string;
221
+ providerType?: string;
222
+ state?: string;
223
+ lifecycle?: 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'interrupted';
224
+ surfaceKind?: 'live_runtime' | 'recovery_snapshot' | 'inactive_record';
225
+ recoveryState?: string | null;
226
+ workspace?: string | null;
227
+ title?: string | null;
228
+ lastActivityAt?: string | null;
229
+ isCached?: boolean;
230
+ }
231
+ export type RepoMeshPeerConnectionState = 'self' | 'connected' | 'connecting' | 'disconnected' | 'failed' | 'closed' | 'unknown';
232
+ export type RepoMeshPeerConnectionTransport = 'local' | 'direct' | 'relay' | 'unknown';
233
+ export interface RepoMeshPeerConnectionStatus {
234
+ perspective: 'selected_coordinator';
235
+ source: 'mesh_peer_status' | 'not_reported';
236
+ state: RepoMeshPeerConnectionState;
237
+ transport: RepoMeshPeerConnectionTransport;
238
+ reported: boolean;
239
+ reason?: string;
240
+ lastStateChangeAt?: string;
241
+ lastConnectedAt?: string;
242
+ lastCommandAt?: string;
216
243
  }
217
244
  export interface RepoMeshNodeStatus {
218
245
  nodeId: string;
219
246
  machineLabel: string;
220
247
  workspace: string;
248
+ repoRoot?: string;
249
+ daemonId?: string;
250
+ machineId?: string;
251
+ machineStatus?: string;
252
+ isLocalWorktree?: boolean;
253
+ worktreeBranch?: string;
221
254
  health: RepoMeshNodeHealth;
222
255
  git?: GitRepoStatus;
223
256
  providers: string[];
224
257
  activeSessions: string[];
258
+ activeSessionDetails?: RepoMeshSessionStatus[];
259
+ providerPriority?: string[];
260
+ launchReady?: boolean;
261
+ lastSeenAt?: string;
262
+ updatedAt?: string;
263
+ connection?: RepoMeshPeerConnectionStatus;
225
264
  error?: string;
226
265
  }
266
+ export type RepoMeshQueueTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed' | 'cancelled';
267
+ export interface RepoMeshQueueTask {
268
+ id: string;
269
+ meshId: string;
270
+ message: string;
271
+ status: RepoMeshQueueTaskStatus;
272
+ targetNodeId?: string;
273
+ targetSessionId?: string;
274
+ assignedNodeId?: string;
275
+ assignedSessionId?: string;
276
+ cancelReason?: string;
277
+ cancelledAt?: string;
278
+ requeueReason?: string;
279
+ requeuedAt?: string;
280
+ requeueCount?: number;
281
+ autoLaunch?: {
282
+ status: 'skipped' | 'started' | 'failed' | 'completed';
283
+ reason?: string;
284
+ nodeId?: string;
285
+ providerType?: string;
286
+ sessionId?: string;
287
+ updatedAt: string;
288
+ };
289
+ dispatchTimestamp?: string;
290
+ createdAt: string;
291
+ updatedAt: string;
292
+ }
293
+ export interface RepoMeshQueueSummary {
294
+ total: number;
295
+ active: number;
296
+ historical: number;
297
+ pending: number;
298
+ assigned: number;
299
+ completed: number;
300
+ failed: number;
301
+ cancelled: number;
302
+ activeCounts: {
303
+ pending: number;
304
+ assigned: number;
305
+ };
306
+ historicalCounts: {
307
+ completed: number;
308
+ failed: number;
309
+ cancelled: number;
310
+ };
311
+ activeAssignments: Array<{
312
+ id: string;
313
+ nodeId?: string;
314
+ sessionId?: string;
315
+ message: string;
316
+ }>;
317
+ }
318
+ export interface RepoMeshQueueStatus {
319
+ tasks: RepoMeshQueueTask[];
320
+ summary: RepoMeshQueueSummary;
321
+ }
322
+ export interface RepoMeshLedgerEntryStatus {
323
+ id: string;
324
+ meshId: string;
325
+ timestamp: string;
326
+ kind: string;
327
+ nodeId?: string;
328
+ sessionId?: string;
329
+ providerType?: string;
330
+ payload: Record<string, unknown>;
331
+ }
332
+ export interface RepoMeshLedgerSummaryStatus {
333
+ meshId: string;
334
+ totalEntries: number;
335
+ taskDispatched: number;
336
+ taskCompleted: number;
337
+ taskFailed: number;
338
+ taskStalled: number;
339
+ sessionLaunched: number;
340
+ checkpointCreated: number;
341
+ lastActivityAt: string | null;
342
+ recentFailures: number;
343
+ }
344
+ export interface RepoMeshLedgerStatus {
345
+ entries: RepoMeshLedgerEntryStatus[];
346
+ summary: RepoMeshLedgerSummaryStatus;
347
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.6",
3
+ "version": "0.9.82-rc.8",
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",
@@ -86,6 +86,8 @@ export interface DaemonInitConfig {
86
86
 
87
87
  /** Relays a command to a remote mesh node daemon */
88
88
  dispatchMeshCommand?: (daemonId: string, command: string, args: Record<string, unknown>) => Promise<any>;
89
+ /** Returns selected-coordinator mesh peer telemetry for a target daemon when available. */
90
+ getMeshPeerConnectionStatus?: (daemonId: string) => Record<string, unknown> | null;
89
91
  }
90
92
 
91
93
  // ─── Result ───
@@ -306,6 +308,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
306
308
  sessionHostControl: config.sessionHostControl,
307
309
  statusInstanceId: config.statusInstanceId,
308
310
  statusVersion: config.statusVersion,
311
+ getMeshPeerConnectionStatus: config.getMeshPeerConnectionStatus,
309
312
  getCdpLogFn: config.getCdpLogFn || ((ideType: string) => LOG.forComponent(`CDP:${ideType}`).asLogFn()),
310
313
  });
311
314
 
@@ -222,26 +222,139 @@ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | un
222
222
  };
223
223
  }
224
224
 
225
+ function hasGitWorktreeChanges(git: Record<string, unknown> | null | undefined): boolean {
226
+ if (!git) return false;
227
+ return Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
228
+ }
229
+
230
+ function getGitSubmoduleDriftState(git: Record<string, unknown> | null | undefined): { dirty: boolean; outOfSync: boolean } {
231
+ const submodules = Array.isArray(git?.submodules) ? git.submodules : [];
232
+ let dirty = false;
233
+ let outOfSync = false;
234
+ for (const entry of submodules) {
235
+ const submodule = readObjectRecord(entry);
236
+ if (readBooleanValue(submodule.dirty) === true) dirty = true;
237
+ if (readBooleanValue(submodule.outOfSync) === true || !!readStringValue(submodule.error)) outOfSync = true;
238
+ }
239
+ return { dirty, outOfSync };
240
+ }
241
+
242
+ function deriveMeshNodeHealthFromGit(git: Record<string, unknown> | null | undefined): 'online' | 'dirty' | 'degraded' {
243
+ if (!git || readBooleanValue(git.isGitRepo) === false) return 'degraded';
244
+ const branch = readStringValue(git.branch);
245
+ if (!branch) return 'degraded';
246
+ const submoduleDrift = getGitSubmoduleDriftState(git);
247
+ if (submoduleDrift.outOfSync) return 'degraded';
248
+ if (submoduleDrift.dirty || hasGitWorktreeChanges(git)) return 'dirty';
249
+ return 'online';
250
+ }
251
+
252
+ function readCachedInlineMeshActiveSessions(node: any): string[] {
253
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
254
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
255
+ const fallbackSession = Object.keys(activeSession).length
256
+ ? activeSession
257
+ : readObjectRecord(node?.activeSession ?? node?.active_session);
258
+ const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
259
+ return sessionId ? [sessionId] : [];
260
+ }
261
+
262
+ function readCachedInlineMeshActiveSessionDetails(node: any): Array<Record<string, unknown>> {
263
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
264
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
265
+ const fallbackSession = Object.keys(activeSession).length
266
+ ? activeSession
267
+ : readObjectRecord(node?.activeSession ?? node?.active_session);
268
+ const sessionId = readStringValue(
269
+ fallbackSession.id,
270
+ fallbackSession.sessionId,
271
+ fallbackSession.session_id,
272
+ node?.activeSessionId,
273
+ node?.active_session_id,
274
+ node?.sessionId,
275
+ node?.session_id,
276
+ );
277
+ if (!sessionId) return [];
278
+ return [{
279
+ sessionId,
280
+ providerType: readStringValue(
281
+ fallbackSession.providerType,
282
+ fallbackSession.provider_type,
283
+ fallbackSession.cliType,
284
+ fallbackSession.cli_type,
285
+ fallbackSession.provider,
286
+ node?.providerType,
287
+ node?.provider_type,
288
+ ),
289
+ state: readStringValue(fallbackSession.status, fallbackSession.state, fallbackSession.lifecycle),
290
+ lifecycle: readStringValue(fallbackSession.lifecycle),
291
+ title: readStringValue(fallbackSession.title, fallbackSession.displayName, fallbackSession.display_name) ?? null,
292
+ workspace: readStringValue(fallbackSession.workspace, node?.workspace) ?? null,
293
+ lastActivityAt: readStringValue(fallbackSession.lastActivityAt, fallbackSession.last_activity_at) ?? null,
294
+ recoveryState: readStringValue(fallbackSession.recoveryState, fallbackSession.recovery_state) ?? null,
295
+ isCached: true,
296
+ }];
297
+ }
298
+
299
+ function readLiveMeshSessionState(record: any): string | undefined {
300
+ return readStringValue(
301
+ record?.meta?.sessionStatus,
302
+ record?.meta?.status,
303
+ record?.meta?.providerStatus,
304
+ record?.status,
305
+ record?.state,
306
+ record?.lifecycle,
307
+ );
308
+ }
309
+
310
+ function toIsoTimestamp(value: unknown): string | null {
311
+ if (typeof value === 'number' && Number.isFinite(value)) return new Date(value).toISOString();
312
+ const stringValue = readStringValue(value);
313
+ return stringValue || null;
314
+ }
315
+
316
+ function summarizeMeshSessionRecord(record: any): Record<string, unknown> {
317
+ return {
318
+ sessionId: readStringValue(record?.sessionId) || 'unknown',
319
+ providerType: readStringValue(record?.providerType),
320
+ state: readLiveMeshSessionState(record),
321
+ lifecycle: readStringValue(record?.lifecycle),
322
+ surfaceKind: getSessionHostSurfaceKind(record as any),
323
+ recoveryState: readStringValue(record?.meta?.runtimeRecoveryState) ?? null,
324
+ workspace: readStringValue(record?.workspace) ?? null,
325
+ title: readStringValue(record?.displayName, record?.workspaceLabel) ?? null,
326
+ lastActivityAt: toIsoTimestamp(record?.updatedAt ?? record?.lastActivityAt ?? record?.last_activity_at),
327
+ isCached: false,
328
+ };
329
+ }
330
+
225
331
  function applyCachedInlineMeshNodeStatus(status: Record<string, unknown>, node: any): boolean {
226
332
  const cachedStatus = readObjectRecord(node?.cachedStatus);
227
333
  const git = buildCachedInlineMeshGitStatus(node);
228
334
  const error = readStringValue(cachedStatus.error, node?.error);
229
335
  const health = readStringValue(cachedStatus.health, node?.health);
230
336
  const machineStatus = readStringValue(cachedStatus.machineStatus, node?.machineStatus);
231
- if (!git && !error && !health) return false;
232
- if (!machineStatus && !git && !error) return false;
337
+ const lastSeenAt = toIsoTimestamp(cachedStatus.lastSeenAt ?? cachedStatus.last_seen_at ?? node?.lastSeenAt ?? node?.last_seen_at);
338
+ const updatedAt = toIsoTimestamp(cachedStatus.updatedAt ?? cachedStatus.updated_at ?? node?.updatedAt ?? node?.updated_at);
339
+ const activeSessions = readCachedInlineMeshActiveSessions(node);
340
+ const activeSessionDetails = readCachedInlineMeshActiveSessionDetails(node);
341
+ if (!git && !error && !health && !machineStatus && !lastSeenAt && !updatedAt && activeSessions.length === 0) return false;
233
342
  if (git) status.git = git;
234
343
  if (error) status.error = error;
344
+ if (machineStatus) status.machineStatus = machineStatus;
345
+ if (lastSeenAt) status.lastSeenAt = lastSeenAt;
346
+ if (updatedAt) status.updatedAt = updatedAt;
347
+ if (activeSessions.length > 0) status.activeSessions = activeSessions;
348
+ if (activeSessionDetails.length > 0) status.activeSessionDetails = activeSessionDetails;
235
349
  if (health) {
236
350
  status.health = health;
237
351
  return true;
238
352
  }
239
353
  if (git) {
240
- const dirty = Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
241
- status.health = git.isGitRepo === false ? 'degraded' : dirty ? 'dirty' : 'online';
354
+ status.health = deriveMeshNodeHealthFromGit(git);
242
355
  return true;
243
356
  }
244
- return false;
357
+ return activeSessions.length > 0 || !!machineStatus || !!lastSeenAt || !!updatedAt;
245
358
  }
246
359
 
247
360
  async function resolveProviderTypeFromPriority(args: {
@@ -661,6 +774,8 @@ export interface CommandRouterDeps {
661
774
  statusVersion?: string;
662
775
  /** Session host control plane */
663
776
  sessionHostControl?: SessionHostControlPlane | null;
777
+ /** Selected-coordinator mesh peer telemetry surface for target daemons, when supported by the runtime. */
778
+ getMeshPeerConnectionStatus?: (daemonId: string) => Record<string, unknown> | null;
664
779
  }
665
780
 
666
781
  export interface CommandRouterResult {
@@ -2913,24 +3028,91 @@ export class DaemonCommandRouter {
2913
3028
  const { readLedgerEntries, getLedgerSummary } = await import('../mesh/mesh-ledger.js');
2914
3029
  const ledgerEntries = readLedgerEntries(meshId, { tail: 20 });
2915
3030
  const ledgerSummary = getLedgerSummary(meshId);
3031
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions
3032
+ ? await this.deps.sessionHostControl.listSessions().catch(() => [])
3033
+ : [];
3034
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
2916
3035
 
3036
+ const localMachineId = loadConfig().machineId || '';
3037
+ const inlineCoordinatorNodeId = meshRecord?.inline && Array.isArray(mesh.nodes)
3038
+ ? readStringValue((mesh.nodes[0] as any)?.id, (mesh.nodes[0] as any)?.nodeId)
3039
+ : undefined;
3040
+ const refreshedAt = new Date().toISOString();
2917
3041
  const nodeStatuses = [];
2918
- for (const node of mesh.nodes || []) {
3042
+ for (const [nodeIndex, node] of (mesh.nodes || []).entries()) {
3043
+ const nodeId = String(node.id || node.nodeId || '');
3044
+ const daemonId = readStringValue(node.daemonId);
3045
+ const providerPriority = readProviderPriorityFromPolicy(node.policy);
3046
+ const isSelfNode = Boolean(
3047
+ nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId,
3048
+ ) || Boolean(
3049
+ daemonId && (daemonId === localMachineId || daemonId === this.deps.statusInstanceId),
3050
+ ) || Boolean(meshRecord?.inline && nodeIndex === 0);
2919
3051
  const status: Record<string, unknown> = {
2920
- nodeId: node.id || node.nodeId,
3052
+ nodeId,
2921
3053
  machineLabel: node.machineLabel || node.id || node.nodeId,
2922
3054
  workspace: node.workspace,
2923
3055
  repoRoot: node.repoRoot,
2924
3056
  isLocalWorktree: node.isLocalWorktree,
2925
3057
  worktreeBranch: node.worktreeBranch,
2926
- daemonId: node.daemonId,
3058
+ daemonId,
2927
3059
  machineId: node.machineId,
3060
+ machineStatus: node.machineStatus,
2928
3061
  health: 'unknown',
2929
3062
  providers: node.providers || [],
3063
+ providerPriority,
2930
3064
  activeSessions: [],
3065
+ activeSessionDetails: [],
3066
+ launchReady: false,
2931
3067
  };
3068
+ if (isSelfNode) {
3069
+ status.connection = {
3070
+ perspective: 'selected_coordinator',
3071
+ source: 'mesh_peer_status',
3072
+ state: 'self',
3073
+ transport: 'local',
3074
+ reported: true,
3075
+ reason: 'Selected coordinator daemon',
3076
+ lastStateChangeAt: refreshedAt,
3077
+ };
3078
+ } else if (daemonId) {
3079
+ const connection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
3080
+ status.connection = connection ?? {
3081
+ perspective: 'selected_coordinator',
3082
+ source: 'not_reported',
3083
+ state: 'unknown',
3084
+ transport: 'unknown',
3085
+ reported: false,
3086
+ reason: 'No live mesh peer telemetry reported by the selected coordinator yet.',
3087
+ };
3088
+ } else {
3089
+ status.connection = {
3090
+ perspective: 'selected_coordinator',
3091
+ source: 'not_reported',
3092
+ state: 'unknown',
3093
+ transport: 'unknown',
3094
+ reported: false,
3095
+ reason: 'Node has no daemon id, so mesh transport cannot be reported from the selected coordinator.',
3096
+ };
3097
+ }
3098
+ const matchedLiveSessionRecords = liveMeshSessions
3099
+ .filter((record) => this.sessionMatchesMeshNode(record, node, nodeId));
3100
+ if (matchedLiveSessionRecords.length > 0) {
3101
+ const sessionIds = matchedLiveSessionRecords
3102
+ .map((record: any) => typeof record?.sessionId === 'string' ? record.sessionId : '')
3103
+ .filter(Boolean);
3104
+ const providerTypes = matchedLiveSessionRecords
3105
+ .map((record: any) => readStringValue(record?.providerType))
3106
+ .filter(Boolean) as string[];
3107
+ status.activeSessions = sessionIds;
3108
+ status.activeSessionDetails = matchedLiveSessionRecords.map(summarizeMeshSessionRecord);
3109
+ if (providerTypes.length > 0) {
3110
+ status.providers = Array.from(new Set([...(Array.isArray(status.providers) ? status.providers as string[] : []), ...providerTypes]));
3111
+ }
3112
+ }
2932
3113
  if (node.workspace && typeof node.workspace === 'string') {
2933
3114
  if (!fs.existsSync(node.workspace as string) && applyCachedInlineMeshNodeStatus(status, node)) {
3115
+ status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === 'online' || isSelfNode);
2934
3116
  nodeStatuses.push(status);
2935
3117
  continue;
2936
3118
  }
@@ -2938,8 +3120,7 @@ export class DaemonCommandRouter {
2938
3120
  const gitStatus = await getGitRepoStatus(node.workspace as string, { timeoutMs: 10_000 });
2939
3121
  status.git = gitStatus;
2940
3122
  if (gitStatus.isGitRepo) {
2941
- const dirty = (gitStatus.staged + gitStatus.modified + gitStatus.untracked + gitStatus.deleted + gitStatus.renamed) > 0;
2942
- status.health = gitStatus.branch ? (dirty ? 'dirty' : 'online') : 'degraded';
3123
+ status.health = deriveMeshNodeHealthFromGit(gitStatus as unknown as Record<string, unknown>);
2943
3124
  } else {
2944
3125
  status.health = 'degraded';
2945
3126
  if (gitStatus.error && !status.error) status.error = gitStatus.error;
@@ -2952,6 +3133,7 @@ export class DaemonCommandRouter {
2952
3133
  } else {
2953
3134
  applyCachedInlineMeshNodeStatus(status, node);
2954
3135
  }
3136
+ status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === 'online' || isSelfNode);
2955
3137
  nodeStatuses.push(status);
2956
3138
  }
2957
3139
 
@@ -2961,6 +3143,7 @@ export class DaemonCommandRouter {
2961
3143
  meshName: mesh.name,
2962
3144
  repoIdentity: mesh.repoIdentity,
2963
3145
  defaultBranch: mesh.defaultBranch,
3146
+ refreshedAt: new Date().toISOString(),
2964
3147
  nodes: nodeStatuses,
2965
3148
  queue: { tasks: queue, summary: queueSummary },
2966
3149
  ledger: { entries: ledgerEntries, summary: ledgerSummary },
package/src/index.ts CHANGED
@@ -103,6 +103,14 @@ export type {
103
103
  LocalMeshNodeEntry,
104
104
  RepoMeshStatus,
105
105
  RepoMeshNodeStatus,
106
+ RepoMeshSessionStatus,
107
+ RepoMeshQueueTask,
108
+ RepoMeshQueueTaskStatus,
109
+ RepoMeshQueueSummary,
110
+ RepoMeshQueueStatus,
111
+ RepoMeshLedgerEntryStatus,
112
+ RepoMeshLedgerSummaryStatus,
113
+ RepoMeshLedgerStatus,
106
114
  } from './repo-mesh-types.js';
107
115
  export { DEFAULT_MESH_POLICY } from './repo-mesh-types.js';
108
116
 
@@ -261,15 +261,146 @@ export interface RepoMeshStatus {
261
261
  repoIdentity: string;
262
262
  refreshedAt: string;
263
263
  nodes: RepoMeshNodeStatus[];
264
+ queue?: RepoMeshQueueStatus;
265
+ ledger?: RepoMeshLedgerStatus;
266
+ }
267
+
268
+ export interface RepoMeshSessionStatus {
269
+ sessionId: string;
270
+ providerType?: string;
271
+ state?: string;
272
+ lifecycle?: 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'interrupted';
273
+ surfaceKind?: 'live_runtime' | 'recovery_snapshot' | 'inactive_record';
274
+ recoveryState?: string | null;
275
+ workspace?: string | null;
276
+ title?: string | null;
277
+ lastActivityAt?: string | null;
278
+ isCached?: boolean;
279
+ }
280
+
281
+ export type RepoMeshPeerConnectionState = 'self' | 'connected' | 'connecting' | 'disconnected' | 'failed' | 'closed' | 'unknown';
282
+ export type RepoMeshPeerConnectionTransport = 'local' | 'direct' | 'relay' | 'unknown';
283
+
284
+ export interface RepoMeshPeerConnectionStatus {
285
+ perspective: 'selected_coordinator';
286
+ source: 'mesh_peer_status' | 'not_reported';
287
+ state: RepoMeshPeerConnectionState;
288
+ transport: RepoMeshPeerConnectionTransport;
289
+ reported: boolean;
290
+ reason?: string;
291
+ lastStateChangeAt?: string;
292
+ lastConnectedAt?: string;
293
+ lastCommandAt?: string;
264
294
  }
265
295
 
266
296
  export interface RepoMeshNodeStatus {
267
297
  nodeId: string;
268
298
  machineLabel: string;
269
299
  workspace: string;
300
+ repoRoot?: string;
301
+ daemonId?: string;
302
+ machineId?: string;
303
+ machineStatus?: string;
304
+ isLocalWorktree?: boolean;
305
+ worktreeBranch?: string;
270
306
  health: RepoMeshNodeHealth;
271
307
  git?: GitRepoStatus;
272
308
  providers: string[];
273
309
  activeSessions: string[];
310
+ activeSessionDetails?: RepoMeshSessionStatus[];
311
+ providerPriority?: string[];
312
+ launchReady?: boolean;
313
+ lastSeenAt?: string;
314
+ updatedAt?: string;
315
+ connection?: RepoMeshPeerConnectionStatus;
274
316
  error?: string;
275
317
  }
318
+
319
+ export type RepoMeshQueueTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed' | 'cancelled';
320
+
321
+ export interface RepoMeshQueueTask {
322
+ id: string;
323
+ meshId: string;
324
+ message: string;
325
+ status: RepoMeshQueueTaskStatus;
326
+ targetNodeId?: string;
327
+ targetSessionId?: string;
328
+ assignedNodeId?: string;
329
+ assignedSessionId?: string;
330
+ cancelReason?: string;
331
+ cancelledAt?: string;
332
+ requeueReason?: string;
333
+ requeuedAt?: string;
334
+ requeueCount?: number;
335
+ autoLaunch?: {
336
+ status: 'skipped' | 'started' | 'failed' | 'completed';
337
+ reason?: string;
338
+ nodeId?: string;
339
+ providerType?: string;
340
+ sessionId?: string;
341
+ updatedAt: string;
342
+ };
343
+ dispatchTimestamp?: string;
344
+ createdAt: string;
345
+ updatedAt: string;
346
+ }
347
+
348
+ export interface RepoMeshQueueSummary {
349
+ total: number;
350
+ active: number;
351
+ historical: number;
352
+ pending: number;
353
+ assigned: number;
354
+ completed: number;
355
+ failed: number;
356
+ cancelled: number;
357
+ activeCounts: {
358
+ pending: number;
359
+ assigned: number;
360
+ };
361
+ historicalCounts: {
362
+ completed: number;
363
+ failed: number;
364
+ cancelled: number;
365
+ };
366
+ activeAssignments: Array<{
367
+ id: string;
368
+ nodeId?: string;
369
+ sessionId?: string;
370
+ message: string;
371
+ }>;
372
+ }
373
+
374
+ export interface RepoMeshQueueStatus {
375
+ tasks: RepoMeshQueueTask[];
376
+ summary: RepoMeshQueueSummary;
377
+ }
378
+
379
+ export interface RepoMeshLedgerEntryStatus {
380
+ id: string;
381
+ meshId: string;
382
+ timestamp: string;
383
+ kind: string;
384
+ nodeId?: string;
385
+ sessionId?: string;
386
+ providerType?: string;
387
+ payload: Record<string, unknown>;
388
+ }
389
+
390
+ export interface RepoMeshLedgerSummaryStatus {
391
+ meshId: string;
392
+ totalEntries: number;
393
+ taskDispatched: number;
394
+ taskCompleted: number;
395
+ taskFailed: number;
396
+ taskStalled: number;
397
+ sessionLaunched: number;
398
+ checkpointCreated: number;
399
+ lastActivityAt: string | null;
400
+ recentFailures: number;
401
+ }
402
+
403
+ export interface RepoMeshLedgerStatus {
404
+ entries: RepoMeshLedgerEntryStatus[];
405
+ summary: RepoMeshLedgerSummaryStatus;
406
+ }