@adhdev/daemon-core 0.9.82-rc.265 → 0.9.82-rc.266

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.
@@ -314,24 +314,8 @@ export interface RepoMeshStatus {
314
314
  queue?: RepoMeshQueueStatus;
315
315
  ledger?: RepoMeshLedgerStatus;
316
316
  }
317
- export interface RepoMeshSessionStatus {
318
- sessionId: string;
319
- providerType?: string;
320
- state?: string;
321
- chatStatus?: string;
322
- lifecycle?: 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'interrupted';
323
- surfaceKind?: 'live_runtime' | 'recovery_snapshot' | 'inactive_record';
324
- recoveryState?: string | null;
325
- workspace?: string | null;
326
- title?: string | null;
327
- role?: string | null;
328
- isSelfCoordinator?: boolean;
329
- statusNote?: string | null;
330
- createdAt?: string | null;
331
- startedAt?: string | null;
332
- lastActivityAt?: string | null;
333
- isCached?: boolean;
334
- }
317
+ export type { RepoMeshSessionStatus } from '@adhdev/mesh-shared';
318
+ import type { RepoMeshSessionStatus } from '@adhdev/mesh-shared';
335
319
  export type RepoMeshPeerConnectionState = 'self' | 'connected' | 'connecting' | 'disconnected' | 'failed' | 'closed' | 'unknown';
336
320
  export type RepoMeshPeerConnectionTransport = 'local' | 'direct' | 'relay' | 'unknown';
337
321
  export interface RepoMeshPeerConnectionStatus {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.265",
3
+ "version": "0.9.82-rc.266",
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",
@@ -46,6 +46,7 @@
46
46
  "author": "vilmire",
47
47
  "license": "AGPL-3.0-or-later",
48
48
  "dependencies": {
49
+ "@adhdev/mesh-shared": "*",
49
50
  "@adhdev/session-host-core": "*",
50
51
  "@agentclientprotocol/sdk": "^0.16.1",
51
52
  "ajv": "^8.20.0",
@@ -27,7 +27,11 @@ import { listProviderHistorySessions } from '../config/chat-history.js';
27
27
  import { detectIDEs } from '../detection/ide-detector.js';
28
28
  import { detectCLI } from '../detection/cli-detector.js';
29
29
  import { getGitRepoStatus } from '../git/git-status.js';
30
- import type { GitSubmoduleStatus } from '../git/git-types.js';
30
+ import {
31
+ normalizeGitStatus as sharedNormalizeGitStatus,
32
+ pickBestTransitGitStatus as sharedPickBestTransitGitStatus,
33
+ summarizeGitShape as sharedSummarizeGitShape,
34
+ } from '@adhdev/mesh-shared';
31
35
  import { SessionRegistry } from '../sessions/registry.js';
32
36
  import { LOG } from '../logging/logger.js';
33
37
  import { logCommand } from '../logging/command-log.js';
@@ -143,39 +147,8 @@ function readBooleanValue(...values: unknown[]): boolean | undefined {
143
147
  return undefined;
144
148
  }
145
149
 
146
- function summarizeRepoMeshDebugGit(git: unknown): Record<string, unknown> | null {
147
- const record = readObjectRecord(git);
148
- if (!Object.keys(record).length) return null;
149
- const submodules = Array.isArray(record.submodules)
150
- ? record.submodules.map((entry: any) => ({
151
- path: readStringValue(entry?.path) ?? null,
152
- commit: readStringValue(entry?.commit)?.slice(0, 12) ?? null,
153
- dirty: readBooleanValue(entry?.dirty) ?? false,
154
- outOfSync: readBooleanValue(entry?.outOfSync, entry?.out_of_sync) ?? false,
155
- }))
156
- : [];
157
- return {
158
- isGitRepo: readBooleanValue(record.isGitRepo),
159
- workspace: readStringValue(record.workspace) ?? null,
160
- repoRoot: readStringValue(record.repoRoot, record.repo_root) ?? null,
161
- branch: readStringValue(record.branch) ?? null,
162
- upstream: readStringValue(record.upstream) ?? null,
163
- upstreamStatus: readStringValue(record.upstreamStatus, record.upstream_status) ?? null,
164
- headCommit: readStringValue(record.headCommit, record.head_commit)?.slice(0, 12) ?? null,
165
- ahead: readNumberValue(record.ahead) ?? null,
166
- behind: readNumberValue(record.behind) ?? null,
167
- dirtyCounts: {
168
- staged: readNumberValue(record.staged) ?? 0,
169
- modified: readNumberValue(record.modified) ?? 0,
170
- untracked: readNumberValue(record.untracked) ?? 0,
171
- deleted: readNumberValue(record.deleted) ?? 0,
172
- renamed: readNumberValue(record.renamed) ?? 0,
173
- },
174
- lastCheckedAt: readNumberValue(record.lastCheckedAt, record.last_checked_at) ?? null,
175
- submoduleCount: submodules.length,
176
- submodules,
177
- };
178
- }
150
+ // summarizeRepoMeshDebugGit was a hand-synced copy of the cloud git-shape
151
+ // summarizer; both now call shared summarizeGitShape (@adhdev/mesh-shared).
179
152
 
180
153
  function summarizeRepoMeshStatusDebug(status: any): Record<string, unknown> {
181
154
  const nodes = Array.isArray(status?.nodes) ? status.nodes : [];
@@ -200,7 +173,7 @@ function summarizeRepoMeshStatusDebug(status: any): Record<string, unknown> {
200
173
  } : null,
201
174
  gitProbePending: node?.gitProbePending === true,
202
175
  launchReady: node?.launchReady === true,
203
- git: summarizeRepoMeshDebugGit(node?.git),
176
+ git: sharedSummarizeGitShape(node?.git),
204
177
  branchConvergence: node?.branchConvergence ?? node?.branch_convergence ?? null,
205
178
  })),
