mixdog 0.8.0 → 0.9.0

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 (285) hide show
  1. package/README.md +47 -23
  2. package/package.json +33 -27
  3. package/scripts/_test-folder-dialog.mjs +30 -0
  4. package/scripts/agent-parallel-smoke.mjs +388 -0
  5. package/scripts/agent-tag-reuse-smoke.mjs +183 -0
  6. package/scripts/background-task-meta-smoke.mjs +38 -0
  7. package/scripts/boot-smoke.mjs +52 -9
  8. package/scripts/build-runtime-linux.sh +348 -0
  9. package/scripts/build-runtime-macos.sh +217 -0
  10. package/scripts/build-runtime-windows.ps1 +242 -0
  11. package/scripts/compact-active-turn-test.mjs +68 -0
  12. package/scripts/compact-smoke.mjs +859 -129
  13. package/scripts/compact-trigger-migration-smoke.mjs +187 -0
  14. package/scripts/fix-brief-fn.mjs +35 -0
  15. package/scripts/fix-format-tool-surface.mjs +24 -0
  16. package/scripts/fix-tool-exec-visible.mjs +42 -0
  17. package/scripts/generate-runtime-manifest.mjs +166 -0
  18. package/scripts/hook-bus-test.mjs +330 -0
  19. package/scripts/lead-workflow-smoke.mjs +33 -39
  20. package/scripts/live-worker-smoke.mjs +43 -37
  21. package/scripts/llm-trace-summary.mjs +315 -0
  22. package/scripts/memory-meta-concurrency-test.mjs +20 -0
  23. package/scripts/output-style-smoke.mjs +56 -15
  24. package/scripts/parent-abort-link-test.mjs +44 -0
  25. package/scripts/patch-agent-brief.mjs +48 -0
  26. package/scripts/patch-app.mjs +21 -0
  27. package/scripts/patch-app2.mjs +18 -0
  28. package/scripts/patch-dist-brief.mjs +96 -0
  29. package/scripts/patch-tool-exec.mjs +70 -0
  30. package/scripts/pretool-ask-runtime-test.mjs +54 -0
  31. package/scripts/provider-toolcall-test.mjs +376 -0
  32. package/scripts/reactive-compact-persist-smoke.mjs +124 -0
  33. package/scripts/sanitize-tool-pairs-test.mjs +260 -0
  34. package/scripts/session-context-bench.mjs +344 -0
  35. package/scripts/session-ingest-smoke.mjs +177 -0
  36. package/scripts/set-effort-config-test.mjs +41 -0
  37. package/scripts/smoke-runtime-negative.ps1 +106 -0
  38. package/scripts/smoke-runtime-negative.sh +97 -0
  39. package/scripts/smoke.mjs +25 -0
  40. package/scripts/tool-result-hook-test.mjs +48 -0
  41. package/scripts/tool-smoke.mjs +1223 -95
  42. package/scripts/toolcall-args-test.mjs +150 -0
  43. package/scripts/tui-background-failure-smoke.mjs +73 -0
  44. package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
  45. package/src/agents/debugger/AGENT.md +8 -0
  46. package/src/agents/explore/AGENT.md +4 -0
  47. package/src/agents/heavy-worker/AGENT.md +9 -3
  48. package/src/agents/maintainer/AGENT.md +4 -0
  49. package/src/agents/reviewer/AGENT.md +8 -0
  50. package/src/agents/scheduler-task/AGENT.md +12 -0
  51. package/src/agents/scheduler-task/agent.json +6 -0
  52. package/src/agents/webhook-handler/AGENT.md +12 -0
  53. package/src/agents/webhook-handler/agent.json +6 -0
  54. package/src/agents/worker/AGENT.md +9 -3
  55. package/src/app.mjs +77 -3
  56. package/src/defaults/hidden-roles.json +17 -12
  57. package/src/headless-role.mjs +117 -0
  58. package/src/help.mjs +30 -0
  59. package/src/hooks/lib/permission-evaluator.cjs +11 -475
  60. package/src/lib/keychain-cjs.cjs +9 -1
  61. package/src/lib/mixdog-debug.cjs +0 -7
  62. package/src/lib/rules-builder.cjs +240 -96
  63. package/src/lib/text-utils.cjs +1 -1
  64. package/src/mixdog-session-runtime.mjs +2325 -450
  65. package/src/output-styles/default.md +12 -28
  66. package/src/output-styles/extreme-simple.md +9 -6
  67. package/src/output-styles/simple.md +22 -9
  68. package/src/repl.mjs +118 -59
  69. package/src/rules/agent/00-common.md +15 -0
  70. package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
  71. package/src/rules/agent/30-explorer.md +22 -0
  72. package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
  73. package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
  74. package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
  75. package/src/rules/lead/01-general.md +9 -5
  76. package/src/rules/lead/04-workflow.md +51 -12
  77. package/src/rules/lead/lead-tool.md +6 -0
  78. package/src/rules/shared/01-tool.md +12 -1
  79. package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
  80. package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
  81. package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
  82. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
  83. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
  84. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
  85. package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
  86. package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
  87. package/src/runtime/agent/orchestrator/config.mjs +174 -55
  88. package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
  89. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/internal-roles.mjs +77 -29
  91. package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
  92. package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
  93. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
  94. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +377 -243
  95. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +146 -93
  96. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
  97. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
  99. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
  100. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
  101. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
  102. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
  103. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
  106. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +221 -70
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +477 -147
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -496
  109. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
  110. package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
  111. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +31 -11
  112. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
  113. package/src/runtime/agent/orchestrator/session/cache/post-edit-marks.mjs +4 -4
  114. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +1 -1
  115. package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
  116. package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
  117. package/src/runtime/agent/orchestrator/session/loop.mjs +851 -674
  118. package/src/runtime/agent/orchestrator/session/manager.mjs +1593 -466
  119. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
  120. package/src/runtime/agent/orchestrator/session/store.mjs +291 -46
  121. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  122. package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
  123. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
  124. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +34 -7
  125. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
  126. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -0
  127. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +9 -9
  128. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
  129. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
  130. package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
  131. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
  132. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
  133. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
  134. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
  135. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
  136. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
  137. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
  138. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
  139. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
  140. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
  141. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
  142. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
  143. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
  144. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +1 -1
  145. package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
  146. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
  147. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
  148. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
  149. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
  150. package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
  151. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
  152. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
  153. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
  154. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
  155. package/src/runtime/channels/index.mjs +152 -24
  156. package/src/runtime/channels/lib/scheduler.mjs +18 -14
  157. package/src/runtime/channels/lib/session-discovery.mjs +3 -2
  158. package/src/runtime/channels/lib/tool-format.mjs +0 -2
  159. package/src/runtime/channels/lib/transcript-discovery.mjs +3 -2
  160. package/src/runtime/channels/lib/webhook.mjs +1 -1
  161. package/src/runtime/channels/tool-defs.mjs +29 -29
  162. package/src/runtime/memory/index.mjs +635 -107
  163. package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
  164. package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
  165. package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
  166. package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
  167. package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
  168. package/src/runtime/memory/lib/memory-cycle1.mjs +38 -17
  169. package/src/runtime/memory/lib/memory-cycle2.mjs +6 -7
  170. package/src/runtime/memory/lib/memory-cycle3.mjs +4 -4
  171. package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -1
  172. package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
  173. package/src/runtime/memory/lib/memory.mjs +88 -9
  174. package/src/runtime/memory/lib/model-profile.mjs +1 -1
  175. package/src/runtime/memory/lib/pg/adapter.mjs +15 -1
  176. package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
  177. package/src/runtime/memory/lib/runtime-fetcher.mjs +37 -3
  178. package/src/runtime/memory/lib/session-ingest.mjs +194 -0
  179. package/src/runtime/memory/lib/trace-store.mjs +96 -51
  180. package/src/runtime/memory/tool-defs.mjs +46 -37
  181. package/src/runtime/search/index.mjs +102 -466
  182. package/src/runtime/search/lib/web-tools.mjs +45 -25
  183. package/src/runtime/search/tool-defs.mjs +16 -23
  184. package/src/runtime/shared/abort-controller.mjs +1 -1
  185. package/src/runtime/shared/atomic-file.mjs +4 -3
  186. package/src/runtime/shared/background-tasks.mjs +122 -11
  187. package/src/runtime/shared/child-spawn-gate.mjs +145 -0
  188. package/src/runtime/shared/config.mjs +7 -4
  189. package/src/runtime/shared/err-text.mjs +131 -4
  190. package/src/runtime/shared/llm/cost.mjs +2 -2
  191. package/src/runtime/shared/llm/http-agent.mjs +23 -7
  192. package/src/runtime/shared/llm/index.mjs +34 -11
  193. package/src/runtime/shared/llm/usage-log.mjs +4 -4
  194. package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
  195. package/src/runtime/shared/singleton-owner.mjs +104 -0
  196. package/src/runtime/shared/tool-execution-contract.mjs +199 -20
  197. package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
  198. package/src/runtime/shared/tool-surface.mjs +624 -98
  199. package/src/runtime/shared/user-data-guard.mjs +0 -2
  200. package/src/standalone/agent-task-status.mjs +203 -0
  201. package/src/standalone/agent-task-status.test.mjs +76 -0
  202. package/src/standalone/agent-tool.mjs +1913 -0
  203. package/src/standalone/channel-worker.mjs +370 -14
  204. package/src/standalone/explore-tool.mjs +165 -70
  205. package/src/standalone/folder-dialog.mjs +314 -0
  206. package/src/standalone/hook-bus.mjs +898 -22
  207. package/src/standalone/memory-runtime-proxy.mjs +320 -0
  208. package/src/standalone/projects.mjs +226 -0
  209. package/src/standalone/provider-admin.mjs +41 -24
  210. package/src/standalone/seeds.mjs +2 -69
  211. package/src/standalone/usage-dashboard.mjs +96 -8
  212. package/src/tui/App.jsx +4798 -2153
  213. package/src/tui/components/AnsiText.jsx +39 -28
  214. package/src/tui/components/ContextPanel.jsx +87 -29
  215. package/src/tui/components/Markdown.jsx +43 -77
  216. package/src/tui/components/MarkdownTable.jsx +9 -184
  217. package/src/tui/components/Message.jsx +28 -11
  218. package/src/tui/components/Picker.jsx +95 -56
  219. package/src/tui/components/PromptInput.jsx +367 -239
  220. package/src/tui/components/QueuedCommands.jsx +1 -1
  221. package/src/tui/components/SlashCommandPalette.jsx +27 -21
  222. package/src/tui/components/Spinner.jsx +67 -38
  223. package/src/tui/components/StatusLine.jsx +606 -38
  224. package/src/tui/components/TextEntryPanel.jsx +128 -9
  225. package/src/tui/components/ToolExecution.jsx +617 -368
  226. package/src/tui/components/TurnDone.jsx +3 -3
  227. package/src/tui/components/UsagePanel.jsx +3 -5
  228. package/src/tui/components/tool-output-format.mjs +365 -0
  229. package/src/tui/components/tool-output-format.test.mjs +220 -0
  230. package/src/tui/dist/index.mjs +8915 -2418
  231. package/src/tui/engine-runtime-notification.test.mjs +115 -0
  232. package/src/tui/engine-tool-result-text.test.mjs +75 -0
  233. package/src/tui/engine.mjs +1455 -279
  234. package/src/tui/figures.mjs +21 -40
  235. package/src/tui/index.jsx +75 -31
  236. package/src/tui/input-editing.mjs +25 -0
  237. package/src/tui/markdown/format-token.mjs +511 -68
  238. package/src/tui/markdown/format-token.test.mjs +216 -0
  239. package/src/tui/markdown/render-ansi.mjs +94 -0
  240. package/src/tui/markdown/render-ansi.test.mjs +108 -0
  241. package/src/tui/markdown/stream-fence.mjs +34 -0
  242. package/src/tui/markdown/stream-fence.test.mjs +26 -0
  243. package/src/tui/markdown/table-layout.mjs +250 -0
  244. package/src/tui/paste-attachments.mjs +0 -7
  245. package/src/tui/spinner-verbs.mjs +1 -2
  246. package/src/tui/statusline-ansi-bridge.mjs +172 -0
  247. package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
  248. package/src/tui/theme.mjs +746 -24
  249. package/src/tui/time-format.mjs +1 -1
  250. package/src/tui/transcript-tool-failures.mjs +67 -0
  251. package/src/tui/transcript-tool-failures.test.mjs +111 -0
  252. package/src/ui/ansi.mjs +1 -2
  253. package/src/ui/markdown.mjs +85 -26
  254. package/src/ui/markdown.test.mjs +70 -0
  255. package/src/ui/model-display.mjs +121 -0
  256. package/src/ui/session-stats.mjs +44 -0
  257. package/src/ui/statusline-context-label.test.mjs +15 -0
  258. package/src/ui/statusline.mjs +386 -178
  259. package/src/ui/tool-card.mjs +3 -16
  260. package/src/vendor/statusline/bin/statusline-lib.mjs +8 -4
  261. package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
  262. package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
  263. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
  264. package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
  265. package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
  266. package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
  267. package/src/workflows/default/WORKFLOW.md +12 -5
  268. package/src/workflows/default/workflow.json +0 -1
  269. package/src/workflows/solo/WORKFLOW.md +15 -0
  270. package/src/workflows/solo/workflow.json +7 -0
  271. package/vendor/ink/build/output.js +6 -1
  272. package/src/agents/scheduler-task.md +0 -3
  273. package/src/agents/web-researcher/AGENT.md +0 -3
  274. package/src/agents/web-researcher/agent.json +0 -6
  275. package/src/agents/webhook-handler.md +0 -3
  276. package/src/rules/bridge/00-common.md +0 -5
  277. package/src/rules/bridge/30-explorer.md +0 -4
  278. package/src/rules/lead/00-tool-lead.md +0 -5
  279. package/src/rules/shared/00-language.md +0 -3
  280. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  281. package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
  282. package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
  283. package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
  284. package/src/standalone/bridge-tool.mjs +0 -1414
  285. package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
