mixdog 0.9.18 → 0.9.20

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 (214) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -29
  3. package/package.json +5 -3
  4. package/scripts/build-runtime-windows.ps1 +242 -242
  5. package/scripts/build-tui.mjs +6 -0
  6. package/scripts/hook-bus-test.mjs +8 -0
  7. package/scripts/log-writer-guard-smoke.mjs +131 -0
  8. package/scripts/output-style-smoke.mjs +2 -2
  9. package/scripts/reactive-compact-persist-smoke.mjs +22 -6
  10. package/scripts/recall-bench-cases.json +10 -0
  11. package/scripts/recall-bench.mjs +91 -2
  12. package/scripts/recall-quality-cases.json +1 -2
  13. package/scripts/recall-usecase-cases.json +1 -1
  14. package/scripts/session-ingest-compaction-smoke.mjs +241 -0
  15. package/scripts/smoke-runtime-negative.ps1 +106 -106
  16. package/scripts/tool-efficiency-diag.mjs +5 -2
  17. package/scripts/tool-smoke.mjs +251 -72
  18. package/src/agents/debugger/AGENT.md +3 -3
  19. package/src/agents/heavy-worker/AGENT.md +7 -10
  20. package/src/agents/maintainer/AGENT.md +1 -2
  21. package/src/agents/reviewer/AGENT.md +1 -2
  22. package/src/agents/worker/AGENT.md +5 -8
  23. package/src/defaults/agents.json +3 -0
  24. package/src/help.mjs +2 -5
  25. package/src/lib/mixdog-debug.cjs +13 -0
  26. package/src/mixdog-session-runtime.mjs +7 -3311
  27. package/src/rules/agent/00-core.md +4 -3
  28. package/src/rules/agent/30-explorer.md +53 -22
  29. package/src/rules/lead/02-channels.md +3 -3
  30. package/src/rules/lead/lead-tool.md +3 -2
  31. package/src/rules/shared/01-tool.md +24 -29
  32. package/src/runtime/agent/orchestrator/context/collect.mjs +33 -9
  33. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +1 -1
  34. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -1
  35. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +24 -3
  36. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +3 -3
  37. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +663 -0
  38. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +6 -0
  39. package/src/runtime/agent/orchestrator/session/context-utils.mjs +2 -2
  40. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +153 -0
  41. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +49 -0
  42. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +250 -35
  43. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +9 -1
  44. package/src/runtime/agent/orchestrator/session/loop.mjs +8 -1860
  45. package/src/runtime/agent/orchestrator/session/manager/agent-runtime-singleton.mjs +29 -0
  46. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +686 -0
  47. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +60 -12
  48. package/src/runtime/agent/orchestrator/session/manager/env-utils.mjs +8 -0
  49. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +159 -0
  50. package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +143 -0
  51. package/src/runtime/agent/orchestrator/session/manager/prefetch-bridge.mjs +268 -0
  52. package/src/runtime/agent/orchestrator/session/manager/provider-cache-key.mjs +22 -0
  53. package/src/runtime/agent/orchestrator/session/manager/runtime-loaders.mjs +26 -0
  54. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +124 -0
  55. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +258 -0
  56. package/src/runtime/agent/orchestrator/session/manager/session-errors.mjs +20 -0
  57. package/src/runtime/agent/orchestrator/session/manager/session-id.mjs +9 -0
  58. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +475 -0
  59. package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +23 -0
  60. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +6 -4
  61. package/src/runtime/agent/orchestrator/session/manager.mjs +88 -2285
  62. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +440 -0
  63. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +153 -0
  64. package/src/runtime/agent/orchestrator/session/store.mjs +4 -1
  65. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +642 -0
  66. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +23 -3
  67. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +198 -6
  68. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +10 -2
  69. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +13 -15
  70. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +6 -1
  71. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +45 -3
  72. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -2
  73. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +195 -3
  74. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +108 -2
  75. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +15 -3
  76. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +7 -0
  77. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +24 -2
  78. package/src/runtime/agent/orchestrator/tools/builtin.mjs +17 -1
  79. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +38 -0
  80. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -4
  81. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +2 -2
  82. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +72 -8
  83. package/src/runtime/agent/orchestrator/tools/shell-policy-danger-target.mjs +5 -1
  84. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +1 -1
  85. package/src/runtime/channels/backends/discord-gateway.mjs +14 -1
  86. package/src/runtime/channels/backends/discord.mjs +43 -1
  87. package/src/runtime/channels/index.mjs +6 -2096
  88. package/src/runtime/channels/lib/inbound-handler.mjs +328 -0
  89. package/src/runtime/channels/lib/inbound-routing.mjs +19 -12
  90. package/src/runtime/channels/lib/interaction-handlers.mjs +260 -0
  91. package/src/runtime/channels/lib/memory-client.mjs +32 -14
  92. package/src/runtime/channels/lib/network-retry.mjs +23 -0
  93. package/src/runtime/channels/lib/output-forwarder.mjs +38 -7
  94. package/src/runtime/channels/lib/owned-runtime.mjs +547 -0
  95. package/src/runtime/channels/lib/owner-heartbeat.mjs +13 -4
  96. package/src/runtime/channels/lib/runtime-paths.mjs +35 -1
  97. package/src/runtime/channels/lib/tool-dispatch.mjs +17 -7
  98. package/src/runtime/channels/lib/transcript-binding.mjs +336 -0
  99. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +39 -2
  100. package/src/runtime/channels/lib/worker-bootstrap.mjs +97 -0
  101. package/src/runtime/channels/lib/worker-ipc.mjs +201 -0
  102. package/src/runtime/channels/lib/worker-main.mjs +771 -0
  103. package/src/runtime/memory/index.mjs +73 -1725
  104. package/src/runtime/memory/lib/embedding-provider.mjs +4 -2
  105. package/src/runtime/memory/lib/embedding-worker.mjs +47 -6
  106. package/src/runtime/memory/lib/http-router.mjs +772 -0
  107. package/src/runtime/memory/lib/memory-action-handlers.mjs +901 -0
  108. package/src/runtime/memory/lib/memory-embed.mjs +28 -7
  109. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +8 -2
  110. package/src/runtime/memory/lib/memory-recall-store.mjs +182 -9
  111. package/src/runtime/memory/lib/query-handlers.mjs +38 -6
  112. package/src/runtime/memory/lib/recall-format.mjs +106 -6
  113. package/src/runtime/memory/lib/session-ingest-runtime.mjs +401 -0
  114. package/src/runtime/memory/lib/session-ingest.mjs +1 -1
  115. package/src/runtime/shared/atomic-file.mjs +10 -4
  116. package/src/runtime/shared/background-tasks.mjs +4 -2
  117. package/src/runtime/shared/tool-execution-contract.mjs +1 -1
  118. package/src/runtime/shared/tool-result-summary.mjs +1 -1
  119. package/src/runtime/shared/tool-surface.mjs +30 -1
  120. package/src/session-runtime/boot-profile.mjs +36 -0
  121. package/src/session-runtime/channel-config-api.mjs +70 -0
  122. package/src/session-runtime/config-lifecycle.mjs +1 -1
  123. package/src/session-runtime/context-status.mjs +181 -0
  124. package/src/session-runtime/cwd-plugins.mjs +46 -3
  125. package/src/session-runtime/env.mjs +17 -0
  126. package/src/session-runtime/lifecycle-api.mjs +242 -0
  127. package/src/session-runtime/mcp-glue.mjs +24 -3
  128. package/src/session-runtime/model-route-api.mjs +198 -0
  129. package/src/session-runtime/output-styles.mjs +44 -10
  130. package/src/session-runtime/provider-auth-api.mjs +135 -0
  131. package/src/session-runtime/resource-api.mjs +282 -0
  132. package/src/session-runtime/runtime-core.mjs +2046 -0
  133. package/src/session-runtime/session-turn-api.mjs +274 -0
  134. package/src/session-runtime/tool-catalog.mjs +18 -264
  135. package/src/session-runtime/tool-defs.mjs +2 -2
  136. package/src/session-runtime/workflow-agents-api.mjs +238 -0
  137. package/src/session-runtime/workflow.mjs +16 -1
  138. package/src/standalone/agent-tool.mjs +2 -2
  139. package/src/standalone/channel-worker.mjs +78 -5
  140. package/src/standalone/explore-tool.mjs +1 -1
  141. package/src/standalone/memory-runtime-proxy.mjs +56 -6
  142. package/src/tui/App.jsx +88 -97
  143. package/src/tui/app/channel-pickers.mjs +45 -0
  144. package/src/tui/app/core-memory-picker.mjs +1 -1
  145. package/src/tui/app/slash-commands.mjs +0 -1
  146. package/src/tui/app/slash-dispatch.mjs +0 -16
  147. package/src/tui/app/transcript-window.mjs +44 -1
  148. package/src/tui/app/use-mouse-input.mjs +15 -2
  149. package/src/tui/app/use-prompt-handlers.mjs +7 -94
  150. package/src/tui/app/use-transcript-scroll.mjs +82 -4
  151. package/src/tui/app/use-transcript-window.mjs +65 -5
  152. package/src/tui/components/PromptInput.jsx +33 -64
  153. package/src/tui/components/ToolExecution.jsx +2 -2
  154. package/src/tui/dist/index.mjs +7908 -7558
  155. package/src/tui/engine/context-state.mjs +145 -0
  156. package/src/tui/engine/prompt-history.mjs +27 -0
  157. package/src/tui/engine/session-api-ext.mjs +478 -0
  158. package/src/tui/engine/session-api.mjs +545 -0
  159. package/src/tui/engine/session-flow.mjs +485 -0
  160. package/src/tui/engine/turn.mjs +1078 -0
  161. package/src/tui/engine.mjs +69 -2582
  162. package/src/tui/index.jsx +7 -0
  163. package/src/tui/lib/voice-setup.mjs +166 -0
  164. package/src/tui/paste-attachments.mjs +12 -5
  165. package/src/tui/prompt-history-store.mjs +125 -12
  166. package/vendor/ink/build/ink.js +16 -1
  167. package/vendor/ink/build/output.js +30 -4
  168. package/vendor/ink/build/render.js +5 -0
  169. package/scripts/bench/cache-probe-tasks.json +0 -8
  170. package/scripts/bench/lead-review-tasks-r3.json +0 -20
  171. package/scripts/bench/lead-review-tasks.json +0 -20
  172. package/scripts/bench/r4-mixed-tasks.json +0 -20
  173. package/scripts/bench/r5-orchestrated-task.json +0 -7
  174. package/scripts/bench/review-tasks.json +0 -20
  175. package/scripts/bench/round-codex.json +0 -114
  176. package/scripts/bench/round-mixdog-lead-r3.json +0 -269
  177. package/scripts/bench/round-mixdog-lead.json +0 -269
  178. package/scripts/bench/round-mixdog.json +0 -126
  179. package/scripts/bench/round-r10-bigsample.json +0 -679
  180. package/scripts/bench/round-r11-codexalign.json +0 -257
  181. package/scripts/bench/round-r13-clientmeta.json +0 -464
  182. package/scripts/bench/round-r14-betafeatures.json +0 -466
  183. package/scripts/bench/round-r15-fulldefault.json +0 -462
  184. package/scripts/bench/round-r16-sessionid.json +0 -466
  185. package/scripts/bench/round-r17-wirebytes.json +0 -456
  186. package/scripts/bench/round-r18-prewarm.json +0 -468
  187. package/scripts/bench/round-r19-clean.json +0 -472
  188. package/scripts/bench/round-r20-prewarm-clean.json +0 -475
  189. package/scripts/bench/round-r21-delta-retry.json +0 -473
  190. package/scripts/bench/round-r22-full-probe.json +0 -693
  191. package/scripts/bench/round-r23-itemprobe.json +0 -701
  192. package/scripts/bench/round-r24-shapefix.json +0 -677
  193. package/scripts/bench/round-r25-serial.json +0 -464
  194. package/scripts/bench/round-r26-parallel3.json +0 -671
  195. package/scripts/bench/round-r27-parallel10.json +0 -894
  196. package/scripts/bench/round-r28-parallel10-stagger.json +0 -882
  197. package/scripts/bench/round-r29-parallel10-stagger166.json +0 -886
  198. package/scripts/bench/round-r30-instid.json +0 -253
  199. package/scripts/bench/round-r31-upgradeprobe.json +0 -256
  200. package/scripts/bench/round-r32-vs-codex-lead.json +0 -254
  201. package/scripts/bench/round-r33-vs-codex-codex.json +0 -115
  202. package/scripts/bench/round-r34-orchestrated.json +0 -120
  203. package/scripts/bench/round-r35-orchestrated-codex.json +0 -61
  204. package/scripts/bench/round-r36-orchestrated-capped.json +0 -128
  205. package/scripts/bench/round-r4-codex.json +0 -114
  206. package/scripts/bench/round-r4-mixed.json +0 -225
  207. package/scripts/bench/round-r5-gpt-lead.json +0 -259
  208. package/scripts/bench/round-r6-codex.json +0 -114
  209. package/scripts/bench/round-r6-solo.json +0 -257
  210. package/scripts/bench/round-r7-full.json +0 -254
  211. package/scripts/bench/round-r8-fulldefault.json +0 -255
  212. package/src/tui/components/prompt-input/voice-indicator.mjs +0 -39
  213. package/src/tui/lib/voice-recorder.mjs +0 -469
  214. package/src/workflows/sequential/WORKFLOW.md +0 -51