206
179
  };
@@ -214,38 +187,8 @@ function logRepoMeshStatusDebug(event: string, fields: Record<string, unknown>):
214
187
  }
215
188
  }
216
189
 
217
- function joinRepoPath(root: string | undefined, relativePath: string | undefined): string | undefined {
218
- const normalizedRoot = typeof root === 'string' ? root.trim().replace(/[\\/]+$/, '') : '';
219
- const normalizedPath = typeof relativePath === 'string' ? relativePath.trim() : '';
220
- if (!normalizedPath) return undefined;
221
- if (/^(?:[A-Za-z]:[\\/]|\/)/.test(normalizedPath)) return normalizedPath;
222
- if (!normalizedRoot) return undefined;
223
- return `${normalizedRoot}/${normalizedPath.replace(/^[\\/]+/, '')}`;
224
- }
225
-
226
- function readGitSubmodules(value: unknown, parentRepoRoot?: string): GitSubmoduleStatus[] | undefined {
227
- if (!Array.isArray(value)) return undefined;
228
- const submodules = value
229
- .map(entry => {
230
- const submodule = readObjectRecord(entry);
231
- const path = readStringValue(submodule.path);
232
- const commit = readStringValue(submodule.commit);
233
- const repoPath = readStringValue(submodule.repoPath, submodule.repo_root)
234
- ?? joinRepoPath(parentRepoRoot, path);
235
- if (!path || !commit || !repoPath) return null;
236
- return {
237
- path,
238
- commit,
239
- repoPath,
240
- dirty: readBooleanValue(submodule.dirty) ?? false,
241
- outOfSync: readBooleanValue(submodule.outOfSync, submodule.out_of_sync) ?? false,
242
- lastCheckedAt: readNumberValue(submodule.lastCheckedAt, submodule.last_checked_at) ?? Date.now(),
243
- ...(readStringValue(submodule.error) ? { error: readStringValue(submodule.error) } : {}),
244
- };
245
- })
246
- .filter((entry): entry is GitSubmoduleStatus => entry !== null);
247
- return submodules.length > 0 ? submodules : undefined;
248
- }
190
+ // joinRepoPath + readGitSubmodules moved to @adhdev/mesh-shared (readGitSubmodules)
191
+ // used via sharedNormalizeGitStatus / sharedPickBestTransitGitStatus below.
249
192
 
