mono-pilot 0.2.10 → 0.2.13

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 (155) hide show
  1. package/README.md +260 -2
  2. package/dist/src/agents-paths.js +36 -0
  3. package/dist/src/brief/blocks.js +83 -0
  4. package/dist/src/brief/defaults.js +60 -0
  5. package/dist/src/brief/frontmatter.js +53 -0
  6. package/dist/src/brief/paths.js +10 -0
  7. package/dist/src/brief/reflection.js +27 -0
  8. package/dist/src/cli.js +62 -5
  9. package/dist/src/cluster/bus.js +102 -0
  10. package/dist/src/cluster/follower.js +137 -0
  11. package/dist/src/cluster/init.js +182 -0
  12. package/dist/src/cluster/leader.js +97 -0
  13. package/dist/src/cluster/log.js +49 -0
  14. package/dist/src/cluster/protocol.js +34 -0
  15. package/dist/src/cluster/services/bus.js +243 -0
  16. package/dist/src/cluster/services/embedding.js +12 -0
  17. package/dist/src/cluster/socket.js +86 -0
  18. package/dist/src/cluster/test-bus.js +175 -0
  19. package/dist/src/cluster_v2/connection-lifecycle.js +31 -0
  20. package/dist/src/cluster_v2/connection-lifecycle.test.js +24 -0
  21. package/dist/src/cluster_v2/connection.js +159 -0
  22. package/dist/src/cluster_v2/connection.test.js +55 -0
  23. package/dist/src/cluster_v2/events.js +102 -0
  24. package/dist/src/cluster_v2/index.js +2 -0
  25. package/dist/src/cluster_v2/observability.js +99 -0
  26. package/dist/src/cluster_v2/observability.test.js +46 -0
  27. package/dist/src/cluster_v2/rpc.js +389 -0
  28. package/dist/src/cluster_v2/rpc.test.js +110 -0
  29. package/dist/src/cluster_v2/runtime.failover.integration.test.js +156 -0
  30. package/dist/src/cluster_v2/runtime.js +531 -0
  31. package/dist/src/cluster_v2/runtime.lease-compromise.integration.test.js +91 -0
  32. package/dist/src/cluster_v2/runtime.lifecycle.integration.test.js +225 -0
  33. package/dist/src/cluster_v2/services/bus.integration.test.js +140 -0
  34. package/dist/src/cluster_v2/services/bus.js +450 -0
  35. package/dist/src/cluster_v2/services/discord/auth-store.js +82 -0
  36. package/dist/src/cluster_v2/services/discord/collector.js +569 -0
  37. package/dist/src/cluster_v2/services/discord/index.js +1 -0
  38. package/dist/src/cluster_v2/services/discord/oauth.js +87 -0
  39. package/dist/src/cluster_v2/services/discord/rpc-client.js +325 -0
  40. package/dist/src/cluster_v2/services/embedding.js +66 -0
  41. package/dist/src/cluster_v2/services/registry-cache.js +107 -0
  42. package/dist/src/cluster_v2/services/registry-cache.test.js +66 -0
  43. package/dist/src/cluster_v2/services/registry.js +36 -0
  44. package/dist/src/cluster_v2/services/twitter/collector.js +1055 -0
  45. package/dist/src/cluster_v2/services/twitter/index.js +1 -0
  46. package/dist/src/config/digest.js +78 -0
  47. package/dist/src/config/discord.js +143 -0
  48. package/dist/src/config/image-gen.js +48 -0
  49. package/dist/src/config/mono-pilot.js +31 -0
  50. package/dist/src/config/twitter.js +100 -0
  51. package/dist/src/extensions/cluster.js +311 -0
  52. package/dist/src/extensions/commands/build-memory.js +76 -0
  53. package/dist/src/extensions/commands/digest/backfill.js +779 -0
  54. package/dist/src/extensions/commands/digest/index.js +1133 -0
  55. package/dist/src/extensions/commands/image-model.js +214 -0
  56. package/dist/src/extensions/game/bus-injection.js +47 -0
  57. package/dist/src/extensions/game/identity.js +83 -0
  58. package/dist/src/extensions/game/mailbox.js +61 -0
  59. package/dist/src/extensions/game/system-prompt.js +134 -0
  60. package/dist/src/extensions/game/tools.js +28 -0
  61. package/dist/src/extensions/lifecycle.js +337 -0
  62. package/dist/src/extensions/mode-runtime.js +26 -2
  63. package/dist/src/extensions/mono-game.js +66 -0
  64. package/dist/src/extensions/mono-pilot.js +100 -18
  65. package/dist/src/extensions/nvim.js +47 -0
  66. package/dist/src/extensions/session-hints.js +1 -2
  67. package/dist/src/extensions/sftp.js +897 -0
  68. package/dist/src/extensions/status.js +676 -0
  69. package/dist/src/extensions/system-events.js +478 -0
  70. package/dist/src/extensions/system-prompt.js +24 -14
  71. package/dist/src/extensions/user-message.js +70 -1
  72. package/dist/src/lsp/client.js +235 -0
  73. package/dist/src/lsp/index.js +165 -0
  74. package/dist/src/lsp/runtime.js +67 -0
  75. package/dist/src/lsp/server.js +242 -0
  76. package/dist/src/memory/build-memory.js +103 -0
  77. package/dist/src/memory/config/defaults.js +55 -0
  78. package/dist/src/memory/config/loader.js +29 -0
  79. package/dist/src/memory/config/paths.js +9 -0
  80. package/dist/src/memory/config/resolve.js +90 -0
  81. package/dist/src/memory/config/types.js +1 -0
  82. package/dist/src/memory/embeddings/batch-runner.js +39 -0
  83. package/dist/src/memory/embeddings/cache.js +47 -0
  84. package/dist/src/memory/embeddings/chunk-limits.js +26 -0
  85. package/dist/src/memory/embeddings/input-limits.js +48 -0
  86. package/dist/src/memory/embeddings/local.js +108 -0
  87. package/dist/src/memory/embeddings/types.js +1 -0
  88. package/dist/src/memory/index-manager.js +552 -0
  89. package/dist/src/memory/indexing/embeddings.js +67 -0
  90. package/dist/src/memory/indexing/files.js +180 -0
  91. package/dist/src/memory/indexing/index-file.js +105 -0
  92. package/dist/src/memory/log.js +38 -0
  93. package/dist/src/memory/paths.js +15 -0
  94. package/dist/src/memory/runtime/index.js +299 -0
  95. package/dist/src/memory/runtime/thread.js +116 -0
  96. package/dist/src/memory/search/fts.js +57 -0
  97. package/dist/src/memory/search/hybrid.js +50 -0
  98. package/dist/src/memory/search/text.js +30 -0
  99. package/dist/src/memory/search/vector.js +43 -0
  100. package/dist/src/memory/session/content-hash.js +7 -0
  101. package/dist/src/memory/session/entry.js +33 -0
  102. package/dist/src/memory/session/flush-policy.js +34 -0
  103. package/dist/src/memory/session/hook.js +191 -0
  104. package/dist/src/memory/session/paths.js +15 -0
  105. package/dist/src/memory/session/session-reader.js +88 -0
  106. package/dist/src/memory/session/transcript/content-hash.js +7 -0
  107. package/dist/src/memory/session/transcript/entry.js +28 -0
  108. package/dist/src/memory/session/transcript/flush.js +56 -0
  109. package/dist/src/memory/session/transcript/paths.js +28 -0
  110. package/dist/src/memory/session/transcript/reader.js +112 -0
  111. package/dist/src/memory/session/transcript/state.js +31 -0
  112. package/dist/src/memory/store/schema.js +89 -0
  113. package/dist/src/memory/store/sqlite.js +89 -0
  114. package/dist/src/memory/types.js +1 -0
  115. package/dist/src/memory/warm.js +25 -0
  116. package/dist/{tools → src/tools}/README.md +28 -2
  117. package/dist/{tools → src/tools}/apply-patch-description.md +8 -2
  118. package/dist/{tools → src/tools}/apply-patch.js +174 -104
  119. package/dist/{tools → src/tools}/apply-patch.test.js +52 -1
  120. package/dist/{tools/ask-question.js → src/tools/ask-user-question.js} +3 -3
  121. package/dist/src/tools/ast-grep.js +357 -0
  122. package/dist/src/tools/brief-write.js +122 -0
  123. package/dist/src/tools/bus-send.js +100 -0
  124. package/dist/{tools → src/tools}/call-mcp-tool.js +20 -24
  125. package/dist/src/tools/codex-apply-patch-description.md +52 -0
  126. package/dist/src/tools/codex-apply-patch.js +540 -0
  127. package/dist/{tools → src/tools}/delete.js +24 -0
  128. package/dist/src/tools/exit-plan-mode.js +83 -0
  129. package/dist/{tools → src/tools}/fetch-mcp-resource.js +31 -3
  130. package/dist/src/tools/generate-image.js +567 -0
  131. package/dist/{tools → src/tools}/glob.js +55 -1
  132. package/dist/{tools → src/tools}/list-mcp-resources.js +32 -3
  133. package/dist/{tools → src/tools}/list-mcp-tools.js +38 -3
  134. package/dist/src/tools/ls.js +48 -0
  135. package/dist/src/tools/lsp-diagnostics.js +67 -0
  136. package/dist/src/tools/lsp-symbols.js +54 -0
  137. package/dist/src/tools/mailbox.js +85 -0
  138. package/dist/src/tools/memory-get.js +90 -0
  139. package/dist/src/tools/memory-search.js +180 -0
  140. package/dist/{tools → src/tools}/plan-mode-reminder.md +3 -4
  141. package/dist/{tools → src/tools}/read-file.js +8 -19
  142. package/dist/{tools → src/tools}/rg.js +10 -20
  143. package/dist/{tools → src/tools}/shell.js +19 -42
  144. package/dist/{tools → src/tools}/subagent.js +255 -6
  145. package/dist/{tools → src/tools}/switch-mode.js +37 -6
  146. package/dist/{tools → src/tools}/web-fetch.js +105 -7
  147. package/dist/{tools → src/tools}/web-search.js +29 -1
  148. package/package.json +21 -9
  149. package/dist/src/utils/mcp-client.js +0 -282
  150. /package/dist/{tools → src/tools}/ask-mode-reminder.md +0 -0
  151. /package/dist/{tools → src/tools}/rg.test.js +0 -0
  152. /package/dist/{tools → src/tools}/semantic-search-description.md +0 -0
  153. /package/dist/{tools → src/tools}/semantic-search.js +0 -0
  154. /package/dist/{tools → src/tools}/shell-description.md +0 -0
  155. /package/dist/{tools → src/tools}/subagent-description.md +0 -0
