@adhdev/daemon-core 0.9.68 → 0.9.70

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.
@@ -260,6 +260,31 @@ export interface CdpTargetFilter {
260
260
  titleExcludes?: string;
261
261
  }
262
262
  export type ProviderVersionCommand = string | Partial<Record<string, string>>;
263
+ export type MeshCoordinatorMcpConfigMode = 'auto_import' | 'manual' | 'none';
264
+ export type MeshCoordinatorMcpConfigFormat = 'claude_mcp_json' | 'hermes_config_yaml';
265
+ export interface ProviderMeshCoordinatorConfig {
266
+ /** Whether ADHDev may select this provider for Repo Mesh coordinator sessions. */
267
+ supported: boolean;
268
+ /** Human-readable reason shown when unsupported or blocked. */
269
+ reason?: string;
270
+ /** How ADHDev mesh MCP tools become visible to the launched CLI. */
271
+ mcpConfig?: {
272
+ mode: MeshCoordinatorMcpConfigMode;
273
+ format?: MeshCoordinatorMcpConfigFormat;
274
+ /** Provider-relative/project-relative config path for auto-import modes, e.g. '.mcp.json'. */
275
+ path?: string;
276
+ /** MCP server name to materialize or display. Defaults to 'adhdev-mesh'. */
277
+ serverName?: string;
278
+ /** Manual setup target path/help command, e.g. 'hermes config path'. */
279
+ configPathCommand?: string;
280
+ /** Whether users need a fresh CLI session after config changes. */
281
+ requiresRestart?: boolean;
282
+ /** User-facing setup explanation for manual modes. */
283
+ instructions?: string;
284
+ /** Copyable setup template. Supports {{meshId}}, {{adhdevMcpCommand}}, {{workspace}}, {{serverName}}. */
285
+ template?: string;
286
+ };
287
+ }
263
288
  export interface ProviderCompatibilityEntry {
264
289
  ideVersion: string;
265
290
  scriptDir: string;
@@ -271,6 +296,10 @@ export interface ProviderModule {
271
296
  name: string;
272
297
  /** Category: determines execution method */
273
298
  category: ProviderCategory;
299
+ /** When provider-owned, daemon treats provider parser output as canonical transcript authority. */
300
+ transcriptAuthority?: 'provider' | 'daemon';
301
+ /** Full context lets provider-owned parsers canonicalize retained history instead of daemon prefix stitching. */
302
+ transcriptContext?: 'full' | 'tail';
274
303
  /** Alias list — allows users to invoke by alternate names (e.g. ['claude', 'claude-code']) */
275
304
  aliases?: string[];
276
305
  /** CDP ports [primary, secondary] (IDE category only) */
@@ -448,6 +477,11 @@ export interface ProviderModule {
448
477
  spawnArgBuilder?: (config: Record<string, string>) => string[];
449
478
  /** ACP agent auth methods (multiple supported — in priority order) */
450
479
  auth?: AcpAuthMethod[];
480
+ /**
481
+ * Repo Mesh coordinator capability and MCP ingestion behavior.
482
+ * Providers must declare this rather than relying on daemon hardcoded CLI quirks.
483
+ */
484
+ meshCoordinator?: ProviderMeshCoordinatorConfig;
451
485
  contractVersion?: number;
452
486
  capabilities?: {
453
487
  input?: {
@@ -12,7 +12,7 @@ export type { ProviderState, ProviderStatus, ActiveChatData, IdeProviderState, C
12
12
  export type { ProviderErrorReason } from './providers/provider-instance.js';
13
13
  import type { ActiveChatData as _ActiveChatData, ProviderErrorReason as _ProviderErrorReason } from './providers/provider-instance.js';
14
14
  import type { WorkspaceEntry } from './config/workspaces.js';
15
- import type { ProviderResumeCapability } from './providers/contracts.js';
15
+ import type { ProviderMeshCoordinatorConfig, ProviderResumeCapability } from './providers/contracts.js';
16
16
  import type { GitCompactSummary, GitWorkspaceUpdate, WorkspaceGitSubscriptionParams } from './git/git-types.js';
17
17
  export type { GitCommandName, GitCompactSummary, GitDiffSummary, GitFailureReason, GitFileChange, GitFileChangeStatus, GitRepoIdentity, GitRepoStatus, GitSnapshot, GitSnapshotCompareSummary, GitSnapshotReason, GitWorkspaceUpdate, WorkspaceGitSubscriptionParams, } from './git/git-types.js';
18
18
  export interface SessionActiveChatData extends Omit<_ActiveChatData, 'messages'> {
@@ -343,6 +343,8 @@ export interface AvailableProviderInfo {
343
343
  lastDetection?: MachineProviderCheckResult;
344
344
  /** Last end-to-end ADHDev verification result, when available. */
345
345
  lastVerification?: MachineProviderCheckResult;
346
+ /** Provider-declared Repo Mesh coordinator/MCP behavior. */
347
+ meshCoordinator?: ProviderMeshCoordinatorConfig;
346
348
  }
347
349
  export interface MachineProviderCheckResult {
348
350
  ok: boolean;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/session-host-core",
3
- "version": "0.9.68",
3
+ "version": "0.9.70",
4
4
  "description": "ADHDev local session host core \u2014 session registry, protocol, buffers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.68",
3
+ "version": "0.9.70",
4
4
  "description": "ADHDev daemon core \u2014 CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,97 @@
1
+ import { join } from 'path'
2
+ import type { ProviderModule, MeshCoordinatorMcpConfigFormat } from '../providers/contracts.js'
3
+
4
+ export type MeshCoordinatorSetup =
5
+ | {
6
+ kind: 'auto_import'
7
+ serverName: string
8
+ configPath: string
9
+ configFormat?: MeshCoordinatorMcpConfigFormat
10
+ }
11
+ | {
12
+ kind: 'manual'
13
+ serverName: string
14
+ configFormat?: MeshCoordinatorMcpConfigFormat
15
+ configPathCommand?: string
16
+ requiresRestart: boolean
17
+ instructions: string
18
+ template: string
19
+ }
20
+ | {
21
+ kind: 'unsupported'
22
+ reason: string
23
+ }
24
+
25
+ export interface ResolveMeshCoordinatorSetupOptions {
26
+ provider?: ProviderModule | null
27
+ meshId: string
28
+ workspace: string
29
+ adhdevMcpCommand?: string
30
+ }
31
+
32
+ const DEFAULT_SERVER_NAME = 'adhdev-mesh'
33
+ const DEFAULT_ADHDEV_MCP_COMMAND = 'adhdev-mcp'
34
+
35
+ export function resolveMeshCoordinatorSetup(options: ResolveMeshCoordinatorSetupOptions): MeshCoordinatorSetup {
36
+ const { provider, meshId, workspace } = options
37
+ const config = provider?.meshCoordinator
38
+ if (!config?.supported) {
39
+ return {
40
+ kind: 'unsupported',
41
+ reason: config?.reason || 'Provider does not declare Repo Mesh coordinator support',
42
+ }
43
+ }
44
+
45
+ const mcpConfig = config.mcpConfig
46
+ if (!mcpConfig || mcpConfig.mode === 'none') {
47
+ return {
48
+ kind: 'unsupported',
49
+ reason: config.reason || 'Provider does not declare a usable Repo Mesh MCP configuration mode',
50
+ }
51
+ }
52
+
53
+ const serverName = mcpConfig.serverName?.trim() || DEFAULT_SERVER_NAME
54
+ if (mcpConfig.mode === 'auto_import') {
55
+ const path = mcpConfig.path?.trim()
56
+ if (!path) {
57
+ return { kind: 'unsupported', reason: 'Provider auto-import MCP config is missing a config path' }
58
+ }
59
+ return {
60
+ kind: 'auto_import',
61
+ serverName,
62
+ configPath: join(workspace, path),
63
+ configFormat: mcpConfig.format,
64
+ }
65
+ }
66
+
67
+ if (mcpConfig.mode === 'manual') {
68
+ const instructions = mcpConfig.instructions?.trim()
69
+ const template = mcpConfig.template
70
+ if (!instructions || !template?.trim()) {
71
+ return { kind: 'unsupported', reason: 'Provider manual MCP setup is missing instructions or template' }
72
+ }
73
+ return {
74
+ kind: 'manual',
75
+ serverName,
76
+ configFormat: mcpConfig.format,
77
+ configPathCommand: mcpConfig.configPathCommand,
78
+ requiresRestart: mcpConfig.requiresRestart === true,
79
+ instructions,
80
+ template: renderMeshCoordinatorTemplate(template, {
81
+ meshId,
82
+ workspace,
83
+ serverName,
84
+ adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND,
85
+ }),
86
+ }
87
+ }
88
+
89
+ return {
90
+ kind: 'unsupported',
91
+ reason: `Unsupported Repo Mesh MCP configuration mode: ${String(mcpConfig.mode)}`,
92
+ }
93
+ }
94
+
95
+ function renderMeshCoordinatorTemplate(template: string, values: Record<string, string>): string {
96
+ return template.replace(/\{\{\s*(meshId|workspace|serverName|adhdevMcpCommand)\s*\}\}/g, (_, key: string) => values[key] || '')
97
+ }
@@ -33,6 +33,7 @@ import type { CommandLogEntry } from '../logging/command-log.js';
33
33
  import { getRecentLogs, LOG_PATH } from '../logging/logger.js';
34
34
  import { createInteractionId, getRecentDebugTrace, recordDebugTrace } from '../logging/debug-trace.js';
35
35
  import { getSessionHostSurfaceKind, partitionSessionHostRecords } from '../session-host/runtime-surface.js';
36
+ import { resolveMeshCoordinatorSetup } from './mesh-coordinator.js';
36
37
  import { buildSessionEntries } from '../status/builders.js';
37
38
  import { buildMachineInfo, buildStatusSnapshot } from '../status/snapshot.js';
38
39
  import { getSessionCompletionMarker } from '../status/snapshot.js';
@@ -1012,13 +1013,52 @@ export class DaemonCommandRouter {
1012
1013
  if (mesh.nodes.length === 0) return { success: false, error: 'No nodes in mesh' };
1013
1014
 
1014
1015
  const workspace = mesh.nodes[0].workspace;
1016
+ const providerMeta = this.deps.providerLoader.resolve?.(cliType) || this.deps.providerLoader.getMeta(cliType);
1017
+ const coordinatorSetup = resolveMeshCoordinatorSetup({
1018
+ provider: providerMeta,
1019
+ meshId,
1020
+ workspace,
1021
+ });
1022
+
1023
+ if (coordinatorSetup.kind === 'unsupported') {
1024
+ return {
1025
+ success: false,
1026
+ code: 'mesh_coordinator_unsupported',
1027
+ error: coordinatorSetup.reason,
1028
+ meshId,
1029
+ cliType,
1030
+ workspace,
1031
+ };
1032
+ }
1033
+
1034
+ if (coordinatorSetup.kind === 'manual') {
1035
+ return {
1036
+ success: false,
1037
+ code: 'mesh_coordinator_manual_mcp_setup_required',
1038
+ error: coordinatorSetup.instructions,
1039
+ meshId,
1040
+ cliType,
1041
+ workspace,
1042
+ meshCoordinatorSetup: coordinatorSetup,
1043
+ };
1044
+ }
1045
+
1046
+ if (coordinatorSetup.configFormat !== 'claude_mcp_json') {
1047
+ return {
1048
+ success: false,
1049
+ code: 'mesh_coordinator_unsupported',
1050
+ error: `Unsupported auto-import MCP config format: ${String(coordinatorSetup.configFormat)}`,
1051
+ meshId,
1052
+ cliType,
1053
+ workspace,
1054
+ };
1055
+ }
1015
1056
 
1016
- // 1. Write .mcp.json to workspace so Claude CLI auto-discovers mesh tools
1057
+ // 1. Write provider-declared MCP config to workspace for CLIs that auto-import it.
1017
1058
  const { existsSync, readFileSync, writeFileSync, copyFileSync } = await import('fs');
1018
- const { join } = await import('path');
1019
- const mcpConfigPath = join(workspace, '.mcp.json');
1059
+ const mcpConfigPath = coordinatorSetup.configPath;
1020
1060
 
1021
- // Backup existing .mcp.json if present
1061
+ // Backup existing MCP config if present.
1022
1062
  const hadExistingMcpConfig = existsSync(mcpConfigPath);
1023
1063
  let existingMcpConfig: any = {};
1024
1064
  if (hadExistingMcpConfig) {
@@ -1028,19 +1068,19 @@ export class DaemonCommandRouter {
1028
1068
  } catch { /* empty */ }
1029
1069
  }
1030
1070
 
1031
- // Merge adhdev-mesh server into existing config
1071
+ // Merge ADHDev mesh server into existing config.
1032
1072
  const mcpConfig = {
1033
1073
  ...existingMcpConfig,
1034
1074
  mcpServers: {
1035
1075
  ...(existingMcpConfig.mcpServers || {}),
1036
- 'adhdev-mesh': {
1076
+ [coordinatorSetup.serverName]: {
1037
1077
  command: 'adhdev-mcp',
1038
1078
  args: ['--repo-mesh', meshId],
1039
1079
  },
1040
1080
  },
1041
1081
  };
1042
1082
  writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2), 'utf-8');
1043
- LOG.info('MeshCoordinator', `Wrote .mcp.json to ${workspace} with adhdev-mesh server`);
1083
+ LOG.info('MeshCoordinator', `Wrote ${mcpConfigPath} with ${coordinatorSetup.serverName} server`);
1044
1084
 
1045
1085
  // 2. Build coordinator system prompt
1046
1086
  let systemPrompt = '';
@@ -345,6 +345,33 @@ export interface CdpTargetFilter {
345
345
 
346
346
  export type ProviderVersionCommand = string | Partial<Record<string, string>>;
347
347
 
348
+ export type MeshCoordinatorMcpConfigMode = 'auto_import' | 'manual' | 'none';
349
+ export type MeshCoordinatorMcpConfigFormat = 'claude_mcp_json' | 'hermes_config_yaml';
350
+
351
+ export interface ProviderMeshCoordinatorConfig {
352
+ /** Whether ADHDev may select this provider for Repo Mesh coordinator sessions. */
353
+ supported: boolean;
354
+ /** Human-readable reason shown when unsupported or blocked. */
355
+ reason?: string;
356
+ /** How ADHDev mesh MCP tools become visible to the launched CLI. */
357
+ mcpConfig?: {
358
+ mode: MeshCoordinatorMcpConfigMode;
359
+ format?: MeshCoordinatorMcpConfigFormat;
360
+ /** Provider-relative/project-relative config path for auto-import modes, e.g. '.mcp.json'. */
361
+ path?: string;
362
+ /** MCP server name to materialize or display. Defaults to 'adhdev-mesh'. */
363
+ serverName?: string;
364
+ /** Manual setup target path/help command, e.g. 'hermes config path'. */
365
+ configPathCommand?: string;
366
+ /** Whether users need a fresh CLI session after config changes. */
367
+ requiresRestart?: boolean;
368
+ /** User-facing setup explanation for manual modes. */
369
+ instructions?: string;
370
+ /** Copyable setup template. Supports {{meshId}}, {{adhdevMcpCommand}}, {{workspace}}, {{serverName}}. */
371
+ template?: string;
372
+ };
373
+ }
374
+
348
375
  export interface ProviderCompatibilityEntry {
349
376
  ideVersion: string;
350
377
  scriptDir: string;
@@ -357,6 +384,10 @@ export interface ProviderModule {
357
384
  name: string;
358
385
  /** Category: determines execution method */
359
386
  category: ProviderCategory;
387
+ /** When provider-owned, daemon treats provider parser output as canonical transcript authority. */
388
+ transcriptAuthority?: 'provider' | 'daemon';
389
+ /** Full context lets provider-owned parsers canonicalize retained history instead of daemon prefix stitching. */
390
+ transcriptContext?: 'full' | 'tail';
360
391
  /** Alias list — allows users to invoke by alternate names (e.g. ['claude', 'claude-code']) */
361
392
  aliases?: string[];
362
393
 
@@ -555,6 +586,12 @@ export interface ProviderModule {
555
586
  /** ACP agent auth methods (multiple supported — in priority order) */
556
587
  auth?: AcpAuthMethod[];
557
588
 
589
+ /**
590
+ * Repo Mesh coordinator capability and MCP ingestion behavior.
591
+ * Providers must declare this rather than relying on daemon hardcoded CLI quirks.
592
+ */
593
+ meshCoordinator?: ProviderMeshCoordinatorConfig;
594
+
558
595
  // ─── Contract version / capability declaration ───
559
596
  contractVersion?: number;
560
597
  capabilities?: {
@@ -7,6 +7,8 @@ const KNOWN_PROVIDER_FIELDS = new Set<string>([
7
7
  'type',
8
8
  'name',
9
9
  'category',
10
+ 'transcriptAuthority',
11
+ 'transcriptContext',
10
12
  'aliases',
11
13
  'cdpPorts',
12
14
  'targetFilter',
@@ -51,6 +53,7 @@ const KNOWN_PROVIDER_FIELDS = new Set<string>([
51
53
  'staticConfigOptions',
52
54
  'spawnArgBuilder',
53
55
  'auth',
56
+ 'meshCoordinator',
54
57
  'contractVersion',
55
58
  'capabilities',
56
59
  'providerVersion',
@@ -125,6 +128,7 @@ export function validateProviderDefinition(raw: unknown): ProviderValidationResu
125
128
 
126
129
  validateCapabilities(provider as unknown as ProviderModule, controls, errors)
127
130
  validateCanonicalHistory(provider.canonicalHistory, errors)
131
+ validateMeshCoordinator(provider.meshCoordinator, errors)
128
132
 
129
133
  for (const control of controls) {
130
134
  validateControl(control as ProviderControlDef, errors)
@@ -233,6 +237,69 @@ function validateCanonicalHistory(raw: unknown, errors: string[]): void {
233
237
  }
234
238
  }
235
239
 
240
+ function validateMeshCoordinator(raw: unknown, errors: string[]): void {
241
+ if (raw === undefined) return
242
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
243
+ errors.push('meshCoordinator must be an object')
244
+ return
245
+ }
246
+
247
+ const meshCoordinator = raw as Record<string, unknown>
248
+ if (typeof meshCoordinator.supported !== 'boolean') {
249
+ errors.push('meshCoordinator.supported must be boolean')
250
+ }
251
+ if (meshCoordinator.reason !== undefined && (typeof meshCoordinator.reason !== 'string' || !meshCoordinator.reason.trim())) {
252
+ errors.push('meshCoordinator.reason must be a non-empty string when provided')
253
+ }
254
+
255
+ const mcpConfig = meshCoordinator.mcpConfig
256
+ if (mcpConfig === undefined) return
257
+ if (!mcpConfig || typeof mcpConfig !== 'object' || Array.isArray(mcpConfig)) {
258
+ errors.push('meshCoordinator.mcpConfig must be an object')
259
+ return
260
+ }
261
+
262
+ const config = mcpConfig as Record<string, unknown>
263
+ const mode = config.mode
264
+ if (!['auto_import', 'manual', 'none'].includes(String(mode))) {
265
+ errors.push('meshCoordinator.mcpConfig.mode must be one of: auto_import, manual, none')
266
+ }
267
+
268
+ const format = config.format
269
+ if (format !== undefined && !['claude_mcp_json', 'hermes_config_yaml'].includes(String(format))) {
270
+ errors.push('meshCoordinator.mcpConfig.format must be one of: claude_mcp_json, hermes_config_yaml')
271
+ }
272
+
273
+ for (const key of ['path', 'serverName', 'configPathCommand', 'instructions', 'template']) {
274
+ const value = config[key]
275
+ if (value !== undefined && (typeof value !== 'string' || !value.trim())) {
276
+ errors.push(`meshCoordinator.mcpConfig.${key} must be a non-empty string when provided`)
277
+ }
278
+ }
279
+
280
+ if (config.requiresRestart !== undefined && typeof config.requiresRestart !== 'boolean') {
281
+ errors.push('meshCoordinator.mcpConfig.requiresRestart must be boolean when provided')
282
+ }
283
+
284
+ if (mode === 'auto_import') {
285
+ if (format === undefined) {
286
+ errors.push('meshCoordinator.mcpConfig.format is required for auto_import MCP setup')
287
+ }
288
+ if (typeof config.path !== 'string' || !config.path.trim()) {
289
+ errors.push('meshCoordinator.mcpConfig.path is required for auto_import MCP setup')
290
+ }
291
+ }
292
+
293
+ if (mode === 'manual') {
294
+ if (typeof config.instructions !== 'string' || !config.instructions.trim()) {
295
+ errors.push('meshCoordinator.mcpConfig.instructions is required for manual MCP setup')
296
+ }
297
+ if (typeof config.template !== 'string' || !config.template.trim()) {
298
+ errors.push('meshCoordinator.mcpConfig.template is required for manual MCP setup')
299
+ }
300
+ }
301
+ }
302
+
236
303
  function validateControl(control: ProviderControlDef, errors: string[]): void {
237
304
  if (!control || typeof control !== 'object') {
238
305
  errors.push('controls: each control must be an object')
@@ -43,7 +43,7 @@ export type { ProviderErrorReason } from './providers/provider-instance.js';
43
43
  // Local import for use in Managed*Entry types below
44
44
  import type { ActiveChatData as _ActiveChatData, ProviderErrorReason as _ProviderErrorReason } from './providers/provider-instance.js';
45
45
  import type { WorkspaceEntry } from './config/workspaces.js';
46
- import type { ProviderResumeCapability } from './providers/contracts.js';
46
+ import type { ProviderMeshCoordinatorConfig, ProviderResumeCapability } from './providers/contracts.js';
47
47
  import type {
48
48
  GitCompactSummary,
49
49
  GitDiffSummary,
@@ -447,6 +447,8 @@ export interface AvailableProviderInfo {
447
447
  lastDetection?: MachineProviderCheckResult;
448
448
  /** Last end-to-end ADHDev verification result, when available. */
449
449
  lastVerification?: MachineProviderCheckResult;
450
+ /** Provider-declared Repo Mesh coordinator/MCP behavior. */
451
+ meshCoordinator?: ProviderMeshCoordinatorConfig;
450
452
  }
451
453
 
452
454
  export interface MachineProviderCheckResult {
@@ -144,6 +144,7 @@ function buildAvailableProviders(
144
144
  machineStatus?: 'disabled' | 'enabled_unchecked' | 'not_detected' | 'detected';
145
145
  lastDetection?: AvailableProviderInfo['lastDetection'];
146
146
  lastVerification?: AvailableProviderInfo['lastVerification'];
147
+ meshCoordinator?: AvailableProviderInfo['meshCoordinator'];
147
148
  }> = providerLoader.getAvailableProviderInfos?.() || providerLoader.getAll();
148
149
  return providers.map((provider) => ({
149
150
  type: provider.type,
@@ -157,6 +158,7 @@ function buildAvailableProviders(
157
158
  ...(provider.machineStatus !== undefined ? { machineStatus: provider.machineStatus } : {}),
158
159
  ...(provider.lastDetection !== undefined ? { lastDetection: provider.lastDetection } : {}),
159
160
  ...(provider.lastVerification !== undefined ? { lastVerification: provider.lastVerification } : {}),
161
+ ...(provider.meshCoordinator !== undefined ? { meshCoordinator: provider.meshCoordinator } : {}),
160
162
  }));
161
163
  }
162
164