@adhdev/daemon-core 0.9.77-rc.9 → 0.9.78
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/boot/daemon-lifecycle.d.ts +3 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +2 -0
- package/dist/commands/mesh-coordinator.d.ts +10 -0
- package/dist/commands/router.d.ts +4 -1
- package/dist/config/mesh-config.d.ts +1 -0
- package/dist/git/git-worktree.d.ts +15 -2
- package/dist/index.d.ts +11 -6
- package/dist/index.js +2117 -300
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2102 -300
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events.d.ts +14 -7
- package/dist/mesh/mesh-ledger-reconciliation.d.ts +55 -0
- package/dist/mesh/mesh-ledger.d.ts +84 -4
- package/dist/mesh/mesh-sync.d.ts +4 -12
- package/dist/mesh/mesh-visualization.d.ts +70 -0
- package/dist/mesh/mesh-work-queue.d.ts +58 -1
- package/dist/mesh/p2p-relay-failure.d.ts +35 -0
- package/dist/providers/chat-message-normalization.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +6 -0
- package/dist/repo-mesh-types.d.ts +2 -0
- package/dist/shared-types.d.ts +38 -0
- package/package.json +1 -1
- package/src/boot/daemon-lifecycle.ts +5 -0
- package/src/cli-adapters/provider-cli-adapter.ts +30 -5
- package/src/commands/cli-manager.ts +0 -4
- package/src/commands/mesh-coordinator.ts +55 -7
- package/src/commands/router.ts +964 -26
- package/src/commands/stream-commands.ts +8 -1
- package/src/config/config.ts +2 -1
- package/src/config/mesh-config.ts +2 -0
- package/src/config/workspaces.ts +1 -1
- package/src/git/git-worktree.ts +56 -4
- package/src/index.d.ts +3 -0
- package/src/index.ts +30 -6
- package/src/mesh/coordinator-prompt.ts +21 -10
- package/src/mesh/mesh-events.ts +532 -22
- package/src/mesh/mesh-ledger-reconciliation.ts +115 -0
- package/src/mesh/mesh-ledger.ts +209 -8
- package/src/mesh/mesh-sync.ts +4 -34
- package/src/mesh/mesh-visualization.ts +341 -0
- package/src/mesh/mesh-work-queue.ts +183 -17
- package/src/mesh/p2p-relay-failure.ts +152 -0
- package/src/providers/acp-provider-instance.ts +2 -1
- package/src/providers/chat-message-normalization.ts +33 -1
- package/src/providers/cli-provider-instance.ts +155 -31
- package/src/providers/extension-provider-instance.ts +2 -1
- package/src/providers/ide-provider-instance.ts +2 -2
- package/src/repo-mesh-types.ts +2 -0
- package/src/shared-types.ts +38 -0
|
@@ -113,11 +113,18 @@ export async function handleOpenPanel(h: CommandHelpers, args: any): Promise<Com
|
|
|
113
113
|
export async function handlePtyInput(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
114
114
|
const { cliType, data, targetSessionId } = args || {};
|
|
115
115
|
if (!data) return { success: false, error: 'data required' };
|
|
116
|
+
|
|
117
|
+
// Filter out VT100/VT420 Device Attributes responses (e.g. \x1b[?1;2c or \x1b[>0;276;0c)
|
|
118
|
+
// These are echoed by xterm.js in the dashboard in response to \x1b[c queries
|
|
119
|
+
// and pollute the CLI input buffer.
|
|
120
|
+
const cleanData = typeof data === 'string' ? data.replace(/\x1b\[[?>][0-9;]*c/g, '') : data;
|
|
121
|
+
if (!cleanData) return { success: true };
|
|
122
|
+
|
|
116
123
|
const adapter = h.getCliAdapter(targetSessionId || cliType);
|
|
117
124
|
if (!adapter || typeof adapter.writeRaw !== 'function') {
|
|
118
125
|
return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || 'unknown'}` };
|
|
119
126
|
}
|
|
120
|
-
await adapter.writeRaw(
|
|
127
|
+
await adapter.writeRaw(cleanData);
|
|
121
128
|
return { success: true };
|
|
122
129
|
}
|
|
123
130
|
|
package/src/config/config.ts
CHANGED
|
@@ -264,7 +264,8 @@ function ensureMachineId(config: ADHDevConfig): { config: ADHDevConfig; changed:
|
|
|
264
264
|
* Get the config directory path
|
|
265
265
|
*/
|
|
266
266
|
export function getConfigDir(): string {
|
|
267
|
-
const
|
|
267
|
+
const override = process.env.ADHDEV_CONFIG_DIR;
|
|
268
|
+
const dir = override && override.trim() ? override.trim() : join(homedir(), '.adhdev');
|
|
268
269
|
if (!existsSync(dir)) {
|
|
269
270
|
mkdirSync(dir, { recursive: true });
|
|
270
271
|
}
|
|
@@ -180,6 +180,7 @@ export interface AddNodeOptions {
|
|
|
180
180
|
workspace: string;
|
|
181
181
|
repoRoot?: string;
|
|
182
182
|
daemonId?: string;
|
|
183
|
+
machineId?: string;
|
|
183
184
|
userOverrides?: Partial<RepoMeshNodeCapabilities>;
|
|
184
185
|
policy?: RepoMeshNodePolicy;
|
|
185
186
|
isLocalWorktree?: boolean;
|
|
@@ -206,6 +207,7 @@ export function addNode(meshId: string, opts: AddNodeOptions): LocalMeshNodeEntr
|
|
|
206
207
|
workspace: opts.workspace.trim(),
|
|
207
208
|
repoRoot: opts.repoRoot,
|
|
208
209
|
daemonId: opts.daemonId,
|
|
210
|
+
machineId: opts.machineId,
|
|
209
211
|
userOverrides: opts.userOverrides || {},
|
|
210
212
|
policy: opts.policy || {},
|
|
211
213
|
isLocalWorktree: opts.isLocalWorktree,
|
package/src/config/workspaces.ts
CHANGED
|
@@ -201,7 +201,7 @@ export function addWorkspaceEntry(
|
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
const v = validateWorkspacePath(abs);
|
|
204
|
-
if (
|
|
204
|
+
if (v.ok !== true) return { error: v.error };
|
|
205
205
|
|
|
206
206
|
const list = [...(config.workspaces || [])];
|
|
207
207
|
if (list.some(w => path.resolve(w.path) === abs)) {
|
package/src/git/git-worktree.ts
CHANGED
|
@@ -20,6 +20,7 @@ const execFileAsync = promisify(execFile);
|
|
|
20
20
|
const WORKTREE_DIR_NAME = '.adhdev-worktrees';
|
|
21
21
|
const GIT_TIMEOUT_MS = 30_000;
|
|
22
22
|
const GIT_MAX_BUFFER = 4 * 1024 * 1024;
|
|
23
|
+
const SUBMODULE_WORKTREE_REMOVE_RE = /working trees containing submodules cannot be moved or removed/i;
|
|
23
24
|
|
|
24
25
|
// ─── Types ──────────────────────────────────────
|
|
25
26
|
|
|
@@ -49,9 +50,23 @@ export interface WorktreeEntry {
|
|
|
49
50
|
bare: boolean;
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
export interface WorktreeRemoveOptions {
|
|
54
|
+
/** Refuse to remove a worktree with uncommitted or untracked changes. */
|
|
55
|
+
requireClean?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* If normal removal fails with Git's submodule-worktree guard, retry with
|
|
58
|
+
* `git worktree remove --force`. Callers must perform their own
|
|
59
|
+
* higher-level managed-path/convergence checks before enabling this.
|
|
60
|
+
*/
|
|
61
|
+
allowSubmoduleForceFallback?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
52
64
|
export interface WorktreeRemoveResult {
|
|
53
65
|
success: true;
|
|
54
66
|
removedPath: string;
|
|
67
|
+
fallback?: 'git_worktree_remove_force_submodule';
|
|
68
|
+
forced?: boolean;
|
|
69
|
+
reason?: 'working_trees_containing_submodules';
|
|
55
70
|
}
|
|
56
71
|
|
|
57
72
|
// ─── Path Resolution ────────────────────────────
|
|
@@ -120,17 +135,30 @@ export async function createWorktree(opts: WorktreeCreateOptions): Promise<Workt
|
|
|
120
135
|
/**
|
|
121
136
|
* Remove a git worktree and clean up the directory.
|
|
122
137
|
*
|
|
123
|
-
* Runs: git worktree remove <worktreePath>
|
|
138
|
+
* Runs: git worktree remove <worktreePath>
|
|
124
139
|
*/
|
|
125
|
-
export async function removeWorktree(repoRoot: string, worktreePath: string): Promise<WorktreeRemoveResult> {
|
|
140
|
+
export async function removeWorktree(repoRoot: string, worktreePath: string, opts: WorktreeRemoveOptions = {}): Promise<WorktreeRemoveResult> {
|
|
126
141
|
if (!existsSync(worktreePath)) {
|
|
127
142
|
// Already gone — just prune
|
|
128
143
|
await pruneWorktrees(repoRoot);
|
|
129
144
|
return { success: true, removedPath: worktreePath };
|
|
130
145
|
}
|
|
131
146
|
|
|
147
|
+
if (opts.requireClean) {
|
|
148
|
+
const { stdout } = await execFileAsync('git', ['status', '--porcelain'], {
|
|
149
|
+
cwd: worktreePath,
|
|
150
|
+
encoding: 'utf8',
|
|
151
|
+
timeout: GIT_TIMEOUT_MS,
|
|
152
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
153
|
+
windowsHide: true,
|
|
154
|
+
});
|
|
155
|
+
if (stdout.trim()) {
|
|
156
|
+
throw new Error(`Refusing to remove dirty worktree: ${worktreePath}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
132
160
|
try {
|
|
133
|
-
await execFileAsync('git', ['worktree', 'remove', worktreePath
|
|
161
|
+
await execFileAsync('git', ['worktree', 'remove', worktreePath], {
|
|
134
162
|
cwd: repoRoot,
|
|
135
163
|
encoding: 'utf8',
|
|
136
164
|
timeout: GIT_TIMEOUT_MS,
|
|
@@ -139,7 +167,31 @@ export async function removeWorktree(repoRoot: string, worktreePath: string): Pr
|
|
|
139
167
|
});
|
|
140
168
|
} catch (error: any) {
|
|
141
169
|
const stderr = typeof error.stderr === 'string' ? error.stderr : '';
|
|
142
|
-
|
|
170
|
+
const stdout = typeof error.stdout === 'string' ? error.stdout : '';
|
|
171
|
+
const detail = `${stderr}\n${stdout}\n${error.message || ''}`;
|
|
172
|
+
if (opts.allowSubmoduleForceFallback && SUBMODULE_WORKTREE_REMOVE_RE.test(detail)) {
|
|
173
|
+
try {
|
|
174
|
+
await execFileAsync('git', ['worktree', 'remove', '--force', worktreePath], {
|
|
175
|
+
cwd: repoRoot,
|
|
176
|
+
encoding: 'utf8',
|
|
177
|
+
timeout: GIT_TIMEOUT_MS,
|
|
178
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
179
|
+
windowsHide: true,
|
|
180
|
+
});
|
|
181
|
+
} catch (forceError: any) {
|
|
182
|
+
const forceStderr = typeof forceError.stderr === 'string' ? forceError.stderr : '';
|
|
183
|
+
const forceStdout = typeof forceError.stdout === 'string' ? forceError.stdout : '';
|
|
184
|
+
throw new Error(`git worktree remove --force fallback failed: ${forceStderr.trim() || forceStdout.trim() || forceError.message}`);
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
success: true,
|
|
188
|
+
removedPath: worktreePath,
|
|
189
|
+
fallback: 'git_worktree_remove_force_submodule',
|
|
190
|
+
forced: true,
|
|
191
|
+
reason: 'working_trees_containing_submodules',
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
throw new Error(`git worktree remove failed: ${stderr.trim() || stdout.trim() || error.message}`);
|
|
143
195
|
}
|
|
144
196
|
|
|
145
197
|
return { success: true, removedPath: worktreePath };
|
package/src/index.d.ts
CHANGED
|
@@ -22,6 +22,9 @@ export { appendRecentActivity, getRecentActivity } from './config/recent-activit
|
|
|
22
22
|
export type { RecentActivityEntry } from './config/recent-activity.js';
|
|
23
23
|
export { getSavedProviderSessions, upsertSavedProviderSession } from './config/saved-sessions.js';
|
|
24
24
|
export type { SavedProviderSessionEntry } from './config/saved-sessions.js';
|
|
25
|
+
export { triggerMeshQueue } from './mesh/mesh-events.js';
|
|
26
|
+
export { P2pRelayFailureError, buildP2pRelayFailurePayload, classifyP2pRelayFailure, isP2pRelayTransportFailure } from './mesh/p2p-relay-failure.js';
|
|
27
|
+
export type { P2pRelayFailureClassification, P2pRelayFailureCode, P2pRelayFailureContext, P2pRelayFailurePayload } from './mesh/p2p-relay-failure.js';
|
|
25
28
|
export { loadState, saveState, resetState } from './config/state-store.js';
|
|
26
29
|
export type { DaemonState } from './config/state-store.js';
|
|
27
30
|
export { detectIDEs } from './detection/ide-detector.js';
|
package/src/index.ts
CHANGED
|
@@ -119,6 +119,7 @@ export type RuntimeWriteOwner = _RuntimeWriteOwner;
|
|
|
119
119
|
export type RuntimeAttachedClient = _RuntimeAttachedClient;
|
|
120
120
|
export type RecentLaunchEntry = _RecentLaunchEntry;
|
|
121
121
|
export type TerminalBackendStatus = _TerminalBackendStatus;
|
|
122
|
+
export type { SessionHostEndpoint } from '@adhdev/session-host-core';
|
|
122
123
|
|
|
123
124
|
// Type aliases — rollup-dts cannot bundle re-exported type aliases at all.
|
|
124
125
|
// Canonical definition lives in shared-types-extra.ts — keep these in sync.
|
|
@@ -150,15 +151,39 @@ export { syncMeshes } from './mesh/mesh-sync.js';
|
|
|
150
151
|
export type { MeshSyncTransport, MeshSyncResult, RemoteMeshRecord } from './mesh/mesh-sync.js';
|
|
151
152
|
|
|
152
153
|
// ── Mesh Task Ledger ──
|
|
153
|
-
export { appendLedgerEntry, readLedgerEntries, getLedgerSummary, getLedgerDir, getSessionRecoveryContext } from './mesh/mesh-ledger.js';
|
|
154
|
-
export type { MeshLedgerEntry, MeshLedgerKind, MeshLedgerSummary, ReadLedgerOptions, SessionRecoveryContext } from './mesh/mesh-ledger.js';
|
|
154
|
+
export { appendLedgerEntry, appendRemoteLedgerEntries, readLedgerEntries, readLedgerSlice, getLedgerSummary, getLedgerDir, getSessionRecoveryContext, MAX_LEDGER_SLICE_LIMIT } from './mesh/mesh-ledger.js';
|
|
155
|
+
export type { AppendRemoteLedgerResult, MeshLedgerEntry, MeshLedgerKind, MeshLedgerSlice, MeshLedgerSummary, ReadLedgerOptions, ReadLedgerSliceOptions, SessionRecoveryContext } from './mesh/mesh-ledger.js';
|
|
156
|
+
export { buildMeshLedgerReconciliationEvidence, buildMeshLedgerReplicaEvidence } from './mesh/mesh-ledger-reconciliation.js';
|
|
157
|
+
export type { MeshLedgerReconciliationEvidence, MeshLedgerReplicaEvidence, MeshLedgerReplicaStatus } from './mesh/mesh-ledger-reconciliation.js';
|
|
155
158
|
|
|
156
159
|
// ── Mesh Work Queue (GUPP) ──
|
|
157
|
-
export { enqueueTask, getQueue, claimNextTask, updateTaskStatus, updateSessionTaskStatus } from './mesh/mesh-work-queue.js';
|
|
158
|
-
export type { MeshWorkQueueEntry, MeshTaskStatus } from './mesh/mesh-work-queue.js';
|
|
160
|
+
export { enqueueTask, getQueue, claimNextTask, updateTaskStatus, updateSessionTaskStatus, cancelTask, requeueTask, getMeshQueueStats } from './mesh/mesh-work-queue.js';
|
|
161
|
+
export type { MeshWorkQueueEntry, MeshTaskStatus, MeshWorkQueueStats } from './mesh/mesh-work-queue.js';
|
|
162
|
+
|
|
163
|
+
// ── Mesh Visualization ──
|
|
164
|
+
// buildMeshGraph and MeshGraph types moved to @adhdev/web-core to avoid
|
|
165
|
+
// bundling Node.js built-ins (fs, path, etc.) into browser builds.
|
|
166
|
+
// Import from '@adhdev/web-core' instead.
|
|
167
|
+
// export { buildMeshGraph } from './mesh/mesh-visualization.js';
|
|
168
|
+
// export type { MeshGraph, MeshGraphNode, MeshGraphEdge, MeshGraphNodeType, MeshGraphEdgeType } from './mesh/mesh-visualization.js';
|
|
159
169
|
|
|
160
170
|
// ── Mesh Events ──
|
|
161
|
-
export { triggerMeshQueue } from './mesh/mesh-events.js';
|
|
171
|
+
export { triggerMeshQueue, drainPendingMeshCoordinatorEvents, getPendingMeshCoordinatorEvents, clearPendingMeshCoordinatorEvents } from './mesh/mesh-events.js';
|
|
172
|
+
export type { PendingMeshCoordinatorEvent } from './mesh/mesh-events.js';
|
|
173
|
+
|
|
174
|
+
// ── Mesh P2P Relay Failure Classification ──
|
|
175
|
+
export {
|
|
176
|
+
P2pRelayFailureError,
|
|
177
|
+
buildP2pRelayFailurePayload,
|
|
178
|
+
classifyP2pRelayFailure,
|
|
179
|
+
isP2pRelayTransportFailure,
|
|
180
|
+
} from './mesh/p2p-relay-failure.js';
|
|
181
|
+
export type {
|
|
182
|
+
P2pRelayFailureClassification,
|
|
183
|
+
P2pRelayFailureCode,
|
|
184
|
+
P2pRelayFailureContext,
|
|
185
|
+
P2pRelayFailurePayload,
|
|
186
|
+
} from './mesh/p2p-relay-failure.js';
|
|
162
187
|
|
|
163
188
|
// ── State Store ──
|
|
164
189
|
export { loadState, saveState, resetState } from './config/state-store.js';
|
|
@@ -374,7 +399,6 @@ export {
|
|
|
374
399
|
} from './session-host/runtime-surface.js';
|
|
375
400
|
export type { SessionHostSurfaceKind, SessionHostSurfaceRecordLike } from './session-host/runtime-surface.js';
|
|
376
401
|
export { shouldAutoRestoreHostedSessionsOnStartup } from './session-host/startup-restore-policy.js';
|
|
377
|
-
export type { SessionHostEndpoint } from '@adhdev/session-host-core';
|
|
378
402
|
|
|
379
403
|
// ── Installer ──
|
|
380
404
|
export { getAIExtensions, installExtensions, launchIDE, isExtensionInstalled } from './installer.js';
|
|
@@ -128,17 +128,24 @@ const TOOLS_SECTION = `## Available Tools
|
|
|
128
128
|
|
|
129
129
|
| Tool | Purpose |
|
|
130
130
|
|------|---------|
|
|
131
|
-
| \`mesh_status\` | Check all nodes' health, git state,
|
|
131
|
+
| \`mesh_status\` | Check all nodes' health, git state, active sessions, and branch convergence |
|
|
132
132
|
| \`mesh_list_nodes\` | List nodes with workspace paths |
|
|
133
|
+
| \`mesh_enqueue_task\` | Add a task to the pull-based work queue; idle nodes auto-claim |
|
|
134
|
+
| \`mesh_view_queue\` | View queue status — pending, assigned, completed, failed, cancelled tasks |
|
|
135
|
+
| \`mesh_queue_cancel\` | Cancel a queue task without deleting audit history |
|
|
136
|
+
| \`mesh_queue_requeue\` | Return a task to pending for retry; clears stale session targets |
|
|
137
|
+
| \`mesh_send_task\` | Legacy push: enqueue a task targeted at a specific node |
|
|
133
138
|
| \`mesh_launch_session\` | Start a new agent session on a node |
|
|
134
|
-
| \`
|
|
135
|
-
| \`
|
|
139
|
+
| \`mesh_read_chat\` | Read recent chat messages from a delegated agent session |
|
|
140
|
+
| \`mesh_read_debug\` | Collect a daemon-side chat/parser debug bundle for a session |
|
|
136
141
|
| \`mesh_task_history\` | Read the task ledger — dispatches, completions, failures. Use to understand what has been done before deciding next steps |
|
|
137
142
|
| \`mesh_git_status\` | Check git status on a specific node |
|
|
138
143
|
| \`mesh_checkpoint\` | Create a git checkpoint on a node |
|
|
139
144
|
| \`mesh_approve\` | Approve/reject a pending agent action |
|
|
140
145
|
| \`mesh_clone_node\` | Create a worktree node for isolated parallel branch work |
|
|
141
|
-
| \`
|
|
146
|
+
| \`mesh_refine_node\` | Validate and merge a completed worktree node back into its base branch |
|
|
147
|
+
| \`mesh_remove_node\` | Remove a node (cleans up worktree if applicable) |
|
|
148
|
+
| \`mesh_cleanup_sessions\` | Manually clean up delegated session records for a node |`;
|
|
142
149
|
|
|
143
150
|
const TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
|
|
144
151
|
|
|
@@ -150,14 +157,16 @@ const WORKFLOW_SECTION = `## Orchestration Workflow
|
|
|
150
157
|
2. **Plan** — Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist. If \`mesh_task_history\` shows a recent failure for a task, decide whether to retry or reassign.
|
|
151
158
|
3. **Queue / Delegate** — The Mesh uses an autonomous pull-based Work Queue:
|
|
152
159
|
a. **General Tasks**: Enqueue tasks using \`mesh_enqueue_task\`. Idle node agents will automatically pull tasks from the queue and begin working.
|
|
153
|
-
b. **Node Preparation**: Call \`mesh_launch_session\`
|
|
160
|
+
b. **Node Preparation**: Reuse an existing idle session on the correct node/provider before launching a new chat/session. Call \`mesh_launch_session\` only when no suitable session exists, when the user explicitly asks for a fresh provider/session, or when branch/worktree isolation requires it. If you need branch isolation for parallel work, call \`mesh_clone_node\` to create a worktree node first.
|
|
154
161
|
c. **Targeted Tasks**: Use \`mesh_send_task\` only when you need to bypass the queue and force a specific node to execute a task immediately.
|
|
155
|
-
d.
|
|
162
|
+
d. For the first dispatch of a new task, provide a **complete, self-contained** instruction that includes all context the agent needs (file paths, line numbers, what to change, why). Do not send partial instructions expecting future follow-up.
|
|
163
|
+
e. For a continuation of the same issue in an existing session, send a concise **delta instruction**: current verified state, the exact failed/blocked step, the newly approved action, and final reporting requirements. Do not resend the full original task or open a new chat solely to continue the same work; that wastes coordinator and worker context.
|
|
156
164
|
4. **Monitor** — Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly. Use \`mesh_view_queue\` to see the status of all pending, assigned, completed, and failed tasks. Do not call \`mesh_read_chat\` again within a few seconds for the same generating session. Use at most one compact \`mesh_read_chat\` check after a completion/approval signal. Handle approvals via \`mesh_approve\`.
|
|
157
165
|
5. **Verify** — When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
|
|
158
166
|
6. **Checkpoint** — Call \`mesh_checkpoint\` to save the work.
|
|
159
|
-
7. **
|
|
160
|
-
8. **
|
|
167
|
+
7. **Converge branches** — Before marking any task complete, classify every touched node/branch into exactly one final state: \`merged_to_main\`, \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\`. Use \`mesh_status\` branchConvergenceSummary and \`mesh_refine_node\` for clean worktree branches when safe. A task that remains on a non-main branch is not fully complete unless the final report names the follow-up state and next step.
|
|
168
|
+
8. **Clean up** — Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
|
|
169
|
+
9. **Report** — Summarize what was done, what changed, any issues, and the branch convergence state.
|
|
161
170
|
|
|
162
171
|
## Failure Recovery
|
|
163
172
|
|
|
@@ -167,7 +176,7 @@ When a node agent stops unexpectedly, the daemon automatically enriches the syst
|
|
|
167
176
|
- A recommendation: **retry**, **reassign**, or **escalate**
|
|
168
177
|
|
|
169
178
|
Follow these recovery rules:
|
|
170
|
-
1. **If "Retry recommended"**:
|
|
179
|
+
1. **If "Retry recommended"**: Check \`mesh_view_queue\` first — the daemon may have auto-requeued. If not, re-launch the session on the same node (\`mesh_launch_session\`), then resend the original task (\`mesh_send_task\`). The system message includes the original task text.
|
|
171
180
|
2. **If "Max retries exceeded"**: Do NOT retry on the same node. Either reassign the task to a different node, or inform the user that the task requires manual intervention.
|
|
172
181
|
3. **If no recovery context**: The stop may be intentional (normal completion). Use \`mesh_read_chat\` once to verify, then move on.
|
|
173
182
|
4. **Always record what happened**: After handling a failure, briefly note the outcome in your report to the user.`;
|
|
@@ -182,7 +191,8 @@ function buildRulesSection(coordinatorCliType?: string): string {
|
|
|
182
191
|
- **Minimize coordinator context.** The coordinator's job is routing, not implementing. Do not read source files, run commands, or analyze code directly — delegate all of that to node agents. Your context should stay lean.
|
|
183
192
|
- **Delegate analysis too.** If you need to understand a bug or explore the codebase, send that investigation as a task to the queue or a node. Do not do it yourself.
|
|
184
193
|
- **Respect explicit provider requests.** If the user names an agent/provider, pass the matching provider type to \`mesh_launch_session\`: Hermes → \`hermes-cli\`, Claude Code/Claude → \`claude-cli\`, Codex → \`codex-cli\`, Gemini → \`gemini-cli\`. Never substitute \`claude-cli\` just because the coordinator itself is Claude Code.
|
|
185
|
-
- **Front-load
|
|
194
|
+
- **Front-load new task messages.** When calling \`mesh_enqueue_task\` or \`mesh_send_task\` for a new task, include everything the agent needs: what files to touch, what the problem is, what the fix should look like. The agent won't ask follow-up questions.
|
|
195
|
+
- **Avoid context-wasting restarts.** For follow-up, retry, commit/push, preview, or cleanup work on the same issue, prefer the existing idle session and send only the delta from its last verified state. Start a fresh chat/session only for genuinely independent work, explicit provider/user request, unsafe transcript contamination, or required branch/worktree isolation.
|
|
186
196
|
- **Don't inspect code.** Treat delegated agent summaries as self-reports, not verification. Verify side effects via \`mesh_git_status\` (including related repo freshness when configured), not by reading source files.
|
|
187
197
|
- **Don't over-parallelize.** Start with 1-2 concurrent tasks. Scale up if they succeed. Never launch a duplicate session or second worker solely because \`mesh_read_chat\` has no final assistant message while the delegated session is still showing tool/terminal activity.
|
|
188
198
|
- **Handle failures with context.** If a task fails, check \`mesh_task_history\` first to see if this task was attempted before and how it failed. Read the chat to understand why, then decide: retry on the same node, reassign to a different node, or escalate to the user.
|
|
@@ -191,5 +201,6 @@ function buildRulesSection(coordinatorCliType?: string): string {
|
|
|
191
201
|
- **Respect node capabilities.** Don't send build tasks to read-only nodes. Don't push from nodes that aren't allowed to.
|
|
192
202
|
- **Never fabricate tool results.** Always call the actual tool; never pretend you did.
|
|
193
203
|
- **Clean up worktree nodes.** After a worktree task completes and its changes are merged or checkpointed, call \`mesh_remove_node\` to free resources.
|
|
204
|
+
- **Do not strand completed branches.** A checkpointed or clean feature/worktree branch is not done by itself. Merge/refine it to the mesh default branch, or explicitly report one of \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\` with the next action.
|
|
194
205
|
- **Name worktree branches meaningfully.** Use descriptive names like \`feat/auth-refactor\` or \`fix/build-123\`.${coordinatorNote}`;
|
|
195
206
|
}
|