@femtomc/mu-server 26.2.75 → 26.2.76

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.
@@ -4,6 +4,7 @@ export type HeartbeatProgramSnapshot = {
4
4
  v: 1;
5
5
  program_id: string;
6
6
  title: string;
7
+ prompt: string | null;
7
8
  enabled: boolean;
8
9
  every_ms: number;
9
10
  reason: string;
@@ -45,6 +46,7 @@ export type HeartbeatProgramRegistryOpts = {
45
46
  dispatchWake: (opts: {
46
47
  programId: string;
47
48
  title: string;
49
+ prompt: string | null;
48
50
  reason: string;
49
51
  metadata: Record<string, unknown>;
50
52
  triggeredAtMs: number;
@@ -61,6 +63,7 @@ export declare class HeartbeatProgramRegistry {
61
63
  get(programId: string): Promise<HeartbeatProgramSnapshot | null>;
62
64
  create(opts: {
63
65
  title: string;
66
+ prompt?: string | null;
64
67
  everyMs?: number;
65
68
  reason?: string;
66
69
  enabled?: boolean;
@@ -69,6 +72,7 @@ export declare class HeartbeatProgramRegistry {
69
72
  update(opts: {
70
73
  programId: string;
71
74
  title?: string;
75
+ prompt?: string | null;
72
76
  everyMs?: number;
73
77
  reason?: string;
74
78
  enabled?: boolean;
@@ -1,5 +1,5 @@
1
1
  import { join } from "node:path";
2
- import { FsJsonlStore } from "@femtomc/mu-core/node";
2
+ import { FsJsonlStore, getStorePaths } from "@femtomc/mu-core/node";
3
3
  const HEARTBEAT_PROGRAMS_FILENAME = "heartbeats.jsonl";
4
4
  function defaultNowMs() {
5
5
  return Date.now();
@@ -10,6 +10,15 @@ function sanitizeMetadata(value) {
10
10
  }
11
11
  return { ...value };
12
12
  }
13
+ function normalizePrompt(value) {
14
+ if (typeof value !== "string") {
15
+ return null;
16
+ }
17
+ if (value.trim().length === 0) {
18
+ return null;
19
+ }
20
+ return value;
21
+ }
13
22
  function normalizeProgram(row) {
14
23
  if (!row || typeof row !== "object" || Array.isArray(row)) {
15
24
  return null;
@@ -38,6 +47,7 @@ function normalizeProgram(row) {
38
47
  v: 1,
39
48
  program_id: programId,
40
49
  title,
50
+ prompt: normalizePrompt(record.prompt),
41
51
  enabled: record.enabled !== false,
42
52
  every_ms: everyMs,
43
53
  reason,
@@ -72,7 +82,7 @@ export class HeartbeatProgramRegistry {
72
82
  this.#nowMs = opts.nowMs ?? defaultNowMs;
73
83
  this.#store =
74
84
  opts.store ??
75
- new FsJsonlStore(join(opts.repoRoot, ".mu", HEARTBEAT_PROGRAMS_FILENAME));
85
+ new FsJsonlStore(join(getStorePaths(opts.repoRoot).storeDir, HEARTBEAT_PROGRAMS_FILENAME));
76
86
  }
77
87
  #scheduleId(programId) {
78
88
  return `heartbeat-program:${programId}`;
@@ -146,6 +156,7 @@ export class HeartbeatProgramRegistry {
146
156
  const result = await this.#dispatchWake({
147
157
  programId: program.program_id,
148
158
  title: program.title,
159
+ prompt: program.prompt,
149
160
  reason: heartbeatReason,
150
161
  metadata: { ...program.metadata },
151
162
  triggeredAtMs: nowMs,
@@ -222,6 +233,7 @@ export class HeartbeatProgramRegistry {
222
233
  v: 1,
223
234
  program_id: `hb-${crypto.randomUUID().slice(0, 12)}`,
224
235
  title,
236
+ prompt: normalizePrompt(opts.prompt),
225
237
  enabled: opts.enabled !== false,
226
238
  every_ms: typeof opts.everyMs === "number" && Number.isFinite(opts.everyMs)
227
239
  ? Math.max(0, Math.trunc(opts.everyMs))
@@ -252,6 +264,9 @@ export class HeartbeatProgramRegistry {
252
264
  }
253
265
  program.title = title;
254
266
  }
267
+ if ("prompt" in opts) {
268
+ program.prompt = normalizePrompt(opts.prompt);
269
+ }
255
270
  if (typeof opts.everyMs === "number" && Number.isFinite(opts.everyMs)) {
256
271
  program.every_ms = Math.max(0, Math.trunc(opts.everyMs));
257
272
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- export type { ControlPlaneActivityEvent, ControlPlaneActivityEventKind, ControlPlaneActivityMutationResult, ControlPlaneActivitySnapshot, ControlPlaneActivityStatus, ControlPlaneActivitySupervisorOpts, } from "./activity_supervisor.js";
2
- export { ControlPlaneActivitySupervisor } from "./activity_supervisor.js";
3
1
  export type { MuConfig, MuConfigPatch, MuConfigPresence } from "./config.js";
4
2
  export { applyMuConfigPatch, DEFAULT_MU_CONFIG, getMuConfigPath, muConfigPresence, normalizeMuConfig, readMuConfigFile, redactMuConfigSecrets, writeMuConfigFile, } from "./config.js";
5
3
  export type { ActiveAdapter, ControlPlaneConfig, ControlPlaneHandle, ControlPlaneSessionLifecycle, ControlPlaneSessionMutationAction, ControlPlaneSessionMutationResult, InterRootQueuePolicy, NotifyOperatorsOpts, NotifyOperatorsResult, OrchestrationQueueState, WakeDeliveryEvent, WakeNotifyContext, WakeNotifyDecision, } from "./control_plane_contract.js";
@@ -17,6 +15,7 @@ export type { HeartbeatProgramDispatchResult, HeartbeatProgramOperationResult, H
17
15
  export { HeartbeatProgramRegistry } from "./heartbeat_programs.js";
18
16
  export type { ActivityHeartbeatSchedulerOpts, HeartbeatRunResult, HeartbeatTickHandler, } from "./heartbeat_scheduler.js";
19
17
  export { ActivityHeartbeatScheduler } from "./heartbeat_scheduler.js";
18
+ export { MemoryIndexMaintainer } from "./memory_index_maintainer.js";
20
19
  export type { ServerContext, ServerInstanceOptions, ServerOptions, ServerRuntime, ServerRuntimeCapabilities, ServerRuntimeOptions, } from "./server.js";
21
20
  export { composeServerRuntime, createContext, createServerFromRuntime } from "./server.js";
22
21
  export type { ShellCommandResult, ShellCommandRunner } from "./session_lifecycle.js";
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- export { ControlPlaneActivitySupervisor } from "./activity_supervisor.js";
2
1
  export { applyMuConfigPatch, DEFAULT_MU_CONFIG, getMuConfigPath, muConfigPresence, normalizeMuConfig, readMuConfigFile, redactMuConfigSecrets, writeMuConfigFile, } from "./config.js";
3
2
  export { DEFAULT_INTER_ROOT_QUEUE_POLICY, normalizeInterRootQueuePolicy, ORCHESTRATION_QUEUE_ALLOWED_TRANSITIONS, ORCHESTRATION_QUEUE_INVARIANTS, } from "./control_plane_contract.js";
4
3
  export { DurableRunQueue, queueStatesForRunStatusFilter, reconcileRunQueue, RUN_QUEUE_RECONCILE_INVARIANTS, runQueuePath, runSnapshotFromQueueSnapshot, runStatusFromQueueState, } from "./run_queue.js";
@@ -8,5 +7,6 @@ export { computeNextScheduleRunAtMs, normalizeCronSchedule } from "./cron_schedu
8
7
  export { CronTimerRegistry } from "./cron_timer.js";
9
8
  export { HeartbeatProgramRegistry } from "./heartbeat_programs.js";
10
9
  export { ActivityHeartbeatScheduler } from "./heartbeat_scheduler.js";
10
+ export { MemoryIndexMaintainer } from "./memory_index_maintainer.js";
11
11
  export { composeServerRuntime, createContext, createServerFromRuntime } from "./server.js";
12
12
  export { createProcessSessionLifecycle } from "./session_lifecycle.js";
@@ -0,0 +1,15 @@
1
+ import { type EventLog } from "@femtomc/mu-core/node";
2
+ import type { MuConfig } from "./config.js";
3
+ import type { ActivityHeartbeatScheduler } from "./heartbeat_scheduler.js";
4
+ export declare class MemoryIndexMaintainer {
5
+ #private;
6
+ constructor(opts: {
7
+ repoRoot: string;
8
+ heartbeatScheduler: ActivityHeartbeatScheduler;
9
+ eventLog: EventLog;
10
+ loadConfigFromDisk: () => Promise<MuConfig>;
11
+ fallbackConfig: MuConfig;
12
+ });
13
+ start(): void;
14
+ stop(): void;
15
+ }
@@ -0,0 +1,165 @@
1
+ import { runContextIndexRebuild, runContextIndexStatus, } from "@femtomc/mu-core/node";
2
+ const MEMORY_INDEX_ACTIVITY_ID = "maintenance:memory-index";
3
+ function describeError(err) {
4
+ if (err instanceof Error) {
5
+ return err.message;
6
+ }
7
+ return String(err);
8
+ }
9
+ function normalizeEveryMs(value) {
10
+ if (!Number.isFinite(value)) {
11
+ return 300_000;
12
+ }
13
+ return Math.max(1_000, Math.min(86_400_000, Math.trunc(value)));
14
+ }
15
+ function maintainerConfigFromMuConfig(config) {
16
+ return {
17
+ enabled: config.control_plane.memory_index.enabled,
18
+ every_ms: normalizeEveryMs(config.control_plane.memory_index.every_ms),
19
+ };
20
+ }
21
+ export class MemoryIndexMaintainer {
22
+ #repoRoot;
23
+ #scheduler;
24
+ #eventLog;
25
+ #loadConfigFromDisk;
26
+ #fallbackConfig;
27
+ #currentEveryMs;
28
+ #started = false;
29
+ constructor(opts) {
30
+ this.#repoRoot = opts.repoRoot;
31
+ this.#scheduler = opts.heartbeatScheduler;
32
+ this.#eventLog = opts.eventLog;
33
+ this.#loadConfigFromDisk = opts.loadConfigFromDisk;
34
+ this.#fallbackConfig = maintainerConfigFromMuConfig(opts.fallbackConfig);
35
+ this.#currentEveryMs = this.#fallbackConfig.every_ms;
36
+ }
37
+ #register(everyMs) {
38
+ this.#currentEveryMs = everyMs;
39
+ this.#scheduler.register({
40
+ activityId: MEMORY_INDEX_ACTIVITY_ID,
41
+ everyMs,
42
+ coalesceMs: 0,
43
+ handler: async ({ reason }) => await this.#tick(reason),
44
+ });
45
+ }
46
+ async #emitTickEvent(payload) {
47
+ await this.#eventLog.emit("memory_index.maintenance_tick", {
48
+ source: "mu-server.memory-index-maintainer",
49
+ payload,
50
+ });
51
+ }
52
+ async #emitRebuildEvent(payload) {
53
+ await this.#eventLog.emit("memory_index.rebuild", {
54
+ source: "mu-server.memory-index-maintainer",
55
+ payload,
56
+ });
57
+ }
58
+ async #emitRebuildFailureEvent(payload) {
59
+ await this.#eventLog.emit("memory_index.rebuild_failed", {
60
+ source: "mu-server.memory-index-maintainer",
61
+ payload,
62
+ });
63
+ }
64
+ async #readConfig() {
65
+ try {
66
+ const config = await this.#loadConfigFromDisk();
67
+ return maintainerConfigFromMuConfig(config);
68
+ }
69
+ catch {
70
+ return this.#fallbackConfig;
71
+ }
72
+ }
73
+ async #tick(reasonRaw) {
74
+ const reason = typeof reasonRaw === "string" && reasonRaw.trim().length > 0 ? reasonRaw.trim() : "scheduled";
75
+ const cfg = await this.#readConfig();
76
+ if (cfg.every_ms !== this.#currentEveryMs) {
77
+ this.#register(cfg.every_ms);
78
+ await this.#emitTickEvent({
79
+ reason,
80
+ action: "rescheduled",
81
+ every_ms: cfg.every_ms,
82
+ enabled: cfg.enabled,
83
+ });
84
+ return { status: "skipped", reason: "rescheduled" };
85
+ }
86
+ if (!cfg.enabled) {
87
+ await this.#emitTickEvent({
88
+ reason,
89
+ action: "disabled",
90
+ every_ms: cfg.every_ms,
91
+ enabled: cfg.enabled,
92
+ });
93
+ return { status: "skipped", reason: "disabled" };
94
+ }
95
+ let status;
96
+ try {
97
+ status = await runContextIndexStatus({ repoRoot: this.#repoRoot });
98
+ }
99
+ catch (err) {
100
+ const error = describeError(err);
101
+ await this.#emitRebuildFailureEvent({
102
+ reason,
103
+ action: "status_failed",
104
+ error,
105
+ });
106
+ return { status: "failed", reason: error };
107
+ }
108
+ const needsRebuild = !status.exists || status.stale_source_count > 0;
109
+ if (!needsRebuild) {
110
+ await this.#emitTickEvent({
111
+ reason,
112
+ action: "up_to_date",
113
+ enabled: cfg.enabled,
114
+ every_ms: cfg.every_ms,
115
+ total_count: status.total_count,
116
+ stale_source_count: status.stale_source_count,
117
+ });
118
+ return { status: "skipped", reason: "up_to_date" };
119
+ }
120
+ const startedAtMs = Date.now();
121
+ try {
122
+ const rebuild = await runContextIndexRebuild({
123
+ repoRoot: this.#repoRoot,
124
+ search: new URLSearchParams(),
125
+ });
126
+ await this.#emitRebuildEvent({
127
+ reason,
128
+ action: "rebuilt",
129
+ duration_ms: Math.max(0, Date.now() - startedAtMs),
130
+ indexed_count: rebuild.indexed_count,
131
+ total_count: rebuild.total_count,
132
+ stale_source_count: rebuild.stale_source_count,
133
+ source_count: rebuild.source_count,
134
+ });
135
+ return { status: "ran" };
136
+ }
137
+ catch (err) {
138
+ const error = describeError(err);
139
+ await this.#emitRebuildFailureEvent({
140
+ reason,
141
+ action: "rebuild_failed",
142
+ duration_ms: Math.max(0, Date.now() - startedAtMs),
143
+ error,
144
+ index_exists: status.exists,
145
+ stale_source_count: status.stale_source_count,
146
+ });
147
+ return { status: "failed", reason: error };
148
+ }
149
+ }
150
+ start() {
151
+ if (this.#started) {
152
+ return;
153
+ }
154
+ this.#started = true;
155
+ this.#register(this.#currentEveryMs);
156
+ this.#scheduler.requestNow(MEMORY_INDEX_ACTIVITY_ID, { reason: "startup", coalesceMs: 0 });
157
+ }
158
+ stop() {
159
+ if (!this.#started) {
160
+ return;
161
+ }
162
+ this.#started = false;
163
+ this.#scheduler.unregister(MEMORY_INDEX_ACTIVITY_ID);
164
+ }
165
+ }
package/dist/run_queue.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { join } from "node:path";
2
- import { FsJsonlStore } from "@femtomc/mu-core/node";
2
+ import { FsJsonlStore, getStorePaths } from "@femtomc/mu-core/node";
3
3
  import { INTER_ROOT_QUEUE_RECONCILE_INVARIANTS, ORCHESTRATION_QUEUE_ALLOWED_TRANSITIONS, reconcileInterRootQueue, } from "./orchestration_queue.js";
