@adhdev/daemon-core 0.9.82-rc.2 → 0.9.82-rc.200

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.
Files changed (244) hide show
  1. package/dist/boot/daemon-lifecycle.d.ts +3 -0
  2. package/dist/boot/process-hardening.d.ts +50 -0
  3. package/dist/chat/source-machine.d.ts +166 -0
  4. package/dist/chat/source-resolver.d.ts +104 -0
  5. package/dist/chat/subscription-updates.d.ts +1 -0
  6. package/dist/cli-adapter-types.d.ts +23 -2
  7. package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
  8. package/dist/cli-adapters/cli-state-engine.d.ts +178 -0
  9. package/dist/cli-adapters/provider-cli-adapter.d.ts +106 -63
  10. package/dist/cli-adapters/provider-cli-parse.d.ts +4 -0
  11. package/dist/cli-adapters/provider-cli-shared.d.ts +29 -0
  12. package/dist/cli-adapters/raw-terminal-io.d.ts +37 -0
  13. package/dist/commands/cli-manager.d.ts +2 -1
  14. package/dist/commands/handler.d.ts +110 -0
  15. package/dist/commands/mesh-coordinator.d.ts +85 -1
  16. package/dist/commands/router.d.ts +29 -1
  17. package/dist/config/chat-history.d.ts +9 -0
  18. package/dist/config/config.d.ts +5 -0
  19. package/dist/config/mesh-config.d.ts +72 -1
  20. package/dist/git/git-commands.d.ts +8 -1
  21. package/dist/git/git-status.d.ts +5 -0
  22. package/dist/git/git-types.d.ts +12 -0
  23. package/dist/index.d.ts +39 -10
  24. package/dist/index.js +29425 -12026
  25. package/dist/index.js.map +1 -1
  26. package/dist/index.mjs +25700 -8378
  27. package/dist/index.mjs.map +1 -1
  28. package/dist/installer.d.ts +1 -4
  29. package/dist/ipc/local-ipc-server.d.ts +91 -0
  30. package/dist/launch.d.ts +1 -1
  31. package/dist/logging/async-batch-writer.d.ts +10 -0
  32. package/dist/mesh/contracts.d.ts +164 -0
  33. package/dist/mesh/coordinator-prompt.d.ts +30 -0
  34. package/dist/mesh/coordinator-registry.d.ts +59 -0
  35. package/dist/mesh/mesh-active-work.d.ts +90 -0
  36. package/dist/mesh/mesh-delivery-policy.d.ts +126 -0
  37. package/dist/mesh/mesh-events.d.ts +167 -6
  38. package/dist/mesh/mesh-fast-forward.d.ts +41 -0
  39. package/dist/mesh/mesh-host-ownership.d.ts +9 -0
  40. package/dist/mesh/mesh-ledger.d.ts +58 -1
  41. package/dist/mesh/mesh-refine-status.d.ts +26 -0
  42. package/dist/mesh/mesh-runtime-store.d.ts +149 -0
  43. package/dist/mesh/mesh-work-queue.d.ts +55 -6
  44. package/dist/mesh/preview-freshness.d.ts +18 -0
  45. package/dist/mesh/refine-config.d.ts +193 -0
  46. package/dist/mesh/worktree-bootstrap-config.d.ts +113 -0
  47. package/dist/providers/approval-utils.d.ts +13 -0
  48. package/dist/providers/chat-message-normalization.d.ts +1 -0
  49. package/dist/providers/cli-provider-instance.d.ts +39 -3
  50. package/dist/providers/contracts.d.ts +130 -6
  51. package/dist/providers/external-sources.d.ts +71 -0
  52. package/dist/providers/native-history/antigravity-cli-transcript.d.ts +100 -0
  53. package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
  54. package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
  55. package/dist/providers/native-history/dispatcher.d.ts +27 -0
  56. package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
  57. package/dist/providers/native-history/index.d.ts +13 -0
  58. package/dist/providers/provider-instance-manager.d.ts +12 -0
  59. package/dist/providers/provider-instance.d.ts +13 -1
  60. package/dist/providers/provider-loader.d.ts +26 -4
  61. package/dist/providers/provider-trust.d.ts +31 -0
  62. package/dist/providers/read-chat-contract.d.ts +29 -0
  63. package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
  64. package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +85 -0
  65. package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
  66. package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +65 -0
  67. package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
  68. package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
  69. package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
  70. package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
  71. package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
  72. package/dist/providers/sdk/v1/index.d.ts +30 -0
  73. package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
  74. package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
  75. package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
  76. package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
  77. package/dist/providers/sdk/v1/types/common/index.d.ts +203 -0
  78. package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
  79. package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
  80. package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
  81. package/dist/providers/spec/adapter.d.ts +60 -0
  82. package/dist/providers/spec/cli-adapter.d.ts +98 -0
  83. package/dist/providers/spec/driver.d.ts +190 -0
  84. package/dist/providers/spec/evaluator.d.ts +55 -0
  85. package/dist/providers/spec/loader.d.ts +14 -0
  86. package/dist/providers/spec/native-history-executor.d.ts +41 -0
  87. package/dist/providers/spec/route.d.ts +4 -0
  88. package/dist/providers/spec/schema.gen.d.ts +599 -0
  89. package/dist/providers/spec/types.d.ts +282 -0
  90. package/dist/providers/transcript-v2.d.ts +176 -0
  91. package/dist/providers/types/interactive-prompt.d.ts +48 -0
  92. package/dist/repo-mesh-types.d.ts +230 -1
  93. package/dist/sessions/registry.d.ts +3 -0
  94. package/dist/shared-types-extra.d.ts +1 -1
  95. package/dist/shared-types.d.ts +42 -1
  96. package/dist/status/normalize.d.ts +1 -1
  97. package/dist/status/normalize.js +1 -0
  98. package/dist/status/normalize.js.map +1 -1
  99. package/dist/status/normalize.mjs +1 -0
  100. package/dist/status/normalize.mjs.map +1 -1
  101. package/dist/status/reporter.d.ts +2 -0
  102. package/dist/status/snapshot.d.ts +1 -0
  103. package/dist/types.d.ts +5 -0
  104. package/package.json +7 -2
  105. package/src/agent-stream/poller.ts +2 -3
  106. package/src/boot/daemon-lifecycle.ts +22 -10
  107. package/src/boot/process-hardening.ts +89 -0
  108. package/src/chat/source-machine.ts +534 -0
  109. package/src/chat/source-resolver.ts +0 -0
  110. package/src/chat/subscription-updates.ts +20 -1
  111. package/src/cli-adapter-types.d.ts +1 -0
  112. package/src/cli-adapter-types.ts +22 -2
  113. package/src/cli-adapters/cli-script-runner.ts +421 -0
  114. package/src/cli-adapters/cli-state-engine.ts +1138 -0
  115. package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
  116. package/src/cli-adapters/provider-cli-adapter.ts +670 -1137
  117. package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
  118. package/src/cli-adapters/provider-cli-parse.ts +13 -0
  119. package/src/cli-adapters/provider-cli-runtime.ts +3 -1
  120. package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
  121. package/src/cli-adapters/provider-cli-shared.ts +59 -11
  122. package/src/cli-adapters/raw-terminal-io.ts +252 -0
  123. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +17 -1
  124. package/src/cli-adapters/terminal-backends/xterm-backend.ts +8 -1
  125. package/src/commands/chat-commands.ts +1787 -60
  126. package/src/commands/cli-manager.ts +283 -14
  127. package/src/commands/handler.ts +809 -2
  128. package/src/commands/mesh-coordinator.ts +331 -122
  129. package/src/commands/router.ts +3915 -546
  130. package/src/config/chat-history.ts +95 -24
  131. package/src/config/config.ts +12 -0
  132. package/src/config/mesh-config.ts +280 -2
  133. package/src/config/recent-activity.ts +8 -2
  134. package/src/daemon/dev-cli-debug.ts +10 -1
  135. package/src/detection/ide-detector.ts +26 -16
  136. package/src/git/git-commands.ts +38 -8
  137. package/src/git/git-status.ts +132 -12
  138. package/src/git/git-summary.ts +3 -0
  139. package/src/git/git-types.ts +13 -0
  140. package/src/git/git-worktree.ts +8 -1
  141. package/src/index.ts +127 -9
  142. package/src/installer.d.ts +1 -1
  143. package/src/installer.ts +8 -6
  144. package/src/ipc/local-ipc-server.ts +278 -0
  145. package/src/launch.d.ts +1 -1
  146. package/src/launch.ts +37 -28
  147. package/src/logging/async-batch-writer.ts +55 -0
  148. package/src/logging/logger.ts +2 -1
  149. package/src/mesh/contracts.ts +329 -0
  150. package/src/mesh/coordinator-prompt.ts +204 -30
  151. package/src/mesh/coordinator-registry.ts +121 -0
  152. package/src/mesh/mesh-active-work.ts +437 -0
  153. package/src/mesh/mesh-delivery-policy.ts +298 -0
  154. package/src/mesh/mesh-events.ts +1352 -109
  155. package/src/mesh/mesh-fast-forward.ts +438 -0
  156. package/src/mesh/mesh-host-ownership.ts +73 -0
  157. package/src/mesh/mesh-ledger.ts +457 -104
  158. package/src/mesh/mesh-refine-status.ts +144 -0
  159. package/src/mesh/mesh-runtime-store.ts +769 -0
  160. package/src/mesh/mesh-work-queue.ts +267 -159
  161. package/src/mesh/preview-freshness.ts +118 -0
  162. package/src/mesh/refine-config.ts +366 -0
  163. package/src/mesh/worktree-bootstrap-config.ts +247 -0
  164. package/src/providers/approval-utils.d.ts +4 -0
  165. package/src/providers/approval-utils.ts +47 -5
  166. package/src/providers/chat-message-normalization.ts +7 -12
  167. package/src/providers/cli-provider-instance.ts +806 -64
  168. package/src/providers/contracts.d.ts +55 -0
  169. package/src/providers/contracts.ts +141 -6
  170. package/src/providers/external-sources.ts +218 -0
  171. package/src/providers/ide-provider-instance.ts +19 -5
  172. package/src/providers/native-history/antigravity-cli-transcript.ts +643 -0
  173. package/src/providers/native-history/claude-cli-transcript.ts +396 -0
  174. package/src/providers/native-history/codex-cli-transcript.ts +419 -0
  175. package/src/providers/native-history/dispatcher.ts +340 -0
  176. package/src/providers/native-history/hermes-cli-transcript.ts +230 -0
  177. package/src/providers/native-history/index.ts +30 -0
  178. package/src/providers/provider-instance-manager.ts +30 -0
  179. package/src/providers/provider-instance.ts +10 -1
  180. package/src/providers/provider-loader.ts +582 -50
  181. package/src/providers/provider-schema.ts +87 -14
  182. package/src/providers/provider-trust.ts +114 -0
  183. package/src/providers/read-chat-contract.ts +76 -16
  184. package/src/providers/sdk/README.md +49 -0
  185. package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
  186. package/src/providers/sdk/v1/builders/cli/detect-status.ts +262 -0
  187. package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
  188. package/src/providers/sdk/v1/builders/cli/parse-approval.ts +250 -0
  189. package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
  190. package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
  191. package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
  192. package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
  193. package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
  194. package/src/providers/sdk/v1/index.ts +152 -0
  195. package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
  196. package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
  197. package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
  198. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +504 -0
  199. package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
  200. package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
  201. package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
  202. package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
  203. package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
  204. package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
  205. package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
  206. package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
  207. package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +125 -0
  208. package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
  209. package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
  210. package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
  211. package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
  212. package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
  213. package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
  214. package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
  215. package/src/providers/sdk/v1/types/cli/index.ts +365 -0
  216. package/src/providers/sdk/v1/types/common/index.ts +229 -0
  217. package/src/providers/sdk/v1/validators/index.ts +19 -0
  218. package/src/providers/sdk/v1/validators/manifest.ts +110 -0
  219. package/src/providers/sdk/v1/validators/taint.ts +309 -0
  220. package/src/providers/spec/adapter.ts +176 -0
  221. package/src/providers/spec/cli-adapter.ts +538 -0
  222. package/src/providers/spec/driver.ts +725 -0
  223. package/src/providers/spec/evaluator.ts +373 -0
  224. package/src/providers/spec/loader.ts +130 -0
  225. package/src/providers/spec/native-history-executor.ts +939 -0
  226. package/src/providers/spec/route.ts +51 -0
  227. package/src/providers/spec/schema.gen.ts +559 -0
  228. package/src/providers/spec/schema.json +237 -0
  229. package/src/providers/spec/types.ts +301 -0
  230. package/src/providers/transcript-v2.ts +567 -0
  231. package/src/providers/types/interactive-prompt.ts +425 -0
  232. package/src/providers/version-archive.ts +38 -20
  233. package/src/repo-mesh-types.ts +250 -1
  234. package/src/sessions/registry.ts +3 -0
  235. package/src/shared-types-extra.ts +1 -1
  236. package/src/shared-types.ts +45 -1
  237. package/src/status/builders.ts +24 -6
  238. package/src/status/normalize.ts +2 -0
  239. package/src/status/reporter.ts +15 -0
  240. package/src/status/snapshot.ts +84 -25
  241. package/src/system/host-memory.ts +29 -12
  242. package/src/types.ts +5 -0
  243. package/dist/mesh/mesh-sync.d.ts +0 -53
  244. package/src/mesh/mesh-sync.ts +0 -111
