@cleocode/contracts 2026.5.130 → 2026.5.131

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 (171) hide show
  1. package/dist/__tests__/atomic-tools-contract.test.d.ts +14 -0
  2. package/dist/__tests__/atomic-tools-contract.test.d.ts.map +1 -0
  3. package/dist/__tests__/atomic-tools-contract.test.js +55 -0
  4. package/dist/__tests__/atomic-tools-contract.test.js.map +1 -0
  5. package/dist/__tests__/gateway-contract.test.d.ts +18 -0
  6. package/dist/__tests__/gateway-contract.test.d.ts.map +1 -0
  7. package/dist/__tests__/gateway-contract.test.js +187 -0
  8. package/dist/__tests__/gateway-contract.test.js.map +1 -0
  9. package/dist/__tests__/mvi.test.d.ts +17 -0
  10. package/dist/__tests__/mvi.test.d.ts.map +1 -0
  11. package/dist/__tests__/mvi.test.js +147 -0
  12. package/dist/__tests__/mvi.test.js.map +1 -0
  13. package/dist/__tests__/operation-def.test.js +1 -1
  14. package/dist/__tests__/operation-def.test.js.map +1 -1
  15. package/dist/boundary.d.ts +26 -0
  16. package/dist/boundary.d.ts.map +1 -1
  17. package/dist/boundary.js +42 -4
  18. package/dist/boundary.js.map +1 -1
  19. package/dist/branch-lock.d.ts +35 -1
  20. package/dist/branch-lock.d.ts.map +1 -1
  21. package/dist/branch-lock.js +23 -2
  22. package/dist/branch-lock.js.map +1 -1
  23. package/dist/daemon/__tests__/health-roundtrip.test.d.ts +13 -0
  24. package/dist/daemon/__tests__/health-roundtrip.test.d.ts.map +1 -0
  25. package/dist/daemon/__tests__/health-roundtrip.test.js +69 -0
  26. package/dist/daemon/__tests__/health-roundtrip.test.js.map +1 -0
  27. package/dist/daemon/health.d.ts +108 -0
  28. package/dist/daemon/health.d.ts.map +1 -0
  29. package/dist/daemon/health.js +95 -0
  30. package/dist/daemon/health.js.map +1 -0
  31. package/dist/daemon/index.d.ts +24 -0
  32. package/dist/daemon/index.d.ts.map +1 -0
  33. package/dist/daemon/index.js +22 -0
  34. package/dist/daemon/index.js.map +1 -0
  35. package/dist/daemon/subsystem.d.ts +121 -0
  36. package/dist/daemon/subsystem.d.ts.map +1 -0
  37. package/dist/daemon/subsystem.js +28 -0
  38. package/dist/daemon/subsystem.js.map +1 -0
  39. package/dist/daemon-ipc/__tests__/daemon-ipc-freeze.test.d.ts +15 -0
  40. package/dist/daemon-ipc/__tests__/daemon-ipc-freeze.test.d.ts.map +1 -0
  41. package/dist/daemon-ipc/__tests__/daemon-ipc-freeze.test.js +40 -0
  42. package/dist/daemon-ipc/__tests__/daemon-ipc-freeze.test.js.map +1 -0
  43. package/dist/daemon-ipc/index.d.ts +44 -0
  44. package/dist/daemon-ipc/index.d.ts.map +1 -0
  45. package/dist/daemon-ipc/index.js +43 -0
  46. package/dist/daemon-ipc/index.js.map +1 -0
  47. package/dist/db-inventory.json +8 -8
  48. package/dist/dispatch/identity.d.ts +1 -1
  49. package/dist/dispatch/identity.d.ts.map +1 -1
  50. package/dist/dispatch/identity.js +3 -0
  51. package/dist/dispatch/identity.js.map +1 -1
  52. package/dist/dispatch/operation-def.d.ts +16 -0
  53. package/dist/dispatch/operation-def.d.ts.map +1 -1
  54. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  55. package/dist/dispatch/operations-registry.js +133 -0
  56. package/dist/dispatch/operations-registry.js.map +1 -1
  57. package/dist/exit-codes.d.ts +1 -1
  58. package/dist/exit-codes.js +1 -1
  59. package/dist/gateway/rpc/__tests__/freeze.test.d.ts +20 -0
  60. package/dist/gateway/rpc/__tests__/freeze.test.d.ts.map +1 -0
  61. package/dist/gateway/rpc/__tests__/freeze.test.js +122 -0
  62. package/dist/gateway/rpc/__tests__/freeze.test.js.map +1 -0
  63. package/dist/gateway/rpc/index.d.ts +23 -0
  64. package/dist/gateway/rpc/index.d.ts.map +1 -0
  65. package/dist/gateway/rpc/index.js +22 -0
  66. package/dist/gateway/rpc/index.js.map +1 -0
  67. package/dist/gateway/rpc/messages.d.ts +263 -0
  68. package/dist/gateway/rpc/messages.d.ts.map +1 -0
  69. package/dist/gateway/rpc/messages.js +152 -0
  70. package/dist/gateway/rpc/messages.js.map +1 -0
  71. package/dist/gateway/rpc/version.d.ts +39 -0
  72. package/dist/gateway/rpc/version.d.ts.map +1 -0
  73. package/dist/gateway/rpc/version.js +39 -0
  74. package/dist/gateway/rpc/version.js.map +1 -0
  75. package/dist/gateway.d.ts +239 -0
  76. package/dist/gateway.d.ts.map +1 -0
  77. package/dist/gateway.js +171 -0
  78. package/dist/gateway.js.map +1 -0
  79. package/dist/goal.d.ts +314 -0
  80. package/dist/goal.d.ts.map +1 -0
  81. package/dist/goal.js +118 -0
  82. package/dist/goal.js.map +1 -0
  83. package/dist/index.d.ts +9 -0
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +10 -0
  86. package/dist/index.js.map +1 -1
  87. package/dist/mvi.d.ts +209 -0
  88. package/dist/mvi.d.ts.map +1 -0
  89. package/dist/mvi.js +99 -0
  90. package/dist/mvi.js.map +1 -0
  91. package/dist/operations/attention.d.ts +114 -0
  92. package/dist/operations/attention.d.ts.map +1 -0
  93. package/dist/operations/attention.js +22 -0
  94. package/dist/operations/attention.js.map +1 -0
  95. package/dist/operations/focus.d.ts +25 -0
  96. package/dist/operations/focus.d.ts.map +1 -1
  97. package/dist/operations/index.d.ts +1 -0
  98. package/dist/operations/index.d.ts.map +1 -1
  99. package/dist/operations/index.js +1 -0
  100. package/dist/operations/index.js.map +1 -1
  101. package/dist/operations/session.d.ts +11 -0
  102. package/dist/operations/session.d.ts.map +1 -1
  103. package/dist/sdk-tool.d.ts +1 -1
  104. package/dist/sdk-tool.js +1 -1
  105. package/dist/supervisor-ipc/__tests__/freeze.test.d.ts +15 -0
  106. package/dist/supervisor-ipc/__tests__/freeze.test.d.ts.map +1 -0
  107. package/dist/supervisor-ipc/__tests__/freeze.test.js +159 -0
  108. package/dist/supervisor-ipc/__tests__/freeze.test.js.map +1 -0
  109. package/dist/supervisor-ipc/index.d.ts +15 -0
  110. package/dist/supervisor-ipc/index.d.ts.map +1 -0
  111. package/dist/supervisor-ipc/index.js +14 -0
  112. package/dist/supervisor-ipc/index.js.map +1 -0
  113. package/dist/supervisor-ipc/messages.d.ts +457 -0
  114. package/dist/supervisor-ipc/messages.d.ts.map +1 -0
  115. package/dist/supervisor-ipc/messages.js +312 -0
  116. package/dist/supervisor-ipc/messages.js.map +1 -0
  117. package/dist/supervisor-ipc/version.d.ts +29 -0
  118. package/dist/supervisor-ipc/version.d.ts.map +1 -0
  119. package/dist/supervisor-ipc/version.js +29 -0
  120. package/dist/supervisor-ipc/version.js.map +1 -0
  121. package/dist/tools/__tests__/composite.test.d.ts +14 -0
  122. package/dist/tools/__tests__/composite.test.d.ts.map +1 -0
  123. package/dist/tools/__tests__/composite.test.js +75 -0
  124. package/dist/tools/__tests__/composite.test.js.map +1 -0
  125. package/dist/tools/atomic.d.ts +195 -0
  126. package/dist/tools/atomic.d.ts.map +1 -0
  127. package/dist/tools/atomic.js +82 -0
  128. package/dist/tools/atomic.js.map +1 -0
  129. package/dist/tools/composite.d.ts +128 -0
  130. package/dist/tools/composite.d.ts.map +1 -0
  131. package/dist/tools/composite.js +88 -0
  132. package/dist/tools/composite.js.map +1 -0
  133. package/package.json +34 -2
  134. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  135. package/src/__tests__/atomic-tools-contract.test.ts +67 -0
  136. package/src/__tests__/gateway-contract.test.ts +219 -0
  137. package/src/__tests__/mvi.test.ts +175 -0
  138. package/src/__tests__/operation-def.test.ts +2 -1
  139. package/src/boundary.ts +74 -4
  140. package/src/branch-lock.ts +49 -2
  141. package/src/daemon/__tests__/health-roundtrip.test.ts +84 -0
  142. package/src/daemon/health.ts +121 -0
  143. package/src/daemon/index.ts +35 -0
  144. package/src/daemon/subsystem.ts +126 -0
  145. package/src/daemon-ipc/__tests__/daemon-ipc-freeze.test.ts +53 -0
  146. package/src/daemon-ipc/index.ts +90 -0
  147. package/src/db-inventory.json +8 -8
  148. package/src/dispatch/identity.ts +3 -0
  149. package/src/dispatch/operation-def.ts +16 -0
  150. package/src/dispatch/operations-registry.ts +140 -0
  151. package/src/exit-codes.ts +1 -1
  152. package/src/gateway/rpc/__tests__/freeze.test.ts +139 -0
  153. package/src/gateway/rpc/index.ts +43 -0
  154. package/src/gateway/rpc/messages.ts +187 -0
  155. package/src/gateway/rpc/version.ts +40 -0
  156. package/src/gateway.ts +252 -0
  157. package/src/goal.ts +348 -0
  158. package/src/index.ts +109 -0
  159. package/src/mvi.ts +236 -0
  160. package/src/operations/attention.ts +117 -0
  161. package/src/operations/focus.ts +26 -0
  162. package/src/operations/index.ts +1 -0
  163. package/src/operations/session.ts +11 -0
  164. package/src/sdk-tool.ts +1 -1
  165. package/src/supervisor-ipc/__tests__/freeze.test.ts +180 -0
  166. package/src/supervisor-ipc/index.ts +62 -0
  167. package/src/supervisor-ipc/messages.ts +396 -0
  168. package/src/supervisor-ipc/version.ts +30 -0
  169. package/src/tools/__tests__/composite.test.ts +88 -0
  170. package/src/tools/atomic.ts +274 -0
  171. package/src/tools/composite.ts +173 -0
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Schema-drift guard for the FROZEN `supervisor-ipc` v1.0 contract (T11339 AC5).
3
+ *
4
+ * Pins the v1.0 protocol version and the exact request/response message-kind
5
+ * set. If a future change adds, removes, or renames a message kind without
6
+ * bumping to a new contract revision, these assertions fail — forcing an
7
+ * explicit, reviewed decision rather than silent drift that would break R2
8
+ * (T11253).
9
+ *
10
+ * Also asserts the Rust wire shape (snake_case keys, `direction` + `kind`
11
+ * discriminators) round-trips through the Zod schemas, proving the TypeScript
12
+ * and Rust sides agree on the same bytes.
13
+ */
14
+
15
+ import { describe, expect, it } from 'vitest';
16
+ import {
17
+ SUPERVISOR_IPC_MESSAGE_KINDS,
18
+ SUPERVISOR_IPC_PROTOCOL_VERSION,
19
+ SUPERVISOR_IPC_REQUEST_KINDS,
20
+ SUPERVISOR_IPC_RESPONSE_KINDS,
21
+ SupervisorIpcEnvelopeSchema,
22
+ SupervisorIpcRequestSchema,
23
+ SupervisorIpcResponseSchema,
24
+ } from '../index.js';
25
+
26
+ describe('supervisor-ipc v1.0 freeze guard (T11339)', () => {
27
+ it('pins the frozen protocol version', () => {
28
+ expect(SUPERVISOR_IPC_PROTOCOL_VERSION).toBe('1.0.0');
29
+ });
30
+
31
+ it('pins the exact frozen request kind set', () => {
32
+ expect([...SUPERVISOR_IPC_REQUEST_KINDS]).toEqual(['spawn', 'restart', 'monitor', 'health']);
33
+ });
34
+
35
+ it('pins the exact frozen response kind set', () => {
36
+ expect([...SUPERVISOR_IPC_RESPONSE_KINDS]).toEqual([
37
+ 'spawned',
38
+ 'restarted',
39
+ 'monitor',
40
+ 'health',
41
+ 'event',
42
+ 'error',
43
+ ]);
44
+ });
45
+
46
+ it('the union schemas cover exactly the frozen kinds', () => {
47
+ // Every frozen request kind must validate against the request union.
48
+ for (const kind of SUPERVISOR_IPC_REQUEST_KINDS) {
49
+ const sample = sampleRequestFor(kind);
50
+ expect(SupervisorIpcRequestSchema.safeParse(sample).success).toBe(true);
51
+ }
52
+ for (const kind of SUPERVISOR_IPC_RESPONSE_KINDS) {
53
+ const sample = sampleResponseFor(kind);
54
+ expect(SupervisorIpcResponseSchema.safeParse(sample).success).toBe(true);
55
+ }
56
+ expect(SUPERVISOR_IPC_MESSAGE_KINDS).toHaveLength(
57
+ SUPERVISOR_IPC_REQUEST_KINDS.length + SUPERVISOR_IPC_RESPONSE_KINDS.length,
58
+ );
59
+ });
60
+
61
+ // T11369 (R2 AC8/AC9) — pin the EXACT complete message-kind tuple. Any
62
+ // in-place add/rename/remove to the v1.0 set fails here, forcing a new
63
+ // versioned directory + version-negotiation shim per the contract-evolution
64
+ // policy (doc slug `daemon-ipc-contract-evolution-policy`) rather than silent
65
+ // drift that would break R4-R7 adapters.
66
+ it('pins the EXACT complete frozen message-kind set (T11369)', () => {
67
+ expect([...SUPERVISOR_IPC_MESSAGE_KINDS]).toEqual([
68
+ 'spawn',
69
+ 'restart',
70
+ 'monitor',
71
+ 'health',
72
+ 'spawned',
73
+ 'restarted',
74
+ 'monitor',
75
+ 'health',
76
+ 'event',
77
+ 'error',
78
+ ]);
79
+ });
80
+ });
81
+
82
+ describe('supervisor-ipc v1.0 wire-shape parity with Rust serde (T11339 AC3)', () => {
83
+ it('parses the exact JSON a Rust request envelope serializes', () => {
84
+ // This is the literal JSON shape produced by
85
+ // cleo_supervisor::ipc::IpcEnvelope::request(...).to_ndjson().
86
+ const wire = {
87
+ protocol_version: '1.0.0',
88
+ id: 'abc',
89
+ direction: 'request',
90
+ request: {
91
+ kind: 'spawn',
92
+ child_id: 'w1',
93
+ program: '/usr/bin/node',
94
+ args: ['build/index.js'],
95
+ env: [{ key: 'PORT', value: '3456' }],
96
+ cwd: '/srv/studio',
97
+ },
98
+ };
99
+ const parsed = SupervisorIpcEnvelopeSchema.safeParse(wire);
100
+ expect(parsed.success).toBe(true);
101
+ if (parsed.success && parsed.data.direction === 'request') {
102
+ expect(parsed.data.request.kind).toBe('spawn');
103
+ }
104
+ });
105
+
106
+ it('parses the exact JSON a Rust response envelope serializes', () => {
107
+ const wire = {
108
+ protocol_version: '1.0.0',
109
+ id: 'abc',
110
+ direction: 'response',
111
+ response: {
112
+ kind: 'health',
113
+ pid: 100,
114
+ child_count: 1,
115
+ uptime_secs: 60,
116
+ protocol_version: '1.0.0',
117
+ },
118
+ };
119
+ const parsed = SupervisorIpcEnvelopeSchema.safeParse(wire);
120
+ expect(parsed.success).toBe(true);
121
+ });
122
+
123
+ it('parses a Rust lifecycle event with the optional restart_delay_ms field', () => {
124
+ const wire = {
125
+ protocol_version: '1.0.0',
126
+ id: 'evt-1',
127
+ direction: 'response',
128
+ response: {
129
+ kind: 'event',
130
+ event: 'child_restarted',
131
+ child_id: 'w1',
132
+ exit_code: 1,
133
+ restart_delay_ms: 2000,
134
+ },
135
+ };
136
+ expect(SupervisorIpcEnvelopeSchema.safeParse(wire).success).toBe(true);
137
+ });
138
+
139
+ it('rejects an unknown message kind (proves the union is closed)', () => {
140
+ const wire = {
141
+ protocol_version: '1.0.0',
142
+ id: 'abc',
143
+ direction: 'request',
144
+ request: { kind: 'teleport', child_id: 'w1' },
145
+ };
146
+ expect(SupervisorIpcEnvelopeSchema.safeParse(wire).success).toBe(false);
147
+ });
148
+ });
149
+
150
+ /** Build a minimal valid request payload for the given kind. */
151
+ function sampleRequestFor(kind: (typeof SUPERVISOR_IPC_REQUEST_KINDS)[number]): unknown {
152
+ switch (kind) {
153
+ case 'spawn':
154
+ return { kind, child_id: 'w1', program: '/bin/true', args: [], env: [] };
155
+ case 'restart':
156
+ return { kind, child_id: 'w1' };
157
+ case 'monitor':
158
+ return { kind };
159
+ case 'health':
160
+ return { kind };
161
+ }
162
+ }
163
+
164
+ /** Build a minimal valid response payload for the given kind. */
165
+ function sampleResponseFor(kind: (typeof SUPERVISOR_IPC_RESPONSE_KINDS)[number]): unknown {
166
+ switch (kind) {
167
+ case 'spawned':
168
+ return { kind, child_id: 'w1', pid: 1 };
169
+ case 'restarted':
170
+ return { kind, child_id: 'w1', pid: 1, restart_count: 1 };
171
+ case 'monitor':
172
+ return { kind, children: [] };
173
+ case 'health':
174
+ return { kind, pid: 1, child_count: 0, uptime_secs: 1, protocol_version: '1.0.0' };
175
+ case 'event':
176
+ return { kind, event: 'child_exited', child_id: 'w1' };
177
+ case 'error':
178
+ return { kind, code: 'E_X', message: 'x' };
179
+ }
180
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * `supervisor-ipc` v1.0 — FROZEN IPC contract barrel.
3
+ *
4
+ * Re-exports the version constants and Zod message schemas + inferred types for
5
+ * the CLEO native process supervisor (`crates/cleo-supervisor`). R2 (T11253)
6
+ * consumes this contract; it is frozen at v1.0 — see
7
+ * {@link SUPERVISOR_IPC_PROTOCOL_VERSION}.
8
+ *
9
+ * @task T11339
10
+ * @packageDocumentation
11
+ */
12
+
13
+ export type {
14
+ ChildState,
15
+ ChildStatus,
16
+ EnvPair,
17
+ ErrorResponse,
18
+ HealthRequest,
19
+ HealthResponse,
20
+ LifecycleEventKind,
21
+ LifecycleEventResponse,
22
+ MonitorRequest,
23
+ MonitorResponse,
24
+ RestartedResponse,
25
+ RestartRequest,
26
+ SpawnedResponse,
27
+ SpawnRequest,
28
+ SupervisorIpcEnvelope,
29
+ SupervisorIpcRequest,
30
+ SupervisorIpcRequestEnvelope,
31
+ SupervisorIpcResponse,
32
+ SupervisorIpcResponseEnvelope,
33
+ } from './messages.js';
34
+
35
+ export {
36
+ ChildStateSchema,
37
+ ChildStatusSchema,
38
+ EnvPairSchema,
39
+ ErrorResponseSchema,
40
+ HealthRequestSchema,
41
+ HealthResponseSchema,
42
+ LifecycleEventKindSchema,
43
+ LifecycleEventResponseSchema,
44
+ MonitorRequestSchema,
45
+ MonitorResponseSchema,
46
+ RestartedResponseSchema,
47
+ RestartRequestSchema,
48
+ SpawnedResponseSchema,
49
+ SpawnRequestSchema,
50
+ SUPERVISOR_IPC_MESSAGE_KINDS,
51
+ SUPERVISOR_IPC_REQUEST_KINDS,
52
+ SUPERVISOR_IPC_RESPONSE_KINDS,
53
+ SupervisorIpcEnvelopeSchema,
54
+ SupervisorIpcRequestEnvelopeSchema,
55
+ SupervisorIpcRequestSchema,
56
+ SupervisorIpcResponseEnvelopeSchema,
57
+ SupervisorIpcResponseSchema,
58
+ } from './messages.js';
59
+ export {
60
+ SUPERVISOR_IPC_CHANNEL_BASENAME,
61
+ SUPERVISOR_IPC_PROTOCOL_VERSION,
62
+ } from './version.js';
@@ -0,0 +1,396 @@
1
+ /**
2
+ * `supervisor-ipc` v1.0 — FROZEN message contract (Zod schemas).
3
+ *
4
+ * This is the byte-for-byte TypeScript mirror of the Rust serde types in
5
+ * `crates/cleo-supervisor/src/ipc.rs`. The wire format is newline-delimited
6
+ * JSON (NDJSON): one {@link SupervisorIpcEnvelope} per line. The supervisor
7
+ * (Rust) is the server; CLEO/Studio (TypeScript) are clients that send
8
+ * {@link SupervisorIpcRequest}s and receive {@link SupervisorIpcResponse}s and
9
+ * unsolicited {@link LifecycleEvent}s.
10
+ *
11
+ * Wire-shape contract (snake_case keys — matches serde defaults):
12
+ *
13
+ * ```jsonc
14
+ * // request
15
+ * { "protocol_version": "1.0.0", "id": "abc", "direction": "request",
16
+ * "request": { "kind": "spawn", "child_id": "w1", "program": "/bin/node",
17
+ * "args": [], "env": [] } }
18
+ * // response
19
+ * { "protocol_version": "1.0.0", "id": "abc", "direction": "response",
20
+ * "response": { "kind": "spawned", "child_id": "w1", "pid": 4242 } }
21
+ * ```
22
+ *
23
+ * FROZEN v1.0: R2 (T11253) consumes this exact set. Do not add/rename/remove
24
+ * fields in place — see {@link SUPERVISOR_IPC_PROTOCOL_VERSION}.
25
+ *
26
+ * @task T11339
27
+ * @packageDocumentation
28
+ */
29
+
30
+ import { z } from 'zod';
31
+
32
+ // ─── Shared value types ───────────────────────────────────────────────────────
33
+
34
+ /**
35
+ * A single environment key/value override applied to a spawned child, layered
36
+ * on top of the supervisor's own environment. Mirrors `EnvPair` in Rust.
37
+ */
38
+ export const EnvPairSchema = z
39
+ .object({
40
+ /** Environment variable name. */
41
+ key: z.string().min(1),
42
+ /** Environment variable value. */
43
+ value: z.string(),
44
+ })
45
+ .strict();
46
+
47
+ /** A single environment key/value override (inferred from {@link EnvPairSchema}). */
48
+ export type EnvPair = z.infer<typeof EnvPairSchema>;
49
+
50
+ /**
51
+ * Liveness state of a supervised child. Mirrors `ChildState` in Rust.
52
+ *
53
+ * - `running` — the child process is alive.
54
+ * - `restarting` — the child exited and a backoff-delayed restart is pending.
55
+ * - `stopped` — the child exited and will not be restarted.
56
+ */
57
+ export const ChildStateSchema = z.enum(['running', 'restarting', 'stopped']);
58
+
59
+ /** Liveness state of a supervised child (inferred). */
60
+ export type ChildState = z.infer<typeof ChildStateSchema>;
61
+
62
+ /**
63
+ * A single child's status row in a monitor snapshot. Mirrors `ChildStatus`.
64
+ */
65
+ export const ChildStatusSchema = z
66
+ .object({
67
+ /** Logical id of the child (stable across restarts). */
68
+ child_id: z.string().min(1),
69
+ /** Current OS pid (0 when not currently running). */
70
+ pid: z.number().int().nonnegative(),
71
+ /** Current liveness state. */
72
+ state: ChildStateSchema,
73
+ /** Total restarts observed for this child. */
74
+ restart_count: z.number().int().nonnegative(),
75
+ })
76
+ .strict();
77
+
78
+ /** A single child's status row (inferred from {@link ChildStatusSchema}). */
79
+ export type ChildStatus = z.infer<typeof ChildStatusSchema>;
80
+
81
+ // ─── Requests (client → supervisor) ───────────────────────────────────────────
82
+
83
+ /**
84
+ * Ask the supervisor to spawn a new child worker. Mirrors `SpawnRequest`.
85
+ */
86
+ export const SpawnRequestSchema = z
87
+ .object({
88
+ /** Tag discriminating this request variant. */
89
+ kind: z.literal('spawn'),
90
+ /** Caller-chosen logical id for the child (stable across restarts). */
91
+ child_id: z.string().min(1),
92
+ /** Absolute path to the program to execute. */
93
+ program: z.string().min(1),
94
+ /** Arguments passed to the program. */
95
+ args: z.array(z.string()).default([]),
96
+ /** Environment overrides applied on top of the supervisor's environment. */
97
+ env: z.array(EnvPairSchema).default([]),
98
+ /** Optional working directory for the child. */
99
+ cwd: z.string().optional(),
100
+ })
101
+ .strict();
102
+
103
+ /** Spawn request payload (inferred from {@link SpawnRequestSchema}). */
104
+ export type SpawnRequest = z.infer<typeof SpawnRequestSchema>;
105
+
106
+ /**
107
+ * Ask the supervisor to restart an existing child by id. Mirrors `RestartRequest`.
108
+ */
109
+ export const RestartRequestSchema = z
110
+ .object({
111
+ /** Tag discriminating this request variant. */
112
+ kind: z.literal('restart'),
113
+ /** Logical id of the child to restart. */
114
+ child_id: z.string().min(1),
115
+ })
116
+ .strict();
117
+
118
+ /** Restart request payload (inferred from {@link RestartRequestSchema}). */
119
+ export type RestartRequest = z.infer<typeof RestartRequestSchema>;
120
+
121
+ /**
122
+ * Ask the supervisor for the status of one or all children. Mirrors `MonitorRequest`.
123
+ */
124
+ export const MonitorRequestSchema = z
125
+ .object({
126
+ /** Tag discriminating this request variant. */
127
+ kind: z.literal('monitor'),
128
+ /** Specific child id to monitor; omit to request all children. */
129
+ child_id: z.string().optional(),
130
+ })
131
+ .strict();
132
+
133
+ /** Monitor request payload (inferred from {@link MonitorRequestSchema}). */
134
+ export type MonitorRequest = z.infer<typeof MonitorRequestSchema>;
135
+
136
+ /**
137
+ * Ask the supervisor for its own health. Mirrors `HealthRequest` (no fields
138
+ * beyond the discriminator).
139
+ */
140
+ export const HealthRequestSchema = z
141
+ .object({
142
+ /** Tag discriminating this request variant. */
143
+ kind: z.literal('health'),
144
+ })
145
+ .strict();
146
+
147
+ /** Health request payload (inferred from {@link HealthRequestSchema}). */
148
+ export type HealthRequest = z.infer<typeof HealthRequestSchema>;
149
+
150
+ /**
151
+ * The discriminated union of all client → supervisor requests. The `kind`
152
+ * field selects the variant, matching the serde `#[serde(tag = "kind")]`
153
+ * tagging on the Rust `IpcRequest` enum.
154
+ */
155
+ export const SupervisorIpcRequestSchema = z.discriminatedUnion('kind', [
156
+ SpawnRequestSchema,
157
+ RestartRequestSchema,
158
+ MonitorRequestSchema,
159
+ HealthRequestSchema,
160
+ ]);
161
+
162
+ /** Any supervisor IPC request (inferred from {@link SupervisorIpcRequestSchema}). */
163
+ export type SupervisorIpcRequest = z.infer<typeof SupervisorIpcRequestSchema>;
164
+
165
+ // ─── Responses (supervisor → client) ──────────────────────────────────────────
166
+
167
+ /**
168
+ * Result of a {@link SpawnRequest}. Mirrors `SpawnResult` carried by the
169
+ * `spawned` response variant.
170
+ */
171
+ export const SpawnedResponseSchema = z
172
+ .object({
173
+ /** Tag discriminating this response variant. */
174
+ kind: z.literal('spawned'),
175
+ /** Logical id of the spawned child. */
176
+ child_id: z.string().min(1),
177
+ /** OS pid assigned to the spawned child. */
178
+ pid: z.number().int().nonnegative(),
179
+ })
180
+ .strict();
181
+
182
+ /** Spawned response payload (inferred from {@link SpawnedResponseSchema}). */
183
+ export type SpawnedResponse = z.infer<typeof SpawnedResponseSchema>;
184
+
185
+ /**
186
+ * Result of a {@link RestartRequest}. Mirrors `RestartResult`.
187
+ */
188
+ export const RestartedResponseSchema = z
189
+ .object({
190
+ /** Tag discriminating this response variant. */
191
+ kind: z.literal('restarted'),
192
+ /** Logical id of the restarted child. */
193
+ child_id: z.string().min(1),
194
+ /** New OS pid after the restart. */
195
+ pid: z.number().int().nonnegative(),
196
+ /** Number of times this child has been restarted in total. */
197
+ restart_count: z.number().int().nonnegative(),
198
+ })
199
+ .strict();
200
+
201
+ /** Restarted response payload (inferred from {@link RestartedResponseSchema}). */
202
+ export type RestartedResponse = z.infer<typeof RestartedResponseSchema>;
203
+
204
+ /**
205
+ * A monitor snapshot for one or more children. Mirrors `MonitorResult`.
206
+ */
207
+ export const MonitorResponseSchema = z
208
+ .object({
209
+ /** Tag discriminating this response variant. */
210
+ kind: z.literal('monitor'),
211
+ /** One row per monitored child. */
212
+ children: z.array(ChildStatusSchema),
213
+ })
214
+ .strict();
215
+
216
+ /** Monitor response payload (inferred from {@link MonitorResponseSchema}). */
217
+ export type MonitorResponse = z.infer<typeof MonitorResponseSchema>;
218
+
219
+ /**
220
+ * A health snapshot for the supervisor itself. Mirrors `HealthResult`.
221
+ */
222
+ export const HealthResponseSchema = z
223
+ .object({
224
+ /** Tag discriminating this response variant. */
225
+ kind: z.literal('health'),
226
+ /** Supervisor process pid. */
227
+ pid: z.number().int().nonnegative(),
228
+ /** Number of children currently tracked. */
229
+ child_count: z.number().int().nonnegative(),
230
+ /** Seconds the supervisor has been running. */
231
+ uptime_secs: z.number().int().nonnegative(),
232
+ /** Frozen protocol version the supervisor speaks. */
233
+ protocol_version: z.string(),
234
+ })
235
+ .strict();
236
+
237
+ /** Health response payload (inferred from {@link HealthResponseSchema}). */
238
+ export type HealthResponse = z.infer<typeof HealthResponseSchema>;
239
+
240
+ /**
241
+ * Lifecycle event kind. Mirrors `LifecycleEventKind`.
242
+ *
243
+ * - `child_exited` — a child process exited.
244
+ * - `child_restarted` — a child was restarted after a crash.
245
+ */
246
+ export const LifecycleEventKindSchema = z.enum(['child_exited', 'child_restarted']);
247
+
248
+ /** Lifecycle event kind (inferred from {@link LifecycleEventKindSchema}). */
249
+ export type LifecycleEventKind = z.infer<typeof LifecycleEventKindSchema>;
250
+
251
+ /**
252
+ * An unsolicited lifecycle event broadcast to all connected clients. Mirrors
253
+ * the `event` response variant wrapping `LifecycleEvent`.
254
+ */
255
+ export const LifecycleEventResponseSchema = z
256
+ .object({
257
+ /** Tag discriminating this response variant. */
258
+ kind: z.literal('event'),
259
+ /** What happened. */
260
+ event: LifecycleEventKindSchema,
261
+ /** Logical id of the affected child. */
262
+ child_id: z.string().min(1),
263
+ /** Exit code, when the OS reported one. */
264
+ exit_code: z.number().int().optional(),
265
+ /** Terminating signal name, when the child was signalled (Unix). */
266
+ signal: z.string().optional(),
267
+ /** Backoff delay (ms) before the pending restart, when applicable. */
268
+ restart_delay_ms: z.number().int().nonnegative().optional(),
269
+ })
270
+ .strict();
271
+
272
+ /** Lifecycle event response payload (inferred from {@link LifecycleEventResponseSchema}). */
273
+ export type LifecycleEventResponse = z.infer<typeof LifecycleEventResponseSchema>;
274
+
275
+ /**
276
+ * An error response correlated to a request. Mirrors `ErrorResult`.
277
+ */
278
+ export const ErrorResponseSchema = z
279
+ .object({
280
+ /** Tag discriminating this response variant. */
281
+ kind: z.literal('error'),
282
+ /** Machine-readable error code (e.g. `E_UNKNOWN_CHILD`). */
283
+ code: z.string().min(1),
284
+ /** Human-readable error message. */
285
+ message: z.string(),
286
+ })
287
+ .strict();
288
+
289
+ /** Error response payload (inferred from {@link ErrorResponseSchema}). */
290
+ export type ErrorResponse = z.infer<typeof ErrorResponseSchema>;
291
+
292
+ /**
293
+ * The discriminated union of all supervisor → client responses and events. The
294
+ * `kind` field selects the variant, matching the serde `#[serde(tag = "kind")]`
295
+ * tagging on the Rust `IpcResponse` enum.
296
+ */
297
+ export const SupervisorIpcResponseSchema = z.discriminatedUnion('kind', [
298
+ SpawnedResponseSchema,
299
+ RestartedResponseSchema,
300
+ MonitorResponseSchema,
301
+ HealthResponseSchema,
302
+ LifecycleEventResponseSchema,
303
+ ErrorResponseSchema,
304
+ ]);
305
+
306
+ /** Any supervisor IPC response (inferred from {@link SupervisorIpcResponseSchema}). */
307
+ export type SupervisorIpcResponse = z.infer<typeof SupervisorIpcResponseSchema>;
308
+
309
+ // ─── Envelope ──────────────────────────────────────────────────────────────────
310
+
311
+ /**
312
+ * A client → supervisor request envelope.
313
+ *
314
+ * The `direction` discriminator + flattened `request` payload mirror the serde
315
+ * `#[serde(tag = "direction")]` `IpcPayload::Request { request }` variant.
316
+ */
317
+ export const SupervisorIpcRequestEnvelopeSchema = z
318
+ .object({
319
+ /** Frozen protocol version. */
320
+ protocol_version: z.string(),
321
+ /** Correlation id echoed back on the matching response. */
322
+ id: z.string().min(1),
323
+ /** Direction discriminator. */
324
+ direction: z.literal('request'),
325
+ /** The request body. */
326
+ request: SupervisorIpcRequestSchema,
327
+ })
328
+ .strict();
329
+
330
+ /** Request envelope (inferred from {@link SupervisorIpcRequestEnvelopeSchema}). */
331
+ export type SupervisorIpcRequestEnvelope = z.infer<typeof SupervisorIpcRequestEnvelopeSchema>;
332
+
333
+ /**
334
+ * A supervisor → client response/event envelope.
335
+ *
336
+ * The `direction` discriminator + flattened `response` payload mirror the serde
337
+ * `IpcPayload::Response { response }` variant.
338
+ */
339
+ export const SupervisorIpcResponseEnvelopeSchema = z
340
+ .object({
341
+ /** Frozen protocol version. */
342
+ protocol_version: z.string(),
343
+ /** Correlation id echoed from the originating request (or a fresh id for events). */
344
+ id: z.string().min(1),
345
+ /** Direction discriminator. */
346
+ direction: z.literal('response'),
347
+ /** The response body. */
348
+ response: SupervisorIpcResponseSchema,
349
+ })
350
+ .strict();
351
+
352
+ /** Response envelope (inferred from {@link SupervisorIpcResponseEnvelopeSchema}). */
353
+ export type SupervisorIpcResponseEnvelope = z.infer<typeof SupervisorIpcResponseEnvelopeSchema>;
354
+
355
+ /**
356
+ * The top-level IPC envelope: a versioned, correlated wrapper that is either a
357
+ * request or a response, discriminated by `direction`. One envelope per NDJSON
358
+ * line. Mirrors the Rust `IpcEnvelope`.
359
+ */
360
+ export const SupervisorIpcEnvelopeSchema = z.discriminatedUnion('direction', [
361
+ SupervisorIpcRequestEnvelopeSchema,
362
+ SupervisorIpcResponseEnvelopeSchema,
363
+ ]);
364
+
365
+ /** Any supervisor IPC envelope (inferred from {@link SupervisorIpcEnvelopeSchema}). */
366
+ export type SupervisorIpcEnvelope = z.infer<typeof SupervisorIpcEnvelopeSchema>;
367
+
368
+ // ─── Frozen message-set guard ──────────────────────────────────────────────────
369
+
370
+ /**
371
+ * The FROZEN v1.0 request `kind` values. The schema-drift guard test pins this
372
+ * tuple; any addition/removal is a contract-breaking change.
373
+ */
374
+ export const SUPERVISOR_IPC_REQUEST_KINDS = ['spawn', 'restart', 'monitor', 'health'] as const;
375
+
376
+ /**
377
+ * The FROZEN v1.0 response `kind` values. The schema-drift guard test pins this
378
+ * tuple; any addition/removal is a contract-breaking change.
379
+ */
380
+ export const SUPERVISOR_IPC_RESPONSE_KINDS = [
381
+ 'spawned',
382
+ 'restarted',
383
+ 'monitor',
384
+ 'health',
385
+ 'event',
386
+ 'error',
387
+ ] as const;
388
+
389
+ /**
390
+ * The complete FROZEN v1.0 message kind set (requests + responses), used by the
391
+ * drift guard on both the TS and Rust sides.
392
+ */
393
+ export const SUPERVISOR_IPC_MESSAGE_KINDS = [
394
+ ...SUPERVISOR_IPC_REQUEST_KINDS,
395
+ ...SUPERVISOR_IPC_RESPONSE_KINDS,
396
+ ] as const;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * FROZEN protocol version for the `supervisor-ipc` v1.0 contract.
3
+ *
4
+ * The Rust supervisor (`crates/cleo-supervisor/src/ipc.rs`) and this TypeScript
5
+ * contract MUST agree on this value. R2 (T11253) consumes the v1.0 message set
6
+ * without churn, so the version is **frozen**: bump only via a new major
7
+ * contract revision in a new directory, never edit this value in place.
8
+ *
9
+ * @see {@link SUPERVISOR_IPC_MESSAGE_KINDS} for the frozen v1.0 message set.
10
+ * @task T11339
11
+ * @packageDocumentation
12
+ */
13
+
14
+ /**
15
+ * The frozen `supervisor-ipc` protocol version string.
16
+ *
17
+ * Mirrors `cleo_supervisor::ipc::IPC_PROTOCOL_VERSION` on the Rust side. Both a
18
+ * Rust schema-drift guard (`crates/cleo-supervisor` `ipc::tests`) and the TS
19
+ * drift test (`__tests__/freeze.test.ts`) pin this exact value.
20
+ */
21
+ export const SUPERVISOR_IPC_PROTOCOL_VERSION = '1.0.0' as const;
22
+
23
+ /**
24
+ * Default base name for the IPC channel.
25
+ *
26
+ * Unix sockets are created under the CLEO home as `<basename>.sock`; Windows
27
+ * named pipes use `\\.\pipe\<basename>.<pid>`. Mirrors
28
+ * `cleo_supervisor::ipc::IPC_CHANNEL_BASENAME`.
29
+ */
30
+ export const SUPERVISOR_IPC_CHANNEL_BASENAME = 'cleo-supervisor' as const;