4
4
  const RUN_QUEUE_FILENAME = "run_queue.jsonl";
5
5
  const DEFAULT_MAX_STEPS = 20;
@@ -204,7 +204,7 @@ export function reconcileRunQueue(rows, policy) {
204
204
  return reconcileInterRootQueue(rows, policy);
205
205
  }
206
206
  export function runQueuePath(repoRoot) {
207
- return join(repoRoot, ".mu", "control-plane", RUN_QUEUE_FILENAME);
207
+ return join(getStorePaths(repoRoot).storeDir, "control-plane", RUN_QUEUE_FILENAME);
208
208
  }
209
209
  function stableCompare(a, b) {
210
210
  if (a.created_at_ms !== b.created_at_ms) {
@@ -70,8 +70,8 @@ export declare class ControlPlaneRunSupervisor {
70
70
  #private;
71
71
  constructor(opts: ControlPlaneRunSupervisorOpts);
72
72
  /**
73
- * Compatibility adapter entrypoint for run-start intent.
74
- * Default architecture should enqueue first, then call this after lease acquisition.
73
+ * Queue-launch entrypoint for run-start intent.
74
+ * Expected call path: enqueue/activate first, then invoke after lease acquisition.
75
75
  */
76
76
  launchStart(opts: {
77
77
  prompt: string;
@@ -81,8 +81,8 @@ export declare class ControlPlaneRunSupervisor {
81
81
  source?: "command" | "api";
82
82
  }): Promise<ControlPlaneRunSnapshot>;
83
83
  /**
84
- * Compatibility adapter entrypoint for run-resume intent.
85
- * No feature-flag branch: queue-first reconcile remains the canonical path.
84
+ * Queue-launch entrypoint for run-resume intent.
85
+ * Queue-first reconcile remains the canonical execution path.
86
86
  */
87
87
  launchResume(opts: {
88
88
  rootIssueId: string;
@@ -1,5 +1,6 @@
1
1
  import { readdir } from "node:fs/promises";
2
2
  import { join, relative } from "node:path";
3
+ import { getStorePaths } from "@femtomc/mu-core/node";
3
4
  const DEFAULT_MAX_STEPS = 20;
4
5
  const ROOT_RE = /\bRoot:\s*(mu-[a-z0-9][a-z0-9-]*)\b/i;
5
6
  const STEP_RE = /^(Step|Done)\s+\d+\/\d+\s+/;
@@ -275,8 +276,8 @@ export class ControlPlaneRunSupervisor {
275
276
  return this.#snapshot(job);
276
277
  }
277
278
  /**
278
- * Compatibility adapter entrypoint for run-start intent.
279
- * Default architecture should enqueue first, then call this after lease acquisition.
279
+ * Queue-launch entrypoint for run-start intent.
280
+ * Expected call path: enqueue/activate first, then invoke after lease acquisition.
280
281
  */
281
282
  async launchStart(opts) {
282
283
  const prompt = opts.prompt.trim();
@@ -297,8 +298,8 @@ export class ControlPlaneRunSupervisor {
297
298
  });
298
299
  }
299
300
  /**
300
- * Compatibility adapter entrypoint for run-resume intent.
301
- * No feature-flag branch: queue-first reconcile remains the canonical path.
301
+ * Queue-launch entrypoint for run-resume intent.
302
+ * Queue-first reconcile remains the canonical execution path.
302
303
  */
303
304
  async launchResume(opts) {
304
305
  const rootIssueId = normalizeIssueId(opts.rootIssueId);
@@ -355,7 +356,7 @@ export class ControlPlaneRunSupervisor {
355
356
  const rootIssueId = job.snapshot.root_issue_id;
356
357
  const traceFiles = [];
357
358
  if (rootIssueId) {
358
- const rootLogsDir = join(this.#repoRoot, ".mu", "logs", rootIssueId);
359
+ const rootLogsDir = join(getStorePaths(this.#repoRoot).logsDir, rootIssueId);
359
360
  try {
360
361
  const entries = await readdir(rootLogsDir, { withFileTypes: true });
361
362
  for (const entry of entries) {
package/dist/server.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { GenerationTelemetryRecorder } from "@femtomc/mu-control-plane";
2
2
  import type { EventEnvelope, JsonlStore } from "@femtomc/mu-core";
3
3
  import { EventLog } from "@femtomc/mu-core/node";
4
- import { ControlPlaneActivitySupervisor } from "./activity_supervisor.js";
5
4
  import { type MuConfig } from "./config.js";
6
5
  import type { ControlPlaneHandle, ControlPlaneSessionLifecycle } from "./control_plane_contract.js";
7
6
  import { type ConfigReader, type ConfigWriter, type ControlPlaneReloader, type ControlPlaneReloadResult, type ControlPlaneSummary } from "./control_plane_reload.js";
@@ -15,7 +14,6 @@ export type ServerOptions = {
15
14
  port?: number;
16
15
  controlPlane?: ControlPlaneHandle | null;
17
16
  heartbeatScheduler?: ActivityHeartbeatScheduler;
18
- activitySupervisor?: ControlPlaneActivitySupervisor;
19
17
  controlPlaneReloader?: ControlPlaneReloader;
20
18
  generationTelemetry?: GenerationTelemetryRecorder;
21
19
  operatorWakeCoalesceMs?: number;
@@ -39,7 +37,6 @@ export declare function createServerFromRuntime(runtime: ServerRuntime, options?
39
37
  fetch: (request: Request) => Promise<Response>;
40
38
  hostname: string;
41
39
  controlPlane: ControlPlaneHandle;
42
- activitySupervisor: ControlPlaneActivitySupervisor;
43
40
  heartbeatPrograms: import("./heartbeat_programs.js").HeartbeatProgramRegistry;
44
41
  cronPrograms: import("./cron_programs.js").CronProgramRegistry;
45
42
  };
package/dist/server.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { GenerationTelemetryRecorder, presentPipelineResultMessage, } from "@femtomc/mu-control-plane";
2
2
  import { currentRunId, EventLog, FsJsonlStore, getStorePaths, JsonlEventSink } from "@femtomc/mu-core/node";
3
- import { ControlPlaneActivitySupervisor } from "./activity_supervisor.js";
4
3
  import { DEFAULT_MU_CONFIG, readMuConfigFile, writeMuConfigFile } from "./config.js";
5
4
  import { bootstrapControlPlane } from "./control_plane.js";
6
5
  import { createReloadManager, } from "./control_plane_reload.js";
7
6
  import { ActivityHeartbeatScheduler } from "./heartbeat_scheduler.js";
8
7
  import { createProcessSessionLifecycle } from "./session_lifecycle.js";
8
+ import { MemoryIndexMaintainer } from "./memory_index_maintainer.js";
9
9
  import { createServerProgramOrchestration } from "./server_program_orchestration.js";
10
10
  import { createServerRequestHandler } from "./server_routing.js";
11
11
  import { toNonNegativeInt } from "./server_types.js";
@@ -32,6 +32,10 @@ function stringField(payload, key) {
32
32
  const trimmed = value.trim();
33
33
  return trimmed.length > 0 ? trimmed : null;
34
34
  }
35
+ function stringFieldRaw(payload, key) {
36
+ const value = payload[key];
37
+ return typeof value === "string" ? value : null;
38
+ }
35
39
  function numberField(payload, key) {
36
40
  const value = payload[key];
37
41
  if (typeof value !== "number" || !Number.isFinite(value)) {
@@ -73,15 +77,25 @@ function extractWakeTurnReply(turnResult) {
73
77
  function buildWakeTurnCommandText(opts) {
74
78
  const wakeSource = stringField(opts.payload, "wake_source") ?? "unknown";
75
79
  const programId = stringField(opts.payload, "program_id") ?? "unknown";
80
+ const title = stringField(opts.payload, "title") ?? "(untitled wake program)";
76
81
  const reason = stringField(opts.payload, "reason") ?? "scheduled";
82
+ const prompt = stringFieldRaw(opts.payload, "prompt");
83
+ const wakeInstruction = prompt ?? opts.message;
77
84
  const payloadSnapshot = stablePayloadSnapshot(opts.payload);
78
85
  return [
79
86
  "Autonomous wake turn triggered by heartbeat/cron scheduler.",
80
87
  `wake_id=${opts.wakeId}`,
81
88
  `wake_source=${wakeSource}`,
82
89
  `program_id=${programId}`,
83
- `reason=${reason}`,
84
- `trigger_message=${opts.message}`,
90
+ `wake_title=${title}`,
91
+ `wake_reason=${reason}`,
92
+ "",
93
+ "wake_prompt:",
94
+ prompt ?? "(none)",
95
+ "",
96
+ "wake_instruction:",
97
+ wakeInstruction,
98
+ "",
85
99
  `payload=${payloadSnapshot}`,
86
100
  "",
87
101
  "If action is needed, produce exactly one `/mu ...` command. If no action is needed, return a short operator response that can be broadcast verbatim.",
@@ -102,24 +116,6 @@ function createServer(options = {}) {
102
116
  const writeConfig = options.configWriter ?? writeMuConfigFile;
103
117
  const fallbackConfig = options.config ?? DEFAULT_MU_CONFIG;
104
118
  const heartbeatScheduler = options.heartbeatScheduler ?? new ActivityHeartbeatScheduler();
105
- const activitySupervisor = options.activitySupervisor ??
106
- new ControlPlaneActivitySupervisor({
107
- heartbeatScheduler,
108
- onEvent: async (event) => {
109
- await context.eventLog.emit(`activity.${event.kind}`, {
110
- source: "mu-server.activity-supervisor",
111
- payload: {
112
- seq: event.seq,
113
- message: event.message,
114
- activity_id: event.activity.activity_id,
115
- kind: event.activity.kind,
116
- status: event.activity.status,
117
- heartbeat_count: event.activity.heartbeat_count,
118
- last_progress: event.activity.last_progress,
119
- },
120
- });
121
- },
122
- });
123
119
  const operatorWakeCoalesceMs = toNonNegativeInt(options.operatorWakeCoalesceMs, DEFAULT_OPERATOR_WAKE_COALESCE_MS);
124
120
  const operatorWakeLastByKey = new Map();
125
121
  const sessionLifecycle = options.sessionLifecycle ?? createProcessSessionLifecycle({ repoRoot });
@@ -458,6 +454,7 @@ function createServer(options = {}) {
458
454
  const handle = reloadManager.getControlPlaneCurrent();
459
455
  handle?.setWakeDeliveryObserver?.(null);
460
456
  reloadManager.setControlPlaneCurrent(null);
457
+ memoryIndexMaintainer.stop();
461
458
  await handle?.stop();
462
459
  },
463
460
  };
@@ -467,10 +464,17 @@ function createServer(options = {}) {
467
464
  eventLog: context.eventLog,
468
465
  emitOperatorWake,
469
466
  });
467
+ const memoryIndexMaintainer = new MemoryIndexMaintainer({
468
+ repoRoot,
469
+ heartbeatScheduler,
470
+ eventLog: context.eventLog,
471
+ loadConfigFromDisk,
472
+ fallbackConfig,
473
+ });
474
+ memoryIndexMaintainer.start();
470
475
  const handleRequest = createServerRequestHandler({
471
476
  context,
472
477
  controlPlaneProxy,
473
- activitySupervisor,
474
478
  heartbeatPrograms,
475
479
  cronPrograms,
476
480
  loadConfigFromDisk,
@@ -485,7 +489,6 @@ function createServer(options = {}) {
485
489
  fetch: handleRequest,
486
490
  hostname: "0.0.0.0",
487
491
  controlPlane: controlPlaneProxy,
488
- activitySupervisor,
489
492
  heartbeatPrograms,
490
493
  cronPrograms,
491
494
  };
@@ -11,13 +11,16 @@ export function createServerProgramOrchestration(opts) {
11
11
  repoRoot: opts.repoRoot,
12
12
  heartbeatScheduler: opts.heartbeatScheduler,
13
13
  dispatchWake: async (wakeOpts) => {
14
+ const prompt = wakeOpts.prompt && wakeOpts.prompt.trim().length > 0 ? wakeOpts.prompt : null;
14
15
  const wakeResult = await opts.emitOperatorWake({
15
16
  dedupeKey: `heartbeat-program:${wakeOpts.programId}`,
16
- message: `Heartbeat wake: ${wakeOpts.title}`,
17
+ message: prompt ?? `Heartbeat wake: ${wakeOpts.title}`,
17
18
  payload: {
18
19
  wake_source: "heartbeat_program",
19
20
  source_ts_ms: wakeOpts.triggeredAtMs,
20
21
  program_id: wakeOpts.programId,
22
+ title: wakeOpts.title,
23
+ prompt,
21
24
  reason: wakeOpts.reason,
22
25
  metadata: wakeOpts.metadata,
23
26
  },
@@ -55,6 +58,8 @@ export function createServerProgramOrchestration(opts) {
55
58
  wake_source: "cron_program",
56
59
  source_ts_ms: wakeOpts.triggeredAtMs,
57
60
  program_id: wakeOpts.programId,
61
+ title: wakeOpts.title,
62
+ prompt: null,
58
63
  reason: wakeOpts.reason,
59
64
  schedule: wakeOpts.schedule,
60
65
  metadata: wakeOpts.metadata,
@@ -1,4 +1,3 @@
1
- import type { ControlPlaneActivitySupervisor } from "./activity_supervisor.js";
2
1
  import type { MuConfig } from "./config.js";
3
2
  import type { ControlPlaneHandle } from "./control_plane_contract.js";
4
3
  import type { CronProgramRegistry } from "./cron_programs.js";
@@ -7,7 +6,6 @@ import type { ServerContext } from "./server.js";
7
6
  export type ServerRoutingDependencies = {
8
7
  context: ServerContext;
9
8
  controlPlaneProxy: ControlPlaneHandle;
10
- activitySupervisor: ControlPlaneActivitySupervisor;
11
9
  heartbeatPrograms: HeartbeatProgramRegistry;
12
10
  cronPrograms: CronProgramRegistry;
13
11
  loadConfigFromDisk: () => Promise<MuConfig>;
@@ -1,13 +1,6 @@
1
- import { activityRoutes } from "./api/activities.js";
2
- import { configRoutes } from "./api/config.js";
3
1
  import { controlPlaneRoutes } from "./api/control_plane.js";
4
2
  import { cronRoutes } from "./api/cron.js";
5
- import { eventRoutes } from "./api/events.js";
6
3
  import { heartbeatRoutes } from "./api/heartbeats.js";
7
- import { identityRoutes } from "./api/identities.js";
8
- import { runRoutes } from "./api/runs.js";
9
- import { sessionFlashRoutes } from "./api/session_flash.js";
10
- import { sessionTurnRoutes } from "./api/session_turn.js";
11
4
  export function createServerRequestHandler(deps) {
12
5
  return async (request) => {
13
6
  const url = new URL(request.url);
@@ -36,50 +29,15 @@ export function createServerRequestHandler(deps) {
36
29
  }, 100);
37
30
  return Response.json({ ok: true, message: "shutdown initiated" }, { headers });
38
31
  }
39
- if (path === "/api/config") {
40
- return configRoutes(request, url, deps, headers);
41
- }
42
- if (path === "/api/control-plane/reload" ||
43
- path === "/api/control-plane/rollback" ||
44
- path === "/api/control-plane/channels") {
32
+ if (path === "/api/control-plane" || path.startsWith("/api/control-plane/")) {
45
33
  return controlPlaneRoutes(request, url, deps, headers);
46
34
  }
47
- if (path === "/api/status") {
48
- return Response.json({
49
- repo_root: deps.context.repoRoot,
50
- control_plane: deps.getControlPlaneStatus(),
51
- }, { headers });
52
- }
53
- if (path === "/api/session-flash" ||
54
- path === "/api/session-flash/ack" ||
55
- path.startsWith("/api/session-flash/")) {
56
- return sessionFlashRoutes(request, url, deps, headers);
57
- }
58
- if (path === "/api/session-turn") {
59
- return sessionTurnRoutes(request, url, deps, headers);
60
- }
61
- if (path === "/api/runs" || path.startsWith("/api/runs/")) {
62
- return runRoutes(request, url, deps, headers);
63
- }
64
35
  if (path === "/api/cron" || path.startsWith("/api/cron/")) {
65
36
  return cronRoutes(request, url, deps, headers);
66
37
  }
67
38
  if (path === "/api/heartbeats" || path.startsWith("/api/heartbeats/")) {
68
39
  return heartbeatRoutes(request, url, deps, headers);
69
40
  }
70
- if (path === "/api/activities" || path.startsWith("/api/activities/")) {
71
- return activityRoutes(request, url, deps, headers);
72
- }
73
- if (path === "/api/identities" || path === "/api/identities/link" || path === "/api/identities/unlink") {
74
- return identityRoutes(request, url, deps, headers);
75
- }
76
- if (path.startsWith("/api/events")) {
77
- const response = await eventRoutes(request, deps.context);
78
- headers.forEach((value, key) => {
79
- response.headers.set(key, value);
80
- });
81
- return response;
82
- }
83
41
  if (path.startsWith("/webhooks/")) {
84
42
  const response = await deps.controlPlaneProxy.handleWebhook(path, request);
85
43
  if (response) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@femtomc/mu-server",
3
- "version": "26.2.75",
3
+ "version": "26.2.76",
4
4
  "description": "HTTP API server for mu control-plane transport/session plus run/activity scheduling coordination.",
5
5
  "keywords": [
6
6
  "mu",
@@ -30,8 +30,8 @@
30
30
  "start": "bun run dist/cli.js"
31
31
  },
32
32
  "dependencies": {
33
- "@femtomc/mu-agent": "26.2.75",
34
- "@femtomc/mu-control-plane": "26.2.75",
35
- "@femtomc/mu-core": "26.2.75"
33
+ "@femtomc/mu-agent": "26.2.76",
34
+ "@femtomc/mu-control-plane": "26.2.76",
35
+ "@femtomc/mu-core": "26.2.76"
36
36
  }
37
37
  }