@knpkv/herdr-coordinator 0.2.1 → 0.3.0

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.
@@ -0,0 +1,875 @@
1
+ import { NodeCrypto, NodeFileSystem, NodePath } from "@effect/platform-node";
2
+ import { SqliteClient } from "@effect/sql-sqlite-node";
3
+ import { makeSqliteWorkBridge } from "@knpkv/herdr-work/sql";
4
+ import { Clock, Context, Crypto, Effect, Equal, FileSystem, Layer, Option, Path, Schedule, Schema, Stream } from "effect";
5
+ import * as SqlClient from "effect/sql/SqlClient";
6
+ import { OrchestratorConflictError, OrchestratorNotFoundError, OrchestratorStorageError, OrchestratorTransitionError, OrchestratorValidationError, OrchestratorWorkerBindingConflictError, OrchestratorWorkerStartAuthorityError, OrchestratorWorkRevisionConflictError } from "./orchestrator-errors.js";
7
+ import { ActivityIdempotencyKey, DispatchRequestId, OrchestratorCommand, OrchestratorDispatchActivated, OrchestratorDispatchActivation, OrchestratorEvent, OrchestratorEventDetail, OrchestratorIdempotencyKey, OrchestratorPendingDispatch, OrchestratorPendingQuery, OrchestratorReceipt, OrchestratorRequest, OrchestratorResult, OrchestratorRoute, OrchestratorRoutedSubmission, OrchestratorSolEscalationSubmission, OrchestratorWorkLink } from "./orchestrator-model.js";
8
+ const Row = Schema.Struct({
9
+ activityIdempotencyKey: Schema.String,
10
+ dispatchRequestId: Schema.String,
11
+ idempotencyKey: Schema.String,
12
+ command: Schema.String,
13
+ acceptedAt: Schema.Number,
14
+ isRouted: Schema.Literals([0, 1]),
15
+ status: Schema.String,
16
+ route: Schema.NullOr(Schema.String),
17
+ workLink: Schema.NullOr(Schema.String)
18
+ });
19
+ const EventRow = Schema.Struct({
20
+ dispatchRequestId: Schema.String,
21
+ sequence: Schema.Number,
22
+ type: Schema.String,
23
+ activityIdempotencyKey: Schema.String,
24
+ occurredAt: Schema.Number,
25
+ detail: Schema.NullOr(Schema.String),
26
+ result: Schema.NullOr(Schema.String)
27
+ });
28
+ const LifecycleRow = Schema.Struct({
29
+ activityIdempotencyKey: Schema.String,
30
+ dispatchRequestId: Schema.String,
31
+ idempotencyKey: Schema.String,
32
+ command: Schema.String,
33
+ acceptedAt: Schema.Number,
34
+ isRouted: Schema.Literals([0, 1]),
35
+ status: Schema.String,
36
+ sequence: Schema.NullOr(Schema.Number),
37
+ type: Schema.NullOr(Schema.String),
38
+ eventActivityIdempotencyKey: Schema.NullOr(Schema.String),
39
+ occurredAt: Schema.NullOr(Schema.Number),
40
+ detail: Schema.NullOr(Schema.String),
41
+ result: Schema.NullOr(Schema.String),
42
+ route: Schema.NullOr(Schema.String),
43
+ workLink: Schema.NullOr(Schema.String)
44
+ });
45
+ const RecoveryRow = Schema.Struct({
46
+ dispatchRequestId: DispatchRequestId,
47
+ acceptedAt: Schema.Number
48
+ });
49
+ const SqliteColumnRow = Schema.Struct({ name: Schema.String });
50
+ const recoveryPageSize = 256;
51
+ const storageError = (operation) => (cause) => new OrchestratorStorageError({ cause, operation });
52
+ const verifyPathIdentity = (path, fileSystem, paths, operation) => fileSystem.readLink(path).pipe(Effect.matchEffect({
53
+ onFailure: (cause) => cause.reason._tag === "NotFound" || cause.reason._tag === "Unknown"
54
+ ? Effect.void
55
+ : Effect.fail(new OrchestratorStorageError({ cause, operation: `${operation}.readlink` })),
56
+ onSuccess: (target) => Effect.fail(new OrchestratorStorageError({ cause: { path, target }, operation }))
57
+ }), Effect.andThen(fileSystem.exists(path).pipe(Effect.mapError((cause) => new OrchestratorStorageError({ cause, operation: `${operation}.exists` })))), Effect.flatMap((exists) => {
58
+ if (!exists) {
59
+ const parent = paths.dirname(path);
60
+ return parent === path ? Effect.void : verifyPathIdentity(parent, fileSystem, paths, operation);
61
+ }
62
+ const parent = paths.dirname(path);
63
+ return Effect.all({
64
+ realPath: fileSystem.realPath(path).pipe(Effect.mapError((cause) => new OrchestratorStorageError({ cause, operation: `${operation}.realpath` }))),
65
+ realParentPath: fileSystem.realPath(parent).pipe(Effect.mapError((cause) => new OrchestratorStorageError({ cause, operation: `${operation}.parent-realpath` })))
66
+ }).pipe(Effect.flatMap(({ realParentPath, realPath }) => {
67
+ const expectedPath = paths.join(realParentPath, paths.basename(path));
68
+ return realPath === expectedPath
69
+ ? Effect.void
70
+ : Effect.fail(new OrchestratorStorageError({ cause: { expectedPath, path, realPath }, operation }));
71
+ }));
72
+ }));
73
+ const decodeCommand = (text) => Effect.try({
74
+ try: () => Schema.decodeUnknownSync(OrchestratorCommand)(JSON.parse(text)),
75
+ catch: storageError("decode.command")
76
+ });
77
+ const decodeRoute = (text) => Effect.try({
78
+ try: () => JSON.parse(text),
79
+ catch: storageError("decode.route.json")
80
+ }).pipe(Effect.flatMap((value) => Schema.decodeUnknownEffect(OrchestratorRoute)(value)), Effect.mapError(storageError("decode.route")));
81
+ const decodeWorkLink = (text) => Effect.try({
82
+ try: () => JSON.parse(text),
83
+ catch: storageError("decode.work-link.json")
84
+ }).pipe(Effect.flatMap((value) => Schema.decodeUnknownEffect(OrchestratorWorkLink)(value)), Effect.mapError(storageError("decode.work-link")));
85
+ const decodeMetadata = Effect.fn("Orchestrator.decodeMetadata")(function* (routeText, workLinkText) {
86
+ const route = routeText === null ? null : yield* decodeRoute(routeText);
87
+ const workLink = workLinkText === null ? null : yield* decodeWorkLink(workLinkText);
88
+ if (route === null && workLink !== null) {
89
+ return yield* new OrchestratorStorageError({
90
+ cause: { route, workLink },
91
+ operation: "decode.metadata-mismatch"
92
+ });
93
+ }
94
+ if (route?.model === "gpt-5.6-luna" && workLink !== null) {
95
+ return yield* new OrchestratorStorageError({
96
+ cause: { route, workLink },
97
+ operation: "decode.metadata-mismatch"
98
+ });
99
+ }
100
+ if (route?.model === "gpt-5.6-sol") {
101
+ if (workLink === null || (route.linkedRequestId !== null && !workLink.lineage.includes(route.linkedRequestId))) {
102
+ return yield* new OrchestratorStorageError({
103
+ cause: { route, workLink },
104
+ operation: "decode.metadata-mismatch"
105
+ });
106
+ }
107
+ }
108
+ return { route, workLink };
109
+ });
110
+ const decodeEvent = (row) => Schema.decodeUnknownEffect(OrchestratorEvent)({
111
+ ...row,
112
+ dispatchRequestId: row.dispatchRequestId,
113
+ type: row.type
114
+ }).pipe(Effect.mapError(storageError("decode.event")));
115
+ const currentStatus = Schema.Literals([
116
+ "accepted",
117
+ "queued",
118
+ "running",
119
+ "settled",
120
+ "delivery_failed",
121
+ "task_failed"
122
+ ]);
123
+ const transitionTarget = Schema.Literals([
124
+ "queued",
125
+ "running",
126
+ "settled",
127
+ "delivery_failed",
128
+ "task_failed"
129
+ ]);
130
+ const validTransition = (from, to) => (from === "accepted" && to === "queued") ||
131
+ (from === "queued" && (to === "running" || to === "delivery_failed")) ||
132
+ (from === "running" &&
133
+ (to === "settled" || to === "delivery_failed" || to === "task_failed"));
134
+ const validEventTransition = (from, to) => to !== "accepted" && validTransition(from, to);
135
+ const isTerminalEvent = (event) => event.type === "settled" || event.type === "delivery_failed" || event.type === "task_failed";
136
+ export class Orchestrator extends Context.Service()("herdr/Orchestrator") {
137
+ }
138
+ class SqliteFileSecurity extends Context.Service()("herdr/SqliteFileSecurity") {
139
+ }
140
+ const secureSqliteFiles = (filename, fileSystem, paths) => {
141
+ const files = [filename, `${filename}-wal`, `${filename}-shm`];
142
+ return Effect.forEach(files, (path) => verifyPathIdentity(path, fileSystem, paths, "sqlite.secure.path-identity").pipe(Effect.flatMap(() => fileSystem.exists(path).pipe(Effect.mapError((cause) => new OrchestratorStorageError({ cause, operation: "sqlite.secure.exists" })))), Effect.flatMap((exists) => exists
143
+ ? fileSystem.chmod(path, 0o600).pipe(Effect.mapError((cause) => new OrchestratorStorageError({ cause, operation: "sqlite.secure.chmod" })))
144
+ : Effect.void)), { discard: true });
145
+ };
146
+ const makeOrchestrator = Effect.gen(function* () {
147
+ const sql = yield* SqlClient.SqlClient;
148
+ const cryptoService = yield* Crypto.Crypto;
149
+ const workSql = makeSqliteWorkBridge(sql);
150
+ const fileSecurity = yield* Effect.serviceOption(SqliteFileSecurity);
151
+ const secureFiles = Option.match(fileSecurity, {
152
+ onNone: () => Effect.void,
153
+ onSome: ({ secure }) => secure
154
+ });
155
+ const now = Clock.currentTimeMillis;
156
+ yield* workSql.initialize.pipe(Effect.mapError(storageError("initialize.work")));
157
+ yield* sql `
158
+ CREATE TABLE IF NOT EXISTS orchestrator_dispatches (
159
+ dispatch_request_id TEXT PRIMARY KEY,
160
+ idempotency_key TEXT NOT NULL UNIQUE,
161
+ activity_idempotency_key TEXT NOT NULL UNIQUE,
162
+ command TEXT NOT NULL,
163
+ accepted_at INTEGER NOT NULL,
164
+ is_routed INTEGER NOT NULL DEFAULT 0 CHECK (is_routed IN (0, 1)),
165
+ status TEXT NOT NULL
166
+ )
167
+ `.pipe(Effect.mapError(storageError("initialize.dispatches")));
168
+ yield* sql `
169
+ CREATE TABLE IF NOT EXISTS orchestrator_events (
170
+ dispatch_request_id TEXT NOT NULL,
171
+ sequence INTEGER NOT NULL,
172
+ type TEXT NOT NULL,
173
+ activity_idempotency_key TEXT NOT NULL,
174
+ occurred_at INTEGER NOT NULL,
175
+ detail TEXT,
176
+ result TEXT,
177
+ PRIMARY KEY (dispatch_request_id, sequence),
178
+ FOREIGN KEY (dispatch_request_id) REFERENCES orchestrator_dispatches(dispatch_request_id)
179
+ )
180
+ `.pipe(Effect.mapError(storageError("initialize.events")));
181
+ yield* sql `
182
+ CREATE TABLE IF NOT EXISTS orchestrator_dispatch_metadata (
183
+ dispatch_request_id TEXT PRIMARY KEY,
184
+ route TEXT NOT NULL,
185
+ work_link TEXT,
186
+ FOREIGN KEY (dispatch_request_id) REFERENCES orchestrator_dispatches(dispatch_request_id)
187
+ )
188
+ `.pipe(Effect.mapError(storageError("initialize.dispatch-metadata")));
189
+ yield* sql.withTransaction(Effect.gen(function* () {
190
+ const columns = yield* sql `PRAGMA table_info(orchestrator_dispatches)`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(SqliteColumnRow))), Effect.mapError(storageError("initialize.dispatch-columns")));
191
+ if (columns.some(({ name }) => name === "is_routed"))
192
+ return;
193
+ yield* sql `
194
+ ALTER TABLE orchestrator_dispatches
195
+ ADD COLUMN is_routed INTEGER NOT NULL DEFAULT 0 CHECK (is_routed IN (0, 1))
196
+ `.pipe(Effect.mapError(storageError("initialize.add-routed-discriminator")));
197
+ yield* sql `
198
+ UPDATE orchestrator_dispatches
199
+ SET is_routed = 1
200
+ WHERE EXISTS (
201
+ SELECT 1 FROM orchestrator_dispatch_metadata metadata
202
+ WHERE metadata.dispatch_request_id = orchestrator_dispatches.dispatch_request_id
203
+ )
204
+ `.pipe(Effect.mapError(storageError("initialize.backfill-routed-discriminator")));
205
+ })).pipe(Effect.catchTag("SqlError", (cause) => Effect.fail(storageError("initialize.route-migration")(cause))));
206
+ yield* sql `
207
+ CREATE INDEX IF NOT EXISTS orchestrator_pending_dispatches_order
208
+ ON orchestrator_dispatches (accepted_at ASC, dispatch_request_id ASC)
209
+ WHERE status IN ('accepted', 'queued')
210
+ `.pipe(Effect.mapError(storageError("initialize.pending-index")));
211
+ yield* sql `
212
+ CREATE INDEX IF NOT EXISTS orchestrator_running_dispatches_order
213
+ ON orchestrator_dispatches (accepted_at ASC, dispatch_request_id ASC)
214
+ WHERE status = 'running'
215
+ `.pipe(Effect.mapError(storageError("initialize.running-index")));
216
+ yield* secureFiles;
217
+ const validateLifecycleChain = Effect.fn("Orchestrator.validateLifecycleChain")(function* (dispatch, events) {
218
+ const dispatchActivityIdempotencyKey = yield* Schema.decodeUnknownEffect(ActivityIdempotencyKey)(dispatch.activityIdempotencyKey).pipe(Effect.mapError(() => new OrchestratorStorageError({
219
+ cause: dispatch.activityIdempotencyKey,
220
+ operation: "transition.lifecycle-chain-mismatch"
221
+ })));
222
+ const dispatchCommand = yield* decodeCommand(dispatch.command);
223
+ const first = events[0];
224
+ if (first === undefined ||
225
+ first.sequence !== 0 ||
226
+ first.type !== "accepted" ||
227
+ first.occurredAt !== dispatch.acceptedAt) {
228
+ return yield* new OrchestratorStorageError({
229
+ cause: { dispatch, event: first },
230
+ operation: "transition.lifecycle-chain-mismatch"
231
+ });
232
+ }
233
+ if (first.dispatchRequestId !== dispatch.dispatchRequestId ||
234
+ first.activityIdempotencyKey !== dispatchActivityIdempotencyKey ||
235
+ first.activityIdempotencyKey !== dispatchCommand.activityIdempotencyKey) {
236
+ return yield* new OrchestratorStorageError({
237
+ cause: { dispatch, event: first },
238
+ operation: "transition.activity-idempotency-mismatch"
239
+ });
240
+ }
241
+ let previousType = first.type;
242
+ let previousOccurredAt = first.occurredAt;
243
+ for (const [index, event] of events.entries()) {
244
+ if (event.dispatchRequestId !== dispatch.dispatchRequestId ||
245
+ event.activityIdempotencyKey !== dispatchActivityIdempotencyKey ||
246
+ event.activityIdempotencyKey !== dispatchCommand.activityIdempotencyKey) {
247
+ return yield* new OrchestratorStorageError({
248
+ cause: { dispatchRequestId: dispatch.dispatchRequestId, event, index, previousType },
249
+ operation: "transition.activity-idempotency-mismatch"
250
+ });
251
+ }
252
+ if (event.sequence !== index ||
253
+ event.occurredAt < previousOccurredAt ||
254
+ (index > 0 && !validEventTransition(previousType, event.type))) {
255
+ return yield* new OrchestratorStorageError({
256
+ cause: { dispatchRequestId: dispatch.dispatchRequestId, event, index, previousType },
257
+ operation: "transition.lifecycle-chain-mismatch"
258
+ });
259
+ }
260
+ previousType = event.type;
261
+ previousOccurredAt = event.occurredAt;
262
+ }
263
+ });
264
+ const loadValidatedEvents = Effect.fn("Orchestrator.loadValidatedEvents")(function* (dispatchRequestId) {
265
+ const decodedDispatchRequestId = yield* Schema.decodeUnknownEffect(DispatchRequestId)(dispatchRequestId).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "dispatch request ID is invalid" })));
266
+ const rows = yield* sql `
267
+ SELECT d.dispatch_request_id AS "dispatchRequestId", d.idempotency_key AS "idempotencyKey",
268
+ d.activity_idempotency_key AS "activityIdempotencyKey", d.command,
269
+ d.accepted_at AS "acceptedAt", d.is_routed AS "isRouted", d.status,
270
+ m.route, m.work_link AS "workLink",
271
+ e.sequence, e.type, e.activity_idempotency_key AS "eventActivityIdempotencyKey",
272
+ e.occurred_at AS "occurredAt", e.detail, e.result
273
+ FROM orchestrator_dispatches d
274
+ LEFT JOIN orchestrator_dispatch_metadata m
275
+ ON m.dispatch_request_id = d.dispatch_request_id
276
+ LEFT JOIN orchestrator_events e
277
+ ON e.dispatch_request_id = d.dispatch_request_id
278
+ WHERE d.dispatch_request_id = ${decodedDispatchRequestId}
279
+ ORDER BY e.sequence ASC
280
+ `.pipe(Effect.mapError(storageError("events.snapshot")));
281
+ const decodedRows = yield* Schema.decodeUnknownEffect(Schema.Array(LifecycleRow))(rows).pipe(Effect.mapError(storageError("events.decode-snapshot")));
282
+ const first = decodedRows[0];
283
+ if (first === undefined) {
284
+ return yield* new OrchestratorNotFoundError({ dispatchRequestId: decodedDispatchRequestId });
285
+ }
286
+ const status = yield* Schema.decodeUnknownEffect(currentStatus)(first.status).pipe(Effect.mapError(() => new OrchestratorStorageError({ cause: first.status, operation: "decode.dispatch.status" })));
287
+ const metadata = yield* decodeMetadata(first.route, first.workLink);
288
+ if ((first.isRouted === 1) !== (metadata.route !== null)) {
289
+ return yield* new OrchestratorStorageError({
290
+ cause: { isRouted: first.isRouted, route: metadata.route },
291
+ operation: "decode.metadata-presence-mismatch"
292
+ });
293
+ }
294
+ const dispatch = {
295
+ activityIdempotencyKey: first.activityIdempotencyKey,
296
+ acceptedAt: first.acceptedAt,
297
+ command: first.command,
298
+ dispatchRequestId: first.dispatchRequestId,
299
+ idempotencyKey: first.idempotencyKey,
300
+ isRouted: first.isRouted,
301
+ status,
302
+ route: metadata.route,
303
+ workLink: metadata.workLink
304
+ };
305
+ const events = yield* Effect.gen(function* () {
306
+ const eventRows = [];
307
+ for (const row of decodedRows) {
308
+ const hasEvent = row.sequence !== null || row.type !== null || row.eventActivityIdempotencyKey !== null ||
309
+ row.occurredAt !== null;
310
+ if (!hasEvent)
311
+ continue;
312
+ if (row.sequence === null || row.type === null || row.eventActivityIdempotencyKey === null ||
313
+ row.occurredAt === null) {
314
+ return yield* new OrchestratorStorageError({ cause: row, operation: "events.snapshot-row-mismatch" });
315
+ }
316
+ eventRows.push({
317
+ activityIdempotencyKey: row.eventActivityIdempotencyKey,
318
+ detail: row.detail,
319
+ dispatchRequestId: row.dispatchRequestId,
320
+ occurredAt: row.occurredAt,
321
+ result: row.result,
322
+ sequence: row.sequence,
323
+ type: row.type
324
+ });
325
+ }
326
+ return yield* Effect.forEach(eventRows, decodeEvent);
327
+ });
328
+ yield* validateLifecycleChain(dispatch, events);
329
+ const last = events.at(-1);
330
+ if (last === undefined || last.type !== dispatch.status) {
331
+ return yield* new OrchestratorStorageError({
332
+ cause: { dispatch, event: last },
333
+ operation: "events.status-event-mismatch"
334
+ });
335
+ }
336
+ const command = yield* decodeCommand(dispatch.command);
337
+ yield* Schema.decodeUnknownEffect(OrchestratorRequest)({
338
+ ...dispatch,
339
+ command
340
+ }).pipe(Effect.mapError(storageError("events.decode-authority")));
341
+ return { dispatch, events };
342
+ });
343
+ const validateLinkedParent = Effect.fn("Orchestrator.validateLinkedParent")(function* (route) {
344
+ if (route?.model !== "gpt-5.6-sol" || route.linkedRequestId === null)
345
+ return;
346
+ const parent = yield* loadValidatedEvents(route.linkedRequestId);
347
+ if (parent.dispatch.route?.model !== "gpt-5.6-luna" ||
348
+ (parent.dispatch.status !== "delivery_failed" && parent.dispatch.status !== "task_failed")) {
349
+ return yield* new OrchestratorValidationError({
350
+ detail: "linked Sol escalation must reference a failed Luna request"
351
+ });
352
+ }
353
+ });
354
+ const validateDurableReadback = Effect.fn("Orchestrator.validateDurableReadback")(function* (dispatch, operation) {
355
+ yield* validateLinkedParent(dispatch.route);
356
+ if (dispatch.route?.model !== "gpt-5.6-sol" || dispatch.workLink === null)
357
+ return;
358
+ yield* workSql.requireDispatchHandoff({
359
+ dispatchRequestId: dispatch.dispatchRequestId,
360
+ handoff: dispatch.workLink.handoff,
361
+ lineage: dispatch.workLink.lineage
362
+ }).pipe(Effect.mapError(storageError(operation)));
363
+ });
364
+ const listPending = Effect.fn("Orchestrator.pending")(function* (query = {}) {
365
+ const decodedQuery = yield* Schema.decodeUnknownEffect(OrchestratorPendingQuery)(query).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "pending query is invalid" })));
366
+ return yield* sql.withTransaction(Effect.gen(function* () {
367
+ const limit = decodedQuery.limit ?? 256;
368
+ const rows = decodedQuery.after === undefined
369
+ ? yield* sql `
370
+ SELECT dispatch_request_id AS "dispatchRequestId", idempotency_key AS "idempotencyKey",
371
+ activity_idempotency_key AS "activityIdempotencyKey", command,
372
+ accepted_at AS "acceptedAt", is_routed AS "isRouted", status,
373
+ m.route, m.work_link AS "workLink"
374
+ FROM orchestrator_dispatches
375
+ LEFT JOIN orchestrator_dispatch_metadata m USING (dispatch_request_id)
376
+ WHERE status IN ('accepted', 'queued')
377
+ ORDER BY accepted_at ASC, dispatch_request_id ASC
378
+ LIMIT ${limit}
379
+ `.pipe(Effect.mapError(storageError("pending.list")))
380
+ : yield* sql `
381
+ SELECT dispatch_request_id AS "dispatchRequestId", idempotency_key AS "idempotencyKey",
382
+ activity_idempotency_key AS "activityIdempotencyKey", command,
383
+ accepted_at AS "acceptedAt", is_routed AS "isRouted", status,
384
+ m.route, m.work_link AS "workLink"
385
+ FROM orchestrator_dispatches
386
+ LEFT JOIN orchestrator_dispatch_metadata m USING (dispatch_request_id)
387
+ WHERE status IN ('accepted', 'queued')
388
+ AND (accepted_at > ${decodedQuery.after.acceptedAt}
389
+ OR (accepted_at = ${decodedQuery.after.acceptedAt}
390
+ AND dispatch_request_id > ${decodedQuery.after.dispatchRequestId}))
391
+ ORDER BY accepted_at ASC, dispatch_request_id ASC
392
+ LIMIT ${limit}
393
+ `.pipe(Effect.mapError(storageError("pending.list")));
394
+ const decodedRows = yield* Schema.decodeUnknownEffect(Schema.Array(Row))(rows).pipe(Effect.mapError(storageError("pending.decode-row")));
395
+ const pending = yield* Effect.forEach(decodedRows, (row) => Effect.gen(function* () {
396
+ const snapshot = yield* loadValidatedEvents(row.dispatchRequestId);
397
+ if (snapshot.dispatch.status !== "accepted" && snapshot.dispatch.status !== "queued") {
398
+ return Option.none();
399
+ }
400
+ yield* validateDurableReadback(snapshot.dispatch, "pending.work-link");
401
+ const command = yield* decodeCommand(snapshot.dispatch.command);
402
+ const pendingInput = {
403
+ acceptedAt: snapshot.dispatch.acceptedAt,
404
+ activityIdempotencyKey: snapshot.dispatch.activityIdempotencyKey,
405
+ command,
406
+ dispatchRequestId: snapshot.dispatch.dispatchRequestId,
407
+ idempotencyKey: snapshot.dispatch.idempotencyKey,
408
+ status: snapshot.dispatch.status
409
+ };
410
+ const pendingDispatch = yield* Schema.decodeUnknownEffect(OrchestratorPendingDispatch)(snapshot.dispatch.route === null
411
+ ? pendingInput
412
+ : { ...pendingInput, route: snapshot.dispatch.route, workLink: snapshot.dispatch.workLink }).pipe(Effect.mapError(storageError("pending.decode")));
413
+ return Option.some(pendingDispatch);
414
+ }));
415
+ return pending.flatMap((value) => Option.match(value, {
416
+ onNone: () => [],
417
+ onSome: (pendingDispatch) => [pendingDispatch]
418
+ }));
419
+ })).pipe(Effect.catchTag("SqlError", (cause) => Effect.fail(new OrchestratorStorageError({ cause, operation: "pending.transaction" }))));
420
+ });
421
+ const allocateTimestamp = Effect.fn("Orchestrator.allocateTimestamp")(function* (floor) {
422
+ return Math.max(yield* now, floor);
423
+ });
424
+ const appendTransition = Effect.fn("Orchestrator.transition")(function* (dispatchRequestId, target, detail, result) {
425
+ const decodedDispatchRequestId = yield* Schema.decodeUnknownEffect(DispatchRequestId)(dispatchRequestId).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "dispatch request ID is invalid" })));
426
+ yield* secureFiles;
427
+ const decodedTarget = yield* Schema.decodeUnknownEffect(transitionTarget)(target).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: `invalid transition target ${target}` })));
428
+ const decodedDetail = yield* Schema.decodeUnknownEffect(Schema.NullOr(OrchestratorEventDetail))(detail).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "invalid transition detail" })));
429
+ const decodedResult = yield* Schema.decodeUnknownEffect(Schema.NullOr(OrchestratorResult))(result).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "invalid transition result" })));
430
+ const event = yield* sql.withTransaction(Effect.gen(function* () {
431
+ const snapshot = yield* loadValidatedEvents(decodedDispatchRequestId).pipe(Effect.catchTag("OrchestratorStorageError", (error) => error.operation === "events.status-event-mismatch"
432
+ ? Effect.fail(new OrchestratorStorageError({ cause: error.cause, operation: "transition.status-event-mismatch" }))
433
+ : Effect.fail(error)));
434
+ const dispatch = snapshot.dispatch;
435
+ yield* validateDurableReadback(dispatch, "transition.work-link");
436
+ if (decodedTarget === "running" && dispatch.route?.model === "gpt-5.6-sol") {
437
+ return yield* new OrchestratorValidationError({
438
+ detail: "routed Sol dispatches require workerStarted with Work lane authority"
439
+ });
440
+ }
441
+ if (!validTransition(dispatch.status, decodedTarget)) {
442
+ return yield* new OrchestratorTransitionError({
443
+ dispatchRequestId: decodedDispatchRequestId,
444
+ from: dispatch.status,
445
+ to: decodedTarget
446
+ });
447
+ }
448
+ const last = snapshot.events.at(-1);
449
+ if (last === undefined) {
450
+ return yield* new OrchestratorStorageError({
451
+ cause: decodedDispatchRequestId,
452
+ operation: "transition.missing-event"
453
+ });
454
+ }
455
+ const timestamp = yield* allocateTimestamp(last.occurredAt);
456
+ const event = yield* Schema.decodeUnknownEffect(OrchestratorEvent)({
457
+ activityIdempotencyKey: last.activityIdempotencyKey,
458
+ dispatchRequestId: decodedDispatchRequestId,
459
+ occurredAt: timestamp,
460
+ sequence: last.sequence + 1,
461
+ type: decodedTarget,
462
+ ...(decodedTarget === "settled"
463
+ ? { detail: null, result: decodedResult }
464
+ : decodedTarget === "delivery_failed" || decodedTarget === "task_failed"
465
+ ? { detail: decodedDetail, result: null }
466
+ : { detail: null, result: null })
467
+ }).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "transition exceeds event bounds" })));
468
+ const insertedRows = yield* sql `
469
+ INSERT INTO orchestrator_events
470
+ (dispatch_request_id, sequence, type, activity_idempotency_key, occurred_at, detail, result)
471
+ VALUES (${event.dispatchRequestId}, ${event.sequence}, ${event.type},
472
+ ${event.activityIdempotencyKey}, ${event.occurredAt}, ${event.detail}, ${event.result})
473
+ ON CONFLICT (dispatch_request_id, sequence) DO NOTHING
474
+ RETURNING dispatch_request_id AS "dispatchRequestId", sequence, type,
475
+ activity_idempotency_key AS "activityIdempotencyKey", occurred_at AS "occurredAt", detail, result
476
+ `.pipe(Effect.mapError(storageError("transition.insert-event")));
477
+ if (insertedRows.length === 0) {
478
+ const winnerRows = yield* sql `
479
+ SELECT dispatch_request_id AS "dispatchRequestId", sequence, type,
480
+ activity_idempotency_key AS "activityIdempotencyKey", occurred_at AS "occurredAt", detail, result
481
+ FROM orchestrator_events
482
+ WHERE dispatch_request_id = ${event.dispatchRequestId} AND sequence = ${event.sequence}
483
+ `.pipe(Effect.mapError(storageError("transition.reload-event")));
484
+ const winner = yield* Schema.decodeUnknownEffect(Schema.Array(EventRow))(winnerRows).pipe(Effect.mapError(storageError("transition.decode-winner")), Effect.flatMap((rows) => {
485
+ const first = rows[0];
486
+ return first === undefined
487
+ ? Effect.fail(new OrchestratorStorageError({ cause: event, operation: "transition.missing-winner" }))
488
+ : decodeEvent(first);
489
+ }));
490
+ if (winner.dispatchRequestId === event.dispatchRequestId && winner.type !== "accepted") {
491
+ return yield* new OrchestratorTransitionError({
492
+ dispatchRequestId: decodedDispatchRequestId,
493
+ from: winner.type,
494
+ to: decodedTarget
495
+ });
496
+ }
497
+ return yield* new OrchestratorStorageError({
498
+ cause: { event, winner },
499
+ operation: "transition.concurrent-event-mismatch"
500
+ });
501
+ }
502
+ yield* sql `
503
+ UPDATE orchestrator_dispatches SET status = ${event.type}
504
+ WHERE dispatch_request_id = ${decodedDispatchRequestId}
505
+ `.pipe(Effect.mapError(storageError("transition.update-dispatch")));
506
+ return event;
507
+ })).pipe(Effect.catchTag("SqlError", (cause) => Effect.fail(storageError("transition.transaction")(cause))));
508
+ return event;
509
+ });
510
+ const workerStarted = Effect.fn("Orchestrator.workerStarted")(function* (activation) {
511
+ const decoded = yield* Schema.decodeUnknownEffect(OrchestratorDispatchActivation)(activation).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "worker-start activation is invalid" })));
512
+ yield* secureFiles;
513
+ return yield* sql.withTransaction(Effect.gen(function* () {
514
+ const snapshot = yield* loadValidatedEvents(decoded.dispatchRequestId);
515
+ const dispatch = snapshot.dispatch;
516
+ if (dispatch.route?.model !== "gpt-5.6-sol" ||
517
+ dispatch.workLink === null ||
518
+ dispatch.workLink.handoff.laneId !== decoded.laneId) {
519
+ return yield* new OrchestratorValidationError({
520
+ detail: "worker start must match the routed dispatch Work lane"
521
+ });
522
+ }
523
+ if (decoded.expectedRevision !== dispatch.workLink.handoff.expectedRevision) {
524
+ return yield* new OrchestratorWorkerStartAuthorityError({
525
+ actualRevision: decoded.expectedRevision,
526
+ expectedRevision: dispatch.workLink.handoff.expectedRevision,
527
+ laneId: decoded.laneId,
528
+ reason: "accepted_revision_mismatch"
529
+ });
530
+ }
531
+ yield* validateDurableReadback(dispatch, "worker-start.work-link");
532
+ if (dispatch.status !== "queued") {
533
+ if (dispatch.status !== "running" &&
534
+ dispatch.status !== "settled" &&
535
+ dispatch.status !== "delivery_failed" &&
536
+ dispatch.status !== "task_failed") {
537
+ return yield* new OrchestratorTransitionError({
538
+ dispatchRequestId: decoded.dispatchRequestId,
539
+ from: dispatch.status,
540
+ to: "running"
541
+ });
542
+ }
543
+ const event = snapshot.events.find(({ type }) => type === "running");
544
+ if (event === undefined) {
545
+ return yield* new OrchestratorTransitionError({
546
+ dispatchRequestId: decoded.dispatchRequestId,
547
+ from: dispatch.status,
548
+ to: "running"
549
+ });
550
+ }
551
+ const binding = yield* workSql.requireAgentBinding(decoded).pipe(Effect.mapError((error) => error._tag === "WorkAgentBindingConflictError"
552
+ ? new OrchestratorWorkerBindingConflictError({ dispatchRequestId: decoded.dispatchRequestId })
553
+ : storageError("worker-start.work-binding")(error)));
554
+ if (event.occurredAt !== binding.checkpoint.occurredAt) {
555
+ return yield* new OrchestratorStorageError({
556
+ cause: { binding, events: snapshot.events },
557
+ operation: "worker-start.replay-boundary-mismatch"
558
+ });
559
+ }
560
+ return yield* Schema.decodeUnknownEffect(OrchestratorDispatchActivated)({ binding, event }).pipe(Effect.mapError(storageError("worker-start.decode-replay")));
561
+ }
562
+ const last = snapshot.events.at(-1);
563
+ if (last === undefined) {
564
+ return yield* new OrchestratorStorageError({
565
+ cause: decoded.dispatchRequestId,
566
+ operation: "worker-start.missing-event"
567
+ });
568
+ }
569
+ const timestamp = yield* allocateTimestamp(last.occurredAt);
570
+ const bindingDecision = yield* workSql.acceptAgentBinding(decoded, timestamp).pipe(Effect.mapError((error) => {
571
+ if (error._tag === "WorkAgentBindingConflictError") {
572
+ return new OrchestratorWorkerBindingConflictError({ dispatchRequestId: decoded.dispatchRequestId });
573
+ }
574
+ if (error._tag === "WorkAgentBindingAuthorityError") {
575
+ return new OrchestratorWorkerStartAuthorityError({
576
+ actualRevision: error.actualRevision,
577
+ expectedRevision: error.expectedRevision,
578
+ laneId: error.laneId,
579
+ reason: error.reason
580
+ });
581
+ }
582
+ return storageError("worker-start.work-binding")(error);
583
+ }));
584
+ if (bindingDecision._tag === "replayed") {
585
+ return yield* new OrchestratorStorageError({
586
+ cause: bindingDecision.binding,
587
+ operation: "worker-start.partial-binding"
588
+ });
589
+ }
590
+ const binding = bindingDecision.binding;
591
+ const event = yield* Schema.decodeUnknownEffect(OrchestratorEvent)({
592
+ activityIdempotencyKey: last.activityIdempotencyKey,
593
+ dispatchRequestId: decoded.dispatchRequestId,
594
+ occurredAt: binding.checkpoint.occurredAt,
595
+ sequence: last.sequence + 1,
596
+ type: "running",
597
+ detail: null,
598
+ result: null
599
+ }).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "worker-start event exceeds bounds" })));
600
+ yield* sql `
601
+ INSERT INTO orchestrator_events
602
+ (dispatch_request_id, sequence, type, activity_idempotency_key, occurred_at, detail, result)
603
+ VALUES (${event.dispatchRequestId}, ${event.sequence}, ${event.type},
604
+ ${event.activityIdempotencyKey}, ${event.occurredAt}, NULL, NULL)
605
+ `.pipe(Effect.mapError(storageError("worker-start.insert-event")));
606
+ const updated = yield* sql `
607
+ UPDATE orchestrator_dispatches SET status = 'running'
608
+ WHERE dispatch_request_id = ${decoded.dispatchRequestId} AND status = 'queued'
609
+ RETURNING dispatch_request_id
610
+ `.pipe(Effect.mapError(storageError("worker-start.update-dispatch")));
611
+ if (updated.length !== 1) {
612
+ return yield* new OrchestratorTransitionError({
613
+ dispatchRequestId: decoded.dispatchRequestId,
614
+ from: dispatch.status,
615
+ to: "running"
616
+ });
617
+ }
618
+ return yield* Schema.decodeUnknownEffect(OrchestratorDispatchActivated)({ binding, event }).pipe(Effect.mapError(storageError("worker-start.decode")));
619
+ })).pipe(Effect.catchTag("SqlError", (cause) => Effect.fail(storageError("worker-start.transaction")(cause))));
620
+ });
621
+ const submitInternal = Effect.fn("Orchestrator.submitInternal")(function* (decodedCommand, decodedKey, route, workLink) {
622
+ const encodedCommand = JSON.stringify(decodedCommand);
623
+ const encodedRoute = route === null ? null : JSON.stringify(route);
624
+ const encodedWorkLink = workLink === null ? null : JSON.stringify(workLink);
625
+ yield* secureFiles;
626
+ return yield* sql.withTransaction(Effect.gen(function* () {
627
+ const replayIds = yield* sql `
628
+ SELECT dispatch_request_id AS "dispatchRequestId"
629
+ FROM orchestrator_dispatches
630
+ WHERE idempotency_key = ${decodedKey}
631
+ OR activity_idempotency_key = ${decodedCommand.activityIdempotencyKey}
632
+ ORDER BY dispatch_request_id ASC
633
+ LIMIT 1
634
+ `.pipe(Effect.mapError(storageError("submit.preload-replay")), Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({
635
+ dispatchRequestId: DispatchRequestId
636
+ })))), Effect.mapError(storageError("submit.decode-preloaded-replay")));
637
+ const replayId = replayIds[0]?.dispatchRequestId;
638
+ const dispatchRequestId = replayId ??
639
+ (yield* cryptoService.randomUUIDv4.pipe(Effect.mapError(storageError("submit.request-id")), Effect.flatMap((value) => Schema.decodeUnknownEffect(DispatchRequestId)(value).pipe(Effect.mapError(() => new OrchestratorStorageError({ cause: value, operation: "submit.request-id-schema" }))))));
640
+ yield* validateLinkedParent(route);
641
+ const acceptedAt = yield* now;
642
+ const insertedRows = yield* sql `
643
+ INSERT INTO orchestrator_dispatches
644
+ (dispatch_request_id, idempotency_key, activity_idempotency_key, command, accepted_at, is_routed, status)
645
+ VALUES (${dispatchRequestId}, ${decodedKey}, ${decodedCommand.activityIdempotencyKey},
646
+ ${encodedCommand}, ${acceptedAt}, ${route === null ? 0 : 1}, 'accepted')
647
+ ON CONFLICT DO NOTHING
648
+ RETURNING dispatch_request_id
649
+ `.pipe(Effect.mapError(storageError("submit.insert-or-reload")));
650
+ const wasInserted = insertedRows.length !== 0;
651
+ if (!wasInserted) {
652
+ const persistedRows = yield* sql `
653
+ SELECT d.dispatch_request_id AS "dispatchRequestId", d.idempotency_key AS "idempotencyKey",
654
+ d.activity_idempotency_key AS "activityIdempotencyKey", d.command,
655
+ d.accepted_at AS "acceptedAt", d.is_routed AS "isRouted", d.status,
656
+ m.route, m.work_link AS "workLink"
657
+ FROM orchestrator_dispatches d
658
+ LEFT JOIN orchestrator_dispatch_metadata m USING (dispatch_request_id)
659
+ WHERE d.idempotency_key = ${decodedKey}
660
+ OR d.activity_idempotency_key = ${decodedCommand.activityIdempotencyKey}
661
+ `.pipe(Effect.mapError(storageError("submit.reload")));
662
+ const persisted = yield* Schema.decodeUnknownEffect(Schema.Array(Row))(persistedRows).pipe(Effect.mapError(storageError("submit.decode-persisted")));
663
+ const first = persisted[0];
664
+ if (first === undefined) {
665
+ return yield* new OrchestratorStorageError({
666
+ cause: { dispatchRequestId, idempotencyKey: decodedKey },
667
+ operation: "submit.insert-or-reload-empty"
668
+ });
669
+ }
670
+ if (first.idempotencyKey !== decodedKey) {
671
+ return yield* new OrchestratorConflictError({
672
+ detail: "activity idempotency key was already used",
673
+ idempotencyKey: decodedKey
674
+ });
675
+ }
676
+ const oldCommand = yield* decodeCommand(first.command);
677
+ if (!Equal.equals(oldCommand, decodedCommand)) {
678
+ return yield* new OrchestratorConflictError({
679
+ detail: "idempotency key was already used for a different command",
680
+ idempotencyKey: decodedKey
681
+ });
682
+ }
683
+ const acceptedAt = yield* Schema.decodeUnknownEffect(Schema.Number)(first.acceptedAt).pipe(Effect.mapError(storageError("submit.decode-accepted-at")));
684
+ const snapshot = yield* loadValidatedEvents(first.dispatchRequestId).pipe(Effect.catchTag("OrchestratorStorageError", (error) => Effect.fail(new OrchestratorStorageError({
685
+ cause: error.cause,
686
+ operation: "submit.accepted-event-mismatch"
687
+ }))));
688
+ if (!Equal.equals(snapshot.dispatch.route, route) || !Equal.equals(snapshot.dispatch.workLink, workLink)) {
689
+ return yield* new OrchestratorConflictError({
690
+ detail: "idempotency key was already used with different route or Work lineage",
691
+ idempotencyKey: decodedKey
692
+ });
693
+ }
694
+ const accepted = snapshot.events[0];
695
+ if (accepted === undefined ||
696
+ accepted.type !== "accepted" ||
697
+ accepted.dispatchRequestId !== first.dispatchRequestId ||
698
+ accepted.activityIdempotencyKey !== first.activityIdempotencyKey ||
699
+ accepted.activityIdempotencyKey !== decodedCommand.activityIdempotencyKey ||
700
+ accepted.occurredAt !== acceptedAt) {
701
+ return yield* new OrchestratorStorageError({
702
+ cause: {
703
+ acceptedAt,
704
+ acceptedEvent: accepted,
705
+ dispatchActivityIdempotencyKey: first.activityIdempotencyKey,
706
+ dispatchRequestId: first.dispatchRequestId
707
+ },
708
+ operation: "submit.accepted-event-mismatch"
709
+ });
710
+ }
711
+ if (route?.model === "gpt-5.6-sol" && workLink !== null) {
712
+ yield* workSql.requireDispatchHandoff({
713
+ dispatchRequestId: first.dispatchRequestId,
714
+ handoff: workLink.handoff,
715
+ lineage: workLink.lineage
716
+ }).pipe(Effect.mapError(storageError("submit.work-link.replay")));
717
+ }
718
+ return yield* Schema.decodeUnknownEffect(OrchestratorReceipt)({
719
+ acceptedAt,
720
+ dispatchRequestId: first.dispatchRequestId,
721
+ idempotencyKey: decodedKey,
722
+ status: "accepted"
723
+ }).pipe(Effect.mapError(storageError("submit.decode-receipt")));
724
+ }
725
+ if (route?.model === "gpt-5.6-sol" && workLink !== null) {
726
+ yield* workSql.acceptDispatchHandoff({
727
+ dispatchRequestId,
728
+ handoff: workLink.handoff,
729
+ lineage: workLink.lineage
730
+ }).pipe(Effect.mapError((error) => error._tag === "WorkDecisionRevisionConflictError"
731
+ ? new OrchestratorWorkRevisionConflictError({
732
+ actualRevision: error.actualRevision,
733
+ expectedRevision: error.expectedRevision,
734
+ laneId: error.laneId
735
+ })
736
+ : storageError("submit.work-link")(error)));
737
+ }
738
+ if (route !== null) {
739
+ yield* sql `
740
+ INSERT INTO orchestrator_dispatch_metadata
741
+ (dispatch_request_id, route, work_link)
742
+ VALUES (${dispatchRequestId}, ${encodedRoute}, ${encodedWorkLink})
743
+ `.pipe(Effect.mapError(storageError("submit.insert-dispatch-metadata")));
744
+ }
745
+ yield* sql `
746
+ INSERT INTO orchestrator_events
747
+ (dispatch_request_id, sequence, type, activity_idempotency_key, occurred_at, detail, result)
748
+ VALUES (${dispatchRequestId}, 0, 'accepted', ${decodedCommand.activityIdempotencyKey}, ${acceptedAt}, NULL, NULL)
749
+ `.pipe(Effect.mapError(storageError("submit.insert-event")));
750
+ return {
751
+ acceptedAt,
752
+ dispatchRequestId,
753
+ idempotencyKey: decodedKey,
754
+ status: "accepted"
755
+ };
756
+ })).pipe(Effect.catchTag("SqlError", (cause) => Effect.fail(storageError("submit.transaction")(cause))));
757
+ });
758
+ const submit = Effect.fn("Orchestrator.submit")(function* (command, idempotencyKey) {
759
+ const decodedCommand = yield* Schema.decodeUnknownEffect(OrchestratorCommand)(command).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "command is not a typed fleet job" })));
760
+ const decodedKey = yield* Schema.decodeUnknownEffect(OrchestratorIdempotencyKey)(idempotencyKey).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "idempotency key is invalid" })));
761
+ return yield* submitInternal(decodedCommand, decodedKey, null, null);
762
+ });
763
+ const submitRouted = Effect.fn("Orchestrator.submitRouted")(function* (input) {
764
+ const decoded = yield* Schema.decodeUnknownEffect(OrchestratorRoutedSubmission)(input).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "routed submission is invalid" })));
765
+ const command = yield* Schema.decodeUnknownEffect(OrchestratorCommand)(decoded.command).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "routed command is invalid" })));
766
+ if (decoded.route.model === "gpt-5.6-sol" &&
767
+ decoded.route.linkedRequestId !== null &&
768
+ decoded.workLink !== null &&
769
+ !decoded.workLink.lineage.includes(decoded.route.linkedRequestId)) {
770
+ return yield* new OrchestratorValidationError({
771
+ detail: "Work lineage must include the linked Luna request"
772
+ });
773
+ }
774
+ return yield* submitInternal(command, decoded.idempotencyKey, decoded.route, decoded.workLink);
775
+ });
776
+ const submitSolEscalation = Effect.fn("Orchestrator.submitSolEscalation")(function* (input) {
777
+ const decoded = yield* Schema.decodeUnknownEffect(OrchestratorSolEscalationSubmission)(input).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "Sol escalation submission is invalid" })));
778
+ return yield* submitInternal({
779
+ activityIdempotencyKey: decoded.command.activityIdempotencyKey,
780
+ actor: decoded.command.actor,
781
+ kind: decoded.command.kind,
782
+ payload: {
783
+ kind: decoded.command.payload.kind,
784
+ mode: decoded.command.payload.mode,
785
+ prompt: decoded.command.payload.prompt,
786
+ repository: decoded.command.payload.repository
787
+ }
788
+ }, decoded.idempotencyKey, {
789
+ action: "dispatch",
790
+ linkedRequestId: decoded.reference.failedLunaRequestId,
791
+ model: "gpt-5.6-sol",
792
+ protocol: "hostd.coordinator.route.v1",
793
+ reason: decoded.reason,
794
+ reasoningEffort: "high"
795
+ }, decoded.reference.workLink);
796
+ });
797
+ const service = {
798
+ events: (dispatchRequestId) => Stream.unwrap(Schema.decodeUnknownEffect(DispatchRequestId)(dispatchRequestId).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "dispatch request ID is invalid" })), Effect.map((decodedDispatchRequestId) => Stream.succeed(undefined).pipe(Stream.concat(Stream.fromSchedule(Schedule.spaced("100 millis")).pipe(Stream.map(() => undefined))), Stream.mapEffect(() => sql.withTransaction(Effect.gen(function* () {
799
+ const snapshot = yield* loadValidatedEvents(decodedDispatchRequestId);
800
+ yield* validateDurableReadback(snapshot.dispatch, "events.work-link");
801
+ return snapshot;
802
+ })).pipe(Effect.catchTag("SqlError", (cause) => Effect.fail(storageError("events.transaction")(cause))))), Stream.mapAccum(() => -1, (lastSequence, snapshot) => {
803
+ const unseen = snapshot.events.filter(({ sequence }) => sequence > lastSequence);
804
+ const last = unseen.at(-1);
805
+ return [last?.sequence ?? lastSequence, unseen];
806
+ }), Stream.takeUntil(isTerminalEvent))))),
807
+ failDelivery: Effect.fn("Orchestrator.failDelivery")((dispatchRequestId, detail) => appendTransition(dispatchRequestId, "delivery_failed", detail, null)),
808
+ failTask: Effect.fn("Orchestrator.failTask")((dispatchRequestId, detail) => appendTransition(dispatchRequestId, "task_failed", detail, null)),
809
+ pending: listPending,
810
+ queue: Effect.fn("Orchestrator.queue")((dispatchRequestId) => appendTransition(dispatchRequestId, "queued", null, null)),
811
+ recover: () => Stream.paginate(undefined, Effect.fn("Orchestrator.recoverPage")(function* (state) {
812
+ void state;
813
+ const rows = yield* sql `
814
+ SELECT dispatch_request_id AS "dispatchRequestId", accepted_at AS "acceptedAt"
815
+ FROM orchestrator_dispatches
816
+ WHERE status = 'running'
817
+ ORDER BY accepted_at ASC, dispatch_request_id ASC
818
+ LIMIT ${recoveryPageSize}
819
+ `.pipe(Effect.mapError(storageError("recover.lookup")));
820
+ const ids = yield* Schema.decodeUnknownEffect(Schema.Array(RecoveryRow))(rows).pipe(Effect.mapError(storageError("recover.decode")));
821
+ const recovered = yield* Effect.forEach(ids, ({ dispatchRequestId }) => appendTransition(dispatchRequestId, "delivery_failed", "orchestrator restarted while the activity was running", null).pipe(Effect.map(Option.some), Effect.catchTag("OrchestratorTransitionError", (error) => error.from === "settled" || error.from === "delivery_failed" || error.from === "task_failed"
822
+ ? Effect.succeed(Option.none())
823
+ : Effect.fail(error))));
824
+ const events = recovered.flatMap((event) => Option.match(event, {
825
+ onNone: () => [],
826
+ onSome: (value) => [value]
827
+ }));
828
+ const next = ids.length === 0 ? Option.none() : Option.some(undefined);
829
+ const page = [events, next];
830
+ return page;
831
+ })),
832
+ run: Effect.fn("Orchestrator.run")((dispatchRequestId) => appendTransition(dispatchRequestId, "running", null, null)),
833
+ settle: Effect.fn("Orchestrator.settle")((dispatchRequestId, result) => appendTransition(dispatchRequestId, "settled", null, result)),
834
+ submit,
835
+ submitRouted,
836
+ submitSolEscalation,
837
+ workerStarted,
838
+ request: Effect.fn("Orchestrator.request")((dispatchRequestId) => Schema.decodeUnknownEffect(DispatchRequestId)(dispatchRequestId).pipe(Effect.mapError(() => new OrchestratorValidationError({ detail: "dispatch request ID is invalid" })), Effect.flatMap(loadValidatedEvents), Effect.flatMap((snapshot) => Effect.gen(function* () {
839
+ yield* validateDurableReadback(snapshot.dispatch, "request.work-link");
840
+ const command = yield* decodeCommand(snapshot.dispatch.command);
841
+ return yield* Schema.decodeUnknownEffect(OrchestratorRequest)({
842
+ ...snapshot.dispatch,
843
+ command
844
+ }).pipe(Effect.mapError(storageError("request.decode")));
845
+ }))))
846
+ };
847
+ return service;
848
+ });
849
+ const orchestratorLayer = Layer.effect(Orchestrator, makeOrchestrator);
850
+ /**
851
+ * Node SQLite and Crypto services used by the durable coordinator layers.
852
+ * Fails closed on non-POSIX paths until an ACL-backed private-directory check exists.
853
+ */
854
+ export const sqliteLayer = (filename) => orchestratorLayer.pipe(Layer.provide(Layer.unwrap(Effect.gen(function* () {
855
+ const fileSystem = yield* FileSystem.FileSystem;
856
+ const paths = yield* Path.Path;
857
+ const directory = paths.dirname(filename);
858
+ const directoryExists = yield* fileSystem.exists(directory).pipe(Effect.mapError((cause) => new OrchestratorStorageError({ cause, operation: "sqlite.secure.directory.exists" })));
859
+ if (!directoryExists) {
860
+ yield* fileSystem.makeDirectory(directory, { recursive: true, mode: 0o700 }).pipe(Effect.mapError((cause) => new OrchestratorStorageError({ cause, operation: "sqlite.secure.directory.create" })));
861
+ }
862
+ const directoryInfo = yield* fileSystem.stat(directory).pipe(Effect.mapError((cause) => new OrchestratorStorageError({ cause, operation: "sqlite.secure.directory.stat" })));
863
+ const privateMode = paths.sep === "/" && (directoryInfo.mode & 0o777) === 0o700;
864
+ if (directoryInfo.type !== "Directory" || !privateMode) {
865
+ return yield* new OrchestratorStorageError({
866
+ cause: { directory, mode: directoryInfo.mode, type: directoryInfo.type },
867
+ operation: "sqlite.secure.directory.private"
868
+ });
869
+ }
870
+ yield* verifyPathIdentity(directory, fileSystem, paths, "sqlite.secure.directory.path-identity");
871
+ yield* Effect.forEach([filename, `${filename}-wal`, `${filename}-shm`], (path) => verifyPathIdentity(path, fileSystem, paths, "sqlite.secure.path-identity"), { discard: true });
872
+ const security = secureSqliteFiles(filename, fileSystem, paths);
873
+ return Layer.mergeAll(SqliteClient.layer({ filename }), NodeCrypto.layer, Layer.succeed(SqliteFileSecurity, { secure: security })).pipe(Layer.tap(() => security));
874
+ })).pipe(Layer.provide(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)))));
875
+ //# sourceMappingURL=orchestrator.js.map