@femtomc/mu-server 26.2.73 → 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 (51) hide show
  1. package/README.md +54 -66
  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_contract.d.ts +1 -7
  18. package/dist/control_plane_run_queue_coordinator.d.ts +1 -7
  19. package/dist/control_plane_run_queue_coordinator.js +1 -62
  20. package/dist/control_plane_telegram_generation.js +1 -0
  21. package/dist/control_plane_wake_delivery.js +1 -0
  22. package/dist/cron_programs.d.ts +21 -35
  23. package/dist/cron_programs.js +32 -113
  24. package/dist/cron_request.d.ts +0 -6
  25. package/dist/cron_request.js +0 -41
  26. package/dist/heartbeat_programs.d.ts +20 -35
  27. package/dist/heartbeat_programs.js +26 -122
  28. package/dist/index.d.ts +2 -2
  29. package/dist/outbound_delivery_router.d.ts +12 -0
  30. package/dist/outbound_delivery_router.js +29 -0
  31. package/dist/run_supervisor.d.ts +1 -16
  32. package/dist/run_supervisor.js +0 -70
  33. package/dist/server.d.ts +0 -5
  34. package/dist/server.js +95 -127
  35. package/dist/server_program_orchestration.d.ts +4 -19
  36. package/dist/server_program_orchestration.js +49 -200
  37. package/dist/server_routing.d.ts +0 -9
  38. package/dist/server_routing.js +19 -654
  39. package/dist/server_runtime.js +0 -1
  40. package/dist/server_types.d.ts +0 -2
  41. package/dist/server_types.js +0 -7
  42. package/package.json +6 -9
  43. package/dist/api/context.d.ts +0 -5
  44. package/dist/api/context.js +0 -1147
  45. package/dist/api/forum.d.ts +0 -2
  46. package/dist/api/forum.js +0 -75
  47. package/dist/api/issues.d.ts +0 -2
  48. package/dist/api/issues.js +0 -173
  49. package/public/assets/index-CxkevQNh.js +0 -100
  50. package/public/assets/index-D_8anM-D.css +0 -1
  51. package/public/index.html +0 -14
@@ -6,43 +6,6 @@ const CRON_PROGRAMS_FILENAME = "cron.jsonl";
6
6
  function defaultNowMs() {
7
7
  return Date.now();
8
8
  }