250
193
  function buildMeshNodeDisplayLabel(node: Record<string, unknown>, nodeId: string, providerPriority: string[]): string {
251
194
  const explicit = readStringValue(node.machineLabel, node.machine_label, node.machineNickname, node.machine_nickname, node.alias);
@@ -382,82 +325,21 @@ function buildMeshNodeMachineIdentity(node: Record<string, unknown>, opts: {
382
325
  };
383
326
  }
384
327
 
328
+ // normalizeInlineMeshGitStatus / scoreInlineMeshGitStatus /
329
+ // buildInlineMeshTransitGitStatus were the standalone-side copies of the cloud
330
+ // transit git normalizers. They now delegate to @adhdev/mesh-shared so the two
331
+ // transports can no longer drift (e.g. on the submodule drop / evidence rules).
332
+
385
333
  function normalizeInlineMeshGitStatus(
386
334
  status: Record<string, unknown>,
387
335
  node: any,
388
336
  options?: { lastCheckedAt?: number },
389
337
  ): Record<string, unknown> | undefined {
390
- const isGitRepo = readBooleanValue(status.isGitRepo);
391
- if (!Object.keys(status).length || isGitRepo === undefined) return undefined;
392
- const conflictFiles = Array.isArray(status.conflictFiles)
393
- ? status.conflictFiles.filter((value: unknown): value is string => typeof value === 'string')
394
- : [];
395
- const conflictCount = readNumberValue(status.conflicts) ?? conflictFiles.length;
396
- const hasConflicts = readBooleanValue(status.hasConflicts) ?? conflictCount > 0;
397
- const repoRoot = readStringValue(status.repoRoot, status.repo_root, node?.repoRoot, node?.repo_root, status.workspace, node?.workspace) || undefined;
398
- const submodules = readGitSubmodules(status.submodules, repoRoot);
399
- return {
400
- workspace: readStringValue(status.workspace, node?.workspace) || '',
401
- repoRoot: repoRoot ?? null,
402
- isGitRepo,
403
- branch: readStringValue(status.branch) ?? null,
404
- headCommit: readStringValue(status.headCommit) ?? null,
405
- headMessage: readStringValue(status.headMessage) ?? null,
406
- upstream: readStringValue(status.upstream) ?? null,
407
- upstreamStatus: readStringValue(status.upstreamStatus, status.upstream_status)
408
- ?? (readStringValue(status.upstream) ? 'unchecked' : 'no_upstream'),
409
- upstreamFetchedAt: readNumberValue(status.upstreamFetchedAt, status.upstream_fetched_at),
410
- upstreamFetchError: readStringValue(status.upstreamFetchError, status.upstream_fetch_error),
411
- ahead: readNumberValue(status.ahead) ?? 0,
412
- behind: readNumberValue(status.behind) ?? 0,
413
- staged: readNumberValue(status.staged) ?? 0,
414
- modified: readNumberValue(status.modified) ?? 0,
415
- untracked: readNumberValue(status.untracked) ?? 0,
416
- deleted: readNumberValue(status.deleted) ?? 0,
417
- renamed: readNumberValue(status.renamed) ?? 0,
418
- hasConflicts,
419
- conflictFiles,
420
- stashCount: readNumberValue(status.stashCount) ?? 0,
421
- lastCheckedAt: options?.lastCheckedAt ?? readNumberValue(status.lastCheckedAt) ?? Date.now(),
422
- ...(submodules ? { submodules } : {}),
423
- };
424
- }
425
-
426
- function scoreInlineMeshGitStatus(git: Record<string, unknown> | undefined): number {
427
- if (!git) return Number.NEGATIVE_INFINITY;
428
- let score = 0;
429
- if (readBooleanValue(git.isGitRepo) === true) score += 50;
430
- if (readBooleanValue(git.isGitRepo) === false) score -= 10;
431
- if (readStringValue(git.branch)) score += 20;
432
- if (readStringValue(git.headCommit)) score += 20;
433
- if (readStringValue(git.upstream)) score += 10;
434
- if (readStringValue(git.upstreamStatus)) score += 5;
435
- if (readNumberValue(git.ahead) !== undefined) score += 2;
436
- if (readNumberValue(git.behind) !== undefined) score += 2;
437
- if (Array.isArray(git.submodules) && git.submodules.length > 0) score += 4 + git.submodules.length;
438
- if (readStringValue(git.error)) score -= 20;
439
- return score;
338
+ return sharedNormalizeGitStatus(status, readObjectRecord(node), options) as Record<string, unknown> | undefined;
440
339
  }
441
340
 
442
341
  function buildInlineMeshTransitGitStatus(node: any): Record<string, unknown> | undefined {
443
- const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
444
- const gitResult = readObjectRecord(rawGit.result);
445
- const directStatus = readObjectRecord(rawGit.status);
446
- const nestedStatus = readObjectRecord(gitResult.status);
447
- const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
448
- const probeGit = readObjectRecord(rawProbe.git);
449
- const probeGitResult = readObjectRecord(probeGit.result);
450
- const probeDirectStatus = readObjectRecord(probeGit.status);
451
- const probeNestedStatus = readObjectRecord(probeGitResult.status);
452
- const candidates = [directStatus, nestedStatus, probeDirectStatus, probeNestedStatus];
453
- let best: { git: Record<string, unknown>; score: number } | null = null;
454
- for (const status of candidates) {
455
- const normalized = normalizeInlineMeshGitStatus(status, node, { lastCheckedAt: Date.now() });
456
- if (!normalized) continue;
457
- const score = scoreInlineMeshGitStatus(normalized);
458
- if (!best || score > best.score) best = { git: normalized, score };
459
- }
460
- return best?.git;
342
+ return sharedPickBestTransitGitStatus(readObjectRecord(node), { lastCheckedAt: Date.now() }) as Record<string, unknown> | undefined;
461
343
  }
462
344
 
463
345
  function shouldRefreshStalePendingAggregate(snapshot: any, options?: { requireDirectPeerTruth?: boolean }): boolean {
@@ -6,101 +6,21 @@
6
6
  * infer these values from agent transcripts in frontend code.
7
7
  */
8
8
 
9
- export type GitFailureReason =
10
- | 'not_git_repo'
11
- | 'git_not_installed'
12
- | 'timeout'
13
- | 'path_outside_repo'
14
- | 'dirty_index_required'
15
- | 'conflict'
16
- | 'invalid_args'
17
- | 'nothing_to_commit'
18
- | 'git_command_failed';
19
-
20
- export interface GitRepoIdentity {
21
- workspace: string;
22
- repoRoot: string | null;
23
- isGitRepo: boolean;
24
- }
25
-
26
- export interface GitSubmoduleStatus {
27
- /** Submodule path relative to repo root */
28
- path: string;
29
- /** Current commit SHA the submodule is at */
30
- commit: string;
31
- /** Path to the submodule repo (absolute) */
32
- repoPath: string;
33
- /** Whether the submodule has uncommitted changes */
34
- dirty: boolean;
35
- /** Whether the submodule commit differs from what the parent repo expects */
36
- outOfSync: boolean;
37
- /** Last checked timestamp */
38
- lastCheckedAt: number;
39
- /** Error message if submodule status could not be read */
40
- error?: string;
41
- }
42
-
43
- export type GitUpstreamFreshness = 'fresh' | 'unchecked' | 'stale' | 'no_upstream' | 'unavailable';
44
-
45
- export interface GitRepoStatus extends GitRepoIdentity {
46
- branch: string | null;
47
- headCommit: string | null;
48
- headMessage: string | null;
49
- upstream: string | null;
50
- /** Whether ahead/behind was verified against a freshly fetched upstream ref. */
51
- upstreamStatus: GitUpstreamFreshness;
52
- /** Timestamp for the fetch that refreshed upstream refs when upstreamStatus === 'fresh'. */
53
- upstreamFetchedAt?: number;
54
- /** Error from the last refresh attempt when upstreamStatus === 'stale'. */
55
- upstreamFetchError?: string;
56
- ahead: number;
57
- behind: number;
58
- staged: number;
59
- modified: number;
60
- untracked: number;
61
- deleted: number;
62
- renamed: number;
63
- /** Aggregate dirty flag including root worktree changes, conflicts, stash, and submodule drift. */
64
- dirty: boolean;
65
- hasConflicts: boolean;
66
- conflictFiles: string[];
67
- stashCount: number;
68
- lastCheckedAt: number;
69
- /** Submodule statuses when auto-discover is enabled */
70
- submodules?: GitSubmoduleStatus[];
71
- /**
72
- * Set when the running daemon's build commit is a STRICT ancestor of this
73
- * repo's HEAD (or a submodule HEAD) — i.e. the live daemon predates committed
74
- * code in this workspace and is awaiting a deploy/restart to catch up.
75
- * Omitted entirely when no staleness is provable (unknown build, commit not
76
- * present in repo, or build commit == HEAD) to avoid over-warning.
77
- */
78
- daemonBuildBehind?: DaemonBuildBehind;
79
- error?: string;
80
- reason?: GitFailureReason;
81
- }
82
-
83
- export interface DaemonBuildBehind {
84
- /** Full build commit baked into the running daemon. */
85
- buildCommit: string;
86
- /** Short build commit. */
87
- buildCommitShort: string;
88
- /** HEAD commit the build commit is behind (repo or submodule). */
89
- head: string;
90
- /** Where the comparison matched: 'root' or the submodule path. */
91
- scope: string;
92
- /**
93
- * Whether any package changed between buildCommit..HEAD affects the daemon
94
- * runtime (daemon-core, standalone, session-host, terminal-mux, ghostty,
95
- * mcp-server). When false, only web/render packages changed — the daemon does
96
- * NOT need a rebuild/restart; only the web deploy is pending. Conservative:
97
- * when the changed-package set can't be determined it defaults to true.
98
- */
99
- isDaemonAffecting: boolean;
100
- /** Distinct package names changed between buildCommit..HEAD (best-effort). */
101
- affectedPackages?: string[];
102
- warning: string;
103
- }
9
+ // Pure shape types now live in @adhdev/mesh-shared so the cloud (web-core) and
10
+ // standalone (daemon-core) transports share ONE definition instead of hand-syncing
11
+ // two. Re-exported here so existing `from './git-types'` / package imports keep
12
+ // working unchanged. Daemon-specific types (GitSnapshot, GitCommandName, …) stay
13
+ // local below and consume these re-exports.
14
+ export type {
15
+ GitFailureReason,
16
+ GitRepoIdentity,
17
+ GitSubmoduleStatus,
18
+ GitUpstreamFreshness,
19
+ GitRepoStatus,
20
+ DaemonBuildBehind,
21
+ } from '@adhdev/mesh-shared';
22
+
23
+ import type { GitRepoIdentity, GitRepoStatus, GitFailureReason, GitUpstreamFreshness } from '@adhdev/mesh-shared';
104
24
 
105
25
  export type GitFileChangeStatus =
106
26
  | 'added'
@@ -370,24 +370,12 @@ export interface RepoMeshStatus {
370
370
  ledger?: RepoMeshLedgerStatus;
371
371
  }
372
372
 
373
- export interface RepoMeshSessionStatus {
374
- sessionId: string;
375
- providerType?: string;
376
- state?: string;
377
- chatStatus?: string;
378
- lifecycle?: 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'interrupted';
379
- surfaceKind?: 'live_runtime' | 'recovery_snapshot' | 'inactive_record';
380
- recoveryState?: string | null;
381
- workspace?: string | null;
382
- title?: string | null;
383
- role?: string | null;
384
- isSelfCoordinator?: boolean;
385
- statusNote?: string | null;
386
- createdAt?: string | null;
387
- startedAt?: string | null;
388
- lastActivityAt?: string | null;
389
- isCached?: boolean;
390
- }
373
+ // RepoMeshSessionStatus shape now lives in @adhdev/mesh-shared (shared with
374
+ // web-core's session normalizer). Re-exported so local RepoMeshNodeStatus/
375
+ // RepoMeshStatus and external `@adhdev/daemon-core/repo-mesh-types` consumers
376
+ // keep resolving it unchanged.
377
+ export type { RepoMeshSessionStatus } from '@adhdev/mesh-shared';
378
+ import type { RepoMeshSessionStatus } from '@adhdev/mesh-shared';
391
379
 
392
380
  export type RepoMeshPeerConnectionState = 'self' | 'connected' | 'connecting' | 'disconnected' | 'failed' | 'closed' | 'unknown';
393
381
  export type RepoMeshPeerConnectionTransport = 'local' | 'direct' | 'relay' | 'unknown';