@@ -8,8 +8,23 @@
8
8
  * 3. How to orchestrate work across nodes
9
9
  *
10
10
  * The prompt is generated dynamically from the current mesh state.
11
+ *
12
+ * User customization:
13
+ * ~/.adhdev/coordinator-prompts/<cliType>.md — full override
14
+ * ~/.adhdev/coordinator-prompts/<cliType>.append.md — appended to default
15
+ * ~/.adhdev/coordinator-prompts/default.md — full override (any CLI)
16
+ * ~/.adhdev/coordinator-prompts/default.append.md — appended to default (any CLI)
17
+ *
18
+ * CLI-specific files take precedence over default.* files. The override file
19
+ * still gets the node/policy facts substituted via the same {{placeholders}}
20
+ * the daemon understands; an override that doesn't reference them just gets
21
+ * a static prompt, which is also fine.
11
22
  */
12
23
 
24
+ import * as fs from 'node:fs';
25
+ import * as os from 'node:os';
26
+ import * as path from 'node:path';
27
+
13
28
  import type {
14
29
  LocalMeshEntry,
15
30
  RepoMeshPolicy,
@@ -27,8 +42,68 @@ export interface CoordinatorPromptContext {
27
42
  coordinatorCliType?: string;
28
43
  }
29
44
 
45
+ /**
46
+ * Compose the final coordinator prompt from four layers, in this precedence:
47
+ *
48
+ * 1. Per-launch `extraSystemPrompt` (always appended, as "## Additional
49
+ * Context"). Never wins as a base — it's launch-scope context.
50
+ * 2. Mesh-level append (`mesh.coordinator.systemPromptAppend` or the legacy
51
+ * `systemPromptSuffix`). Stacks after whichever base won.
52
+ * 3. User-file append (`~/.adhdev/coordinator-prompts/<cli>.append.md` or
53
+ * `default.append.md`). Also stacks; same placeholder expansion as the
54
+ * override path.
55
+ * 4. Base prompt, picked in this order:
56
+ * a. `mesh.coordinator.systemPromptOverride` (mesh-level override)
57
+ * b. user-file override (`~/.adhdev/coordinator-prompts/<cli>.md` or
58
+ * `default.md`)
59
+ * c. daemon default (assembled from identity/nodes/policy/tools/…)
60
+ *
61
+ * That layering lets a user customize prompts at three increasing scopes
62
+ * (machine, mesh, single launch) without losing the daemon's stock rules.
63
+ */
30
64
  export function buildCoordinatorSystemPrompt(ctx: CoordinatorPromptContext): string {
31
- const { mesh, status, userInstruction, coordinatorCliType } = ctx;
65
+ const { mesh, userInstruction, coordinatorCliType } = ctx;
66
+
67
+ // ── Pick the base prompt ──
68
+ const meshOverride = mesh.coordinator?.systemPromptOverride?.trim();
69
+ let base: string;
70
+ if (meshOverride) {
71
+ base = expandPromptPlaceholders(meshOverride, ctx);
72
+ } else {
73
+ const userOverride = readUserPromptFile(coordinatorCliType, 'md');
74
+ if (userOverride !== null) {
75
+ base = expandPromptPlaceholders(userOverride, ctx);
76
+ } else {
77
+ base = buildDefaultCoordinatorPrompt(ctx);
78
+ }
79
+ }
80
+
81
+ const sections: string[] = [base];
82
+
83
+ // ── User-level append runs after whichever base won ──
84
+ const userAppend = readUserPromptFile(coordinatorCliType, 'append.md');
85
+ if (userAppend !== null) {
86
+ sections.push(expandPromptPlaceholders(userAppend, ctx));
87
+ }
88
+
89
+ // ── Mesh-level append (prefer the new field, fall back to the legacy alias) ──
90
+ const meshAppend = (mesh.coordinator?.systemPromptAppend
91
+ ?? mesh.coordinator?.systemPromptSuffix)?.trim();
92
+ if (meshAppend) {
93
+ sections.push(expandPromptPlaceholders(meshAppend, ctx));
94
+ }
95
+
96
+ // ── Per-launch context lands last so it's the most recent thing the
97
+ // agent reads. Marked as Additional Context, not a rule update. ──
98
+ if (userInstruction) {
99
+ sections.push(`## Additional Context\n${userInstruction}`);
100
+ }
101
+
102
+ return sections.join('\n\n');
103
+ }
104
+
105
+ function buildDefaultCoordinatorPrompt(ctx: CoordinatorPromptContext): string {
106
+ const { mesh, status, coordinatorCliType } = ctx;
32
107
  const sections: string[] = [];
33
108
 
34
109
  // ── Identity ──
@@ -61,22 +136,88 @@ Repository: \`${mesh.repoIdentity}\`${mesh.defaultBranch ? `\nDefault branch: \`
61
136
  // ── Rules ──
62
137
  sections.push(buildRulesSection(coordinatorCliType));
63
138
 
64
- // ── User instruction ──
65
- if (userInstruction) {
66
- sections.push(`## Additional Context\n${userInstruction}`);
67
- }
139
+ return sections.join('\n\n');
140
+ }
68
141
 
