@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
@@ -4,43 +4,6 @@ const HEARTBEAT_PROGRAMS_FILENAME = "heartbeats.jsonl";
4
4
  function defaultNowMs() {
5
5
  return Date.now();
6
6
  }
7
- function normalizeTarget(input) {
8
- if (!input || typeof input !== "object" || Array.isArray(input)) {
9
- return null;
10
- }
11
- const record = input;
12
- const kind = typeof record.kind === "string" ? record.kind.trim().toLowerCase() : "";
13
- if (kind === "run") {
14
- const jobId = typeof record.job_id === "string" ? record.job_id.trim() : "";
15
- const rootIssueId = typeof record.root_issue_id === "string" ? record.root_issue_id.trim() : "";
16
- if (!jobId && !rootIssueId) {
17
- return null;
18
- }
19
- return {
20
- kind: "run",
21
- job_id: jobId || null,
22
- root_issue_id: rootIssueId || null,
23
- };
24
- }
25
- if (kind === "activity") {
26
- const activityId = typeof record.activity_id === "string" ? record.activity_id.trim() : "";
27
- if (!activityId) {
28
- return null;
29
- }
30
- return {
31
- kind: "activity",
32
- activity_id: activityId,
33
- };
34
- }
35
- return null;
36
- }
37
- function normalizeWakeMode(value) {
38
- if (typeof value !== "string") {
39
- return "immediate";
40
- }
41
- const normalized = value.trim().toLowerCase().replaceAll("-", "_");
42
- return normalized === "next_heartbeat" ? "next_heartbeat" : "immediate";
43
- }
44
7
  function sanitizeMetadata(value) {
45
8
  if (!value || typeof value !== "object" || Array.isArray(value)) {
46
9
  return {};
@@ -54,8 +17,7 @@ function normalizeProgram(row) {
54
17
  const record = row;
55
18
  const programId = typeof record.program_id === "string" ? record.program_id.trim() : "";
56
19
  const title = typeof record.title === "string" ? record.title.trim() : "";
57
- const target = normalizeTarget(record.target);
58
- if (!programId || !title || !target) {
20
+ if (!programId || !title) {
59
21
  return null;
60
22
  }
61
23
  const everyMsRaw = record.every_ms;
@@ -70,14 +32,8 @@ function normalizeProgram(row) {
70
32
  ? Math.trunc(record.last_triggered_at_ms)
71
33
  : null;
72
34
  const lastResultRaw = typeof record.last_result === "string" ? record.last_result.trim().toLowerCase() : null;
73
- const lastResult = lastResultRaw === "ok" ||
74
- lastResultRaw === "not_found" ||
75
- lastResultRaw === "not_running" ||
76
- lastResultRaw === "failed"
77
- ? lastResultRaw
78
- : null;
35
+ const lastResult = lastResultRaw === "ok" || lastResultRaw === "coalesced" || lastResultRaw === "failed" ? lastResultRaw : null;
79
36
  const reason = typeof record.reason === "string" && record.reason.trim().length > 0 ? record.reason.trim() : "scheduled";
80
- const wakeMode = normalizeWakeMode(record.wake_mode);
81
37
  return {
82
38
  v: 1,
83
39
  program_id: programId,
@@ -85,8 +41,6 @@ function normalizeProgram(row) {
85
41
  enabled: record.enabled !== false,
86
42
  every_ms: everyMs,
87
43
  reason,
88
- wake_mode: wakeMode,
89
- target,
90
44
  metadata: sanitizeMetadata(record.metadata),
91
45
  created_at_ms: createdAt,
92
46
  updated_at_ms: updatedAt,
@@ -106,16 +60,14 @@ function sortPrograms(programs) {
106
60
  export class HeartbeatProgramRegistry {
107
61
  #store;
108
62
  #heartbeatScheduler;
109
- #runHeartbeat;
110
- #activityHeartbeat;
63
+ #dispatchWake;
111
64
  #onTickEvent;
112
65
  #nowMs;
113
66
  #programs = new Map();
114
67
  #loaded = null;
115
68
  constructor(opts) {
116
69
  this.#heartbeatScheduler = opts.heartbeatScheduler;
117
- this.#runHeartbeat = opts.runHeartbeat;
118
- this.#activityHeartbeat = opts.activityHeartbeat;
70
+ this.#dispatchWake = opts.dispatchWake;
119
71
  this.#onTickEvent = opts.onTickEvent;
120
72
  this.#nowMs = opts.nowMs ?? defaultNowMs;
121
73
  this.#store =
@@ -128,7 +80,6 @@ export class HeartbeatProgramRegistry {
128
80
  #snapshot(program) {
129
81
  return {
130
82
  ...program,
131
- target: program.target.kind === "run" ? { ...program.target } : { ...program.target },
132
83
  metadata: { ...program.metadata },
133
84
  };
134
85
  }
@@ -187,50 +138,38 @@ export class HeartbeatProgramRegistry {
187
138
  const nowMs = Math.trunc(this.#nowMs());
188
139
  program.last_triggered_at_ms = nowMs;
189
140
  program.updated_at_ms = nowMs;
190
- let heartbeatResult;
141
+ let tickResult;
191
142
  let eventStatus = "ok";
192
143
  let eventReason = heartbeatReason;
193
- let eventMessage = `heartbeat program tick: ${program.title}`;
144
+ let eventMessage = `heartbeat program dispatched wake: ${program.title}`;
194
145
  try {
195
- const result = program.target.kind === "run"
196
- ? await this.#runHeartbeat({
197
- jobId: program.target.job_id,
198
- rootIssueId: program.target.root_issue_id,
199
- reason: heartbeatReason,
200
- wakeMode: program.wake_mode,
201
- })
202
- : await this.#activityHeartbeat({
203
- activityId: program.target.activity_id,
204
- reason: heartbeatReason,
205
- });
206
- if (result.ok) {
146
+ const result = await this.#dispatchWake({
147
+ programId: program.program_id,
148
+ title: program.title,
149
+ reason: heartbeatReason,
150
+ metadata: { ...program.metadata },
151
+ triggeredAtMs: nowMs,
152
+ });
153
+ if (result.status === "ok") {
207
154
  program.last_result = "ok";
208
155
  program.last_error = null;
209
- heartbeatResult = { status: "ran" };
156
+ tickResult = { status: "ran" };
210
157
  }
211
- else if (result.reason === "not_running") {
212
- program.last_result = "not_running";
158
+ else if (result.status === "coalesced") {
159
+ program.last_result = "coalesced";
213
160
  program.last_error = null;
214
- eventStatus = "not_running";
215
- eventReason = result.reason;
216
- eventMessage = `heartbeat program skipped (not running): ${program.title}`;
217
- heartbeatResult = { status: "skipped", reason: "not_running" };
218
- }
219
- else if (result.reason === "not_found") {
220
- program.last_result = "not_found";
221
- program.last_error = null;
222
- eventStatus = "not_found";
223
- eventReason = result.reason;
224
- eventMessage = `heartbeat program skipped (not found): ${program.title}`;
225
- heartbeatResult = { status: "skipped", reason: "not_found" };
161
+ eventStatus = "coalesced";
162
+ eventReason = result.reason ?? "coalesced";
163
+ eventMessage = `heartbeat program coalesced wake: ${program.title}`;
164
+ tickResult = { status: "skipped", reason: "coalesced" };
226
165
  }
227
166
  else {
228
167
  program.last_result = "failed";
229
- program.last_error = result.reason ?? "heartbeat_program_tick_failed";
168
+ program.last_error = result.reason;
230
169
  eventStatus = "failed";
231
- eventReason = program.last_error;
170
+ eventReason = result.reason;
232
171
  eventMessage = `heartbeat program failed: ${program.title}`;
233
- heartbeatResult = { status: "failed", reason: program.last_error };
172
+ tickResult = { status: "failed", reason: result.reason };
234
173
  }
235
174
  }
236
175
  catch (err) {
@@ -239,23 +178,7 @@ export class HeartbeatProgramRegistry {
239
178
  eventStatus = "failed";
240
179
  eventReason = program.last_error;
241
180
  eventMessage = `heartbeat program failed: ${program.title}`;
242
- heartbeatResult = { status: "failed", reason: program.last_error };
243
- }
244
- const shouldAutoDisableOnTerminal = program.target.kind === "run" &&
245
- program.metadata.auto_disable_on_terminal === true &&
246
- heartbeatResult.status === "skipped" &&
247
- (heartbeatResult.reason === "not_running" || heartbeatResult.reason === "not_found");
248
- if (shouldAutoDisableOnTerminal) {
249
- program.enabled = false;
250
- program.every_ms = 0;
251
- program.updated_at_ms = Math.trunc(this.#nowMs());
252
- program.metadata = {
253
- ...program.metadata,
254
- auto_disabled_at_ms: Math.trunc(this.#nowMs()),
255
- auto_disabled_reason: heartbeatResult.status === "skipped" ? (heartbeatResult.reason ?? null) : null,
256
- };
257
- this.#heartbeatScheduler.unregister(this.#scheduleId(program.program_id));
258
- eventMessage = `${eventMessage} (auto-disabled)`;
181
+ tickResult = { status: "failed", reason: program.last_error };
259
182
  }
260
183
  await this.#persist();
261
184
  await this.#emitTickEvent({
@@ -268,7 +191,7 @@ export class HeartbeatProgramRegistry {
268
191
  }).catch(() => {
269
192
  // best effort only
270
193
  });
271
- return heartbeatResult;
194
+ return tickResult;
272
195
  }
273
196
  async list(opts = {}) {
274
197
  await this.#ensureLoaded();
@@ -278,9 +201,6 @@ export class HeartbeatProgramRegistry {
278
201
  if (typeof opts.enabled === "boolean" && program.enabled !== opts.enabled) {
279
202
  return false;
280
203
  }
281
- if (opts.targetKind && program.target.kind !== opts.targetKind) {
282
- return false;
283
- }
284
204
  return true;
285
205
  })
286
206
  .slice(0, limit)
@@ -297,10 +217,6 @@ export class HeartbeatProgramRegistry {
297
217
  if (!title) {
298
218
  throw new Error("heartbeat_program_title_required");
299
219
  }
300
- const target = normalizeTarget(opts.target);
301
- if (!target) {
302
- throw new Error("heartbeat_program_invalid_target");
303
- }
304
220
  const nowMs = Math.trunc(this.#nowMs());
305
221
  const program = {
306
222
  v: 1,
@@ -311,8 +227,6 @@ export class HeartbeatProgramRegistry {
311
227
  ? Math.max(0, Math.trunc(opts.everyMs))
312
228
  : 15_000,
313
229
  reason: opts.reason?.trim() || "scheduled",
314
- wake_mode: normalizeWakeMode(opts.wakeMode),
315
- target,
316
230
  metadata: sanitizeMetadata(opts.metadata),
317
231
  created_at_ms: nowMs,
318
232
  updated_at_ms: nowMs,
@@ -344,19 +258,9 @@ export class HeartbeatProgramRegistry {
344
258
  if (typeof opts.reason === "string") {
345
259
  program.reason = opts.reason.trim() || "scheduled";
346
260
  }
347
- if (typeof opts.wakeMode === "string") {
348
- program.wake_mode = normalizeWakeMode(opts.wakeMode);
349
- }
350
261
  if (typeof opts.enabled === "boolean") {
351
262
  program.enabled = opts.enabled;
352
263
  }
353
- if (opts.target) {
354
- const target = normalizeTarget(opts.target);
355
- if (!target) {
356
- return { ok: false, reason: "invalid_target", program: this.#snapshot(program) };
357
- }
358
- program.target = target;
359
- }
360
264
  if (opts.metadata) {
361
265
  program.metadata = sanitizeMetadata(opts.metadata);
362
266
  }
package/dist/index.d.ts CHANGED
@@ -7,13 +7,13 @@ export { DEFAULT_INTER_ROOT_QUEUE_POLICY, normalizeInterRootQueuePolicy, ORCHEST
7
7
  export type { DurableRunQueueClaimOpts, DurableRunQueueEnqueueOpts, DurableRunQueueOpts, DurableRunQueueSnapshot, DurableRunQueueState, DurableRunQueueTransitionOpts, RunQueueReconcilePlan, } from "./run_queue.js";
8
8
  export { DurableRunQueue, queueStatesForRunStatusFilter, reconcileRunQueue, RUN_QUEUE_RECONCILE_INVARIANTS, runQueuePath, runSnapshotFromQueueSnapshot, runStatusFromQueueState, } from "./run_queue.js";
9
9
  export { bootstrapControlPlane, detectAdapters } from "./control_plane.js";
10
- export type { CronProgramLifecycleAction, CronProgramLifecycleEvent, CronProgramOperationResult, CronProgramRegistryOpts, CronProgramSnapshot, CronProgramStatusSnapshot, CronProgramTarget, CronProgramTickEvent, CronProgramWakeMode, } from "./cron_programs.js";
10
+ export type { CronProgramDispatchResult, CronProgramLifecycleAction, CronProgramLifecycleEvent, CronProgramOperationResult, CronProgramRegistryOpts, CronProgramSnapshot, CronProgramStatusSnapshot, CronProgramTickEvent, } from "./cron_programs.js";
11
11
  export { CronProgramRegistry } from "./cron_programs.js";
12
12
  export type { CronProgramSchedule as CronSchedule, CronProgramSchedule } from "./cron_schedule.js";
13
13
  export { computeNextScheduleRunAtMs, normalizeCronSchedule } from "./cron_schedule.js";
14
14
  export type { CronTimerRegistryOpts, CronTimerSnapshot } from "./cron_timer.js";
15
15
  export { CronTimerRegistry } from "./cron_timer.js";
16
- export type { HeartbeatProgramOperationResult, HeartbeatProgramRegistryOpts, HeartbeatProgramSnapshot, HeartbeatProgramTarget, HeartbeatProgramTickEvent, HeartbeatProgramWakeMode, } from "./heartbeat_programs.js";
16
+ export type { HeartbeatProgramDispatchResult, HeartbeatProgramOperationResult, HeartbeatProgramRegistryOpts, HeartbeatProgramSnapshot, HeartbeatProgramTickEvent, } from "./heartbeat_programs.js";
17
17
  export { HeartbeatProgramRegistry } from "./heartbeat_programs.js";
18
18
  export type { ActivityHeartbeatSchedulerOpts, HeartbeatRunResult, HeartbeatTickHandler, } from "./heartbeat_scheduler.js";
19
19
  export { ActivityHeartbeatScheduler } from "./heartbeat_scheduler.js";
@@ -0,0 +1,44 @@
1
+ export type OrchestrationQueueState = "queued" | "active" | "waiting_review" | "refining" | "done" | "failed" | "cancelled";
2
+ /**
3
+ * Inter-root scheduler policy knob for durable queue drain.
4
+ * - sequential: exactly one active root (`max_active_roots=1`)
5
+ * - parallel: bounded fanout (`max_active_roots>=1`)
6
+ */
7
+ export type InterRootQueuePolicy = {
8
+ mode: "sequential";
9
+ max_active_roots: 1;
10
+ } | {
11
+ mode: "parallel";
12
+ max_active_roots: number;
13
+ };
14
+ export declare const DEFAULT_INTER_ROOT_QUEUE_POLICY: InterRootQueuePolicy;
15
+ export declare function normalizeInterRootQueuePolicy(policy: InterRootQueuePolicy | null | undefined): InterRootQueuePolicy;
16
+ /**
17
+ * Allowed queue transitions. Queue tests should enforce this table exactly.
18
+ */
19
+ export declare const ORCHESTRATION_QUEUE_ALLOWED_TRANSITIONS: Record<OrchestrationQueueState, readonly OrchestrationQueueState[]>;
20
+ export declare const ORCHESTRATION_QUEUE_INVARIANTS: readonly ["ORCH-QUEUE-001: Queue writes are durable before acknowledging enqueue/start/resume requests.", "ORCH-QUEUE-002: Queue dispatch must claim exactly one active item at a time per root slot.", "ORCH-QUEUE-003: Terminal states (done|failed|cancelled) are immutable.", "ORCH-QUEUE-004: Review path is active -> waiting_review -> (done | refining).", "ORCH-QUEUE-005: Refinement re-enters execution only via refining -> queued.", "ORCH-QUEUE-006: sequential policy permits <=1 active root; parallel permits <=max_active_roots active roots."];
21
+ export type InterRootQueueSnapshot = {
22
+ queue_id: string;
23
+ root_issue_id: string | null;
24
+ state: OrchestrationQueueState;
25
+ job_id: string | null;
26
+ created_at_ms: number;
27
+ };
28
+ export type InterRootQueueReconcilePlan = {
29
+ policy: InterRootQueuePolicy;
30
+ max_active_roots: number;
31
+ active_root_count: number;
32
+ available_root_slots: number;
33
+ activate_queue_ids: string[];
34
+ launch_queue_ids: string[];
35
+ };
36
+ export declare const INTER_ROOT_QUEUE_RECONCILE_INVARIANTS: readonly ["ORCH-INTER-ROOT-RECON-001: one queue snapshot + policy yields one deterministic activation/launch plan.", "ORCH-INTER-ROOT-RECON-002: activation order is FIFO (`created_at_ms`, then `queue_id`) with per-root slot dedupe.", "ORCH-INTER-ROOT-RECON-003: sequential policy admits <=1 occupied root; parallel admits <=max_active_roots roots.", "ORCH-INTER-ROOT-RECON-004: launch candidates are active rows without bound job ids, one launch per root slot."];
37
+ /**
38
+ * Deterministic inter-root queue reconcile primitive.
39
+ *
40
+ * Computes queue activation/launch intentions from durable queue state and policy. The caller is
41
+ * responsible for performing side effects (claim, launch, bind, transition) and reconciling again
42
+ * against refreshed queue/runtime snapshots.
43
+ */
44
+ export declare function reconcileInterRootQueue<Row extends InterRootQueueSnapshot>(rows: readonly Row[], policy: InterRootQueuePolicy): InterRootQueueReconcilePlan;
@@ -0,0 +1,111 @@
1
+ export const DEFAULT_INTER_ROOT_QUEUE_POLICY = {
2
+ mode: "sequential",
3
+ max_active_roots: 1,
4
+ };
5
+ export function normalizeInterRootQueuePolicy(policy) {
6
+ if (!policy) {
7
+ return DEFAULT_INTER_ROOT_QUEUE_POLICY;
8
+ }
9
+ if (policy.mode === "parallel") {
10
+ return {
11
+ mode: "parallel",
12
+ max_active_roots: Math.max(1, Math.trunc(policy.max_active_roots)),
13
+ };
14
+ }
15
+ return DEFAULT_INTER_ROOT_QUEUE_POLICY;
16
+ }
17
+ /**
18
+ * Allowed queue transitions. Queue tests should enforce this table exactly.
19
+ */
20
+ export const ORCHESTRATION_QUEUE_ALLOWED_TRANSITIONS = {
21
+ queued: ["active", "cancelled"],
22
+ active: ["waiting_review", "done", "failed", "cancelled"],
23
+ waiting_review: ["refining", "done", "failed", "cancelled"],
24
+ refining: ["queued", "failed", "cancelled"],
25
+ done: [],
26
+ failed: [],
27
+ cancelled: [],
28
+ };
29
+ export const ORCHESTRATION_QUEUE_INVARIANTS = [
30
+ "ORCH-QUEUE-001: Queue writes are durable before acknowledging enqueue/start/resume requests.",
31
+ "ORCH-QUEUE-002: Queue dispatch must claim exactly one active item at a time per root slot.",
32
+ "ORCH-QUEUE-003: Terminal states (done|failed|cancelled) are immutable.",
33
+ "ORCH-QUEUE-004: Review path is active -> waiting_review -> (done | refining).",
34
+ "ORCH-QUEUE-005: Refinement re-enters execution only via refining -> queued.",
35
+ "ORCH-QUEUE-006: sequential policy permits <=1 active root; parallel permits <=max_active_roots active roots.",
36
+ ];
37
+ const INTER_ROOT_OCCUPIED_STATES = new Set(["active", "waiting_review", "refining"]);
38
+ export const INTER_ROOT_QUEUE_RECONCILE_INVARIANTS = [
39
+ "ORCH-INTER-ROOT-RECON-001: one queue snapshot + policy yields one deterministic activation/launch plan.",
40
+ "ORCH-INTER-ROOT-RECON-002: activation order is FIFO (`created_at_ms`, then `queue_id`) with per-root slot dedupe.",
41
+ "ORCH-INTER-ROOT-RECON-003: sequential policy admits <=1 occupied root; parallel admits <=max_active_roots roots.",
42
+ "ORCH-INTER-ROOT-RECON-004: launch candidates are active rows without bound job ids, one launch per root slot.",
43
+ ];
44
+ function stableCompare(a, b) {
45
+ if (a.created_at_ms !== b.created_at_ms) {
46
+ return a.created_at_ms - b.created_at_ms;
47
+ }
48
+ return a.queue_id.localeCompare(b.queue_id);
49
+ }
50
+ function normalizeMaxActiveRoots(policy) {
51
+ if (policy.mode === "parallel") {
52
+ return Math.max(1, Math.trunc(policy.max_active_roots));
53
+ }
54
+ return 1;
55
+ }
56
+ function queueRootSlotKey(row) {
57
+ return row.root_issue_id ? `root:${row.root_issue_id}` : `queue:${row.queue_id}`;
58
+ }
59
+ /**
60
+ * Deterministic inter-root queue reconcile primitive.
61
+ *
62
+ * Computes queue activation/launch intentions from durable queue state and policy. The caller is
63
+ * responsible for performing side effects (claim, launch, bind, transition) and reconciling again
64
+ * against refreshed queue/runtime snapshots.
65
+ */
66
+ export function reconcileInterRootQueue(rows, policy) {
67
+ const sorted = [...rows].sort(stableCompare);
68
+ const maxActiveRoots = normalizeMaxActiveRoots(policy);
69
+ const occupiedRoots = new Set();
70
+ const launchRoots = new Set();
71
+ const launchQueueIds = [];
72
+ for (const row of sorted) {
73
+ if (!INTER_ROOT_OCCUPIED_STATES.has(row.state)) {
74
+ continue;
75
+ }
76
+ const slotKey = queueRootSlotKey(row);
77
+ occupiedRoots.add(slotKey);
78
+ if (row.state !== "active" || row.job_id != null || launchRoots.has(slotKey)) {
79
+ continue;
80
+ }
81
+ launchRoots.add(slotKey);
82
+ launchQueueIds.push(row.queue_id);
83
+ }
84
+ const availableRootSlots = Math.max(0, maxActiveRoots - occupiedRoots.size);
85
+ const claimedRoots = new Set();
86
+ const activateQueueIds = [];
87
+ if (availableRootSlots > 0) {
88
+ for (const row of sorted) {
89
+ if (row.state !== "queued") {
90
+ continue;
91
+ }
92
+ const slotKey = queueRootSlotKey(row);
93
+ if (occupiedRoots.has(slotKey) || claimedRoots.has(slotKey)) {
94
+ continue;
95
+ }
96
+ claimedRoots.add(slotKey);
97
+ activateQueueIds.push(row.queue_id);
98
+ if (activateQueueIds.length >= availableRootSlots) {
99
+ break;
100
+ }
101
+ }
102
+ }
103
+ return {
104
+ policy,
105
+ max_active_roots: maxActiveRoots,
106
+ active_root_count: occupiedRoots.size,
107
+ available_root_slots: availableRootSlots,
108
+ activate_queue_ids: activateQueueIds,
109
+ launch_queue_ids: launchQueueIds,
110
+ };
111
+ }
@@ -0,0 +1,12 @@
1
+ import { type Channel, type OutboxDeliveryHandlerResult, type OutboxRecord } from "@femtomc/mu-control-plane";
2
+ export type OutboundDeliveryDriver = {
3
+ channel: Channel;
4
+ deliver: (record: OutboxRecord) => Promise<OutboxDeliveryHandlerResult>;
5
+ };
6
+ export declare class OutboundDeliveryRouter {
7
+ #private;
8
+ constructor(drivers: readonly OutboundDeliveryDriver[]);
9
+ supportsChannel(channel: Channel): boolean;
10
+ supportedChannels(): Channel[];
11
+ deliver(record: OutboxRecord): Promise<undefined | OutboxDeliveryHandlerResult>;
12
+ }
@@ -0,0 +1,29 @@
1
+ import { ChannelSchema, } from "@femtomc/mu-control-plane";
2
+ export class OutboundDeliveryRouter {
3
+ #driversByChannel = new Map();
4
+ constructor(drivers) {
5
+ for (const driver of drivers) {
6
+ if (this.#driversByChannel.has(driver.channel)) {
7
+ throw new Error(`duplicate outbound delivery driver: ${driver.channel}`);
8
+ }
9
+ this.#driversByChannel.set(driver.channel, driver);
10
+ }
11
+ }
12
+ supportsChannel(channel) {
13
+ return this.#driversByChannel.has(channel);
14
+ }
15
+ supportedChannels() {
16
+ return [...this.#driversByChannel.keys()].sort((a, b) => a.localeCompare(b));
17
+ }
18
+ async deliver(record) {
19
+ const parsedChannel = ChannelSchema.safeParse(record.envelope.channel);
20
+ if (!parsedChannel.success) {
21
+ return undefined;
22
+ }
23
+ const driver = this.#driversByChannel.get(parsedChannel.data);
24
+ if (!driver) {
25
+ return undefined;
26
+ }
27
+ return await driver.deliver(record);
28
+ }
29
+ }
@@ -1,5 +1,5 @@
1
1
  import type { JsonlStore } from "@femtomc/mu-core";
2
- import { type InterRootQueuePolicy, type InterRootQueueReconcilePlan, type OrchestrationQueueState } from "@femtomc/mu-orchestrator";
2
+ import { type InterRootQueuePolicy, type InterRootQueueReconcilePlan, type OrchestrationQueueState } from "./orchestration_queue.js";
3
3
  import type { ControlPlaneRunMode, ControlPlaneRunSnapshot, ControlPlaneRunStatus } from "./run_supervisor.js";
4
4
  export type DurableRunQueueState = OrchestrationQueueState;
5
5
  export type DurableRunQueueSnapshot = {