@adhdev/daemon-core 0.9.66 → 0.9.68
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/config/mesh-config.d.ts +47 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +525 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +515 -18
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/coordinator-prompt.d.ts +18 -0
- package/dist/mesh/mesh-sync.d.ts +51 -0
- package/dist/repo-mesh-types.d.ts +168 -0
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/commands/router.ts +156 -0
- package/src/config/mesh-config.ts +228 -0
- package/src/index.ts +33 -0
- package/src/mesh/coordinator-prompt.ts +156 -0
- package/src/mesh/mesh-sync.ts +109 -0
- package/src/repo-mesh-types.ts +212 -0
package/src/index.ts
CHANGED
|
@@ -81,6 +81,26 @@ export type {
|
|
|
81
81
|
ExtensionProviderState,
|
|
82
82
|
} from './shared-types.js';
|
|
83
83
|
|
|
84
|
+
// ── Repo Mesh Types (cross-package) ──
|
|
85
|
+
export type {
|
|
86
|
+
RepoMesh,
|
|
87
|
+
RepoMeshNode,
|
|
88
|
+
RepoMeshNodeHealth,
|
|
89
|
+
RepoMeshPolicy,
|
|
90
|
+
RepoMeshNodePolicy,
|
|
91
|
+
RepoMeshNodeCapabilities,
|
|
92
|
+
DetectedCommand,
|
|
93
|
+
ProjectContextSnapshot,
|
|
94
|
+
ProjectContextSource,
|
|
95
|
+
RepoMeshCoordinatorConfig,
|
|
96
|
+
LocalMeshConfig,
|
|
97
|
+
LocalMeshEntry,
|
|
98
|
+
LocalMeshNodeEntry,
|
|
99
|
+
RepoMeshStatus,
|
|
100
|
+
RepoMeshNodeStatus,
|
|
101
|
+
} from './repo-mesh-types.js';
|
|
102
|
+
export { DEFAULT_MESH_POLICY } from './repo-mesh-types.js';
|
|
103
|
+
|
|
84
104
|
// ── Git Surface ──
|
|
85
105
|
export * from './git/index.js';
|
|
86
106
|
|
|
@@ -111,6 +131,19 @@ export type { RecentActivityEntry } from './config/recent-activity.js';
|
|
|
111
131
|
export { getSavedProviderSessions, upsertSavedProviderSession } from './config/saved-sessions.js';
|
|
112
132
|
export type { SavedProviderSessionEntry } from './config/saved-sessions.js';
|
|
113
133
|
|
|
134
|
+
// ── Mesh Config ──
|
|
135
|
+
export {
|
|
136
|
+
listMeshes, getMesh, getMeshByRepo, createMesh, updateMesh, deleteMesh,
|
|
137
|
+
addNode, removeNode, updateNode, normalizeRepoIdentity,
|
|
138
|
+
} from './config/mesh-config.js';
|
|
139
|
+
export type { CreateMeshOptions, UpdateMeshOptions, AddNodeOptions } from './config/mesh-config.js';
|
|
140
|
+
|
|
141
|
+
// ── Mesh Coordinator ──
|
|
142
|
+
export { buildCoordinatorSystemPrompt } from './mesh/coordinator-prompt.js';
|
|
143
|
+
export type { CoordinatorPromptContext } from './mesh/coordinator-prompt.js';
|
|
144
|
+
export { syncMeshes } from './mesh/mesh-sync.js';
|
|
145
|
+
export type { MeshSyncTransport, MeshSyncResult, RemoteMeshRecord } from './mesh/mesh-sync.js';
|
|
146
|
+
|
|
114
147
|
// ── State Store ──
|
|
115
148
|
export { loadState, saveState, resetState } from './config/state-store.js';
|
|
116
149
|
export type { DaemonState } from './config/state-store.js';
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coordinator Prompt — System prompt template for mesh coordinator sessions
|
|
3
|
+
*
|
|
4
|
+
* When an MCP server starts in mesh mode, this prompt is injected into the
|
|
5
|
+
* coordinator agent's context so it understands:
|
|
6
|
+
* 1. What the mesh is (repo, nodes, policy)
|
|
7
|
+
* 2. What tools are available
|
|
8
|
+
* 3. How to orchestrate work across nodes
|
|
9
|
+
*
|
|
10
|
+
* The prompt is generated dynamically from the current mesh state.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type {
|
|
14
|
+
LocalMeshEntry,
|
|
15
|
+
RepoMeshPolicy,
|
|
16
|
+
RepoMeshStatus,
|
|
17
|
+
RepoMeshNodeStatus,
|
|
18
|
+
} from '../repo-mesh-types.js';
|
|
19
|
+
|
|
20
|
+
// ─── Prompt Builder ─────────────────────────────
|
|
21
|
+
|
|
22
|
+
export interface CoordinatorPromptContext {
|
|
23
|
+
mesh: LocalMeshEntry;
|
|
24
|
+
status?: RepoMeshStatus;
|
|
25
|
+
userInstruction?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function buildCoordinatorSystemPrompt(ctx: CoordinatorPromptContext): string {
|
|
29
|
+
const { mesh, status, userInstruction } = ctx;
|
|
30
|
+
const sections: string[] = [];
|
|
31
|
+
|
|
32
|
+
// ── Identity ──
|
|
33
|
+
sections.push(`You are a **Repo Mesh Coordinator** — a technical team lead who orchestrates work across multiple agent sessions on a shared Git repository.
|
|
34
|
+
|
|
35
|
+
Your mesh: **${mesh.name}**
|
|
36
|
+
Repository: \`${mesh.repoIdentity}\`${mesh.defaultBranch ? `\nDefault branch: \`${mesh.defaultBranch}\`` : ''}`);
|
|
37
|
+
|
|
38
|
+
// ── Nodes ──
|
|
39
|
+
if (status?.nodes?.length) {
|
|
40
|
+
sections.push(buildNodeStatusSection(status.nodes));
|
|
41
|
+
} else if (mesh.nodes.length) {
|
|
42
|
+
sections.push(buildNodeConfigSection(mesh));
|
|
43
|
+
} else {
|
|
44
|
+
sections.push('## Nodes\nNo nodes configured yet. Ask the user to add nodes with `adhdev mesh add-node`.');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Policy ──
|
|
48
|
+
sections.push(buildPolicySection(mesh.policy));
|
|
49
|
+
|
|
50
|
+
// ── Tools ──
|
|
51
|
+
sections.push(TOOLS_SECTION);
|
|
52
|
+
|
|
53
|
+
// ── Workflow ──
|
|
54
|
+
sections.push(WORKFLOW_SECTION);
|
|
55
|
+
|
|
56
|
+
// ── Rules ──
|
|
57
|
+
sections.push(RULES_SECTION);
|
|
58
|
+
|
|
59
|
+
// ── User instruction ──
|
|
60
|
+
if (userInstruction) {
|
|
61
|
+
sections.push(`## Additional Context\n${userInstruction}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (mesh.coordinator.systemPromptSuffix) {
|
|
65
|
+
sections.push(mesh.coordinator.systemPromptSuffix);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return sections.join('\n\n');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ─── Section Builders ───────────────────────────
|
|
72
|
+
|
|
73
|
+
function buildNodeStatusSection(nodes: RepoMeshNodeStatus[]): string {
|
|
74
|
+
const lines = ['## Current Node Status', ''];
|
|
75
|
+
for (const n of nodes) {
|
|
76
|
+
const healthIcon = n.health === 'online' ? '🟢' :
|
|
77
|
+
n.health === 'dirty' ? '🟡' :
|
|
78
|
+
n.health === 'offline' ? '⚫' : '🔴';
|
|
79
|
+
const sessions = n.activeSessions.length > 0
|
|
80
|
+
? `sessions: ${n.activeSessions.join(', ')}`
|
|
81
|
+
: 'no active sessions';
|
|
82
|
+
const branch = n.git?.branch ? `branch: \`${n.git.branch}\`` : '';
|
|
83
|
+
lines.push(`- ${healthIcon} **${n.machineLabel}** (${n.nodeId})`);
|
|
84
|
+
lines.push(` workspace: \`${n.workspace}\` | ${branch} | ${sessions}`);
|
|
85
|
+
if (n.error) lines.push(` ⚠️ ${n.error}`);
|
|
86
|
+
}
|
|
87
|
+
return lines.join('\n');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function buildNodeConfigSection(mesh: LocalMeshEntry): string {
|
|
91
|
+
const lines = ['## Configured Nodes', ''];
|
|
92
|
+
for (const n of mesh.nodes) {
|
|
93
|
+
const labels: string[] = [];
|
|
94
|
+
if (n.isLocalWorktree) labels.push('worktree');
|
|
95
|
+
if (n.policy.readOnly) labels.push('read-only');
|
|
96
|
+
const suffix = labels.length ? ` [${labels.join(', ')}]` : '';
|
|
97
|
+
lines.push(`- **${n.workspace}** (${n.id})${suffix}`);
|
|
98
|
+
}
|
|
99
|
+
lines.push('', '_Use `mesh_status` to probe live health before delegating work._');
|
|
100
|
+
return lines.join('\n');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function buildPolicySection(policy: RepoMeshPolicy): string {
|
|
104
|
+
const rules: string[] = [];
|
|
105
|
+
if (policy.requirePreTaskCheckpoint) rules.push('- Create a git checkpoint **before** starting each task');
|
|
106
|
+
if (policy.requirePostTaskCheckpoint) rules.push('- Create a git checkpoint **after** each task completes');
|
|
107
|
+
if (policy.requireApprovalForPush) rules.push('- **Ask for user approval** before pushing to remote');
|
|
108
|
+
if (policy.requireApprovalForDestructiveGit) rules.push('- **Ask for user approval** before destructive git operations (force push, reset, etc.)');
|
|
109
|
+
|
|
110
|
+
const dirtyBehavior = {
|
|
111
|
+
block: '- **Do not** send tasks to nodes with dirty workspaces',
|
|
112
|
+
warn: '- Warn the user if a node has uncommitted changes before sending a task',
|
|
113
|
+
checkpoint_then_continue: '- Auto-checkpoint dirty nodes before sending tasks',
|
|
114
|
+
}[policy.dirtyWorkspaceBehavior] || '';
|
|
115
|
+
if (dirtyBehavior) rules.push(dirtyBehavior);
|
|
116
|
+
|
|
117
|
+
rules.push(`- Maximum **${policy.maxParallelTasks}** tasks running in parallel`);
|
|
118
|
+
|
|
119
|
+
return `## Policy\n${rules.join('\n')}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const TOOLS_SECTION = `## Available Tools
|
|
123
|
+
|
|
124
|
+
| Tool | Purpose |
|
|
125
|
+
|------|---------|
|
|
126
|
+
| \`mesh_status\` | Check all nodes' health, git state, and active sessions |
|
|
127
|
+
| \`mesh_list_nodes\` | List nodes with workspace paths |
|
|
128
|
+
| \`mesh_launch_session\` | Start a new agent session on a node |
|
|
129
|
+
| \`mesh_send_task\` | Send a task (natural language) to a running agent |
|
|
130
|
+
| \`mesh_read_chat\` | Read an agent's recent messages to check progress |
|
|
131
|
+
| \`mesh_git_status\` | Check git status on a specific node |
|
|
132
|
+
| \`mesh_checkpoint\` | Create a git checkpoint on a node |
|
|
133
|
+
| \`mesh_approve\` | Approve/reject a pending agent action |`;
|
|
134
|
+
|
|
135
|
+
const WORKFLOW_SECTION = `## Orchestration Workflow
|
|
136
|
+
|
|
137
|
+
1. **Assess** — Call \`mesh_status\` to see which nodes are healthy and available.
|
|
138
|
+
2. **Plan** — Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist.
|
|
139
|
+
3. **Delegate** — For each task:
|
|
140
|
+
a. Pick the best node (consider: health, dirty state, current workload).
|
|
141
|
+
b. If no session exists, call \`mesh_launch_session\` to start one.
|
|
142
|
+
c. Call \`mesh_send_task\` with a clear, self-contained natural-language instruction.
|
|
143
|
+
4. **Monitor** — Periodically call \`mesh_read_chat\` to check progress. Handle approvals via \`mesh_approve\`.
|
|
144
|
+
5. **Verify** — When a task reports completion, call \`mesh_git_status\` to verify changes were made.
|
|
145
|
+
6. **Checkpoint** — Call \`mesh_checkpoint\` to save the work.
|
|
146
|
+
7. **Report** — Summarize what was done, what changed, and any issues.`;
|
|
147
|
+
|
|
148
|
+
const RULES_SECTION = `## Rules
|
|
149
|
+
|
|
150
|
+
- **Be conversational.** Delegate work the way a tech lead would — clear, specific instructions in natural language.
|
|
151
|
+
- **Don't inspect code.** Trust the agent's output. Verify via git diff/status, not by reading source files.
|
|
152
|
+
- **Don't over-parallelize.** Start with 1-2 concurrent tasks. Scale up if they succeed.
|
|
153
|
+
- **Handle failures gracefully.** If a task fails, read the chat to understand why, then retry or reassign.
|
|
154
|
+
- **Keep the user informed.** Report progress after each delegation round.
|
|
155
|
+
- **Respect node capabilities.** Don't send build tasks to read-only nodes. Don't push from nodes that aren't allowed to.
|
|
156
|
+
- **Never fabricate tool results.** Always call the actual tool; never pretend you did.`;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mesh Sync — Sync local mesh config to/from cloud D1
|
|
3
|
+
*
|
|
4
|
+
* When cloud is available, this module pushes local mesh config
|
|
5
|
+
* to the server and pulls remote meshes that were created from
|
|
6
|
+
* other machines. The local ~/.adhdev/meshes.json remains the
|
|
7
|
+
* canonical source; cloud is a persistence/relay layer.
|
|
8
|
+
*
|
|
9
|
+
* This is called lazily (not on daemon startup) — only when the
|
|
10
|
+
* user explicitly opens the mesh page or runs `adhdev mesh sync`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { listMeshes, getMesh, createMesh, deleteMesh, addNode, removeNode } from '../config/mesh-config.js';
|
|
14
|
+
import type { LocalMeshEntry, LocalMeshNodeEntry } from '../repo-mesh-types.js';
|
|
15
|
+
|
|
16
|
+
export interface MeshSyncTransport {
|
|
17
|
+
/** GET /api/v1/repo-meshes */
|
|
18
|
+
listRemoteMeshes(): Promise<{ meshes: RemoteMeshRecord[] }>;
|
|
19
|
+
/** POST /api/v1/repo-meshes */
|
|
20
|
+
createRemoteMesh(data: {
|
|
21
|
+
name: string;
|
|
22
|
+
repo_identity: string;
|
|
23
|
+
repo_remote_url?: string;
|
|
24
|
+
default_branch?: string;
|
|
25
|
+
policy?: string;
|
|
26
|
+
}): Promise<{ mesh: RemoteMeshRecord }>;
|
|
27
|
+
/** DELETE /api/v1/repo-meshes/:id */
|
|
28
|
+
deleteRemoteMesh(meshId: string): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RemoteMeshRecord {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
repo_identity: string;
|
|
35
|
+
repo_remote_url: string | null;
|
|
36
|
+
default_branch: string | null;
|
|
37
|
+
policy: string;
|
|
38
|
+
status: string;
|
|
39
|
+
created_at: string;
|
|
40
|
+
updated_at: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface MeshSyncResult {
|
|
44
|
+
pushed: number;
|
|
45
|
+
pulled: number;
|
|
46
|
+
deleted: number;
|
|
47
|
+
errors: string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Push local meshes to cloud (upsert by repo_identity).
|
|
52
|
+
* Pull remote meshes that don't exist locally.
|
|
53
|
+
*/
|
|
54
|
+
export async function syncMeshes(transport: MeshSyncTransport): Promise<MeshSyncResult> {
|
|
55
|
+
const result: MeshSyncResult = { pushed: 0, pulled: 0, deleted: 0, errors: [] };
|
|
56
|
+
|
|
57
|
+
let remoteMeshes: RemoteMeshRecord[];
|
|
58
|
+
try {
|
|
59
|
+
const res = await transport.listRemoteMeshes();
|
|
60
|
+
remoteMeshes = res.meshes;
|
|
61
|
+
} catch (e: any) {
|
|
62
|
+
result.errors.push(`Failed to list remote meshes: ${e.message}`);
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const localMeshes = listMeshes();
|
|
67
|
+
const remoteByIdentity = new Map(remoteMeshes.map(m => [m.repo_identity, m]));
|
|
68
|
+
const localByIdentity = new Map(localMeshes.map(m => [m.repoIdentity, m]));
|
|
69
|
+
|
|
70
|
+
// Push: local meshes not in cloud
|
|
71
|
+
for (const local of localMeshes) {
|
|
72
|
+
if (!remoteByIdentity.has(local.repoIdentity)) {
|
|
73
|
+
try {
|
|
74
|
+
await transport.createRemoteMesh({
|
|
75
|
+
name: local.name,
|
|
76
|
+
repo_identity: local.repoIdentity,
|
|
77
|
+
repo_remote_url: local.repoRemoteUrl,
|
|
78
|
+
default_branch: local.defaultBranch,
|
|
79
|
+
policy: JSON.stringify(local.policy),
|
|
80
|
+
});
|
|
81
|
+
result.pushed++;
|
|
82
|
+
} catch (e: any) {
|
|
83
|
+
result.errors.push(`Push failed for "${local.name}": ${e.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Pull: remote meshes not in local
|
|
89
|
+
for (const remote of remoteMeshes) {
|
|
90
|
+
if (!localByIdentity.has(remote.repo_identity)) {
|
|
91
|
+
try {
|
|
92
|
+
let policy;
|
|
93
|
+
try { policy = JSON.parse(remote.policy); } catch { policy = undefined; }
|
|
94
|
+
createMesh({
|
|
95
|
+
name: remote.name,
|
|
96
|
+
repoIdentity: remote.repo_identity,
|
|
97
|
+
repoRemoteUrl: remote.repo_remote_url || undefined,
|
|
98
|
+
defaultBranch: remote.default_branch || undefined,
|
|
99
|
+
policy,
|
|
100
|
+
});
|
|
101
|
+
result.pulled++;
|
|
102
|
+
} catch (e: any) {
|
|
103
|
+
result.errors.push(`Pull failed for "${remote.name}": ${e.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repo Mesh Types — Cross-package type definitions for repo-scoped orchestration
|
|
3
|
+
*
|
|
4
|
+
* A Repo Mesh is a repo-scoped execution environment that groups
|
|
5
|
+
* machines/workspaces around one Git repository identity. A coordinator
|
|
6
|
+
* agent delegates work to mesh nodes via natural conversation.
|
|
7
|
+
*
|
|
8
|
+
* These types are OSS-level and usable without cloud infrastructure.
|
|
9
|
+
* Import via: import type { ... } from '@adhdev/daemon-core/repo-mesh-types'
|
|
10
|
+
*
|
|
11
|
+
* IMPORTANT: This file must remain runtime-free (types only).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { GitRepoStatus, GitCompactSummary } from './git/git-types.js';
|
|
15
|
+
|
|
16
|
+
// ─── Core Mesh Types ────────────────────────────
|
|
17
|
+
|
|
18
|
+
export interface RepoMesh {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
repoIdentity: string;
|
|
22
|
+
repoRemoteUrl?: string;
|
|
23
|
+
defaultBranch?: string;
|
|
24
|
+
policy: RepoMeshPolicy;
|
|
25
|
+
coordinator: RepoMeshCoordinatorConfig;
|
|
26
|
+
projectContext: ProjectContextSnapshot;
|
|
27
|
+
nodes: RepoMeshNode[];
|
|
28
|
+
status: 'active' | 'archived' | 'deleted';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RepoMeshNode {
|
|
32
|
+
id: string;
|
|
33
|
+
daemonId: string;
|
|
34
|
+
machineId?: string;
|
|
35
|
+
machineLabel: string;
|
|
36
|
+
workspace: string;
|
|
37
|
+
repoRoot?: string;
|
|
38
|
+
git?: GitCompactSummary;
|
|
39
|
+
providers: string[];
|
|
40
|
+
detectedCapabilities: RepoMeshNodeCapabilities;
|
|
41
|
+
userOverrides: Partial<RepoMeshNodeCapabilities>;
|
|
42
|
+
effectiveCapabilities: RepoMeshNodeCapabilities;
|
|
43
|
+
policy: RepoMeshNodePolicy;
|
|
44
|
+
health: RepoMeshNodeHealth;
|
|
45
|
+
status: 'enabled' | 'disabled' | 'removed';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type RepoMeshNodeHealth =
|
|
49
|
+
| 'online'
|
|
50
|
+
| 'offline'
|
|
51
|
+
| 'degraded'
|
|
52
|
+
| 'dirty'
|
|
53
|
+
| 'wrong_branch'
|
|
54
|
+
| 'unknown';
|
|
55
|
+
|
|
56
|
+
// ─── Policy Types ───────────────────────────────
|
|
57
|
+
|
|
58
|
+
export interface RepoMeshPolicy {
|
|
59
|
+
requirePreTaskCheckpoint: boolean;
|
|
60
|
+
requirePostTaskCheckpoint: boolean;
|
|
61
|
+
requireApprovalForPush: boolean;
|
|
62
|
+
requireApprovalForDestructiveGit: boolean;
|
|
63
|
+
dirtyWorkspaceBehavior: 'block' | 'warn' | 'checkpoint_then_continue';
|
|
64
|
+
maxParallelTasks: number;
|
|
65
|
+
allowedProviders?: string[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface RepoMeshNodePolicy {
|
|
69
|
+
readOnly?: boolean;
|
|
70
|
+
canPush?: boolean;
|
|
71
|
+
maxConcurrentSessions?: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const DEFAULT_MESH_POLICY: RepoMeshPolicy = {
|
|
75
|
+
requirePreTaskCheckpoint: false,
|
|
76
|
+
requirePostTaskCheckpoint: true,
|
|
77
|
+
requireApprovalForPush: true,
|
|
78
|
+
requireApprovalForDestructiveGit: true,
|
|
79
|
+
dirtyWorkspaceBehavior: 'warn',
|
|
80
|
+
maxParallelTasks: 2,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// ─── Capabilities ───────────────────────────────
|
|
84
|
+
|
|
85
|
+
export interface RepoMeshNodeCapabilities {
|
|
86
|
+
platform?: string;
|
|
87
|
+
packageManagers?: string[];
|
|
88
|
+
detectedCommands?: DetectedCommand[];
|
|
89
|
+
canRunLongJobs?: boolean;
|
|
90
|
+
canRunDocker?: boolean;
|
|
91
|
+
canRunBrowserE2E?: boolean;
|
|
92
|
+
canAccessSecrets?: boolean;
|
|
93
|
+
canPush?: boolean;
|
|
94
|
+
readOnly?: boolean;
|
|
95
|
+
userLabels?: string[];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface DetectedCommand {
|
|
99
|
+
command: string;
|
|
100
|
+
sourcePath: string;
|
|
101
|
+
confidence: 'high' | 'medium' | 'low';
|
|
102
|
+
requiresApproval?: boolean;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── Project Context ────────────────────────────
|
|
106
|
+
|
|
107
|
+
export interface ProjectContextSnapshot {
|
|
108
|
+
version: number;
|
|
109
|
+
generatedAt: string;
|
|
110
|
+
sources: ProjectContextSource[];
|
|
111
|
+
repo: {
|
|
112
|
+
identity: string;
|
|
113
|
+
remoteUrl?: string;
|
|
114
|
+
defaultBranch?: string;
|
|
115
|
+
currentBranches: string[];
|
|
116
|
+
};
|
|
117
|
+
layout: {
|
|
118
|
+
packageManager?: string;
|
|
119
|
+
workspaceFiles: string[];
|
|
120
|
+
packageRoots: string[];
|
|
121
|
+
likelyEntryPoints: string[];
|
|
122
|
+
};
|
|
123
|
+
commands: {
|
|
124
|
+
build?: DetectedCommand[];
|
|
125
|
+
test?: DetectedCommand[];
|
|
126
|
+
typecheck?: DetectedCommand[];
|
|
127
|
+
lint?: DetectedCommand[];
|
|
128
|
+
e2e?: DetectedCommand[];
|
|
129
|
+
};
|
|
130
|
+
instructions: {
|
|
131
|
+
files: string[];
|
|
132
|
+
summary: string;
|
|
133
|
+
};
|
|
134
|
+
conventions: {
|
|
135
|
+
pathHints: string[];
|
|
136
|
+
validationNotes: string[];
|
|
137
|
+
riskyAreas: string[];
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface ProjectContextSource {
|
|
142
|
+
kind: 'daemon_status' | 'git' | 'project_file' | 'instruction_file' | 'user_override' | 'probe_error';
|
|
143
|
+
nodeId?: string;
|
|
144
|
+
path?: string;
|
|
145
|
+
observedAt: string;
|
|
146
|
+
confidence: 'high' | 'medium' | 'low';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ─── Coordinator Config ─────────────────────────
|
|
150
|
+
|
|
151
|
+
export interface RepoMeshCoordinatorConfig {
|
|
152
|
+
/** Provider to use for coordinator session (e.g. 'claude-cli', 'cursor') */
|
|
153
|
+
providerType?: string;
|
|
154
|
+
/** Preferred node to run coordinator on (null = auto) */
|
|
155
|
+
preferredNodeId?: string;
|
|
156
|
+
/** Additional system prompt context for coordinator */
|
|
157
|
+
systemPromptSuffix?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── Local Mesh Config (OSS standalone) ─────────
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Local mesh configuration stored in ~/.adhdev/meshes.json
|
|
164
|
+
* Used by OSS standalone mode without cloud infrastructure.
|
|
165
|
+
*/
|
|
166
|
+
export interface LocalMeshConfig {
|
|
167
|
+
meshes: LocalMeshEntry[];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface LocalMeshEntry {
|
|
171
|
+
id: string;
|
|
172
|
+
name: string;
|
|
173
|
+
repoIdentity: string;
|
|
174
|
+
repoRemoteUrl?: string;
|
|
175
|
+
defaultBranch?: string;
|
|
176
|
+
policy: RepoMeshPolicy;
|
|
177
|
+
coordinator: RepoMeshCoordinatorConfig;
|
|
178
|
+
nodes: LocalMeshNodeEntry[];
|
|
179
|
+
createdAt: string;
|
|
180
|
+
updatedAt: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export interface LocalMeshNodeEntry {
|
|
184
|
+
id: string;
|
|
185
|
+
workspace: string;
|
|
186
|
+
repoRoot?: string;
|
|
187
|
+
userOverrides: Partial<RepoMeshNodeCapabilities>;
|
|
188
|
+
policy: RepoMeshNodePolicy;
|
|
189
|
+
/** For single-machine mesh: same daemon, different worktree */
|
|
190
|
+
isLocalWorktree?: boolean;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ─── Mesh Status (runtime, not persisted) ───────
|
|
194
|
+
|
|
195
|
+
export interface RepoMeshStatus {
|
|
196
|
+
meshId: string;
|
|
197
|
+
meshName: string;
|
|
198
|
+
repoIdentity: string;
|
|
199
|
+
refreshedAt: string;
|
|
200
|
+
nodes: RepoMeshNodeStatus[];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface RepoMeshNodeStatus {
|
|
204
|
+
nodeId: string;
|
|
205
|
+
machineLabel: string;
|
|
206
|
+
workspace: string;
|
|
207
|
+
health: RepoMeshNodeHealth;
|
|
208
|
+
git?: GitRepoStatus;
|
|
209
|
+
providers: string[];
|
|
210
|
+
activeSessions: string[];
|
|
211
|
+
error?: string;
|
|
212
|
+
}
|