@@ -99,11 +99,6 @@ function pruneStalePluginDataLogSiblings(
99
99
  return { removed, kept: candidates.length - removed };
100
100
  }
101
101
 
102
- /** @deprecated use pruneStalePluginDataLogSiblings */
103
- function prunePluginDataLogFiles(dataDir, maxFiles) {
104
- return pruneStalePluginDataLogSiblings(dataDir, maxFiles);
105
- }
106
-
107
102
  const SESSION_START_CRITICAL_LOG = 'session-start-critical.log';
108
103
  const SESSION_START_CRITICAL_MAX_BYTES = 64 * 1024;
109
104
  const SESSION_START_CRITICAL_KEEP_BYTES = 64 * 1024;
@@ -154,10 +149,8 @@ module.exports = {
154
149
  isMixdogDebugEnabled,
155
150
  isStalePluginLogSibling,
156
151
  pruneStalePluginDataLogSiblings,
157
- prunePluginDataLogFiles,
158
152
  appendSessionStartCriticalLog,
159
153
  sessionStartCriticalFallback,
160
154
  DEFAULT_STALE_LOG_SIBLING_MAX,
161
- DEFAULT_PLUGIN_LOG_MAX_FILES: DEFAULT_STALE_LOG_SIBLING_MAX,
162
155
  SESSION_START_CRITICAL_LOG,
163
156
  };
