@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 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: string;
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: string) => Effect.Effect<PauseDecision>;
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
  };
@@ -1 +1 @@
1
- {"version":3,"file":"driver.d.mts","names":[],"sources":["../src/driver.ts"],"mappings":"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"}
1
+ {"version":3,"file":"driver.d.mts","names":[],"sources":["../src/driver.ts"],"mappings":"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"}
@@ -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: string;
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: string;
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: string;
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: string;
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: string;
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: string;
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: string;
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: string;
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: string;
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: string;
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: string;
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: string;
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: string) => Effect.Effect<InboxItem | undefined>;
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: string) => Effect.Effect<PauseDecision>;
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: string) => Effect.Effect<boolean>;
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: string, acknowledged: boolean) => Effect.Effect<void>;
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
@@ -1 +1 @@
1
- {"version":3,"file":"inbox.d.mts","names":[],"sources":["../src/inbox.ts"],"mappings":"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"}
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
@@ -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":";;AA4DA,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,OAAO,aAAa;IACvC,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,IAAI;IACvB,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"}
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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yolk-sdk/harness",
3
- "version": "0.1.0-canary.78",
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.78"
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: string
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: string
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: string
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: string
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: string }
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: string
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: string
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: string
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: string) => Effect.Effect<boolean>
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: string,
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: string
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: string | undefined
146
+ readonly liveToken: DrainToken | undefined
135
147
  readonly park: Park | undefined
136
- readonly leasedGeneration: string | undefined
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, {