@yolk-sdk/harness 0.1.0-canary.78 → 0.1.0-canary.80
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.
- package/README.md +10 -0
- package/dist/driver.d.mts +3 -3
- package/dist/driver.d.mts.map +1 -1
- package/dist/driver.mjs.map +1 -1
- package/dist/inbox.d.mts +25 -17
- package/dist/inbox.d.mts.map +1 -1
- package/dist/inbox.mjs +9 -3
- package/dist/inbox.mjs.map +1 -1
- package/package.json +2 -2
- package/src/driver.ts +3 -2
- package/src/inbox.ts +25 -13
package/README.md
CHANGED
|
@@ -120,6 +120,16 @@ Hosts still own tools, prompts, auth, HITL payload persistence, and `'use workfl
|
|
|
120
120
|
|
|
121
121
|
`Driver.pause` / `resumeHitl` / `stop` compose HITL onto the existing coordinator. Inbox items have no payload. Protocol match helpers live in `@yolk-sdk/harness/outcome`. Parked waits are not durable and are not shutdown claims.
|
|
122
122
|
|
|
123
|
+
## Token types
|
|
124
|
+
|
|
125
|
+
`DrainToken` and `ParkGeneration` are distinct branded string schemas/types exported from
|
|
126
|
+
`@yolk-sdk/harness/inbox`. Forward `context.drainToken` into dequeue/pause and the returned park
|
|
127
|
+
generation into HITL admission. Custom adapters and typed fixtures must now construct or decode
|
|
128
|
+
these types rather than return raw strings. JSON stays unchanged.
|
|
129
|
+
|
|
130
|
+
The brands are nominal only: even an empty or forged string can decode. Inbox still checks live
|
|
131
|
+
ownership and freshness; decoding does not authorize a token or make an old park restorable.
|
|
132
|
+
|
|
123
133
|
## Restart contract
|
|
124
134
|
|
|
125
135
|
Ownership is per shared Driver instance. The host or platform supplies cross-process exclusivity and quiesces producers before closing that instance. Raw administrative `RunStore` mutations must be quiesced with Driver; runtime claim ownership belongs to Driver.
|
package/dist/driver.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RunStore } from "./store.mjs";
|
|
2
2
|
import { InterruptReason, Promotable, RunCoordinator } from "./coordinator.mjs";
|
|
3
|
-
import { HitlAdmission, HitlDecision, Inbox, InboxItem, ParkedResponse, PauseDecision } from "./inbox.mjs";
|
|
3
|
+
import { DrainToken, HitlAdmission, HitlDecision, Inbox, InboxItem, ParkedResponse, PauseDecision } from "./inbox.mjs";
|
|
4
4
|
import { Context, Data, Effect, Layer } from "effect";
|
|
5
5
|
|
|
6
6
|
//#region src/driver.d.ts
|
|
@@ -16,7 +16,7 @@ type ResumeSuspendedResult = {
|
|
|
16
16
|
readonly exhausted: ReadonlyArray<string>;
|
|
17
17
|
};
|
|
18
18
|
type DrainContext = {
|
|
19
|
-
readonly drainToken:
|
|
19
|
+
readonly drainToken: DrainToken;
|
|
20
20
|
readonly readyResponses: ReadonlyArray<ParkedResponse>;
|
|
21
21
|
};
|
|
22
22
|
type Drain = (runId: string, force: boolean, scope: Promotable, context: DrainContext) => Effect.Effect<void, never, Inbox | RunStore>;
|
|
@@ -91,7 +91,7 @@ type DriverApi = {
|
|
|
91
91
|
readonly awaitIdle: (runId: string) => Effect.Effect<void>;
|
|
92
92
|
readonly resumeSuspended: Effect.Effect<ResumeSuspendedResult>;
|
|
93
93
|
readonly admit: (item: InboxItem) => Effect.Effect<void>;
|
|
94
|
-
readonly pause: (runId: string, requestIds: ReadonlyArray<string>, drainToken:
|
|
94
|
+
readonly pause: (runId: string, requestIds: ReadonlyArray<string>, drainToken: DrainToken) => Effect.Effect<PauseDecision>;
|
|
95
95
|
readonly resumeHitl: (runId: string, admission: HitlAdmission) => Effect.Effect<HitlDecision>;
|
|
96
96
|
readonly stop: (runId: string) => Effect.Effect<StopDecision>;
|
|
97
97
|
};
|
package/dist/driver.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driver.d.mts","names":[],"sources":["../src/driver.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"driver.d.mts","names":[],"sources":["../src/driver.ts"],"mappings":"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"}
|
package/dist/driver.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driver.mjs","names":["stopDecision"],"sources":["../src/driver.ts"],"sourcesContent":["import { Context, Data, Effect, Exit, Layer, Predicate, Semaphore } from 'effect'\nimport { StopDecision as stopDecision } from './outcome-constructors-internal.ts'\nimport { RunCoordinator, type InterruptReason, type Promotable } from './coordinator.ts'\nimport {\n DrainBegin,\n Inbox,\n RecoveryAttempt,\n type HitlAdmission,\n type HitlDecision,\n type InboxItem,\n type ParkedResponse,\n type PauseDecision\n} from './inbox.ts'\nimport { RunStore } from './store.ts'\n\nexport type { InterruptReason } from './coordinator.ts'\n\nexport const defaultMaxResumeAttempts = 10\n\nexport class InvalidMaxResumeAttempts extends Data.TaggedError('InvalidMaxResumeAttempts')<{\n readonly maxResumeAttempts: number\n}> {}\n\nconst isValidMaxResumeAttempts = (value: number) => Number.isSafeInteger(value) && value >= 0\n\nexport type ResumeSuspendedResult = {\n readonly resumed: ReadonlyArray<string>\n readonly exhausted: ReadonlyArray<string>\n}\n\nexport type DrainContext = {\n readonly drainToken: string\n readonly readyResponses: ReadonlyArray<ParkedResponse>\n}\n\nexport type Drain = (\n runId: string,\n force: boolean,\n scope: Promotable,\n context: DrainContext\n) => Effect.Effect<void, never, Inbox | RunStore>\n\nexport type DriverLayerOptions = {\n readonly drain?: Drain\n readonly maxResumeAttempts?: number\n}\n\nexport type StopDecision =\n | { readonly _tag: 'Interrupted' }\n | { readonly _tag: 'ParkCleared' }\n | { readonly _tag: 'Idle' }\n\nexport const StopDecision = stopDecision\n\nexport type DriverApi = {\n readonly active: Effect.Effect<ReadonlySet<string>>\n readonly isActive: (runId: string) => Effect.Effect<boolean>\n readonly run: (runId: string) => Effect.Effect<void>\n readonly wake: (runId: string, scope?: Promotable) => Effect.Effect<void>\n readonly interrupt: (\n runId: string,\n options?: { readonly reason?: InterruptReason; readonly awaitSettlement?: boolean }\n ) => Effect.Effect<boolean>\n readonly awaitIdle: (runId: string) => Effect.Effect<void>\n readonly resumeSuspended: Effect.Effect<ResumeSuspendedResult>\n readonly admit: (item: InboxItem) => Effect.Effect<void>\n readonly pause: (\n runId: string,\n requestIds: ReadonlyArray<string>,\n drainToken: string\n ) => Effect.Effect<PauseDecision>\n readonly resumeHitl: (runId: string, admission: HitlAdmission) => Effect.Effect<HitlDecision>\n readonly stop: (runId: string) => Effect.Effect<StopDecision>\n}\n\nexport class Driver extends Context.Service<Driver, DriverApi>()('@yolk-sdk/harness/Driver') {\n static layer(): Layer.Layer<Driver, never, RunStore | Inbox | RunCoordinator>\n static layer(options?: {\n readonly maxResumeAttempts?: undefined\n }): Layer.Layer<Driver, never, RunStore | Inbox | RunCoordinator>\n static layer(\n options?: Pick<DriverLayerOptions, 'maxResumeAttempts'>\n ): Layer.Layer<Driver, InvalidMaxResumeAttempts, RunStore | Inbox | RunCoordinator>\n static layer(options?: Pick<DriverLayerOptions, 'maxResumeAttempts'>) {\n return Layer.effect(\n Driver,\n Effect.gen(function* () {\n const maxResumeAttempts = options?.maxResumeAttempts ?? defaultMaxResumeAttempts\n\n if (!isValidMaxResumeAttempts(maxResumeAttempts)) {\n return yield* Effect.fail(new InvalidMaxResumeAttempts({ maxResumeAttempts }))\n }\n\n const store = yield* RunStore\n const inbox = yield* Inbox\n const sweep = yield* Semaphore.make(1)\n const coordinator = yield* RunCoordinator\n\n const resumeSuspended = sweep.withPermits(1)(\n Effect.gen(function* () {\n const claimed = yield* store.claimed\n const resumed: Array<string> = []\n const exhausted: Array<string> = []\n\n for (const runId of claimed) {\n const admission = yield* inbox.admitRecovery(\n runId,\n Effect.gen(function* () {\n if (yield* coordinator.isActive(runId)) return RecoveryAttempt.Skip()\n\n if (!(yield* store.isClaimed(runId))) return RecoveryAttempt.Skip()\n const count = yield* store.resumeCount(runId)\n\n if (count >= maxResumeAttempts) {\n yield* store.release(runId)\n\n return RecoveryAttempt.Exhausted()\n }\n\n yield* store.incrementResumeCount(runId)\n\n return RecoveryAttempt.Resume()\n }),\n coordinator.wake(runId, 'input')\n )\n\n if (Predicate.isTagged(admission, 'Resumed')) resumed.push(runId)\n else if (Predicate.isTagged(admission, 'Exhausted')) exhausted.push(runId)\n }\n\n return { resumed, exhausted }\n })\n )\n\n return Driver.of({\n active: coordinator.active,\n isActive: coordinator.isActive,\n run: runId => {\n const continueRun = (): Effect.Effect<void> =>\n inbox.startIfUnblocked(runId, coordinator.captureRun(runId)).pipe(\n Effect.flatMap(ticket => {\n if (ticket === undefined) return Effect.void\n\n if (Predicate.isTagged(ticket, 'Stopping')) {\n return ticket.awaitSettlement.pipe(Effect.andThen(Effect.suspend(continueRun)))\n }\n\n return ticket.join\n })\n )\n\n return continueRun()\n },\n wake: (runId, scope = 'input') =>\n inbox.wakeIfUnblocked(runId, scope, coordinator.wake(runId, scope)).pipe(Effect.asVoid),\n interrupt: (runId, interruptOptions) =>\n coordinator.interrupt(runId, interruptOptions?.reason ?? 'user', interruptOptions),\n awaitIdle: coordinator.awaitIdle,\n resumeSuspended,\n admit: item =>\n inbox.enqueueAndWake(\n item,\n coordinator.wake(item.runId, item.delivery === 'steer' ? 'steer' : 'input')\n ),\n pause: (runId, requestIds, drainToken) => inbox.park(runId, requestIds, drainToken),\n resumeHitl: (runId, admission) =>\n inbox.acceptHitl(runId, admission, coordinator.wake(runId, 'input')),\n stop: runId =>\n inbox\n .invalidate(\n runId,\n coordinator\n .terminalStop(runId, 'user')\n .pipe(\n Effect.map(\n receipt =>\n Predicate.isTagged(receipt, 'Interrupted') ||\n Predicate.isTagged(receipt, 'LiveStopping')\n )\n ),\n store\n .isClaimed(runId)\n .pipe(Effect.flatMap(claimed => (claimed ? store.release(runId) : Effect.void)))\n )\n .pipe(\n Effect.map(result => {\n if (result.interrupted) return StopDecision.Interrupted()\n\n if (result.hadPark) return StopDecision.ParkCleared()\n\n return StopDecision.Idle()\n })\n )\n })\n })\n )\n }\n\n static coordinatedLayer(): Layer.Layer<Driver, never, RunStore | Inbox>\n static coordinatedLayer(options?: {\n readonly drain?: Drain\n readonly maxResumeAttempts?: undefined\n }): Layer.Layer<Driver, never, RunStore | Inbox>\n static coordinatedLayer(\n options?: DriverLayerOptions\n ): Layer.Layer<Driver, InvalidMaxResumeAttempts, RunStore | Inbox>\n static coordinatedLayer(options?: DriverLayerOptions) {\n return Layer.unwrap(\n Effect.gen(function* () {\n const store = yield* RunStore\n const inbox = yield* Inbox\n // Keep compatibility option reads lazy and in drain/max order.\n const hostDrain = options?.drain ?? ((_runId, _force, _scope, _context) => Effect.void)\n const maxResumeAttempts = options?.maxResumeAttempts ?? defaultMaxResumeAttempts\n const storeLayer = Layer.succeed(RunStore, store)\n const inboxLayer = Layer.succeed(Inbox, inbox)\n\n const coordinator = RunCoordinator.layer({\n drain: (runId, force, scope) =>\n inbox.beginDrain(runId, scope).pipe(\n Effect.flatMap(begun => {\n if (DrainBegin.$is('Skip')(begun)) return Effect.void\n\n return Effect.suspend(() =>\n hostDrain(runId, force, scope, {\n drainToken: begun.drainToken,\n readyResponses: begun.readyResponses\n })\n ).pipe(\n Effect.provideService(Inbox, inbox),\n Effect.provideService(RunStore, store),\n Effect.onExit(exit =>\n inbox.endDrain(runId, begun.drainToken, Exit.isSuccess(exit))\n )\n )\n })\n )\n }).pipe(Layer.provide(storeLayer))\n\n return Driver.layer({ maxResumeAttempts }).pipe(\n Layer.provide(coordinator),\n Layer.provide(storeLayer),\n Layer.provide(inboxLayer)\n )\n })\n )\n }\n}\n\nexport const makeHarness = <DE, DR, SE, SR, IE, IR>(input: {\n readonly driver: Layer.Layer<Driver, DE, DR>\n readonly store: Layer.Layer<RunStore, SE, SR>\n readonly inbox: Layer.Layer<Inbox, IE, IR>\n}): Layer.Layer<Driver | RunStore | Inbox, DE | SE | IE, DR | SR | IR> =>\n Layer.mergeAll(input.driver, input.store, input.inbox)\n\nexport const admit = (item: InboxItem) => Effect.flatMap(Driver, driver => driver.admit(item))\n\nexport function makeDriverLayer(): Layer.Layer<Driver, never, RunStore | Inbox>\nexport function makeDriverLayer(options?: {\n readonly drain?: Drain\n readonly maxResumeAttempts?: undefined\n}): Layer.Layer<Driver, never, RunStore | Inbox>\nexport function makeDriverLayer(\n options?: DriverLayerOptions\n): Layer.Layer<Driver, InvalidMaxResumeAttempts, RunStore | Inbox>\nexport function makeDriverLayer(options?: DriverLayerOptions) {\n return options === undefined ? Driver.coordinatedLayer() : Driver.coordinatedLayer(options)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"driver.mjs","names":["stopDecision"],"sources":["../src/driver.ts"],"sourcesContent":["import { Context, Data, Effect, Exit, Layer, Predicate, Semaphore } from 'effect'\nimport { StopDecision as stopDecision } from './outcome-constructors-internal.ts'\nimport { RunCoordinator, type InterruptReason, type Promotable } from './coordinator.ts'\nimport {\n DrainBegin,\n Inbox,\n RecoveryAttempt,\n type DrainToken,\n type HitlAdmission,\n type HitlDecision,\n type InboxItem,\n type ParkedResponse,\n type PauseDecision\n} from './inbox.ts'\nimport { RunStore } from './store.ts'\n\nexport type { InterruptReason } from './coordinator.ts'\n\nexport const defaultMaxResumeAttempts = 10\n\nexport class InvalidMaxResumeAttempts extends Data.TaggedError('InvalidMaxResumeAttempts')<{\n readonly maxResumeAttempts: number\n}> {}\n\nconst isValidMaxResumeAttempts = (value: number) => Number.isSafeInteger(value) && value >= 0\n\nexport type ResumeSuspendedResult = {\n readonly resumed: ReadonlyArray<string>\n readonly exhausted: ReadonlyArray<string>\n}\n\nexport type DrainContext = {\n readonly drainToken: DrainToken\n readonly readyResponses: ReadonlyArray<ParkedResponse>\n}\n\nexport type Drain = (\n runId: string,\n force: boolean,\n scope: Promotable,\n context: DrainContext\n) => Effect.Effect<void, never, Inbox | RunStore>\n\nexport type DriverLayerOptions = {\n readonly drain?: Drain\n readonly maxResumeAttempts?: number\n}\n\nexport type StopDecision =\n | { readonly _tag: 'Interrupted' }\n | { readonly _tag: 'ParkCleared' }\n | { readonly _tag: 'Idle' }\n\nexport const StopDecision = stopDecision\n\nexport type DriverApi = {\n readonly active: Effect.Effect<ReadonlySet<string>>\n readonly isActive: (runId: string) => Effect.Effect<boolean>\n readonly run: (runId: string) => Effect.Effect<void>\n readonly wake: (runId: string, scope?: Promotable) => Effect.Effect<void>\n readonly interrupt: (\n runId: string,\n options?: { readonly reason?: InterruptReason; readonly awaitSettlement?: boolean }\n ) => Effect.Effect<boolean>\n readonly awaitIdle: (runId: string) => Effect.Effect<void>\n readonly resumeSuspended: Effect.Effect<ResumeSuspendedResult>\n readonly admit: (item: InboxItem) => Effect.Effect<void>\n readonly pause: (\n runId: string,\n requestIds: ReadonlyArray<string>,\n drainToken: DrainToken\n ) => Effect.Effect<PauseDecision>\n readonly resumeHitl: (runId: string, admission: HitlAdmission) => Effect.Effect<HitlDecision>\n readonly stop: (runId: string) => Effect.Effect<StopDecision>\n}\n\nexport class Driver extends Context.Service<Driver, DriverApi>()('@yolk-sdk/harness/Driver') {\n static layer(): Layer.Layer<Driver, never, RunStore | Inbox | RunCoordinator>\n static layer(options?: {\n readonly maxResumeAttempts?: undefined\n }): Layer.Layer<Driver, never, RunStore | Inbox | RunCoordinator>\n static layer(\n options?: Pick<DriverLayerOptions, 'maxResumeAttempts'>\n ): Layer.Layer<Driver, InvalidMaxResumeAttempts, RunStore | Inbox | RunCoordinator>\n static layer(options?: Pick<DriverLayerOptions, 'maxResumeAttempts'>) {\n return Layer.effect(\n Driver,\n Effect.gen(function* () {\n const maxResumeAttempts = options?.maxResumeAttempts ?? defaultMaxResumeAttempts\n\n if (!isValidMaxResumeAttempts(maxResumeAttempts)) {\n return yield* Effect.fail(new InvalidMaxResumeAttempts({ maxResumeAttempts }))\n }\n\n const store = yield* RunStore\n const inbox = yield* Inbox\n const sweep = yield* Semaphore.make(1)\n const coordinator = yield* RunCoordinator\n\n const resumeSuspended = sweep.withPermits(1)(\n Effect.gen(function* () {\n const claimed = yield* store.claimed\n const resumed: Array<string> = []\n const exhausted: Array<string> = []\n\n for (const runId of claimed) {\n const admission = yield* inbox.admitRecovery(\n runId,\n Effect.gen(function* () {\n if (yield* coordinator.isActive(runId)) return RecoveryAttempt.Skip()\n\n if (!(yield* store.isClaimed(runId))) return RecoveryAttempt.Skip()\n const count = yield* store.resumeCount(runId)\n\n if (count >= maxResumeAttempts) {\n yield* store.release(runId)\n\n return RecoveryAttempt.Exhausted()\n }\n\n yield* store.incrementResumeCount(runId)\n\n return RecoveryAttempt.Resume()\n }),\n coordinator.wake(runId, 'input')\n )\n\n if (Predicate.isTagged(admission, 'Resumed')) resumed.push(runId)\n else if (Predicate.isTagged(admission, 'Exhausted')) exhausted.push(runId)\n }\n\n return { resumed, exhausted }\n })\n )\n\n return Driver.of({\n active: coordinator.active,\n isActive: coordinator.isActive,\n run: runId => {\n const continueRun = (): Effect.Effect<void> =>\n inbox.startIfUnblocked(runId, coordinator.captureRun(runId)).pipe(\n Effect.flatMap(ticket => {\n if (ticket === undefined) return Effect.void\n\n if (Predicate.isTagged(ticket, 'Stopping')) {\n return ticket.awaitSettlement.pipe(Effect.andThen(Effect.suspend(continueRun)))\n }\n\n return ticket.join\n })\n )\n\n return continueRun()\n },\n wake: (runId, scope = 'input') =>\n inbox.wakeIfUnblocked(runId, scope, coordinator.wake(runId, scope)).pipe(Effect.asVoid),\n interrupt: (runId, interruptOptions) =>\n coordinator.interrupt(runId, interruptOptions?.reason ?? 'user', interruptOptions),\n awaitIdle: coordinator.awaitIdle,\n resumeSuspended,\n admit: item =>\n inbox.enqueueAndWake(\n item,\n coordinator.wake(item.runId, item.delivery === 'steer' ? 'steer' : 'input')\n ),\n pause: (runId, requestIds, drainToken) => inbox.park(runId, requestIds, drainToken),\n resumeHitl: (runId, admission) =>\n inbox.acceptHitl(runId, admission, coordinator.wake(runId, 'input')),\n stop: runId =>\n inbox\n .invalidate(\n runId,\n coordinator\n .terminalStop(runId, 'user')\n .pipe(\n Effect.map(\n receipt =>\n Predicate.isTagged(receipt, 'Interrupted') ||\n Predicate.isTagged(receipt, 'LiveStopping')\n )\n ),\n store\n .isClaimed(runId)\n .pipe(Effect.flatMap(claimed => (claimed ? store.release(runId) : Effect.void)))\n )\n .pipe(\n Effect.map(result => {\n if (result.interrupted) return StopDecision.Interrupted()\n\n if (result.hadPark) return StopDecision.ParkCleared()\n\n return StopDecision.Idle()\n })\n )\n })\n })\n )\n }\n\n static coordinatedLayer(): Layer.Layer<Driver, never, RunStore | Inbox>\n static coordinatedLayer(options?: {\n readonly drain?: Drain\n readonly maxResumeAttempts?: undefined\n }): Layer.Layer<Driver, never, RunStore | Inbox>\n static coordinatedLayer(\n options?: DriverLayerOptions\n ): Layer.Layer<Driver, InvalidMaxResumeAttempts, RunStore | Inbox>\n static coordinatedLayer(options?: DriverLayerOptions) {\n return Layer.unwrap(\n Effect.gen(function* () {\n const store = yield* RunStore\n const inbox = yield* Inbox\n // Keep compatibility option reads lazy and in drain/max order.\n const hostDrain = options?.drain ?? ((_runId, _force, _scope, _context) => Effect.void)\n const maxResumeAttempts = options?.maxResumeAttempts ?? defaultMaxResumeAttempts\n const storeLayer = Layer.succeed(RunStore, store)\n const inboxLayer = Layer.succeed(Inbox, inbox)\n\n const coordinator = RunCoordinator.layer({\n drain: (runId, force, scope) =>\n inbox.beginDrain(runId, scope).pipe(\n Effect.flatMap(begun => {\n if (DrainBegin.$is('Skip')(begun)) return Effect.void\n\n return Effect.suspend(() =>\n hostDrain(runId, force, scope, {\n drainToken: begun.drainToken,\n readyResponses: begun.readyResponses\n })\n ).pipe(\n Effect.provideService(Inbox, inbox),\n Effect.provideService(RunStore, store),\n Effect.onExit(exit =>\n inbox.endDrain(runId, begun.drainToken, Exit.isSuccess(exit))\n )\n )\n })\n )\n }).pipe(Layer.provide(storeLayer))\n\n return Driver.layer({ maxResumeAttempts }).pipe(\n Layer.provide(coordinator),\n Layer.provide(storeLayer),\n Layer.provide(inboxLayer)\n )\n })\n )\n }\n}\n\nexport const makeHarness = <DE, DR, SE, SR, IE, IR>(input: {\n readonly driver: Layer.Layer<Driver, DE, DR>\n readonly store: Layer.Layer<RunStore, SE, SR>\n readonly inbox: Layer.Layer<Inbox, IE, IR>\n}): Layer.Layer<Driver | RunStore | Inbox, DE | SE | IE, DR | SR | IR> =>\n Layer.mergeAll(input.driver, input.store, input.inbox)\n\nexport const admit = (item: InboxItem) => Effect.flatMap(Driver, driver => driver.admit(item))\n\nexport function makeDriverLayer(): Layer.Layer<Driver, never, RunStore | Inbox>\nexport function makeDriverLayer(options?: {\n readonly drain?: Drain\n readonly maxResumeAttempts?: undefined\n}): Layer.Layer<Driver, never, RunStore | Inbox>\nexport function makeDriverLayer(\n options?: DriverLayerOptions\n): Layer.Layer<Driver, InvalidMaxResumeAttempts, RunStore | Inbox>\nexport function makeDriverLayer(options?: DriverLayerOptions) {\n return options === undefined ? Driver.coordinatedLayer() : Driver.coordinatedLayer(options)\n}\n"],"mappings":"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"}
|
package/dist/inbox.d.mts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { CapturedRun, Promotable } from "./coordinator.mjs";
|
|
2
2
|
import { Context, Data, Effect, Layer } from "effect";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
3
4
|
|
|
4
5
|
//#region src/inbox.d.ts
|
|
6
|
+
/** Nominal identity only. All strings stay admissible so stale/empty/foreign
|
|
7
|
+
* tokens retain the inbox's existing no-op decisions rather than parse failures. */
|
|
8
|
+
declare const DrainToken: Schema.brand<Schema.String, "DrainToken">;
|
|
9
|
+
type DrainToken = typeof DrainToken.Type;
|
|
10
|
+
/** Nominal identity only; freshness and instance ownership are checked by Inbox. */
|
|
11
|
+
declare const ParkGeneration: Schema.brand<Schema.String, "ParkGeneration">;
|
|
12
|
+
type ParkGeneration = typeof ParkGeneration.Type;
|
|
5
13
|
type InboxKind = 'input' | 'hitl';
|
|
6
14
|
type InboxItem = {
|
|
7
15
|
readonly id: string;
|
|
@@ -14,7 +22,7 @@ type ParkedResponse = {
|
|
|
14
22
|
readonly requestId: string;
|
|
15
23
|
};
|
|
16
24
|
type ParkedState = {
|
|
17
|
-
readonly generation:
|
|
25
|
+
readonly generation: ParkGeneration;
|
|
18
26
|
readonly requestIds: ReadonlyArray<string>;
|
|
19
27
|
readonly responses: ReadonlyArray<ParkedResponse>;
|
|
20
28
|
readonly ready: boolean;
|
|
@@ -22,7 +30,7 @@ type ParkedState = {
|
|
|
22
30
|
type HitlAdmission = {
|
|
23
31
|
readonly itemId: string;
|
|
24
32
|
readonly requestId: string;
|
|
25
|
-
readonly generation:
|
|
33
|
+
readonly generation: ParkGeneration;
|
|
26
34
|
};
|
|
27
35
|
type HitlDecision = {
|
|
28
36
|
readonly _tag: 'Accepted';
|
|
@@ -39,7 +47,7 @@ type HitlDecision = {
|
|
|
39
47
|
};
|
|
40
48
|
type PauseDecision = {
|
|
41
49
|
readonly _tag: 'Parked';
|
|
42
|
-
readonly generation:
|
|
50
|
+
readonly generation: ParkGeneration;
|
|
43
51
|
} | {
|
|
44
52
|
readonly _tag: 'Stale';
|
|
45
53
|
};
|
|
@@ -61,7 +69,7 @@ type DrainBegin = {
|
|
|
61
69
|
readonly _tag: 'Skip';
|
|
62
70
|
} | {
|
|
63
71
|
readonly _tag: 'Run';
|
|
64
|
-
readonly drainToken:
|
|
72
|
+
readonly drainToken: DrainToken;
|
|
65
73
|
readonly readyResponses: ReadonlyArray<ParkedResponse>;
|
|
66
74
|
};
|
|
67
75
|
declare const HitlDecision: {
|
|
@@ -157,11 +165,11 @@ declare const PauseDecision: {
|
|
|
157
165
|
}, "_tag">;
|
|
158
166
|
readonly Parked: Data.TaggedEnum.ConstructorFrom<{
|
|
159
167
|
readonly _tag: "Parked";
|
|
160
|
-
readonly generation:
|
|
168
|
+
readonly generation: ParkGeneration;
|
|
161
169
|
}, "_tag">;
|
|
162
170
|
readonly $is: <Tag extends "Stale" | "Parked">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
163
171
|
readonly _tag: "Parked";
|
|
164
|
-
readonly generation:
|
|
172
|
+
readonly generation: ParkGeneration;
|
|
165
173
|
}, {
|
|
166
174
|
readonly _tag: Tag;
|
|
167
175
|
}> | Extract<{
|
|
@@ -176,7 +184,7 @@ declare const PauseDecision: {
|
|
|
176
184
|
}) => any;
|
|
177
185
|
readonly Parked: (args: {
|
|
178
186
|
readonly _tag: "Parked";
|
|
179
|
-
readonly generation:
|
|
187
|
+
readonly generation: ParkGeneration;
|
|
180
188
|
}) => any;
|
|
181
189
|
}>(cases: Cases): (value: PauseDecision) => import("effect/Unify").Unify<ReturnType<Cases["Stale" | "Parked"]>>;
|
|
182
190
|
<Cases extends {
|
|
@@ -185,7 +193,7 @@ declare const PauseDecision: {
|
|
|
185
193
|
}) => any;
|
|
186
194
|
readonly Parked: (args: {
|
|
187
195
|
readonly _tag: "Parked";
|
|
188
|
-
readonly generation:
|
|
196
|
+
readonly generation: ParkGeneration;
|
|
189
197
|
}) => any;
|
|
190
198
|
}>(value: PauseDecision, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Stale" | "Parked"]>>;
|
|
191
199
|
};
|
|
@@ -292,7 +300,7 @@ declare const DrainBegin: {
|
|
|
292
300
|
}, "_tag">;
|
|
293
301
|
readonly Run: Data.TaggedEnum.ConstructorFrom<{
|
|
294
302
|
readonly _tag: "Run";
|
|
295
|
-
readonly drainToken:
|
|
303
|
+
readonly drainToken: DrainToken;
|
|
296
304
|
readonly readyResponses: ReadonlyArray<ParkedResponse>;
|
|
297
305
|
}, "_tag">;
|
|
298
306
|
readonly $is: <Tag extends "Skip" | "Run">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
@@ -301,7 +309,7 @@ declare const DrainBegin: {
|
|
|
301
309
|
readonly _tag: Tag;
|
|
302
310
|
}> | Extract<{
|
|
303
311
|
readonly _tag: "Run";
|
|
304
|
-
readonly drainToken:
|
|
312
|
+
readonly drainToken: DrainToken;
|
|
305
313
|
readonly readyResponses: ReadonlyArray<ParkedResponse>;
|
|
306
314
|
}, {
|
|
307
315
|
readonly _tag: Tag;
|
|
@@ -313,7 +321,7 @@ declare const DrainBegin: {
|
|
|
313
321
|
}) => any;
|
|
314
322
|
readonly Run: (args: {
|
|
315
323
|
readonly _tag: "Run";
|
|
316
|
-
readonly drainToken:
|
|
324
|
+
readonly drainToken: DrainToken;
|
|
317
325
|
readonly readyResponses: ReadonlyArray<ParkedResponse>;
|
|
318
326
|
}) => any;
|
|
319
327
|
}>(cases: Cases): (value: DrainBegin) => import("effect/Unify").Unify<ReturnType<Cases["Skip" | "Run"]>>;
|
|
@@ -323,7 +331,7 @@ declare const DrainBegin: {
|
|
|
323
331
|
}) => any;
|
|
324
332
|
readonly Run: (args: {
|
|
325
333
|
readonly _tag: "Run";
|
|
326
|
-
readonly drainToken:
|
|
334
|
+
readonly drainToken: DrainToken;
|
|
327
335
|
readonly readyResponses: ReadonlyArray<ParkedResponse>;
|
|
328
336
|
}) => any;
|
|
329
337
|
}>(value: DrainBegin, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Skip" | "Run"]>>;
|
|
@@ -331,14 +339,14 @@ declare const DrainBegin: {
|
|
|
331
339
|
};
|
|
332
340
|
type InboxApi = {
|
|
333
341
|
readonly enqueue: (item: InboxItem) => Effect.Effect<void>;
|
|
334
|
-
readonly takePromotable: (runId: string, scope: Promotable, drainToken:
|
|
342
|
+
readonly takePromotable: (runId: string, scope: Promotable, drainToken: DrainToken) => Effect.Effect<InboxItem | undefined>;
|
|
335
343
|
readonly pending: (runId: string) => Effect.Effect<ReadonlyArray<InboxItem>>;
|
|
336
344
|
readonly parked: (runId: string) => Effect.Effect<ParkedState | undefined>;
|
|
337
|
-
readonly park: (runId: string, requestIds: ReadonlyArray<string>, drainToken:
|
|
345
|
+
readonly park: (runId: string, requestIds: ReadonlyArray<string>, drainToken: DrainToken) => Effect.Effect<PauseDecision>;
|
|
338
346
|
readonly acceptHitl: (runId: string, admission: HitlAdmission, onReady: Effect.Effect<void>) => Effect.Effect<HitlDecision>;
|
|
339
|
-
readonly clearPark: (runId: string, generation:
|
|
347
|
+
readonly clearPark: (runId: string, generation: ParkGeneration) => Effect.Effect<boolean>;
|
|
340
348
|
readonly beginDrain: (runId: string, scope: Promotable) => Effect.Effect<DrainBegin>;
|
|
341
|
-
readonly endDrain: (runId: string, drainToken:
|
|
349
|
+
readonly endDrain: (runId: string, drainToken: DrainToken, acknowledged: boolean) => Effect.Effect<void>;
|
|
342
350
|
readonly invalidate: (runId: string, interrupt: Effect.Effect<boolean>, releaseIdleClaim: Effect.Effect<void>) => Effect.Effect<{
|
|
343
351
|
readonly hadPark: boolean;
|
|
344
352
|
readonly interrupted: boolean;
|
|
@@ -356,5 +364,5 @@ declare class Inbox extends Inbox_base {
|
|
|
356
364
|
/** Backward-compatible delegation to the owning inbox layer. */
|
|
357
365
|
declare const makeInMemoryInboxLayer: () => Layer.Layer<Inbox>;
|
|
358
366
|
//#endregion
|
|
359
|
-
export { DrainBegin, HitlAdmission, HitlDecision, Inbox, InboxApi, InboxItem, InboxKind, ParkedResponse, ParkedState, PauseDecision, RecoveryAdmission, RecoveryAttempt, makeInMemoryInboxLayer };
|
|
367
|
+
export { DrainBegin, DrainToken, HitlAdmission, HitlDecision, Inbox, InboxApi, InboxItem, InboxKind, ParkGeneration, ParkedResponse, ParkedState, PauseDecision, RecoveryAdmission, RecoveryAttempt, makeInMemoryInboxLayer };
|
|
360
368
|
//# sourceMappingURL=inbox.d.mts.map
|
package/dist/inbox.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbox.d.mts","names":[],"sources":["../src/inbox.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"inbox.d.mts","names":[],"sources":["../src/inbox.ts"],"mappings":"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"}
|
package/dist/inbox.mjs
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Context, Data, Effect, Layer, Predicate, Ref, Semaphore } from "effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
2
3
|
//#region src/inbox.ts
|
|
4
|
+
/** Nominal identity only. All strings stay admissible so stale/empty/foreign
|
|
5
|
+
* tokens retain the inbox's existing no-op decisions rather than parse failures. */
|
|
6
|
+
const DrainToken = Schema.String.pipe(Schema.brand("DrainToken"));
|
|
7
|
+
/** Nominal identity only; freshness and instance ownership are checked by Inbox. */
|
|
8
|
+
const ParkGeneration = Schema.String.pipe(Schema.brand("ParkGeneration"));
|
|
3
9
|
const HitlDecision = Data.taggedEnum();
|
|
4
10
|
const PauseDecision = Data.taggedEnum();
|
|
5
11
|
const RecoveryAttempt = Data.taggedEnum();
|
|
@@ -77,7 +83,7 @@ var Inbox = class Inbox extends Context.Service()("@yolk-sdk/harness/Inbox") {
|
|
|
77
83
|
const control = yield* getControl(runId);
|
|
78
84
|
if (control.liveToken !== drainToken) return PauseDecision.Stale();
|
|
79
85
|
generationSeq += 1;
|
|
80
|
-
const generation = String(generationSeq);
|
|
86
|
+
const generation = ParkGeneration.make(String(generationSeq));
|
|
81
87
|
yield* writeControl(runId, {
|
|
82
88
|
pending: control.pending,
|
|
83
89
|
liveToken: control.liveToken,
|
|
@@ -130,7 +136,7 @@ var Inbox = class Inbox extends Context.Service()("@yolk-sdk/harness/Inbox") {
|
|
|
130
136
|
const pending = control.pending;
|
|
131
137
|
if (parkBlocked(control.park) || pending === void 0) return DrainBegin.Skip();
|
|
132
138
|
drainSeq += 1;
|
|
133
|
-
const drainToken = `d${drainSeq}
|
|
139
|
+
const drainToken = DrainToken.make(`d${drainSeq}`);
|
|
134
140
|
const park = control.park;
|
|
135
141
|
const ready = park !== void 0 && parkComplete(park) ? park : void 0;
|
|
136
142
|
yield* writeControl(runId, {
|
|
@@ -224,6 +230,6 @@ var Inbox = class Inbox extends Context.Service()("@yolk-sdk/harness/Inbox") {
|
|
|
224
230
|
/** Backward-compatible delegation to the owning inbox layer. */
|
|
225
231
|
const makeInMemoryInboxLayer = () => Inbox.layer();
|
|
226
232
|
//#endregion
|
|
227
|
-
export { DrainBegin, HitlDecision, Inbox, PauseDecision, RecoveryAdmission, RecoveryAttempt, makeInMemoryInboxLayer };
|
|
233
|
+
export { DrainBegin, DrainToken, HitlDecision, Inbox, ParkGeneration, PauseDecision, RecoveryAdmission, RecoveryAttempt, makeInMemoryInboxLayer };
|
|
228
234
|
|
|
229
235
|
//# sourceMappingURL=inbox.mjs.map
|
package/dist/inbox.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbox.mjs","names":[],"sources":["../src/inbox.ts"],"sourcesContent":["import { Context, Data, Effect, Layer, Predicate, Ref, Semaphore } from 'effect'\nimport type { CapturedRun, Promotable } from './coordinator.ts'\n\nexport type InboxKind = 'input' | 'hitl'\n\nexport type InboxItem = {\n readonly id: string\n readonly runId: string\n readonly delivery: Promotable | 'queue'\n readonly kind: InboxKind\n}\n\nexport type ParkedResponse = {\n readonly itemId: string\n readonly requestId: string\n}\n\nexport type ParkedState = {\n readonly generation: string\n readonly requestIds: ReadonlyArray<string>\n readonly responses: ReadonlyArray<ParkedResponse>\n readonly ready: boolean\n}\n\nexport type HitlAdmission = {\n readonly itemId: string\n readonly requestId: string\n readonly generation: string\n}\n\nexport type HitlDecision =\n | { readonly _tag: 'Accepted' }\n | { readonly _tag: 'Ready' }\n | { readonly _tag: 'Duplicate' }\n | { readonly _tag: 'Stale' }\n | { readonly _tag: 'UnknownRequest' }\n | { readonly _tag: 'NotParked' }\n\nexport type PauseDecision =\n | { readonly _tag: 'Parked'; readonly generation: string }\n | { readonly _tag: 'Stale' }\n\nexport type RecoveryAttempt =\n | { readonly _tag: 'Skip' }\n | { readonly _tag: 'Exhausted' }\n | { readonly _tag: 'Resume' }\n\nexport type RecoveryAdmission =\n | { readonly _tag: 'Skip' }\n | { readonly _tag: 'Exhausted' }\n | { readonly _tag: 'Resumed' }\n\nexport type DrainBegin =\n | { readonly _tag: 'Skip' }\n | {\n readonly _tag: 'Run'\n readonly drainToken: string\n readonly readyResponses: ReadonlyArray<ParkedResponse>\n }\n\nexport const HitlDecision = Data.taggedEnum<HitlDecision>()\n\nexport const PauseDecision = Data.taggedEnum<PauseDecision>()\n\nexport const RecoveryAttempt = Data.taggedEnum<RecoveryAttempt>()\n\nexport const RecoveryAdmission = Data.taggedEnum<RecoveryAdmission>()\n\nexport const DrainBegin = Data.taggedEnum<DrainBegin>()\n\nexport type InboxApi = {\n readonly enqueue: (item: InboxItem) => Effect.Effect<void>\n readonly takePromotable: (\n runId: string,\n scope: Promotable,\n drainToken: string\n ) => Effect.Effect<InboxItem | undefined>\n readonly pending: (runId: string) => Effect.Effect<ReadonlyArray<InboxItem>>\n readonly parked: (runId: string) => Effect.Effect<ParkedState | undefined>\n readonly park: (\n runId: string,\n requestIds: ReadonlyArray<string>,\n drainToken: string\n ) => Effect.Effect<PauseDecision>\n readonly acceptHitl: (\n runId: string,\n admission: HitlAdmission,\n onReady: Effect.Effect<void>\n ) => Effect.Effect<HitlDecision>\n readonly clearPark: (runId: string, generation: string) => Effect.Effect<boolean>\n readonly beginDrain: (runId: string, scope: Promotable) => Effect.Effect<DrainBegin>\n readonly endDrain: (\n runId: string,\n drainToken: string,\n acknowledged: boolean\n ) => Effect.Effect<void>\n readonly invalidate: (\n runId: string,\n interrupt: Effect.Effect<boolean>,\n releaseIdleClaim: Effect.Effect<void>\n ) => Effect.Effect<{ readonly hadPark: boolean; readonly interrupted: boolean }>\n readonly enqueueAndWake: (item: InboxItem, wake: Effect.Effect<void>) => Effect.Effect<void>\n readonly wakeIfUnblocked: (\n runId: string,\n scope: Promotable,\n wake: Effect.Effect<void>\n ) => Effect.Effect<boolean>\n readonly startIfUnblocked: <E, R>(\n runId: string,\n start: Effect.Effect<CapturedRun<E>, E, R>\n ) => Effect.Effect<CapturedRun<E> | undefined, E, R>\n readonly admitRecovery: <E, R>(\n runId: string,\n attempt: Effect.Effect<RecoveryAttempt, E, R>,\n wake: Effect.Effect<void>\n ) => Effect.Effect<RecoveryAdmission, E, R>\n}\n\nconst isPromotableAt = (item: InboxItem, scope: Promotable) => {\n if (scope === 'steer') return item.delivery === 'steer'\n\n return item.delivery === 'steer' || item.delivery === 'input' || item.delivery === 'queue'\n}\n\ntype Park = {\n readonly generation: string\n readonly requestIds: ReadonlyArray<string>\n readonly responses: ReadonlyArray<ParkedResponse>\n readonly readyWoken: boolean\n}\n\ntype RunControl = {\n readonly pending: Promotable | undefined\n readonly liveToken: string | undefined\n readonly park: Park | undefined\n readonly leasedGeneration: string | undefined\n}\n\nconst emptyControl: RunControl = {\n pending: undefined,\n liveToken: undefined,\n park: undefined,\n leasedGeneration: undefined\n}\n\nconst widerPending = (current: Promotable | undefined, next: Promotable): Promotable =>\n current === 'input' || next === 'input' ? 'input' : 'steer'\n\nconst remainingPending = (pending: Promotable, scope: Promotable): Promotable | undefined =>\n scope === 'steer' && pending === 'input' ? 'input' : undefined\n\nconst itemScope = (item: InboxItem): Promotable => (item.delivery === 'steer' ? 'steer' : 'input')\n\nconst parkComplete = (park: Park) =>\n park.requestIds.every(requestId =>\n park.responses.some(response => response.requestId === requestId)\n )\n\nconst parkBlocked = (park: Park | undefined) => park !== undefined && !parkComplete(park)\n\nconst parkedState = (park: Park): ParkedState => ({\n generation: park.generation,\n requestIds: park.requestIds,\n responses: park.responses,\n ready: parkComplete(park)\n})\n\nconst isPrunable = (control: RunControl) =>\n control.pending === undefined &&\n control.liveToken === undefined &&\n control.park === undefined &&\n control.leasedGeneration === undefined\n\nexport class Inbox extends Context.Service<Inbox, InboxApi>()('@yolk-sdk/harness/Inbox') {\n /** Fresh queue, admission gate and HITL generations per layer acquisition. */\n static layer = (): Layer.Layer<Inbox> =>\n Layer.effect(\n this,\n Effect.gen(function* () {\n const items = yield* Ref.make<ReadonlyArray<InboxItem>>([])\n const controls = yield* Ref.make<ReadonlyMap<string, RunControl>>(new Map())\n const gate = yield* Semaphore.make(1)\n let generationSeq = 0\n let drainSeq = 0\n\n const withGate = <A, E, R>(body: Effect.Effect<A, E, R>) => gate.withPermits(1)(body)\n\n const getControl = (runId: string) =>\n Ref.get(controls).pipe(Effect.map(current => current.get(runId) ?? emptyControl))\n\n const writeControl = (runId: string, next: RunControl) =>\n Ref.update(controls, current => {\n const copy = new Map(current)\n\n if (isPrunable(next)) copy.delete(runId)\n else copy.set(runId, next)\n\n return copy\n })\n\n const dropQueued = (runId: string) =>\n Ref.update(items, current => current.filter(item => item.runId !== runId))\n\n const acceptHitlPure = (control: RunControl, admission: HitlAdmission): HitlDecision => {\n const park = control.park\n\n if (park === undefined) return HitlDecision.NotParked()\n\n if (park.generation !== admission.generation) return HitlDecision.Stale()\n\n if (!park.requestIds.includes(admission.requestId)) return HitlDecision.UnknownRequest()\n\n if (\n park.responses.some(\n response =>\n response.itemId === admission.itemId || response.requestId === admission.requestId\n )\n ) {\n return HitlDecision.Duplicate()\n }\n\n if (park.readyWoken) return HitlDecision.Duplicate()\n\n const responses = [\n ...park.responses,\n { itemId: admission.itemId, requestId: admission.requestId }\n ]\n\n return parkComplete({ ...park, responses })\n ? HitlDecision.Ready()\n : HitlDecision.Accepted()\n }\n\n return Inbox.of({\n enqueue: item => Ref.update(items, current => [...current, item]),\n takePromotable: (runId, scope, drainToken) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (control.liveToken === undefined || control.liveToken !== drainToken) {\n return undefined\n }\n\n return yield* Ref.modify(items, current => {\n const index = current.findIndex(\n item => item.runId === runId && isPromotableAt(item, scope)\n )\n\n if (index < 0) return [undefined, current] as const\n const taken = current[index]\n\n return [taken, current.filter((_, itemIndex) => itemIndex !== index)] as const\n })\n })\n )\n ),\n pending: runId =>\n Ref.get(items).pipe(\n Effect.map(current => current.filter(item => item.runId === runId))\n ),\n parked: runId =>\n getControl(runId).pipe(\n Effect.map(control =>\n control.park === undefined ? undefined : parkedState(control.park)\n )\n ),\n park: (runId, requestIds, drainToken) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (control.liveToken !== drainToken) return PauseDecision.Stale()\n generationSeq += 1\n const generation = String(generationSeq)\n yield* writeControl(runId, {\n pending: control.pending,\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: {\n generation,\n requestIds: [...requestIds],\n responses: [],\n readyWoken: false\n }\n })\n\n return PauseDecision.Parked({ generation })\n })\n )\n ),\n acceptHitl: (runId, admission, onReady) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n const decision = acceptHitlPure(control, admission)\n\n if (\n !Predicate.isTagged(decision, 'Accepted') &&\n !Predicate.isTagged(decision, 'Ready')\n ) {\n return decision\n }\n\n const park = control.park\n\n if (park === undefined) return HitlDecision.NotParked()\n\n const nextPark: Park = {\n ...park,\n responses: [\n ...park.responses,\n { itemId: admission.itemId, requestId: admission.requestId }\n ],\n readyWoken: Predicate.isTagged(decision, 'Ready')\n }\n\n yield* writeControl(runId, {\n pending: Predicate.isTagged(decision, 'Ready')\n ? widerPending(control.pending, 'input')\n : control.pending,\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: nextPark\n })\n\n if (Predicate.isTagged(decision, 'Ready')) yield* onReady\n\n return decision\n })\n )\n ),\n clearPark: (runId, generation) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (control.park === undefined || control.park.generation !== generation) {\n return false\n }\n\n yield* writeControl(runId, {\n pending: control.pending,\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: undefined\n })\n\n return true\n })\n )\n ),\n beginDrain: (runId, scope) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n const pending = control.pending\n\n if (parkBlocked(control.park) || pending === undefined) {\n return DrainBegin.Skip()\n }\n\n drainSeq += 1\n const drainToken = `d${drainSeq}`\n const park = control.park\n const ready = park !== undefined && parkComplete(park) ? park : undefined\n yield* writeControl(runId, {\n pending: remainingPending(pending, scope),\n liveToken: drainToken,\n park,\n leasedGeneration: ready?.generation\n })\n\n return DrainBegin.Run({\n drainToken,\n readyResponses: ready === undefined ? [] : ready.responses\n })\n })\n )\n ),\n endDrain: (runId, drainToken, acknowledged) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (control.liveToken !== drainToken) return\n const leased = control.leasedGeneration\n const park = control.park\n\n const clearPark =\n acknowledged &&\n leased !== undefined &&\n park !== undefined &&\n park.generation === leased\n\n yield* writeControl(runId, {\n pending: control.pending,\n liveToken: undefined,\n leasedGeneration: undefined,\n park: clearPark ? undefined : park\n })\n })\n )\n ),\n invalidate: (runId, interrupt, releaseIdleClaim) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n const hadPark = control.park !== undefined\n yield* dropQueued(runId)\n yield* writeControl(runId, emptyControl)\n const interrupted = yield* interrupt\n\n if (!interrupted) yield* releaseIdleClaim\n\n return { hadPark, interrupted }\n })\n )\n ),\n enqueueAndWake: (item, wake) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n yield* Ref.update(items, current => [...current, item])\n const control = yield* getControl(item.runId)\n\n if (parkBlocked(control.park)) return\n yield* writeControl(item.runId, {\n pending: widerPending(control.pending, itemScope(item)),\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: control.park\n })\n yield* wake\n })\n )\n ),\n wakeIfUnblocked: (runId, scope, wake) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (parkBlocked(control.park)) return false\n yield* writeControl(runId, {\n pending: widerPending(control.pending, scope),\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: control.park\n })\n yield* wake\n\n return true\n })\n )\n ),\n startIfUnblocked: (runId, start) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (parkBlocked(control.park)) return undefined\n const ticket = yield* start\n\n if (Predicate.isTagged(ticket, 'Started')) {\n yield* writeControl(runId, {\n pending: widerPending(control.pending, 'input'),\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: control.park\n })\n }\n\n return ticket\n })\n )\n ),\n admitRecovery: (runId, attempt, wake) =>\n Effect.uninterruptibleMask(restore =>\n restore(gate.take(1)).pipe(\n Effect.flatMap(() =>\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (parkBlocked(control.park)) return RecoveryAdmission.Skip()\n const decision = yield* attempt\n\n if (!RecoveryAttempt.$is('Resume')(decision)) return decision\n yield* writeControl(runId, {\n pending: widerPending(control.pending, 'input'),\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: control.park\n })\n yield* wake\n\n return RecoveryAdmission.Resumed()\n }).pipe(Effect.ensuring(gate.release(1)))\n )\n )\n )\n })\n })\n )\n}\n\n/** Backward-compatible delegation to the owning inbox layer. */\nexport const makeInMemoryInboxLayer = (): Layer.Layer<Inbox> => Inbox.layer()\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"inbox.mjs","names":[],"sources":["../src/inbox.ts"],"sourcesContent":["import { Context, Data, Effect, Layer, Predicate, Ref, Semaphore } from 'effect'\nimport * as Schema from 'effect/Schema'\nimport type { CapturedRun, Promotable } from './coordinator.ts'\n\n/** Nominal identity only. All strings stay admissible so stale/empty/foreign\n * tokens retain the inbox's existing no-op decisions rather than parse failures. */\nexport const DrainToken = Schema.String.pipe(Schema.brand('DrainToken'))\n\nexport type DrainToken = typeof DrainToken.Type\n\n/** Nominal identity only; freshness and instance ownership are checked by Inbox. */\nexport const ParkGeneration = Schema.String.pipe(Schema.brand('ParkGeneration'))\n\nexport type ParkGeneration = typeof ParkGeneration.Type\n\nexport type InboxKind = 'input' | 'hitl'\n\nexport type InboxItem = {\n readonly id: string\n readonly runId: string\n readonly delivery: Promotable | 'queue'\n readonly kind: InboxKind\n}\n\nexport type ParkedResponse = {\n readonly itemId: string\n readonly requestId: string\n}\n\nexport type ParkedState = {\n readonly generation: ParkGeneration\n readonly requestIds: ReadonlyArray<string>\n readonly responses: ReadonlyArray<ParkedResponse>\n readonly ready: boolean\n}\n\nexport type HitlAdmission = {\n readonly itemId: string\n readonly requestId: string\n readonly generation: ParkGeneration\n}\n\nexport type HitlDecision =\n | { readonly _tag: 'Accepted' }\n | { readonly _tag: 'Ready' }\n | { readonly _tag: 'Duplicate' }\n | { readonly _tag: 'Stale' }\n | { readonly _tag: 'UnknownRequest' }\n | { readonly _tag: 'NotParked' }\n\nexport type PauseDecision =\n | { readonly _tag: 'Parked'; readonly generation: ParkGeneration }\n | { readonly _tag: 'Stale' }\n\nexport type RecoveryAttempt =\n | { readonly _tag: 'Skip' }\n | { readonly _tag: 'Exhausted' }\n | { readonly _tag: 'Resume' }\n\nexport type RecoveryAdmission =\n | { readonly _tag: 'Skip' }\n | { readonly _tag: 'Exhausted' }\n | { readonly _tag: 'Resumed' }\n\nexport type DrainBegin =\n | { readonly _tag: 'Skip' }\n | {\n readonly _tag: 'Run'\n readonly drainToken: DrainToken\n readonly readyResponses: ReadonlyArray<ParkedResponse>\n }\n\nexport const HitlDecision = Data.taggedEnum<HitlDecision>()\n\nexport const PauseDecision = Data.taggedEnum<PauseDecision>()\n\nexport const RecoveryAttempt = Data.taggedEnum<RecoveryAttempt>()\n\nexport const RecoveryAdmission = Data.taggedEnum<RecoveryAdmission>()\n\nexport const DrainBegin = Data.taggedEnum<DrainBegin>()\n\nexport type InboxApi = {\n readonly enqueue: (item: InboxItem) => Effect.Effect<void>\n readonly takePromotable: (\n runId: string,\n scope: Promotable,\n drainToken: DrainToken\n ) => Effect.Effect<InboxItem | undefined>\n readonly pending: (runId: string) => Effect.Effect<ReadonlyArray<InboxItem>>\n readonly parked: (runId: string) => Effect.Effect<ParkedState | undefined>\n readonly park: (\n runId: string,\n requestIds: ReadonlyArray<string>,\n drainToken: DrainToken\n ) => Effect.Effect<PauseDecision>\n readonly acceptHitl: (\n runId: string,\n admission: HitlAdmission,\n onReady: Effect.Effect<void>\n ) => Effect.Effect<HitlDecision>\n readonly clearPark: (runId: string, generation: ParkGeneration) => Effect.Effect<boolean>\n readonly beginDrain: (runId: string, scope: Promotable) => Effect.Effect<DrainBegin>\n readonly endDrain: (\n runId: string,\n drainToken: DrainToken,\n acknowledged: boolean\n ) => Effect.Effect<void>\n readonly invalidate: (\n runId: string,\n interrupt: Effect.Effect<boolean>,\n releaseIdleClaim: Effect.Effect<void>\n ) => Effect.Effect<{ readonly hadPark: boolean; readonly interrupted: boolean }>\n readonly enqueueAndWake: (item: InboxItem, wake: Effect.Effect<void>) => Effect.Effect<void>\n readonly wakeIfUnblocked: (\n runId: string,\n scope: Promotable,\n wake: Effect.Effect<void>\n ) => Effect.Effect<boolean>\n readonly startIfUnblocked: <E, R>(\n runId: string,\n start: Effect.Effect<CapturedRun<E>, E, R>\n ) => Effect.Effect<CapturedRun<E> | undefined, E, R>\n readonly admitRecovery: <E, R>(\n runId: string,\n attempt: Effect.Effect<RecoveryAttempt, E, R>,\n wake: Effect.Effect<void>\n ) => Effect.Effect<RecoveryAdmission, E, R>\n}\n\nconst isPromotableAt = (item: InboxItem, scope: Promotable) => {\n if (scope === 'steer') return item.delivery === 'steer'\n\n return item.delivery === 'steer' || item.delivery === 'input' || item.delivery === 'queue'\n}\n\ntype Park = {\n readonly generation: ParkGeneration\n readonly requestIds: ReadonlyArray<string>\n readonly responses: ReadonlyArray<ParkedResponse>\n readonly readyWoken: boolean\n}\n\ntype RunControl = {\n readonly pending: Promotable | undefined\n readonly liveToken: DrainToken | undefined\n readonly park: Park | undefined\n readonly leasedGeneration: ParkGeneration | undefined\n}\n\nconst emptyControl: RunControl = {\n pending: undefined,\n liveToken: undefined,\n park: undefined,\n leasedGeneration: undefined\n}\n\nconst widerPending = (current: Promotable | undefined, next: Promotable): Promotable =>\n current === 'input' || next === 'input' ? 'input' : 'steer'\n\nconst remainingPending = (pending: Promotable, scope: Promotable): Promotable | undefined =>\n scope === 'steer' && pending === 'input' ? 'input' : undefined\n\nconst itemScope = (item: InboxItem): Promotable => (item.delivery === 'steer' ? 'steer' : 'input')\n\nconst parkComplete = (park: Park) =>\n park.requestIds.every(requestId =>\n park.responses.some(response => response.requestId === requestId)\n )\n\nconst parkBlocked = (park: Park | undefined) => park !== undefined && !parkComplete(park)\n\nconst parkedState = (park: Park): ParkedState => ({\n generation: park.generation,\n requestIds: park.requestIds,\n responses: park.responses,\n ready: parkComplete(park)\n})\n\nconst isPrunable = (control: RunControl) =>\n control.pending === undefined &&\n control.liveToken === undefined &&\n control.park === undefined &&\n control.leasedGeneration === undefined\n\nexport class Inbox extends Context.Service<Inbox, InboxApi>()('@yolk-sdk/harness/Inbox') {\n /** Fresh queue, admission gate and HITL generations per layer acquisition. */\n static layer = (): Layer.Layer<Inbox> =>\n Layer.effect(\n this,\n Effect.gen(function* () {\n const items = yield* Ref.make<ReadonlyArray<InboxItem>>([])\n const controls = yield* Ref.make<ReadonlyMap<string, RunControl>>(new Map())\n const gate = yield* Semaphore.make(1)\n let generationSeq = 0\n let drainSeq = 0\n\n const withGate = <A, E, R>(body: Effect.Effect<A, E, R>) => gate.withPermits(1)(body)\n\n const getControl = (runId: string) =>\n Ref.get(controls).pipe(Effect.map(current => current.get(runId) ?? emptyControl))\n\n const writeControl = (runId: string, next: RunControl) =>\n Ref.update(controls, current => {\n const copy = new Map(current)\n\n if (isPrunable(next)) copy.delete(runId)\n else copy.set(runId, next)\n\n return copy\n })\n\n const dropQueued = (runId: string) =>\n Ref.update(items, current => current.filter(item => item.runId !== runId))\n\n const acceptHitlPure = (control: RunControl, admission: HitlAdmission): HitlDecision => {\n const park = control.park\n\n if (park === undefined) return HitlDecision.NotParked()\n\n if (park.generation !== admission.generation) return HitlDecision.Stale()\n\n if (!park.requestIds.includes(admission.requestId)) return HitlDecision.UnknownRequest()\n\n if (\n park.responses.some(\n response =>\n response.itemId === admission.itemId || response.requestId === admission.requestId\n )\n ) {\n return HitlDecision.Duplicate()\n }\n\n if (park.readyWoken) return HitlDecision.Duplicate()\n\n const responses = [\n ...park.responses,\n { itemId: admission.itemId, requestId: admission.requestId }\n ]\n\n return parkComplete({ ...park, responses })\n ? HitlDecision.Ready()\n : HitlDecision.Accepted()\n }\n\n return Inbox.of({\n enqueue: item => Ref.update(items, current => [...current, item]),\n takePromotable: (runId, scope, drainToken) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (control.liveToken === undefined || control.liveToken !== drainToken) {\n return undefined\n }\n\n return yield* Ref.modify(items, current => {\n const index = current.findIndex(\n item => item.runId === runId && isPromotableAt(item, scope)\n )\n\n if (index < 0) return [undefined, current] as const\n const taken = current[index]\n\n return [taken, current.filter((_, itemIndex) => itemIndex !== index)] as const\n })\n })\n )\n ),\n pending: runId =>\n Ref.get(items).pipe(\n Effect.map(current => current.filter(item => item.runId === runId))\n ),\n parked: runId =>\n getControl(runId).pipe(\n Effect.map(control =>\n control.park === undefined ? undefined : parkedState(control.park)\n )\n ),\n park: (runId, requestIds, drainToken) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (control.liveToken !== drainToken) return PauseDecision.Stale()\n generationSeq += 1\n const generation = ParkGeneration.make(String(generationSeq))\n yield* writeControl(runId, {\n pending: control.pending,\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: {\n generation,\n requestIds: [...requestIds],\n responses: [],\n readyWoken: false\n }\n })\n\n return PauseDecision.Parked({ generation })\n })\n )\n ),\n acceptHitl: (runId, admission, onReady) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n const decision = acceptHitlPure(control, admission)\n\n if (\n !Predicate.isTagged(decision, 'Accepted') &&\n !Predicate.isTagged(decision, 'Ready')\n ) {\n return decision\n }\n\n const park = control.park\n\n if (park === undefined) return HitlDecision.NotParked()\n\n const nextPark: Park = {\n ...park,\n responses: [\n ...park.responses,\n { itemId: admission.itemId, requestId: admission.requestId }\n ],\n readyWoken: Predicate.isTagged(decision, 'Ready')\n }\n\n yield* writeControl(runId, {\n pending: Predicate.isTagged(decision, 'Ready')\n ? widerPending(control.pending, 'input')\n : control.pending,\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: nextPark\n })\n\n if (Predicate.isTagged(decision, 'Ready')) yield* onReady\n\n return decision\n })\n )\n ),\n clearPark: (runId, generation) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (control.park === undefined || control.park.generation !== generation) {\n return false\n }\n\n yield* writeControl(runId, {\n pending: control.pending,\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: undefined\n })\n\n return true\n })\n )\n ),\n beginDrain: (runId, scope) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n const pending = control.pending\n\n if (parkBlocked(control.park) || pending === undefined) {\n return DrainBegin.Skip()\n }\n\n drainSeq += 1\n const drainToken = DrainToken.make(`d${drainSeq}`)\n const park = control.park\n const ready = park !== undefined && parkComplete(park) ? park : undefined\n yield* writeControl(runId, {\n pending: remainingPending(pending, scope),\n liveToken: drainToken,\n park,\n leasedGeneration: ready?.generation\n })\n\n return DrainBegin.Run({\n drainToken,\n readyResponses: ready === undefined ? [] : ready.responses\n })\n })\n )\n ),\n endDrain: (runId, drainToken, acknowledged) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (control.liveToken !== drainToken) return\n const leased = control.leasedGeneration\n const park = control.park\n\n const clearPark =\n acknowledged &&\n leased !== undefined &&\n park !== undefined &&\n park.generation === leased\n\n yield* writeControl(runId, {\n pending: control.pending,\n liveToken: undefined,\n leasedGeneration: undefined,\n park: clearPark ? undefined : park\n })\n })\n )\n ),\n invalidate: (runId, interrupt, releaseIdleClaim) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n const hadPark = control.park !== undefined\n yield* dropQueued(runId)\n yield* writeControl(runId, emptyControl)\n const interrupted = yield* interrupt\n\n if (!interrupted) yield* releaseIdleClaim\n\n return { hadPark, interrupted }\n })\n )\n ),\n enqueueAndWake: (item, wake) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n yield* Ref.update(items, current => [...current, item])\n const control = yield* getControl(item.runId)\n\n if (parkBlocked(control.park)) return\n yield* writeControl(item.runId, {\n pending: widerPending(control.pending, itemScope(item)),\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: control.park\n })\n yield* wake\n })\n )\n ),\n wakeIfUnblocked: (runId, scope, wake) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (parkBlocked(control.park)) return false\n yield* writeControl(runId, {\n pending: widerPending(control.pending, scope),\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: control.park\n })\n yield* wake\n\n return true\n })\n )\n ),\n startIfUnblocked: (runId, start) =>\n withGate(\n Effect.uninterruptible(\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (parkBlocked(control.park)) return undefined\n const ticket = yield* start\n\n if (Predicate.isTagged(ticket, 'Started')) {\n yield* writeControl(runId, {\n pending: widerPending(control.pending, 'input'),\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: control.park\n })\n }\n\n return ticket\n })\n )\n ),\n admitRecovery: (runId, attempt, wake) =>\n Effect.uninterruptibleMask(restore =>\n restore(gate.take(1)).pipe(\n Effect.flatMap(() =>\n Effect.gen(function* () {\n const control = yield* getControl(runId)\n\n if (parkBlocked(control.park)) return RecoveryAdmission.Skip()\n const decision = yield* attempt\n\n if (!RecoveryAttempt.$is('Resume')(decision)) return decision\n yield* writeControl(runId, {\n pending: widerPending(control.pending, 'input'),\n liveToken: control.liveToken,\n leasedGeneration: control.leasedGeneration,\n park: control.park\n })\n yield* wake\n\n return RecoveryAdmission.Resumed()\n }).pipe(Effect.ensuring(gate.release(1)))\n )\n )\n )\n })\n })\n )\n}\n\n/** Backward-compatible delegation to the owning inbox layer. */\nexport const makeInMemoryInboxLayer = (): Layer.Layer<Inbox> => Inbox.layer()\n"],"mappings":";;;;;AAMA,MAAa,aAAa,OAAO,OAAO,KAAK,OAAO,MAAM,YAAY,CAAC;;AAKvE,MAAa,iBAAiB,OAAO,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AA6D/E,MAAa,eAAe,KAAK,WAAyB;AAE1D,MAAa,gBAAgB,KAAK,WAA0B;AAE5D,MAAa,kBAAkB,KAAK,WAA4B;AAEhE,MAAa,oBAAoB,KAAK,WAA8B;AAEpE,MAAa,aAAa,KAAK,WAAuB;AAkDtD,MAAM,kBAAkB,MAAiB,UAAsB;CAC7D,IAAI,UAAU,SAAS,OAAO,KAAK,aAAa;CAEhD,OAAO,KAAK,aAAa,WAAW,KAAK,aAAa,WAAW,KAAK,aAAa;AACrF;AAgBA,MAAM,eAA2B;CAC/B,SAAS,KAAA;CACT,WAAW,KAAA;CACX,MAAM,KAAA;CACN,kBAAkB,KAAA;AACpB;AAEA,MAAM,gBAAgB,SAAiC,SACrD,YAAY,WAAW,SAAS,UAAU,UAAU;AAEtD,MAAM,oBAAoB,SAAqB,UAC7C,UAAU,WAAW,YAAY,UAAU,UAAU,KAAA;AAEvD,MAAM,aAAa,SAAiC,KAAK,aAAa,UAAU,UAAU;AAE1F,MAAM,gBAAgB,SACpB,KAAK,WAAW,OAAM,cACpB,KAAK,UAAU,MAAK,aAAY,SAAS,cAAc,SAAS,CAClE;AAEF,MAAM,eAAe,SAA2B,SAAS,KAAA,KAAa,CAAC,aAAa,IAAI;AAExF,MAAM,eAAe,UAA6B;CAChD,YAAY,KAAK;CACjB,YAAY,KAAK;CACjB,WAAW,KAAK;CAChB,OAAO,aAAa,IAAI;AAC1B;AAEA,MAAM,cAAc,YAClB,QAAQ,YAAY,KAAA,KACpB,QAAQ,cAAc,KAAA,KACtB,QAAQ,SAAS,KAAA,KACjB,QAAQ,qBAAqB,KAAA;AAE/B,IAAa,QAAb,MAAa,cAAc,QAAQ,QAAyB,EAAE,yBAAyB,EAAE;;CAEvF,OAAO,cACL,MAAM,OACJ,MACA,OAAO,IAAI,aAAa;EACtB,MAAM,QAAQ,OAAO,IAAI,KAA+B,CAAC,CAAC;EAC1D,MAAM,WAAW,OAAO,IAAI,qBAAsC,IAAI,IAAI,CAAC;EAC3E,MAAM,OAAO,OAAO,UAAU,KAAK,CAAC;EACpC,IAAI,gBAAgB;EACpB,IAAI,WAAW;EAEf,MAAM,YAAqB,SAAiC,KAAK,YAAY,CAAC,EAAE,IAAI;EAEpF,MAAM,cAAc,UAClB,IAAI,IAAI,QAAQ,EAAE,KAAK,OAAO,KAAI,YAAW,QAAQ,IAAI,KAAK,KAAK,YAAY,CAAC;EAElF,MAAM,gBAAgB,OAAe,SACnC,IAAI,OAAO,WAAU,YAAW;GAC9B,MAAM,OAAO,IAAI,IAAI,OAAO;GAE5B,IAAI,WAAW,IAAI,GAAG,KAAK,OAAO,KAAK;QAClC,KAAK,IAAI,OAAO,IAAI;GAEzB,OAAO;EACT,CAAC;EAEH,MAAM,cAAc,UAClB,IAAI,OAAO,QAAO,YAAW,QAAQ,QAAO,SAAQ,KAAK,UAAU,KAAK,CAAC;EAE3E,MAAM,kBAAkB,SAAqB,cAA2C;GACtF,MAAM,OAAO,QAAQ;GAErB,IAAI,SAAS,KAAA,GAAW,OAAO,aAAa,UAAU;GAEtD,IAAI,KAAK,eAAe,UAAU,YAAY,OAAO,aAAa,MAAM;GAExE,IAAI,CAAC,KAAK,WAAW,SAAS,UAAU,SAAS,GAAG,OAAO,aAAa,eAAe;GAEvF,IACE,KAAK,UAAU,MACb,aACE,SAAS,WAAW,UAAU,UAAU,SAAS,cAAc,UAAU,SAC7E,GAEA,OAAO,aAAa,UAAU;GAGhC,IAAI,KAAK,YAAY,OAAO,aAAa,UAAU;GAEnD,MAAM,YAAY,CAChB,GAAG,KAAK,WACR;IAAE,QAAQ,UAAU;IAAQ,WAAW,UAAU;GAAU,CAC7D;GAEA,OAAO,aAAa;IAAE,GAAG;IAAM;GAAU,CAAC,IACtC,aAAa,MAAM,IACnB,aAAa,SAAS;EAC5B;EAEA,OAAO,MAAM,GAAG;GACd,UAAS,SAAQ,IAAI,OAAO,QAAO,YAAW,CAAC,GAAG,SAAS,IAAI,CAAC;GAChE,iBAAiB,OAAO,OAAO,eAC7B,SACE,OAAO,gBACL,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAO,WAAW,KAAK;IAEvC,IAAI,QAAQ,cAAc,KAAA,KAAa,QAAQ,cAAc,YAC3D;IAGF,OAAO,OAAO,IAAI,OAAO,QAAO,YAAW;KACzC,MAAM,QAAQ,QAAQ,WACpB,SAAQ,KAAK,UAAU,SAAS,eAAe,MAAM,KAAK,CAC5D;KAEA,IAAI,QAAQ,GAAG,OAAO,CAAC,KAAA,GAAW,OAAO;KAGzC,OAAO,CAFO,QAAQ,QAEP,QAAQ,QAAQ,GAAG,cAAc,cAAc,KAAK,CAAC;IACtE,CAAC;GACH,CAAC,CACH,CACF;GACF,UAAS,UACP,IAAI,IAAI,KAAK,EAAE,KACb,OAAO,KAAI,YAAW,QAAQ,QAAO,SAAQ,KAAK,UAAU,KAAK,CAAC,CACpE;GACF,SAAQ,UACN,WAAW,KAAK,EAAE,KAChB,OAAO,KAAI,YACT,QAAQ,SAAS,KAAA,IAAY,KAAA,IAAY,YAAY,QAAQ,IAAI,CACnE,CACF;GACF,OAAO,OAAO,YAAY,eACxB,SACE,OAAO,gBACL,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAO,WAAW,KAAK;IAEvC,IAAI,QAAQ,cAAc,YAAY,OAAO,cAAc,MAAM;IACjE,iBAAiB;IACjB,MAAM,aAAa,eAAe,KAAK,OAAO,aAAa,CAAC;IAC5D,OAAO,aAAa,OAAO;KACzB,SAAS,QAAQ;KACjB,WAAW,QAAQ;KACnB,kBAAkB,QAAQ;KAC1B,MAAM;MACJ;MACA,YAAY,CAAC,GAAG,UAAU;MAC1B,WAAW,CAAC;MACZ,YAAY;KACd;IACF,CAAC;IAED,OAAO,cAAc,OAAO,EAAE,WAAW,CAAC;GAC5C,CAAC,CACH,CACF;GACF,aAAa,OAAO,WAAW,YAC7B,SACE,OAAO,gBACL,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAO,WAAW,KAAK;IACvC,MAAM,WAAW,eAAe,SAAS,SAAS;IAElD,IACE,CAAC,UAAU,SAAS,UAAU,UAAU,KACxC,CAAC,UAAU,SAAS,UAAU,OAAO,GAErC,OAAO;IAGT,MAAM,OAAO,QAAQ;IAErB,IAAI,SAAS,KAAA,GAAW,OAAO,aAAa,UAAU;IAEtD,MAAM,WAAiB;KACrB,GAAG;KACH,WAAW,CACT,GAAG,KAAK,WACR;MAAE,QAAQ,UAAU;MAAQ,WAAW,UAAU;KAAU,CAC7D;KACA,YAAY,UAAU,SAAS,UAAU,OAAO;IAClD;IAEA,OAAO,aAAa,OAAO;KACzB,SAAS,UAAU,SAAS,UAAU,OAAO,IACzC,aAAa,QAAQ,SAAS,OAAO,IACrC,QAAQ;KACZ,WAAW,QAAQ;KACnB,kBAAkB,QAAQ;KAC1B,MAAM;IACR,CAAC;IAED,IAAI,UAAU,SAAS,UAAU,OAAO,GAAG,OAAO;IAElD,OAAO;GACT,CAAC,CACH,CACF;GACF,YAAY,OAAO,eACjB,SACE,OAAO,gBACL,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAO,WAAW,KAAK;IAEvC,IAAI,QAAQ,SAAS,KAAA,KAAa,QAAQ,KAAK,eAAe,YAC5D,OAAO;IAGT,OAAO,aAAa,OAAO;KACzB,SAAS,QAAQ;KACjB,WAAW,QAAQ;KACnB,kBAAkB,QAAQ;KAC1B,MAAM,KAAA;IACR,CAAC;IAED,OAAO;GACT,CAAC,CACH,CACF;GACF,aAAa,OAAO,UAClB,SACE,OAAO,gBACL,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAO,WAAW,KAAK;IACvC,MAAM,UAAU,QAAQ;IAExB,IAAI,YAAY,QAAQ,IAAI,KAAK,YAAY,KAAA,GAC3C,OAAO,WAAW,KAAK;IAGzB,YAAY;IACZ,MAAM,aAAa,WAAW,KAAK,IAAI,UAAU;IACjD,MAAM,OAAO,QAAQ;IACrB,MAAM,QAAQ,SAAS,KAAA,KAAa,aAAa,IAAI,IAAI,OAAO,KAAA;IAChE,OAAO,aAAa,OAAO;KACzB,SAAS,iBAAiB,SAAS,KAAK;KACxC,WAAW;KACX;KACA,kBAAkB,OAAO;IAC3B,CAAC;IAED,OAAO,WAAW,IAAI;KACpB;KACA,gBAAgB,UAAU,KAAA,IAAY,CAAC,IAAI,MAAM;IACnD,CAAC;GACH,CAAC,CACH,CACF;GACF,WAAW,OAAO,YAAY,iBAC5B,SACE,OAAO,gBACL,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAO,WAAW,KAAK;IAEvC,IAAI,QAAQ,cAAc,YAAY;IACtC,MAAM,SAAS,QAAQ;IACvB,MAAM,OAAO,QAAQ;IAErB,MAAM,YACJ,gBACA,WAAW,KAAA,KACX,SAAS,KAAA,KACT,KAAK,eAAe;IAEtB,OAAO,aAAa,OAAO;KACzB,SAAS,QAAQ;KACjB,WAAW,KAAA;KACX,kBAAkB,KAAA;KAClB,MAAM,YAAY,KAAA,IAAY;IAChC,CAAC;GACH,CAAC,CACH,CACF;GACF,aAAa,OAAO,WAAW,qBAC7B,SACE,OAAO,gBACL,OAAO,IAAI,aAAa;IAEtB,MAAM,WAAU,OADO,WAAW,KAAK,GACf,SAAS,KAAA;IACjC,OAAO,WAAW,KAAK;IACvB,OAAO,aAAa,OAAO,YAAY;IACvC,MAAM,cAAc,OAAO;IAE3B,IAAI,CAAC,aAAa,OAAO;IAEzB,OAAO;KAAE;KAAS;IAAY;GAChC,CAAC,CACH,CACF;GACF,iBAAiB,MAAM,SACrB,SACE,OAAO,gBACL,OAAO,IAAI,aAAa;IACtB,OAAO,IAAI,OAAO,QAAO,YAAW,CAAC,GAAG,SAAS,IAAI,CAAC;IACtD,MAAM,UAAU,OAAO,WAAW,KAAK,KAAK;IAE5C,IAAI,YAAY,QAAQ,IAAI,GAAG;IAC/B,OAAO,aAAa,KAAK,OAAO;KAC9B,SAAS,aAAa,QAAQ,SAAS,UAAU,IAAI,CAAC;KACtD,WAAW,QAAQ;KACnB,kBAAkB,QAAQ;KAC1B,MAAM,QAAQ;IAChB,CAAC;IACD,OAAO;GACT,CAAC,CACH,CACF;GACF,kBAAkB,OAAO,OAAO,SAC9B,SACE,OAAO,gBACL,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAO,WAAW,KAAK;IAEvC,IAAI,YAAY,QAAQ,IAAI,GAAG,OAAO;IACtC,OAAO,aAAa,OAAO;KACzB,SAAS,aAAa,QAAQ,SAAS,KAAK;KAC5C,WAAW,QAAQ;KACnB,kBAAkB,QAAQ;KAC1B,MAAM,QAAQ;IAChB,CAAC;IACD,OAAO;IAEP,OAAO;GACT,CAAC,CACH,CACF;GACF,mBAAmB,OAAO,UACxB,SACE,OAAO,gBACL,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAO,WAAW,KAAK;IAEvC,IAAI,YAAY,QAAQ,IAAI,GAAG,OAAO,KAAA;IACtC,MAAM,SAAS,OAAO;IAEtB,IAAI,UAAU,SAAS,QAAQ,SAAS,GACtC,OAAO,aAAa,OAAO;KACzB,SAAS,aAAa,QAAQ,SAAS,OAAO;KAC9C,WAAW,QAAQ;KACnB,kBAAkB,QAAQ;KAC1B,MAAM,QAAQ;IAChB,CAAC;IAGH,OAAO;GACT,CAAC,CACH,CACF;GACF,gBAAgB,OAAO,SAAS,SAC9B,OAAO,qBAAoB,YACzB,QAAQ,KAAK,KAAK,CAAC,CAAC,EAAE,KACpB,OAAO,cACL,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAO,WAAW,KAAK;IAEvC,IAAI,YAAY,QAAQ,IAAI,GAAG,OAAO,kBAAkB,KAAK;IAC7D,MAAM,WAAW,OAAO;IAExB,IAAI,CAAC,gBAAgB,IAAI,QAAQ,EAAE,QAAQ,GAAG,OAAO;IACrD,OAAO,aAAa,OAAO;KACzB,SAAS,aAAa,QAAQ,SAAS,OAAO;KAC9C,WAAW,QAAQ;KACnB,kBAAkB,QAAQ;KAC1B,MAAM,QAAQ;IAChB,CAAC;IACD,OAAO;IAEP,OAAO,kBAAkB,QAAQ;GACnC,CAAC,EAAE,KAAK,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAC1C,CACF,CACF;EACJ,CAAC;CACH,CAAC,CACH;AACJ;;AAGA,MAAa,+BAAmD,MAAM,MAAM"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yolk-sdk/harness",
|
|
3
|
-
"version": "0.1.0-canary.
|
|
3
|
+
"version": "0.1.0-canary.80",
|
|
4
4
|
"description": "Domain-free agent run lifecycle: coordinator, store, inbox, and pluggable drivers.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
81
|
"effect": "4.0.0-rc.115",
|
|
82
|
-
"@yolk-sdk/agent": "^0.1.0-canary.
|
|
82
|
+
"@yolk-sdk/agent": "^0.1.0-canary.80"
|
|
83
83
|
},
|
|
84
84
|
"scripts": {
|
|
85
85
|
"build": "tsdown",
|
package/src/driver.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
DrainBegin,
|
|
6
6
|
Inbox,
|
|
7
7
|
RecoveryAttempt,
|
|
8
|
+
type DrainToken,
|
|
8
9
|
type HitlAdmission,
|
|
9
10
|
type HitlDecision,
|
|
10
11
|
type InboxItem,
|
|
@@ -29,7 +30,7 @@ export type ResumeSuspendedResult = {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
export type DrainContext = {
|
|
32
|
-
readonly drainToken:
|
|
33
|
+
readonly drainToken: DrainToken
|
|
33
34
|
readonly readyResponses: ReadonlyArray<ParkedResponse>
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -67,7 +68,7 @@ export type DriverApi = {
|
|
|
67
68
|
readonly pause: (
|
|
68
69
|
runId: string,
|
|
69
70
|
requestIds: ReadonlyArray<string>,
|
|
70
|
-
drainToken:
|
|
71
|
+
drainToken: DrainToken
|
|
71
72
|
) => Effect.Effect<PauseDecision>
|
|
72
73
|
readonly resumeHitl: (runId: string, admission: HitlAdmission) => Effect.Effect<HitlDecision>
|
|
73
74
|
readonly stop: (runId: string) => Effect.Effect<StopDecision>
|
package/src/inbox.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { Context, Data, Effect, Layer, Predicate, Ref, Semaphore } from 'effect'
|
|
2
|
+
import * as Schema from 'effect/Schema'
|
|
2
3
|
import type { CapturedRun, Promotable } from './coordinator.ts'
|
|
3
4
|
|
|
5
|
+
/** Nominal identity only. All strings stay admissible so stale/empty/foreign
|
|
6
|
+
* tokens retain the inbox's existing no-op decisions rather than parse failures. */
|
|
7
|
+
export const DrainToken = Schema.String.pipe(Schema.brand('DrainToken'))
|
|
8
|
+
|
|
9
|
+
export type DrainToken = typeof DrainToken.Type
|
|
10
|
+
|
|
11
|
+
/** Nominal identity only; freshness and instance ownership are checked by Inbox. */
|
|
12
|
+
export const ParkGeneration = Schema.String.pipe(Schema.brand('ParkGeneration'))
|
|
13
|
+
|
|
14
|
+
export type ParkGeneration = typeof ParkGeneration.Type
|
|
15
|
+
|
|
4
16
|
export type InboxKind = 'input' | 'hitl'
|
|
5
17
|
|
|
6
18
|
export type InboxItem = {
|
|
@@ -16,7 +28,7 @@ export type ParkedResponse = {
|
|
|
16
28
|
}
|
|
17
29
|
|
|
18
30
|
export type ParkedState = {
|
|
19
|
-
readonly generation:
|
|
31
|
+
readonly generation: ParkGeneration
|
|
20
32
|
readonly requestIds: ReadonlyArray<string>
|
|
21
33
|
readonly responses: ReadonlyArray<ParkedResponse>
|
|
22
34
|
readonly ready: boolean
|
|
@@ -25,7 +37,7 @@ export type ParkedState = {
|
|
|
25
37
|
export type HitlAdmission = {
|
|
26
38
|
readonly itemId: string
|
|
27
39
|
readonly requestId: string
|
|
28
|
-
readonly generation:
|
|
40
|
+
readonly generation: ParkGeneration
|
|
29
41
|
}
|
|
30
42
|
|
|
31
43
|
export type HitlDecision =
|
|
@@ -37,7 +49,7 @@ export type HitlDecision =
|
|
|
37
49
|
| { readonly _tag: 'NotParked' }
|
|
38
50
|
|
|
39
51
|
export type PauseDecision =
|
|
40
|
-
| { readonly _tag: 'Parked'; readonly generation:
|
|
52
|
+
| { readonly _tag: 'Parked'; readonly generation: ParkGeneration }
|
|
41
53
|
| { readonly _tag: 'Stale' }
|
|
42
54
|
|
|
43
55
|
export type RecoveryAttempt =
|
|
@@ -54,7 +66,7 @@ export type DrainBegin =
|
|
|
54
66
|
| { readonly _tag: 'Skip' }
|
|
55
67
|
| {
|
|
56
68
|
readonly _tag: 'Run'
|
|
57
|
-
readonly drainToken:
|
|
69
|
+
readonly drainToken: DrainToken
|
|
58
70
|
readonly readyResponses: ReadonlyArray<ParkedResponse>
|
|
59
71
|
}
|
|
60
72
|
|
|
@@ -73,25 +85,25 @@ export type InboxApi = {
|
|
|
73
85
|
readonly takePromotable: (
|
|
74
86
|
runId: string,
|
|
75
87
|
scope: Promotable,
|
|
76
|
-
drainToken:
|
|
88
|
+
drainToken: DrainToken
|
|
77
89
|
) => Effect.Effect<InboxItem | undefined>
|
|
78
90
|
readonly pending: (runId: string) => Effect.Effect<ReadonlyArray<InboxItem>>
|
|
79
91
|
readonly parked: (runId: string) => Effect.Effect<ParkedState | undefined>
|
|
80
92
|
readonly park: (
|
|
81
93
|
runId: string,
|
|
82
94
|
requestIds: ReadonlyArray<string>,
|
|
83
|
-
drainToken:
|
|
95
|
+
drainToken: DrainToken
|
|
84
96
|
) => Effect.Effect<PauseDecision>
|
|
85
97
|
readonly acceptHitl: (
|
|
86
98
|
runId: string,
|
|
87
99
|
admission: HitlAdmission,
|
|
88
100
|
onReady: Effect.Effect<void>
|
|
89
101
|
) => Effect.Effect<HitlDecision>
|
|
90
|
-
readonly clearPark: (runId: string, generation:
|
|
102
|
+
readonly clearPark: (runId: string, generation: ParkGeneration) => Effect.Effect<boolean>
|
|
91
103
|
readonly beginDrain: (runId: string, scope: Promotable) => Effect.Effect<DrainBegin>
|
|
92
104
|
readonly endDrain: (
|
|
93
105
|
runId: string,
|
|
94
|
-
drainToken:
|
|
106
|
+
drainToken: DrainToken,
|
|
95
107
|
acknowledged: boolean
|
|
96
108
|
) => Effect.Effect<void>
|
|
97
109
|
readonly invalidate: (
|
|
@@ -123,7 +135,7 @@ const isPromotableAt = (item: InboxItem, scope: Promotable) => {
|
|
|
123
135
|
}
|
|
124
136
|
|
|
125
137
|
type Park = {
|
|
126
|
-
readonly generation:
|
|
138
|
+
readonly generation: ParkGeneration
|
|
127
139
|
readonly requestIds: ReadonlyArray<string>
|
|
128
140
|
readonly responses: ReadonlyArray<ParkedResponse>
|
|
129
141
|
readonly readyWoken: boolean
|
|
@@ -131,9 +143,9 @@ type Park = {
|
|
|
131
143
|
|
|
132
144
|
type RunControl = {
|
|
133
145
|
readonly pending: Promotable | undefined
|
|
134
|
-
readonly liveToken:
|
|
146
|
+
readonly liveToken: DrainToken | undefined
|
|
135
147
|
readonly park: Park | undefined
|
|
136
|
-
readonly leasedGeneration:
|
|
148
|
+
readonly leasedGeneration: ParkGeneration | undefined
|
|
137
149
|
}
|
|
138
150
|
|
|
139
151
|
const emptyControl: RunControl = {
|
|
@@ -274,7 +286,7 @@ export class Inbox extends Context.Service<Inbox, InboxApi>()('@yolk-sdk/harness
|
|
|
274
286
|
|
|
275
287
|
if (control.liveToken !== drainToken) return PauseDecision.Stale()
|
|
276
288
|
generationSeq += 1
|
|
277
|
-
const generation = String(generationSeq)
|
|
289
|
+
const generation = ParkGeneration.make(String(generationSeq))
|
|
278
290
|
yield* writeControl(runId, {
|
|
279
291
|
pending: control.pending,
|
|
280
292
|
liveToken: control.liveToken,
|
|
@@ -366,7 +378,7 @@ export class Inbox extends Context.Service<Inbox, InboxApi>()('@yolk-sdk/harness
|
|
|
366
378
|
}
|
|
367
379
|
|
|
368
380
|
drainSeq += 1
|
|
369
|
-
const drainToken = `d${drainSeq}`
|
|
381
|
+
const drainToken = DrainToken.make(`d${drainSeq}`)
|
|
370
382
|
const park = control.park
|
|
371
383
|
const ready = park !== undefined && parkComplete(park) ? park : undefined
|
|
372
384
|
yield* writeControl(runId, {
|