69
- if (mesh.coordinator?.systemPromptSuffix) {
70
- sections.push(mesh.coordinator.systemPromptSuffix);
142
+ /**
143
+ * Look up a user-customization file under ~/.adhdev/coordinator-prompts/.
144
+ *
145
+ * Lookup order:
146
+ * 1. <cliType>.<suffix> — provider-specific
147
+ * 2. default.<suffix> — shared across providers
148
+ *
149
+ * Returns null when neither exists; an empty/whitespace-only file is also
150
+ * treated as "no override" so users can drop in a stub without affecting
151
+ * behavior. Read errors (permission, IO) are swallowed and logged-as-null
152
+ * intentionally: a broken override file should never block coordinator
153
+ * launch, it should just behave as if the file weren't there.
154
+ */
155
+ function readUserPromptFile(cliType: string | undefined, suffix: string): string | null {
156
+ const dir = path.join(os.homedir(), '.adhdev', 'coordinator-prompts');
157
+ const candidates: string[] = [];
158
+ if (cliType) candidates.push(path.join(dir, `${cliType}.${suffix}`));
159
+ candidates.push(path.join(dir, `default.${suffix}`));
160
+ for (const p of candidates) {
161
+ try {
162
+ const text = fs.readFileSync(p, 'utf8');
163
+ if (text.trim()) return text;
164
+ } catch { /* missing file is the common case — keep going */ }
71
165
  }
166
+ return null;
167
+ }
72
168
 
73
- return sections.join('\n\n');
169
+ /**
170
+ * Expand `{{placeholder}}` tokens against current mesh state.
171
+ *
172
+ * Tokens we support today:
173
+ * {{meshName}} — mesh.name
174
+ * {{repo}} — mesh.repoIdentity
175
+ * {{defaultBranch}} — mesh.defaultBranch or empty
176
+ * {{cliType}} — coordinator CLI type or empty
177
+ * {{nodes}} — full node section (status if known, otherwise config)
178
+ * {{policy}} — full policy section
179
+ * {{tools}} — the canonical tools table
180
+ * {{workflow}} — the canonical orchestration workflow
181
+ * {{rules}} — the canonical rules section (with coordinatorNote)
182
+ * {{toolExposurePreflight}} — the MCP-missing preflight reminder
183
+ *
184
+ * Unknown tokens are left as-is — that way typos are obvious in the rendered
185
+ * prompt instead of silently disappearing. Tokens are not recursive: an
186
+ * expanded value's own {{...}} content stays literal.
187
+ */
188
+ function expandPromptPlaceholders(template: string, ctx: CoordinatorPromptContext): string {
189
+ const { mesh, status, coordinatorCliType } = ctx;
190
+ const nodesSection = status?.nodes?.length
191
+ ? buildNodeStatusSection(status.nodes)
192
+ : mesh.nodes.length
193
+ ? buildNodeConfigSection(mesh)
194
+ : '## Nodes\nNo nodes configured yet. Ask the user to add nodes with `adhdev mesh add-node`.';
195
+ const replacements: Record<string, string> = {
196
+ meshName: mesh.name,
197
+ repo: mesh.repoIdentity,
198
+ defaultBranch: mesh.defaultBranch || '',
199
+ cliType: coordinatorCliType || '',
200
+ nodes: nodesSection,
201
+ policy: buildPolicySection({ ...DEFAULT_MESH_POLICY, ...(mesh.policy || {}) }),
202
+ tools: TOOLS_SECTION,
203
+ workflow: WORKFLOW_SECTION,
204
+ rules: buildRulesSection(coordinatorCliType),
205
+ toolExposurePreflight: TOOL_EXPOSURE_PREFLIGHT_SECTION,
206
+ };
207
+ return template.replace(/\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g, (m, key) => {
208
+ return Object.prototype.hasOwnProperty.call(replacements, key) ? replacements[key] : m;
209
+ });
74
210
  }
75
211
 
76
212
  // ─── Section Builders ───────────────────────────
77
213
 
78
214
  function buildNodeStatusSection(nodes: RepoMeshNodeStatus[]): string {
79
- const lines = ['## Current Node Status', ''];
215
+ const lines = [
216
+ '## Current Node Status',
217
+ '',
218
+ 'Node labels are display context, not aliases. Use exact `nodeId` values in mesh tool calls; do not invent shorthand names such as M1/M2 unless they are explicitly configured labels.',
219
+ '',
220
+ ];
80
221
  for (const n of nodes) {
81
222
  const healthIcon = n.health === 'online' ? '🟢' :
82
223
  n.health === 'dirty' ? '🟡' :
@@ -85,31 +226,65 @@ function buildNodeStatusSection(nodes: RepoMeshNodeStatus[]): string {
85
226
  ? `sessions: ${n.activeSessions.join(', ')}`
86
227
  : 'no active sessions';
87
228
  const branch = n.git?.branch ? `branch: \`${n.git.branch}\`` : '';
88
- lines.push(`- ${healthIcon} **${n.machineLabel}** (${n.nodeId})`);
89
- lines.push(` workspace: \`${n.workspace}\` | ${branch} | ${sessions}`);
229
+ const context = [
230
+ n.daemonId ? `daemon: \`${n.daemonId}\`` : '',
231
+ n.providers?.length ? `providers: ${n.providers.join(', ')}` : '',
232
+ ].filter(Boolean).join(' | ');
233
+ lines.push(`- ${healthIcon} **${n.machineLabel}** (nodeId: \`${n.nodeId}\`)`);
234
+ lines.push(` workspace: \`${n.workspace}\`${context ? ` | ${context}` : ''} | ${branch} | ${sessions}`);
90
235
  if (n.error) lines.push(` ⚠️ ${n.error}`);
236
+ const nodePrompt = typeof (n as any).systemPrompt === 'string' ? (n as any).systemPrompt.trim() : '';
237
+ if (nodePrompt) {
238
+ lines.push(` 📌 Node instruction: ${indentFollowing(nodePrompt, ' ')}`);
239
+ }
91
240
  }
92
241
  return lines.join('\n');
93
242
  }
94
243
 
95
244
  function buildNodeConfigSection(mesh: LocalMeshEntry): string {
96
- const lines = ['## Configured Nodes', ''];
245
+ const lines = [
246
+ '## Configured Nodes',
247
+ '',
248
+ 'Node labels are display context, not aliases. Use exact `nodeId` values in mesh tool calls; do not invent shorthand names such as M1/M2 unless they are explicitly configured labels.',
249
+ '',
250
+ ];
97
251
  for (const n of mesh.nodes) {
98
252
  const labels: string[] = [];
99
253
  if (n.isLocalWorktree) labels.push('worktree');
100
254
  if (n.policy?.readOnly) labels.push('read-only');
101
255
  const suffix = labels.length ? ` [${labels.join(', ')}]` : '';
102
- lines.push(`- **${n.workspace}** (${n.id})${suffix}`);
256
+ const explicitMachineLabel = typeof (n as any).machineLabel === 'string' ? (n as any).machineLabel : '';
257
+ const explicitLabel = explicitMachineLabel ? ` label: **${explicitMachineLabel}** |` : '';
258
+ const providerPriority = n.policy?.providerPriority?.length ? ` | providers: ${n.policy.providerPriority.join(', ')}` : '';
259
+ lines.push(`- ${explicitLabel} nodeId: \`${n.id}\` | workspace: \`${n.workspace}\`${n.daemonId ? ` | daemon: \`${n.daemonId}\`` : ''}${providerPriority}${suffix}`);
260
+ const nodePrompt = typeof (n as any).systemPrompt === 'string' ? (n as any).systemPrompt.trim() : '';
261
+ if (nodePrompt) {
262
+ lines.push(` 📌 Node instruction: ${indentFollowing(nodePrompt, ' ')}`);
263
+ }
103
264
  }
104
265
  lines.push('', '_Use `mesh_status` to probe live health before delegating work._');
105
266
  return lines.join('\n');
106
267
  }
107
268
 
269
+ /**
270
+ * Indent every line after the first by `pad`. Used so multi-line node
271
+ * instructions still visually nest under the node bullet without the
272
+ * second line dangling at column zero.
273
+ */
274
+ function indentFollowing(text: string, pad: string): string {
275
+ const lines = text.split('\n');
276
+ if (lines.length === 1) return lines[0];
277
+ return [lines[0], ...lines.slice(1).map(l => pad + l)].join('\n');
278
+ }
279
+
108
280
  function buildPolicySection(policy: RepoMeshPolicy): string {
109
281
  const rules: string[] = [];
110
282
  if (policy.requirePreTaskCheckpoint) rules.push('- Create a git checkpoint **before** starting each task');
111
283
  if (policy.requirePostTaskCheckpoint) rules.push('- Create a git checkpoint **after** each task completes');
112
284
  if (policy.requireApprovalForPush) rules.push('- **Ask for user approval** before pushing to remote');
285
+ if (policy.allowAutoPublishSubmoduleMainCommits) {
286
+ rules.push('- Refinery may auto-publish unreachable submodule gitlink commits to submodule origin/main with non-force pushes after validation and patch-equivalence pass');
287
+ }
113
288
  if (policy.requireApprovalForDestructiveGit) rules.push('- **Ask for user approval** before destructive git operations (force push, reset, etc.)');
114
289
 
115
290
  const dirtyBehavior = {
@@ -140,6 +315,7 @@ const TOOLS_SECTION = `## Available Tools
140
315
  | \`mesh_read_debug\` | Collect a daemon-side chat/parser debug bundle for a session |
141
316
  | \`mesh_task_history\` | Read the task ledger — dispatches, completions, failures. Use to understand what has been done before deciding next steps |
142
317
  | \`mesh_git_status\` | Check git status on a specific node |
318
+ | \`mesh_fast_forward_node\` | Safely dry-run or explicitly execute an obvious clean fast-forward without launching an agent session |
143
319
  | \`mesh_checkpoint\` | Create a git checkpoint on a node |
144
320
  | \`mesh_approve\` | Approve/reject a pending agent action |
145
321
  | \`mesh_clone_node\` | Create a worktree node for isolated parallel branch work |
@@ -161,10 +337,10 @@ const WORKFLOW_SECTION = `## Orchestration Workflow
161
337
  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.
162
338
  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
339
  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.
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\`.
340
+ 4. **Monitor** — Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly. Do **not** repeatedly call \`mesh_status\` or \`mesh_view_queue\` just to wait for assigned/generating work. After dispatching a direct or queued task, send one progress update with the task/session handle, then stop. Wait for \`pendingCoordinatorEvents\` or another completion/approval/status signal, an explicit user status request, or a real timeout/stall signal before reading status/chat/queue again. Use at most one compact \`mesh_read_chat\` check after a terminal signal. Handle approvals via \`mesh_approve\`.
165
341
  5. **Verify** — When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
166
342
  6. **Checkpoint** — Call \`mesh_checkpoint\` to save the work.
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.
343
+ 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. For obvious clean branch catch-up (ahead 0, behind > 0, upstream fresh, no dirty/stash/submodule issues), use \`mesh_fast_forward_node\` dry-run first and execute only when explicitly safe/approved; this avoids consuming an agent session. Use \`mesh_refine_node\` for clean worktree branches when safe. Before/refine merging root commits that contain submodule gitlink changes, require each submodule commit to be reachable from the configured submodule remote main branch, not merely present on a feature ref or local checkout. If \`mesh_refine_node\` returns \`submodule_reachability_failed\` or publish-required evidence, keep the public convergence bucket as \`blocked_review\`; unless \`allowAutoPublishSubmoduleMainCommits\` is explicitly enabled and Refinery reports successful non-force publish plus post-publish verification, ask the user for explicit approval to push/publish the unreachable submodule commit(s) to submodule main, then rerun \`mesh_refine_node\`. Do not merge the root branch until the submodule commit(s) are reachable from submodule origin/main. 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
344
  8. **Clean up** — Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
169
345
  9. **Report** — Summarize what was done, what changed, any issues, and the branch convergence state.
170
346
 
@@ -188,19 +364,17 @@ function buildRulesSection(coordinatorCliType?: string): string {
188
364
 
189
365
  return `## Rules
190
366
 
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.
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.
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.
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.
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.
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.
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.
199
- - **Check history before starting.** At the beginning of a coordination session, call \`mesh_task_history\` to understand what was previously delegated and its outcomes. This prevents duplicate work and informs recovery decisions.
200
- - **Keep the user informed.** Report progress after each delegation round one or two sentences, not a narration.
201
- - **Respect node capabilities.** Don't send build tasks to read-only nodes. Don't push from nodes that aren't allowed to.
202
- - **Never fabricate tool results.** Always call the actual tool; never pretend you did.
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.
205
- - **Name worktree branches meaningfully.** Use descriptive names like \`feat/auth-refactor\` or \`fix/build-123\`.${coordinatorNote}`;
367
+ - **Route, don't implement.** Delegate all code reading, analysis, and execution to node agents. Never read source files or run commands in the coordinatorkeep context lean.
368
+ - **Front-load task messages.** Include everything the agent needs (files, problem, expected fix) in \`mesh_enqueue_task\` / \`mesh_send_task\`. Append a structured result request at the end: ask the worker to conclude with a JSON block containing \`status\`, \`changedFiles\`, \`gitStatus\`, \`validationResults\`, \`errors\`, \`nextAction\`. The daemon parses this automatically; you can read it from \`mesh_task_history\`.
369
+ - **Reuse idle sessions.** For follow-up, retry, commit/push, or cleanup on the same issue, send only the delta to the existing idle session. Start fresh only for independent work, provider mismatch, transcript contamination, or required worktree isolation.
370
+ - **Respect explicit provider requests.** Map: Hermes \`hermes-cli\`, Claude/Claude Code \`claude-cli\`, Codex \`codex-cli\`, Gemini \`gemini-cli\`, Antigravity \`antigravity-cli\`. Never substitute the coordinator's own runtime.
371
+ - **Verify via git, not source.** Use \`mesh_git_status\` to confirm side effects. Treat agent summaries as self-reports, not verification.
372
+ - **Limit parallelism.** Start with 1–2 tasks; scale only on success. Never duplicate a session because \`mesh_read_chat\` shows no final message while tool/terminal activity is ongoing.
373
+ - **Check history first.** Call \`mesh_task_history\` at session start to avoid duplicate work and inform recovery. On failure, read task history before retrying.
374
+ - **Converge branches.** After worktree tasks: refine/fast-forward, or classify as \`pushed_feature_branch_needs_merge\` / \`blocked_review\` / \`cleanup_candidate\` / \`not_mergeable\`. Clean up with \`mesh_remove_node\`.
375
+ - **Refinery is config-driven.** \`mesh_refine_node\` must run validation from \`.adhdev/refine.{json,yaml,yml}\` or \`repo-mesh.refine.*\`. Heuristics are scaffolding only.
376
+ - **Submodule reachability = publish-needed.** \`submodule_reachability_failed\` classify as \`blocked_review\`, request user approval to push to submodule main, then rerun \`mesh_refine_node\`.
377
+ - **Honor per-node instructions.** When a node carries a 📌 Node instruction in the nodes section, include the relevant parts of that instruction in the task message you send to that node. Don't paraphrase the instruction into your own words — quote it verbatim so the worker agent sees exactly what the user wrote.
378
+ - **Never fabricate tool results.** Always call the actual tool.
379
+ - **Keep the user informed.** One or two sentences after each delegation round.${coordinatorNote}`;
206
380
  }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * MeshCoordinatorRegistry — Persisted record of active mesh coordinator sessions.
3
+ *
4
+ * Survives daemon restarts: when a CLI coordinator session is re-attached after
5
+ * a daemon restart the in-memory `settings.meshCoordinatorFor` on the provider
6
+ * instance is gone, so the registry file fills the gap.
7
+ *
8
+ * Keyed by sessionId (the CLI instance key / runtimeKey).
9
+ */
10
+
11
+ import { join } from 'path';
12
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
13
+ import { getDaemonDataDir } from '../config/config.js';
14
+
15
+ export interface CoordinatorRegistryEntry {
16
+ meshId: string;
17
+ sessionId: string;
18
+ workspace?: string;
19
+ startedAt: number;
20
+ /** CLI type used to launch the coordinator (claude-cli / codex-cli / …). */
21
+ cliType?: string;
22
+ /** Final system prompt sent to the coordinator after all overrides, appends,
23
+ * and extraSystemPrompt have been applied. Surfaced via the session-info
24
+ * endpoint so users can audit exactly what prompt the agent saw. */
25
+ systemPrompt?: string;
26
+ /** Per-launch extraSystemPrompt the caller passed in, if any. Stored
27
+ * separately from `systemPrompt` so the UI can show what the user
28
+ * added vs. what the daemon's default template produced. */
29
+ extraSystemPrompt?: string;
30
+ /** How the prompt was actually injected (cli_arg / context_file / …). */
31
+ injection?: { mode: string; target?: string };
32
+ /** Path of the MCP config file the daemon wrote for this session. */
33
+ mcpConfigPath?: string;
34
+ }
35
+
36
+ const _registry = new Map<string, CoordinatorRegistryEntry>();
37
+
38
+ function getRegistryPath(): string {
39
+ return join(getDaemonDataDir(), 'mesh-coordinators.json');
40
+ }
41
+
42
+ /** Load persisted coordinator registry from disk into in-memory map. Called once on daemon boot. */
43
+ export function loadMeshCoordinatorRegistry(): void {
44
+ const path = getRegistryPath();
45
+ if (!existsSync(path)) return;
46
+ try {
47
+ const raw = JSON.parse(readFileSync(path, 'utf-8'));
48
+ if (!Array.isArray(raw)) return;
49
+ _registry.clear();
50
+ for (const entry of raw) {
51
+ if (typeof entry?.sessionId === 'string' && typeof entry?.meshId === 'string') {
52
+ _registry.set(entry.sessionId, entry as CoordinatorRegistryEntry);
53
+ }
54
+ }
55
+ } catch { /* ignore corrupt file */ }
56
+ }
57
+
58
+ function saveRegistry(): void {
59
+ try {
60
+ writeFileSync(
61
+ getRegistryPath(),
62
+ JSON.stringify([..._registry.values()], null, 2),
63
+ { encoding: 'utf-8', mode: 0o600 },
64
+ );
65
+ } catch { /* best-effort */ }
66
+ }
67
+
68
+ /** Register a coordinator session. Persists to disk immediately. */
69
+ export function registerMeshCoordinator(entry: CoordinatorRegistryEntry): void {
70
+ _registry.set(entry.sessionId, entry);
71
+ saveRegistry();
72
+ }
73
+
74
+ /** Remove a coordinator session by sessionId. Persists to disk.
75
+ *
76
+ * Also best-effort strips any context_file wrapper block we wrote into
77
+ * the workspace at launch time (AGENTS.md / GEMINI.md), because those
78
+ * files are auto-loaded by their CLIs on every subsequent launch — and
79
+ * a wrapper block surviving the coordinator session would silently
80
+ * inject the coordinator system prompt into ordinary non-coordinator
81
+ * sessions in the same workspace, which is exactly the bug we're
82
+ * fixing here.
83
+ *
84
+ * Stripping rules:
85
+ * - Look for the start sentinel anywhere in the file; if absent,
86
+ * leave the file alone (user-authored content).
87
+ * - If the wrapper block was the only thing in the file, delete the
88
+ * file outright so we don't leave behind an empty AGENTS.md.
89
+ * - Otherwise drop only the block between the sentinels and trim a
90
+ * leading/trailing blank line so we don't leave dangling separators.
91
+ */
92
+ export function unregisterMeshCoordinator(sessionId: string): void {
93
+ const entry = _registry.get(sessionId);
94
+ if (!_registry.delete(sessionId)) return;
95
+ saveRegistry();
96
+ if (!entry) return;
97
+ const workspace = entry.workspace;
98
+ const target = entry.injection?.mode === 'context_file' && typeof entry.injection.target === 'string'
99
+ ? entry.injection.target
100
+ : '';
101
+ if (!workspace || !target) return;
102
+ // injection.target is workspace-relative (AGENTS.md / GEMINI.md); we don't
103
+ // resolve absolute paths here because we never wrote one — applyMesh-
104
+ // CoordinatorSystemPromptInjection joins via path.join(workspace, …).
105
+ const filePath = `${workspace.replace(/\/$/, '')}/${target}`;
106
+ try {
107
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
108
+ const { stripCoordinatorWrapperFile } = require('../commands/mesh-coordinator.js');
109
+ stripCoordinatorWrapperFile(filePath);
110
+ } catch { /* best-effort cleanup; never throw out of unregister */ }
111
+ }
112
+
113
+ /** Look up a coordinator entry by session ID. Returns undefined if not registered. */
114
+ export function getCoordinatorForSession(sessionId: string): CoordinatorRegistryEntry | undefined {
115
+ return _registry.get(sessionId);
116
+ }
117
+
118
+ /** List all coordinator entries for a given workspace path. */
119
+ export function listCoordinatorsForWorkspace(workspace: string): CoordinatorRegistryEntry[] {
120
+ return [..._registry.values()].filter(e => e.workspace === workspace);
121
+ }