@femtomc/mu-server 26.2.72 → 26.2.74

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 (55) hide show
  1. package/README.md +54 -49
  2. package/dist/api/control_plane.js +56 -0
  3. package/dist/api/cron.js +2 -23
  4. package/dist/api/heartbeats.js +1 -66
  5. package/dist/api/identities.js +3 -2
  6. package/dist/api/runs.js +0 -83
  7. package/dist/api/session_flash.d.ts +60 -0
  8. package/dist/api/session_flash.js +326 -0
  9. package/dist/api/session_turn.d.ts +38 -0
  10. package/dist/api/session_turn.js +423 -0
  11. package/dist/config.d.ts +9 -4
  12. package/dist/config.js +24 -24
  13. package/dist/control_plane.d.ts +2 -16
  14. package/dist/control_plane.js +57 -83
  15. package/dist/control_plane_adapter_registry.d.ts +19 -0
  16. package/dist/control_plane_adapter_registry.js +74 -0
  17. package/dist/control_plane_bootstrap_helpers.js +4 -1
  18. package/dist/control_plane_contract.d.ts +3 -12
  19. package/dist/control_plane_contract.js +1 -1
  20. package/dist/control_plane_run_queue_coordinator.d.ts +1 -7
  21. package/dist/control_plane_run_queue_coordinator.js +1 -62
  22. package/dist/control_plane_telegram_generation.js +1 -0
  23. package/dist/control_plane_wake_delivery.js +1 -0
  24. package/dist/cron_programs.d.ts +21 -35
  25. package/dist/cron_programs.js +32 -113
  26. package/dist/cron_request.d.ts +0 -6
  27. package/dist/cron_request.js +0 -41
  28. package/dist/heartbeat_programs.d.ts +20 -35
  29. package/dist/heartbeat_programs.js +26 -122
  30. package/dist/index.d.ts +2 -2
  31. package/dist/orchestration_queue.d.ts +44 -0
  32. package/dist/orchestration_queue.js +111 -0
  33. package/dist/outbound_delivery_router.d.ts +12 -0
  34. package/dist/outbound_delivery_router.js +29 -0
  35. package/dist/run_queue.d.ts +1 -1
  36. package/dist/run_queue.js +78 -79
  37. package/dist/run_supervisor.d.ts +2 -17
  38. package/dist/run_supervisor.js +1 -71
  39. package/dist/server.d.ts +0 -5
  40. package/dist/server.js +95 -127
  41. package/dist/server_program_orchestration.d.ts +4 -19
  42. package/dist/server_program_orchestration.js +49 -200
  43. package/dist/server_routing.d.ts +0 -9
  44. package/dist/server_routing.js +19 -151
  45. package/dist/server_runtime.js +0 -1
  46. package/dist/server_types.d.ts +0 -2
  47. package/dist/server_types.js +0 -7
  48. package/package.json +6 -10
  49. package/dist/api/forum.d.ts +0 -2
  50. package/dist/api/forum.js +0 -75
  51. package/dist/api/issues.d.ts +0 -2
  52. package/dist/api/issues.js +0 -173
  53. package/public/assets/index-CxkevQNh.js +0 -100
  54. package/public/assets/index-D_8anM-D.css +0 -1
  55. package/public/index.html +0 -14
package/dist/run_queue.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { join } from "node:path";
2
2
  import { FsJsonlStore } from "@femtomc/mu-core/node";
3
- import { INTER_ROOT_QUEUE_RECONCILE_INVARIANTS, ORCHESTRATION_QUEUE_ALLOWED_TRANSITIONS, reconcileInterRootQueue, } from "@femtomc/mu-orchestrator";
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;
6
6
  const DEFAULT_MAX_OPERATION_IDS = 128;
@@ -227,6 +227,16 @@ function snapshotClone(value) {
227
227
  applied_operation_ids: [...value.applied_operation_ids],
228
228
  };
229
229
  }
