@femtomc/mu-server 26.2.69 → 26.2.71

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 (58) hide show
  1. package/README.md +7 -3
  2. package/dist/api/activities.d.ts +2 -0
  3. package/dist/api/activities.js +160 -0
  4. package/dist/api/config.d.ts +2 -0
  5. package/dist/api/config.js +45 -0
  6. package/dist/api/control_plane.d.ts +2 -0
  7. package/dist/api/control_plane.js +28 -0
  8. package/dist/api/cron.d.ts +2 -0
  9. package/dist/api/cron.js +182 -0
  10. package/dist/api/events.js +77 -19
  11. package/dist/api/forum.js +52 -18
  12. package/dist/api/heartbeats.d.ts +2 -0
  13. package/dist/api/heartbeats.js +211 -0
  14. package/dist/api/identities.d.ts +2 -0
  15. package/dist/api/identities.js +103 -0
  16. package/dist/api/issues.js +120 -33
  17. package/dist/api/runs.d.ts +2 -0
  18. package/dist/api/runs.js +207 -0
  19. package/dist/cli.js +58 -3
  20. package/dist/config.d.ts +4 -21
  21. package/dist/config.js +24 -75
  22. package/dist/control_plane.d.ts +7 -114
  23. package/dist/control_plane.js +238 -654
  24. package/dist/control_plane_bootstrap_helpers.d.ts +16 -0
  25. package/dist/control_plane_bootstrap_helpers.js +85 -0
  26. package/dist/control_plane_contract.d.ts +176 -0
  27. package/dist/control_plane_contract.js +1 -0
  28. package/dist/control_plane_reload.d.ts +63 -0
  29. package/dist/control_plane_reload.js +525 -0
  30. package/dist/control_plane_run_outbox.d.ts +7 -0
  31. package/dist/control_plane_run_outbox.js +52 -0
  32. package/dist/control_plane_run_queue_coordinator.d.ts +48 -0
  33. package/dist/control_plane_run_queue_coordinator.js +327 -0
  34. package/dist/control_plane_telegram_generation.d.ts +27 -0
  35. package/dist/control_plane_telegram_generation.js +520 -0
  36. package/dist/control_plane_wake_delivery.d.ts +50 -0
  37. package/dist/control_plane_wake_delivery.js +123 -0
  38. package/dist/cron_request.d.ts +8 -0
  39. package/dist/cron_request.js +65 -0
  40. package/dist/index.d.ts +7 -2
  41. package/dist/index.js +4 -1
  42. package/dist/run_queue.d.ts +95 -0
  43. package/dist/run_queue.js +817 -0
  44. package/dist/run_supervisor.d.ts +20 -0
  45. package/dist/run_supervisor.js +25 -1
  46. package/dist/server.d.ts +12 -49
  47. package/dist/server.js +365 -2128
  48. package/dist/server_program_orchestration.d.ts +38 -0
  49. package/dist/server_program_orchestration.js +254 -0
  50. package/dist/server_routing.d.ts +31 -0
  51. package/dist/server_routing.js +230 -0
  52. package/dist/server_runtime.d.ts +30 -0
  53. package/dist/server_runtime.js +43 -0
  54. package/dist/server_types.d.ts +3 -0
  55. package/dist/server_types.js +16 -0
  56. package/dist/session_lifecycle.d.ts +11 -0
  57. package/dist/session_lifecycle.js +149 -0
  58. package/package.json +7 -6