@@ -0,0 +1,282 @@
1
+ import { join } from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import {
4
+ clean,
5
+ toolResponseText,
6
+ isEmptyRecallText,
7
+ currentSessionRecallRows,
8
+ } from './session-text.mjs';
9
+ import {
10
+ addPlugin as registryAddPlugin,
11
+ removePlugin as registryRemovePlugin,
12
+ updatePlugin as registryUpdatePlugin,
13
+ } from '../standalone/plugin-admin.mjs';
14
+ import {
15
+ normalizePluginMcpServerConfig,
16
+ pluginMcpServerName,
17
+ pluginRawMcpServers,
18
+ pluginMcpEnableScript,
19
+ resolveContainedPluginPath,
20
+ } from './plugin-mcp.mjs';
21
+
22
+ // MCP servers, skills, plugins, hooks, and memory/recall surfaces. Extracted
23
+ // verbatim from the runtime API object; stateless helpers are imported directly
24
+ // and the runtime injects live state getters plus the closure callbacks.
25
+ export function createResourceApi(deps) {
26
+ const {
27
+ getConfig, getSession, getCurrentCwd,
28
+ cfgMod, mgr, hooks, STANDALONE_DATA_DIR,
29
+ saveConfigAndAdopt, connectConfiguredMcp, invalidatePreSessionToolSurface,
30
+ recreateCurrentSessionIfReady, normalizeMcpServerInput, mcpStatus,
31
+ skillsStatus, skillContent, addProjectSkill, pluginsStatus, getMemoryModule,
32
+ reloadFullConfig,
33
+ } = deps;
34
+ return {
35
+ mcpStatus() {
36
+ return mcpStatus();
37
+ },
38
+ async reconnectMcp() {
39
+ reloadFullConfig();
40
+ const status = await connectConfiguredMcp({ reset: true });
41
+ invalidatePreSessionToolSurface();
42
+ const session = getSession();
43
+ if (session?.id) mgr.closeSession(session.id, 'cli-mcp-reconnect');
44
+ await recreateCurrentSessionIfReady();
45
+ return status;
46
+ },
47
+ async addMcpServer(input = {}) {
48
+ const { name, config: serverConfig } = normalizeMcpServerInput(input);
49
+ const nextConfig = { ...getConfig() };
50
+ nextConfig.mcpServers = {
51
+ ...(nextConfig.mcpServers || {}),
52
+ [name]: serverConfig,
53
+ };
54
+ saveConfigAndAdopt(nextConfig);
55
+ const status = await connectConfiguredMcp({ reset: true });
56
+ invalidatePreSessionToolSurface();
57
+ const session = getSession();
58
+ if (session?.id) mgr.closeSession(session.id, 'cli-mcp-add');
59
+ await recreateCurrentSessionIfReady();
60
+ return { name, status };
61
+ },
62
+ async removeMcpServer(name) {
63
+ const serverName = clean(name);
64
+ if (!serverName) throw new Error('MCP server name is required');
65
+ const nextConfig = { ...getConfig() };
66
+ const current = nextConfig.mcpServers && typeof nextConfig.mcpServers === 'object'
67
+ ? { ...nextConfig.mcpServers }
68
+ : {};
69
+ if (!Object.prototype.hasOwnProperty.call(current, serverName)) {
70
+ throw new Error(`MCP server not configured: ${serverName}`);
71
+ }
72
+ delete current[serverName];
73
+ saveConfigAndAdopt({ ...nextConfig, mcpServers: current });
74
+ const status = await connectConfiguredMcp({ reset: true });
75
+ invalidatePreSessionToolSurface();
76
+ const session = getSession();
77
+ if (session?.id) mgr.closeSession(session.id, 'cli-mcp-remove');
78
+ await recreateCurrentSessionIfReady();
79
+ return status;
80
+ },
81
+ async setMcpServerEnabled(name, enabled) {
82
+ const serverName = clean(name);
83
+ if (!serverName) throw new Error('MCP server name is required');
84
+ const nextConfig = { ...getConfig() };
85
+ const current = nextConfig.mcpServers && typeof nextConfig.mcpServers === 'object'
86
+ ? { ...nextConfig.mcpServers }
87
+ : {};
88
+ if (!Object.prototype.hasOwnProperty.call(current, serverName)) {
89
+ throw new Error(`MCP server not configured: ${serverName}`);
90
+ }
91
+ current[serverName] = { ...(current[serverName] || {}), enabled: enabled !== false };
92
+ saveConfigAndAdopt({ ...nextConfig, mcpServers: current });
93
+ const status = await connectConfiguredMcp({ reset: true });
94
+ invalidatePreSessionToolSurface();
95
+ const session = getSession();
96
+ if (session?.id) mgr.closeSession(session.id, 'cli-mcp-toggle');
97
+ await recreateCurrentSessionIfReady();
98
+ return status;
99
+ },
100
+ skillsStatus() {
101
+ return skillsStatus();
102
+ },
103
+ skillContent(name) {
104
+ return skillContent(name);
105
+ },
106
+ async addSkill(input = {}) {
107
+ const skill = addProjectSkill(input);
108
+ const session = getSession();
109
+ if (session?.id) mgr.closeSession(session.id, 'cli-skill-add');
110
+ await recreateCurrentSessionIfReady();
111
+ return { skill, status: skillsStatus() };
112
+ },
113
+ async reloadSkills() {
114
+ const session = getSession();
115
+ if (session?.id) mgr.closeSession(session.id, 'cli-skills-reload');
116
+ await recreateCurrentSessionIfReady();
117
+ return skillsStatus();
118
+ },
119
+ pluginsStatus() {
120
+ return pluginsStatus();
121
+ },
122
+ async reloadPlugins() {
123
+ const session = getSession();
124
+ if (session?.id) mgr.closeSession(session.id, 'cli-plugins-reload');
125
+ await recreateCurrentSessionIfReady();
126
+ return pluginsStatus();
127
+ },
128
+ async addPlugin(source) {
129
+ const dataDir = cfgMod.getPluginData?.();
130
+ const plugin = registryAddPlugin(source, { dataDir });
131
+ const session = getSession();
132
+ if (session?.id) mgr.closeSession(session.id, 'cli-plugin-add');
133
+ await recreateCurrentSessionIfReady();
134
+ return { plugin, status: pluginsStatus() };
135
+ },
136
+ async updatePlugin(plugin = {}) {
137
+ const key = clean(plugin.id || plugin.name || plugin);
138
+ const dataDir = cfgMod.getPluginData?.();
139
+ const updated = registryUpdatePlugin(key, { dataDir });
140
+ const session = getSession();
141
+ if (session?.id) mgr.closeSession(session.id, 'cli-plugin-update');
142
+ await recreateCurrentSessionIfReady();
143
+ return { plugin: updated, status: pluginsStatus() };
144
+ },
145
+ async removePlugin(plugin = {}) {
146
+ const key = clean(plugin.id || plugin.name || plugin);
147
+ const dataDir = cfgMod.getPluginData?.();
148
+ const removed = registryRemovePlugin(key, { dataDir });
149
+ const nextConfig = { ...getConfig() };
150
+ const serverName = pluginMcpServerName(plugin);
151
+ const prefix = `${serverName}--`;
152
+ const hasMatch = nextConfig.mcpServers && Object.keys(nextConfig.mcpServers).some(
153
+ (k) => k === serverName || k.startsWith(prefix)
154
+ );
155
+ if (hasMatch) {
156
+ const current = { ...nextConfig.mcpServers };
157
+ for (const k of Object.keys(current)) {
158
+ if (k === serverName || k.startsWith(prefix)) delete current[k];
159
+ }
160
+ saveConfigAndAdopt({ ...nextConfig, mcpServers: current });
161
+ await connectConfiguredMcp({ reset: true });
162
+ invalidatePreSessionToolSurface();
163
+ }
164
+ const session = getSession();
165
+ if (session?.id) mgr.closeSession(session.id, 'cli-plugin-remove');
166
+ await recreateCurrentSessionIfReady();
167
+ return { plugin: removed, status: pluginsStatus() };
168
+ },
169
+ async enablePluginMcp(plugin = {}) {
170
+ const root = clean(plugin.root);
171
+ const script = pluginMcpEnableScript(root, plugin);
172
+ if (!root || !script) throw new Error('plugin has no MCP script');
173
+ const serverName = pluginMcpServerName(plugin);
174
+ const nextConfig = { ...getConfig() };
175
+ const manifestMcp = pluginRawMcpServers(root, script);
176
+ if (manifestMcp) {
177
+ const { rawServers, mcpRoot } = manifestMcp;
178
+ const keys = Object.keys(rawServers).filter((k) => {
179
+ const v = rawServers[k];
180
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
181
+ });
182
+ const ownedPrefix = `${serverName}--`;
183
+ const nextServers = {};
184
+ for (const [k, v] of Object.entries(nextConfig.mcpServers || {})) {
185
+ if (k === serverName || k.startsWith(ownedPrefix)) continue;
186
+ nextServers[k] = v;
187
+ }
188
+ for (const serverKey of keys) {
189
+ const cfg = normalizePluginMcpServerConfig(rawServers[serverKey], mcpRoot);
190
+ cfg.env = {
191
+ ...(cfg.env || {}),
192
+ MIXDOG_PLUGIN_ROOT: root,
193
+ MIXDOG_PLUGIN_DATA: join(cfgMod.getPluginData?.() || STANDALONE_DATA_DIR, 'plugins', 'data', clean(plugin.id || plugin.name || serverName)),
194
+ };
195
+ const key = keys.length === 1 ? serverName : `${serverName}--${serverKey}`;
196
+ nextServers[key] = cfg;
197
+ }
198
+ nextConfig.mcpServers = nextServers;
199
+ } else {
200
+ const scriptPath = resolveContainedPluginPath(root, script);
201
+ if (!scriptPath || !existsSync(scriptPath)) throw new Error(`plugin MCP script not found: ${join(root, script)}`);
202
+ nextConfig.mcpServers = {
203
+ ...(nextConfig.mcpServers || {}),
204
+ [serverName]: {
205
+ command: 'node',
206
+ args: [scriptPath],
207
+ cwd: root,
208
+ env: {
209
+ MIXDOG_PLUGIN_ROOT: root,
210
+ MIXDOG_PLUGIN_DATA: join(cfgMod.getPluginData?.() || STANDALONE_DATA_DIR, 'plugins', 'data', clean(plugin.id || plugin.name || serverName)),
211
+ },
212
+ },
213
+ };
214
+ }
215
+ saveConfigAndAdopt(nextConfig);
216
+ const status = await connectConfiguredMcp({ reset: true });
217
+ invalidatePreSessionToolSurface();
218
+ const session = getSession();
219
+ if (session?.id) mgr.closeSession(session.id, 'cli-plugin-mcp-enable');
220
+ await recreateCurrentSessionIfReady();
221
+ return { serverName, status };
222
+ },
223
+ hooksStatus() {
224
+ return hooks.status();
225
+ },
226
+ addHookRule(rule) {
227
+ return hooks.addRule(rule);
228
+ },
229
+ setHookRuleEnabled(index, enabled) {
230
+ return hooks.setRuleEnabled(index, enabled);
231
+ },
232
+ deleteHookRule(index) {
233
+ return hooks.deleteRule(index);
234
+ },
235
+ async memoryControl(args = {}) {
236
+ const memoryMod = await getMemoryModule();
237
+ if (!memoryMod?.handleToolCall) throw new Error('memory runtime is not available');
238
+ return toolResponseText(await memoryMod.handleToolCall('memory', args || {}));
239
+ },
240
+ async recall(query, args = {}) {
241
+ const session = getSession();
242
+ const currentCwd = getCurrentCwd();
243
+ const baseQuery = query || args?.query || '';
244
+ if (args?.currentSession !== false && session?.id) {
245
+ const currentText = currentSessionRecallRows(session, baseQuery, { limit: args?.limit });
246
+ if (!isEmptyRecallText(currentText)) return currentText;
247
+ }
248
+ const memoryMod = await getMemoryModule();
249
+ if (!memoryMod?.handleToolCall) throw new Error('memory runtime is not available');
250
+ const baseArgs = {
251
+ ...(args || {}),
252
+ query: baseQuery,
253
+ cwd: args?.cwd || currentCwd,
254
+ ...(session?.id ? { currentSessionId: session.id } : {}),
255
+ };
256
+ let result = '(no results)';
257
+ if (session?.id && args?.currentSession !== false && args?.forceCycleOnEmpty !== false) {
258
+ const messages = Array.isArray(session.messages) ? session.messages : [];
259
+ if (messages.length > 0) {
260
+ await memoryMod.handleToolCall('memory', {
261
+ action: 'ingest_session',
262
+ sessionId: session.id,
263
+ cwd: currentCwd,
264
+ messages,
265
+ });
266
+ result = toolResponseText(await memoryMod.handleToolCall('recall', {
267
+ ...baseArgs,
268
+ sessionId: session.id,
269
+ currentSession: true,
270
+ projectScope: baseArgs.projectScope || 'all',
271
+ includeRaw: baseArgs.includeRaw !== false,
272
+ includeArchived: baseArgs.includeArchived !== false,
273
+ }));
274
+ }
275
+ }
276
+ if (isEmptyRecallText(result)) {
277
+ result = toolResponseText(await memoryMod.handleToolCall('recall', baseArgs));
278
+ }
279
+ return result;
280
+ },
281
+ };
282
+ }