230
+ function normalizeRunStatus(value) {
231
+ if (typeof value !== "string") {
232
+ return null;
233
+ }
234
+ const normalized = value.trim().toLowerCase();
235
+ if (normalized === "running" || normalized === "completed" || normalized === "failed" || normalized === "cancelled") {
236
+ return normalized;
237
+ }
238
+ return null;
239
+ }
230
240
  function normalizeQueueRecordRow(row, nowMs, maxOperationIds) {
231
241
  if (!row || typeof row !== "object" || Array.isArray(row)) {
232
242
  return null;
@@ -235,78 +245,73 @@ function normalizeQueueRecordRow(row, nowMs, maxOperationIds) {
235
245
  const queueId = normalizeString(record.queue_id);
236
246
  const mode = normalizeRunMode(record.mode);
237
247
  const state = normalizeQueueState(record.state);
238
- if (queueId && mode && state) {
239
- const createdAt = normalizeTimestamp(record.created_at_ms, nowMs);
240
- const updatedAt = normalizeTimestamp(record.updated_at_ms, createdAt);
241
- const revision = Math.max(1, normalizeMaxSteps(record.revision, 1));
242
- const dedupeKey = normalizeString(record.dedupe_key) ?? `queue:${queueId}`;
243
- const prompt = normalizePrompt(record.prompt);
244
- const rootIssueId = normalizeIssueId(record.root_issue_id);
245
- return {
246
- snapshot: {
247
- v: 1,
248
- queue_id: queueId,
249
- dedupe_key: dedupeKey,
250
- mode,
251
- state,
252
- prompt,
253
- root_issue_id: rootIssueId,
254
- max_steps: normalizeMaxSteps(record.max_steps, DEFAULT_MAX_STEPS),
255
- command_id: normalizeString(record.command_id),
256
- source: normalizeRunSource(record.source),
257
- job_id: normalizeString(record.job_id),
258
- started_at_ms: normalizeNullableTimestamp(record.started_at_ms),
259
- updated_at_ms: updatedAt,
260
- finished_at_ms: normalizeNullableTimestamp(record.finished_at_ms),
261
- exit_code: normalizeNullableInt(record.exit_code),
262
- pid: normalizeNullableInt(record.pid),
263
- last_progress: normalizeString(record.last_progress),
264
- created_at_ms: createdAt,
265
- revision,
266
- applied_operation_ids: normalizeAppliedOperationIds(record.applied_operation_ids, maxOperationIds),
267
- },
268
- migrated: false,
269
- };
270
- }
271
- const legacyMode = normalizeRunMode(record.mode);
272
- const legacyStatusRaw = typeof record.status === "string" ? record.status.trim().toLowerCase() : "";
273
- const legacyStatus = legacyStatusRaw === "running" ||
274
- legacyStatusRaw === "completed" ||
275
- legacyStatusRaw === "failed" ||
276
- legacyStatusRaw === "cancelled"
277
- ? legacyStatusRaw
278
- : null;
279
- const legacyJobId = normalizeString(record.job_id);
280
- if (!legacyMode || !legacyStatus || !legacyJobId) {
248
+ if (!queueId || !mode || !state) {
249
+ return null;
250
+ }
251
+ const createdAt = normalizeTimestamp(record.created_at_ms, nowMs);
252
+ const updatedAt = normalizeTimestamp(record.updated_at_ms, createdAt);
253
+ const revision = Math.max(1, normalizeMaxSteps(record.revision, 1));
254
+ const dedupeKey = normalizeString(record.dedupe_key) ?? `queue:${queueId}`;
255
+ const prompt = normalizePrompt(record.prompt);
256
+ const rootIssueId = normalizeIssueId(record.root_issue_id);
257
+ return {
258
+ v: 1,
259
+ queue_id: queueId,
260
+ dedupe_key: dedupeKey,
261
+ mode,
262
+ state,
263
+ prompt,
264
+ root_issue_id: rootIssueId,
265
+ max_steps: normalizeMaxSteps(record.max_steps, DEFAULT_MAX_STEPS),
266
+ command_id: normalizeString(record.command_id),
267
+ source: normalizeRunSource(record.source),
268
+ job_id: normalizeString(record.job_id),
269
+ started_at_ms: normalizeNullableTimestamp(record.started_at_ms),
270
+ updated_at_ms: updatedAt,
271
+ finished_at_ms: normalizeNullableTimestamp(record.finished_at_ms),
272
+ exit_code: normalizeNullableInt(record.exit_code),
273
+ pid: normalizeNullableInt(record.pid),
274
+ last_progress: normalizeString(record.last_progress),
275
+ created_at_ms: createdAt,
276
+ revision,
277
+ applied_operation_ids: normalizeAppliedOperationIds(record.applied_operation_ids, maxOperationIds),
278
+ };
279
+ }
280
+ function queueSnapshotFromRunSnapshotRecord(row, nowMs) {
281
+ if (!row || typeof row !== "object" || Array.isArray(row)) {
282
+ return null;
283
+ }
284
+ const record = row;
285
+ const mode = normalizeRunMode(record.mode);
286
+ const status = normalizeRunStatus(record.status);
287
+ const jobId = normalizeString(record.job_id);
288
+ if (!mode || !status || !jobId) {
281
289
  return null;
282
290
  }
283
291
  const createdAt = normalizeTimestamp(record.started_at_ms, nowMs);
284
292
  const updatedAt = normalizeTimestamp(record.updated_at_ms, createdAt);
285
- const queueIdFromLegacy = `legacy-${legacyJobId}`;
293
+ const queueId = normalizeString(record.queue_id) ?? `rq-sync-${jobId}`;
286
294
  return {
287
- snapshot: {
288
- v: 1,
289
- queue_id: queueIdFromLegacy,
290
- dedupe_key: `legacy:${legacyJobId}`,
291
- mode: legacyMode,
292
- state: queueStateFromRunStatus(legacyStatus),
293
- prompt: normalizePrompt(record.prompt),
294
- root_issue_id: normalizeIssueId(record.root_issue_id),
295
- max_steps: normalizeMaxSteps(record.max_steps, DEFAULT_MAX_STEPS),
296
- command_id: normalizeString(record.command_id),
297
- source: normalizeRunSource(record.source),
298
- job_id: legacyJobId,
299
- started_at_ms: normalizeNullableTimestamp(record.started_at_ms),
300
- updated_at_ms: updatedAt,
301
- finished_at_ms: normalizeNullableTimestamp(record.finished_at_ms),
302
- exit_code: normalizeNullableInt(record.exit_code),
303
- pid: normalizeNullableInt(record.pid),
304
- last_progress: normalizeString(record.last_progress),
305
- created_at_ms: createdAt,
306
- revision: 1,
307
- applied_operation_ids: [],
308
- },
309
- migrated: true,
295
+ v: 1,
296
+ queue_id: queueId,
297
+ dedupe_key: `runtime:${jobId}`,
298
+ mode,
299
+ state: queueStateFromRunStatus(status),
300
+ prompt: normalizePrompt(record.prompt),
301
+ root_issue_id: normalizeIssueId(record.root_issue_id),
302
+ max_steps: normalizeMaxSteps(record.max_steps, DEFAULT_MAX_STEPS),
303
+ command_id: normalizeString(record.command_id),
304
+ source: normalizeRunSource(record.source),
305
+ job_id: jobId,
306
+ started_at_ms: normalizeNullableTimestamp(record.started_at_ms),
307
+ updated_at_ms: updatedAt,
308
+ finished_at_ms: normalizeNullableTimestamp(record.finished_at_ms),
309
+ exit_code: normalizeNullableInt(record.exit_code),
310
+ pid: normalizeNullableInt(record.pid),
311
+ last_progress: normalizeString(record.last_progress),
312
+ created_at_ms: createdAt,
313
+ revision: 1,
314
+ applied_operation_ids: [],
310
315
  };
311
316
  }
312
317
  function mergeQueueAndRunSnapshot(queue, run) {
@@ -444,7 +449,6 @@ export class DurableRunQueue {
444
449
  }
445
450
  async #load() {
446
451
  const rows = await this.#store.read();
447
- let migrated = false;
448
452
  const byId = new Map();
449
453
  const nowMs = Math.trunc(this.#nowMs());
450
454
  for (const row of rows) {
@@ -452,22 +456,18 @@ export class DurableRunQueue {
452
456
  if (!normalized) {
453
457
  continue;
454
458
  }
455
- migrated = migrated || normalized.migrated;
456
- const existing = byId.get(normalized.snapshot.queue_id);
459
+ const existing = byId.get(normalized.queue_id);
457
460
  if (!existing) {
458
- byId.set(normalized.snapshot.queue_id, normalized.snapshot);
461
+ byId.set(normalized.queue_id, normalized);
459
462
  continue;
460
463
  }
461
- byId.set(normalized.snapshot.queue_id, chooseLatest(existing, normalized.snapshot));
464
+ byId.set(normalized.queue_id, chooseLatest(existing, normalized));
462
465
  }
463
466
  this.#rowsById.clear();
464
467
  for (const row of byId.values()) {
465
468
  this.#rowsById.set(row.queue_id, row);
466
469
  }
467
470
  this.#rebuildIndexes();
468
- if (migrated) {
469
- await this.#persist();
470
- }
471
471
  }
472
472
  async #persist() {
473
473
  const rows = this.#allRowsSorted().map((row) => snapshotClone(row));
@@ -756,11 +756,10 @@ export class DurableRunQueue {
756
756
  }
757
757
  }
758
758
  if (!row && opts.createIfMissing) {
759
- const migrated = normalizeQueueRecordRow(opts.run, nowMs, this.#maxOperationIds);
760
- if (!migrated) {
759
+ const next = queueSnapshotFromRunSnapshotRecord(opts.run, nowMs);
760
+ if (!next) {
761
761
  return null;
762
762
  }
763
- const next = migrated.snapshot;
764
763
  if (operationId) {
765
764
  this.#rememberOperation(next, operationId);
766
765
  next.revision += 1;
@@ -1,8 +1,6 @@
1
1
  import type { CommandRecord } from "@femtomc/mu-control-plane";
2
- import { ActivityHeartbeatScheduler } from "./heartbeat_scheduler.js";
3
2
  export type ControlPlaneRunMode = "run_start" | "run_resume";
4
3
  export type ControlPlaneRunStatus = "running" | "completed" | "failed" | "cancelled";
5
- export type ControlPlaneRunWakeMode = "immediate" | "next_heartbeat";
6
4
  export type ControlPlaneRunSnapshot = {
7
5
  job_id: string;
8
6
  mode: ControlPlaneRunMode;
@@ -33,12 +31,7 @@ export type ControlPlaneRunInterruptResult = {
33
31
  reason: "not_found" | "not_running" | "missing_target" | null;
34
32
  run: ControlPlaneRunSnapshot | null;
35
33
  };
36
- export type ControlPlaneRunHeartbeatResult = {
37
- ok: boolean;
38
- reason: "not_found" | "not_running" | "missing_target" | null;
39
- run: ControlPlaneRunSnapshot | null;
40
- };
41
- export type ControlPlaneRunEventKind = "run_started" | "run_root_discovered" | "run_progress" | "run_heartbeat" | "run_interrupt_requested" | "run_completed" | "run_failed" | "run_cancelled";
34
+ export type ControlPlaneRunEventKind = "run_started" | "run_root_discovered" | "run_progress" | "run_interrupt_requested" | "run_completed" | "run_failed" | "run_cancelled";
42
35
  export type ControlPlaneRunEvent = {
43
36
  seq: number;
44
37
  ts_ms: number;
@@ -61,8 +54,6 @@ export type ControlPlaneRunSupervisorOpts = {
61
54
  argv: string[];
62
55
  cwd: string;
63
56
  }) => ControlPlaneRunProcess;
64
- heartbeatIntervalMs?: number;
65
- heartbeatScheduler?: ActivityHeartbeatScheduler;
66
57
  maxStoredLines?: number;
67
58
  maxHistory?: number;
68
59
  onEvent?: (event: ControlPlaneRunEvent) => void | Promise<void>;
@@ -73,7 +64,7 @@ export type ControlPlaneRunSupervisorOpts = {
73
64
  * Contract with the durable queue/reconcile layer:
74
65
  * - this supervisor executes already-selected work; it does not decide inter-root scheduling policy
75
66
  * - sequential/parallel root policy is enforced by queue leasing before launch
76
- * - compatibility adapters may call launch directly during migration, but the default path is queue-first
67
+ * - queue-first launch is the supported execution path
77
68
  */
78
69
  export declare class ControlPlaneRunSupervisor {
79
70
  #private;
@@ -112,12 +103,6 @@ export declare class ControlPlaneRunSupervisor {
112
103
  jobId?: string | null;
113
104
  rootIssueId?: string | null;
114
105
  }): ControlPlaneRunInterruptResult;
115
- heartbeat(opts: {
116
- jobId?: string | null;
117
- rootIssueId?: string | null;
118
- reason?: string | null;
119
- wakeMode?: string | null;
120
- }): ControlPlaneRunHeartbeatResult;
121
106
  startFromCommand(command: CommandRecord): Promise<ControlPlaneRunSnapshot | null>;
122
107
  stop(): Promise<void>;
123
108
  }
@@ -1,6 +1,5 @@
1
1
  import { readdir } from "node:fs/promises";
2
2
  import { join, relative } from "node:path";
3
- import { ActivityHeartbeatScheduler } from "./heartbeat_scheduler.js";
4
3
  const DEFAULT_MAX_STEPS = 20;
5
4
  const ROOT_RE = /\bRoot:\s*(mu-[a-z0-9][a-z0-9-]*)\b/i;
6
5
  const STEP_RE = /^(Step|Done)\s+\d+\/\d+\s+/;
@@ -45,13 +44,6 @@ function normalizeIssueId(value) {
45
44
  }
46
45
  return trimmed.toLowerCase();
47
46
  }
48
- function normalizeWakeMode(value) {
49
- if (typeof value !== "string") {
50
- return "immediate";
51
- }
52
- const normalized = value.trim().toLowerCase().replaceAll("-", "_");
53
- return normalized === "next_heartbeat" ? "next_heartbeat" : "immediate";
54
- }
55
47
  function pushBounded(lines, line, maxLines) {
56
48
  lines.push(line);
57
49
  if (lines.length <= maxLines) {
@@ -103,15 +95,12 @@ function describeRun(snapshot) {
103
95
  * Contract with the durable queue/reconcile layer:
104
96
  * - this supervisor executes already-selected work; it does not decide inter-root scheduling policy
105
97
  * - sequential/parallel root policy is enforced by queue leasing before launch
106
- * - compatibility adapters may call launch directly during migration, but the default path is queue-first
98
+ * - queue-first launch is the supported execution path
107
99
  */
108
100
  export class ControlPlaneRunSupervisor {
109
101
  #repoRoot;
110
102
  #nowMs;
111
103
  #spawnProcess;
112
- #heartbeatIntervalMs;
113
- #heartbeatScheduler;
114
- #ownsHeartbeatScheduler;
115
104
  #maxStoredLines;
116
105
  #maxHistory;
117
106
  #onEvent;
@@ -123,9 +112,6 @@ export class ControlPlaneRunSupervisor {
123
112
  this.#repoRoot = opts.repoRoot;
124
113
  this.#nowMs = opts.nowMs ?? defaultNowMs;
125
114
  this.#spawnProcess = opts.spawnProcess ?? defaultSpawnProcess;
126
- this.#heartbeatIntervalMs = Math.max(2_000, Math.trunc(opts.heartbeatIntervalMs ?? 15_000));
127
- this.#heartbeatScheduler = opts.heartbeatScheduler ?? new ActivityHeartbeatScheduler();
128
- this.#ownsHeartbeatScheduler = !opts.heartbeatScheduler;
129
115
  this.#maxStoredLines = Math.max(50, Math.trunc(opts.maxStoredLines ?? 1_000));
130
116
  this.#maxHistory = Math.max(20, Math.trunc(opts.maxHistory ?? 200));
131
117
  this.#onEvent = opts.onEvent ?? null;
@@ -253,7 +239,6 @@ export class ControlPlaneRunSupervisor {
253
239
  stderr_lines: [],
254
240
  log_hints: new Set(),
255
241
  interrupt_requested: false,
256
- next_heartbeat_reason: null,
257
242
  hard_kill_timer: null,
258
243
  };
259
244
  this.#jobsById.set(snapshot.job_id, job);
@@ -261,34 +246,11 @@ export class ControlPlaneRunSupervisor {
261
246
  this.#jobIdByRootIssueId.set(snapshot.root_issue_id, snapshot.job_id);
262
247
  }
263
248
  this.#emit("run_started", job, `🚀 Started ${describeRun(snapshot)} (job ${snapshot.job_id}, pid ${snapshot.pid ?? "?"})`);
264
- this.#heartbeatScheduler.register({
265
- activityId: snapshot.job_id,
266
- everyMs: this.#heartbeatIntervalMs,
267
- handler: async ({ reason }) => {
268
- if (job.snapshot.status !== "running") {
269
- return { status: "skipped", reason: "not_running" };
270
- }
271
- const normalizedReason = reason?.trim();
272
- const heartbeatReason = normalizedReason && normalizedReason.length > 0 && normalizedReason !== "interval"
273
- ? normalizedReason
274
- : job.next_heartbeat_reason;
275
- if (heartbeatReason) {
276
- job.next_heartbeat_reason = null;
277
- }
278
- const elapsedSec = Math.max(0, Math.trunc((this.#nowMs() - job.snapshot.started_at_ms) / 1_000));
279
- const root = job.snapshot.root_issue_id ?? job.snapshot.job_id;
280
- const progress = job.snapshot.last_progress ? ` · ${job.snapshot.last_progress}` : "";
281
- const reasonSuffix = heartbeatReason ? ` · wake=${heartbeatReason}` : "";
282
- this.#emit("run_heartbeat", job, `⏱ ${root} running for ${elapsedSec}s${progress}${reasonSuffix}`);
283
- return { status: "ran" };
284
- },
285
- });
286
249
  void (async () => {
287
250
  const stdoutTask = consumeStreamLines(process.stdout, (line) => this.#handleLine(job, "stdout", line));
288
251
  const stderrTask = consumeStreamLines(process.stderr, (line) => this.#handleLine(job, "stderr", line));
289
252
  const exitCode = await process.exited.catch(() => -1);
290
253
  await Promise.allSettled([stdoutTask, stderrTask]);
291
- this.#heartbeatScheduler.unregister(job.snapshot.job_id);
292
254
  if (job.hard_kill_timer) {
293
255
  clearTimeout(job.hard_kill_timer);
294
256
  job.hard_kill_timer = null;
@@ -451,34 +413,6 @@ export class ControlPlaneRunSupervisor {
451
413
  this.#emit("run_interrupt_requested", job, `⚠️ Interrupt requested for ${root}.`);
452
414
  return { ok: true, reason: null, run: this.#snapshot(job) };
453
415
  }
454
- heartbeat(opts) {
455
- const target = opts.jobId?.trim() || opts.rootIssueId?.trim() || "";
456
- if (target.length === 0) {
457
- return { ok: false, reason: "missing_target", run: null };
458
- }
459
- const job = this.#resolveJob(target);
460
- if (!job) {
461
- return { ok: false, reason: "not_found", run: null };
462
- }
463
- if (job.snapshot.status !== "running") {
464
- return { ok: false, reason: "not_running", run: this.#snapshot(job) };
465
- }
466
- const reason = opts.reason?.trim() || "manual";
467
- const wakeMode = normalizeWakeMode(opts.wakeMode);
468
- if (wakeMode === "next_heartbeat") {
469
- job.next_heartbeat_reason = reason;
470
- this.#touch(job);
471
- return { ok: true, reason: null, run: this.#snapshot(job) };
472
- }
473
- if (reason !== "interval") {
474
- job.next_heartbeat_reason = null;
475
- }
476
- this.#heartbeatScheduler.requestNow(job.snapshot.job_id, {
477
- reason,
478
- coalesceMs: 0,
479
- });
480
- return { ok: true, reason: null, run: this.#snapshot(job) };
481
- }
482
416
  async startFromCommand(command) {
483
417
  switch (command.target_type) {
484
418
  case "run start": {
@@ -508,7 +442,6 @@ export class ControlPlaneRunSupervisor {
508
442
  }
509
443
  async stop() {
510
444
  for (const job of this.#jobsById.values()) {
511
- this.#heartbeatScheduler.unregister(job.snapshot.job_id);
512
445
  if (job.hard_kill_timer) {
513
446
  clearTimeout(job.hard_kill_timer);
514
447
  job.hard_kill_timer = null;
@@ -522,8 +455,5 @@ export class ControlPlaneRunSupervisor {
522
455
  }
523
456
  }
524
457
  }
525
- if (this.#ownsHeartbeatScheduler) {
526
- this.#heartbeatScheduler.stop();
527
- }
528
458
  }
529
459
  }
package/dist/server.d.ts CHANGED
@@ -1,8 +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 { ForumStore } from "@femtomc/mu-forum";
5
- import { IssueStore } from "@femtomc/mu-issue";
6
4
  import { ControlPlaneActivitySupervisor } from "./activity_supervisor.js";
7
5
  import { type MuConfig } from "./config.js";
8
6
  import type { ControlPlaneHandle, ControlPlaneSessionLifecycle } from "./control_plane_contract.js";
@@ -21,7 +19,6 @@ export type ServerOptions = {
21
19
  controlPlaneReloader?: ControlPlaneReloader;
22
20
  generationTelemetry?: GenerationTelemetryRecorder;
23
21
  operatorWakeCoalesceMs?: number;
24
- autoRunHeartbeatEveryMs?: number;
25
22
  config?: MuConfig;
26
23
  configReader?: ConfigReader;
27
24
  configWriter?: ConfigWriter;
@@ -31,8 +28,6 @@ export type ServerOptions = {
31
28
  export type ServerInstanceOptions = Omit<ServerOptions, "repoRoot" | "controlPlane" | "heartbeatScheduler" | "generationTelemetry" | "config" | "sessionLifecycle">;
32
29
  export type ServerContext = {
33
30
  repoRoot: string;
34
- issueStore: IssueStore;
35
- forumStore: ForumStore;
36
31
  eventLog: EventLog;
37
32
  eventsStore: JsonlStore<EventEnvelope>;
38
33
  };