@@ -0,0 +1,16 @@
1
+ import { type MessagingOperatorBackend, MessagingOperatorRuntime } from "@femtomc/mu-agent";
2
+ import { ControlPlaneOutbox, type OutboxDeliveryHandlerResult, type OutboxDispatchOutcome, type OutboxRecord } from "@femtomc/mu-control-plane";
3
+ import type { ControlPlaneConfig } from "./control_plane_contract.js";
4
+ export declare function buildMessagingOperatorRuntime(opts: {
5
+ repoRoot: string;
6
+ config: ControlPlaneConfig;
7
+ backend?: MessagingOperatorBackend;
8
+ }): MessagingOperatorRuntime | null;
9
+ export declare function createOutboxDrainLoop(opts: {
10
+ outbox: ControlPlaneOutbox;
11
+ deliver: (record: OutboxRecord) => Promise<undefined | OutboxDeliveryHandlerResult>;
12
+ onOutcome?: (outcome: OutboxDispatchOutcome) => void | Promise<void>;
13
+ }): {
14
+ scheduleOutboxDrain: () => void;
15
+ stop: () => void;
16
+ };
@@ -0,0 +1,85 @@
1
+ import { ApprovedCommandBroker, CommandContextResolver, MessagingOperatorRuntime, operatorExtensionPaths, PiMessagingOperatorBackend, } from "@femtomc/mu-agent";
2
+ import { ControlPlaneOutboxDispatcher, } from "@femtomc/mu-control-plane";
3
+ const OUTBOX_DRAIN_INTERVAL_MS = 500;
4
+ export function buildMessagingOperatorRuntime(opts) {
5
+ if (!opts.config.operator.enabled) {
6
+ return null;
7
+ }
8
+ const backend = opts.backend ??
9
+ new PiMessagingOperatorBackend({
10
+ provider: opts.config.operator.provider ?? undefined,
11
+ model: opts.config.operator.model ?? undefined,
12
+ extensionPaths: operatorExtensionPaths,
13
+ });
14
+ return new MessagingOperatorRuntime({
15
+ backend,
16
+ broker: new ApprovedCommandBroker({
17
+ runTriggersEnabled: opts.config.operator.run_triggers_enabled,
18
+ contextResolver: new CommandContextResolver({ allowedRepoRoots: [opts.repoRoot] }),
19
+ }),
20
+ enabled: true,
21
+ });
22
+ }
23
+ export function createOutboxDrainLoop(opts) {
24
+ const dispatcher = new ControlPlaneOutboxDispatcher({
25
+ outbox: opts.outbox,
26
+ deliver: opts.deliver,
27
+ });
28
+ let drainingOutbox = false;
29
+ let drainRequested = false;
30
+ let stopped = false;
31
+ const drainOutboxNow = async () => {
32
+ if (stopped) {
33
+ return;
34
+ }
35
+ if (drainingOutbox) {
36
+ drainRequested = true;
37
+ return;
38
+ }
39
+ drainingOutbox = true;
40
+ try {
41
+ do {
42
+ drainRequested = false;
43
+ const outcomes = await dispatcher.drainDue();
44
+ if (opts.onOutcome) {
45
+ for (const outcome of outcomes) {
46
+ try {
47
+ await opts.onOutcome(outcome);
48
+ }
49
+ catch {
50
+ // Keep telemetry callbacks non-fatal.
51
+ }
52
+ }
53
+ }
54
+ } while (drainRequested && !stopped);
55
+ }
56
+ catch {
57
+ // Swallow errors — dispatcher handles retry progression internally.
58
+ }
59
+ finally {
60
+ drainingOutbox = false;
61
+ }
62
+ };
63
+ const scheduleOutboxDrain = () => {
64
+ if (stopped) {
65
+ return;
66
+ }
67
+ queueMicrotask(() => {
68
+ void drainOutboxNow();
69
+ });
70
+ };
71
+ const interval = setInterval(() => {
72
+ scheduleOutboxDrain();
73
+ }, OUTBOX_DRAIN_INTERVAL_MS);
74
+ scheduleOutboxDrain();
75
+ return {
76
+ scheduleOutboxDrain,
77
+ stop: () => {
78
+ if (stopped) {
79
+ return;
80
+ }
81
+ stopped = true;
82
+ clearInterval(interval);
83
+ },
84
+ };
85
+ }
@@ -0,0 +1,176 @@
1
+ import type { Channel, CommandPipelineResult, ReloadableGenerationIdentity } from "@femtomc/mu-control-plane";
2
+ import type { ControlPlaneRunHeartbeatResult, ControlPlaneRunInterruptResult, ControlPlaneRunSnapshot, ControlPlaneRunTrace } from "./run_supervisor.js";
3
+ import type { MuConfig } from "./config.js";
4
+ /**
5
+ * Boundary contracts for server/control-plane composition.
6
+ *
7
+ * Dependency direction:
8
+ * - Domain/application code should depend on these contracts.
9
+ * - Interface adapters in `control_plane.ts` implement these seams.
10
+ */
11
+ export type ControlPlaneConfig = MuConfig["control_plane"];
12
+ /**
13
+ * Durable orchestration queue contract (default-on path).
14
+ *
15
+ * Scheduler policy/state-machine primitives are owned by `@femtomc/mu-orchestrator` and
16
+ * re-exported here so existing server/control-plane callers keep a stable import surface.
17
+ */
18
+ export type { InterRootQueuePolicy, OrchestrationQueueState } from "@femtomc/mu-orchestrator";
19
+ export { DEFAULT_INTER_ROOT_QUEUE_POLICY, normalizeInterRootQueuePolicy, ORCHESTRATION_QUEUE_ALLOWED_TRANSITIONS, ORCHESTRATION_QUEUE_INVARIANTS, } from "@femtomc/mu-orchestrator";
20
+ export type ActiveAdapter = {
21
+ name: Channel;
22
+ route: string;
23
+ };
24
+ export type TelegramGenerationRollbackTrigger = "manual" | "warmup_failed" | "health_gate_failed" | "cutover_failed" | "post_cutover_health_failed" | "rollback_unavailable" | "rollback_failed";
25
+ export type TelegramGenerationReloadResult = {
26
+ handled: boolean;
27
+ ok: boolean;
28
+ reason: string;
29
+ route: string;
30
+ from_generation: ReloadableGenerationIdentity | null;
31
+ to_generation: ReloadableGenerationIdentity | null;
32
+ active_generation: ReloadableGenerationIdentity | null;
33
+ warmup: {
34
+ ok: boolean;
35
+ elapsed_ms: number;
36
+ error?: string;
37
+ } | null;
38
+ cutover: {
39
+ ok: boolean;
40
+ elapsed_ms: number;
41
+ error?: string;
42
+ } | null;
43
+ drain: {
44
+ ok: boolean;
45
+ elapsed_ms: number;
46
+ timed_out: boolean;
47
+ forced_stop: boolean;
48
+ error?: string;
49
+ } | null;
50
+ rollback: {
51
+ requested: boolean;
52
+ trigger: TelegramGenerationRollbackTrigger | null;
53
+ attempted: boolean;
54
+ ok: boolean;
55
+ error?: string;
56
+ };
57
+ error?: string;
58
+ };
59
+ export type ControlPlaneGenerationContext = ReloadableGenerationIdentity;
60
+ export type TelegramGenerationSwapHooks = {
61
+ onWarmup?: (ctx: {
62
+ generation: ReloadableGenerationIdentity;
63
+ reason: string;
64
+ }) => void | Promise<void>;
65
+ onCutover?: (ctx: {
66
+ from_generation: ReloadableGenerationIdentity | null;
67
+ to_generation: ReloadableGenerationIdentity;
68
+ reason: string;
69
+ }) => void | Promise<void>;
70
+ onDrain?: (ctx: {
71
+ generation: ReloadableGenerationIdentity;
72
+ reason: string;
73
+ timeout_ms: number;
74
+ }) => void | Promise<void>;
75
+ };
76
+ export type ControlPlaneSessionMutationAction = "reload" | "update";
77
+ export type ControlPlaneSessionMutationResult = {
78
+ ok: boolean;
79
+ action: ControlPlaneSessionMutationAction;
80
+ message: string;
81
+ details?: Record<string, unknown>;
82
+ };
83
+ export type ControlPlaneSessionLifecycle = {
84
+ reload: () => Promise<ControlPlaneSessionMutationResult>;
85
+ update: () => Promise<ControlPlaneSessionMutationResult>;
86
+ };
87
+ export type WakeDeliveryState = "queued" | "duplicate" | "skipped" | "delivered" | "retried" | "dead_letter";
88
+ export type WakeNotifyDecision = {
89
+ state: "queued" | "duplicate" | "skipped";
90
+ reason_code: string;
91
+ binding_id: string;
92
+ channel: Channel;
93
+ dedupe_key: string;
94
+ outbox_id: string | null;
95
+ };
96
+ export type WakeNotifyContext = {
97
+ wakeId: string;
98
+ wakeSource?: string | null;
99
+ programId?: string | null;
100
+ sourceTsMs?: number | null;
101
+ };
102
+ export type NotifyOperatorsOpts = {
103
+ message: string;
104
+ dedupeKey: string;
105
+ wake?: WakeNotifyContext | null;
106
+ metadata?: Record<string, unknown>;
107
+ };
108
+ export type NotifyOperatorsResult = {
109
+ queued: number;
110
+ duplicate: number;
111
+ skipped: number;
112
+ decisions: WakeNotifyDecision[];
113
+ };
114
+ export type WakeDeliveryEvent = {
115
+ state: "delivered" | "retried" | "dead_letter";
116
+ reason_code: string;
117
+ wake_id: string;
118
+ dedupe_key: string;
119
+ binding_id: string;
120
+ channel: Channel;
121
+ outbox_id: string;
122
+ outbox_dedupe_key: string;
123
+ attempt_count: number;
124
+ };
125
+ export type WakeDeliveryObserver = (event: WakeDeliveryEvent) => void | Promise<void>;
126
+ export type ControlPlaneHandle = {
127
+ activeAdapters: ActiveAdapter[];
128
+ handleWebhook(path: string, req: Request): Promise<Response | null>;
129
+ notifyOperators?(opts: NotifyOperatorsOpts): Promise<NotifyOperatorsResult>;
130
+ setWakeDeliveryObserver?(observer: WakeDeliveryObserver | null): void;
131
+ reloadTelegramGeneration?(opts: {
132
+ config: ControlPlaneConfig;
133
+ reason: string;
134
+ }): Promise<TelegramGenerationReloadResult>;
135
+ listRuns?(opts?: {
136
+ status?: string;
137
+ limit?: number;
138
+ }): Promise<ControlPlaneRunSnapshot[]>;
139
+ getRun?(idOrRoot: string): Promise<ControlPlaneRunSnapshot | null>;
140
+ /**
141
+ * Run lifecycle boundary: accepts start intent into the default queue/reconcile path.
142
+ * Compatibility adapters may dispatch immediately after enqueue, but must preserve queue invariants.
143
+ */
144
+ startRun?(opts: {
145
+ prompt: string;
146
+ maxSteps?: number;
147
+ }): Promise<ControlPlaneRunSnapshot>;
148
+ /**
149
+ * Run lifecycle boundary: accepts resume intent into the default queue/reconcile path.
150
+ * No flag-based alternate path is allowed.
151
+ */
152
+ resumeRun?(opts: {
153
+ rootIssueId: string;
154
+ maxSteps?: number;
155
+ }): Promise<ControlPlaneRunSnapshot>;
156
+ interruptRun?(opts: {
157
+ jobId?: string | null;
158
+ rootIssueId?: string | null;
159
+ }): Promise<ControlPlaneRunInterruptResult>;
160
+ heartbeatRun?(opts: {
161
+ jobId?: string | null;
162
+ rootIssueId?: string | null;
163
+ reason?: string | null;
164
+ wakeMode?: string | null;
165
+ }): Promise<ControlPlaneRunHeartbeatResult>;
166
+ traceRun?(opts: {
167
+ idOrRoot: string;
168
+ limit?: number;
169
+ }): Promise<ControlPlaneRunTrace | null>;
170
+ submitTerminalCommand?(opts: {
171
+ commandText: string;
172
+ repoRoot: string;
173
+ requestId?: string;
174
+ }): Promise<CommandPipelineResult>;
175
+ stop(): Promise<void>;
176
+ };
@@ -0,0 +1 @@
1
+ export { DEFAULT_INTER_ROOT_QUEUE_POLICY, normalizeInterRootQueuePolicy, ORCHESTRATION_QUEUE_ALLOWED_TRANSITIONS, ORCHESTRATION_QUEUE_INVARIANTS, } from "@femtomc/mu-orchestrator";
@@ -0,0 +1,63 @@
1
+ import { GenerationTelemetryRecorder, type ReloadableGenerationIdentity, type ReloadLifecycleReason } from "@femtomc/mu-control-plane";
2
+ import type { ControlPlaneConfig, ControlPlaneHandle, TelegramGenerationReloadResult } from "./control_plane_contract.js";
3
+ import { ControlPlaneGenerationSupervisor } from "./generation_supervisor.js";
4
+ export type ControlPlaneSummary = {
5
+ active: boolean;
6
+ adapters: string[];
7
+ routes: Array<{
8
+ name: string;
9
+ route: string;
10
+ }>;
11
+ };
12
+ export type ControlPlaneReloadResult = {
13
+ ok: boolean;
14
+ reason: string;
15
+ previous_control_plane: ControlPlaneSummary;
16
+ control_plane: ControlPlaneSummary;
17
+ generation: {
18
+ attempt_id: string;
19
+ coalesced: boolean;
20
+ from_generation: ReloadableGenerationIdentity | null;
21
+ to_generation: ReloadableGenerationIdentity;
22
+ active_generation: ReloadableGenerationIdentity | null;
23
+ outcome: "success" | "failure";
24
+ };
25
+ telegram_generation?: TelegramGenerationReloadResult;
26
+ error?: string;
27
+ };
28
+ export type ControlPlaneReloader = (opts: {
29
+ repoRoot: string;
30
+ previous: ControlPlaneHandle | null;
31
+ config: ControlPlaneConfig;
32
+ generation: ReloadableGenerationIdentity;
33
+ }) => Promise<ControlPlaneHandle | null>;
34
+ export type ConfigReader = (repoRoot: string) => Promise<import("./config.js").MuConfig>;
35
+ export type ConfigWriter = (repoRoot: string, config: import("./config.js").MuConfig) => Promise<string>;
36
+ export declare function summarizeControlPlane(handle: ControlPlaneHandle | null): ControlPlaneSummary;
37
+ export type ReloadManagerDeps = {
38
+ repoRoot: string;
39
+ initialControlPlane: ControlPlaneHandle | null;
40
+ controlPlaneReloader: ControlPlaneReloader;
41
+ generationTelemetry: GenerationTelemetryRecorder;
42
+ loadConfigFromDisk: () => Promise<import("./config.js").MuConfig>;
43
+ };
44
+ export type ReloadManager = {
45
+ reloadControlPlane: (reason: ReloadLifecycleReason) => Promise<ControlPlaneReloadResult>;
46
+ getControlPlaneStatus: () => {
47
+ active: boolean;
48
+ adapters: string[];
49
+ routes: Array<{
50
+ name: string;
51
+ route: string;
52
+ }>;
53
+ generation: import("@femtomc/mu-control-plane").GenerationSupervisorSnapshot;
54
+ observability: {
55
+ counters: ReturnType<GenerationTelemetryRecorder["counters"]>;
56
+ };
57
+ };
58
+ getControlPlaneCurrent: () => ControlPlaneHandle | null;
59
+ setControlPlaneCurrent: (handle: ControlPlaneHandle | null) => void;
60
+ generationSupervisor: ControlPlaneGenerationSupervisor;
61
+ generationTelemetry: GenerationTelemetryRecorder;
62
+ };
63
+ export declare function createReloadManager(deps: ReloadManagerDeps): ReloadManager;