@adhdev/daemon-core 0.9.66 → 0.9.67

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.
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Repo Mesh Config — Local mesh configuration stored in ~/.adhdev/meshes.json
3
+ *
4
+ * Manages repo mesh definitions for OSS standalone mode.
5
+ * Cloud mode syncs these to D1 via server routes; standalone mode
6
+ * uses this file as the single source of truth.
7
+ */
8
+ import type { LocalMeshEntry, LocalMeshNodeEntry, RepoMeshPolicy, RepoMeshNodePolicy, RepoMeshNodeCapabilities, RepoMeshCoordinatorConfig } from '../repo-mesh-types.js';
9
+ /**
10
+ * Normalize a Git remote URL into a stable identity string.
11
+ * e.g. "git@github.com:user/repo.git" → "github.com/user/repo"
12
+ * "https://github.com/user/repo.git" → "github.com/user/repo"
13
+ */
14
+ export declare function normalizeRepoIdentity(remoteUrl: string): string;
15
+ export declare function listMeshes(): LocalMeshEntry[];
16
+ export declare function getMesh(meshId: string): LocalMeshEntry | undefined;
17
+ export declare function getMeshByRepo(repoIdentity: string): LocalMeshEntry | undefined;
18
+ export interface CreateMeshOptions {
19
+ name: string;
20
+ repoRemoteUrl?: string;
21
+ repoIdentity?: string;
22
+ defaultBranch?: string;
23
+ policy?: Partial<RepoMeshPolicy>;
24
+ coordinator?: RepoMeshCoordinatorConfig;
25
+ }
26
+ export declare function createMesh(opts: CreateMeshOptions): LocalMeshEntry;
27
+ export interface UpdateMeshOptions {
28
+ name?: string;
29
+ defaultBranch?: string;
30
+ policy?: Partial<RepoMeshPolicy>;
31
+ coordinator?: RepoMeshCoordinatorConfig;
32
+ }
33
+ export declare function updateMesh(meshId: string, opts: UpdateMeshOptions): LocalMeshEntry | undefined;
34
+ export declare function deleteMesh(meshId: string): boolean;
35
+ export interface AddNodeOptions {
36
+ workspace: string;
37
+ repoRoot?: string;
38
+ userOverrides?: Partial<RepoMeshNodeCapabilities>;
39
+ policy?: RepoMeshNodePolicy;
40
+ isLocalWorktree?: boolean;
41
+ }
42
+ export declare function addNode(meshId: string, opts: AddNodeOptions): LocalMeshNodeEntry | undefined;
43
+ export declare function removeNode(meshId: string, nodeId: string): boolean;
44
+ export declare function updateNode(meshId: string, nodeId: string, opts: {
45
+ userOverrides?: Partial<RepoMeshNodeCapabilities>;
46
+ policy?: RepoMeshNodePolicy;
47
+ }): LocalMeshNodeEntry | undefined;
package/dist/index.d.ts CHANGED
@@ -5,6 +5,8 @@
5
5
  */
6
6
  export type { ChatBubbleState, ChatMessage, ExtensionInfo, CommandResult as CoreCommandResult, ProviderConfig, DaemonEvent, StatusResponse, SystemInfo, DetectedIde, ProviderInfo, AgentEntry, } from './types.js';
7
7
  export type { SessionEntry, CompactSessionEntry, CompactDaemonEntry, CloudDaemonSummaryEntry, DashboardBootstrapDaemonEntry, VersionUpdateReason, CloudStatusReportPayload, DaemonStatusEventPayload, DashboardStatusEventPayload, SessionTransport, SessionKind, SessionCapability, AgentSessionStream, ReadChatCursor, ReadChatSyncResult, TransportTopic, SessionChatTailSubscriptionParams, SessionRuntimeOutputSubscriptionParams, MachineRuntimeSubscriptionParams, SessionHostDiagnosticsSubscriptionParams, SessionModalSubscriptionParams, DaemonMetadataSubscriptionParams, WorkspaceGitSubscriptionParams, SessionChatTailUpdate, MachineRuntimeUpdate, SessionHostDiagnosticsUpdate, SessionModalUpdate, DaemonMetadataUpdate, TopicUpdateEnvelope, SubscribeRequest, UnsubscribeRequest, StandaloneWsStatusPayload, AvailableProviderInfo, AcpConfigOption, AcpMode, ProviderControlSchema, StatusReportPayload, MachineInfo, SessionHostDiagnosticsSnapshot, SessionHostRecord, SessionHostWriteOwner, SessionHostAttachedClient, SessionHostLogEntry, SessionHostRequestTrace, SessionHostRuntimeTransition, DetectedIdeInfo, WorkspaceEntry, ProviderSummaryItem, ProviderSummaryMetadata, ProviderState, ProviderStatus, ProviderErrorReason, SessionActiveChatData, ActiveChatData, IdeProviderState, CliProviderState, AcpProviderState, ExtensionProviderState, } from './shared-types.js';