@@ -0,0 +1,337 @@
1
+ import { LSP } from "../lsp/index.js";
2
+ import { deriveAgentId } from "../agents-paths.js";
3
+ import { loadResolvedMemorySearchConfig } from "../memory/config/loader.js";
4
+ import { initCluster, closeCluster } from "../cluster/init.js";
5
+ import { getActiveClusterV2Service, initClusterV2, closeClusterV2, onClusterV2DiscordChannelBatch, onClusterV2LeaderOffline, onClusterV2LeaderRecovered, onClusterV2TwitterCollectorStartupFailed, onClusterV2TwitterPullBatch, onClusterV2TwitterPullFailed, } from "../cluster_v2/index.js";
6
+ import { setMemoryWorkersEmbeddingProvider, closeMemorySearchManagers } from "../memory/runtime/index.js";
7
+ import { warmMemorySearch } from "../memory/warm.js";
8
+ import { setMailBoxHandle } from "./game/mailbox.js";
9
+ import { publishSystemEvent } from "./system-events.js";
10
+ let activeClusterVersion = null;
11
+ /**
12
+ * Initialize all subsystems. Fire-and-forget from session_start.
13
+ */
14
+ export async function initSubsystems(pi, ctx, options) {
15
+ const agentId = deriveAgentId(ctx.cwd);
16
+ const sessionManager = ctx.sessionManager;
17
+ const getSessionId = () => sessionManager?.getSessionId?.() ?? "unknown";
18
+ const disposers = [];
19
+ // 1. LSP
20
+ LSP.init(ctx.cwd);
21
+ // 2. Cluster + 3. Memory (cluster provides embedding for memory)
22
+ const settings = await loadResolvedMemorySearchConfig();
23
+ let cluster = null;
24
+ const useClusterV2 = process.env.MONO_PILOT_CLUSTER_VERSION === "2" || process.env.MONO_PILOT_CLUSTER_V2 === "1";
25
+ if (useClusterV2) {
26
+ disposers.push(onClusterV2DiscordChannelBatch((event) => {
27
+ publishDiscordChannelBatchSystemEvent(ctx, event);
28
+ }));
29
+ disposers.push(onClusterV2TwitterPullBatch((event) => {
30
+ publishTwitterPullBatchSystemEvent(ctx, event);
31
+ }));
32
+ disposers.push(onClusterV2TwitterPullFailed((event) => {
33
+ publishTwitterPullFailedSystemEvent(ctx, event);
34
+ }));
35
+ disposers.push(onClusterV2TwitterCollectorStartupFailed((event) => {
36
+ publishTwitterCollectorStartupFailedSystemEvent(ctx, event);
37
+ }));
38
+ disposers.push(onClusterV2LeaderOffline(() => {
39
+ publishSystemEvent({
40
+ source: "cluster",
41
+ level: "warning",
42
+ message: "Leader offline. Re-election in progress.",
43
+ dedupeKey: "cluster|leader_offline",
44
+ toast: false,
45
+ ctx,
46
+ });
47
+ }));
48
+ disposers.push(onClusterV2LeaderRecovered(() => {
49
+ refreshMemoryEmbeddingProviderFromActiveClusterV2();
50
+ void publishClusterV2LeaderRecoveredEvent(ctx);
51
+ }));
52
+ }
53
+ try {
54
+ if (settings.enabled && settings.provider === "local") {
55
+ if (useClusterV2) {
56
+ cluster = await initClusterV2({
57
+ ...settings.local,
58
+ agentId,
59
+ displayName: options?.displayName,
60
+ getSessionId,
61
+ });
62
+ activeClusterVersion = "v2";
63
+ }
64
+ else {
65
+ cluster = await initCluster({
66
+ ...settings.local,
67
+ agentId,
68
+ displayName: options?.displayName,
69
+ getSessionId,
70
+ });
71
+ activeClusterVersion = "v1";
72
+ }
73
+ setMemoryWorkersEmbeddingProvider(cluster.embedding);
74
+ }
75
+ }
76
+ catch (error) {
77
+ for (const dispose of [...disposers].reverse()) {
78
+ dispose();
79
+ }
80
+ throw error;
81
+ }
82
+ if (settings.enabled && settings.sync.onSessionStart) {
83
+ startMemoryWarmupInBackground(ctx, { workspaceDir: ctx.cwd, agentId });
84
+ }
85
+ // 4. Bus (message injection into agent conversation)
86
+ const bus = cluster?.bus ?? null;
87
+ if (bus) {
88
+ if (options?.busChannels && options.busChannels.length > 0) {
89
+ await bus.subscribe(options.busChannels);
90
+ }
91
+ const filter = options?.busMessageFilter;
92
+ const injector = options?.busMessageInjector ?? createDefaultBusMessageInjector(pi);
93
+ bus.onMessage((msg) => {
94
+ if (filter && !filter(msg))
95
+ return;
96
+ injector(msg);
97
+ });
98
+ }
99
+ setMailBoxHandle(bus ?? null);
100
+ const dispose = disposers.length > 0
101
+ ? () => {
102
+ for (const fn of [...disposers].reverse()) {
103
+ fn();
104
+ }
105
+ }
106
+ : undefined;
107
+ return { bus, dispose };
108
+ }
109
+ /**
110
+ * Shutdown all subsystems in reverse dependency order.
111
+ */
112
+ export async function shutdownSubsystems(handles) {
113
+ try {
114
+ handles?.dispose?.();
115
+ // Bus
116
+ if (handles?.bus)
117
+ handles.bus.close();
118
+ setMailBoxHandle(null);
119
+ // Memory
120
+ await closeMemorySearchManagers();
121
+ // Cluster
122
+ if (activeClusterVersion === "v2") {
123
+ await closeClusterV2();
124
+ }
125
+ else {
126
+ await closeCluster();
127
+ }
128
+ activeClusterVersion = null;
129
+ }
130
+ catch (err) {
131
+ console.warn(`[subsystems] shutdown failed: ${String(err)}`);
132
+ }
133
+ }
134
+ function formatLeaderLabel(leader) {
135
+ const name = leader.displayName?.trim();
136
+ return name ? `${name} (${leader.agentId})` : leader.agentId;
137
+ }
138
+ function buildLeaderKey(leaders) {
139
+ return leaders
140
+ .map((leader) => leader.agentId)
141
+ .sort()
142
+ .join(",");
143
+ }
144
+ function buildLeaderLabel(leaders) {
145
+ return leaders.map((leader) => formatLeaderLabel(leader)).join(", ");
146
+ }
147
+ async function publishClusterV2LeaderRecoveredEvent(ctx) {
148
+ const active = getActiveClusterV2Service();
149
+ const bus = active?.bus ?? null;
150
+ if (!bus) {
151
+ publishSystemEvent({
152
+ source: "cluster",
153
+ level: "info",
154
+ message: "Re-election complete.",
155
+ dedupeKey: "cluster|leader_elected",
156
+ toast: true,
157
+ ctx,
158
+ });
159
+ return;
160
+ }
161
+ try {
162
+ const roster = await bus.roster();
163
+ const leaders = roster.agents.filter((agent) => agent.role === "leader");
164
+ if (leaders.length === 0) {
165
+ publishSystemEvent({
166
+ source: "cluster",
167
+ level: "info",
168
+ message: "Re-election complete.",
169
+ dedupeKey: "cluster|leader_elected",
170
+ toast: true,
171
+ ctx,
172
+ });
173
+ return;
174
+ }
175
+ const leaderKey = buildLeaderKey(leaders);
176
+ const leaderLabel = buildLeaderLabel(leaders);
177
+ publishSystemEvent({
178
+ source: "cluster",
179
+ level: "info",
180
+ message: `Re-election complete. Leader: ${leaderLabel}.`,
181
+ dedupeKey: `cluster|leader_elected|${leaderKey}`,
182
+ toast: true,
183
+ ctx,
184
+ });
185
+ }
186
+ catch {
187
+ publishSystemEvent({
188
+ source: "cluster",
189
+ level: "info",
190
+ message: "Re-election complete.",
191
+ dedupeKey: "cluster|leader_elected",
192
+ toast: true,
193
+ ctx,
194
+ });
195
+ }
196
+ }
197
+ function refreshMemoryEmbeddingProviderFromActiveClusterV2() {
198
+ const active = getActiveClusterV2Service();
199
+ if (!active) {
200
+ return;
201
+ }
202
+ setMemoryWorkersEmbeddingProvider(active.embedding);
203
+ }
204
+ function publishDiscordChannelBatchSystemEvent(ctx, event) {
205
+ const channelLabel = event.channelAlias?.trim() ||
206
+ (event.guildName?.trim() && event.channelName?.trim()
207
+ ? `${event.guildName.trim()} / ${event.channelName.trim()}`
208
+ : event.channelName?.trim()) ||
209
+ event.channelId;
210
+ publishSystemEvent({
211
+ source: "discord",
212
+ level: "info",
213
+ message: `Channel ${channelLabel} collected ${event.count} messages.`,
214
+ dedupeKey: `discord|channel_batch|${event.scope}|${event.channelId}|${event.sequence}`,
215
+ toast: false,
216
+ ctx,
217
+ });
218
+ }
219
+ function publishTwitterPullBatchSystemEvent(ctx, event) {
220
+ publishSystemEvent({
221
+ source: "twitter",
222
+ level: "info",
223
+ message: `For You pull complete: ${event.count}/${event.requestedCount} tweets.`,
224
+ dedupeKey: `twitter|pull_batch|${event.scope}|${event.sequence}`,
225
+ toast: false,
226
+ ctx,
227
+ });
228
+ }
229
+ function publishTwitterPullFailedSystemEvent(ctx, event) {
230
+ const triggerLabel = event.trigger === "startup" ? "startup" : "interval";
231
+ publishSystemEvent({
232
+ source: "twitter",
233
+ level: "warning",
234
+ message: `For You pull failed (${triggerLabel}): ${event.error}`,
235
+ dedupeKey: `twitter|pull_failed|${event.scope}|${event.trigger}`,
236
+ toast: false,
237
+ ctx,
238
+ });
239
+ }
240
+ function publishTwitterCollectorStartupFailedSystemEvent(ctx, event) {
241
+ publishSystemEvent({
242
+ source: "twitter",
243
+ level: "warning",
244
+ message: `Collector startup failed: ${event.error}`,
245
+ dedupeKey: `twitter|collector_start_failed|${event.scope}`,
246
+ toast: false,
247
+ ctx,
248
+ });
249
+ }
250
+ function startMemoryWarmupInBackground(ctx, params) {
251
+ let startNotified = false;
252
+ const notifyWarmupStartIfNeeded = () => {
253
+ if (startNotified) {
254
+ return;
255
+ }
256
+ startNotified = true;
257
+ publishSystemEvent({
258
+ source: "memory",
259
+ level: "info",
260
+ message: "Memory warmup in progress.",
261
+ dedupeKey: `memory|warmup|start|${params.agentId}`,
262
+ toast: false,
263
+ ctx,
264
+ });
265
+ };
266
+ void warmMemorySearch({
267
+ ...params,
268
+ onWorkDetected: notifyWarmupStartIfNeeded,
269
+ })
270
+ .then((result) => {
271
+ if (!result.attempted || !startNotified) {
272
+ return;
273
+ }
274
+ if (result.succeeded) {
275
+ publishSystemEvent({
276
+ source: "memory",
277
+ level: "info",
278
+ message: "Memory warmup complete.",
279
+ dedupeKey: `memory|warmup|done|${params.agentId}`,
280
+ toast: false,
281
+ ctx,
282
+ });
283
+ return;
284
+ }
285
+ publishSystemEvent({
286
+ source: "memory",
287
+ level: "warning",
288
+ message: `Memory warmup failed: ${result.error ?? "unknown error"}`,
289
+ dedupeKey: `memory|warmup|failed|${params.agentId}`,
290
+ toast: false,
291
+ ctx,
292
+ });
293
+ })
294
+ .catch((error) => {
295
+ if (!startNotified) {
296
+ return;
297
+ }
298
+ const message = error instanceof Error ? error.message : String(error);
299
+ publishSystemEvent({
300
+ source: "memory",
301
+ level: "warning",
302
+ message: `Memory warmup failed: ${message}`,
303
+ dedupeKey: `memory|warmup|failed|${params.agentId}`,
304
+ toast: false,
305
+ ctx,
306
+ });
307
+ });
308
+ }
309
+ // --- Bus message injection (debounced) ---
310
+ function createDefaultBusMessageInjector(pi) {
311
+ let pending = [];
312
+ let timer = null;
313
+ function flush() {
314
+ if (pending.length === 0)
315
+ return;
316
+ const msgs = pending;
317
+ pending = [];
318
+ timer = null;
319
+ const lines = msgs.map((m) => {
320
+ const text = typeof m.payload === "object" && m.payload !== null && "text" in m.payload
321
+ ? m.payload.text
322
+ : JSON.stringify(m.payload);
323
+ const ch = m.channel && m.channel !== "public" ? ` [${m.channel}]` : "";
324
+ return `[from ${m.from}${ch}] ${text}`;
325
+ });
326
+ const envelope = "<bus_messages>\n" + lines.join("\n") + "\n</bus_messages>\n\n" +
327
+ "You received the above messages from other agents via the message bus. " +
328
+ "Respond in character. Use the bus_send tool to reply.";
329
+ pi.sendUserMessage(envelope, { deliverAs: "followUp" });
330
+ }
331
+ return (msg) => {
332
+ pending.push(msg);
333
+ if (timer)
334
+ clearTimeout(timer);
335
+ timer = setTimeout(flush, 300);
336
+ };
337
+ }
@@ -1,4 +1,11 @@
1
1
  export const MODE_STATE_ENTRY_TYPE = "switch-mode-state";