@@ -3,24 +3,28 @@
3
3
  /**
4
4
  * mixdog rules builder.
5
5
  *
6
- * Three surfaces:
7
- * - buildInjectionContent Lead session
8
- * - buildBridgeInjectionContent bridge session BP1 (true cross-role common)
9
- * - buildBridgeRoleSpecificContent bridge session BP3 (role-specific instructions)
6
+ * Exported surfaces:
7
+ * - buildSharedToolContent BP1: shared tool policy
8
+ * - buildLeadRoleContent BP2: Lead role/system rules
9
+ * - buildAgentRoleContent BP2: agent role/system rules
10
+ * - buildAgentRetrievalInjectionContent — BP2: narrow read-only retrieval role
11
+ * - buildLeadMetaContent — BP3: Lead memory/meta context
12
+ * - buildAgentRoleSpecificContent — BP4-adjacent user/task data
13
+ * - buildInjectionContent — legacy joined Lead session content
10
14
  *
11
15
  * 4-BP cache layout (composeSystemPrompt):
12
- * BP1 = bridge BP1 content (this file's buildBridgeInjectionContent) — every role identical
13
- * BP2 = scoped role catalog (collect.mjs loadScopedRoleCatalog) role family / self
14
- * BP3 = project context + role marker + permission + role-specific instructions
15
- * BP4 = task brief + memory recap (5m volatile)
16
+ * BP1 = shared tool policy + compact skill manifest
17
+ * BP2 = role/system rules (Lead / agent / hidden role)
18
+ * BP3 = stable memory/meta context
19
+ * BP4 = live user/task messages and compacted tail
16
20
  *
17
21
  * Source files (rules/):
18
- * - shared/01-tool.md — universal tool policy (Lead + bridge BP1, identical full set)
19
- * - lead/00-tool-lead.md — Lead-specific control-tower / delegation / ToolSearch guidance
22
+ * - shared/01-tool.md — universal tool policy (Lead + agent BP1, identical full set)
23
+ * - lead/lead-tool.md — Lead-specific control-tower / delegation / ToolSearch guidance
20
24
  * - lead/01-04 — Lead workflow / channels / team / general
21
25
  * - output-styles/<name>.md — Lead output style, selected by config outputStyle
22
- * - bridge/00-common.md bridge common behavior + universal worker contract (BP1)
23
- * - bridge/10..50-*.md — per-hidden-role bodies (consumed by loadScopedRoleCatalog)
26
+ * - agent/00-common.md agent common behavior + universal worker contract (BP2)
27
+ * - agent/10..50-*.md — per-hidden-role bodies (consumed by loadScopedRoleInstructions)
24
28
  *
25
29
  * Core memory snapshot and session recap are injected separately by
26
30
  * hooks/session-start.cjs from the memory worker (pgdata) (Lead only).
@@ -56,8 +60,96 @@ function readUnifiedConfig(dataDir) {
56
60
  return {};
57
61
  }
58
62
 
63
+ function readAgentConfig(dataDir) {
64
+ const unified = readUnifiedConfig(dataDir);
65
+ return unified.agent && typeof unified.agent === 'object' ? unified.agent : unified;
66
+ }
67
+
68
+ const PROFILE_LANGUAGE_PROMPTS = Object.freeze({
69
+ en: 'English',
70
+ ko: 'Korean (한국어)',
71
+ ja: 'Japanese (日本語)',
72
+ 'zh-Hans': 'Simplified Chinese (简体中文)',
73
+ 'zh-Hant': 'Traditional Chinese (繁體中文)',
74
+ es: 'Spanish (Español)',
75
+ fr: 'French (Français)',
76
+ de: 'German (Deutsch)',
77
+ pt: 'Portuguese (Português)',
78
+ ru: 'Russian (Русский)',
79
+ it: 'Italian (Italiano)',
80
+ vi: 'Vietnamese (Tiếng Việt)',
81
+ th: 'Thai (ภาษาไทย)',
82
+ id: 'Indonesian (Bahasa Indonesia)',
83
+ hi: 'Hindi (हिन्दी)',
84
+ ar: 'Arabic (العربية)',
85
+ tr: 'Turkish (Türkçe)',
86
+ pl: 'Polish (Polski)',
87
+ nl: 'Dutch (Nederlands)',
88
+ uk: 'Ukrainian (Українська)',
89
+ });
90
+
91
+ const PROFILE_TITLE_MAX = 64;
92
+
93
+ function normalizeProfileConfig(value = {}) {
94
+ const raw = value && typeof value === 'object' ? value : {};
95
+ const title = String(raw.title ?? raw.name ?? '').trim().slice(0, PROFILE_TITLE_MAX);
96
+ const requested = String(raw.language ?? raw.lang ?? 'system').trim();
97
+ const language = requested === 'system' || PROFILE_LANGUAGE_PROMPTS[requested] ? requested : 'system';
98
+ return { title, language };
99
+ }
100
+
101
+ function systemLocaleId(locale) {
102
+ let parsed = null;
103
+ try { parsed = new Intl.Locale(locale || ''); } catch {}
104
+ const language = parsed?.language || String(locale || '').split(/[-_]/)[0] || '';
105
+ if (language === 'zh') {
106
+ const script = parsed?.script;
107
+ const region = parsed?.region;
108
+ return script === 'Hant' || ['HK', 'MO', 'TW'].includes(region) ? 'zh-Hant' : 'zh-Hans';
109
+ }
110
+ return PROFILE_LANGUAGE_PROMPTS[language] ? language : null;
111
+ }
112
+
113
+ function profileLanguagePrompt(language) {
114
+ const selected = String(language || 'system');
115
+ if (selected !== 'system') {
116
+ return PROFILE_LANGUAGE_PROMPTS[selected]
117
+ ? { prompt: PROFILE_LANGUAGE_PROMPTS[selected], source: 'profile', locale: null }
118
+ : null;
119
+ }
120
+ const locale = Intl.DateTimeFormat().resolvedOptions().locale || '';
121
+ const id = systemLocaleId(locale);
122
+ if (!id || !PROFILE_LANGUAGE_PROMPTS[id]) return null;
123
+ return { prompt: PROFILE_LANGUAGE_PROMPTS[id], source: 'system-locale', locale };
124
+ }
125
+
126
+ function buildProfilePreferencesContent(dataDir) {
127
+ const profile = normalizeProfileConfig(readAgentConfig(dataDir).profile);
128
+ const lines = [];
129
+ if (profile.title) {
130
+ lines.push(`- Use "${profile.title}" when directly addressing the user (greetings, answers, questions). Do not repeat it in routine progress updates or pre-tool preambles.`);
131
+ }
132
+ const shell = process.platform === 'win32' ? 'powershell' : 'bash';
133
+ lines.push(`- Shell environment: ${shell}. When using shell, write commands and scripts in ${shell} syntax unless the user specifies otherwise. Keep commands, paths, symbols, and exact errors verbatim.`);
134
+ return lines.length ? `# Profile Preferences\n\n${lines.join('\n')}` : '';
135
+ }
136
+
137
+ function buildLanguageSection(dataDir) {
138
+ const profile = normalizeProfileConfig(readAgentConfig(dataDir).profile);
139
+ const language = profileLanguagePrompt(profile.language);
140
+ if (!language?.prompt) return '';
141
+ const source = language.source === 'system-locale' && language.locale
142
+ ? ` from system locale ${language.locale}`
143
+ : '';
144
+ const lines = [
145
+ `- Default user-facing response language${source}: ${language.prompt}. Use it for ALL user-facing prose, including pre-tool preambles, progress updates, questions, final reports, and notices. Every such message — even a single-line preamble before a tool call — MUST be written in ${language.prompt}, and in no other language. This overrides any tone implied by the output style. Switch languages only when the user writes in another language or explicitly asks you to.`,
146
+ `- Code identifiers, paths, commands, symbols, API names, and exact errors should remain in their original form.`,
147
+ ];
148
+ return `# Language\n\n${lines.join('\n')}`;
149
+ }
150
+
59
151
  function stripFrontmatter(markdown) {
60
- return String(markdown || '').replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n*/, '').trim();
152
+ return String(markdown || '').replace(/^---[ \t]*\r?\n[\s\S]*?\r?\n---[ \t]*(?:\r?\n|$)/, '').trim();
61
153
  }