8
+ export type { RepoMesh, RepoMeshNode, RepoMeshNodeHealth, RepoMeshPolicy, RepoMeshNodePolicy, RepoMeshNodeCapabilities, DetectedCommand, ProjectContextSnapshot, ProjectContextSource, RepoMeshCoordinatorConfig, LocalMeshConfig, LocalMeshEntry, LocalMeshNodeEntry, RepoMeshStatus, RepoMeshNodeStatus, } from './repo-mesh-types.js';
9
+ export { DEFAULT_MESH_POLICY } from './repo-mesh-types.js';
8
10
  export * from './git/index.js';
9
11
  import type { RuntimeWriteOwner as _RuntimeWriteOwner } from './shared-types-extra.js';
10
12
  import type { RuntimeAttachedClient as _RuntimeAttachedClient } from './shared-types-extra.js';
@@ -23,6 +25,12 @@ export { appendRecentActivity, getRecentActivity } from './config/recent-activit
23
25
  export type { RecentActivityEntry } from './config/recent-activity.js';
24
26
  export { getSavedProviderSessions, upsertSavedProviderSession } from './config/saved-sessions.js';
25
27
  export type { SavedProviderSessionEntry } from './config/saved-sessions.js';
28
+ export { listMeshes, getMesh, getMeshByRepo, createMesh, updateMesh, deleteMesh, addNode, removeNode, updateNode, normalizeRepoIdentity, } from './config/mesh-config.js';
29
+ export type { CreateMeshOptions, UpdateMeshOptions, AddNodeOptions } from './config/mesh-config.js';
30
+ export { buildCoordinatorSystemPrompt } from './mesh/coordinator-prompt.js';
31
+ export type { CoordinatorPromptContext } from './mesh/coordinator-prompt.js';
32
+ export { syncMeshes } from './mesh/mesh-sync.js';
33
+ export type { MeshSyncTransport, MeshSyncResult, RemoteMeshRecord } from './mesh/mesh-sync.js';
26
34
  export { loadState, saveState, resetState } from './config/state-store.js';
27
35
  export type { DaemonState } from './config/state-store.js';
28
36
  export { detectIDEs } from './detection/ide-detector.js';