2
+ function normalizeOptionalString(value) {
3
+ if (typeof value !== "string") {
4
+ return undefined;
5
+ }
6
+ const trimmed = value.trim();
7
+ return trimmed.length > 0 ? trimmed : undefined;
8
+ }
2
9
  export const PLAN_MODE_STILL_ACTIVE_REMINDER = `<system_reminder>
3
10
  Plan mode is still active. Continue with the task in the current mode.
4
11
  </system_reminder>`;
@@ -24,6 +31,7 @@ export function parseModeStateEntry(entry) {
24
31
  return undefined;
25
32
  const state = data;
26
33
  if (state.activeMode === "plan" || state.activeMode === "agent" || state.activeMode === "ask") {
34
+ const planFilePath = normalizeOptionalString(state.planFilePath);
27
35
  return {
28
36
  activeMode: state.activeMode,
29
37
  pendingReminder: state.pendingReminder === "plan-entry" ||
@@ -31,12 +39,15 @@ export function parseModeStateEntry(entry) {
31
39
  state.pendingReminder === "ask-entry"
32
40
  ? state.pendingReminder
33
41
  : undefined,
42
+ planFilePath,
34
43
  };
35
44
  }
36
45
  if (typeof state.planModeActive === "boolean") {
46
+ const planFilePath = normalizeOptionalString(state.planFilePath);
37
47
  return {
38
48
  activeMode: state.planModeActive ? "plan" : "agent",
39
49
  pendingReminder: undefined,
50
+ planFilePath,
40
51
  };
41
52
  }
42
53
  return undefined;
@@ -51,6 +62,7 @@ export function createModeStateData(snapshot) {
51
62
  return {
52
63
  activeMode: snapshot.activeMode,
53
64
  pendingReminder: snapshot.pendingReminder,
65
+ planFilePath: snapshot.planFilePath,
54
66
  planModeActive: snapshot.activeMode === "plan",
55
67
  };
56
68
  }
@@ -64,7 +76,7 @@ export function hasMessageEntries(entries) {
64
76
  }
65
77
  return false;
66
78
  }
67
- export function buildRuntimeEnvelope(userQuery, reminder, mcpInstructions, rulesEnvelope) {
79
+ export function buildRuntimeEnvelope(userQuery, reminder, mcpInstructions, rulesEnvelope, briefReminder) {
68
80
  const sections = [];
69
81
  if (rulesEnvelope) {
70
82
  sections.push(rulesEnvelope.trim());
@@ -75,6 +87,9 @@ export function buildRuntimeEnvelope(userQuery, reminder, mcpInstructions, rules
75
87
  if (reminder) {
76
88
  sections.push(reminder.trim());
77
89
  }
90
+ if (briefReminder) {
91
+ sections.push(briefReminder.trim());
92
+ }
78
93
  sections.push(`<user_query>\n${userQuery}\n</user_query>`);
79
94
  return sections.join("\n\n");
80
95
  }
@@ -86,13 +101,22 @@ class ModeRuntimeStore {
86
101
  getSnapshot() {
87
102
  return { ...this.state };
88
103
  }
89
- setMode(nextMode) {
104
+ setMode(nextMode, options) {
105
+ const nextPlanFilePath = normalizeOptionalString(options?.planFilePath);
90
106
  if (this.state.activeMode === nextMode) {
107
+ if (nextMode === "plan" && nextPlanFilePath !== undefined && nextPlanFilePath !== this.state.planFilePath) {
108
+ this.state.planFilePath = nextPlanFilePath;
109
+ this.state.pendingReminder = "plan-entry";
110
+ return { changed: true, snapshot: this.getSnapshot() };
111
+ }
91
112
  return { changed: false, snapshot: this.getSnapshot() };
92
113
  }
93
114
  this.state.activeMode = nextMode;
94
115
  switch (nextMode) {
95
116
  case "plan":
117
+ if (nextPlanFilePath !== undefined) {
118
+ this.state.planFilePath = nextPlanFilePath;
119
+ }
96
120
  this.state.pendingReminder = "plan-entry";
97
121
  break;
98
122
  case "ask":
@@ -0,0 +1,66 @@
1
+ import { setBusSendDefaultChannel, setBusSendHandle } from "../tools/bus-send.js";
2
+ import registerGameSystemPromptExtension from "./game/system-prompt.js";
3
+ import { initSubsystems, shutdownSubsystems } from "./lifecycle.js";
4
+ import { getGameChannel, getGameGmChannel, loadGameIdentity } from "./game/identity.js";
5
+ import { createGameBusMessageInjector } from "./game/bus-injection.js";
6
+ import { setMailBoxHandle } from "./game/mailbox.js";
7
+ import { resolveGameToolExtensions } from "./game/tools.js";
8
+ import { registerClusterCommands, setClusterCommandDefaultChannel, setClusterHandle, } from "./cluster.js";
9
+ export default function monoGameExtension(pi) {
10
+ pi.registerFlag("game-channel", {
11
+ description: "Override the game channel name",
12
+ type: "string",
13
+ });
14
+ registerGameSystemPromptExtension(pi);
15
+ registerClusterCommands(pi);
16
+ let handles = null;
17
+ pi.on("session_start", async (_event, ctx) => {
18
+ if (handles) {
19
+ setClusterHandle(null);
20
+ setClusterCommandDefaultChannel(undefined);
21
+ setBusSendHandle(null);
22
+ setBusSendDefaultChannel(undefined);
23
+ setMailBoxHandle(null);
24
+ await shutdownSubsystems(handles);
25
+ handles = null;
26
+ }
27
+ const identity = loadGameIdentity(ctx.cwd);
28
+ const channelOverride = pi.getFlag("game-channel");
29
+ const gameChannel = getGameChannel(ctx.cwd, typeof channelOverride === "string" ? channelOverride : undefined);
30
+ const gmChannel = getGameGmChannel(gameChannel);
31
+ const roleTools = resolveGameToolExtensions(identity.tools);
32
+ for (const register of roleTools) {
33
+ register(pi);
34
+ }
35
+ setBusSendDefaultChannel(gameChannel);
36
+ try {
37
+ const h = await initSubsystems(pi, ctx, {
38
+ displayName: identity.displayName,
39
+ busChannels: [gameChannel, gmChannel],
40
+ busMessageFilter: (msg) => {
41
+ if (!msg.channel)
42
+ return false;
43
+ return msg.channel === gmChannel;
44
+ },
45
+ busMessageInjector: createGameBusMessageInjector(pi, { gmChannel }),
46
+ });
47
+ handles = h;
48
+ setClusterHandle(h.bus);
49
+ setClusterCommandDefaultChannel(gameChannel);
50
+ setBusSendHandle(h.bus);
51
+ setMailBoxHandle(h.bus, { gmChannel });
52
+ }
53
+ catch (err) {
54
+ console.warn(`[subsystems] init failed: ${String(err)}`);
55
+ }
56
+ });
57
+ pi.on("session_shutdown", async () => {
58
+ setClusterHandle(null);
59
+ setClusterCommandDefaultChannel(undefined);
60
+ setBusSendHandle(null);
61
+ setBusSendDefaultChannel(undefined);
62
+ setMailBoxHandle(null);
63
+ await shutdownSubsystems(handles);
64
+ handles = null;
65
+ });
66
+ }
@@ -1,45 +1,127 @@
1
- import shellExtension from "../../tools/shell.js";
2
- import globExtension from "../../tools/glob.js";
3
- import rgExtension from "../../tools/rg.js";
4
- import readFileExtension from "../../tools/read-file.js";
5
- import deleteExtension from "../../tools/delete.js";
6
- import semanticSearchExtension from "../../tools/semantic-search.js";
7
- import webSearchExtension from "../../tools/web-search.js";
8
- import webFetchExtension from "../../tools/web-fetch.js";
9
- import askQuestionExtension from "../../tools/ask-question.js";
10
- import subagentExtension from "../../tools/subagent.js";
11
- import listMcpResourcesExtension from "../../tools/list-mcp-resources.js";
12
- import fetchMcpResourceExtension from "../../tools/fetch-mcp-resource.js";
13
- import listMcpToolsExtension from "../../tools/list-mcp-tools.js";
14
- import callMcpToolExtension from "../../tools/call-mcp-tool.js";
15
- import switchModeExtension from "../../tools/switch-mode.js";
16
- import applyPatchExtension from "../../tools/apply-patch.js";
1
+ import { onCompaction } from "../brief/reflection.js";
2
+ import lsExtension from "../tools/ls.js";
3
+ import shellExtension from "../tools/shell.js";
4
+ import globExtension from "../tools/glob.js";
5
+ import rgExtension from "../tools/rg.js";
6
+ import astGrepExtension from "../tools/ast-grep.js";
7
+ import readFileExtension from "../tools/read-file.js";
8
+ import deleteExtension from "../tools/delete.js";
9
+ import semanticSearchExtension from "../tools/semantic-search.js";
10
+ import webSearchExtension from "../tools/web-search.js";
11
+ import webFetchExtension from "../tools/web-fetch.js";
12
+ import generateImageExtension from "../tools/generate-image.js";
13
+ import askUserQuestionExtension from "../tools/ask-user-question.js";
14
+ import subagentExtension from "../tools/subagent.js";
15
+ import listMcpResourcesExtension from "../tools/list-mcp-resources.js";
16
+ import fetchMcpResourceExtension from "../tools/fetch-mcp-resource.js";
17
+ import listMcpToolsExtension from "../tools/list-mcp-tools.js";
18
+ import callMcpToolExtension from "../tools/call-mcp-tool.js";
19
+ import switchModeExtension from "../tools/switch-mode.js";
20
+ import exitPlanModeExtension from "../tools/exit-plan-mode.js";
21
+ // import applyPatchExtension from "../tools/apply-patch.js";
22
+ import codexApplyPatchExtension from "../tools/codex-apply-patch.js";
17
23
  import userMessageExtension from "./user-message.js";
18
24
  import systemPromptExtension from "./system-prompt.js";
19
25
  import sessionHintsExtension from "./session-hints.js";
26
+ import nvimExtension from "./nvim.js";
27
+ import lspDiagnosticsExtension from "../tools/lsp-diagnostics.js";
28
+ import lspSymbolsExtension from "../tools/lsp-symbols.js";
29
+ import briefWriteExtension from "../tools/brief-write.js";
30
+ import memorySearchExtension from "../tools/memory-search.js";
31
+ import memoryGetExtension from "../tools/memory-get.js";
32
+ import busSendExtension, { setBusSendHandle } from "../tools/bus-send.js";
33
+ import mailboxExtension from "../tools/mailbox.js";
34
+ import { registerSessionMemoryHook } from "../memory/session/hook.js";
35
+ import { registerBuildMemoryCommand } from "./commands/build-memory.js";
36
+ import { registerDigestCommand } from "./commands/digest/index.js";
37
+ import { registerClusterCommands, setClusterHandle } from "./cluster.js";
38
+ import { registerStatusCommand } from "./status.js";
39
+ import { registerImageModelCommands } from "./commands/image-model.js";
40
+ import { registerSftpCommands } from "./sftp.js";
41
+ import { registerSystemEvents } from "./system-events.js";
42
+ import { initSubsystems, shutdownSubsystems } from "./lifecycle.js";
20
43
  const toolExtensions = [
21
44
  shellExtension,
22
45
  globExtension,
23
46
  rgExtension,
47
+ astGrepExtension,
24
48
  readFileExtension,
25
49
  deleteExtension,
50
+ lsExtension,
26
51
  semanticSearchExtension,
27
52
  webSearchExtension,
28
53
  webFetchExtension,
29
- askQuestionExtension,
54
+ generateImageExtension,
55
+ askUserQuestionExtension,
30
56
  subagentExtension,
31
57
  listMcpResourcesExtension,
32
58
  fetchMcpResourceExtension,
33
59
  listMcpToolsExtension,
34
60
  callMcpToolExtension,
35
61
  switchModeExtension,
36
- applyPatchExtension,
62
+ exitPlanModeExtension,
63
+ // applyPatchExtension,
64
+ codexApplyPatchExtension,
37
65
  userMessageExtension,
38
66
  systemPromptExtension,
39
67
  sessionHintsExtension,
68
+ nvimExtension,
69
+ lspDiagnosticsExtension,
70
+ lspSymbolsExtension,
71
+ briefWriteExtension,
72
+ memorySearchExtension,
73
+ memoryGetExtension,
74
+ busSendExtension,
75
+ mailboxExtension,
40
76
  ];
41
77
  export default function monoPilotExtension(pi) {
42
78
  for (const register of toolExtensions) {
43
79
  register(pi);
44
80
  }
81
+ registerSessionMemoryHook(pi);
82
+ registerSystemEvents(pi);
83
+ registerBuildMemoryCommand(pi);
84
+ registerDigestCommand(pi);
85
+ registerClusterCommands(pi);
86
+ registerStatusCommand(pi);
87
+ registerImageModelCommands(pi);
88
+ registerSftpCommands(pi);
89
+ let handles = null;
90
+ pi.on("session_start", async (_event, ctx) => {
91
+ if (handles) {
92
+ setClusterHandle(null);
93
+ setBusSendHandle(null);
94
+ await shutdownSubsystems(handles);
95
+ handles = null;
96
+ }
97
+ try {
98
+ const h = await initSubsystems(pi, ctx, {
99
+ busMessageInjector: (msg) => {
100
+ const text = typeof msg.payload === "object" && msg.payload !== null && "text" in msg.payload
101
+ ? msg.payload.text
102
+ : JSON.stringify(msg.payload);
103
+ const sender = msg.fromName ? `${msg.fromName} (${msg.from})` : msg.from;
104
+ const ch = msg.channel && msg.channel !== "public" ? ` [${msg.channel}]` : "";
105
+ const envelope = `<bus_messages>\n[from ${sender}${ch}] ${text}\n</bus_messages>\n\n` +
106
+ "You received the above messages from other agents via the message bus.";
107
+ pi.sendUserMessage(envelope, { deliverAs: "followUp" });
108
+ },
109
+ });
110
+ handles = h;
111
+ setClusterHandle(h.bus);
112
+ setBusSendHandle(h.bus);
113
+ }
114
+ catch (err) {
115
+ console.warn(`[subsystems] init failed: ${String(err)}`);
116
+ }
117
+ });
118
+ pi.on("session_compact", async () => {
119
+ onCompaction();
120
+ });
121
+ pi.on("session_shutdown", async () => {
122
+ setClusterHandle(null);
123
+ setBusSendHandle(null);
124
+ await shutdownSubsystems(handles);
125
+ handles = null;
126
+ });
45
127
  }
@@ -0,0 +1,47 @@
1
+ import { spawnSync } from "node:child_process";
2
+ const NVIM_SHORTCUT = "alt+o";
3
+ function commandExists(command) {
4
+ const probe = process.platform === "win32" ? "where" : "which";
5
+ const result = spawnSync(probe, [command], { stdio: "ignore", shell: process.platform === "win32" });
6
+ return result.status === 0;
7
+ }
8
+ function resolveEditor() {
9
+ if (commandExists("nvim"))
10
+ return "nvim";
11
+ if (commandExists("vim"))
12
+ return "vim";
13
+ return undefined;
14
+ }
15
+ function openWorkspaceInEditor(ctx, editor) {
16
+ const result = spawnSync(editor, [ctx.cwd], {
17
+ stdio: "inherit",
18
+ shell: process.platform === "win32",
19
+ cwd: ctx.cwd,
20
+ });
21
+ if (result.error) {
22
+ return { ok: false, reason: result.error.message };
23
+ }
24
+ if (result.status !== 0) {
25
+ return { ok: false, reason: `${editor} exited with code ${result.status ?? 1}` };
26
+ }
27
+ return { ok: true };
28
+ }
29
+ export default function nvimExtension(pi) {
30
+ pi.registerShortcut(NVIM_SHORTCUT, {
31
+ description: "Open workspace in nvim file explorer (fallback: vim)",
32
+ handler: async (ctx) => {
33
+ if (!ctx.hasUI) {
34
+ return;
35
+ }
36
+ const editor = resolveEditor();
37
+ if (!editor) {
38
+ ctx.ui.notify("nvim/vim not found in PATH.", "warning");
39
+ return;
40
+ }
41
+ const result = openWorkspaceInEditor(ctx, editor);
42
+ if (!result.ok) {
43
+ ctx.ui.notify(`Failed to open ${editor}: ${result.reason}`, "warning");
44
+ }
45
+ },
46
+ });
47
+ }