62
154
 
63
155
  function normalizeOutputStyleName(value) {
@@ -91,8 +183,8 @@ function loadOutputStyle({ PLUGIN_ROOT, DATA_DIR }) {
91
183
  }
92
184
 
93
185
  /**
94
- * Resolve the DATA_DIR subdir whose *.md instruction tree folds into a hidden
95
- * role's BP3 role-specific block, from the role's `instructionDir` metadata in
186
+ * Resolve the DATA_DIR subdir whose *.md instruction tree is sent as
187
+ * BP4-adjacent user/task data, from the role's `instructionDir` metadata in
96
188
  * defaults/hidden-roles.json. Returns null when the role declares none.
97
189
  *
98
190
  * Mirrors internal-roles.mjs getRoleInstructionDir — rules-builder is CommonJS
@@ -154,67 +246,92 @@ function composeUserAddressBullet(memoryConfig) {
154
246
  return `- User address form: ${userTitle} (use this address form exactly as written; do not combine it with any other name or honorific)`;
155
247
  }
156
248
 
157
- /**
158
- * Build the Lead injection content.
159
- */
160
- function buildInjectionContent({ PLUGIN_ROOT, DATA_DIR }) {
249
+ function splitLeadGeneral(general, addressBullet = '') {
250
+ const lines = String(general || '').split(/\r?\n/);
251
+ const roleLines = [];
252
+ const metaLines = [];
253
+ let inMeta = false;
254
+ for (const line of lines) {
255
+ if (/language used by the user/i.test(line)) {
256
+ if (!inMeta) {
257
+ metaLines.push('# General');
258
+ metaLines.push('');
259
+ inMeta = true;
260
+ }
261
+ metaLines.push(line);
262
+ } else {
263
+ roleLines.push(line);
264
+ }
265
+ }
266
+ if (addressBullet) {
267
+ if (!inMeta) {
268
+ metaLines.push('# General');
269
+ metaLines.push('');
270
+ inMeta = true;
271
+ }
272
+ metaLines.push(addressBullet);
273
+ }
274
+ return {
275
+ role: roleLines.join('\n').trim(),
276
+ meta: metaLines.join('\n').trim(),
277
+ };
278
+ }
279
+
280
+ function buildSharedToolContent({ PLUGIN_ROOT }) {
281
+ const SHARED_DIR = path.join(PLUGIN_ROOT, 'rules', 'shared');
282
+ return readOptional(path.join(SHARED_DIR, '01-tool.md'));
283
+ }
284
+
285
+ function buildLeadRoleContent({ PLUGIN_ROOT, DATA_DIR }) {
161
286
  const RULES_DIR = path.join(PLUGIN_ROOT, 'rules');
162
- const SHARED_DIR = path.join(RULES_DIR, 'shared');
163
287
  const LEAD_DIR = path.join(RULES_DIR, 'lead');
164
- const HISTORY_DIR = path.join(DATA_DIR, 'history');
165
-
166
288
  const memoryConfig = readConfigSection(DATA_DIR, 'memory');
167
- const parts = [];
168
-
169
- // Language policy injects first — global default, applied to every Lead reply
170
- // and to internal role communication regardless of locale.
171
- const language = readOptional(path.join(SHARED_DIR, '00-language.md'));
172
- if (language) parts.push(language);
173
-
289
+ const addressBullet = composeUserAddressBullet(memoryConfig);
174
290
  const general = readOptional(path.join(LEAD_DIR, '01-general.md'));
175
- if (general) {
176
- const addressBullet = composeUserAddressBullet(memoryConfig);
177
- parts.push(addressBullet ? `${general}\n${addressBullet}` : general);
178
- }
179
-
180
- const tool = readOptional(path.join(SHARED_DIR, '01-tool.md'));
181
- if (tool) parts.push(tool);
291
+ const generalSplit = splitLeadGeneral(general, addressBullet);
292
+ const parts = [];
182
293
 
183
- const toolLead = readOptional(path.join(LEAD_DIR, '00-tool-lead.md'));
294
+ const toolLead = readOptional(path.join(LEAD_DIR, 'lead-tool.md'));
184
295
  if (toolLead) parts.push(toolLead);
185
296
 
297
+ if (generalSplit.role) parts.push(generalSplit.role);
298
+
186
299
  const channels = readOptional(path.join(LEAD_DIR, '02-channels.md'));
187
300
  if (channels) parts.push(channels);
188
301
 
189
302
  const workflow = readOptional(path.join(LEAD_DIR, '04-workflow.md'));
190
303
  if (workflow) parts.push(workflow);
191
304
 
305
+ return parts.join('\n\n');
306
+ }
307
+
308
+ function buildLeadMetaContent({ PLUGIN_ROOT, DATA_DIR }) {
309
+ const RULES_DIR = path.join(PLUGIN_ROOT, 'rules');
310
+ const LEAD_DIR = path.join(RULES_DIR, 'lead');
311
+ const HISTORY_DIR = path.join(DATA_DIR, 'history');
312
+ const memoryConfig = readConfigSection(DATA_DIR, 'memory');
313
+ const addressBullet = composeUserAddressBullet(memoryConfig);
314
+ const general = readOptional(path.join(LEAD_DIR, '01-general.md'));
315
+ const generalSplit = splitLeadGeneral(general, addressBullet);
316
+ const parts = [];
317
+
318
+ const profilePreferences = buildProfilePreferencesContent(DATA_DIR);
319
+ if (profilePreferences) parts.push(profilePreferences);
320
+
321
+ const languageSection = buildLanguageSection(DATA_DIR);
322
+ if (languageSection) parts.push(languageSection);
323
+
324
+ if (generalSplit.meta) parts.push(generalSplit.meta);
325
+
192
326
  const userProfile = readOptional(path.join(HISTORY_DIR, 'user.md'));
193
327
  if (userProfile) parts.push(`# User Profile\n\n${userProfile}`);
194
328
 
195
329
  const botPersona = readOptional(path.join(HISTORY_DIR, 'bot.md'));
196
330
  if (botPersona) parts.push(`# Bot Persona\n\n${botPersona}`);
197
331
 
198
- // User workflow context — low-token, optional
199
332
  const userWorkflowMd = readOptional(path.join(DATA_DIR, 'user-workflow.md'));
200
333
  if (userWorkflowMd) parts.push(`# User Workflow\n\n${userWorkflowMd}`);
201
334
 
202
- const userWorkflowJsonPath = path.join(DATA_DIR, 'user-workflow.json');
203
- try {
204
- const json = JSON.parse(fs.readFileSync(userWorkflowJsonPath, 'utf8'));
205
- if (json && Array.isArray(json.roles) && json.roles.length > 0) {
206
- const lines = json.roles.map(r => {
207
- const name = r.name || '';
208
- const preset = r.preset || '';
209
- const permission = r.permission || '';
210
- return `- ${name}${preset ? ` (preset: ${preset})` : ''}${permission ? ` [${permission}]` : ''}`;
211
- });
212
- parts.push(`# User Workflow Roles\n\n${lines.join('\n')}`);
213
- }
214
- } catch { /* absent or invalid — skip */ }
215
-
216
- // Keep output style last so user/profile/workflow context cannot soften the
217
- // final formatting contract for user-visible replies.
218
335
  const outputStyle = loadOutputStyle({ PLUGIN_ROOT, DATA_DIR });
219
336
  if (outputStyle) parts.push(outputStyle);
220
337
 
@@ -222,61 +339,83 @@ function buildInjectionContent({ PLUGIN_ROOT, DATA_DIR }) {
222
339
  }
223
340
 
224
341
  /**
225
- * BP1 true cross-role common. Identical for every bridge role; the
226
- * role-specific stuff (per-event webhook instructions, per-task schedule
227
- * instructions, hidden role tool detail) lives in BP3 instead.
342
+ * Build the Lead injection content.
343
+ */
344
+ function buildInjectionContent({ PLUGIN_ROOT, DATA_DIR }) {
345
+ const parts = [];
346
+
347
+ const tool = buildSharedToolContent({ PLUGIN_ROOT, DATA_DIR });
348
+ if (tool) parts.push(tool);
349
+
350
+ const role = buildLeadRoleContent({ PLUGIN_ROOT, DATA_DIR });
351
+ if (role) parts.push(role);
352
+
353
+ const meta = buildLeadMetaContent({ PLUGIN_ROOT, DATA_DIR });
354
+ if (meta) parts.push(meta);
355
+
356
+ return parts.join('\n\n');
357
+ }
358
+
359
+ /**
360
+ * Legacy joined agent injection. New sessions consume shared tool policy as
361
+ * BP1 and buildAgentRoleContent() as BP2; this export remains for older smoke
362
+ * tests / callers that expect the combined shape.
228
363
  *
229
364
  * @param {object} opts
230
365
  * @param {string} opts.PLUGIN_ROOT
231
366
  * @param {string} opts.DATA_DIR
232
367
  * @returns {string}
233
368
  */
234
- function buildBridgeInjectionContent({ PLUGIN_ROOT, DATA_DIR }) {
235
- const RULES_DIR = path.join(PLUGIN_ROOT, 'rules');
236
- const SHARED_DIR = path.join(RULES_DIR, 'shared');
237
- const BRIDGE_DIR = path.join(RULES_DIR, 'bridge');
369
+ function buildAgentInjectionContent({ PLUGIN_ROOT, DATA_DIR }) {
238
370
  const parts = [];
239
371
 
240
- // 0. Language policy — global default, language-neutral plugin behavior.
241
- const language = readOptional(path.join(SHARED_DIR, '00-language.md'));
242
- if (language) parts.push(language);
243
-
244
- // 1. Universal tool policy — same full set Lead receives.
245
- const tool = readOptional(path.join(SHARED_DIR, '01-tool.md'));
372
+ const tool = buildSharedToolContent({ PLUGIN_ROOT, DATA_DIR });
246
373
  if (tool) parts.push(tool);
247
374
 
248
- // 2. Bridge common behavior.
249
- const common = readOptional(path.join(BRIDGE_DIR, '00-common.md'));
250
- if (common) parts.push(common);
251
-
252
- // 3. User-defined work-role overrides (DATA_DIR/roles/*.md). Pool-wide.
253
- const rolesDir = path.join(DATA_DIR, 'roles');
254
- const collected = collectMarkdownFilesRecursive(rolesDir);
255
- if (collected.length > 0) {
256
- collected.sort();
257
- const blocks = collected.map(f => readOptional(f)).filter(Boolean);
258
- if (blocks.length > 0) {
259
- parts.push(['# Agent roles', '', blocks.join('\n\n')].join('\n'));
260
- }
261
- }
262
-
263
- // userTitle / address form is intentionally NOT injected here — bridge
264
- // workers produce tool I/O, not user-facing replies, so the persona signal
265
- // only biases response language/tone without serving a purpose. Lead BP1
266
- // (buildInjectionContent above) still carries it.
375
+ const role = buildAgentRoleContent({ PLUGIN_ROOT, DATA_DIR });
376
+ if (role) parts.push(role);
267
377
 
268
378
  return parts.join('\n\n');
269
379
  }
270
380
 
381
+ function buildAgentRoleContent({ PLUGIN_ROOT, profile = 'full' }) {
382
+ if (String(profile || 'full') === 'retrieval') {
383
+ return buildAgentRetrievalInjectionContent();
384
+ }
385
+ const AGENT_DIR = path.join(PLUGIN_ROOT, 'rules', 'agent');
386
+ return readOptional(path.join(AGENT_DIR, '00-common.md'));
387
+ }
388
+
389
+ /**
390
+ * BP2 role rules for narrow hidden retrieval roles. These roles already carry a separate
391
+ * read-only tool schema shard, so keeping the full agent worker prefix does
392
+ * not improve cross-role cache reuse and only adds unrelated shell/edit/git
393
+ * guidance.
394
+ *
395
+ * @returns {string}
396
+ */
397
+ function buildAgentRetrievalInjectionContent() {
398
+ return [
399
+ '# Tool Use',
400
+ '',
401
+ '- Batch independent read-only lookups in the same tool turn.',
402
+ '- Use code_graph for symbols/dependencies, grep for exact text, find/glob/list for files, and read only known paths/windows.',
403
+ '',
404
+ '# Agent Constraints',
405
+ '',
406
+ '- Read-only retrieval role: do not edit files, run shell, or use git.',
407
+ '- Follow the role output contract exactly; do not add handoff prose.',
408
+ ].join('\n');
409
+ }
410
+
271
411
  /**
272
- * BP3 role-specific instructions. Only the calling role's own task / tool
273
- * detail body emits — webhook-handler gets webhooks/<all-events>/, scheduler
274
- * gets schedules/<all-tasks>/, hidden retrieval roles get their own tool
275
- * detail. Other roles return ''.
412
+ * BP4-adjacent role-specific data. Only the calling role's own task / tool
413
+ * detail body emits — webhook-handler gets webhooks/<all-events>/ and
414
+ * scheduler gets schedules/<all-tasks>/. Other roles return ''.
276
415
  *
277
416
  * NOTE: webhook-event narrowing (one event per call) requires the inbound
278
- * payload's event id at compose time; not implemented yet, so all 4 webhook
279
- * instructions still bake into webhook-handler BP3 for now.
417
+ * payload's event id at compose time; not implemented yet, so all webhook
418
+ * instruction files still ride with the webhook-handler user/task context.
280
419
  *
281
420
  * @param {object} opts
282
421
  * @param {string} opts.PLUGIN_ROOT
@@ -284,7 +423,7 @@ function buildBridgeInjectionContent({ PLUGIN_ROOT, DATA_DIR }) {
284
423
  * @param {string|null} opts.currentRole
285
424
  * @returns {string}
286
425
  */
287
- function buildBridgeRoleSpecificContent({ PLUGIN_ROOT, DATA_DIR, currentRole }) {
426
+ function buildAgentRoleSpecificContent({ PLUGIN_ROOT, DATA_DIR, currentRole }) {
288
427
  if (!currentRole) return '';
289
428
  const parts = [];
290
429
 
@@ -298,7 +437,7 @@ function buildBridgeRoleSpecificContent({ PLUGIN_ROOT, DATA_DIR, currentRole })
298
437
  const collected = collectMarkdownFilesRecursive(dir);
299
438
  if (collected.length > 0) {
300
439
  collected.sort();
301
- const blocks = collected.map(f => readOptional(f)).filter(Boolean);
440
+ const blocks = collected.map(f => stripFrontmatter(readOptional(f))).filter(Boolean);
302
441
  if (blocks.length > 0) {
303
442
  parts.push([`# Agent ${subdirForRole}`, '', blocks.join('\n\n')].join('\n'));
304
443
  }
@@ -309,7 +448,12 @@ function buildBridgeRoleSpecificContent({ PLUGIN_ROOT, DATA_DIR, currentRole })
309
448
  }
310
449
 
311
450
  module.exports = {
451
+ buildSharedToolContent,
452
+ buildLeadRoleContent,
453
+ buildLeadMetaContent,
454
+ buildAgentRoleContent,
312
455
  buildInjectionContent,
313
- buildBridgeInjectionContent,
314
- buildBridgeRoleSpecificContent,
456
+ buildAgentInjectionContent,
457
+ buildAgentRetrievalInjectionContent,
458
+ buildAgentRoleSpecificContent,
315
459
  };
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * Single source of truth for the regex set that strips:
9
9
  * - markdown fences, headers, list markers, bold
10
- * - tool/system tags emitted by Claude Code (system-reminder, tool-use-id,
10
+ * - tool/system tags from assistant transcripts (system-reminder, tool-use-id,
11
11
  * local-command-*, etc.)
12
12
  * - mixdog tags (channel, schedule-context, teammate-message)
13
13
  * - URLs, emoji, "Ran X", "Process exited", and other transcript noise