package/dist/index.js CHANGED
@@ -3339,6 +3339,7 @@ __export(index_exports, {
3339
3339
  DEFAULT_DAEMON_PORT: () => DEFAULT_DAEMON_PORT,
3340
3340
  DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS: () => DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
3341
3341
  DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
3342
+ DEFAULT_MESH_POLICY: () => DEFAULT_MESH_POLICY,
3342
3343
  DEFAULT_SESSION_HOST_APP_NAME: () => DEFAULT_SESSION_HOST_APP_NAME,
3343
3344
  DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
3344
3345
  DEFAULT_SESSION_HOST_READY_TIMEOUT_MS: () => DEFAULT_SESSION_HOST_READY_TIMEOUT_MS,
@@ -3372,11 +3373,13 @@ __export(index_exports, {
3372
3373
  SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory,
3373
3374
  TurnSnapshotTracker: () => TurnSnapshotTracker,
3374
3375
  VersionArchive: () => VersionArchive,
3376
+ addNode: () => addNode,
3375
3377
  appendRecentActivity: () => appendRecentActivity,
3376
3378
  buildAssistantChatMessage: () => buildAssistantChatMessage,
3377
3379
  buildChatMessage: () => buildChatMessage,
3378
3380
  buildChatMessageSignature: () => buildChatMessageSignature,
3379
3381
  buildChatTailDeliverySignature: () => buildChatTailDeliverySignature,
3382
+ buildCoordinatorSystemPrompt: () => buildCoordinatorSystemPrompt,
3380
3383
  buildMachineInfo: () => buildMachineInfo,
3381
3384
  buildPinnedGlobalInstallCommand: () => buildPinnedGlobalInstallCommand,
3382
3385
  buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
@@ -3399,6 +3402,8 @@ __export(index_exports, {
3399
3402
  createGitSnapshotStore: () => createGitSnapshotStore,
3400
3403
  createGitWorkspaceMonitor: () => createGitWorkspaceMonitor,
3401
3404
  createInteractionId: () => createInteractionId,
3405
+ createMesh: () => createMesh,
3406
+ deleteMesh: () => deleteMesh,
3402
3407
  detectAllVersions: () => detectAllVersions,
3403
3408
  detectCLIs: () => detectCLIs,
3404
3409
  detectIDEs: () => detectIDEs,
@@ -3417,6 +3422,8 @@ __export(index_exports, {
3417
3422
  getGitRepoStatus: () => getGitRepoStatus,
3418
3423
  getHostMemorySnapshot: () => getHostMemorySnapshot,
3419
3424
  getLogLevel: () => getLogLevel,
3425
+ getMesh: () => getMesh,
3426
+ getMeshByRepo: () => getMeshByRepo,
3420
3427
  getNpmExecOptions: () => getNpmExecOptions,
3421
3428
  getRecentActivity: () => getRecentActivity,
3422
3429
  getRecentCommands: () => getRecentCommands,
@@ -3447,6 +3454,7 @@ __export(index_exports, {
3447
3454
  launchIDE: () => launchIDE,
3448
3455
  launchWithCdp: () => launchWithCdp,
3449
3456
  listHostedCliRuntimes: () => listHostedCliRuntimes,
3457
+ listMeshes: () => listMeshes,
3450
3458
  loadConfig: () => loadConfig,
3451
3459
  loadState: () => loadState,
3452
3460
  logCommand: () => logCommand,
@@ -3462,6 +3470,7 @@ __export(index_exports, {
3462
3470
  normalizeInputEnvelope: () => normalizeInputEnvelope,
3463
3471
  normalizeManagedStatus: () => normalizeManagedStatus,
3464
3472
  normalizeMessageParts: () => normalizeMessageParts,
3473
+ normalizeRepoIdentity: () => normalizeRepoIdentity,
3465
3474
  normalizeSessionModalFields: () => normalizeSessionModalFields,
3466
3475
  parsePorcelainV2Status: () => parsePorcelainV2Status,
3467
3476
  parseProviderSourceConfigUpdate: () => parseProviderSourceConfigUpdate,
@@ -3473,6 +3482,7 @@ __export(index_exports, {
3473
3482
  readChatHistory: () => readChatHistory,
3474
3483
  recordDebugTrace: () => recordDebugTrace,
3475
3484
  registerExtensionProviders: () => registerExtensionProviders,
3485
+ removeNode: () => removeNode,
3476
3486
  resetConfig: () => resetConfig,
3477
3487
  resetDebugRuntimeConfig: () => resetDebugRuntimeConfig,
3478
3488
  resetState: () => resetState,
@@ -3495,11 +3505,24 @@ __export(index_exports, {
3495
3505
  spawnDetachedDaemonUpgradeHelper: () => spawnDetachedDaemonUpgradeHelper,
3496
3506
  startDaemonDevSupport: () => startDaemonDevSupport,
3497
3507
  summarizeGitStatus: () => summarizeGitStatus,
3508
+ syncMeshes: () => syncMeshes,
3498
3509
  updateConfig: () => updateConfig,
3510
+ updateMesh: () => updateMesh,
3511
+ updateNode: () => updateNode,
3499
3512
  upsertSavedProviderSession: () => upsertSavedProviderSession
3500
3513
  });
3501
3514
  module.exports = __toCommonJS(index_exports);
3502
3515
 
3516
+ // src/repo-mesh-types.ts
3517
+ var DEFAULT_MESH_POLICY = {
3518
+ requirePreTaskCheckpoint: false,
3519
+ requirePostTaskCheckpoint: true,
3520
+ requireApprovalForPush: true,
3521
+ requireApprovalForDestructiveGit: true,
3522
+ dirtyWorkspaceBehavior: "warn",
3523
+ maxParallelTasks: 2
3524
+ };
3525
+
3503
3526
  // src/git/git-executor.ts
3504
3527
  var import_node_child_process = require("child_process");
3505
3528
  var import_node_fs = require("fs");
@@ -5297,9 +5320,304 @@ function getSavedProviderSessions(state, filters) {
5297
5320
  })).sort((a, b) => b.lastUsedAt - a.lastUsedAt);
5298
5321
  }
5299
5322
 
5300
- // src/config/state-store.ts
5323
+ // src/config/mesh-config.ts
5301
5324
  var import_fs2 = require("fs");
5302
5325
  var import_path2 = require("path");
5326
+ var import_crypto3 = require("crypto");
5327
+ init_config();
5328
+ function getMeshConfigPath() {
5329
+ return (0, import_path2.join)(getConfigDir(), "meshes.json");
5330
+ }
5331
+ function loadMeshConfig() {
5332
+ const path26 = getMeshConfigPath();
5333
+ if (!(0, import_fs2.existsSync)(path26)) return { meshes: [] };
5334
+ try {
5335
+ const raw = JSON.parse((0, import_fs2.readFileSync)(path26, "utf-8"));
5336
+ if (!raw || !Array.isArray(raw.meshes)) return { meshes: [] };
5337
+ return raw;
5338
+ } catch {
5339
+ return { meshes: [] };
5340
+ }
5341
+ }
5342
+ function saveMeshConfig(config) {
5343
+ const path26 = getMeshConfigPath();
5344
+ (0, import_fs2.writeFileSync)(path26, JSON.stringify(config, null, 2), { encoding: "utf-8", mode: 384 });
5345
+ }
5346
+ function normalizeRepoIdentity(remoteUrl) {
5347
+ let identity = remoteUrl.trim();
5348
+ if (identity.startsWith("http://") || identity.startsWith("https://")) {
5349
+ try {
5350
+ const url = new URL(identity);
5351
+ const path26 = url.pathname.replace(/^\//, "").replace(/\.git$/, "");
5352
+ return `${url.hostname}/${path26}`;
5353
+ } catch {
5354
+ }
5355
+ }
5356
+ const sshMatch = identity.match(/^(?:ssh:\/\/)?[\w.-]+@([\w.-]+)[:/]([\w.\-/]+?)(?:\.git)?$/);
5357
+ if (sshMatch) return `${sshMatch[1]}/${sshMatch[2]}`;
5358
+ return identity;
5359
+ }
5360
+ function listMeshes() {
5361
+ return loadMeshConfig().meshes;
5362
+ }
5363
+ function getMesh(meshId) {
5364
+ return loadMeshConfig().meshes.find((m) => m.id === meshId);
5365
+ }
5366
+ function getMeshByRepo(repoIdentity) {
5367
+ return loadMeshConfig().meshes.find((m) => m.repoIdentity === repoIdentity);
5368
+ }
5369
+ function createMesh(opts) {
5370
+ const config = loadMeshConfig();
5371
+ if (config.meshes.length >= 20) {
5372
+ throw new Error("Maximum 20 meshes allowed");
5373
+ }
5374
+ const repoIdentity = opts.repoIdentity || (opts.repoRemoteUrl ? normalizeRepoIdentity(opts.repoRemoteUrl) : "");
5375
+ if (!repoIdentity) throw new Error("Either repoRemoteUrl or repoIdentity is required");
5376
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5377
+ const mesh = {
5378
+ id: `mesh_${(0, import_crypto3.randomUUID)().replace(/-/g, "")}`,
5379
+ name: opts.name.trim().slice(0, 100),
5380
+ repoIdentity,
5381
+ repoRemoteUrl: opts.repoRemoteUrl,
5382
+ defaultBranch: opts.defaultBranch,
5383
+ policy: { ...DEFAULT_MESH_POLICY, ...opts.policy },
5384
+ coordinator: opts.coordinator || {},
5385
+ nodes: [],
5386
+ createdAt: now,
5387
+ updatedAt: now
5388
+ };
5389
+ config.meshes.push(mesh);
5390
+ saveMeshConfig(config);
5391
+ return mesh;
5392
+ }
5393
+ function updateMesh(meshId, opts) {
5394
+ const config = loadMeshConfig();
5395
+ const mesh = config.meshes.find((m) => m.id === meshId);
5396
+ if (!mesh) return void 0;
5397
+ if (opts.name !== void 0) mesh.name = opts.name.trim().slice(0, 100);
5398
+ if (opts.defaultBranch !== void 0) mesh.defaultBranch = opts.defaultBranch;
5399
+ if (opts.policy) mesh.policy = { ...mesh.policy, ...opts.policy };
5400
+ if (opts.coordinator) mesh.coordinator = opts.coordinator;
5401
+ mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
5402
+ saveMeshConfig(config);
5403
+ return mesh;
5404
+ }
5405
+ function deleteMesh(meshId) {
5406
+ const config = loadMeshConfig();
5407
+ const idx = config.meshes.findIndex((m) => m.id === meshId);
5408
+ if (idx === -1) return false;
5409
+ config.meshes.splice(idx, 1);
5410
+ saveMeshConfig(config);
5411
+ return true;
5412
+ }
5413
+ function addNode(meshId, opts) {
5414
+ const config = loadMeshConfig();
5415
+ const mesh = config.meshes.find((m) => m.id === meshId);
5416
+ if (!mesh) return void 0;
5417
+ if (mesh.nodes.length >= 10) {
5418
+ throw new Error("Maximum 10 nodes per mesh");
5419
+ }
5420
+ if (mesh.nodes.some((n) => n.workspace === opts.workspace)) {
5421
+ throw new Error("This workspace is already in the mesh");
5422
+ }
5423
+ const node = {
5424
+ id: `node_${(0, import_crypto3.randomUUID)().replace(/-/g, "")}`,
5425
+ workspace: opts.workspace.trim(),
5426
+ repoRoot: opts.repoRoot,
5427
+ userOverrides: opts.userOverrides || {},
5428
+ policy: opts.policy || {},
5429
+ isLocalWorktree: opts.isLocalWorktree
5430
+ };
5431
+ mesh.nodes.push(node);
5432
+ mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
5433
+ saveMeshConfig(config);
5434
+ return node;
5435
+ }
5436
+ function removeNode(meshId, nodeId) {
5437
+ const config = loadMeshConfig();
5438
+ const mesh = config.meshes.find((m) => m.id === meshId);
5439
+ if (!mesh) return false;
5440
+ const idx = mesh.nodes.findIndex((n) => n.id === nodeId);
5441
+ if (idx === -1) return false;
5442
+ mesh.nodes.splice(idx, 1);
5443
+ mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
5444
+ saveMeshConfig(config);
5445
+ return true;
5446
+ }
5447
+ function updateNode(meshId, nodeId, opts) {
5448
+ const config = loadMeshConfig();
5449
+ const mesh = config.meshes.find((m) => m.id === meshId);
5450
+ if (!mesh) return void 0;
5451
+ const node = mesh.nodes.find((n) => n.id === nodeId);
5452
+ if (!node) return void 0;
5453
+ if (opts.userOverrides) node.userOverrides = { ...node.userOverrides, ...opts.userOverrides };
5454
+ if (opts.policy) node.policy = { ...node.policy, ...opts.policy };
5455
+ mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
5456
+ saveMeshConfig(config);
5457
+ return node;
5458
+ }
5459
+
5460
+ // src/mesh/coordinator-prompt.ts
5461
+ function buildCoordinatorSystemPrompt(ctx) {
5462
+ const { mesh, status, userInstruction } = ctx;
5463
+ const sections = [];
5464
+ sections.push(`You are a **Repo Mesh Coordinator** \u2014 a technical team lead who orchestrates work across multiple agent sessions on a shared Git repository.
5465
+
5466
+ Your mesh: **${mesh.name}**
5467
+ Repository: \`${mesh.repoIdentity}\`${mesh.defaultBranch ? `
5468
+ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
5469
+ if (status?.nodes?.length) {
5470
+ sections.push(buildNodeStatusSection(status.nodes));
5471
+ } else if (mesh.nodes.length) {
5472
+ sections.push(buildNodeConfigSection(mesh));
5473
+ } else {
5474
+ sections.push("## Nodes\nNo nodes configured yet. Ask the user to add nodes with `adhdev mesh add-node`.");
5475
+ }
5476
+ sections.push(buildPolicySection(mesh.policy));
5477
+ sections.push(TOOLS_SECTION);
5478
+ sections.push(WORKFLOW_SECTION);
5479
+ sections.push(RULES_SECTION);
5480
+ if (userInstruction) {
5481
+ sections.push(`## Additional Context
5482
+ ${userInstruction}`);
5483
+ }
5484
+ if (mesh.coordinator.systemPromptSuffix) {
5485
+ sections.push(mesh.coordinator.systemPromptSuffix);
5486
+ }
5487
+ return sections.join("\n\n");
5488
+ }
5489
+ function buildNodeStatusSection(nodes) {
5490
+ const lines = ["## Current Node Status", ""];
5491
+ for (const n of nodes) {
5492
+ const healthIcon = n.health === "online" ? "\u{1F7E2}" : n.health === "dirty" ? "\u{1F7E1}" : n.health === "offline" ? "\u26AB" : "\u{1F534}";
5493
+ const sessions = n.activeSessions.length > 0 ? `sessions: ${n.activeSessions.join(", ")}` : "no active sessions";
5494
+ const branch = n.git?.branch ? `branch: \`${n.git.branch}\`` : "";
5495
+ lines.push(`- ${healthIcon} **${n.machineLabel}** (${n.nodeId})`);
5496
+ lines.push(` workspace: \`${n.workspace}\` | ${branch} | ${sessions}`);
5497
+ if (n.error) lines.push(` \u26A0\uFE0F ${n.error}`);
5498
+ }
5499
+ return lines.join("\n");
5500
+ }
5501
+ function buildNodeConfigSection(mesh) {
5502
+ const lines = ["## Configured Nodes", ""];
5503
+ for (const n of mesh.nodes) {
5504
+ const labels = [];
5505
+ if (n.isLocalWorktree) labels.push("worktree");
5506
+ if (n.policy.readOnly) labels.push("read-only");
5507
+ const suffix = labels.length ? ` [${labels.join(", ")}]` : "";
5508
+ lines.push(`- **${n.workspace}** (${n.id})${suffix}`);
5509
+ }
5510
+ lines.push("", "_Use `mesh_status` to probe live health before delegating work._");
5511
+ return lines.join("\n");
5512
+ }
5513
+ function buildPolicySection(policy) {
5514
+ const rules = [];
5515
+ if (policy.requirePreTaskCheckpoint) rules.push("- Create a git checkpoint **before** starting each task");
5516
+ if (policy.requirePostTaskCheckpoint) rules.push("- Create a git checkpoint **after** each task completes");
5517
+ if (policy.requireApprovalForPush) rules.push("- **Ask for user approval** before pushing to remote");
5518
+ if (policy.requireApprovalForDestructiveGit) rules.push("- **Ask for user approval** before destructive git operations (force push, reset, etc.)");
5519
+ const dirtyBehavior = {
5520
+ block: "- **Do not** send tasks to nodes with dirty workspaces",
5521
+ warn: "- Warn the user if a node has uncommitted changes before sending a task",
5522
+ checkpoint_then_continue: "- Auto-checkpoint dirty nodes before sending tasks"
5523
+ }[policy.dirtyWorkspaceBehavior] || "";
5524
+ if (dirtyBehavior) rules.push(dirtyBehavior);
5525
+ rules.push(`- Maximum **${policy.maxParallelTasks}** tasks running in parallel`);
5526
+ return `## Policy
5527
+ ${rules.join("\n")}`;
5528
+ }
5529
+ var TOOLS_SECTION = `## Available Tools
5530
+
5531
+ | Tool | Purpose |
5532
+ |------|---------|
5533
+ | \`mesh_status\` | Check all nodes' health, git state, and active sessions |
5534
+ | \`mesh_list_nodes\` | List nodes with workspace paths |
5535
+ | \`mesh_launch_session\` | Start a new agent session on a node |
5536
+ | \`mesh_send_task\` | Send a task (natural language) to a running agent |
5537
+ | \`mesh_read_chat\` | Read an agent's recent messages to check progress |
5538
+ | \`mesh_git_status\` | Check git status on a specific node |
5539
+ | \`mesh_checkpoint\` | Create a git checkpoint on a node |
5540
+ | \`mesh_approve\` | Approve/reject a pending agent action |`;
5541
+ var WORKFLOW_SECTION = `## Orchestration Workflow
5542
+
5543
+ 1. **Assess** \u2014 Call \`mesh_status\` to see which nodes are healthy and available.
5544
+ 2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist.
5545
+ 3. **Delegate** \u2014 For each task:
5546
+ a. Pick the best node (consider: health, dirty state, current workload).
5547
+ b. If no session exists, call \`mesh_launch_session\` to start one.
5548
+ c. Call \`mesh_send_task\` with a clear, self-contained natural-language instruction.
5549
+ 4. **Monitor** \u2014 Periodically call \`mesh_read_chat\` to check progress. Handle approvals via \`mesh_approve\`.
5550
+ 5. **Verify** \u2014 When a task reports completion, call \`mesh_git_status\` to verify changes were made.
5551
+ 6. **Checkpoint** \u2014 Call \`mesh_checkpoint\` to save the work.
5552
+ 7. **Report** \u2014 Summarize what was done, what changed, and any issues.`;
5553
+ var RULES_SECTION = `## Rules
5554
+
5555
+ - **Be conversational.** Delegate work the way a tech lead would \u2014 clear, specific instructions in natural language.
5556
+ - **Don't inspect code.** Trust the agent's output. Verify via git diff/status, not by reading source files.
5557
+ - **Don't over-parallelize.** Start with 1-2 concurrent tasks. Scale up if they succeed.
5558
+ - **Handle failures gracefully.** If a task fails, read the chat to understand why, then retry or reassign.
5559
+ - **Keep the user informed.** Report progress after each delegation round.
5560
+ - **Respect node capabilities.** Don't send build tasks to read-only nodes. Don't push from nodes that aren't allowed to.
5561
+ - **Never fabricate tool results.** Always call the actual tool; never pretend you did.`;
5562
+
5563
+ // src/mesh/mesh-sync.ts
5564
+ async function syncMeshes(transport) {
5565
+ const result = { pushed: 0, pulled: 0, deleted: 0, errors: [] };
5566
+ let remoteMeshes;
5567
+ try {
5568
+ const res = await transport.listRemoteMeshes();
5569
+ remoteMeshes = res.meshes;
5570
+ } catch (e) {
5571
+ result.errors.push(`Failed to list remote meshes: ${e.message}`);
5572
+ return result;
5573
+ }
5574
+ const localMeshes = listMeshes();
5575
+ const remoteByIdentity = new Map(remoteMeshes.map((m) => [m.repo_identity, m]));
5576
+ const localByIdentity = new Map(localMeshes.map((m) => [m.repoIdentity, m]));
5577
+ for (const local of localMeshes) {
5578
+ if (!remoteByIdentity.has(local.repoIdentity)) {
5579
+ try {
5580
+ await transport.createRemoteMesh({
5581
+ name: local.name,
5582
+ repo_identity: local.repoIdentity,
5583
+ repo_remote_url: local.repoRemoteUrl,
5584
+ default_branch: local.defaultBranch,
5585
+ policy: JSON.stringify(local.policy)
5586
+ });
5587
+ result.pushed++;
5588
+ } catch (e) {
5589
+ result.errors.push(`Push failed for "${local.name}": ${e.message}`);
5590
+ }
5591
+ }
5592
+ }
5593
+ for (const remote of remoteMeshes) {
5594
+ if (!localByIdentity.has(remote.repo_identity)) {
5595
+ try {
5596
+ let policy;
5597
+ try {
5598
+ policy = JSON.parse(remote.policy);
5599
+ } catch {
5600
+ policy = void 0;
5601
+ }
5602
+ createMesh({
5603
+ name: remote.name,
5604
+ repoIdentity: remote.repo_identity,
5605
+ repoRemoteUrl: remote.repo_remote_url || void 0,
5606
+ defaultBranch: remote.default_branch || void 0,
5607
+ policy
5608
+ });
5609
+ result.pulled++;
5610
+ } catch (e) {
5611
+ result.errors.push(`Pull failed for "${remote.name}": ${e.message}`);
5612
+ }
5613
+ }
5614
+ }
5615
+ return result;
5616
+ }
5617
+
5618
+ // src/config/state-store.ts
5619
+ var import_fs3 = require("fs");
5620
+ var import_path3 = require("path");
5303
5621
  init_config();
5304
5622
  var DEFAULT_STATE = {
5305
5623
  recentActivity: [],
@@ -5313,7 +5631,7 @@ function isPlainObject2(value) {
5313
5631
  return !!value && typeof value === "object" && !Array.isArray(value);
5314
5632
  }
5315
5633
  function getStatePath() {
5316
- return (0, import_path2.join)(getConfigDir(), "state.json");
5634
+ return (0, import_path3.join)(getConfigDir(), "state.json");
5317
5635
  }
5318
5636
  function normalizeState(raw) {
5319
5637
  const parsed = isPlainObject2(raw) ? raw : {};
@@ -5349,11 +5667,11 @@ function normalizeState(raw) {
5349
5667
  }
5350
5668
  function loadState() {
5351
5669
  const statePath = getStatePath();
5352
- if (!(0, import_fs2.existsSync)(statePath)) {
5670
+ if (!(0, import_fs3.existsSync)(statePath)) {
5353
5671
  return { ...DEFAULT_STATE };
5354
5672
  }
5355
5673
  try {
5356
- const raw = (0, import_fs2.readFileSync)(statePath, "utf-8");
5674
+ const raw = (0, import_fs3.readFileSync)(statePath, "utf-8");
5357
5675
  return normalizeState(JSON.parse(raw));
5358
5676
  } catch {
5359
5677
  return { ...DEFAULT_STATE };
@@ -5362,7 +5680,7 @@ function loadState() {
5362
5680
  function saveState(state) {
5363
5681
  const statePath = getStatePath();
5364
5682
  const normalized = normalizeState(state);
5365
- (0, import_fs2.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
5683
+ (0, import_fs3.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
5366
5684
  }
5367
5685
  function resetState() {
5368
5686
  saveState({ ...DEFAULT_STATE });
@@ -5370,7 +5688,7 @@ function resetState() {
5370
5688
 
5371
5689
  // src/detection/ide-detector.ts
5372
5690
  var import_child_process = require("child_process");
5373
- var import_fs3 = require("fs");
5691
+ var import_fs4 = require("fs");
5374
5692
  var import_os2 = require("os");
5375
5693
  var path7 = __toESM(require("path"));
5376
5694
  var BUILTIN_IDE_DEFINITIONS = [];
@@ -5394,7 +5712,7 @@ function findCliCommand(command) {
5394
5712
  if (path7.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
5395
5713
  const candidate = trimmed.startsWith("~") ? path7.join((0, import_os2.homedir)(), trimmed.slice(1)) : trimmed;
5396
5714
  const resolved = path7.isAbsolute(candidate) ? candidate : path7.resolve(candidate);
5397
- return (0, import_fs3.existsSync)(resolved) ? resolved : null;
5715
+ return (0, import_fs4.existsSync)(resolved) ? resolved : null;
5398
5716
  }
5399
5717
  try {
5400
5718
  const result = (0, import_child_process.execSync)(
@@ -5425,9 +5743,9 @@ function checkPathExists(paths) {
5425
5743
  if (normalized.includes("*")) {
5426
5744
  const username = home.split(/[\\/]/).pop() || "";
5427
5745
  const resolved = normalized.replace("*", username);
5428
- if ((0, import_fs3.existsSync)(resolved)) return resolved;
5746
+ if ((0, import_fs4.existsSync)(resolved)) return resolved;
5429
5747
  } else {
5430
- if ((0, import_fs3.existsSync)(normalized)) return normalized;
5748
+ if ((0, import_fs4.existsSync)(normalized)) return normalized;
5431
5749
  }
5432
5750
  }
5433
5751
  return null;
@@ -5441,7 +5759,7 @@ async function detectIDEs(providerLoader) {
5441
5759
  let resolvedCli = cliPath;
5442
5760
  if (!resolvedCli && appPath && os21 === "darwin") {
5443
5761
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
5444
- if ((0, import_fs3.existsSync)(bundledCli)) resolvedCli = bundledCli;
5762
+ if ((0, import_fs4.existsSync)(bundledCli)) resolvedCli = bundledCli;
5445
5763
  }
5446
5764
  if (!resolvedCli && appPath && os21 === "win32") {
5447
5765
  const { dirname: dirname7 } = await import("path");
@@ -5454,7 +5772,7 @@ async function detectIDEs(providerLoader) {
5454
5772
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
5455
5773
  ];
5456
5774
  for (const c of candidates) {
5457
- if ((0, import_fs3.existsSync)(c)) {
5775
+ if ((0, import_fs4.existsSync)(c)) {
5458
5776
  resolvedCli = c;
5459
5777
  break;
5460
5778
  }
@@ -5480,7 +5798,7 @@ async function detectIDEs(providerLoader) {
5480
5798
  var import_child_process2 = require("child_process");
5481
5799
  var os2 = __toESM(require("os"));
5482
5800
  var path8 = __toESM(require("path"));
5483
- var import_fs4 = require("fs");
5801
+ var import_fs5 = require("fs");
5484
5802
  function parseVersion(raw) {
5485
5803
  const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
5486
5804
  return match ? match[1] : raw.split("\n")[0].slice(0, 100);
@@ -5504,7 +5822,7 @@ function resolveCommandPath(command) {
5504
5822
  if (isExplicitCommandPath(trimmed)) {
5505
5823
  const expanded = expandHome(trimmed);
5506
5824
  const candidate = path8.isAbsolute(expanded) ? expanded : path8.resolve(expanded);
5507
- return (0, import_fs4.existsSync)(candidate) ? candidate : null;
5825
+ return (0, import_fs5.existsSync)(candidate) ? candidate : null;
5508
5826
  }
5509
5827
  return null;
5510
5828
  }
@@ -14242,7 +14560,7 @@ var DaemonCommandHandler = class {
14242
14560
  var os13 = __toESM(require("os"));
14243
14561
  var path16 = __toESM(require("path"));
14244
14562
  var crypto4 = __toESM(require("crypto"));
14245
- var import_fs5 = require("fs");
14563
+ var import_fs6 = require("fs");
14246
14564
  var import_child_process6 = require("child_process");
14247
14565
  var import_chalk = __toESM(require("chalk"));
14248
14566
  init_provider_cli_adapter();
@@ -16380,7 +16698,7 @@ function commandExists(command) {
16380
16698
  const trimmed = command.trim();
16381
16699
  if (!trimmed) return false;
16382
16700
  if (isExplicitCommand(trimmed)) {
16383
- return (0, import_fs5.existsSync)(expandExecutable(trimmed));
16701
+ return (0, import_fs6.existsSync)(expandExecutable(trimmed));
16384
16702
  }
16385
16703
  try {
16386
16704
  (0, import_child_process6.execFileSync)(process.platform === "win32" ? "where" : "which", [trimmed], {
@@ -28854,6 +29172,7 @@ async function shutdownDaemonComponents(components) {
28854
29172
  DEFAULT_DAEMON_PORT,
28855
29173
  DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
28856
29174
  DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
29175
+ DEFAULT_MESH_POLICY,
28857
29176
  DEFAULT_SESSION_HOST_APP_NAME,
28858
29177
  DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
28859
29178
  DEFAULT_SESSION_HOST_READY_TIMEOUT_MS,
@@ -28887,11 +29206,13 @@ async function shutdownDaemonComponents(components) {
28887
29206
  SessionHostPtyTransportFactory,
28888
29207
  TurnSnapshotTracker,
28889
29208
  VersionArchive,
29209
+ addNode,
28890
29210
  appendRecentActivity,
28891
29211
  buildAssistantChatMessage,
28892
29212
  buildChatMessage,
28893
29213
  buildChatMessageSignature,
28894
29214
  buildChatTailDeliverySignature,
29215
+ buildCoordinatorSystemPrompt,
28895
29216
  buildMachineInfo,
28896
29217
  buildPinnedGlobalInstallCommand,
28897
29218
  buildRuntimeSystemChatMessage,
@@ -28914,6 +29235,8 @@ async function shutdownDaemonComponents(components) {
28914
29235
  createGitSnapshotStore,
28915
29236
  createGitWorkspaceMonitor,
28916
29237
  createInteractionId,
29238
+ createMesh,
29239
+ deleteMesh,
28917
29240
  detectAllVersions,
28918
29241
  detectCLIs,
28919
29242
  detectIDEs,
@@ -28932,6 +29255,8 @@ async function shutdownDaemonComponents(components) {
28932
29255
  getGitRepoStatus,
28933
29256
  getHostMemorySnapshot,
28934
29257
  getLogLevel,
29258
+ getMesh,
29259
+ getMeshByRepo,
28935
29260
  getNpmExecOptions,
28936
29261
  getRecentActivity,
28937
29262
  getRecentCommands,
@@ -28962,6 +29287,7 @@ async function shutdownDaemonComponents(components) {
28962
29287
  launchIDE,
28963
29288
  launchWithCdp,
28964
29289
  listHostedCliRuntimes,
29290
+ listMeshes,
28965
29291
  loadConfig,
28966
29292
  loadState,
28967
29293
  logCommand,
@@ -28977,6 +29303,7 @@ async function shutdownDaemonComponents(components) {
28977
29303
  normalizeInputEnvelope,
28978
29304
  normalizeManagedStatus,
28979
29305
  normalizeMessageParts,
29306
+ normalizeRepoIdentity,
28980
29307
  normalizeSessionModalFields,
28981
29308
  parsePorcelainV2Status,
28982
29309
  parseProviderSourceConfigUpdate,
@@ -28988,6 +29315,7 @@ async function shutdownDaemonComponents(components) {
28988
29315
  readChatHistory,
28989
29316
  recordDebugTrace,
28990
29317
  registerExtensionProviders,
29318
+ removeNode,
28991
29319
  resetConfig,
28992
29320
  resetDebugRuntimeConfig,
28993
29321
  resetState,
@@ -29010,7 +29338,10 @@ async function shutdownDaemonComponents(components) {
29010
29338
  spawnDetachedDaemonUpgradeHelper,
29011
29339
  startDaemonDevSupport,
29012
29340
  summarizeGitStatus,
29341
+ syncMeshes,
29013
29342
  updateConfig,
29343
+ updateMesh,
29344
+ updateNode,
29014
29345
  upsertSavedProviderSession
29015
29346
  });
29016
29347
  //# sourceMappingURL=index.js.map