9
- function normalizeTarget(input) {
10
- if (!input || typeof input !== "object" || Array.isArray(input)) {
11
- return null;
12
- }
13
- const record = input;
14
- const kind = typeof record.kind === "string" ? record.kind.trim().toLowerCase() : "";
15
- if (kind === "run") {
16
- const jobId = typeof record.job_id === "string" ? record.job_id.trim() : "";
17
- const rootIssueId = typeof record.root_issue_id === "string" ? record.root_issue_id.trim() : "";
18
- if (!jobId && !rootIssueId) {
19
- return null;
20
- }
21
- return {
22
- kind: "run",
23
- job_id: jobId || null,
24
- root_issue_id: rootIssueId || null,
25
- };
26
- }
27
- if (kind === "activity") {
28
- const activityId = typeof record.activity_id === "string" ? record.activity_id.trim() : "";
29
- if (!activityId) {
30
- return null;
31
- }
32
- return {
33
- kind: "activity",
34
- activity_id: activityId,
35
- };
36
- }
37
- return null;
38
- }
39
- function normalizeWakeMode(value) {
40
- if (typeof value !== "string") {
41
- return "immediate";
42
- }
43
- const normalized = value.trim().toLowerCase().replaceAll("-", "_");
44
- return normalized === "next_heartbeat" ? "next_heartbeat" : "immediate";
45
- }
46
9
  function sanitizeMetadata(value) {
47
10
  if (!value || typeof value !== "object" || Array.isArray(value)) {
48
11
  return {};
@@ -56,7 +19,6 @@ function normalizeProgram(row) {
56
19
  const record = row;
57
20
  const programId = typeof record.program_id === "string" ? record.program_id.trim() : "";
58
21
  const title = typeof record.title === "string" ? record.title.trim() : "";
59
- const target = normalizeTarget(record.target);
60
22
  const createdAt = typeof record.created_at_ms === "number" && Number.isFinite(record.created_at_ms)
61
23
  ? Math.trunc(record.created_at_ms)
62
24
  : defaultNowMs();
@@ -64,7 +26,7 @@ function normalizeProgram(row) {
64
26
  nowMs: createdAt,
65
27
  defaultEveryAnchorMs: createdAt,
66
28
  });
67
- if (!programId || !title || !target || !schedule) {
29
+ if (!programId || !title || !schedule) {
68
30
  return null;
69
31
  }
70
32
  const updatedAt = typeof record.updated_at_ms === "number" && Number.isFinite(record.updated_at_ms)
@@ -77,14 +39,8 @@ function normalizeProgram(row) {
77
39
  ? Math.trunc(record.last_triggered_at_ms)
78
40
  : null;
79
41
  const lastResultRaw = typeof record.last_result === "string" ? record.last_result.trim().toLowerCase() : null;
80
- const lastResult = lastResultRaw === "ok" ||
81
- lastResultRaw === "not_found" ||
82
- lastResultRaw === "not_running" ||
83
- lastResultRaw === "failed"
84
- ? lastResultRaw
85
- : null;
42
+ const lastResult = lastResultRaw === "ok" || lastResultRaw === "coalesced" || lastResultRaw === "failed" ? lastResultRaw : null;
86
43
  const reason = typeof record.reason === "string" && record.reason.trim().length > 0 ? record.reason.trim() : "scheduled";
87
- const wakeMode = normalizeWakeMode(record.wake_mode);
88
44
  return {
89
45
  v: 1,
90
46
  program_id: programId,
@@ -92,8 +48,6 @@ function normalizeProgram(row) {
92
48
  enabled: record.enabled !== false,
93
49
  schedule,
94
50
  reason,
95
- wake_mode: wakeMode,
96
- target,
97
51
  metadata: sanitizeMetadata(record.metadata),
98
52
  created_at_ms: createdAt,
99
53
  updated_at_ms: updatedAt,
@@ -112,17 +66,13 @@ function sortPrograms(programs) {
112
66
  });
113
67
  }
114
68
  function shouldRetry(result) {
115
- if (result.status === "failed") {
116
- return true;
117
- }
118
- return result.status === "skipped" && result.reason === "requests-in-flight";
69
+ return result.status === "failed";
119
70
  }
120
71
  export class CronProgramRegistry {
121
72
  #store;
122
73
  #heartbeatScheduler;
123
74
  #timer;
124
- #runHeartbeat;
125
- #activityHeartbeat;
75
+ #dispatchWake;
126
76
  #onTickEvent;
127
77
  #onLifecycleEvent;
128
78
  #nowMs;
@@ -130,8 +80,7 @@ export class CronProgramRegistry {
130
80
  #loaded = null;
131
81
  constructor(opts) {
132
82
  this.#heartbeatScheduler = opts.heartbeatScheduler;
133
- this.#runHeartbeat = opts.runHeartbeat;
134
- this.#activityHeartbeat = opts.activityHeartbeat;
83
+ this.#dispatchWake = opts.dispatchWake;
135
84
  this.#onTickEvent = opts.onTickEvent;
136
85
  this.#onLifecycleEvent = opts.onLifecycleEvent;
137
86
  this.#nowMs = opts.nowMs ?? defaultNowMs;
@@ -149,7 +98,6 @@ export class CronProgramRegistry {
149
98
  return {
150
99
  ...program,
151
100
  schedule: { ...program.schedule },
152
- target: program.target.kind === "run" ? { ...program.target } : { ...program.target },
153
101
  metadata: { ...program.metadata },
154
102
  };
155
103
  }
@@ -251,50 +199,39 @@ export class CronProgramRegistry {
251
199
  const nowMs = Math.trunc(this.#nowMs());
252
200
  program.last_triggered_at_ms = nowMs;
253
201
  program.updated_at_ms = nowMs;
254
- let heartbeatResult;
202
+ let dispatchResult;
255
203
  let eventStatus = "ok";
256
204
  let eventReason = triggerReason;
257
- let eventMessage = `cron program tick: ${program.title}`;
205
+ let eventMessage = `cron program dispatched wake: ${program.title}`;
258
206
  try {
259
- const executionResult = program.target.kind === "run"
260
- ? await this.#runHeartbeat({
261
- jobId: program.target.job_id,
262
- rootIssueId: program.target.root_issue_id,
263
- reason: triggerReason,
264
- wakeMode: program.wake_mode,
265
- })
266
- : await this.#activityHeartbeat({
267
- activityId: program.target.activity_id,
268
- reason: triggerReason,
269
- });
270
- if (executionResult.ok) {
207
+ const executionResult = await this.#dispatchWake({
208
+ programId: program.program_id,
209
+ title: program.title,
210
+ reason: triggerReason,
211
+ metadata: { ...program.metadata },
212
+ triggeredAtMs: nowMs,
213
+ schedule: { ...program.schedule },
214
+ });
215
+ if (executionResult.status === "ok") {
271
216
  program.last_result = "ok";
272
217
  program.last_error = null;
273
- heartbeatResult = { status: "ran" };
218
+ dispatchResult = { status: "ran" };
274
219
  }
275
- else if (executionResult.reason === "not_running") {
276
- program.last_result = "not_running";
220
+ else if (executionResult.status === "coalesced") {
221
+ program.last_result = "coalesced";
277
222
  program.last_error = null;
278
- eventStatus = "not_running";
279
- eventReason = executionResult.reason;
280
- eventMessage = `cron program skipped (not running): ${program.title}`;
281
- heartbeatResult = { status: "skipped", reason: "not_running" };
282
- }
283
- else if (executionResult.reason === "not_found") {
284
- program.last_result = "not_found";
285
- program.last_error = null;
286
- eventStatus = "not_found";
287
- eventReason = executionResult.reason;
288
- eventMessage = `cron program skipped (not found): ${program.title}`;
289
- heartbeatResult = { status: "skipped", reason: "not_found" };
223
+ eventStatus = "coalesced";
224
+ eventReason = executionResult.reason ?? "coalesced";
225
+ eventMessage = `cron program coalesced wake: ${program.title}`;
226
+ dispatchResult = { status: "skipped", reason: "coalesced" };
290
227
  }
291
228
  else {
292
229
  program.last_result = "failed";
293
- program.last_error = executionResult.reason ?? "cron_program_tick_failed";
230
+ program.last_error = executionResult.reason;
294
231
  eventStatus = "failed";
295
- eventReason = program.last_error;
232
+ eventReason = executionResult.reason;
296
233
  eventMessage = `cron program failed: ${program.title}`;
297
- heartbeatResult = { status: "failed", reason: program.last_error };
234
+ dispatchResult = { status: "failed", reason: executionResult.reason };
298
235
  }
299
236
  }
300
237
  catch (err) {
@@ -303,9 +240,9 @@ export class CronProgramRegistry {
303
240
  eventStatus = "failed";
304
241
  eventReason = program.last_error;
305
242
  eventMessage = `cron program failed: ${program.title}`;
306
- heartbeatResult = { status: "failed", reason: program.last_error };
243
+ dispatchResult = { status: "failed", reason: program.last_error };
307
244
  }
308
- if (opts.advanceSchedule && !shouldRetry(heartbeatResult)) {
245
+ if (opts.advanceSchedule && !shouldRetry(dispatchResult)) {
309
246
  if (program.schedule.kind === "at") {
310
247
  program.enabled = false;
311
248
  program.next_run_at_ms = null;
@@ -336,7 +273,7 @@ export class CronProgramRegistry {
336
273
  }).catch(() => {
337
274
  // best effort only
338
275
  });
339
- return heartbeatResult;
276
+ return dispatchResult;
340
277
  }
341
278
  async list(opts = {}) {
342
279
  await this.#ensureLoaded();
@@ -346,9 +283,6 @@ export class CronProgramRegistry {
346
283
  if (typeof opts.enabled === "boolean" && program.enabled !== opts.enabled) {
347
284
  return false;
348
285
  }
349
- if (opts.targetKind && program.target.kind !== opts.targetKind) {
350
- return false;
351
- }
352
286
  if (opts.scheduleKind && program.schedule.kind !== opts.scheduleKind) {
353
287
  return false;
354
288
  }
@@ -379,10 +313,6 @@ export class CronProgramRegistry {
379
313
  if (!title) {
380
314
  throw new Error("cron_program_title_required");
381
315
  }
382
- const target = normalizeTarget(opts.target);
383
- if (!target) {
384
- throw new Error("cron_program_invalid_target");
385
- }
386
316
  const nowMs = Math.trunc(this.#nowMs());
387
317
  const schedule = normalizeCronSchedule(opts.schedule, {
388
318
  nowMs,
@@ -398,8 +328,6 @@ export class CronProgramRegistry {
398
328
  enabled: opts.enabled !== false,
399
329
  schedule,
400
330
  reason: opts.reason?.trim() || "scheduled",
401
- wake_mode: normalizeWakeMode(opts.wakeMode),
402
- target,
403
331
  metadata: sanitizeMetadata(opts.metadata),
404
332
  created_at_ms: nowMs,
405
333
  updated_at_ms: nowMs,
@@ -438,23 +366,14 @@ export class CronProgramRegistry {
438
366
  if (typeof opts.reason === "string") {
439
367
  program.reason = opts.reason.trim() || "scheduled";
440
368
  }
441
- if (typeof opts.wakeMode === "string") {
442
- program.wake_mode = normalizeWakeMode(opts.wakeMode);
443
- }
444
369
  if (typeof opts.enabled === "boolean") {
445
370
  program.enabled = opts.enabled;
446
371
  }
447
- if (opts.target) {
448
- const target = normalizeTarget(opts.target);
449
- if (!target) {
450
- return { ok: false, reason: "invalid_target", program: this.#snapshot(program) };
451
- }
452
- program.target = target;
453
- }
454
372
  if (opts.schedule) {
373
+ const nowMs = Math.trunc(this.#nowMs());
455
374
  const normalizedSchedule = normalizeCronSchedule(opts.schedule, {
456
- nowMs: Math.trunc(this.#nowMs()),
457
- defaultEveryAnchorMs: program.schedule.kind === "every" ? program.schedule.anchor_ms : Math.trunc(this.#nowMs()),
375
+ nowMs,
376
+ defaultEveryAnchorMs: program.schedule.kind === "every" ? program.schedule.anchor_ms : nowMs,
458
377
  });
459
378
  if (!normalizedSchedule) {
460
379
  return { ok: false, reason: "invalid_schedule", program: this.#snapshot(program) };
@@ -1,8 +1,2 @@
1
- import type { CronProgramTarget } from "./cron_programs.js";
2
- export type ParsedCronTarget = {
3
- target: CronProgramTarget | null;
4
- error: string | null;
5
- };
6
- export declare function parseCronTarget(body: Record<string, unknown>): ParsedCronTarget;
7
1
  export declare function hasCronScheduleInput(body: Record<string, unknown>): boolean;
8
2
  export declare function cronScheduleInputFromBody(body: Record<string, unknown>): Record<string, unknown>;
@@ -1,44 +1,3 @@
1
- export function parseCronTarget(body) {
2
- const targetKind = typeof body.target_kind === "string" ? body.target_kind.trim().toLowerCase() : "";
3
- if (targetKind === "run") {
4
- const jobId = typeof body.run_job_id === "string" ? body.run_job_id.trim() : "";
5
- const rootIssueId = typeof body.run_root_issue_id === "string" ? body.run_root_issue_id.trim() : "";
6
- if (!jobId && !rootIssueId) {
7
- return {
8
- target: null,
9
- error: "run target requires run_job_id or run_root_issue_id",
10
- };
11
- }
12
- return {
13
- target: {
14
- kind: "run",
15
- job_id: jobId || null,
16
- root_issue_id: rootIssueId || null,
17
- },
18
- error: null,
19
- };
20
- }
21
- if (targetKind === "activity") {
22
- const activityId = typeof body.activity_id === "string" ? body.activity_id.trim() : "";
23
- if (!activityId) {
24
- return {
25
- target: null,
26
- error: "activity target requires activity_id",
27
- };
28
- }
29
- return {
30
- target: {
31
- kind: "activity",
32
- activity_id: activityId,
33
- },
34
- error: null,
35
- };
36
- }
37
- return {
38
- target: null,
39
- error: "target_kind must be run or activity",
40
- };
41
- }
42
1
  export function hasCronScheduleInput(body) {
43
2
  return (body.schedule != null ||
44
3
  body.schedule_kind != null ||
@@ -1,14 +1,5 @@
1
1
  import type { JsonlStore } from "@femtomc/mu-core";
2
2
  import type { ActivityHeartbeatScheduler } from "./heartbeat_scheduler.js";
3
- export type HeartbeatProgramTarget = {
4
- kind: "run";
5
- job_id: string | null;
6
- root_issue_id: string | null;
7
- } | {
8
- kind: "activity";
9
- activity_id: string;
10
- };
11
- export type HeartbeatProgramWakeMode = "immediate" | "next_heartbeat";
12
3
  export type HeartbeatProgramSnapshot = {
13
4
  v: 1;
14
5
  program_id: string;
@@ -16,49 +7,48 @@ export type HeartbeatProgramSnapshot = {
16
7
  enabled: boolean;
17
8
  every_ms: number;
18
9
  reason: string;
19
- wake_mode: HeartbeatProgramWakeMode;
20
- target: HeartbeatProgramTarget;
21
10
  metadata: Record<string, unknown>;
22
11
  created_at_ms: number;
23
12
  updated_at_ms: number;
24
13
  last_triggered_at_ms: number | null;
25
- last_result: "ok" | "not_found" | "not_running" | "failed" | null;
14
+ last_result: "ok" | "coalesced" | "failed" | null;
26
15
  last_error: string | null;
27
16
  };
28
17
  export type HeartbeatProgramOperationResult = {
29
18
  ok: boolean;
30
- reason: "not_found" | "missing_target" | "invalid_target" | "not_running" | "failed" | null;
19
+ reason: "not_found" | "missing_target" | "not_running" | "failed" | null;
31
20
  program: HeartbeatProgramSnapshot | null;
32
21
  };
33
22
  export type HeartbeatProgramTickEvent = {
34
23
  ts_ms: number;
35
24
  program_id: string;
36
25
  message: string;
37
- status: "ok" | "not_found" | "not_running" | "failed";
26
+ status: "ok" | "coalesced" | "failed";
38
27
  reason: string | null;
39
28
  program: HeartbeatProgramSnapshot;
40
29
  };
30
+ export type HeartbeatProgramDispatchResult = {
31
+ status: "ok";
32
+ reason?: string | null;
33
+ } | {
34
+ status: "coalesced";
35
+ reason?: string | null;
36
+ } | {
37
+ status: "failed";
38
+ reason: string;
39
+ };
41
40
  export type HeartbeatProgramRegistryOpts = {
42
41
  repoRoot: string;
43
42
  heartbeatScheduler: ActivityHeartbeatScheduler;
44
43
  nowMs?: () => number;
45
44
  store?: JsonlStore<HeartbeatProgramSnapshot>;
46
- runHeartbeat: (opts: {
47
- jobId?: string | null;
48
- rootIssueId?: string | null;
49
- reason?: string | null;
50
- wakeMode?: HeartbeatProgramWakeMode;
51
- }) => Promise<{
52
- ok: boolean;
53
- reason: "not_found" | "not_running" | "missing_target" | null;
54
- }>;
55
- activityHeartbeat: (opts: {
56
- activityId?: string | null;
57
- reason?: string | null;
58
- }) => Promise<{
59
- ok: boolean;
60
- reason: "not_found" | "not_running" | "missing_target" | null;
61
- }>;
45
+ dispatchWake: (opts: {
46
+ programId: string;
47
+ title: string;
48
+ reason: string;
49
+ metadata: Record<string, unknown>;
50
+ triggeredAtMs: number;
51
+ }) => Promise<HeartbeatProgramDispatchResult>;
62
52
  onTickEvent?: (event: HeartbeatProgramTickEvent) => void | Promise<void>;
63
53
  };
64
54
  export declare class HeartbeatProgramRegistry {
@@ -66,16 +56,13 @@ export declare class HeartbeatProgramRegistry {
66
56
  constructor(opts: HeartbeatProgramRegistryOpts);
67
57
  list(opts?: {
68
58
  enabled?: boolean;
69
- targetKind?: "run" | "activity";
70
59
  limit?: number;
71
60
  }): Promise<HeartbeatProgramSnapshot[]>;
72
61
  get(programId: string): Promise<HeartbeatProgramSnapshot | null>;
73
62
  create(opts: {
74
63
  title: string;
75
- target: HeartbeatProgramTarget;
76
64
  everyMs?: number;
77
65
  reason?: string;
78
- wakeMode?: HeartbeatProgramWakeMode;
79
66
  enabled?: boolean;
80
67
  metadata?: Record<string, unknown>;
81
68
  }): Promise<HeartbeatProgramSnapshot>;
@@ -84,9 +71,7 @@ export declare class HeartbeatProgramRegistry {
84
71
  title?: string;
85
72
  everyMs?: number;
86
73
  reason?: string;
87
- wakeMode?: HeartbeatProgramWakeMode;
88
74
  enabled?: boolean;
89
- target?: HeartbeatProgramTarget;
90
75
  metadata?: Record<string, unknown>;
91
76
  }): Promise<HeartbeatProgramOperationResult>;
92
77
  remove(programId: string): Promise<HeartbeatProgramOperationResult>;